summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-02-08 20:40:26 -0500
committerGitHub <noreply@github.com>2024-02-09 01:40:26 +0000
commite5e2c45998d3a655c4b2d78c0a1fcb61e09c1982 (patch)
tree4a3af21378652245bdd2e58cc615458d5c163c2c /cli
parentb07a156b1d2548c07c7e822ab69d2ef9bfaca630 (diff)
fix: upgrade to deno_ast 0.33 (#22341)
* Uses diagnostics from deno_ast * Real fix for https://github.com/denoland/deno/pull/22310 * Moves `deno lint --json` code here * Upgrades swc Closes #22117 Closes #22109 Closes #21927 Closes #20993
Diffstat (limited to 'cli')
-rw-r--r--cli/Cargo.toml14
-rw-r--r--cli/cache/module_info.rs2
-rw-r--r--cli/cache/parsed_source.rs6
-rw-r--r--cli/diagnostics.rs678
-rw-r--r--cli/errors.rs9
-rw-r--r--cli/lsp/analysis.rs49
-rw-r--r--cli/lsp/code_lens.rs2
-rw-r--r--cli/lsp/documents.rs8
-rw-r--r--cli/lsp/testing/collectors.rs2
-rw-r--r--cli/main.rs1
-rw-r--r--cli/node.rs2
-rw-r--r--cli/tests/testdata/lint/expected_from_stdin_json.out2
-rw-r--r--cli/tests/testdata/lint/expected_json.out6
-rw-r--r--cli/tests/testdata/lint/with_report_config_override.out4
-rw-r--r--cli/tools/doc.rs137
-rw-r--r--cli/tools/lint.rs181
-rw-r--r--cli/tools/registry/diagnostics.rs175
-rw-r--r--cli/tools/registry/graph.rs25
-rw-r--r--cli/tools/registry/mod.rs11
-rw-r--r--cli/tools/registry/tar.rs2
-rw-r--r--cli/tools/repl/session.rs15
-rw-r--r--cli/tools/test/mod.rs2
-rw-r--r--cli/tools/test/reporters/pretty.rs2
-rw-r--r--cli/tools/vendor/analyze.rs3
-rw-r--r--cli/util/fs.rs12
-rw-r--r--cli/util/import_map.rs5
26 files changed, 243 insertions, 1112 deletions
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index fdef096ad..dffce000a 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -53,21 +53,21 @@ winapi.workspace = true
winres.workspace = true
[dependencies]
-deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "dep_graph", "module_specifier", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
+deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
deno_cache_dir = "=0.6.1"
deno_config = "=0.9.2"
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
-deno_doc = { version = "=0.100.0", features = ["html"] }
-deno_emit = "=0.35.0"
-deno_graph = "=0.64.2"
-deno_lint = { version = "=0.55.2", features = ["docs"] }
+deno_doc = { version = "=0.103.0", features = ["html"] }
+deno_emit = "=0.36.0"
+deno_graph = "=0.65.0"
+deno_lint = { version = "=0.56.0", features = ["docs"] }
deno_lockfile.workspace = true
deno_npm = "=0.16.0"
deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] }
deno_semver = "=0.5.4"
deno_task_shell = "=0.14.3"
deno_terminal.workspace = true
-eszip = "=0.60.0"
+eszip = "=0.62.0"
napi_sym.workspace = true
async-trait.workspace = true
@@ -89,7 +89,7 @@ dotenvy = "0.15.7"
dprint-plugin-json = "=0.19.1"
dprint-plugin-jupyter = "=0.1.2"
dprint-plugin-markdown = "=0.16.3"
-dprint-plugin-typescript = "=0.88.10"
+dprint-plugin-typescript = "=0.89.0"
env_logger = "=0.10.0"
fancy-regex = "=0.10.0"
# If you disable the default __vendored_zlib_ng feature above, you _must_ be able to link against `-lz`.
diff --git a/cli/cache/module_info.rs b/cli/cache/module_info.rs
index 063809f84..6bb718038 100644
--- a/cli/cache/module_info.rs
+++ b/cli/cache/module_info.rs
@@ -149,7 +149,7 @@ impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> {
specifier: &ModuleSpecifier,
source: Arc<str>,
media_type: MediaType,
- ) -> Result<ModuleInfo, deno_ast::Diagnostic> {
+ ) -> Result<ModuleInfo, deno_ast::ParseDiagnostic> {
// attempt to load from the cache
let source_hash = ModuleInfoCacheSourceHash::from_source(source.as_bytes());
match self.module_info_cache.get_module_info(
diff --git a/cli/cache/parsed_source.rs b/cli/cache/parsed_source.rs
index 75170aaf9..8d98587e2 100644
--- a/cli/cache/parsed_source.rs
+++ b/cli/cache/parsed_source.rs
@@ -32,7 +32,7 @@ impl<'a> LazyGraphSourceParser<'a> {
pub fn get_or_parse_source(
&self,
module_specifier: &ModuleSpecifier,
- ) -> Result<Option<deno_ast::ParsedSource>, deno_ast::Diagnostic> {
+ ) -> Result<Option<deno_ast::ParsedSource>, deno_ast::ParseDiagnostic> {
let Some(deno_graph::Module::Js(module)) = self.graph.get(module_specifier)
else {
return Ok(None);
@@ -53,7 +53,7 @@ impl ParsedSourceCache {
pub fn get_parsed_source_from_js_module(
&self,
module: &deno_graph::JsModule,
- ) -> Result<ParsedSource, deno_ast::Diagnostic> {
+ ) -> Result<ParsedSource, deno_ast::ParseDiagnostic> {
self.get_or_parse_module(
&module.specifier,
module.source.clone(),
@@ -68,7 +68,7 @@ impl ParsedSourceCache {
specifier: &deno_graph::ModuleSpecifier,
source: Arc<str>,
media_type: MediaType,
- ) -> deno_core::anyhow::Result<ParsedSource, deno_ast::Diagnostic> {
+ ) -> deno_core::anyhow::Result<ParsedSource, deno_ast::ParseDiagnostic> {
let parser = self.as_capturing_parser();
// this will conditionally parse because it's using a CapturingModuleParser
parser.parse_module(ParseOptions {
diff --git a/cli/diagnostics.rs b/cli/diagnostics.rs
deleted file mode 100644
index 7eff66d76..000000000
--- a/cli/diagnostics.rs
+++ /dev/null
@@ -1,678 +0,0 @@
-// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-
-use std::borrow::Cow;
-use std::fmt;
-use std::fmt::Display;
-use std::fmt::Write as _;
-use std::path::PathBuf;
-
-use deno_ast::ModuleSpecifier;
-use deno_ast::SourcePos;
-use deno_ast::SourceRange;
-use deno_ast::SourceRanged;
-use deno_ast::SourceTextInfo;
-use deno_terminal::colors;
-use unicode_width::UnicodeWidthStr;
-
-use crate::cache::LazyGraphSourceParser;
-
-pub trait SourceTextStore {
- fn get_source_text<'a>(
- &'a self,
- specifier: &ModuleSpecifier,
- ) -> Option<Cow<'a, SourceTextInfo>>;
-}
-
-pub struct SourceTextParsedSourceStore<'a>(pub LazyGraphSourceParser<'a>);
-
-impl<'a> SourceTextParsedSourceStore<'a> {
- pub fn get_source_text_from_store(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Option<Cow<'_, SourceTextInfo>> {
- let parsed_source = self.0.get_or_parse_source(specifier).ok()??;
- Some(Cow::Owned(parsed_source.text_info().clone()))
- }
-}
-
-impl SourceTextStore for SourceTextParsedSourceStore<'_> {
- fn get_source_text<'a>(
- &'a self,
- specifier: &ModuleSpecifier,
- ) -> Option<Cow<'a, SourceTextInfo>> {
- match self.get_source_text_from_store(specifier) {
- Some(text_info) => Some(text_info),
- None => {
- // todo(#22117): this is extremely hacky and bad because the file
- // may have changed by the time we get here. Instead of doing this,
- // we should store the text info in the diagnostics
- if specifier.scheme() == "file" {
- let path = specifier.to_file_path().ok()?;
- let text = std::fs::read_to_string(path).ok()?;
- Some(Cow::Owned(SourceTextInfo::new(text.into())))
- } else {
- None
- }
- }
- }
- }
-}
-
-pub enum DiagnosticLevel {
- Error,
- Warning,
-}
-
-#[derive(Clone, Copy, Debug)]
-pub struct DiagnosticSourceRange {
- pub start: DiagnosticSourcePos,
- pub end: DiagnosticSourcePos,
-}
-
-#[derive(Clone, Copy, Debug)]
-pub enum DiagnosticSourcePos {
- SourcePos(SourcePos),
- ByteIndex(usize),
- LineAndCol {
- // 0-indexed line number
- line: usize,
- // 0-indexed column number
- column: usize,
- },
-}
-
-impl DiagnosticSourcePos {
- fn pos(&self, source: &SourceTextInfo) -> SourcePos {
- match self {
- DiagnosticSourcePos::SourcePos(pos) => *pos,
- DiagnosticSourcePos::ByteIndex(index) => source.range().start() + *index,
- DiagnosticSourcePos::LineAndCol { line, column } => {
- source.line_start(*line) + *column
- }
- }
- }
-}
-
-#[derive(Clone, Debug)]
-pub enum DiagnosticLocation<'a> {
- /// The diagnostic is relevant to a specific path.
- Path { path: PathBuf },
- /// The diagnostic is relevant to an entire module.
- Module {
- /// The specifier of the module that contains the diagnostic.
- specifier: Cow<'a, ModuleSpecifier>,
- },
- /// The diagnostic is relevant to a specific position in a module.
- ///
- /// This variant will get the relevant `SouceTextInfo` from the cache using
- /// the given specifier, and will then calculate the line and column numbers
- /// from the given `SourcePos`.
- ModulePosition {
- /// The specifier of the module that contains the diagnostic.
- specifier: Cow<'a, ModuleSpecifier>,
- /// The source position of the diagnostic.
- source_pos: DiagnosticSourcePos,
- },
-}
-
-impl<'a> DiagnosticLocation<'a> {
- /// Return the line and column number of the diagnostic.
- ///
- /// The line number is 1-indexed.
- ///
- /// The column number is 1-indexed. This is the number of UTF-16 code units
- /// from the start of the line to the diagnostic.
- /// Why UTF-16 code units? Because that's what VS Code understands, and
- /// everyone uses VS Code. :)
- fn position(&self, sources: &dyn SourceTextStore) -> Option<(usize, usize)> {
- match self {
- DiagnosticLocation::Path { .. } => None,
- DiagnosticLocation::Module { .. } => None,
- DiagnosticLocation::ModulePosition {
- specifier,
- source_pos,
- } => {
- let source = sources.get_source_text(specifier).expect(
- "source text should be in the cache if the location is in a file",
- );
- let pos = source_pos.pos(&source);
- let line_index = source.line_index(pos);
- let line_start_pos = source.line_start(line_index);
- let content = source.range_text(&SourceRange::new(line_start_pos, pos));
- let line = line_index + 1;
- let column = content.encode_utf16().count() + 1;
- Some((line, column))
- }
- }
- }
-}
-
-pub struct DiagnosticSnippet<'a> {
- /// The source text for this snippet. The
- pub source: DiagnosticSnippetSource<'a>,
- /// The piece of the snippet that should be highlighted.
- pub highlight: DiagnosticSnippetHighlight<'a>,
-}
-
-pub struct DiagnosticSnippetHighlight<'a> {
- /// The range of the snippet that should be highlighted.
- pub range: DiagnosticSourceRange,
- /// The style of the highlight.
- pub style: DiagnosticSnippetHighlightStyle,
- /// An optional inline description of the highlight.
- pub description: Option<Cow<'a, str>>,
-}
-
-pub enum DiagnosticSnippetHighlightStyle {
- /// The highlight is an error. This will place red carets under the highlight.
- Error,
- #[allow(dead_code)]
- /// The highlight is a warning. This will place yellow carets under the
- /// highlight.
- Warning,
- #[allow(dead_code)]
- /// The highlight shows code additions. This will place green + signs under
- /// the highlight and will highlight the code in green.
- Addition,
- /// The highlight shows a hint. This will place blue dashes under the
- /// highlight.
- Hint,
-}
-
-impl DiagnosticSnippetHighlightStyle {
- fn style_underline(
- &self,
- s: impl std::fmt::Display,
- ) -> impl std::fmt::Display {
- match self {
- DiagnosticSnippetHighlightStyle::Error => colors::red_bold(s),
- DiagnosticSnippetHighlightStyle::Warning => colors::yellow_bold(s),
- DiagnosticSnippetHighlightStyle::Addition => colors::green_bold(s),
- DiagnosticSnippetHighlightStyle::Hint => colors::intense_blue(s),
- }
- }
-
- fn underline_char(&self) -> char {
- match self {
- DiagnosticSnippetHighlightStyle::Error => '^',
- DiagnosticSnippetHighlightStyle::Warning => '^',
- DiagnosticSnippetHighlightStyle::Addition => '+',
- DiagnosticSnippetHighlightStyle::Hint => '-',
- }
- }
-}
-
-pub enum DiagnosticSnippetSource<'a> {
- /// The specifier of the module that should be displayed in this snippet. The
- /// contents of the file will be retrieved from the `SourceTextStore`.
- Specifier(Cow<'a, ModuleSpecifier>),
- #[allow(dead_code)]
- /// The source text that should be displayed in this snippet.
- ///
- /// This should be used if the text of the snippet is not available in the
- /// `SourceTextStore`.
- SourceTextInfo(Cow<'a, deno_ast::SourceTextInfo>),
-}
-
-impl<'a> DiagnosticSnippetSource<'a> {
- fn to_source_text_info(
- &self,
- sources: &'a dyn SourceTextStore,
- ) -> Cow<'a, SourceTextInfo> {
- match self {
- DiagnosticSnippetSource::Specifier(specifier) => {
- sources.get_source_text(specifier).expect(
- "source text should be in the cache if snippet source is a specifier",
- )
- }
- DiagnosticSnippetSource::SourceTextInfo(info) => info.clone(),
- }
- }
-}
-
-/// Returns the text of the line with the given number.
-fn line_text(source: &SourceTextInfo, line_number: usize) -> &str {
- source.line_text(line_number - 1)
-}
-
-/// Returns the text of the line that contains the given position, split at the
-/// given position.
-fn line_text_split(
- source: &SourceTextInfo,
- pos: DiagnosticSourcePos,
-) -> (&str, &str) {
- let pos = pos.pos(source);
- let line_index = source.line_index(pos);
- let line_start_pos = source.line_start(line_index);
- let line_end_pos = source.line_end(line_index);
- let before = source.range_text(&SourceRange::new(line_start_pos, pos));
- let after = source.range_text(&SourceRange::new(pos, line_end_pos));
- (before, after)
-}
-
-/// Returns the text of the line that contains the given positions, split at the
-/// given positions.
-///
-/// If the positions are on different lines, this will panic.
-fn line_text_split3(
- source: &SourceTextInfo,
- start_pos: DiagnosticSourcePos,
- end_pos: DiagnosticSourcePos,
-) -> (&str, &str, &str) {
- let start_pos = start_pos.pos(source);
- let end_pos = end_pos.pos(source);
- let line_index = source.line_index(start_pos);
- assert_eq!(
- line_index,
- source.line_index(end_pos),
- "start and end must be on the same line"
- );
- let line_start_pos = source.line_start(line_index);
- let line_end_pos = source.line_end(line_index);
- let before = source.range_text(&SourceRange::new(line_start_pos, start_pos));
- let between = source.range_text(&SourceRange::new(start_pos, end_pos));
- let after = source.range_text(&SourceRange::new(end_pos, line_end_pos));
- (before, between, after)
-}
-
-/// Returns the line number (1 indexed) of the line that contains the given
-/// position.
-fn line_number(source: &SourceTextInfo, pos: DiagnosticSourcePos) -> usize {
- source.line_index(pos.pos(source)) + 1
-}
-
-pub trait Diagnostic {
- /// The level of the diagnostic.
- fn level(&self) -> DiagnosticLevel;
-
- /// The diagnostic code, like `no-explicit-any` or `ban-untagged-ignore`.
- fn code(&self) -> impl fmt::Display + '_;
-
- /// The human-readable diagnostic message.
- fn message(&self) -> impl fmt::Display + '_;
-
- /// The location this diagnostic is associated with.
- fn location(&self) -> DiagnosticLocation;
-
- /// A snippet showing the source code associated with the diagnostic.
- fn snippet(&self) -> Option<DiagnosticSnippet<'_>>;
-
- /// A hint for fixing the diagnostic.
- fn hint(&self) -> Option<impl fmt::Display + '_>;
-
- /// A snippet showing how the diagnostic can be fixed.
- fn snippet_fixed(&self) -> Option<DiagnosticSnippet<'_>>;
-
- fn info(&self) -> Cow<'_, [Cow<'_, str>]>;
-
- /// An optional URL to the documentation for the diagnostic.
- fn docs_url(&self) -> Option<impl fmt::Display + '_>;
-
- fn display<'a>(
- &'a self,
- sources: &'a dyn SourceTextStore,
- ) -> DiagnosticDisplay<'a, Self> {
- DiagnosticDisplay {
- diagnostic: self,
- sources,
- }
- }
-}
-
-struct RepeatingCharFmt(char, usize);
-impl fmt::Display for RepeatingCharFmt {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- for _ in 0..self.1 {
- f.write_char(self.0)?;
- }
- Ok(())
- }
-}
-
-/// How many spaces a tab should be displayed as. 2 is the default used for
-/// `deno fmt`, so we'll use that here.
-const TAB_WIDTH: usize = 2;
-
-struct ReplaceTab<'a>(&'a str);
-impl fmt::Display for ReplaceTab<'_> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut written = 0;
- for (i, c) in self.0.char_indices() {
- if c == '\t' {
- self.0[written..i].fmt(f)?;
- RepeatingCharFmt(' ', TAB_WIDTH).fmt(f)?;
- written = i + 1;
- }
- }
- self.0[written..].fmt(f)?;
- Ok(())
- }
-}
-
-/// The width of the string as displayed, assuming tabs are 2 spaces wide.
-///
-/// This display width assumes that zero-width-joined characters are the width
-/// of their consituent characters. This means that "Person: Red Hair" (which is
-/// represented as "Person" + "ZWJ" + "Red Hair") will have a width of 4.
-///
-/// Whether this is correct is unfortunately dependent on the font / terminal
-/// being used. Here is a list of what terminals consider the length of
-/// "Person: Red Hair" to be:
-///
-/// | Terminal | Rendered Width |
-/// | ---------------- | -------------- |
-/// | Windows Terminal | 5 chars |
-/// | iTerm (macOS) | 2 chars |
-/// | Terminal (macOS) | 2 chars |
-/// | VS Code terminal | 4 chars |
-/// | GNOME Terminal | 4 chars |
-///
-/// If we really wanted to, we could try and detect the terminal being used and
-/// adjust the width accordingly. However, this is probably not worth the
-/// effort.
-fn display_width(str: &str) -> usize {
- str.width_cjk() + (str.chars().filter(|c| *c == '\t').count() * TAB_WIDTH)
-}
-
-pub struct DiagnosticDisplay<'a, T: Diagnostic + ?Sized> {
- diagnostic: &'a T,
- sources: &'a dyn SourceTextStore,
-}
-
-impl<T: Diagnostic + ?Sized> Display for DiagnosticDisplay<'_, T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- print_diagnostic(f, self.sources, self.diagnostic)
- }
-}
-
-// error[missing-return-type]: missing explicit return type on public function
-// at /mnt/artemis/Projects/github.com/denoland/deno/test.ts:1:16
-// |
-// 1 | export function test() {
-// | ^^^^
-// = hint: add an explicit return type to the function
-// |
-// 1 | export function test(): string {
-// | ^^^^^^^^
-//
-// info: all functions that are exported from a module must have an explicit return type to support fast check and documentation generation.
-// docs: https://jsr.io/d/missing-return-type
-fn print_diagnostic(
- io: &mut dyn std::fmt::Write,
- sources: &dyn SourceTextStore,
- diagnostic: &(impl Diagnostic + ?Sized),
-) -> Result<(), std::fmt::Error> {
- match diagnostic.level() {
- DiagnosticLevel::Error => {
- write!(
- io,
- "{}",
- colors::red_bold(format_args!("error[{}]", diagnostic.code()))
- )?;
- }
- DiagnosticLevel::Warning => {
- write!(
- io,
- "{}",
- colors::yellow_bold(format_args!("warning[{}]", diagnostic.code()))
- )?;
- }
- }
-
- writeln!(io, ": {}", colors::bold(diagnostic.message()))?;
-
- let mut max_line_number_digits = 1;
- if let Some(snippet) = diagnostic.snippet() {
- let source = snippet.source.to_source_text_info(sources);
- let last_line = line_number(&source, snippet.highlight.range.end);
- max_line_number_digits = max_line_number_digits.max(last_line.ilog10() + 1);
- }
- if let Some(snippet) = diagnostic.snippet_fixed() {
- let source = snippet.source.to_source_text_info(sources);
- let last_line = line_number(&source, snippet.highlight.range.end);
- max_line_number_digits = max_line_number_digits.max(last_line.ilog10() + 1);
- }
-
- let location = diagnostic.location();
- write!(
- io,
- "{}{}",
- RepeatingCharFmt(' ', max_line_number_digits as usize),
- colors::intense_blue("-->"),
- )?;
- match &location {
- DiagnosticLocation::Path { path } => {
- write!(io, " {}", colors::cyan(path.display()))?;
- }
- DiagnosticLocation::Module { specifier }
- | DiagnosticLocation::ModulePosition { specifier, .. } => {
- if let Ok(path) = specifier.to_file_path() {
- write!(io, " {}", colors::cyan(path.display()))?;
- } else {
- write!(io, " {}", colors::cyan(specifier.as_str()))?;
- }
- }
- }
- if let Some((line, column)) = location.position(sources) {
- write!(
- io,
- "{}",
- colors::yellow(format_args!(":{}:{}", line, column))
- )?;
- }
- writeln!(io)?;
-
- if let Some(snippet) = diagnostic.snippet() {
- print_snippet(io, sources, &snippet, max_line_number_digits)?;
- };
-
- if let Some(hint) = diagnostic.hint() {
- write!(
- io,
- "{} {} ",
- RepeatingCharFmt(' ', max_line_number_digits as usize),
- colors::intense_blue("=")
- )?;
- writeln!(io, "{}: {}", colors::bold("hint"), hint)?;
- }
-
- if let Some(snippet) = diagnostic.snippet_fixed() {
- print_snippet(io, sources, &snippet, max_line_number_digits)?;
- }
-
- writeln!(io)?;
-
- let mut needs_final_newline = false;
- for info in diagnostic.info().iter() {
- needs_final_newline = true;
- writeln!(io, " {}: {}", colors::intense_blue("info"), info)?;
- }
- if let Some(docs_url) = diagnostic.docs_url() {
- needs_final_newline = true;
- writeln!(io, " {}: {}", colors::intense_blue("docs"), docs_url)?;
- }
-
- if needs_final_newline {
- writeln!(io)?;
- }
-
- Ok(())
-}
-
-/// Prints a snippet to the given writer and returns the line number indent.
-fn print_snippet(
- io: &mut dyn std::fmt::Write,
- sources: &dyn SourceTextStore,
- snippet: &DiagnosticSnippet<'_>,
- max_line_number_digits: u32,
-) -> Result<(), std::fmt::Error> {
- let DiagnosticSnippet { source, highlight } = snippet;
-
- fn print_padded(
- io: &mut dyn std::fmt::Write,
- text: impl std::fmt::Display,
- padding: u32,
- ) -> Result<(), std::fmt::Error> {
- for _ in 0..padding {
- write!(io, " ")?;
- }
- write!(io, "{}", text)?;
- Ok(())
- }
-
- let source = source.to_source_text_info(sources);
-
- let start_line_number = line_number(&source, highlight.range.start);
- let end_line_number = line_number(&source, highlight.range.end);
-
- print_padded(io, colors::intense_blue(" | "), max_line_number_digits)?;
- writeln!(io)?;
- for line_number in start_line_number..=end_line_number {
- print_padded(
- io,
- colors::intense_blue(format_args!("{} | ", line_number)),
- max_line_number_digits - line_number.ilog10() - 1,
- )?;
-
- let padding_width;
- let highlight_width;
- if line_number == start_line_number && start_line_number == end_line_number
- {
- let (before, between, after) =
- line_text_split3(&source, highlight.range.start, highlight.range.end);
- write!(io, "{}", ReplaceTab(before))?;
- match highlight.style {
- DiagnosticSnippetHighlightStyle::Addition => {
- write!(io, "{}", colors::green(ReplaceTab(between)))?;
- }
- _ => {
- write!(io, "{}", ReplaceTab(between))?;
- }
- }
- writeln!(io, "{}", ReplaceTab(after))?;
- padding_width = display_width(before);
- highlight_width = display_width(between);
- } else if line_number == start_line_number {
- let (before, after) = line_text_split(&source, highlight.range.start);
- write!(io, "{}", ReplaceTab(before))?;
- match highlight.style {
- DiagnosticSnippetHighlightStyle::Addition => {
- write!(io, "{}", colors::green(ReplaceTab(after)))?;
- }
- _ => {
- write!(io, "{}", ReplaceTab(after))?;
- }
- }
- writeln!(io)?;
- padding_width = display_width(before);
- highlight_width = display_width(after);
- } else if line_number == end_line_number {
- let (before, after) = line_text_split(&source, highlight.range.end);
- match highlight.style {
- DiagnosticSnippetHighlightStyle::Addition => {
- write!(io, "{}", colors::green(ReplaceTab(before)))?;
- }
- _ => {
- write!(io, "{}", ReplaceTab(before))?;
- }
- }
- write!(io, "{}", ReplaceTab(after))?;
- writeln!(io)?;
- padding_width = 0;
- highlight_width = display_width(before);
- } else {
- let line = line_text(&source, line_number);
- writeln!(io, "{}", ReplaceTab(line))?;
- padding_width = 0;
- highlight_width = display_width(line);
- }
-
- print_padded(io, colors::intense_blue(" | "), max_line_number_digits)?;
- write!(io, "{}", RepeatingCharFmt(' ', padding_width))?;
- let underline =
- RepeatingCharFmt(highlight.style.underline_char(), highlight_width);
- write!(io, "{}", highlight.style.style_underline(underline))?;
-
- if line_number == end_line_number {
- if let Some(description) = &highlight.description {
- write!(io, " {}", highlight.style.style_underline(description))?;
- }
- }
-
- writeln!(io)?;
- }
-
- Ok(())
-}
-
-#[cfg(test)]
-mod tests {
- use std::borrow::Cow;
-
- use deno_ast::ModuleSpecifier;
- use deno_ast::SourceTextInfo;
-
- use super::SourceTextStore;
-
- struct TestSource {
- specifier: ModuleSpecifier,
- text_info: SourceTextInfo,
- }
-
- impl SourceTextStore for TestSource {
- fn get_source_text<'a>(
- &'a self,
- specifier: &ModuleSpecifier,
- ) -> Option<Cow<'a, SourceTextInfo>> {
- if specifier == &self.specifier {
- Some(Cow::Borrowed(&self.text_info))
- } else {
- None
- }
- }
- }
-
- #[test]
- fn test_display_width() {
- assert_eq!(super::display_width("abc"), 3);
- assert_eq!(super::display_width("\t"), 2);
- assert_eq!(super::display_width("\t\t123"), 7);
- assert_eq!(super::display_width("πŸŽ„"), 2);
- assert_eq!(super::display_width("πŸŽ„πŸŽ„"), 4);
- assert_eq!(super::display_width("πŸ§‘β€πŸ¦°"), 4);
- }
-
- #[test]
- fn test_position_in_file_from_text_info_simple() {
- let specifier: ModuleSpecifier = "file:///dev/test.ts".parse().unwrap();
- let text_info = SourceTextInfo::new("foo\nbar\nbaz".into());
- let pos = text_info.line_start(1);
- let sources = TestSource {
- specifier: specifier.clone(),
- text_info,
- };
- let location = super::DiagnosticLocation::ModulePosition {
- specifier: Cow::Borrowed(&specifier),
- source_pos: super::DiagnosticSourcePos::SourcePos(pos),
- };
- let position = location.position(&sources).unwrap();
- assert_eq!(position, (2, 1))
- }
-
- #[test]
- fn test_position_in_file_from_text_info_emoji() {
- let specifier: ModuleSpecifier = "file:///dev/test.ts".parse().unwrap();
- let text_info = SourceTextInfo::new("πŸ§‘β€πŸ¦°text".into());
- let pos = text_info.line_start(0) + 11; // the end of the emoji
- let sources = TestSource {
- specifier: specifier.clone(),
- text_info,
- };
- let location = super::DiagnosticLocation::ModulePosition {
- specifier: Cow::Borrowed(&specifier),
- source_pos: super::DiagnosticSourcePos::SourcePos(pos),
- };
- let position = location.position(&sources).unwrap();
- assert_eq!(position, (1, 6))
- }
-}
diff --git a/cli/errors.rs b/cli/errors.rs
index c2539df7e..fce286f15 100644
--- a/cli/errors.rs
+++ b/cli/errors.rs
@@ -9,7 +9,7 @@
//! Diagnostics are compile-time type errors, whereas JsErrors are runtime
//! exceptions.
-use deno_ast::Diagnostic;
+use deno_ast::ParseDiagnostic;
use deno_core::error::AnyError;
use deno_graph::source::ResolveError;
use deno_graph::ModuleError;
@@ -22,7 +22,7 @@ fn get_import_map_error_class(_: &ImportMapError) -> &'static str {
"URIError"
}
-fn get_diagnostic_class(_: &Diagnostic) -> &'static str {
+fn get_diagnostic_class(_: &ParseDiagnostic) -> &'static str {
"SyntaxError"
}
@@ -67,7 +67,10 @@ pub fn get_error_class_name(e: &AnyError) -> &'static str {
e.downcast_ref::<ImportMapError>()
.map(get_import_map_error_class)
})
- .or_else(|| e.downcast_ref::<Diagnostic>().map(get_diagnostic_class))
+ .or_else(|| {
+ e.downcast_ref::<ParseDiagnostic>()
+ .map(get_diagnostic_class)
+ })
.or_else(|| {
e.downcast_ref::<ModuleGraphError>()
.map(get_module_graph_error_class)
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs
index 6c6d7cab4..96ee422c6 100644
--- a/cli/lsp/analysis.rs
+++ b/cli/lsp/analysis.rs
@@ -20,6 +20,7 @@ use deno_core::serde::Deserialize;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::ModuleSpecifier;
+use deno_lint::diagnostic::LintDiagnostic;
use deno_lint::rules::LintRule;
use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_node::NpmResolver;
@@ -118,15 +119,21 @@ impl Reference {
}
}
-fn as_lsp_range(range: &deno_lint::diagnostic::Range) -> Range {
+fn as_lsp_range(diagnostic: &LintDiagnostic) -> Range {
+ let start_lc = diagnostic
+ .text_info
+ .line_and_column_index(diagnostic.range.start);
+ let end_lc = diagnostic
+ .text_info
+ .line_and_column_index(diagnostic.range.end);
Range {
start: Position {
- line: range.start.line_index as u32,
- character: range.start.column_index as u32,
+ line: start_lc.line_index as u32,
+ character: start_lc.column_index as u32,
},
end: Position {
- line: range.end.line_index as u32,
- character: range.end.column_index as u32,
+ line: end_lc.line_index as u32,
+ character: end_lc.column_index as u32,
},
}
}
@@ -142,12 +149,12 @@ pub fn get_lint_references(
lint_diagnostics
.into_iter()
.map(|d| Reference {
+ range: as_lsp_range(&d),
category: Category::Lint {
message: d.message,
code: d.code,
hint: d.hint,
},
- range: as_lsp_range(&d.range),
})
.collect(),
)
@@ -1061,36 +1068,6 @@ mod tests {
}
#[test]
- fn test_as_lsp_range() {
- let fixture = deno_lint::diagnostic::Range {
- start: deno_lint::diagnostic::Position {
- line_index: 0,
- column_index: 2,
- byte_index: 23,
- },
- end: deno_lint::diagnostic::Position {
- line_index: 1,
- column_index: 0,
- byte_index: 33,
- },
- };
- let actual = as_lsp_range(&fixture);
- assert_eq!(
- actual,
- lsp::Range {
- start: lsp::Position {
- line: 0,
- character: 2,
- },
- end: lsp::Position {
- line: 1,
- character: 0,
- },
- }
- );
- }
-
- #[test]
fn test_try_reverse_map_package_json_exports() {
let exports = json!({
".": {
diff --git a/cli/lsp/code_lens.rs b/cli/lsp/code_lens.rs
index adf1d5c63..59787dd84 100644
--- a/cli/lsp/code_lens.rs
+++ b/cli/lsp/code_lens.rs
@@ -560,7 +560,7 @@ mod tests {
Deno.test(`test template literal name`, () => {});
"#;
let parsed_module = deno_ast::parse_module(deno_ast::ParseParams {
- specifier: specifier.to_string(),
+ specifier: specifier.clone(),
text_info: SourceTextInfo::new(source.into()),
media_type: MediaType::TypeScript,
capture_tokens: true,
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs
index c758d341b..94d0e979b 100644
--- a/cli/lsp/documents.rs
+++ b/cli/lsp/documents.rs
@@ -246,7 +246,7 @@ impl AssetOrDocument {
pub fn maybe_parsed_source(
&self,
- ) -> Option<Result<deno_ast::ParsedSource, deno_ast::Diagnostic>> {
+ ) -> Option<Result<deno_ast::ParsedSource, deno_ast::ParseDiagnostic>> {
self.document().and_then(|d| d.maybe_parsed_source())
}
@@ -283,7 +283,7 @@ impl DocumentDependencies {
}
type ModuleResult = Result<deno_graph::JsModule, deno_graph::ModuleGraphError>;
-type ParsedSourceResult = Result<ParsedSource, deno_ast::Diagnostic>;
+type ParsedSourceResult = Result<ParsedSource, deno_ast::ParseDiagnostic>;
#[derive(Debug)]
struct DocumentInner {
@@ -595,7 +595,7 @@ impl Document {
pub fn maybe_parsed_source(
&self,
- ) -> Option<Result<deno_ast::ParsedSource, deno_ast::Diagnostic>> {
+ ) -> Option<Result<deno_ast::ParsedSource, deno_ast::ParseDiagnostic>> {
self.0.maybe_parsed_source.clone()
}
@@ -1855,7 +1855,7 @@ fn parse_source(
maybe_headers: Option<&HashMap<String, String>>,
) -> ParsedSourceResult {
deno_ast::parse_module(deno_ast::ParseParams {
- specifier: specifier.to_string(),
+ specifier: specifier.clone(),
text_info,
media_type: MediaType::from_specifier_and_headers(specifier, maybe_headers),
capture_tokens: true,
diff --git a/cli/lsp/testing/collectors.rs b/cli/lsp/testing/collectors.rs
index a66e56948..8579ccc7d 100644
--- a/cli/lsp/testing/collectors.rs
+++ b/cli/lsp/testing/collectors.rs
@@ -644,7 +644,7 @@ pub mod tests {
let specifier = resolve_url("file:///a/example.ts").unwrap();
let parsed_module = deno_ast::parse_module(deno_ast::ParseParams {
- specifier: specifier.to_string(),
+ specifier: specifier.clone(),
text_info: deno_ast::SourceTextInfo::new(source.into()),
media_type: deno_ast::MediaType::TypeScript,
capture_tokens: true,
diff --git a/cli/main.rs b/cli/main.rs
index 9d0ade085..5e446efb8 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -5,7 +5,6 @@ mod auth_tokens;
mod cache;
mod cdp;
mod deno_std;
-mod diagnostics;
mod emit;
mod errors;
mod factory;
diff --git a/cli/node.rs b/cli/node.rs
index a66713685..cbe0aaaf1 100644
--- a/cli/node.rs
+++ b/cli/node.rs
@@ -67,7 +67,7 @@ impl CliCjsCodeAnalyzer {
}
let parsed_source = deno_ast::parse_script(deno_ast::ParseParams {
- specifier: specifier.to_string(),
+ specifier: specifier.clone(),
text_info: deno_ast::SourceTextInfo::new(source.into()),
media_type,
capture_tokens: true,
diff --git a/cli/tests/testdata/lint/expected_from_stdin_json.out b/cli/tests/testdata/lint/expected_from_stdin_json.out
index 26bf7ddc7..9e1188bcd 100644
--- a/cli/tests/testdata/lint/expected_from_stdin_json.out
+++ b/cli/tests/testdata/lint/expected_from_stdin_json.out
@@ -1,6 +1,7 @@
{
"diagnostics": [
{
+ "filename": "[WILDCARD]$deno$stdin.ts",
"range": {
"start": {
"line": 1,
@@ -13,7 +14,6 @@
"bytePos": 11
}
},
- "filename": "[WILDCARD]$deno$stdin.ts",
"message": "`any` type is not allowed",
"code": "no-explicit-any",
"hint": [WILDCARD]
diff --git a/cli/tests/testdata/lint/expected_json.out b/cli/tests/testdata/lint/expected_json.out
index 08ea0d3e0..95c3d30ba 100644
--- a/cli/tests/testdata/lint/expected_json.out
+++ b/cli/tests/testdata/lint/expected_json.out
@@ -1,6 +1,7 @@
{
"diagnostics": [
{
+ "filename": "[WILDCARD]file1.js",
"range": {
"start": {
"line": 1,
@@ -13,12 +14,12 @@
"bytePos": 19
}
},
- "filename": "[WILDCARD]file1.js",
"message": "Ignore directive requires lint rule name(s)",
"code": "ban-untagged-ignore",
"hint": [WILDCARD]
},
{
+ "filename": "[WILDCARD]file1.js",
"range": {
"start": {
"line": 2,
@@ -31,12 +32,12 @@
"bytePos": 36
}
},
- "filename": "[WILDCARD]file1.js",
"message": "Empty block statement",
"code": "no-empty",
"hint": [WILDCARD]
},
{
+ "filename": "[WILDCARD]file2.ts",
"range": {
"start": {
"line": 3,
@@ -49,7 +50,6 @@
"bytePos": 59
}
},
- "filename": "[WILDCARD]file2.ts",
"message": "Empty block statement",
"code": "no-empty",
"hint": [WILDCARD]
diff --git a/cli/tests/testdata/lint/with_report_config_override.out b/cli/tests/testdata/lint/with_report_config_override.out
index ac633d911..7ca748158 100644
--- a/cli/tests/testdata/lint/with_report_config_override.out
+++ b/cli/tests/testdata/lint/with_report_config_override.out
@@ -1,6 +1,7 @@
{
"diagnostics": [
{
+ "filename": "[WILDCARD]a.ts",
"range": {
"start": {
"line": 1,
@@ -13,12 +14,12 @@
"bytePos": 12
}
},
- "filename": "[WILDCARD]a.ts",
"message": "TODO should be tagged with (@username) or (#issue)",
"code": "ban-untagged-todo",
"hint": "Add a user tag or issue reference to the TODO comment, e.g. TODO(@djones), TODO(djones), TODO(#123)"
},
{
+ "filename": "[WILDCARD]a.ts",
"range": {
"start": {
"line": 2,
@@ -31,7 +32,6 @@
"bytePos": 25
}
},
- "filename": "[WILDCARD]a.ts",
"message": "`add` is never used",
"code": "no-unused-vars",
"hint": "If this is intentional, prefix it with an underscore like `_add`"
diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs
index 729ee05fc..d2cd0c2a2 100644
--- a/cli/tools/doc.rs
+++ b/cli/tools/doc.rs
@@ -4,24 +4,14 @@ use crate::args::DocFlags;
use crate::args::DocHtmlFlag;
use crate::args::DocSourceFileFlag;
use crate::args::Flags;
-use crate::cache::LazyGraphSourceParser;
use crate::colors;
-use crate::diagnostics::Diagnostic;
-use crate::diagnostics::DiagnosticLevel;
-use crate::diagnostics::DiagnosticLocation;
-use crate::diagnostics::DiagnosticSnippet;
-use crate::diagnostics::DiagnosticSnippetHighlight;
-use crate::diagnostics::DiagnosticSnippetHighlightStyle;
-use crate::diagnostics::DiagnosticSnippetSource;
-use crate::diagnostics::DiagnosticSourcePos;
-use crate::diagnostics::DiagnosticSourceRange;
-use crate::diagnostics::SourceTextParsedSourceStore;
use crate::display::write_json_to_stdout;
use crate::display::write_to_stdout_ignore_sigpipe;
use crate::factory::CliFactory;
use crate::graph_util::graph_lock_or_exit;
use crate::tsc::get_types_declaration_file_text;
use crate::util::fs::collect_specifiers;
+use deno_ast::diagnostics::Diagnostic;
use deno_config::glob::FilePatterns;
use deno_config::glob::PathOrPatternSet;
use deno_core::anyhow::bail;
@@ -34,10 +24,7 @@ use deno_graph::ModuleAnalyzer;
use deno_graph::ModuleParser;
use deno_graph::ModuleSpecifier;
use doc::DocDiagnostic;
-use doc::DocDiagnosticKind;
use indexmap::IndexMap;
-use lsp_types::Url;
-use std::borrow::Cow;
use std::collections::BTreeMap;
use std::rc::Rc;
@@ -143,10 +130,7 @@ pub async fn doc(flags: Flags, doc_flags: DocFlags) -> Result<(), AnyError> {
if doc_flags.lint {
let diagnostics = doc_parser.take_diagnostics();
- check_diagnostics(
- LazyGraphSourceParser::new(parsed_source_cache, &graph),
- &diagnostics,
- )?;
+ check_diagnostics(&diagnostics)?;
}
doc_nodes_by_url
@@ -252,6 +236,7 @@ async fn generate_docs_directory(
hide_module_doc_title: false,
href_resolver: Rc::new(DocResolver { deno_ns }),
sidebar_flatten_namespaces: false,
+ usage_composer: None,
};
let files = deno_doc::html::generate(options, doc_nodes_by_url)
@@ -308,118 +293,7 @@ fn print_docs_to_stdout(
write_to_stdout_ignore_sigpipe(details.as_bytes()).map_err(AnyError::from)
}
-impl Diagnostic for DocDiagnostic {
- fn level(&self) -> DiagnosticLevel {
- DiagnosticLevel::Error
- }
-
- fn code(&self) -> impl std::fmt::Display + '_ {
- match self.kind {
- DocDiagnosticKind::MissingJsDoc => "missing-jsdoc",
- DocDiagnosticKind::MissingExplicitType => "missing-explicit-type",
- DocDiagnosticKind::MissingReturnType => "missing-return-type",
- DocDiagnosticKind::PrivateTypeRef { .. } => "private-type-ref",
- }
- }
-
- fn message(&self) -> impl std::fmt::Display + '_ {
- match &self.kind {
- DocDiagnosticKind::MissingJsDoc => {
- Cow::Borrowed("exported symbol is missing JSDoc documentation")
- }
- DocDiagnosticKind::MissingExplicitType => {
- Cow::Borrowed("exported symbol is missing an explicit type annotation")
- }
- DocDiagnosticKind::MissingReturnType => Cow::Borrowed(
- "exported function is missing an explicit return type annotation",
- ),
- DocDiagnosticKind::PrivateTypeRef {
- reference, name, ..
- } => Cow::Owned(format!(
- "public type '{name}' references private type '{reference}'",
- )),
- }
- }
-
- fn location(&self) -> DiagnosticLocation {
- let specifier = Url::parse(&self.location.filename).unwrap();
- DiagnosticLocation::ModulePosition {
- specifier: Cow::Owned(specifier),
- source_pos: DiagnosticSourcePos::ByteIndex(self.location.byte_index),
- }
- }
-
- fn snippet(&self) -> Option<DiagnosticSnippet<'_>> {
- let specifier = Url::parse(&self.location.filename).unwrap();
- Some(DiagnosticSnippet {
- source: DiagnosticSnippetSource::Specifier(Cow::Owned(specifier)),
- highlight: DiagnosticSnippetHighlight {
- style: DiagnosticSnippetHighlightStyle::Error,
- range: DiagnosticSourceRange {
- start: DiagnosticSourcePos::ByteIndex(self.location.byte_index),
- end: DiagnosticSourcePos::ByteIndex(self.location.byte_index + 1),
- },
- description: None,
- },
- })
- }
-
- fn hint(&self) -> Option<impl std::fmt::Display + '_> {
- match &self.kind {
- DocDiagnosticKind::PrivateTypeRef { .. } => {
- Some("make the referenced type public or remove the reference")
- }
- _ => None,
- }
- }
- fn snippet_fixed(&self) -> Option<DiagnosticSnippet<'_>> {
- match &self.kind {
- DocDiagnosticKind::PrivateTypeRef {
- reference_location, ..
- } => {
- let specifier = Url::parse(&reference_location.filename).unwrap();
- Some(DiagnosticSnippet {
- source: DiagnosticSnippetSource::Specifier(Cow::Owned(specifier)),
- highlight: DiagnosticSnippetHighlight {
- style: DiagnosticSnippetHighlightStyle::Hint,
- range: DiagnosticSourceRange {
- start: DiagnosticSourcePos::ByteIndex(
- reference_location.byte_index,
- ),
- end: DiagnosticSourcePos::ByteIndex(
- reference_location.byte_index + 1,
- ),
- },
- description: Some(Cow::Borrowed("this is the referenced type")),
- },
- })
- }
- _ => None,
- }
- }
-
- fn info(&self) -> std::borrow::Cow<'_, [std::borrow::Cow<'_, str>]> {
- match &self.kind {
- DocDiagnosticKind::MissingJsDoc => Cow::Borrowed(&[]),
- DocDiagnosticKind::MissingExplicitType => Cow::Borrowed(&[]),
- DocDiagnosticKind::MissingReturnType => Cow::Borrowed(&[]),
- DocDiagnosticKind::PrivateTypeRef { .. } => {
- Cow::Borrowed(&[Cow::Borrowed(
- "to ensure documentation is complete all types that are exposed in the public API must be public",
- )])
- }
- }
- }
-
- fn docs_url(&self) -> Option<impl std::fmt::Display + '_> {
- None::<&str>
- }
-}
-
-fn check_diagnostics(
- source_parser: LazyGraphSourceParser,
- diagnostics: &[DocDiagnostic],
-) -> Result<(), AnyError> {
+fn check_diagnostics(diagnostics: &[DocDiagnostic]) -> Result<(), AnyError> {
if diagnostics.is_empty() {
return Ok(());
}
@@ -441,8 +315,7 @@ fn check_diagnostics(
for (_, diagnostics_by_col) in diagnostics_by_lc {
for (_, diagnostics) in diagnostics_by_col {
for diagnostic in diagnostics {
- let sources = SourceTextParsedSourceStore(source_parser);
- log::error!("{}", diagnostic.display(&sources));
+ log::error!("{}", diagnostic.display());
}
}
}
diff --git a/cli/tools/lint.rs b/cli/tools/lint.rs
index e9f84fd77..32b47e453 100644
--- a/cli/tools/lint.rs
+++ b/cli/tools/lint.rs
@@ -8,33 +8,22 @@ use crate::args::LintOptions;
use crate::args::LintReporterKind;
use crate::args::LintRulesConfig;
use crate::colors;
-use crate::diagnostics::Diagnostic;
-use crate::diagnostics::DiagnosticLevel;
-use crate::diagnostics::DiagnosticLocation;
-use crate::diagnostics::DiagnosticSnippet;
-use crate::diagnostics::DiagnosticSnippetHighlight;
-use crate::diagnostics::DiagnosticSnippetHighlightStyle;
-use crate::diagnostics::DiagnosticSnippetSource;
-use crate::diagnostics::DiagnosticSourcePos;
-use crate::diagnostics::DiagnosticSourceRange;
-use crate::diagnostics::SourceTextStore;
use crate::factory::CliFactory;
use crate::tools::fmt::run_parallelized;
use crate::util::file_watcher;
use crate::util::fs::canonicalize_path;
+use crate::util::fs::specifier_from_file_path;
use crate::util::fs::FileCollector;
use crate::util::path::is_script_ext;
use crate::util::sync::AtomicFlag;
+use deno_ast::diagnostics::Diagnostic;
use deno_ast::MediaType;
-use deno_ast::ModuleSpecifier;
use deno_ast::ParsedSource;
-use deno_ast::SourceTextInfo;
use deno_config::glob::FilePatterns;
use deno_core::anyhow::bail;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::serde_json;
-use deno_core::url;
use deno_lint::diagnostic::LintDiagnostic;
use deno_lint::linter::LintFileOptions;
use deno_lint::linter::Linter;
@@ -44,7 +33,6 @@ use deno_lint::rules::LintRule;
use log::debug;
use log::info;
use serde::Serialize;
-use std::borrow::Cow;
use std::fs;
use std::io::stdin;
use std::io::Read;
@@ -124,9 +112,12 @@ pub async fn lint(flags: Flags, lint_flags: LintFlags) -> Result<(), AnyError> {
let reporter_lock = Arc::new(Mutex::new(create_reporter(reporter_kind)));
let lint_rules = get_config_rules_err_empty(lint_options.rules)?;
let file_path = cli_options.initial_cwd().join(STDIN_FILE_NAME);
- let file_path = file_path.to_string_lossy();
let r = lint_stdin(&file_path, lint_rules);
- let success = handle_lint_result(&file_path, r, reporter_lock.clone());
+ let success = handle_lint_result(
+ &file_path.to_string_lossy(),
+ r,
+ reporter_lock.clone(),
+ );
reporter_lock.lock().unwrap().close(1);
success
} else {
@@ -278,13 +269,13 @@ fn lint_file(
source_code: String,
lint_rules: Vec<&'static dyn LintRule>,
) -> Result<(Vec<LintDiagnostic>, ParsedSource), AnyError> {
- let filename = file_path.to_string_lossy().to_string();
- let media_type = MediaType::from_path(file_path);
+ let specifier = specifier_from_file_path(file_path)?;
+ let media_type = MediaType::from_specifier(&specifier);
let linter = create_linter(lint_rules);
let (source, file_diagnostics) = linter.lint_file(LintFileOptions {
- filename,
+ specifier,
media_type,
source_code: source_code.clone(),
})?;
@@ -296,7 +287,7 @@ fn lint_file(
/// Treats input as TypeScript.
/// Compatible with `--json` flag.
fn lint_stdin(
- file_path: &str,
+ file_path: &Path,
lint_rules: Vec<&'static dyn LintRule>,
) -> Result<(Vec<LintDiagnostic>, ParsedSource), AnyError> {
let mut source_code = String::new();
@@ -307,7 +298,7 @@ fn lint_stdin(
let linter = create_linter(lint_rules);
let (source, file_diagnostics) = linter.lint_file(LintFileOptions {
- filename: file_path.to_string(),
+ specifier: specifier_from_file_path(file_path)?,
source_code: source_code.clone(),
media_type: MediaType::TypeScript,
})?;
@@ -324,7 +315,10 @@ fn handle_lint_result(
match result {
Ok((mut file_diagnostics, source)) => {
- sort_diagnostics(&mut file_diagnostics);
+ file_diagnostics.sort_by(|a, b| match a.specifier.cmp(&b.specifier) {
+ std::cmp::Ordering::Equal => a.range.start.cmp(&b.range.start),
+ file_order => file_order,
+ });
for d in file_diagnostics.iter() {
reporter.visit_diagnostic(d, &source);
}
@@ -359,77 +353,11 @@ impl PrettyLintReporter {
}
}
-impl Diagnostic for LintDiagnostic {
- fn level(&self) -> DiagnosticLevel {
- DiagnosticLevel::Error
- }
-
- fn code(&self) -> impl std::fmt::Display + '_ {
- &self.code
- }
-
- fn message(&self) -> impl std::fmt::Display + '_ {
- &self.message
- }
-
- fn location(&self) -> DiagnosticLocation {
- let specifier = url::Url::from_file_path(&self.filename).unwrap();
- DiagnosticLocation::ModulePosition {
- specifier: Cow::Owned(specifier),
- source_pos: DiagnosticSourcePos::ByteIndex(self.range.start.byte_index),
- }
- }
-
- fn snippet(&self) -> Option<DiagnosticSnippet<'_>> {
- let specifier = url::Url::from_file_path(&self.filename).unwrap();
- let range = DiagnosticSourceRange {
- start: DiagnosticSourcePos::ByteIndex(self.range.start.byte_index),
- end: DiagnosticSourcePos::ByteIndex(self.range.end.byte_index),
- };
- Some(DiagnosticSnippet {
- source: DiagnosticSnippetSource::Specifier(Cow::Owned(specifier)),
- highlight: DiagnosticSnippetHighlight {
- range,
- style: DiagnosticSnippetHighlightStyle::Error,
- description: None,
- },
- })
- }
-
- fn hint(&self) -> Option<impl std::fmt::Display + '_> {
- self.hint.as_ref().map(|h| h as &dyn std::fmt::Display)
- }
-
- fn snippet_fixed(&self) -> Option<DiagnosticSnippet<'_>> {
- None // todo
- }
-
- fn info(&self) -> Cow<'_, [std::borrow::Cow<'_, str>]> {
- Cow::Borrowed(&[])
- }
-
- fn docs_url(&self) -> Option<impl std::fmt::Display + '_> {
- Some(format!("https://lint.deno.land/#{}", &self.code))
- }
-}
-
-struct OneSource<'a>(&'a ParsedSource);
-
-impl SourceTextStore for OneSource<'_> {
- fn get_source_text<'a>(
- &'a self,
- _specifier: &ModuleSpecifier,
- ) -> Option<Cow<'a, SourceTextInfo>> {
- Some(Cow::Borrowed(self.0.text_info()))
- }
-}
-
impl LintReporter for PrettyLintReporter {
- fn visit_diagnostic(&mut self, d: &LintDiagnostic, source: &ParsedSource) {
+ fn visit_diagnostic(&mut self, d: &LintDiagnostic, _source: &ParsedSource) {
self.lint_count += 1;
- let sources = OneSource(source);
- eprintln!("{}", d.display(&sources));
+ eprintln!("{}", d.display());
}
fn visit_error(&mut self, file_path: &str, err: &AnyError) {
@@ -466,11 +394,12 @@ impl LintReporter for CompactLintReporter {
fn visit_diagnostic(&mut self, d: &LintDiagnostic, _source: &ParsedSource) {
self.lint_count += 1;
+ let line_and_column = d.text_info.line_and_column_display(d.range.start);
eprintln!(
"{}: line {}, col {} - {} ({})",
- d.filename,
- d.range.start.line_index + 1,
- d.range.start.column_index + 1,
+ d.specifier,
+ line_and_column.line_number,
+ line_and_column.column_number,
d.message,
d.code
)
@@ -496,9 +425,47 @@ impl LintReporter for CompactLintReporter {
}
}
+// WARNING: Ensure doesn't change because it's used in the JSON output
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct JsonDiagnosticLintPosition {
+ /// The 1-indexed line number.
+ pub line: usize,
+ /// The 0-indexed column index.
+ pub col: usize,
+ pub byte_pos: usize,
+}
+
+impl JsonDiagnosticLintPosition {
+ pub fn new(byte_index: usize, loc: deno_ast::LineAndColumnIndex) -> Self {
+ JsonDiagnosticLintPosition {
+ line: loc.line_index + 1,
+ col: loc.column_index,
+ byte_pos: byte_index,
+ }
+ }
+}
+
+// WARNING: Ensure doesn't change because it's used in the JSON output
+#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
+struct JsonLintDiagnosticRange {
+ pub start: JsonDiagnosticLintPosition,
+ pub end: JsonDiagnosticLintPosition,
+}
+
+// WARNING: Ensure doesn't change because it's used in the JSON output
+#[derive(Clone, Serialize)]
+struct JsonLintDiagnostic {
+ pub filename: String,
+ pub range: JsonLintDiagnosticRange,
+ pub message: String,
+ pub code: String,
+ pub hint: Option<String>,
+}
+
#[derive(Serialize)]
struct JsonLintReporter {
- diagnostics: Vec<LintDiagnostic>,
+ diagnostics: Vec<JsonLintDiagnostic>,
errors: Vec<LintError>,
}
@@ -513,7 +480,22 @@ impl JsonLintReporter {
impl LintReporter for JsonLintReporter {
fn visit_diagnostic(&mut self, d: &LintDiagnostic, _source: &ParsedSource) {
- self.diagnostics.push(d.clone());
+ self.diagnostics.push(JsonLintDiagnostic {
+ filename: d.specifier.to_string(),
+ range: JsonLintDiagnosticRange {
+ start: JsonDiagnosticLintPosition::new(
+ d.range.start.as_byte_index(d.text_info.range().start),
+ d.text_info.line_and_column_index(d.range.start),
+ ),
+ end: JsonDiagnosticLintPosition::new(
+ d.range.end.as_byte_index(d.text_info.range().start),
+ d.text_info.line_and_column_index(d.range.end),
+ ),
+ },
+ message: d.message.clone(),
+ code: d.code.clone(),
+ hint: d.hint.clone(),
+ });
}
fn visit_error(&mut self, file_path: &str, err: &AnyError) {
@@ -530,19 +512,16 @@ impl LintReporter for JsonLintReporter {
}
}
-fn sort_diagnostics(diagnostics: &mut [LintDiagnostic]) {
+fn sort_diagnostics(diagnostics: &mut [JsonLintDiagnostic]) {
// Sort so that we guarantee a deterministic output which is useful for tests
diagnostics.sort_by(|a, b| {
use std::cmp::Ordering;
let file_order = a.filename.cmp(&b.filename);
match file_order {
Ordering::Equal => {
- let line_order =
- a.range.start.line_index.cmp(&b.range.start.line_index);
+ let line_order = a.range.start.line.cmp(&b.range.start.line);
match line_order {
- Ordering::Equal => {
- a.range.start.column_index.cmp(&b.range.start.column_index)
- }
+ Ordering::Equal => a.range.start.col.cmp(&b.range.start.col),
_ => line_order,
}
}
diff --git a/cli/tools/registry/diagnostics.rs b/cli/tools/registry/diagnostics.rs
index e7f947303..aeb5d61e2 100644
--- a/cli/tools/registry/diagnostics.rs
+++ b/cli/tools/registry/diagnostics.rs
@@ -1,28 +1,25 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::borrow::Cow;
-use std::fmt::Display;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::Mutex;
+use deno_ast::diagnostics::Diagnostic;
+use deno_ast::diagnostics::DiagnosticLevel;
+use deno_ast::diagnostics::DiagnosticLocation;
+use deno_ast::diagnostics::DiagnosticSnippet;
+use deno_ast::diagnostics::DiagnosticSnippetHighlight;
+use deno_ast::diagnostics::DiagnosticSnippetHighlightStyle;
+use deno_ast::diagnostics::DiagnosticSourcePos;
+use deno_ast::diagnostics::DiagnosticSourceRange;
use deno_ast::swc::common::util::take::Take;
+use deno_ast::SourceTextInfo;
use deno_core::anyhow::anyhow;
use deno_core::error::AnyError;
use deno_graph::FastCheckDiagnostic;
use lsp_types::Url;
-use crate::cache::LazyGraphSourceParser;
-use crate::diagnostics::Diagnostic;
-use crate::diagnostics::DiagnosticLevel;
-use crate::diagnostics::DiagnosticLocation;
-use crate::diagnostics::DiagnosticSnippet;
-use crate::diagnostics::DiagnosticSnippetHighlight;
-use crate::diagnostics::DiagnosticSnippetHighlightStyle;
-use crate::diagnostics::DiagnosticSnippetSource;
-use crate::diagnostics::DiagnosticSourcePos;
-use crate::diagnostics::DiagnosticSourceRange;
-use crate::diagnostics::SourceTextParsedSourceStore;
use crate::util::import_map::ImportMapUnfurlDiagnostic;
#[derive(Clone, Default)]
@@ -31,16 +28,12 @@ pub struct PublishDiagnosticsCollector {
}
impl PublishDiagnosticsCollector {
- pub fn print_and_error(
- &self,
- sources: LazyGraphSourceParser,
- ) -> Result<(), AnyError> {
+ pub fn print_and_error(&self) -> Result<(), AnyError> {
let mut errors = 0;
let mut has_zap_errors = false;
let diagnostics = self.diagnostics.lock().unwrap().take();
- let sources = SourceTextParsedSourceStore(sources);
for diagnostic in diagnostics {
- eprint!("{}", diagnostic.display(&sources));
+ eprint!("{}", diagnostic.display());
if matches!(diagnostic.level(), DiagnosticLevel::Error) {
errors += 1;
}
@@ -90,6 +83,7 @@ pub enum PublishDiagnostic {
InvalidExternalImport {
kind: String,
imported: Url,
+ text_info: SourceTextInfo,
referrer: deno_graph::Range,
},
}
@@ -110,22 +104,22 @@ impl Diagnostic for PublishDiagnostic {
}
}
- fn code(&self) -> impl Display + '_ {
+ fn code(&self) -> Cow<'_, str> {
use PublishDiagnostic::*;
match &self {
FastCheck(diagnostic) => diagnostic.code(),
- ImportMapUnfurl(diagnostic) => diagnostic.code(),
- InvalidPath { .. } => "invalid-path",
- DuplicatePath { .. } => "case-insensitive-duplicate-path",
- UnsupportedFileType { .. } => "unsupported-file-type",
- InvalidExternalImport { .. } => "invalid-external-import",
+ ImportMapUnfurl(diagnostic) => Cow::Borrowed(diagnostic.code()),
+ InvalidPath { .. } => Cow::Borrowed("invalid-path"),
+ DuplicatePath { .. } => Cow::Borrowed("case-insensitive-duplicate-path"),
+ UnsupportedFileType { .. } => Cow::Borrowed("unsupported-file-type"),
+ InvalidExternalImport { .. } => Cow::Borrowed("invalid-external-import"),
}
}
- fn message(&self) -> impl Display + '_ {
+ fn message(&self) -> Cow<'_, str> {
use PublishDiagnostic::*;
match &self {
- FastCheck(diagnostic) => Cow::Owned(diagnostic.to_string()) ,
+ FastCheck(diagnostic) => diagnostic.message(),
ImportMapUnfurl(diagnostic) => Cow::Borrowed(diagnostic.message()),
InvalidPath { message, .. } => Cow::Borrowed(message.as_str()),
DuplicatePath { .. } => {
@@ -141,21 +135,15 @@ impl Diagnostic for PublishDiagnostic {
fn location(&self) -> DiagnosticLocation {
use PublishDiagnostic::*;
match &self {
- FastCheck(diagnostic) => match diagnostic.range() {
- Some(range) => DiagnosticLocation::ModulePosition {
- specifier: Cow::Borrowed(diagnostic.specifier()),
- source_pos: DiagnosticSourcePos::SourcePos(range.range.start),
- },
- None => DiagnosticLocation::Module {
- specifier: Cow::Borrowed(diagnostic.specifier()),
- },
- },
+ FastCheck(diagnostic) => diagnostic.location(),
ImportMapUnfurl(diagnostic) => match diagnostic {
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport {
specifier,
+ text_info,
range,
} => DiagnosticLocation::ModulePosition {
specifier: Cow::Borrowed(specifier),
+ text_info: Cow::Borrowed(text_info),
source_pos: DiagnosticSourcePos::SourcePos(range.start),
},
},
@@ -168,41 +156,31 @@ impl Diagnostic for PublishDiagnostic {
UnsupportedFileType { specifier, .. } => DiagnosticLocation::Module {
specifier: Cow::Borrowed(specifier),
},
- InvalidExternalImport { referrer, .. } => {
- DiagnosticLocation::ModulePosition {
- specifier: Cow::Borrowed(&referrer.specifier),
- source_pos: DiagnosticSourcePos::LineAndCol {
- line: referrer.start.line,
- column: referrer.start.character,
- },
- }
- }
+ InvalidExternalImport {
+ referrer,
+ text_info,
+ ..
+ } => DiagnosticLocation::ModulePosition {
+ specifier: Cow::Borrowed(&referrer.specifier),
+ text_info: Cow::Borrowed(text_info),
+ source_pos: DiagnosticSourcePos::LineAndCol {
+ line: referrer.start.line,
+ column: referrer.start.character,
+ },
+ },
}
}
fn snippet(&self) -> Option<DiagnosticSnippet<'_>> {
match &self {
- PublishDiagnostic::FastCheck(diagnostic) => {
- diagnostic.range().map(|range| DiagnosticSnippet {
- source: DiagnosticSnippetSource::Specifier(Cow::Borrowed(
- diagnostic.specifier(),
- )),
- highlight: DiagnosticSnippetHighlight {
- style: DiagnosticSnippetHighlightStyle::Error,
- range: DiagnosticSourceRange {
- start: DiagnosticSourcePos::SourcePos(range.range.start),
- end: DiagnosticSourcePos::SourcePos(range.range.end),
- },
- description: diagnostic.range_description().map(Cow::Borrowed),
- },
- })
- }
+ PublishDiagnostic::FastCheck(diagnostic) => diagnostic.snippet(),
PublishDiagnostic::ImportMapUnfurl(diagnostic) => match diagnostic {
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport {
- specifier,
+ text_info,
range,
+ ..
} => Some(DiagnosticSnippet {
- source: DiagnosticSnippetSource::Specifier(Cow::Borrowed(specifier)),
+ source: Cow::Borrowed(text_info),
highlight: DiagnosticSnippetHighlight {
style: DiagnosticSnippetHighlightStyle::Warning,
range: DiagnosticSourceRange {
@@ -216,44 +194,44 @@ impl Diagnostic for PublishDiagnostic {
PublishDiagnostic::InvalidPath { .. } => None,
PublishDiagnostic::DuplicatePath { .. } => None,
PublishDiagnostic::UnsupportedFileType { .. } => None,
- PublishDiagnostic::InvalidExternalImport { referrer, .. } => {
- Some(DiagnosticSnippet {
- source: DiagnosticSnippetSource::Specifier(Cow::Borrowed(
- &referrer.specifier,
- )),
- highlight: DiagnosticSnippetHighlight {
- style: DiagnosticSnippetHighlightStyle::Error,
- range: DiagnosticSourceRange {
- start: DiagnosticSourcePos::LineAndCol {
- line: referrer.start.line,
- column: referrer.start.character,
- },
- end: DiagnosticSourcePos::LineAndCol {
- line: referrer.end.line,
- column: referrer.end.character,
- },
+ PublishDiagnostic::InvalidExternalImport {
+ referrer,
+ text_info,
+ ..
+ } => Some(DiagnosticSnippet {
+ source: Cow::Borrowed(text_info),
+ highlight: DiagnosticSnippetHighlight {
+ style: DiagnosticSnippetHighlightStyle::Error,
+ range: DiagnosticSourceRange {
+ start: DiagnosticSourcePos::LineAndCol {
+ line: referrer.start.line,
+ column: referrer.start.character,
+ },
+ end: DiagnosticSourcePos::LineAndCol {
+ line: referrer.end.line,
+ column: referrer.end.character,
},
- description: Some("the specifier".into()),
},
- })
- }
+ description: Some("the specifier".into()),
+ },
+ }),
}
}
- fn hint(&self) -> Option<impl Display + '_> {
+ fn hint(&self) -> Option<Cow<'_, str>> {
match &self {
- PublishDiagnostic::FastCheck(diagnostic) => Some(diagnostic.fix_hint()),
+ PublishDiagnostic::FastCheck(diagnostic) => diagnostic.hint(),
PublishDiagnostic::ImportMapUnfurl(_) => None,
PublishDiagnostic::InvalidPath { .. } => Some(
- "rename or remove the file, or add it to 'publish.exclude' in the config file",
+ Cow::Borrowed("rename or remove the file, or add it to 'publish.exclude' in the config file"),
),
PublishDiagnostic::DuplicatePath { .. } => Some(
- "rename or remove the file",
+ Cow::Borrowed("rename or remove the file"),
),
PublishDiagnostic::UnsupportedFileType { .. } => Some(
- "remove the file, or add it to 'publish.exclude' in the config file",
+ Cow::Borrowed("remove the file, or add it to 'publish.exclude' in the config file"),
),
- PublishDiagnostic::InvalidExternalImport { .. } => Some("replace this import with one from jsr or npm, or vendor the dependency into your package")
+ PublishDiagnostic::InvalidExternalImport { .. } => Some(Cow::Borrowed("replace this import with one from jsr or npm, or vendor the dependency into your package"))
}
}
@@ -264,12 +242,7 @@ impl Diagnostic for PublishDiagnostic {
fn info(&self) -> Cow<'_, [Cow<'_, str>]> {
match &self {
PublishDiagnostic::FastCheck(diagnostic) => {
- let infos = diagnostic
- .additional_info()
- .iter()
- .map(|s| Cow::Borrowed(*s))
- .collect();
- Cow::Owned(infos)
+ diagnostic.info()
}
PublishDiagnostic::ImportMapUnfurl(diagnostic) => match diagnostic {
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. } => Cow::Borrowed(&[
@@ -296,25 +269,23 @@ impl Diagnostic for PublishDiagnostic {
}
}
- fn docs_url(&self) -> Option<impl Display + '_> {
+ fn docs_url(&self) -> Option<Cow<'_, str>> {
match &self {
- PublishDiagnostic::FastCheck(diagnostic) => {
- Some(format!("https://jsr.io/go/{}", diagnostic.code()))
- }
+ PublishDiagnostic::FastCheck(diagnostic) => diagnostic.docs_url(),
PublishDiagnostic::ImportMapUnfurl(diagnostic) => match diagnostic {
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. } => None,
},
PublishDiagnostic::InvalidPath { .. } => {
- Some("https://jsr.io/go/invalid-path".to_owned())
- }
- PublishDiagnostic::DuplicatePath { .. } => {
- Some("https://jsr.io/go/case-insensitive-duplicate-path".to_owned())
+ Some(Cow::Borrowed("https://jsr.io/go/invalid-path"))
}
+ PublishDiagnostic::DuplicatePath { .. } => Some(Cow::Borrowed(
+ "https://jsr.io/go/case-insensitive-duplicate-path",
+ )),
PublishDiagnostic::UnsupportedFileType { .. } => {
- Some("https://jsr.io/go/unsupported-file-type".to_owned())
+ Some(Cow::Borrowed("https://jsr.io/go/unsupported-file-type"))
}
PublishDiagnostic::InvalidExternalImport { .. } => {
- Some("https://jsr.io/go/invalid-external-import".to_owned())
+ Some(Cow::Borrowed("https://jsr.io/go/invalid-external-import"))
}
}
}
diff --git a/cli/tools/registry/graph.rs b/cli/tools/registry/graph.rs
index d9fb665c4..3445d55e7 100644
--- a/cli/tools/registry/graph.rs
+++ b/cli/tools/registry/graph.rs
@@ -2,8 +2,10 @@
use std::collections::HashSet;
use std::collections::VecDeque;
+use std::sync::Arc;
use deno_ast::ModuleSpecifier;
+use deno_ast::SourceTextInfo;
use deno_config::ConfigFile;
use deno_config::WorkspaceConfig;
use deno_core::anyhow::bail;
@@ -76,7 +78,9 @@ pub fn collect_invalid_external_imports(
let mut skip_specifiers: HashSet<Url> = HashSet::new();
let mut collect_if_invalid =
- |skip_specifiers: &mut HashSet<Url>, resolution: &ResolutionResolved| {
+ |skip_specifiers: &mut HashSet<Url>,
+ text: &Arc<str>,
+ resolution: &ResolutionResolved| {
if visited.insert(resolution.specifier.clone()) {
match resolution.specifier.scheme() {
"file" | "data" | "node" => {}
@@ -88,6 +92,7 @@ pub fn collect_invalid_external_imports(
diagnostics_collector.push(
PublishDiagnostic::InvalidExternalImport {
kind: format!("non-JSR '{}'", resolution.specifier.scheme()),
+ text_info: SourceTextInfo::new(text.clone()),
imported: resolution.specifier.clone(),
referrer: resolution.range.clone(),
},
@@ -98,6 +103,7 @@ pub fn collect_invalid_external_imports(
diagnostics_collector.push(
PublishDiagnostic::InvalidExternalImport {
kind: format!("'{}'", resolution.specifier.scheme()),
+ text_info: SourceTextInfo::new(text.clone()),
imported: resolution.specifier.clone(),
referrer: resolution.range.clone(),
},
@@ -128,10 +134,10 @@ pub fn collect_invalid_external_imports(
for (_, dep) in &module.dependencies {
if let Some(resolved) = dep.maybe_code.ok() {
- collect_if_invalid(&mut skip_specifiers, resolved);
+ collect_if_invalid(&mut skip_specifiers, &module.source, resolved);
}
if let Some(resolved) = dep.maybe_type.ok() {
- collect_if_invalid(&mut skip_specifiers, resolved);
+ collect_if_invalid(&mut skip_specifiers, &module.source, resolved);
}
}
}
@@ -144,7 +150,7 @@ pub fn collect_fast_check_type_graph_diagnostics(
packages: &[MemberRoots],
diagnostics_collector: &PublishDiagnosticsCollector,
) -> bool {
- let mut seen_diagnostics = HashSet::new();
+ let mut had_diagnostic = false;
let mut seen_modules = HashSet::with_capacity(graph.specifiers_count());
for package in packages {
let mut pending = VecDeque::new();
@@ -161,12 +167,9 @@ pub fn collect_fast_check_type_graph_diagnostics(
let Some(es_module) = module.js() else {
continue;
};
- if let Some(diagnostic) = es_module.fast_check_diagnostic() {
- for diagnostic in diagnostic.flatten_multiple() {
- if !seen_diagnostics.insert(diagnostic.message_with_range_for_test())
- {
- continue;
- }
+ if let Some(diagnostics) = es_module.fast_check_diagnostics() {
+ for diagnostic in diagnostics {
+ had_diagnostic = true;
diagnostics_collector
.push(PublishDiagnostic::FastCheck(diagnostic.clone()));
if matches!(
@@ -197,5 +200,5 @@ pub fn collect_fast_check_type_graph_diagnostics(
}
}
- !seen_diagnostics.is_empty()
+ had_diagnostic
}
diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs
index 5f03fa6fd..cfdec04c5 100644
--- a/cli/tools/registry/mod.rs
+++ b/cli/tools/registry/mod.rs
@@ -643,7 +643,6 @@ async fn publish_package(
struct PreparePackagesData {
publish_order_graph: PublishOrderGraph,
- graph: Arc<deno_graph::ModuleGraph>,
package_by_name: HashMap<String, Rc<PreparedPublishPackage>>,
}
@@ -678,7 +677,7 @@ async fn prepare_packages_for_publishing(
let package = prepare_publish(
&deno_json,
source_cache.clone(),
- graph.clone(),
+ graph,
import_map,
diagnostics_collector,
)
@@ -689,7 +688,6 @@ async fn prepare_packages_for_publishing(
let package_by_name = HashMap::from([(package_name, package)]);
return Ok(PreparePackagesData {
publish_order_graph,
- graph,
package_by_name,
});
};
@@ -743,7 +741,6 @@ async fn prepare_packages_for_publishing(
}
Ok(PreparePackagesData {
publish_order_graph,
- graph,
package_by_name,
})
}
@@ -849,11 +846,7 @@ pub async fn publish(
)
.await?;
- let source_parser = LazyGraphSourceParser::new(
- cli_factory.parsed_source_cache(),
- &prepared_data.graph,
- );
- diagnostics_collector.print_and_error(source_parser)?;
+ diagnostics_collector.print_and_error()?;
if prepared_data.package_by_name.is_empty() {
bail!("No packages to publish");
diff --git a/cli/tools/registry/tar.rs b/cli/tools/registry/tar.rs
index e63a76516..6543fbf2e 100644
--- a/cli/tools/registry/tar.rs
+++ b/cli/tools/registry/tar.rs
@@ -206,7 +206,7 @@ fn resolve_content_maybe_unfurling(
let text = String::from_utf8(data)?;
deno_ast::parse_module(deno_ast::ParseParams {
- specifier: specifier.to_string(),
+ specifier: specifier.clone(),
text_info: deno_ast::SourceTextInfo::from_string(text),
media_type,
capture_tokens: false,
diff --git a/cli/tools/repl/session.rs b/cli/tools/repl/session.rs
index e98f4b430..a52eb095f 100644
--- a/cli/tools/repl/session.rs
+++ b/cli/tools/repl/session.rs
@@ -16,14 +16,15 @@ use crate::tools::test::worker_has_tests;
use crate::tools::test::TestEvent;
use crate::tools::test::TestEventSender;
+use deno_ast::diagnostics::Diagnostic;
use deno_ast::swc::ast as swc_ast;
use deno_ast::swc::common::comments::CommentKind;
use deno_ast::swc::visit::noop_visit_type;
use deno_ast::swc::visit::Visit;
use deno_ast::swc::visit::VisitWith;
-use deno_ast::DiagnosticsError;
use deno_ast::ImportsNotUsedAsValues;
use deno_ast::ModuleSpecifier;
+use deno_ast::ParseDiagnosticsError;
use deno_ast::ParsedSource;
use deno_ast::SourcePos;
use deno_ast::SourceRangedForSpanned;
@@ -324,7 +325,7 @@ impl ReplSession {
&mut self,
line: &str,
) -> EvaluationOutput {
- fn format_diagnostic(diagnostic: &deno_ast::Diagnostic) -> String {
+ fn format_diagnostic(diagnostic: &deno_ast::ParseDiagnostic) -> String {
let display_position = diagnostic.display_position();
format!(
"{}: {} at {}:{}",
@@ -377,11 +378,11 @@ impl ReplSession {
}
Err(err) => {
// handle a parsing diagnostic
- match err.downcast_ref::<deno_ast::Diagnostic>() {
+ match err.downcast_ref::<deno_ast::ParseDiagnostic>() {
Some(diagnostic) => {
Ok(EvaluationOutput::Error(format_diagnostic(diagnostic)))
}
- None => match err.downcast_ref::<DiagnosticsError>() {
+ None => match err.downcast_ref::<ParseDiagnosticsError>() {
Some(diagnostics) => Ok(EvaluationOutput::Error(
diagnostics
.0
@@ -786,13 +787,13 @@ fn parse_source_as(
media_type: deno_ast::MediaType,
) -> Result<deno_ast::ParsedSource, AnyError> {
let specifier = if media_type == deno_ast::MediaType::Tsx {
- "repl.tsx"
+ ModuleSpecifier::parse("file:///repl.tsx").unwrap()
} else {
- "repl.ts"
+ ModuleSpecifier::parse("file:///repl.ts").unwrap()
};
let parsed = deno_ast::parse_module(deno_ast::ParseParams {
- specifier: specifier.to_string(),
+ specifier,
text_info: deno_ast::SourceTextInfo::from_string(source),
media_type,
capture_tokens: true,
diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs
index 332bfa8c8..c138abec2 100644
--- a/cli/tools/test/mod.rs
+++ b/cli/tools/test/mod.rs
@@ -752,7 +752,7 @@ fn extract_files_from_source_comments(
media_type: MediaType,
) -> Result<Vec<File>, AnyError> {
let parsed_source = deno_ast::parse_module(deno_ast::ParseParams {
- specifier: specifier.to_string(),
+ specifier: specifier.clone(),
text_info: deno_ast::SourceTextInfo::new(source),
media_type,
capture_tokens: false,
diff --git a/cli/tools/test/reporters/pretty.rs b/cli/tools/test/reporters/pretty.rs
index c49081dd6..4e8a1f402 100644
--- a/cli/tools/test/reporters/pretty.rs
+++ b/cli/tools/test/reporters/pretty.rs
@@ -141,7 +141,7 @@ impl PrettyTestReporter {
.child_results_buffer
.entry(description.parent_id)
.or_default()
- .remove(&description.id);
+ .shift_remove(&description.id);
}
fn write_output_end(&mut self) {
diff --git a/cli/tools/vendor/analyze.rs b/cli/tools/vendor/analyze.rs
index c804fa1ce..2b00f6bf4 100644
--- a/cli/tools/vendor/analyze.rs
+++ b/cli/tools/vendor/analyze.rs
@@ -61,6 +61,7 @@ fn export_specifier_has_default(s: &ExportSpecifier) -> bool {
#[cfg(test)]
mod test {
use deno_ast::MediaType;
+ use deno_ast::ModuleSpecifier;
use deno_ast::ParseParams;
use deno_ast::ParsedSource;
use deno_ast::SourceTextInfo;
@@ -101,7 +102,7 @@ mod test {
fn parse_module(text: &str) -> ParsedSource {
deno_ast::parse_module(ParseParams {
- specifier: "file:///mod.ts".to_string(),
+ specifier: ModuleSpecifier::parse("file:///mod.ts").unwrap(),
capture_tokens: false,
maybe_syntax: None,
media_type: MediaType::TypeScript,
diff --git a/cli/util/fs.rs b/cli/util/fs.rs
index bab36b31e..c81686f95 100644
--- a/cli/util/fs.rs
+++ b/cli/util/fs.rs
@@ -361,8 +361,7 @@ pub fn collect_specifiers(
if path.is_dir() {
result.push(PathOrPattern::Path(path));
} else if !files.exclude.matches_path(&path) {
- let url = ModuleSpecifier::from_file_path(&path)
- .map_err(|_| anyhow!("Invalid file path '{}'", path.display()))?;
+ let url = specifier_from_file_path(&path)?;
prepared.push(url);
}
}
@@ -385,7 +384,7 @@ pub fn collect_specifiers(
.collect_file_patterns(files)?;
let mut collected_files_as_urls = collected_files
.iter()
- .map(|f| ModuleSpecifier::from_file_path(f).unwrap())
+ .map(|f| specifier_from_file_path(f).unwrap())
.collect::<Vec<ModuleSpecifier>>();
collected_files_as_urls.sort();
@@ -703,6 +702,13 @@ impl LaxSingleProcessFsFlag {
}
}
+pub fn specifier_from_file_path(
+ path: &Path,
+) -> Result<ModuleSpecifier, AnyError> {
+ ModuleSpecifier::from_file_path(path)
+ .map_err(|_| anyhow!("Invalid file path '{}'", path.display()))
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/cli/util/import_map.rs b/cli/util/import_map.rs
index 2656389b8..b8b8b9a1a 100644
--- a/cli/util/import_map.rs
+++ b/cli/util/import_map.rs
@@ -4,6 +4,7 @@ use std::collections::HashSet;
use deno_ast::ParsedSource;
use deno_ast::SourceRange;
+use deno_ast::SourceTextInfo;
use deno_core::serde_json;
use deno_core::ModuleSpecifier;
use deno_graph::DefaultModuleAnalyzer;
@@ -72,6 +73,7 @@ fn values_to_set<'a>(
pub enum ImportMapUnfurlDiagnostic {
UnanalyzableDynamicImport {
specifier: ModuleSpecifier,
+ text_info: SourceTextInfo,
range: SourceRange,
},
}
@@ -150,6 +152,7 @@ impl<'a> ImportMapUnfurler<'a> {
ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport {
specifier: url.to_owned(),
range: SourceRange::new(start_pos, end_pos),
+ text_info: parsed_source.text_info().clone(),
},
);
}
@@ -295,7 +298,7 @@ mod tests {
fn parse_ast(specifier: &Url, source_code: &str) -> ParsedSource {
let media_type = MediaType::from_specifier(specifier);
deno_ast::parse_module(deno_ast::ParseParams {
- specifier: specifier.to_string(),
+ specifier: specifier.clone(),
media_type,
capture_tokens: false,
maybe_syntax: None,