From bf8a6c05900ae69e5bb750edf0d0b4dbe77bde26 Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Tue, 22 Oct 2024 11:35:57 +0200 Subject: [PATCH] fix(noTypeOnlyImportAttributes): ignore .cts files --- CHANGELOG.md | 17 +++++++++++++++++ .../no_initializer_with_definite.rs | 2 +- .../no_type_only_import_attributes.rs | 19 ++++++++++++++++++- crates/biome_js_analyze/tests/spec_tests.rs | 4 ++-- .../noTypeOnlyImportAttributes/valid.cts | 3 +++ .../noTypeOnlyImportAttributes/valid.cts.snap | 10 ++++++++++ .../valid_cjs.package.json | 3 +++ crates/biome_js_syntax/src/file_source.rs | 14 ++++++++++---- 8 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 crates/biome_js_analyze/tests/specs/correctness/noTypeOnlyImportAttributes/valid.cts create mode 100644 crates/biome_js_analyze/tests/specs/correctness/noTypeOnlyImportAttributes/valid.cts.snap create mode 100644 crates/biome_js_analyze/tests/specs/correctness/noTypeOnlyImportAttributes/valid_cjs.package.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ca11a462ccc..3087b982a4e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -98,6 +98,23 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b - `noSuspiciousSemicolonInJsx` now catches suspicious semicolons in React fragments. Contributed by @vasucp1207 +- The syntax rule `noTypeOnlyImportAttributes` now ignores `.cts` files ([#4361](https://github.com/biomejs/biome/issues/4361)). + + Since TypeScript 5.3, type-only imports can be associated to an import attribute in CommonJS-enabled files. + See the [TypeScript docs](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-3.html#stable-support-resolution-mode-in-import-types). + + The following code is no longer reported as a syntax error: + + ```cts + import type { TypeFromRequire } from "pkg" with { + "resolution-mode": "require" + }; + ``` + + Note that this is only allowed in files ending with the `cts` extension. + + Contributed by @Conaclos + ### CLI #### Enhancements diff --git a/crates/biome_js_analyze/src/syntax/correctness/no_initializer_with_definite.rs b/crates/biome_js_analyze/src/syntax/correctness/no_initializer_with_definite.rs index 7842285971a4..87ee79d0b7b3 100644 --- a/crates/biome_js_analyze/src/syntax/correctness/no_initializer_with_definite.rs +++ b/crates/biome_js_analyze/src/syntax/correctness/no_initializer_with_definite.rs @@ -7,7 +7,7 @@ declare_syntax_rule! { /// /// ## Examples /// - /// ```js + /// ```ts /// let foo!: string = "bar"; /// ``` pub NoInitializerWithDefinite { diff --git a/crates/biome_js_analyze/src/syntax/correctness/no_type_only_import_attributes.rs b/crates/biome_js_analyze/src/syntax/correctness/no_type_only_import_attributes.rs index a40373d8208b..84a58e732c90 100644 --- a/crates/biome_js_analyze/src/syntax/correctness/no_type_only_import_attributes.rs +++ b/crates/biome_js_analyze/src/syntax/correctness/no_type_only_import_attributes.rs @@ -8,11 +8,23 @@ use biome_rowan::{AstNode, AstSeparatedList, TextRange}; declare_syntax_rule! { /// Disallow type-only imports and exports with import attributes. /// + /// There is one exception: TypeScript 5.3 and above allow this in CommonJS files, e.g. files ending with the `.cts` extension. + /// See the [TypeScript docs](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-3.html#stable-support-resolution-mode-in-import-types). + /// /// ## Examples /// - /// ```js + /// ### Invalid + /// + /// ```ts /// import type { A } from "./a.json" with { type: "json" }; /// ``` + /// + /// ### Valid + /// + /// ```cts + /// import type { A } from "./a.json" with { "resolution-mode": "require" }; + /// ``` + /// pub NoTypeOnlyImportAttributes { version: "1.5.0", name: "noTypeOnlyImportAttributes", @@ -27,6 +39,11 @@ impl Rule for NoTypeOnlyImportAttributes { type Options = (); fn run(ctx: &RuleContext) -> Self::Signals { + let extension = ctx.file_path().extension()?; + if extension.as_encoded_bytes() == b"cts" { + // Ignore `*.cts` + return None; + } let module_item = ctx.query(); match module_item { AnyJsModuleItem::AnyJsStatement(_) => None, diff --git a/crates/biome_js_analyze/tests/spec_tests.rs b/crates/biome_js_analyze/tests/spec_tests.rs index e9858cb0ce32..05d5805d86a7 100644 --- a/crates/biome_js_analyze/tests/spec_tests.rs +++ b/crates/biome_js_analyze/tests/spec_tests.rs @@ -13,8 +13,8 @@ use biome_test_utils::{ use std::ops::Deref; use std::{ffi::OsStr, fs::read_to_string, path::Path, slice}; -tests_macros::gen_tests! {"tests/specs/**/*.{cjs,js,jsx,tsx,ts,json,jsonc,svelte}", crate::run_test, "module"} -tests_macros::gen_tests! {"tests/suppression/**/*.{cjs,js,jsx,tsx,ts,json,jsonc,svelte}", crate::run_suppression_test, "module"} +tests_macros::gen_tests! {"tests/specs/**/*.{cjs,cts,js,jsx,tsx,ts,json,jsonc,svelte}", crate::run_test, "module"} +tests_macros::gen_tests! {"tests/suppression/**/*.{cjs,cts,js,jsx,tsx,ts,json,jsonc,svelte}", crate::run_suppression_test, "module"} fn run_test(input: &'static str, _: &str, _: &str, _: &str) { register_leak_checker(); diff --git a/crates/biome_js_analyze/tests/specs/correctness/noTypeOnlyImportAttributes/valid.cts b/crates/biome_js_analyze/tests/specs/correctness/noTypeOnlyImportAttributes/valid.cts new file mode 100644 index 000000000000..b0ad8e1ccf2a --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/noTypeOnlyImportAttributes/valid.cts @@ -0,0 +1,3 @@ +import type { TypeFromRequire } from "pkg" with { + "resolution-mode": "require" +}; \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/correctness/noTypeOnlyImportAttributes/valid.cts.snap b/crates/biome_js_analyze/tests/specs/correctness/noTypeOnlyImportAttributes/valid.cts.snap new file mode 100644 index 000000000000..c63cf0868806 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/noTypeOnlyImportAttributes/valid.cts.snap @@ -0,0 +1,10 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: valid.cts +--- +# Input +```cts +import type { TypeFromRequire } from "pkg" with { + "resolution-mode": "require" +}; +``` diff --git a/crates/biome_js_analyze/tests/specs/correctness/noTypeOnlyImportAttributes/valid_cjs.package.json b/crates/biome_js_analyze/tests/specs/correctness/noTypeOnlyImportAttributes/valid_cjs.package.json new file mode 100644 index 000000000000..6a0d2ef2aa8b --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/noTypeOnlyImportAttributes/valid_cjs.package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} \ No newline at end of file diff --git a/crates/biome_js_syntax/src/file_source.rs b/crates/biome_js_syntax/src/file_source.rs index 9d091b81d548..7d7bfd220c5a 100644 --- a/crates/biome_js_syntax/src/file_source.rs +++ b/crates/biome_js_syntax/src/file_source.rs @@ -281,10 +281,16 @@ impl JsFileSource { } } Language::TypeScript { .. } => { - if matches!(self.variant, LanguageVariant::Jsx) { - "tsx" - } else { - "ts" + match self.variant { + LanguageVariant::Standard => "ts", + LanguageVariant::StandardRestricted => { + // This could also be `mts`. + // We choose `cts` because we expect this extension to be more widely used. + // Moreover, it allows more valid syntax such as `import type` with import + // attributes (See `noTypeOnlyImportAttributes` syntax rule). + "cts" + } + LanguageVariant::Jsx => "tsx", } } }