diff --git a/src/language-js/parse/oxc.js b/src/language-js/parse/oxc.js index daad95f6b..79be36b7c 100644 --- a/src/language-js/parse/oxc.js +++ b/src/language-js/parse/oxc.js @@ -5,6 +5,7 @@ import { tryCombinationsSync } from "../../utilities/try-combinations.js"; import postprocess from "./postprocess/index.js"; import createParser from "./utilities/create-parser.js"; import jsxRegexp from "./utilities/jsx-regexp.evaluate.js"; +import { shouldEnableJsx } from "./utilities/jsx-support.js"; import { getSourceType, SOURCE_TYPE_COMBINATIONS, @@ -102,18 +103,24 @@ function parseJs(text, options) { function getLanguageCombinations(text, options) { const filepath = options?.filepath; - if (typeof filepath === "string") { - if (/\.(?:jsx|tsx)$/i.test(filepath)) { - return ["tsx"]; - } - - if (filepath.toLowerCase().endsWith(".d.ts")) { - return ["dts"]; - } + if ( + typeof filepath === "string" && + filepath.toLowerCase().endsWith(".d.ts") + ) { + return ["dts"]; } - const shouldEnableJsx = jsxRegexp.test(text); - return shouldEnableJsx ? ["tsx", "ts", "dts"] : ["ts", "tsx", "dts"]; + const isTsx = shouldEnableJsx(filepath); + + if (isTsx === true) { + return ["tsx"]; + } + + if (isTsx === false) { + return ["ts"]; + } + + return jsxRegexp.test(text) ? ["tsx", "ts", "dts"] : ["ts", "tsx", "dts"]; } function parseTs(text, options) { diff --git a/src/language-js/parse/utilities/jsx-support.js b/src/language-js/parse/utilities/jsx-support.js new file mode 100644 index 000000000..ca60679ee --- /dev/null +++ b/src/language-js/parse/utilities/jsx-support.js @@ -0,0 +1,23 @@ +/** +Follow https://typescript-eslint.io/packages/parser/#jsx +- `.js`, `.mjs`, `.cjs`, `.jsx`, `.tsx` files are always parsed as `{jsx: true}`. +- `.ts`, `.mts`, `.cts` files are always parsed as `{jsx: false}`. +*/ + +function shouldEnableJsx(filepath) { + if (typeof filepath !== "string") { + return; + } + + filepath = filepath.toLowerCase(); + + if (/\.(?:js|mjs|cjs|jsx|tsx)$/.test(filepath)) { + return true; + } + + if (/\.(?:ts|mts|cts)$/.test(filepath)) { + return false; + } +} + +export { shouldEnableJsx }; diff --git a/src/language-js/parse/yuku.js b/src/language-js/parse/yuku.js index 5f4bd752d..e7f05eb12 100644 --- a/src/language-js/parse/yuku.js +++ b/src/language-js/parse/yuku.js @@ -5,6 +5,7 @@ import { tryCombinationsSync } from "../../utilities/try-combinations.js"; import postprocess from "./postprocess/index.js"; import createParser from "./utilities/create-parser.js"; import jsxRegexp from "./utilities/jsx-regexp.evaluate.js"; +import { shouldEnableJsx } from "./utilities/jsx-support.js"; import { getSourceType, SOURCE_TYPE_COMBINATIONS, @@ -86,18 +87,24 @@ function parseJs(text, options) { function getLanguageCombinations(text, options) { const filepath = options?.filepath; - if (typeof filepath === "string") { - if (/\.(?:jsx|tsx)$/i.test(filepath)) { - return ["tsx"]; - } - - if (filepath.toLowerCase().endsWith(".d.ts")) { - return ["dts"]; - } + if ( + typeof filepath === "string" && + filepath.toLowerCase().endsWith(".d.ts") + ) { + return ["dts"]; } - const shouldEnableJsx = jsxRegexp.test(text); - return shouldEnableJsx ? ["tsx", "ts", "dts"] : ["ts", "tsx", "dts"]; + const isTsx = shouldEnableJsx(filepath); + + if (isTsx === true) { + return ["tsx"]; + } + + if (isTsx === false) { + return ["ts"]; + } + + return jsxRegexp.test(text) ? ["tsx", "ts", "dts"] : ["ts", "tsx", "dts"]; } function parseTs(text, options) { diff --git a/tests/format/misc/filename-matters/jsx-file-extension/__snapshots__/format.test.js.snap b/tests/format/misc/filename-matters/jsx-file-extension/__snapshots__/format.test.js.snap new file mode 100644 index 000000000..b4b121eb5 --- /dev/null +++ b/tests/format/misc/filename-matters/jsx-file-extension/__snapshots__/format.test.js.snap @@ -0,0 +1,166 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`snippet: test.cjs format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "acorn", "espree", "flow", "meriyah", "typescript", "hermes", "oxc", "oxc-ts", "yuku", "yuku-ts", "__babel_estree"] + printWidth: 80 (default) | +=====================================input====================================== +
+=====================================output===================================== +; + +================================================================================ +`; + +exports[`snippet: test.cts format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "acorn", "espree", "flow", "meriyah", "typescript", "hermes", "oxc", "oxc-ts", "yuku", "yuku-ts", "__babel_estree"] + printWidth: 80 (default) | +=====================================input====================================== + +=====================================output===================================== +; + +================================================================================ +`; + +exports[`snippet: test.cts format[oxc-ts] 1`] = ` +"Expected \`>\` but found \`/\` (1:6) +> 1 | + | ^" +`; + +exports[`snippet: test.cts format[typescript] 1`] = ` +"'>' expected. (1:6) +> 1 | + | ^ +Cause: '>' expected." +`; + +exports[`snippet: test.cts format[yuku-ts] 1`] = ` +"Expected '>' to close a type assertion, but found '/' (1:6) +> 1 | + | ^" +`; + +exports[`snippet: test.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "acorn", "espree", "flow", "meriyah", "typescript", "hermes", "oxc", "oxc-ts", "yuku", "yuku-ts", "__babel_estree"] + printWidth: 80 (default) | +=====================================input====================================== + +=====================================output===================================== +; + +================================================================================ +`; + +exports[`snippet: test.jsx format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "acorn", "espree", "flow", "meriyah", "typescript", "hermes", "oxc", "oxc-ts", "yuku", "yuku-ts", "__babel_estree"] + printWidth: 80 (default) | +=====================================input====================================== + +=====================================output===================================== +; + +================================================================================ +`; + +exports[`snippet: test.mjs format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "acorn", "espree", "flow", "meriyah", "typescript", "hermes", "oxc", "oxc-ts", "yuku", "yuku-ts", "__babel_estree"] + printWidth: 80 (default) | +=====================================input====================================== + +=====================================output===================================== +; + +================================================================================ +`; + +exports[`snippet: test.mts format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "acorn", "espree", "flow", "meriyah", "typescript", "hermes", "oxc", "oxc-ts", "yuku", "yuku-ts", "__babel_estree"] + printWidth: 80 (default) | +=====================================input====================================== + +=====================================output===================================== +; + +================================================================================ +`; + +exports[`snippet: test.mts format[oxc-ts] 1`] = ` +"Expected \`>\` but found \`/\` (1:6) +> 1 | + | ^" +`; + +exports[`snippet: test.mts format[typescript] 1`] = ` +"'>' expected. (1:6) +> 1 | + | ^ +Cause: '>' expected." +`; + +exports[`snippet: test.mts format[yuku-ts] 1`] = ` +"Expected '>' to close a type assertion, but found '/' (1:6) +> 1 | + | ^" +`; + +exports[`snippet: test.ts format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "acorn", "espree", "flow", "meriyah", "typescript", "hermes", "oxc", "oxc-ts", "yuku", "yuku-ts", "__babel_estree"] + printWidth: 80 (default) | +=====================================input====================================== + +=====================================output===================================== +; + +================================================================================ +`; + +exports[`snippet: test.ts format[oxc-ts] 1`] = ` +"Expected \`>\` but found \`/\` (1:6) +> 1 | + | ^" +`; + +exports[`snippet: test.ts format[typescript] 1`] = ` +"'>' expected. (1:6) +> 1 | + | ^ +Cause: '>' expected." +`; + +exports[`snippet: test.ts format[yuku-ts] 1`] = ` +"Expected '>' to close a type assertion, but found '/' (1:6) +> 1 | + | ^" +`; + +exports[`snippet: test.tsx format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "acorn", "espree", "flow", "meriyah", "typescript", "hermes", "oxc", "oxc-ts", "yuku", "yuku-ts", "__babel_estree"] + printWidth: 80 (default) | +=====================================input====================================== + +=====================================output===================================== +; + +================================================================================ +`; + +exports[`snippet: test.unset-filepath format 1`] = ` +====================================options===================================== +parsers: ["babel", "babel-ts", "acorn", "espree", "flow", "meriyah", "typescript", "hermes", "oxc", "oxc-ts", "yuku", "yuku-ts", "__babel_estree"] + printWidth: 80 (default) | +=====================================input====================================== + +=====================================output===================================== +; + +================================================================================ +`; diff --git a/tests/format/misc/filename-matters/jsx-file-extension/format.test.js b/tests/format/misc/filename-matters/jsx-file-extension/format.test.js new file mode 100644 index 000000000..b10f69d08 --- /dev/null +++ b/tests/format/misc/filename-matters/jsx-file-extension/format.test.js @@ -0,0 +1,45 @@ +runFormatTest( + { + snippets: [ + ".js", + ".cjs", + ".mjs", + ".jsx", + ".ts", + ".mts", + ".cts", + ".tsx", + ".unset-filepath", + ].map((extension) => { + const filename = `test${extension}`; + return { + name: filename, + code: "", + filename, + }; + }), + importMeta: import.meta, + }, + [ + "babel", + "babel-ts", + "acorn", + "espree", + "flow", + "meriyah", + "typescript", + "hermes", + "oxc", + "oxc-ts", + "yuku", + "yuku-ts", + "__babel_estree", + ], + { + errors: { + typescript: ["test.ts", "test.mts", "test.cts"], + "oxc-ts": ["test.ts", "test.mts", "test.cts"], + "yuku-ts": ["test.ts", "test.mts", "test.cts"], + }, + }, +); diff --git a/tests/format/typescript/module/__snapshots__/format.test.js.snap b/tests/format/typescript/module/__snapshots__/format.test.js.snap index 32dcebc5c..3df855ecf 100644 --- a/tests/format/typescript/module/__snapshots__/format.test.js.snap +++ b/tests/format/typescript/module/__snapshots__/format.test.js.snap @@ -142,6 +142,15 @@ exports[`keyword.ts format[babel-ts] 1`] = ` Cause: \`module ... {}\` declarations must have a string name. Use \`namespace ... {}\` instead. (1:0)" `; +exports[`keyword.ts format[oxc-ts] 1`] = ` +"Only ambient modules can use quoted names. (35:24) + 33 | namespace /* module */ X {} + 34 | module /* namespace */ X {} +> 35 | module /* namespace */ "x" {} + | ^^^ + 36 |" +`; + exports[`module-nested.ts format 1`] = ` ====================================options===================================== parsers: ["typescript"] diff --git a/tests/format/typescript/module/format.test.js b/tests/format/typescript/module/format.test.js index 6c2be929d..a922f5ab7 100644 --- a/tests/format/typescript/module/format.test.js +++ b/tests/format/typescript/module/format.test.js @@ -1,3 +1,6 @@ runFormatTest(import.meta, ["typescript"], { - errors: { "babel-ts": ["global.ts", "keyword.ts", "module-nested.ts"] }, + errors: { + "babel-ts": ["global.ts", "keyword.ts", "module-nested.ts"], + "oxc-ts": ["keyword.ts"], + }, }); diff --git a/tests/format/typescript/trailing-comma/__snapshots__/format.test.js.snap b/tests/format/typescript/trailing-comma/__snapshots__/format.test.js.snap index a066069ed..8db9e0b79 100644 --- a/tests/format/typescript/trailing-comma/__snapshots__/format.test.js.snap +++ b/tests/format/typescript/trailing-comma/__snapshots__/format.test.js.snap @@ -84,6 +84,16 @@ const { ================================================================================ `; +exports[`invalid.ts - {"trailingComma":"all"} format[oxc-ts] 1`] = ` +"A rest parameter or binding pattern may not have a trailing comma. (5:10) + 3 | longKeySoThisWillGoOnMultipleLines2, + 4 | longKeySoThisWillGoOnMultipleLines3, +> 5 | ...rest, + | ^ + 6 | } = something; + 7 |" +`; + exports[`invalid.ts - {"trailingComma":"all"} format[yuku-ts] 1`] = ` "Rest element cannot have a trailing comma in object destructuring. (1:7) > 1 | const { @@ -125,6 +135,16 @@ const { ================================================================================ `; +exports[`invalid.ts - {"trailingComma":"es5"} format[oxc-ts] 1`] = ` +"A rest parameter or binding pattern may not have a trailing comma. (5:10) + 3 | longKeySoThisWillGoOnMultipleLines2, + 4 | longKeySoThisWillGoOnMultipleLines3, +> 5 | ...rest, + | ^ + 6 | } = something; + 7 |" +`; + exports[`invalid.ts - {"trailingComma":"es5"} format[yuku-ts] 1`] = ` "Rest element cannot have a trailing comma in object destructuring. (1:7) > 1 | const { @@ -166,6 +186,16 @@ const { ================================================================================ `; +exports[`invalid.ts - {"trailingComma":"none"} format[oxc-ts] 1`] = ` +"A rest parameter or binding pattern may not have a trailing comma. (5:10) + 3 | longKeySoThisWillGoOnMultipleLines2, + 4 | longKeySoThisWillGoOnMultipleLines3, +> 5 | ...rest, + | ^ + 6 | } = something; + 7 |" +`; + exports[`invalid.ts - {"trailingComma":"none"} format[yuku-ts] 1`] = ` "Rest element cannot have a trailing comma in object destructuring. (1:7) > 1 | const { diff --git a/tests/format/typescript/trailing-comma/format.test.js b/tests/format/typescript/trailing-comma/format.test.js index be76b6919..35c6d730e 100644 --- a/tests/format/typescript/trailing-comma/format.test.js +++ b/tests/format/typescript/trailing-comma/format.test.js @@ -1,4 +1,4 @@ -const errors = { "yuku-ts": ["invalid.ts"] }; +const errors = { "oxc-ts": ["invalid.ts"], "yuku-ts": ["invalid.ts"] }; runFormatTest(import.meta, ["typescript"], { trailingComma: "all", errors }); runFormatTest(import.meta, ["typescript"], { trailingComma: "es5", errors });