2024-01-23 10:37:43 -05:00
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std ::borrow ::Cow ;
2024-01-24 16:24:52 -05:00
use std ::path ::PathBuf ;
2024-01-23 10:37:43 -05:00
use std ::sync ::Arc ;
use std ::sync ::Mutex ;
2024-02-08 20:40:26 -05:00
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 ;
2024-01-23 10:37:43 -05:00
use deno_ast ::swc ::common ::util ::take ::Take ;
2024-02-29 06:54:57 -05:00
use deno_ast ::SourcePos ;
use deno_ast ::SourceRanged ;
2024-02-08 20:40:26 -05:00
use deno_ast ::SourceTextInfo ;
2024-01-23 10:37:43 -05:00
use deno_core ::anyhow ::anyhow ;
use deno_core ::error ::AnyError ;
use deno_graph ::FastCheckDiagnostic ;
2024-01-24 16:24:52 -05:00
use lsp_types ::Url ;
2024-01-23 10:37:43 -05:00
2024-02-27 10:13:16 -05:00
use super ::unfurl ::SpecifierUnfurlerDiagnostic ;
2024-01-23 10:37:43 -05:00
#[ derive(Clone, Default) ]
pub struct PublishDiagnosticsCollector {
diagnostics : Arc < Mutex < Vec < PublishDiagnostic > > > ,
}
impl PublishDiagnosticsCollector {
2024-02-08 20:40:26 -05:00
pub fn print_and_error ( & self ) -> Result < ( ) , AnyError > {
2024-01-23 10:37:43 -05:00
let mut errors = 0 ;
2024-02-19 10:28:41 -05:00
let mut has_slow_types_errors = false ;
2024-02-29 06:54:57 -05:00
let mut diagnostics = self . diagnostics . lock ( ) . unwrap ( ) . take ( ) ;
diagnostics . sort_by_cached_key ( | d | d . sorting_key ( ) ) ;
2024-01-23 10:37:43 -05:00
for diagnostic in diagnostics {
2024-02-08 20:40:26 -05:00
eprint! ( " {} " , diagnostic . display ( ) ) ;
2024-01-23 10:37:43 -05:00
if matches! ( diagnostic . level ( ) , DiagnosticLevel ::Error ) {
errors + = 1 ;
}
2024-01-31 21:16:52 -05:00
if matches! ( diagnostic , PublishDiagnostic ::FastCheck ( .. ) ) {
2024-02-19 10:28:41 -05:00
has_slow_types_errors = true ;
2024-01-31 21:16:52 -05:00
}
2024-01-23 10:37:43 -05:00
}
if errors > 0 {
2024-02-19 10:28:41 -05:00
if has_slow_types_errors {
2024-01-31 21:16:52 -05:00
eprintln! (
2024-02-19 10:28:41 -05:00
" This package contains errors for slow types. Fixing these errors will: \n "
2024-01-31 21:16:52 -05:00
) ;
2024-02-19 10:28:41 -05:00
eprintln! (
" 1. Significantly improve your package users' type checking performance. "
) ;
eprintln! ( " 2. Improve the automatic documentation generation. " ) ;
eprintln! ( " 3. Enable automatic .d.ts generation for Node.js. " ) ;
eprintln! (
" \n Don't want to bother? You can choose to skip this step by "
) ;
eprintln! ( " providing the --allow-slow-types flag. \n " ) ;
2024-01-31 21:16:52 -05:00
}
2024-01-23 10:37:43 -05:00
Err ( anyhow! (
" Found {} problem{} " ,
errors ,
if errors = = 1 { " " } else { " s " }
) )
} else {
Ok ( ( ) )
}
}
pub fn push ( & self , diagnostic : PublishDiagnostic ) {
self . diagnostics . lock ( ) . unwrap ( ) . push ( diagnostic ) ;
}
}
pub enum PublishDiagnostic {
2024-01-24 08:49:33 -05:00
FastCheck ( FastCheckDiagnostic ) ,
2024-02-27 10:13:16 -05:00
SpecifierUnfurl ( SpecifierUnfurlerDiagnostic ) ,
2024-01-24 16:59:18 -05:00
InvalidPath {
path : PathBuf ,
message : String ,
} ,
DuplicatePath {
path : PathBuf ,
} ,
UnsupportedFileType {
specifier : Url ,
kind : String ,
} ,
InvalidExternalImport {
kind : String ,
imported : Url ,
2024-02-08 20:40:26 -05:00
text_info : SourceTextInfo ,
2024-01-24 16:59:18 -05:00
referrer : deno_graph ::Range ,
} ,
2024-02-29 06:54:57 -05:00
UnsupportedJsxTsx {
specifier : Url ,
} ,
}
impl PublishDiagnostic {
fn sorting_key ( & self ) -> ( String , String , Option < SourcePos > ) {
let loc = self . location ( ) ;
let ( specifier , source_pos ) = match loc {
DiagnosticLocation ::Module { specifier } = > ( specifier . to_string ( ) , None ) ,
DiagnosticLocation ::Path { path } = > ( path . display ( ) . to_string ( ) , None ) ,
DiagnosticLocation ::ModulePosition {
specifier ,
source_pos ,
text_info ,
} = > (
specifier . to_string ( ) ,
Some ( match source_pos {
DiagnosticSourcePos ::SourcePos ( s ) = > s ,
DiagnosticSourcePos ::ByteIndex ( index ) = > {
text_info . range ( ) . start ( ) + index
}
DiagnosticSourcePos ::LineAndCol { line , column } = > {
text_info . line_start ( line ) + column
}
} ) ,
) ,
} ;
( self . code ( ) . to_string ( ) , specifier , source_pos )
}
2024-01-23 10:37:43 -05:00
}
impl Diagnostic for PublishDiagnostic {
fn level ( & self ) -> DiagnosticLevel {
2024-01-24 16:59:18 -05:00
use PublishDiagnostic ::* ;
2024-01-23 10:37:43 -05:00
match self {
2024-01-24 16:59:18 -05:00
FastCheck ( FastCheckDiagnostic ::UnsupportedJavaScriptEntrypoint {
..
} ) = > DiagnosticLevel ::Warning ,
FastCheck ( _ ) = > DiagnosticLevel ::Error ,
2024-02-27 10:13:16 -05:00
SpecifierUnfurl ( _ ) = > DiagnosticLevel ::Warning ,
2024-01-24 16:59:18 -05:00
InvalidPath { .. } = > DiagnosticLevel ::Error ,
DuplicatePath { .. } = > DiagnosticLevel ::Error ,
UnsupportedFileType { .. } = > DiagnosticLevel ::Warning ,
InvalidExternalImport { .. } = > DiagnosticLevel ::Error ,
2024-02-29 06:54:57 -05:00
UnsupportedJsxTsx { .. } = > DiagnosticLevel ::Warning ,
2024-01-23 10:37:43 -05:00
}
}
2024-02-08 20:40:26 -05:00
fn code ( & self ) -> Cow < '_ , str > {
2024-01-24 16:59:18 -05:00
use PublishDiagnostic ::* ;
2024-01-23 10:37:43 -05:00
match & self {
2024-01-24 16:59:18 -05:00
FastCheck ( diagnostic ) = > diagnostic . code ( ) ,
2024-02-27 10:13:16 -05:00
SpecifierUnfurl ( diagnostic ) = > Cow ::Borrowed ( diagnostic . code ( ) ) ,
2024-02-08 20:40:26 -05:00
InvalidPath { .. } = > Cow ::Borrowed ( " invalid-path " ) ,
DuplicatePath { .. } = > Cow ::Borrowed ( " case-insensitive-duplicate-path " ) ,
UnsupportedFileType { .. } = > Cow ::Borrowed ( " unsupported-file-type " ) ,
InvalidExternalImport { .. } = > Cow ::Borrowed ( " invalid-external-import " ) ,
2024-02-29 06:54:57 -05:00
UnsupportedJsxTsx { .. } = > Cow ::Borrowed ( " unsupported-jsx-tsx " ) ,
2024-01-23 10:37:43 -05:00
}
}
2024-02-08 20:40:26 -05:00
fn message ( & self ) -> Cow < '_ , str > {
2024-01-24 16:59:18 -05:00
use PublishDiagnostic ::* ;
2024-01-23 10:37:43 -05:00
match & self {
2024-02-08 20:40:26 -05:00
FastCheck ( diagnostic ) = > diagnostic . message ( ) ,
2024-02-27 10:13:16 -05:00
SpecifierUnfurl ( diagnostic ) = > Cow ::Borrowed ( diagnostic . message ( ) ) ,
2024-01-24 16:59:18 -05:00
InvalidPath { message , .. } = > Cow ::Borrowed ( message . as_str ( ) ) ,
DuplicatePath { .. } = > {
2024-01-24 16:24:52 -05:00
Cow ::Borrowed ( " package path is a case insensitive duplicate of another path in the package " )
}
2024-01-24 16:59:18 -05:00
UnsupportedFileType { kind , .. } = > {
Cow ::Owned ( format! ( " unsupported file type ' {kind} ' " ) )
2024-01-24 16:24:52 -05:00
}
2024-01-24 16:59:18 -05:00
InvalidExternalImport { kind , .. } = > Cow ::Owned ( format! ( " invalid import to a {kind} specifier " ) ) ,
2024-02-29 06:54:57 -05:00
UnsupportedJsxTsx { .. } = > Cow ::Borrowed ( " JSX and TSX files are currently not supported " ) ,
2024-01-23 10:37:43 -05:00
}
}
fn location ( & self ) -> DiagnosticLocation {
2024-01-24 16:59:18 -05:00
use PublishDiagnostic ::* ;
2024-01-23 10:37:43 -05:00
match & self {
2024-02-08 20:40:26 -05:00
FastCheck ( diagnostic ) = > diagnostic . location ( ) ,
2024-02-27 10:13:16 -05:00
SpecifierUnfurl ( diagnostic ) = > match diagnostic {
SpecifierUnfurlerDiagnostic ::UnanalyzableDynamicImport {
2024-01-24 08:49:33 -05:00
specifier ,
2024-02-08 20:40:26 -05:00
text_info ,
2024-01-24 08:49:33 -05:00
range ,
2024-01-24 16:24:52 -05:00
} = > DiagnosticLocation ::ModulePosition {
2024-01-24 08:49:33 -05:00
specifier : Cow ::Borrowed ( specifier ) ,
2024-02-08 20:40:26 -05:00
text_info : Cow ::Borrowed ( text_info ) ,
2024-01-24 08:49:33 -05:00
source_pos : DiagnosticSourcePos ::SourcePos ( range . start ) ,
} ,
} ,
2024-01-24 16:59:18 -05:00
InvalidPath { path , .. } = > {
2024-01-24 16:24:52 -05:00
DiagnosticLocation ::Path { path : path . clone ( ) }
}
2024-01-24 16:59:18 -05:00
DuplicatePath { path , .. } = > {
2024-01-24 16:24:52 -05:00
DiagnosticLocation ::Path { path : path . clone ( ) }
}
2024-01-24 16:59:18 -05:00
UnsupportedFileType { specifier , .. } = > DiagnosticLocation ::Module {
specifier : Cow ::Borrowed ( specifier ) ,
} ,
2024-02-08 20:40:26 -05:00
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 ,
} ,
} ,
2024-02-29 06:54:57 -05:00
UnsupportedJsxTsx { specifier } = > DiagnosticLocation ::Module {
specifier : Cow ::Borrowed ( specifier ) ,
} ,
2024-01-23 10:37:43 -05:00
}
}
fn snippet ( & self ) -> Option < DiagnosticSnippet < '_ > > {
match & self {
2024-02-08 20:40:26 -05:00
PublishDiagnostic ::FastCheck ( diagnostic ) = > diagnostic . snippet ( ) ,
2024-02-27 10:13:16 -05:00
PublishDiagnostic ::SpecifierUnfurl ( diagnostic ) = > match diagnostic {
SpecifierUnfurlerDiagnostic ::UnanalyzableDynamicImport {
2024-02-08 20:40:26 -05:00
text_info ,
2024-01-24 08:49:33 -05:00
range ,
2024-02-08 20:40:26 -05:00
..
2024-01-24 08:49:33 -05:00
} = > Some ( DiagnosticSnippet {
2024-02-08 20:40:26 -05:00
source : Cow ::Borrowed ( text_info ) ,
2024-01-24 08:49:33 -05:00
highlight : DiagnosticSnippetHighlight {
style : DiagnosticSnippetHighlightStyle ::Warning ,
range : DiagnosticSourceRange {
start : DiagnosticSourcePos ::SourcePos ( range . start ) ,
end : DiagnosticSourcePos ::SourcePos ( range . end ) ,
} ,
description : Some ( " the unanalyzable dynamic import " . into ( ) ) ,
} ,
} ) ,
} ,
2024-01-24 16:24:52 -05:00
PublishDiagnostic ::InvalidPath { .. } = > None ,
PublishDiagnostic ::DuplicatePath { .. } = > None ,
PublishDiagnostic ::UnsupportedFileType { .. } = > None ,
2024-02-08 20:40:26 -05:00
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 ,
2024-01-24 16:59:18 -05:00
} ,
} ,
2024-02-08 20:40:26 -05:00
description : Some ( " the specifier " . into ( ) ) ,
} ,
} ) ,
2024-02-29 06:54:57 -05:00
PublishDiagnostic ::UnsupportedJsxTsx { .. } = > None ,
2024-01-23 10:37:43 -05:00
}
}
2024-02-08 20:40:26 -05:00
fn hint ( & self ) -> Option < Cow < '_ , str > > {
2024-01-23 10:37:43 -05:00
match & self {
2024-02-08 20:40:26 -05:00
PublishDiagnostic ::FastCheck ( diagnostic ) = > diagnostic . hint ( ) ,
2024-02-27 10:13:16 -05:00
PublishDiagnostic ::SpecifierUnfurl ( _ ) = > None ,
2024-01-24 16:24:52 -05:00
PublishDiagnostic ::InvalidPath { .. } = > Some (
2024-02-08 20:40:26 -05:00
Cow ::Borrowed ( " rename or remove the file, or add it to 'publish.exclude' in the config file " ) ,
2024-01-24 16:24:52 -05:00
) ,
PublishDiagnostic ::DuplicatePath { .. } = > Some (
2024-02-08 20:40:26 -05:00
Cow ::Borrowed ( " rename or remove the file " ) ,
2024-01-24 16:24:52 -05:00
) ,
PublishDiagnostic ::UnsupportedFileType { .. } = > Some (
2024-02-08 20:40:26 -05:00
Cow ::Borrowed ( " remove the file, or add it to 'publish.exclude' in the config file " ) ,
2024-01-24 16:24:52 -05:00
) ,
2024-02-29 06:54:57 -05:00
PublishDiagnostic ::InvalidExternalImport { .. } = > Some ( Cow ::Borrowed ( " replace this import with one from jsr or npm, or vendor the dependency into your package " ) ) ,
PublishDiagnostic ::UnsupportedJsxTsx { .. } = > None ,
2024-01-23 10:37:43 -05:00
}
}
fn snippet_fixed ( & self ) -> Option < DiagnosticSnippet < '_ > > {
2024-03-03 23:25:28 -05:00
match & self {
PublishDiagnostic ::InvalidExternalImport { imported , .. } = > {
match super ::api ::get_jsr_alternative ( imported ) {
Some ( replacement ) = > {
let replacement = SourceTextInfo ::new ( replacement . into ( ) ) ;
let start = replacement . line_start ( 0 ) ;
let end = replacement . line_end ( 0 ) ;
Some ( DiagnosticSnippet {
source : Cow ::Owned ( replacement ) ,
highlight : DiagnosticSnippetHighlight {
style : DiagnosticSnippetHighlightStyle ::Hint ,
range : DiagnosticSourceRange {
start : DiagnosticSourcePos ::SourcePos ( start ) ,
end : DiagnosticSourcePos ::SourcePos ( end ) ,
} ,
description : Some ( " try this specifier " . into ( ) ) ,
} ,
} )
}
None = > None ,
}
}
_ = > None ,
}
2024-01-23 10:37:43 -05:00
}
fn info ( & self ) -> Cow < '_ , [ Cow < '_ , str > ] > {
match & self {
2024-01-24 08:49:33 -05:00
PublishDiagnostic ::FastCheck ( diagnostic ) = > {
2024-02-08 20:40:26 -05:00
diagnostic . info ( )
2024-01-23 10:37:43 -05:00
}
2024-02-27 10:13:16 -05:00
PublishDiagnostic ::SpecifierUnfurl ( diagnostic ) = > match diagnostic {
SpecifierUnfurlerDiagnostic ::UnanalyzableDynamicImport { .. } = > Cow ::Borrowed ( & [
Cow ::Borrowed ( " after publishing this package, imports from the local import map / package.json do not work " ) ,
2024-01-24 08:49:33 -05:00
Cow ::Borrowed ( " dynamic imports that can not be analyzed at publish time will not be rewritten automatically " ) ,
2024-02-27 10:13:16 -05:00
Cow ::Borrowed ( " make sure the dynamic import is resolvable at runtime without an import map / package.json " )
2024-01-24 08:49:33 -05:00
] ) ,
} ,
2024-01-24 16:24:52 -05:00
PublishDiagnostic ::InvalidPath { .. } = > Cow ::Borrowed ( & [
Cow ::Borrowed ( " to portably support all platforms, including windows, the allowed characters in package paths are limited " ) ,
] ) ,
PublishDiagnostic ::DuplicatePath { .. } = > Cow ::Borrowed ( & [
Cow ::Borrowed ( " to support case insensitive file systems, no two package paths may differ only by case " ) ,
] ) ,
PublishDiagnostic ::UnsupportedFileType { .. } = > Cow ::Borrowed ( & [
Cow ::Borrowed ( " only files and directories are supported " ) ,
Cow ::Borrowed ( " the file was ignored and will not be published " )
] ) ,
2024-01-24 16:59:18 -05:00
PublishDiagnostic ::InvalidExternalImport { imported , .. } = > Cow ::Owned ( vec! [
Cow ::Owned ( format! ( " the import was resolved to ' {} ' " , imported ) ) ,
Cow ::Borrowed ( " this specifier is not allowed to be imported on jsr " ) ,
Cow ::Borrowed ( " jsr only supports importing `jsr:`, `npm:`, and `data:` specifiers " ) ,
] ) ,
2024-02-29 06:54:57 -05:00
PublishDiagnostic ::UnsupportedJsxTsx { .. } = > Cow ::Owned ( vec! [
Cow ::Borrowed ( " follow https://github.com/jsr-io/jsr/issues/24 for updates " ) ,
] )
2024-01-23 10:37:43 -05:00
}
}
2024-02-08 20:40:26 -05:00
fn docs_url ( & self ) -> Option < Cow < '_ , str > > {
2024-01-23 10:37:43 -05:00
match & self {
2024-02-08 20:40:26 -05:00
PublishDiagnostic ::FastCheck ( diagnostic ) = > diagnostic . docs_url ( ) ,
2024-02-27 10:13:16 -05:00
PublishDiagnostic ::SpecifierUnfurl ( diagnostic ) = > match diagnostic {
SpecifierUnfurlerDiagnostic ::UnanalyzableDynamicImport { .. } = > None ,
2024-01-24 08:49:33 -05:00
} ,
2024-01-24 16:24:52 -05:00
PublishDiagnostic ::InvalidPath { .. } = > {
2024-02-08 20:40:26 -05:00
Some ( Cow ::Borrowed ( " https://jsr.io/go/invalid-path " ) )
2024-01-24 16:24:52 -05:00
}
2024-02-08 20:40:26 -05:00
PublishDiagnostic ::DuplicatePath { .. } = > Some ( Cow ::Borrowed (
" https://jsr.io/go/case-insensitive-duplicate-path " ,
) ) ,
2024-01-24 16:59:18 -05:00
PublishDiagnostic ::UnsupportedFileType { .. } = > {
2024-02-08 20:40:26 -05:00
Some ( Cow ::Borrowed ( " https://jsr.io/go/unsupported-file-type " ) )
2024-01-24 16:59:18 -05:00
}
PublishDiagnostic ::InvalidExternalImport { .. } = > {
2024-02-08 20:40:26 -05:00
Some ( Cow ::Borrowed ( " https://jsr.io/go/invalid-external-import " ) )
2024-01-24 16:59:18 -05:00
}
2024-02-29 06:54:57 -05:00
PublishDiagnostic ::UnsupportedJsxTsx { .. } = > None ,
2024-01-23 10:37:43 -05:00
}
}
}