diff --git a/src/fast_check/swc_helpers.rs b/src/fast_check/swc_helpers.rs index 811089fc2..d7c9d08db 100644 --- a/src/fast_check/swc_helpers.rs +++ b/src/fast_check/swc_helpers.rs @@ -1,26 +1,17 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_ast::swc::ast::Expr; -use deno_ast::swc::ast::Ident; -use deno_ast::swc::ast::Lit; -use deno_ast::swc::ast::MemberProp; -use deno_ast::swc::ast::Prop; -use deno_ast::swc::ast::PropName; -use deno_ast::swc::ast::PropOrSpread; -use deno_ast::swc::ast::ReturnStmt; -use deno_ast::swc::ast::Stmt; -use deno_ast::swc::ast::TsEntityName; -use deno_ast::swc::ast::TsKeywordType; -use deno_ast::swc::ast::TsKeywordTypeKind; -use deno_ast::swc::ast::TsLit; -use deno_ast::swc::ast::TsLitType; -use deno_ast::swc::ast::TsTupleElement; -use deno_ast::swc::ast::TsType; -use deno_ast::swc::ast::TsTypeAnn; -use deno_ast::swc::ast::TsTypeOperator; -use deno_ast::swc::ast::TsTypeOperatorOp; -use deno_ast::swc::ast::TsTypeRef; +use std::rc::Rc; + +use deno_ast::swc::ast::*; +use deno_ast::swc::codegen::text_writer::JsWriter; +use deno_ast::swc::codegen::Node; +use deno_ast::swc::common::comments::SingleThreadedComments; +use deno_ast::swc::common::FileName; +use deno_ast::swc::common::SourceMap; use deno_ast::swc::common::DUMMY_SP; +use deno_ast::swc_codegen_config; +use deno_ast::ModuleSpecifier; +use deno_ast::SourceTextInfo; pub fn ident(name: String) -> Ident { Ident { @@ -267,3 +258,44 @@ pub fn maybe_lit_to_ts_type(lit: &Lit) -> Option { Lit::JSXText(_) => None, } } + +pub fn emit( + specifier: &ModuleSpecifier, + comments: &SingleThreadedComments, + text_info: &SourceTextInfo, + module: &deno_ast::swc::ast::Module, +) -> Result<(String, Vec), anyhow::Error> { + let source_map = Rc::new(SourceMap::default()); + let file_name = FileName::Url(specifier.clone()); + source_map.new_source_file(file_name, text_info.text_str().to_string()); + + let mut src_map_buf = vec![]; + let mut buf = vec![]; + { + let mut writer = Box::new(JsWriter::new( + source_map.clone(), + "\n", + &mut buf, + Some(&mut src_map_buf), + )); + writer.set_indent_str(" "); // two spaces + + let mut emitter = deno_ast::swc::codegen::Emitter { + cfg: swc_codegen_config(), + comments: Some(comments), + cm: source_map.clone(), + wr: writer, + }; + module.emit_with(&mut emitter)?; + } + let src = String::from_utf8(buf)?; + let map = { + let mut buf = Vec::new(); + source_map + .build_source_map_from(&src_map_buf, None) + .to_writer(&mut buf)?; + buf + }; + + Ok((src, map)) +} diff --git a/src/fast_check/transform.rs b/src/fast_check/transform.rs index a811ff75e..00a2ec3e3 100644 --- a/src/fast_check/transform.rs +++ b/src/fast_check/transform.rs @@ -4,86 +4,19 @@ #![allow(clippy::disallowed_methods)] #![allow(clippy::disallowed_types)] -use std::rc::Rc; use std::sync::Arc; -use deno_ast::swc::ast::Accessibility; -use deno_ast::swc::ast::ArrayLit; -use deno_ast::swc::ast::ArrowExpr; -use deno_ast::swc::ast::BindingIdent; -use deno_ast::swc::ast::BlockStmt; -use deno_ast::swc::ast::BlockStmtOrExpr; -use deno_ast::swc::ast::CallExpr; -use deno_ast::swc::ast::Callee; -use deno_ast::swc::ast::Class; -use deno_ast::swc::ast::ClassMember; -use deno_ast::swc::ast::ClassProp; -use deno_ast::swc::ast::Decl; -use deno_ast::swc::ast::DefaultDecl; -use deno_ast::swc::ast::Expr; -use deno_ast::swc::ast::Function; -use deno_ast::swc::ast::Ident; -use deno_ast::swc::ast::Lit; -use deno_ast::swc::ast::MemberProp; -use deno_ast::swc::ast::MethodKind; -use deno_ast::swc::ast::ModuleDecl; -use deno_ast::swc::ast::ModuleItem; -use deno_ast::swc::ast::ObjectLit; -use deno_ast::swc::ast::ObjectPatProp; -use deno_ast::swc::ast::Param; -use deno_ast::swc::ast::ParamOrTsParamProp; -use deno_ast::swc::ast::ParenExpr; -use deno_ast::swc::ast::Pat; -use deno_ast::swc::ast::PrivateName; -use deno_ast::swc::ast::PrivateProp; -use deno_ast::swc::ast::Prop; -use deno_ast::swc::ast::PropName; -use deno_ast::swc::ast::PropOrSpread; -use deno_ast::swc::ast::ReturnStmt; -use deno_ast::swc::ast::Stmt; -use deno_ast::swc::ast::Str; -use deno_ast::swc::ast::TsArrayType; -use deno_ast::swc::ast::TsAsExpr; -use deno_ast::swc::ast::TsEntityName; -use deno_ast::swc::ast::TsIntersectionType; -use deno_ast::swc::ast::TsKeywordType; -use deno_ast::swc::ast::TsKeywordTypeKind; -use deno_ast::swc::ast::TsLit; -use deno_ast::swc::ast::TsModuleDecl; -use deno_ast::swc::ast::TsModuleName; -use deno_ast::swc::ast::TsModuleRef; -use deno_ast::swc::ast::TsNamespaceBody; -use deno_ast::swc::ast::TsOptionalType; -use deno_ast::swc::ast::TsParamPropParam; -use deno_ast::swc::ast::TsParenthesizedType; -use deno_ast::swc::ast::TsRestType; -use deno_ast::swc::ast::TsTupleElement; -use deno_ast::swc::ast::TsTupleType; -use deno_ast::swc::ast::TsType; -use deno_ast::swc::ast::TsTypeAnn; -use deno_ast::swc::ast::TsTypeOperator; -use deno_ast::swc::ast::TsTypeOperatorOp; -use deno_ast::swc::ast::TsTypeParamInstantiation; -use deno_ast::swc::ast::TsTypeRef; -use deno_ast::swc::ast::TsUnionOrIntersectionType; -use deno_ast::swc::ast::TsUnionType; -use deno_ast::swc::ast::VarDecl; -use deno_ast::swc::codegen::text_writer::JsWriter; -use deno_ast::swc::codegen::Node; +use deno_ast::swc::ast::*; use deno_ast::swc::common::comments::CommentKind; use deno_ast::swc::common::comments::SingleThreadedComments; use deno_ast::swc::common::comments::SingleThreadedCommentsMapInner; -use deno_ast::swc::common::FileName; -use deno_ast::swc::common::SourceMap; use deno_ast::swc::common::Spanned; use deno_ast::swc::common::DUMMY_SP; -use deno_ast::swc_codegen_config; use deno_ast::ModuleSpecifier; use deno_ast::MultiThreadedComments; use deno_ast::ParsedSource; use deno_ast::SourceRange; use deno_ast::SourceRangedForSpanned; -use deno_ast::SourceTextInfo; use crate::DefaultModuleAnalyzer; use crate::ModuleGraph; @@ -92,6 +25,7 @@ use crate::WorkspaceMember; use super::range_finder::ModulePublicRanges; use super::swc_helpers::any_type_ann; +use super::swc_helpers::emit; use super::swc_helpers::get_return_stmts_with_arg_from_function_body; use super::swc_helpers::ident; use super::swc_helpers::is_never_type; @@ -238,6 +172,7 @@ struct FastCheckTransformer<'a> { parsed_source: &'a ParsedSource, should_error_on_first_diagnostic: bool, diagnostics: Vec, + is_decl_file: bool, } impl<'a> FastCheckTransformer<'a> { @@ -249,6 +184,7 @@ impl<'a> FastCheckTransformer<'a> { should_error_on_first_diagnostic: bool, ) -> Self { Self { + is_decl_file: parsed_source.media_type().is_declaration(), graph, specifier, public_ranges, @@ -509,6 +445,10 @@ impl<'a> FastCheckTransformer<'a> { n: &mut Class, comments: &mut CommentsMut, ) -> Result<(), Vec> { + if self.is_decl_file { + return Ok(()); // no need to do anything + } + let mut members = Vec::with_capacity(n.body.len()); let mut had_private = false; if let Some(super_class) = &n.super_class { @@ -854,6 +794,9 @@ impl<'a> FastCheckTransformer<'a> { is_overload: bool, is_set_accessor: bool, ) -> Result<(), Vec> { + if self.is_decl_file { + return Ok(()); // no need to do anything + } if is_overload { for (i, param) in n.params.iter_mut().enumerate() { *param = Param { @@ -1136,59 +1079,71 @@ impl<'a> FastCheckTransformer<'a> { ) -> Result> { n.decls.retain(|n| self.public_ranges.contains(&n.range())); - for decl in &mut n.decls { - match &mut decl.name { - Pat::Ident(ident) => { - if ident.type_ann.is_none() { - let inferred_type = decl - .init - .as_ref() - .and_then(|e| self.maybe_infer_type_from_expr(e)); - match inferred_type { - Some(t) => { - ident.type_ann = Some(Box::new(TsTypeAnn { - span: DUMMY_SP, - type_ann: Box::new(t), - })); - decl.init = Some(obj_as_any_expr()); - } - None => { - let is_init_leavable = match decl.init.as_mut() { - Some(init) => self.maybe_transform_expr_if_leavable( - init, - Some(ident.id.range()), - )?, - None => false, - }; - if !is_init_leavable { - self.mark_diagnostic( - FastCheckDiagnostic::MissingExplicitType { - range: self.source_range_to_range(ident.range()), - }, - )?; - } + // don't need to do anything for these in a declaration file + if !self.is_decl_file { + for decl in &mut n.decls { + self.transform_var_declarator(decl)?; + } + } + + Ok(!n.decls.is_empty()) + } + + fn transform_var_declarator( + &mut self, + n: &mut VarDeclarator, + ) -> Result<(), Vec> { + match &mut n.name { + Pat::Ident(ident) => { + if ident.type_ann.is_none() { + let inferred_type = n + .init + .as_ref() + .and_then(|e| self.maybe_infer_type_from_expr(e)); + match inferred_type { + Some(t) => { + ident.type_ann = Some(Box::new(TsTypeAnn { + span: DUMMY_SP, + type_ann: Box::new(t), + })); + n.init = Some(obj_as_any_expr()); + } + None => { + let is_init_leavable = match n.init.as_mut() { + Some(init) => self.maybe_transform_expr_if_leavable( + init, + Some(ident.id.range()), + )?, + None => false, + }; + if !is_init_leavable { + self.mark_diagnostic( + FastCheckDiagnostic::MissingExplicitType { + range: self.source_range_to_range(ident.range()), + }, + )?; } } - } else { - decl.init = Some(obj_as_any_expr()); } - } - Pat::Array(_) - | Pat::Rest(_) - | Pat::Object(_) - | Pat::Assign(_) - | Pat::Invalid(_) - | Pat::Expr(_) => { - self.mark_diagnostic( - FastCheckDiagnostic::UnsupportedDestructuring { - range: self.source_range_to_range(decl.name.range()), - }, - )?; + } else { + n.init = Some(obj_as_any_expr()); } } + Pat::Array(_) + | Pat::Rest(_) + | Pat::Object(_) + | Pat::Assign(_) + | Pat::Invalid(_) + | Pat::Expr(_) => { + self.mark_diagnostic( + FastCheckDiagnostic::UnsupportedDestructuring { + range: self.source_range_to_range(n.name.range()), + }, + )?; + } } - Ok(!n.decls.is_empty()) + Ok(()) } fn transform_ts_module( @@ -1578,47 +1533,6 @@ fn prefix_idents_in_pat(pat: &mut Pat, prefix: &str) { } } -pub fn emit( - specifier: &ModuleSpecifier, - comments: &SingleThreadedComments, - text_info: &SourceTextInfo, - module: &deno_ast::swc::ast::Module, -) -> Result<(String, Vec), anyhow::Error> { - let source_map = Rc::new(SourceMap::default()); - let file_name = FileName::Url(specifier.clone()); - source_map.new_source_file(file_name, text_info.text_str().to_string()); - - let mut src_map_buf = vec![]; - let mut buf = vec![]; - { - let mut writer = Box::new(JsWriter::new( - source_map.clone(), - "\n", - &mut buf, - Some(&mut src_map_buf), - )); - writer.set_indent_str(" "); // two spaces - - let mut emitter = deno_ast::swc::codegen::Emitter { - cfg: swc_codegen_config(), - comments: Some(comments), - cm: source_map.clone(), - wr: writer, - }; - module.emit_with(&mut emitter)?; - } - let src = String::from_utf8(buf)?; - let map = { - let mut buf = Vec::new(); - source_map - .build_source_map_from(&src_map_buf, None) - .to_writer(&mut buf)?; - buf - }; - - Ok((src, map)) -} - fn infer_simple_type_from_type(t: &TsType) -> Option { match t { TsType::TsKeywordType(_) => Some(t.clone()), diff --git a/src/fast_check/transform_dts.rs b/src/fast_check/transform_dts.rs index 6acb4a584..0eab2dbcc 100644 --- a/src/fast_check/transform_dts.rs +++ b/src/fast_check/transform_dts.rs @@ -1,25 +1,19 @@ -use deno_ast::{ - swc::{ - ast::{ - BindingIdent, ClassMember, Decl, DefaultDecl, ExportDecl, - ExportDefaultDecl, ExportDefaultExpr, Expr, Ident, Lit, MethodKind, - Module, ModuleDecl, ModuleItem, OptChainBase, Pat, Prop, PropName, - PropOrSpread, Stmt, TsFnOrConstructorType, TsFnParam, TsFnType, - TsKeywordType, TsKeywordTypeKind, TsLit, TsNamespaceBody, - TsPropertySignature, TsTupleElement, TsTupleType, TsType, TsTypeAnn, - TsTypeElement, TsTypeLit, VarDecl, VarDeclKind, VarDeclarator, - }, - common::DUMMY_SP, - }, - ModuleSpecifier, ParsedSource, SourceRange, SourceRangedForSpanned, -}; - -use crate::{FastCheckDiagnostic, FastCheckDiagnosticRange}; - -use super::swc_helpers::{ - any_type_ann, maybe_lit_to_ts_type, maybe_lit_to_ts_type_const, ts_readonly, - ts_tuple_element, type_ann, -}; +use deno_ast::swc::ast::*; +use deno_ast::swc::common::DUMMY_SP; +use deno_ast::ModuleSpecifier; +use deno_ast::ParsedSource; +use deno_ast::SourceRange; +use deno_ast::SourceRangedForSpanned; + +use crate::FastCheckDiagnostic; +use crate::FastCheckDiagnosticRange; + +use super::swc_helpers::any_type_ann; +use super::swc_helpers::maybe_lit_to_ts_type; +use super::swc_helpers::maybe_lit_to_ts_type_const; +use super::swc_helpers::ts_readonly; +use super::swc_helpers::ts_tuple_element; +use super::swc_helpers::type_ann; #[derive(Debug, Clone, thiserror::Error)] pub enum FastCheckDtsDiagnostic { @@ -927,12 +921,15 @@ impl<'a> FastCheckDtsTransformer<'a> { #[cfg(test)] mod tests { - use crate::{ - fast_check::{transform::emit, transform_dts::FastCheckDtsTransformer}, - source::{MemoryLoader, Source}, - symbols::RootSymbol, - BuildOptions, DefaultModuleParser, GraphKind, ModuleGraph, - }; + use crate::fast_check::swc_helpers::emit; + use crate::fast_check::transform_dts::FastCheckDtsTransformer; + use crate::source::MemoryLoader; + use crate::source::Source; + use crate::symbols::RootSymbol; + use crate::BuildOptions; + use crate::DefaultModuleParser; + use crate::GraphKind; + use crate::ModuleGraph; use url::Url; async fn transform_dts_test(source: &str, expected: &str) { diff --git a/src/lib.rs b/src/lib.rs index 849ea9143..2ff4a21fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,8 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +#![deny(clippy::print_stderr)] +#![deny(clippy::print_stdout)] + mod analyzer; mod ast; mod graph; diff --git a/tests/specs/graph/JsrSpecifiers_FastCheck_Dts.txt b/tests/specs/graph/JsrSpecifiers_FastCheck_Dts.txt new file mode 100644 index 000000000..53bc984af --- /dev/null +++ b/tests/specs/graph/JsrSpecifiers_FastCheck_Dts.txt @@ -0,0 +1,138 @@ +# https://jsr.io/@scope/a/meta.json +{"versions": { "1.0.0": {} } } + +# https://jsr.io/@scope/a/1.0.0_meta.json +{ "exports": { ".": "./mod.js" } } + +# https://jsr.io/@scope/a/1.0.0/other.d.ts +export class Other {} + +# https://jsr.io/@scope/a/1.0.0/mod.d.ts +import { Other } from "./other.d.ts"; // will be removed because not used + +export class Test { + prop: typeof random; + method(): typeof public; + static prop: number; +} + +declare function random(): number; + +declare var private: number; // will be removed +declare var public: number; + +# https://jsr.io/@scope/a/1.0.0/mod.js +/// +export class Test {} + +# mod.ts +import 'jsr:@scope/a' + +# output +{ + "roots": [ + "file:///mod.ts" + ], + "modules": [ + { + "kind": "esm", + "dependencies": [ + { + "specifier": "jsr:@scope/a", + "code": { + "specifier": "jsr:@scope/a", + "span": { + "start": { + "line": 0, + "character": 7 + }, + "end": { + "line": 0, + "character": 21 + } + } + } + } + ], + "size": 22, + "mediaType": "TypeScript", + "specifier": "file:///mod.ts" + }, + { + "kind": "esm", + "dependencies": [ + { + "specifier": "./other.d.ts", + "code": { + "specifier": "https://jsr.io/@scope/a/1.0.0/other.d.ts", + "span": { + "start": { + "line": 0, + "character": 22 + }, + "end": { + "line": 0, + "character": 36 + } + } + } + } + ], + "size": 283, + "mediaType": "Dts", + "specifier": "https://jsr.io/@scope/a/1.0.0/mod.d.ts" + }, + { + "kind": "esm", + "size": 58, + "typesDependency": { + "specifier": "./mod.d.ts", + "dependency": { + "specifier": "https://jsr.io/@scope/a/1.0.0/mod.d.ts", + "span": { + "start": { + "line": 0, + "character": 21 + }, + "end": { + "line": 0, + "character": 33 + } + } + } + }, + "mediaType": "JavaScript", + "specifier": "https://jsr.io/@scope/a/1.0.0/mod.js" + }, + { + "kind": "esm", + "size": 22, + "mediaType": "Dts", + "specifier": "https://jsr.io/@scope/a/1.0.0/other.d.ts" + } + ], + "redirects": { + "jsr:@scope/a": "https://jsr.io/@scope/a/1.0.0/mod.js" + }, + "packages": { + "@scope/a": "@scope/a@1.0.0" + } +} + +Fast check https://jsr.io/@scope/a/1.0.0/mod.d.ts: + {} + export class Test { + prop: typeof random; + method(): typeof public; + static prop: number; + } + declare function random(): number; + declare var public: number; + --- DTS --- + export declare class Test { + prop: typeof random; + method(): typeof public; + static prop: number; + } + declare function random(): number; + declare var public: number; diff --git a/tests/specs/graph/JsrSpecifiers_FastCheck_NestedJavaScriptDeclFile.txt b/tests/specs/graph/JsrSpecifiers_FastCheck_NestedJavaScriptDeclFile.txt index 59aaca442..8c4621a71 100644 --- a/tests/specs/graph/JsrSpecifiers_FastCheck_NestedJavaScriptDeclFile.txt +++ b/tests/specs/graph/JsrSpecifiers_FastCheck_NestedJavaScriptDeclFile.txt @@ -201,7 +201,7 @@ Fast check https://jsr.io/@scope/a/1.0.0/a.d.ts: Fast check https://jsr.io/@scope/a/1.0.0/b.d.ts: {} export class B1 { - prop!: string; + prop: string; } --- DTS --- export declare class B1 {