From e38932c78dbaaf0bc1b6a941d3945de854eef6c5 Mon Sep 17 00:00:00 2001 From: Kenta Moriuchi Date: Wed, 15 Nov 2023 00:15:34 +0900 Subject: [PATCH 1/3] feat: Add no-import-assetions rule --- docs/rules/no_import_assertions.md | 17 ++++++ src/rules.rs | 2 + src/rules/no_import_assertions.rs | 95 ++++++++++++++++++++++++++++++ www/static/docs.json | 7 +++ 4 files changed, 121 insertions(+) create mode 100644 docs/rules/no_import_assertions.md create mode 100644 src/rules/no_import_assertions.rs diff --git a/docs/rules/no_import_assertions.md b/docs/rules/no_import_assertions.md new file mode 100644 index 000000000..b1cad6985 --- /dev/null +++ b/docs/rules/no_import_assertions.md @@ -0,0 +1,17 @@ +Disallows the `assert` keyword for import attributes + +ES import attributes (previously called import assetions) has been changed to +use the `with` keyword. The old syntax using `assert` is still supported, but +deprecated. + +### Invalid: + +```typescript +import obj from "./obj.json" assert { type: "json" }; +``` + +### Valid: + +```typescript +import obj from "./obj.json" with { type: "json" }; +``` diff --git a/src/rules.rs b/src/rules.rs index ac1c0ea61..9385cb25b 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -58,6 +58,7 @@ pub mod no_fallthrough; pub mod no_func_assign; pub mod no_global_assign; pub mod no_implicit_declare_namespace_export; +pub mod no_import_assertions; pub mod no_import_assign; pub mod no_inferrable_types; pub mod no_inner_declarations; @@ -277,6 +278,7 @@ fn get_all_rules_raw() -> Vec<&'static dyn LintRule> { &no_func_assign::NoFuncAssign, &no_global_assign::NoGlobalAssign, &no_implicit_declare_namespace_export::NoImplicitDeclareNamespaceExport, + &no_import_assertions::NoImportAssertions, &no_import_assign::NoImportAssign, &no_inferrable_types::NoInferrableTypes, &no_inner_declarations::NoInnerDeclarations, diff --git a/src/rules/no_import_assertions.rs b/src/rules/no_import_assertions.rs new file mode 100644 index 000000000..8e657e4a9 --- /dev/null +++ b/src/rules/no_import_assertions.rs @@ -0,0 +1,95 @@ +// Copyright 2020-2023 the Deno authors. All rights reserved. MIT license. +use super::{Context, LintRule}; +use crate::handler::{Handler, Traverse}; +use crate::Program; +use deno_ast::swc::parser::token::{IdentLike, KnownIdent, Token, Word}; +use deno_ast::view as ast_view; +use deno_ast::{SourceRanged, SourceRangedForSpanned}; +use if_chain::if_chain; + +#[derive(Debug)] +pub struct NoImportAssertions; + +const CODE: &str = "no-import-assertions"; +const MESSAGE: &str = + "The `assert` keyword is deprecated for import attributes"; +const HINT: &str = "Instead use the `with` keyword"; + +impl LintRule for NoImportAssertions { + fn tags(&self) -> &'static [&'static str] { + &["recommended"] + } + + fn code(&self) -> &'static str { + CODE + } + + fn lint_program_with_ast_view( + &self, + context: &mut Context, + program: Program<'_>, + ) { + NoImportAssertionsHandler.traverse(program, context); + } + + #[cfg(feature = "docs")] + fn docs(&self) -> &'static str { + include_str!("../../docs/rules/no_import_assertions.md") + } +} + +struct NoImportAssertionsHandler; + +impl Handler for NoImportAssertionsHandler { + fn import_decl( + &mut self, + import_decl: &ast_view::ImportDecl, + ctx: &mut Context, + ) { + if_chain! { + if let Some(with) = import_decl.with; + if let Some(prev_token_and_span) = with.start().previous_token_fast(ctx.program()); + if let Token::Word(word) = &prev_token_and_span.token; + if let Word::Ident(ident_like) = word; + if let IdentLike::Known(known_ident) = ident_like; + if matches!(known_ident, KnownIdent::Assert); + then { + ctx.add_diagnostic_with_hint( + prev_token_and_span.span.range(), + CODE, + MESSAGE, + HINT, + ); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn no_import_assertions_valid() { + assert_lint_ok! { + NoImportAssertions, + r#"import foo from './foo.js';"#, + r#"import foo from './foo.js' with { bar: 'bar' };"#, + }; + } + + #[test] + fn no_import_assertions_invalid() { + assert_lint_err! { + NoImportAssertions, + MESSAGE, + HINT, + r#"import foo from './foo.js' assert { bar: 'bar' };"#: [ + { + line: 1, + col: 27, + }, + ], + }; + } +} diff --git a/www/static/docs.json b/www/static/docs.json index 8e6b45129..69521c2a0 100644 --- a/www/static/docs.json +++ b/www/static/docs.json @@ -339,6 +339,13 @@ "docs": "Disallows the use of implicit exports in [\"ambient\" namespaces].\n\nTypeScript implicitly export all members of an [\"ambient\" namespaces], except\nwhether a named export is present.\n\n[\"ambient\" namespaces]: https://www.typescriptlang.org/docs/handbook/namespaces.html#ambient-namespaces\n\n### Invalid:\n\n```ts\n// foo.ts or foo.d.ts\ndeclare namespace ns {\n interface ImplicitlyExported {}\n export type Exported = true;\n}\n```\n\n### Valid:\n\n```ts\n// foo.ts or foo.d.ts\ndeclare namespace ns {\n interface NonExported {}\n export {};\n}\n\ndeclare namespace ns {\n interface Exported {}\n export { Exported };\n}\n\ndeclare namespace ns {\n export interface Exported {}\n}\n```\n", "tags": [] }, + { + "code": "no-import-assertions", + "docs": "Disallows the `assert` keyword for import attributes\n\nES import attributes (previously called import assetions) has been changed to\nuse the `with` keyword. The old syntax using `assert` is still supported, but\ndeprecated.\n\n### Invalid:\n\n```typescript\nimport obj from \"./obj.json\" assert { type: \"json\" };\n```\n\n### Valid:\n\n```typescript\nimport obj from \"./obj.json\" with { type: \"json\" };\n```\n", + "tags": [ + "recommended" + ] + }, { "code": "no-import-assign", "docs": "Disallows reassignment of imported module bindings\n\nES module import bindings should be treated as read-only since modifying them\nduring code execution will likely result in runtime errors. It also makes for\npoor code readability and difficult maintenance.\n\n### Invalid:\n\n```typescript\nimport defaultMod, { namedMod } from \"./mod.js\";\nimport * as modNameSpace from \"./mod2.js\";\n\ndefaultMod = 0;\nnamedMod = true;\nmodNameSpace.someExportedMember = \"hello\";\nmodNameSpace = {};\n```\n\n### Valid:\n\n```typescript\nimport defaultMod, { namedMod } from \"./mod.js\";\nimport * as modNameSpace from \"./mod2.js\";\n\n// properties of bound imports may be set\ndefaultMod.prop = 1;\nnamedMod.prop = true;\nmodNameSpace.someExportedMember.prop = \"hello\";\n```\n", From 8102440f3c91356356c021eeebe9315251de9517 Mon Sep 17 00:00:00 2001 From: Kenta Moriuchi Date: Wed, 15 Nov 2023 02:01:03 +0900 Subject: [PATCH 2/3] fix: dynamic imports --- docs/rules/no_import_assertions.md | 2 ++ src/rules/no_import_assertions.rs | 56 ++++++++++++++++++++++++++++++ www/static/docs.json | 2 +- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/docs/rules/no_import_assertions.md b/docs/rules/no_import_assertions.md index b1cad6985..af80cda5d 100644 --- a/docs/rules/no_import_assertions.md +++ b/docs/rules/no_import_assertions.md @@ -8,10 +8,12 @@ deprecated. ```typescript import obj from "./obj.json" assert { type: "json" }; +import("./obj2.json", { assert: { type: "json" } }); ``` ### Valid: ```typescript import obj from "./obj.json" with { type: "json" }; +import("./obj2.json", { with: { type: "json" } }); ``` diff --git a/src/rules/no_import_assertions.rs b/src/rules/no_import_assertions.rs index 8e657e4a9..62d3f460a 100644 --- a/src/rules/no_import_assertions.rs +++ b/src/rules/no_import_assertions.rs @@ -63,6 +63,47 @@ impl Handler for NoImportAssertionsHandler { } } } + + fn call_expr(&mut self, call_expr: &ast_view::CallExpr, ctx: &mut Context) { + if_chain! { + if matches!(call_expr.callee, ast_view::Callee::Import(_)); + if let Some(expr_or_spread) = call_expr.args.get(1); + if let ast_view::Expr::Object(object_lit) = expr_or_spread.expr; + then { + for prop_or_spread in object_lit.props.iter() { + if_chain! { + if let ast_view::PropOrSpread::Prop(prop) = prop_or_spread; + if let ast_view::Prop::KeyValue(key_value_prop) = prop; + then { + match key_value_prop.key { + ast_view::PropName::Ident(ident) => { + if ident.sym().as_ref() == "assert" { + ctx.add_diagnostic_with_hint( + ident.range(), + CODE, + MESSAGE, + HINT, + ); + } + }, + ast_view::PropName::Str(str) => { + if str.value().as_ref() == "assert" { + ctx.add_diagnostic_with_hint( + str.range(), + CODE, + MESSAGE, + HINT, + ); + } + } + _ => (), + } + } + } + } + } + } + } } #[cfg(test)] @@ -75,6 +116,9 @@ mod tests { NoImportAssertions, r#"import foo from './foo.js';"#, r#"import foo from './foo.js' with { bar: 'bar' };"#, + r#"import('./foo.js');"#, + r#"import('./foo.js', { with: { bar: 'bar' } });"#, + r#"import('./foo.js', { "with": { bar: 'bar' } });"#, }; } @@ -90,6 +134,18 @@ mod tests { col: 27, }, ], + r#"import('./foo.js', { assert: { bar: 'bar' } });"#: [ + { + line: 1, + col: 21, + }, + ], + r#"import('./foo.js', { "assert": { bar: 'bar' } });"#: [ + { + line: 1, + col: 21, + }, + ], }; } } diff --git a/www/static/docs.json b/www/static/docs.json index 69521c2a0..a5ab882b7 100644 --- a/www/static/docs.json +++ b/www/static/docs.json @@ -341,7 +341,7 @@ }, { "code": "no-import-assertions", - "docs": "Disallows the `assert` keyword for import attributes\n\nES import attributes (previously called import assetions) has been changed to\nuse the `with` keyword. The old syntax using `assert` is still supported, but\ndeprecated.\n\n### Invalid:\n\n```typescript\nimport obj from \"./obj.json\" assert { type: \"json\" };\n```\n\n### Valid:\n\n```typescript\nimport obj from \"./obj.json\" with { type: \"json\" };\n```\n", + "docs": "Disallows the `assert` keyword for import attributes\n\nES import attributes (previously called import assetions) has been changed to\nuse the `with` keyword. The old syntax using `assert` is still supported, but\ndeprecated.\n\n### Invalid:\n\n```typescript\nimport obj from \"./obj.json\" assert { type: \"json\" };\nimport(\"./obj2.json\", { assert: { type: \"json\" } });\n```\n\n### Valid:\n\n```typescript\nimport obj from \"./obj.json\" with { type: \"json\" };\nimport(\"./obj2.json\", { with: { type: \"json\" } });\n```\n", "tags": [ "recommended" ] From 3994f51c350f2a0a7491c673ff8d6d48b85d3a95 Mon Sep 17 00:00:00 2001 From: Kenta Moriuchi Date: Wed, 15 Nov 2023 02:02:33 +0900 Subject: [PATCH 3/3] fmt --- src/rules/no_import_assertions.rs | 62 +++++++++++++++---------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/rules/no_import_assertions.rs b/src/rules/no_import_assertions.rs index 62d3f460a..4b2e83501 100644 --- a/src/rules/no_import_assertions.rs +++ b/src/rules/no_import_assertions.rs @@ -65,44 +65,44 @@ impl Handler for NoImportAssertionsHandler { } fn call_expr(&mut self, call_expr: &ast_view::CallExpr, ctx: &mut Context) { - if_chain! { - if matches!(call_expr.callee, ast_view::Callee::Import(_)); - if let Some(expr_or_spread) = call_expr.args.get(1); - if let ast_view::Expr::Object(object_lit) = expr_or_spread.expr; - then { - for prop_or_spread in object_lit.props.iter() { - if_chain! { - if let ast_view::PropOrSpread::Prop(prop) = prop_or_spread; - if let ast_view::Prop::KeyValue(key_value_prop) = prop; - then { - match key_value_prop.key { - ast_view::PropName::Ident(ident) => { - if ident.sym().as_ref() == "assert" { - ctx.add_diagnostic_with_hint( - ident.range(), - CODE, - MESSAGE, - HINT, - ); - } - }, - ast_view::PropName::Str(str) => { - if str.value().as_ref() == "assert" { - ctx.add_diagnostic_with_hint( - str.range(), - CODE, - MESSAGE, - HINT, - ); - } + if_chain! { + if matches!(call_expr.callee, ast_view::Callee::Import(_)); + if let Some(expr_or_spread) = call_expr.args.get(1); + if let ast_view::Expr::Object(object_lit) = expr_or_spread.expr; + then { + for prop_or_spread in object_lit.props.iter() { + if_chain! { + if let ast_view::PropOrSpread::Prop(prop) = prop_or_spread; + if let ast_view::Prop::KeyValue(key_value_prop) = prop; + then { + match key_value_prop.key { + ast_view::PropName::Ident(ident) => { + if ident.sym().as_ref() == "assert" { + ctx.add_diagnostic_with_hint( + ident.range(), + CODE, + MESSAGE, + HINT, + ); + } + }, + ast_view::PropName::Str(str) => { + if str.value().as_ref() == "assert" { + ctx.add_diagnostic_with_hint( + str.range(), + CODE, + MESSAGE, + HINT, + ); } - _ => (), } + _ => (), } } } } } + } } }