From 00dcece336eb7864b58832555f3df90463d02e6b Mon Sep 17 00:00:00 2001 From: Boshen Date: Wed, 14 May 2025 22:59:26 +0800 Subject: [PATCH] fix(span)!: `SourceType::from_path(".js")` return js instead of jsx Previously `SourceType::from_path(".js")` treats a `.js` file as `.jsx` to maximize parse rate for the linter. Force the `.jsx` change in the linter instead of a generic solution for `SourceType::from_path`. --- apps/oxlint/fixtures/linter/js_as_jsx.js | 2 + apps/oxlint/src/lint.rs | 6 ++ .../_-A all fixtures__linter@oxlint.snap | 2 +- .../snapshots/_fixtures__linter@oxlint.snap | 12 +++- ...ixtures__linter__js_and_jsx.js@oxlint.snap | 12 ++++ ...fixtures__linter__js_as_jsx.js@oxlint.snap | 21 ++++++ crates/oxc_linter/src/service/runtime.rs | 7 +- crates/oxc_span/src/source_type.rs | 12 ++-- .../oxc_transformer/src/options/babel/mod.rs | 3 + tasks/coverage/snapshots/parser_babel.snap | 67 ++++++++++--------- .../coverage/snapshots/parser_typescript.snap | 23 ++++--- tasks/coverage/snapshots/semantic_babel.snap | 8 +-- tasks/prettier_conformance/src/lib.rs | 1 + tasks/prettier_conformance/src/spec.rs | 5 +- tasks/transform_conformance/src/test_case.rs | 6 +- 15 files changed, 122 insertions(+), 65 deletions(-) create mode 100644 apps/oxlint/fixtures/linter/js_as_jsx.js create mode 100644 apps/oxlint/src/snapshots/_fixtures__linter__js_and_jsx.js@oxlint.snap create mode 100644 apps/oxlint/src/snapshots/_fixtures__linter__js_as_jsx.js@oxlint.snap diff --git a/apps/oxlint/fixtures/linter/js_as_jsx.js b/apps/oxlint/fixtures/linter/js_as_jsx.js new file mode 100644 index 0000000000000..e130e46d3f36f --- /dev/null +++ b/apps/oxlint/fixtures/linter/js_as_jsx.js @@ -0,0 +1,2 @@ +debugger; +
// Should `.js` file pass as `.jsx`. diff --git a/apps/oxlint/src/lint.rs b/apps/oxlint/src/lint.rs index 8208581bced80..c1cd57f404625 100644 --- a/apps/oxlint/src/lint.rs +++ b/apps/oxlint/src/lint.rs @@ -778,6 +778,12 @@ mod test { Tester::new().test_and_snapshot(args); } + #[test] + fn js_and_jsx() { + let args = &["fixtures/linter/js_as_jsx.js"]; + Tester::new().test_and_snapshot(args); + } + #[test] fn lint_vue_file() { let args = &["fixtures/vue/debugger.vue"]; diff --git a/apps/oxlint/src/snapshots/_-A all fixtures__linter@oxlint.snap b/apps/oxlint/src/snapshots/_-A all fixtures__linter@oxlint.snap index fe6cd3391cc4d..5b9d90b549c4c 100644 --- a/apps/oxlint/src/snapshots/_-A all fixtures__linter@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-A all fixtures__linter@oxlint.snap @@ -6,7 +6,7 @@ arguments: -A all fixtures/linter working directory: ---------- Found 0 warnings and 0 errors. -Finished in ms on 3 files with 0 rules using 1 threads. +Finished in ms on 4 files with 0 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_fixtures__linter@oxlint.snap b/apps/oxlint/src/snapshots/_fixtures__linter@oxlint.snap index ba8feb6dd0a38..45cae04933d7d 100644 --- a/apps/oxlint/src/snapshots/_fixtures__linter@oxlint.snap +++ b/apps/oxlint/src/snapshots/_fixtures__linter@oxlint.snap @@ -20,6 +20,14 @@ working directory: `---- help: Remove the debugger statement + ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\eslint(no-debugger)]8;;\: `debugger` statement is not allowed + ,-[fixtures/linter/js_as_jsx.js:1:1] + 1 | debugger; + : ^^^^^^^^^ + 2 |
// Should `.js` file pass as `.jsx`. + `---- + help: Remove the debugger statement + ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/use-isnan.html\eslint(use-isnan)]8;;\: Requires calls to isNaN() when checking for NaN ,-[fixtures/linter/nan.js:1:8] 1 | 123 == NaN; @@ -27,8 +35,8 @@ working directory: `---- help: Use the isNaN function to compare with NaN. -Found 3 warnings and 0 errors. -Finished in ms on 3 files with 101 rules using 1 threads. +Found 4 warnings and 0 errors. +Finished in ms on 4 files with 101 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_fixtures__linter__js_and_jsx.js@oxlint.snap b/apps/oxlint/src/snapshots/_fixtures__linter__js_and_jsx.js@oxlint.snap new file mode 100644 index 0000000000000..dab8f41f46802 --- /dev/null +++ b/apps/oxlint/src/snapshots/_fixtures__linter__js_and_jsx.js@oxlint.snap @@ -0,0 +1,12 @@ +--- +source: apps/oxlint/src/tester.rs +--- +########## +arguments: fixtures/linter/js_and_jsx.js +working directory: +---------- +Found 0 warnings and 0 errors. +Finished in ms on 0 files with 101 rules using 1 threads. +---------- +CLI result: LintSucceeded +---------- diff --git a/apps/oxlint/src/snapshots/_fixtures__linter__js_as_jsx.js@oxlint.snap b/apps/oxlint/src/snapshots/_fixtures__linter__js_as_jsx.js@oxlint.snap new file mode 100644 index 0000000000000..185506f6ae6da --- /dev/null +++ b/apps/oxlint/src/snapshots/_fixtures__linter__js_as_jsx.js@oxlint.snap @@ -0,0 +1,21 @@ +--- +source: apps/oxlint/src/tester.rs +--- +########## +arguments: fixtures/linter/js_as_jsx.js +working directory: +---------- + + ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\eslint(no-debugger)]8;;\: `debugger` statement is not allowed + ,-[fixtures/linter/js_as_jsx.js:1:1] + 1 | debugger; + : ^^^^^^^^^ + 2 |
// Should `.js` file pass as `.jsx`. + `---- + help: Remove the debugger statement + +Found 1 warning and 0 errors. +Finished in ms on 1 file with 101 rules using 1 threads. +---------- +CLI result: LintSucceeded +---------- diff --git a/crates/oxc_linter/src/service/runtime.rs b/crates/oxc_linter/src/service/runtime.rs index 17cb06ad141a1..60d5857c45a50 100644 --- a/crates/oxc_linter/src/service/runtime.rs +++ b/crates/oxc_linter/src/service/runtime.rs @@ -221,7 +221,12 @@ impl<'l> Runtime<'l> { if not_supported_yet { return None; } - let source_type = source_type.unwrap_or_default(); + + let mut source_type = source_type.unwrap_or_default(); + // Treat JS and JSX files to maximize chance of parsing files. + if source_type.is_javascript() { + source_type = source_type.with_jsx(true); + } let file_result = self.file_system.read_to_string(path).map_err(|e| { Error::new(OxcDiagnostic::error(format!( diff --git a/crates/oxc_span/src/source_type.rs b/crates/oxc_span/src/source_type.rs index 9963f0b02639c..8581dcfda2d37 100644 --- a/crates/oxc_span/src/source_type.rs +++ b/crates/oxc_span/src/source_type.rs @@ -149,8 +149,8 @@ impl From for SourceType { }; let variant = match file_ext { - Js | Mjs | Cjs | Jsx | Tsx => LanguageVariant::Jsx, - Ts | Mts | Cts => LanguageVariant::Standard, + Jsx | Tsx => LanguageVariant::Jsx, + Js | Mjs | Cjs | Ts | Mts | Cts => LanguageVariant::Standard, }; SourceType { language, module_kind, variant } @@ -673,7 +673,7 @@ mod tests { assert!(!ty.is_typescript(), "{ty:?}"); } - assert_eq!(SourceType::jsx(), js); + assert_eq!(SourceType::mjs(), js); assert_eq!(SourceType::jsx().with_module(true), jsx); assert!(js.is_module()); @@ -686,9 +686,9 @@ mod tests { assert!(!cjs.is_strict()); assert!(jsx.is_strict()); - assert!(js.is_jsx()); - assert!(mjs.is_jsx()); - assert!(cjs.is_jsx()); + assert!(js.is_javascript()); + assert!(mjs.is_javascript()); + assert!(cjs.is_javascript()); assert!(jsx.is_jsx()); } } diff --git a/crates/oxc_transformer/src/options/babel/mod.rs b/crates/oxc_transformer/src/options/babel/mod.rs index 4fdce0da86e81..820e074f852c5 100644 --- a/crates/oxc_transformer/src/options/babel/mod.rs +++ b/crates/oxc_transformer/src/options/babel/mod.rs @@ -169,6 +169,9 @@ impl BabelOptions { pub fn is_jsx(&self) -> bool { self.plugins.syntax_jsx + || self.presets.jsx.is_some() + || self.plugins.react_jsx.is_some() + || self.plugins.react_jsx_dev.is_some() } pub fn is_typescript(&self) -> bool { diff --git a/tasks/coverage/snapshots/parser_babel.snap b/tasks/coverage/snapshots/parser_babel.snap index d872581e6ed67..562e5534441d6 100644 --- a/tasks/coverage/snapshots/parser_babel.snap +++ b/tasks/coverage/snapshots/parser_babel.snap @@ -1,9 +1,9 @@ commit: 578ac4df parser_babel Summary: -AST Parsed : 2309/2322 (99.44%) -Positive Passed: 2288/2322 (98.54%) -Negative Passed: 1561/1673 (93.31%) +AST Parsed : 2311/2322 (99.53%) +Positive Passed: 2290/2322 (98.62%) +Negative Passed: 1565/1673 (93.54%) Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/invalid-startindex-and-startline-specified-without-startcolumn/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/startline-and-startcolumn-specified/input.js @@ -48,14 +48,6 @@ Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/fl Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/flow/expect-plugin/export-type/input.js -Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-fragment/input.js - -Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-jsx-expression/input.js - -Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/jsx/errors/_no_plugin/input.js - -Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/jsx/errors/_no_plugin-non-BMP-identifier/input.js - Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-like-in-conditional-2/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/satisfies-const-error/input.ts @@ -353,22 +345,6 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2022 2 │ ╰──── -Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-ts-type-param-no-flow/input.js - - × Unexpected token. Did you mean `{'>'}` or `>`? - ╭─[babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-ts-type-param-no-flow/input.js:1:10] - 1 │
() => {} - · ▲ - ╰──── - -Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-ts-type-param-no-flow-babel-7/input.js - - × Unexpected token. Did you mean `{'>'}` or `>`? - ╭─[babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-ts-type-param-no-flow-babel-7/input.js:1:10] - 1 │
() => {} - · ▲ - ╰──── - Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/arrow-function/generic-tsx-babel-7/input.ts × Unexpected token. Did you mean `{'>'}` or `>`? @@ -3878,9 +3854,9 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc ╰──── × Unexpected token - ╭─[babel/packages/babel-parser/test/fixtures/es2015/modules/invalid-xml-comment-in-module/input.js:1:2] + ╭─[babel/packages/babel-parser/test/fixtures/es2015/modules/invalid-xml-comment-in-module/input.js:1:1] 1 │ - · ─ + · ─ ╰──── × Expected `,` but found `Identifier` @@ -11518,10 +11494,37 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc · ─ ╰──── - × Unexpected token. Did you mean `{'>'}` or `>`? - ╭─[babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-type-param/input.js:1:10] + × Unexpected token + ╭─[babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-fragment/input.js:3:5] + 2 │ return ( + 3 │ <>Hello + · ─ + 4 │ ); + ╰──── + + × Unexpected token + ╭─[babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-jsx-expression/input.js:1:1] + 1 │
{name}
+ · ─ + ╰──── + + × Unexpected token + ╭─[babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-type-param/input.js:1:1] 1 │
() => {} - · ▲ + · ─ + ╰──── + + × Unexpected token + ╭─[babel/packages/babel-parser/test/fixtures/jsx/errors/_no_plugin/input.js:1:1] + 1 │
+ · ─ + ╰──── + + × Unexpected token + ╭─[babel/packages/babel-parser/test/fixtures/jsx/errors/_no_plugin-non-BMP-identifier/input.js:1:1] + 1 │ < + · ─ + 2 │ 𠮷 ╰──── × Unexpected token. Did you mean `{'>'}` or `>`? diff --git a/tasks/coverage/snapshots/parser_typescript.snap b/tasks/coverage/snapshots/parser_typescript.snap index 5161c953cda9f..3a6176bec8527 100644 --- a/tasks/coverage/snapshots/parser_typescript.snap +++ b/tasks/coverage/snapshots/parser_typescript.snap @@ -15414,41 +15414,42 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private help: Did you mean to write 'undefined | null | undefined'? × Unexpected token - ╭─[typescript/tests/cases/compiler/parseJsxElementInUnaryExpressionNoCrash1.ts:1:4] + ╭─[typescript/tests/cases/compiler/parseJsxElementInUnaryExpressionNoCrash1.ts:1:2] 1 │ ~< < - · ─ + · ─ ╰──── × Unexpected token - ╭─[typescript/tests/cases/compiler/parseJsxElementInUnaryExpressionNoCrash2.ts:1:9] + ╭─[typescript/tests/cases/compiler/parseJsxElementInUnaryExpressionNoCrash2.ts:1:2] 1 │ ~<> < + · ─ ╰──── × Unexpected token - ╭─[typescript/tests/cases/compiler/parseJsxElementInUnaryExpressionNoCrash3.ts:1:4] + ╭─[typescript/tests/cases/compiler/parseJsxElementInUnaryExpressionNoCrash3.ts:1:2] 1 │ !< {:> - · ─ + · ─ ╰──── × Unexpected token - ╭─[typescript/tests/cases/compiler/parseUnaryExpressionNoTypeAssertionInJsx1.ts:2:21] + ╭─[typescript/tests/cases/compiler/parseUnaryExpressionNoTypeAssertionInJsx1.ts:2:13] 1 │ const x = "oops"; 2 │ const y = + x; - · ─── + · ─ ╰──── × Unexpected token - ╭─[typescript/tests/cases/compiler/parseUnaryExpressionNoTypeAssertionInJsx2.ts:2:15] + ╭─[typescript/tests/cases/compiler/parseUnaryExpressionNoTypeAssertionInJsx2.ts:2:13] 1 │ const x = "oops"; 2 │ const y = + <> x; - · ─── + · ─ ╰──── × Unexpected token - ╭─[typescript/tests/cases/compiler/parseUnaryExpressionNoTypeAssertionInJsx3.ts:2:14] + ╭─[typescript/tests/cases/compiler/parseUnaryExpressionNoTypeAssertionInJsx3.ts:2:13] 1 │ const x = "oops"; 2 │ const y = + <1234> x; - · ──── + · ─ ╰──── × Unexpected token diff --git a/tasks/coverage/snapshots/semantic_babel.snap b/tasks/coverage/snapshots/semantic_babel.snap index ef8429c3430c5..217d4ece4a480 100644 --- a/tasks/coverage/snapshots/semantic_babel.snap +++ b/tasks/coverage/snapshots/semantic_babel.snap @@ -2,7 +2,7 @@ commit: 578ac4df semantic_babel Summary: AST Parsed : 2322/2322 (100.00%) -Positive Passed: 1905/2322 (82.04%) +Positive Passed: 1907/2322 (82.13%) semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/annex-b/enabled/3.3-function-in-if-body/input.js Symbol scope ID mismatch for "f": after transform: SymbolId(0): ScopeId(4294967294) @@ -236,12 +236,6 @@ Unresolved references mismatch: after transform: ["T", "foo"] rebuilt : ["foo"] -semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-ts-type-param-no-flow/input.js -Unexpected token. Did you mean `{'>'}` or `>`? - -semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/jsx/errors/_no-plugin-ts-type-param-no-flow-babel-7/input.js -Unexpected token. Did you mean `{'>'}` or `>`? - semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/arrow-function/arrow-function-with-newline/input.ts Unresolved references mismatch: after transform: ["arguments", "t"] diff --git a/tasks/prettier_conformance/src/lib.rs b/tasks/prettier_conformance/src/lib.rs index 3dd2a3c5abe6b..f41dfce768c81 100644 --- a/tasks/prettier_conformance/src/lib.rs +++ b/tasks/prettier_conformance/src/lib.rs @@ -411,6 +411,7 @@ impl TestRunner { formatter_options: FormatOptions, ) -> String { let allocator = Allocator::default(); + let source_type = source_type.with_jsx(source_type.is_javascript()); let ret = Parser::new(&allocator, source_text, source_type) .with_options(ParseOptions { preserve_parens: false, diff --git a/tasks/prettier_conformance/src/spec.rs b/tasks/prettier_conformance/src/spec.rs index f17b91f691ac3..68a4c321c9b01 100644 --- a/tasks/prettier_conformance/src/spec.rs +++ b/tasks/prettier_conformance/src/spec.rs @@ -36,7 +36,10 @@ impl SpecParser { self.source_text.clone_from(&spec_content); let allocator = Allocator::default(); - let source_type = SourceType::from_path(spec).unwrap_or_default(); + let mut source_type = SourceType::from_path(spec).unwrap_or_default(); + if source_type.is_javascript() { + source_type = source_type.with_jsx(true); + } let mut ret = Parser::new(&allocator, &spec_content, source_type).parse(); self.visit_program(&mut ret.program); diff --git a/tasks/transform_conformance/src/test_case.rs b/tasks/transform_conformance/src/test_case.rs index d4bad7884d0fe..74716ed078369 100644 --- a/tasks/transform_conformance/src/test_case.rs +++ b/tasks/transform_conformance/src/test_case.rs @@ -90,10 +90,8 @@ impl TestCase { fn source_type(path: &Path, options: &BabelOptions) -> SourceType { // Some babel test cases have a js extension, but contain typescript code. // Therefore, if the typescript plugin exists, enable typescript. - let mut source_type = SourceType::from_path(path) - .unwrap() - .with_script(true) - .with_jsx(options.plugins.syntax_jsx); + let mut source_type = + SourceType::from_path(path).unwrap().with_script(true).with_jsx(options.is_jsx()); source_type = match options.source_type.as_deref() { Some("unambiguous") => source_type.with_unambiguous(true), Some("script") => source_type.with_script(true),