diff --git a/package.json b/package.json index 95e94a23e..bdbc5e2ed 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "json5": "2.2.3", "leven": "4.1.0", "linguist-languages": "9.1.0", - "meriyah": "6.1.4", + "meriyah": "7.0.0", "micromatch": "4.0.8", "minimist": "1.2.8", "n-readlines": "1.0.1", diff --git a/scripts/build/shims/method-is-well-formed.js b/scripts/build/shims/method-is-well-formed.js new file mode 100644 index 000000000..60b76ecf4 --- /dev/null +++ b/scripts/build/shims/method-is-well-formed.js @@ -0,0 +1,42 @@ +import { createMethodShim } from "./shared.js"; + +const LeadSurrogateMin = 0xd800; +const LeadSurrogateMax = 0xdbff; +const TrailSurrogateMin = 0xdc00; + +const stringIsWellFormed = + String.prototype.isWellFormed ?? + /* + This implementation is not verified, should only apply to meriyah package + https://github.com/meriyah/meriyah/pull/527 + */ + function () { + const { length } = this; + for (let index = 0; index < length; index++) { + const code = this.charCodeAt(index); + + // Single UTF-16 unit + if ((code & 0xfc00) !== LeadSurrogateMin) { + continue; + } + + // unpaired surrogate + if ( + code > LeadSurrogateMax || + ++index >= length || + (this.charCodeAt(index) & 0xfc00) !== TrailSurrogateMin + ) { + return false; + } + } + + return true; + }; + +const isWellFormed = createMethodShim("isWellFormed", function () { + if (typeof this === "string") { + return stringIsWellFormed; + } +}); + +export default isWellFormed; diff --git a/scripts/build/transform/index.js b/scripts/build/transform/index.js index 6a9df77d4..07e82694c 100644 --- a/scripts/build/transform/index.js +++ b/scripts/build/transform/index.js @@ -29,16 +29,20 @@ const packageTransforms = new Map([ ], [transforms["object-has-own"], ["@babel/parser", "meriyah"]], [transforms["string-raw"], ["camelcase", "@angular/compiler"]], + [transforms["method-is-well-formed"], ["meriyah"]], /* spell-checker: enable */ ]); const allTransforms = Object.values(transforms); +const sourceTransforms = allTransforms.filter( + (transform) => transform !== transforms["method-is-well-formed"], +); const isPackageFile = (file, packageName) => file.startsWith(path.join(PROJECT_ROOT, `node_modules/${packageName}/`)); function getTransforms(original, file) { if (file.startsWith(SOURCE_DIR)) { - return allTransforms; + return sourceTransforms; } const transforms = []; @@ -52,9 +56,14 @@ function getTransforms(original, file) { } function transform(original, file, buildOptions) { - const transforms = getTransforms(original, file, buildOptions).filter( - (transform) => !transform.shouldSkip(original, file, buildOptions), - ); + const transforms = // For test + ( + buildOptions.__isSyntaxTransformUnitTest + ? allTransforms + : getTransforms(original, file, buildOptions) + ).filter( + (transform) => !transform.shouldSkip(original, file, buildOptions), + ); if (transforms.length === 0) { return original; diff --git a/scripts/build/transform/transforms/index.js b/scripts/build/transform/transforms/index.js index c12059b57..2a08e2e11 100644 --- a/scripts/build/transform/transforms/index.js +++ b/scripts/build/transform/transforms/index.js @@ -10,6 +10,8 @@ export { default as "method-find-last-index" } from "./transform-method-find-las export { default as "method-replace-all" } from "./transform-method-replace-all.js"; // Node.js 20 export { default as "method-to-reversed" } from "./transform-method-to-reversed.js"; +// Node.js 20 +export { default as "method-is-well-formed" } from "./transform-method-is-well-formed.js"; // Node.js 16.9.0 export { default as "object-has-own" } from "./transform-object-has-own.js"; // For performance diff --git a/scripts/build/transform/transforms/transform-method-is-well-formed.js b/scripts/build/transform/transforms/transform-method-is-well-formed.js new file mode 100644 index 000000000..d1053659c --- /dev/null +++ b/scripts/build/transform/transforms/transform-method-is-well-formed.js @@ -0,0 +1,8 @@ +import createMethodCallTransform from "./create-method-call-transform.js"; + +const transformIsWellFormed = createMethodCallTransform({ + methodName: "isWellFormed", + argumentsLength: 0, +}); + +export default transformIsWellFormed; diff --git a/src/language-js/parse/meriyah.js b/src/language-js/parse/meriyah.js index 14a70a114..5af8ae0b9 100644 --- a/src/language-js/parse/meriyah.js +++ b/src/language-js/parse/meriyah.js @@ -9,6 +9,11 @@ import { SOURCE_TYPE_MODULE, } from "./utils/source-types.js"; +/** +@import {ESTree as MeriyahESTree} from "meriyah"; +@import {SOURCE_TYPE_SCRIPT} from "./utils/source-types.js"; +*/ + // https://github.com/meriyah/meriyah/blob/4676f60b6c149d7082bde2c9147f9ae2359c8075/src/parser.ts#L185 const parseOptions = { // Allow module code @@ -25,8 +30,6 @@ const parseOptions = { raw: true, // Enabled directives directives: true, - // Allow return in the global scope - globalReturn: true, // Enable implied strict mode impliedStrict: false, // Enable non-standard parenthesized expression node @@ -37,19 +40,25 @@ const parseOptions = { // source: false, // Enable React JSX parsing jsx: true, - // Creates unique key for in ObjectPattern when key value are same - uniqueKeyInPattern: false, + // Validate regular expressions with runtime, default `true` + validateRegex: false, }; +/** +@param {string} text +@param {SOURCE_TYPE_MODULE | SOURCE_TYPE_SCRIPT} sourceType +*/ function parseWithOptions(text, sourceType) { + /** @type {MeriyahESTree.Comment[]} */ const comments = []; - /** @type {any} */ const ast = meriyahParse(text, { ...parseOptions, - module: sourceType === SOURCE_TYPE_MODULE, + sourceType: sourceType === SOURCE_TYPE_MODULE ? sourceType : "commonjs", onComment: comments, }); + + // @ts-expect-error -- Safe ast.comments = comments; return ast; diff --git a/tests/config/run-format-test.js b/tests/config/run-format-test.js index 9bac63e82..64bf003bf 100644 --- a/tests/config/run-format-test.js +++ b/tests/config/run-format-test.js @@ -39,7 +39,6 @@ const unstableTests = new Map( "flow/hook/hook-type-annotation.js", "typescript/prettier-ignore/mapped-types.ts", "typescript/prettier-ignore/issue-14238.ts", - "js/comments/html-like/comment.js", "js/for/continue-and-break-comment-without-blocks.js", "js/sequence-expression/parenthesized.js", "typescript/satisfies-operators/comments-unstable.ts", @@ -71,13 +70,6 @@ const meriyahDisabledTests = new Set( [ // Parsing to different ASTs "js/decorators/member-expression.js", - // Meriyah parse RegExp relay on runtime behavior - // The following fails on Node.js < 20 - "js/babel-plugins/regex-v-flag.js", - "js/regex/v-flag.js", - "js/regex/d-flag.js", - "js/babel-plugins/regexp-modifiers.js", - "js/regex/regexp-modifiers.js", ].map((file) => path.join(__dirname, "../format", file)), ); const babelTsDisabledTests = new Set( diff --git a/tests/format/js/babel-plugins/__snapshots__/format.test.js.snap b/tests/format/js/babel-plugins/__snapshots__/format.test.js.snap index 8b2d6a5a4..fb1323814 100644 --- a/tests/format/js/babel-plugins/__snapshots__/format.test.js.snap +++ b/tests/format/js/babel-plugins/__snapshots__/format.test.js.snap @@ -2065,7 +2065,7 @@ let newScore = boundScore(0, 100, add(7, double(person.score))); ================================================================================ `; -exports[`pipeline-operator-hack.js [acorn] format 1`] = ` +exports[`pipeline-operator-hack.cjs [acorn] format 1`] = ` "Unexpected token (5:3) 3 | 4 | return list @@ -2077,7 +2077,7 @@ exports[`pipeline-operator-hack.js [acorn] format 1`] = ` Cause: Unexpected token (5:2)" `; -exports[`pipeline-operator-hack.js [espree] format 1`] = ` +exports[`pipeline-operator-hack.cjs [espree] format 1`] = ` "Unexpected token > (5:3) 3 | 4 | return list @@ -2089,7 +2089,7 @@ exports[`pipeline-operator-hack.js [espree] format 1`] = ` Cause: Unexpected token >" `; -exports[`pipeline-operator-hack.js [meriyah] format 1`] = ` +exports[`pipeline-operator-hack.cjs [meriyah] format 1`] = ` "Unexpected token: '>' (5:3) 3 | 4 | return list @@ -2101,7 +2101,7 @@ exports[`pipeline-operator-hack.js [meriyah] format 1`] = ` Cause: [5:2-5:3]: Unexpected token: '>'" `; -exports[`pipeline-operator-hack.js [oxc] format 1`] = ` +exports[`pipeline-operator-hack.cjs [oxc] format 1`] = ` "Unexpected token (5:3) 3 | 4 | return list @@ -2112,7 +2112,7 @@ exports[`pipeline-operator-hack.js [oxc] format 1`] = ` 8 | // (The % token isn't final; it might instead be @ or ? or #.)" `; -exports[`pipeline-operator-hack.js format 1`] = ` +exports[`pipeline-operator-hack.cjs format 1`] = ` ====================================options===================================== parsers: ["babel", "babel-ts", "babel-flow"] printWidth: 80 diff --git a/tests/format/js/babel-plugins/format.test.js b/tests/format/js/babel-plugins/format.test.js index 967ffd903..438e34a0b 100644 --- a/tests/format/js/babel-plugins/format.test.js +++ b/tests/format/js/babel-plugins/format.test.js @@ -15,7 +15,7 @@ runFormatTest(import.meta, ["babel", "babel-ts", "babel-flow"], { "partial-application.js", "pipeline-operator-fsharp.js", "pipeline-operator-minimal.js", - "pipeline-operator-hack.js", + "pipeline-operator-hack.cjs", "throw-expressions.js", "typescript.js", "v8intrinsic.js", @@ -39,7 +39,7 @@ runFormatTest(import.meta, ["babel", "babel-ts", "babel-flow"], { "partial-application.js", "pipeline-operator-fsharp.js", "pipeline-operator-minimal.js", - "pipeline-operator-hack.js", + "pipeline-operator-hack.cjs", "throw-expressions.js", "typescript.js", "v8intrinsic.js", @@ -63,7 +63,7 @@ runFormatTest(import.meta, ["babel", "babel-ts", "babel-flow"], { "partial-application.js", "pipeline-operator-fsharp.js", "pipeline-operator-minimal.js", - "pipeline-operator-hack.js", + "pipeline-operator-hack.cjs", "throw-expressions.js", "typescript.js", "v8intrinsic.js", @@ -71,7 +71,6 @@ runFormatTest(import.meta, ["babel", "babel-ts", "babel-flow"], { "module-blocks.js", "async-do-expressions.js", "explicit-resource-management.js", - "regexp-modifiers.js", "source-phase-imports.js", "deferred-import-evaluation.js", "optional-chaining-assignment.js", @@ -89,7 +88,7 @@ runFormatTest(import.meta, ["babel", "babel-ts", "babel-flow"], { "optional-chaining-assignment.js", "partial-application.js", "pipeline-operator-fsharp.js", - "pipeline-operator-hack.js", + "pipeline-operator-hack.cjs", "pipeline-operator-minimal.js", "throw-expressions.js", "typescript.js", @@ -108,7 +107,7 @@ runFormatTest(import.meta, ["babel", "babel-ts", "babel-flow"], { "optional-chaining-assignment.js", "partial-application.js", "pipeline-operator-fsharp.js", - "pipeline-operator-hack.js", + "pipeline-operator-hack.cjs", "pipeline-operator-minimal.js", "throw-expressions.js", "typescript.js", diff --git a/tests/format/js/babel-plugins/pipeline-operator-hack.js b/tests/format/js/babel-plugins/pipeline-operator-hack.cjs similarity index 100% rename from tests/format/js/babel-plugins/pipeline-operator-hack.js rename to tests/format/js/babel-plugins/pipeline-operator-hack.cjs diff --git a/tests/format/js/comments/html-like/__snapshots__/format.test.js.snap b/tests/format/js/comments/html-like/__snapshots__/format.test.js.snap index e5d82d34e..4c1978476 100644 --- a/tests/format/js/comments/html-like/__snapshots__/format.test.js.snap +++ b/tests/format/js/comments/html-like/__snapshots__/format.test.js.snap @@ -11,7 +11,7 @@ alert(1) --> =====================================output===================================== ================================================================================ diff --git a/tests/format/js/new-target/__snapshots__/format.test.js.snap b/tests/format/js/new-target/__snapshots__/format.test.js.snap index 3aeb1e236..6b45d98b4 100644 --- a/tests/format/js/new-target/__snapshots__/format.test.js.snap +++ b/tests/format/js/new-target/__snapshots__/format.test.js.snap @@ -33,14 +33,6 @@ const x = new.target; ^~~~~~~~~~" `; -exports[`outside-functions.js [meriyah] format 1`] = ` -"new.target only allowed within functions or static blocks (1:15) -> 1 | const x = new.target; - | ^^^^^^ - 2 | -Cause: [1:14-1:20]: new.target only allowed within functions or static blocks" -`; - exports[`outside-functions.js format 1`] = ` ====================================options===================================== parsers: ["babel", "flow", "typescript"] @@ -87,14 +79,6 @@ new.target; ^~~~~~~~~~" `; -exports[`range.js [meriyah] format 1`] = ` -"new.target only allowed within functions or static blocks (2:5) - 1 | const x = -> 2 | new.target; - | ^^^^^^ -Cause: [2:4-2:10]: new.target only allowed within functions or static blocks" -`; - exports[`range.js format 1`] = ` ====================================options===================================== parsers: ["babel", "flow", "typescript"] diff --git a/tests/format/js/new-target/format.test.js b/tests/format/js/new-target/format.test.js index e844ec5ae..d702699de 100644 --- a/tests/format/js/new-target/format.test.js +++ b/tests/format/js/new-target/format.test.js @@ -2,7 +2,6 @@ runFormatTest(import.meta, ["babel", "flow", "typescript"], { errors: { acorn: true, espree: true, - meriyah: true, flow: true, hermes: true, }, diff --git a/tests/format/js/regex/format.test.js b/tests/format/js/regex/format.test.js index 1005e8831..67c251cce 100644 --- a/tests/format/js/regex/format.test.js +++ b/tests/format/js/regex/format.test.js @@ -1,5 +1 @@ -runFormatTest(import.meta, ["babel", "flow", "typescript"], { - errors: { - meriyah: ["regexp-modifiers.js"], - }, -}); +runFormatTest(import.meta, ["babel", "flow", "typescript"]); diff --git a/tests/format/misc/errors/js/export-name/__snapshots__/format.test.js.snap b/tests/format/misc/errors/js/export-name/__snapshots__/format.test.js.snap new file mode 100644 index 000000000..d518688cf --- /dev/null +++ b/tests/format/misc/errors/js/export-name/__snapshots__/format.test.js.snap @@ -0,0 +1,333 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`snippet: #0 [__babel_estree] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:10) +> 1 | import { "\\uD83C" as b } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:9)" +`; + +exports[`snippet: #0 [acorn] format 1`] = ` +"An export name cannot include a lone surrogate. (1:10) +> 1 | import { "\\uD83C" as b } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate. (1:9)" +`; + +exports[`snippet: #0 [babel] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:10) +> 1 | import { "\\uD83C" as b } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:9)" +`; + +exports[`snippet: #0 [babel-flow] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:10) +> 1 | import { "\\uD83C" as b } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:9)" +`; + +exports[`snippet: #0 [babel-ts] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:10) +> 1 | import { "\\uD83C" as b } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:9)" +`; + +exports[`snippet: #0 [espree] format 1`] = ` +"An export name cannot include a lone surrogate. (1:10) +> 1 | import { "\\uD83C" as b } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate." +`; + +exports[`snippet: #0 [espree] format 2`] = ` +"An export name cannot include a lone surrogate. (1:10) +> 1 | import { "\\uD83C" as b } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate." +`; + +exports[`snippet: #0 [flow] format 1`] = ` +"Unexpected string, expected an identifier (1:10) +> 1 | import { "\\uD83C" as b } from "./foo"; + | ^^^^^^^^" +`; + +exports[`snippet: #0 [hermes] format 1`] = ` +"'identifier' expected in import specifier (1:9) +> 1 | import { "\\uD83C" as b } from "./foo"; + | ^ +Cause: 'identifier' expected in import specifier (1:9) +import { "\\uD83C" as b } from "./foo"; + ~~^" +`; + +exports[`snippet: #0 [meriyah] format 1`] = ` +"An export name cannot include a lone surrogate (1:10) +> 1 | import { "\\uD83C" as b } from "./foo"; + | ^^^^^^^^ +Cause: [1:9-1:17]: An export name cannot include a lone surrogate" +`; + +exports[`snippet: #0 [oxc] format 1`] = ` +"An export name cannot include a unicode lone surrogate (1:10) +> 1 | import { "\\uD83C" as b } from "./foo"; + | ^^^^^^^^" +`; + +exports[`snippet: #0 [oxc-ts] format 1`] = ` +"An export name cannot include a unicode lone surrogate (1:10) +> 1 | import { "\\uD83C" as b } from "./foo"; + | ^^^^^^^^" +`; + +exports[`snippet: #1 [__babel_estree] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:10) +> 1 | export { "\\uD83C" } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:9)" +`; + +exports[`snippet: #1 [acorn] format 1`] = ` +"An export name cannot include a lone surrogate. (1:10) +> 1 | export { "\\uD83C" } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate. (1:9)" +`; + +exports[`snippet: #1 [babel] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:10) +> 1 | export { "\\uD83C" } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:9)" +`; + +exports[`snippet: #1 [babel-flow] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:10) +> 1 | export { "\\uD83C" } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:9)" +`; + +exports[`snippet: #1 [babel-ts] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:10) +> 1 | export { "\\uD83C" } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:9)" +`; + +exports[`snippet: #1 [espree] format 1`] = ` +"An export name cannot include a lone surrogate. (1:10) +> 1 | export { "\\uD83C" } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate." +`; + +exports[`snippet: #1 [espree] format 2`] = ` +"An export name cannot include a lone surrogate. (1:10) +> 1 | export { "\\uD83C" } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate." +`; + +exports[`snippet: #1 [flow] format 1`] = ` +"Unexpected string, expected an identifier (1:10) +> 1 | export { "\\uD83C" } from "./foo"; + | ^^^^^^^^" +`; + +exports[`snippet: #1 [hermes] format 1`] = ` +"'identifier' expected in export clause (1:9) +> 1 | export { "\\uD83C" } from "./foo"; + | ^ +Cause: 'identifier' expected in export clause (1:9) +export { "\\uD83C" } from "./foo"; + ~~^" +`; + +exports[`snippet: #1 [meriyah] format 1`] = ` +"An export name cannot include a lone surrogate (1:10) +> 1 | export { "\\uD83C" } from "./foo"; + | ^^^^^^^^ +Cause: [1:9-1:17]: An export name cannot include a lone surrogate" +`; + +exports[`snippet: #1 [oxc] format 1`] = ` +"An export name cannot include a unicode lone surrogate (1:10) +> 1 | export { "\\uD83C" } from "./foo"; + | ^^^^^^^^" +`; + +exports[`snippet: #1 [oxc-ts] format 1`] = ` +"An export name cannot include a unicode lone surrogate (1:10) +> 1 | export { "\\uD83C" } from "./foo"; + | ^^^^^^^^" +`; + +exports[`snippet: #2 [__babel_estree] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:10) +> 1 | export { "\\uD83C" as b } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:9)" +`; + +exports[`snippet: #2 [acorn] format 1`] = ` +"An export name cannot include a lone surrogate. (1:10) +> 1 | export { "\\uD83C" as b } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate. (1:9)" +`; + +exports[`snippet: #2 [babel] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:10) +> 1 | export { "\\uD83C" as b } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:9)" +`; + +exports[`snippet: #2 [babel-flow] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:10) +> 1 | export { "\\uD83C" as b } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:9)" +`; + +exports[`snippet: #2 [babel-ts] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:10) +> 1 | export { "\\uD83C" as b } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:9)" +`; + +exports[`snippet: #2 [espree] format 1`] = ` +"An export name cannot include a lone surrogate. (1:10) +> 1 | export { "\\uD83C" as b } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate." +`; + +exports[`snippet: #2 [espree] format 2`] = ` +"An export name cannot include a lone surrogate. (1:10) +> 1 | export { "\\uD83C" as b } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate." +`; + +exports[`snippet: #2 [flow] format 1`] = ` +"Unexpected string, expected an identifier (1:10) +> 1 | export { "\\uD83C" as b } from "./foo"; + | ^^^^^^^^" +`; + +exports[`snippet: #2 [hermes] format 1`] = ` +"'identifier' expected in export clause (1:9) +> 1 | export { "\\uD83C" as b } from "./foo"; + | ^ +Cause: 'identifier' expected in export clause (1:9) +export { "\\uD83C" as b } from "./foo"; + ~~^" +`; + +exports[`snippet: #2 [meriyah] format 1`] = ` +"An export name cannot include a lone surrogate (1:10) +> 1 | export { "\\uD83C" as b } from "./foo"; + | ^^^^^^^^ +Cause: [1:9-1:17]: An export name cannot include a lone surrogate" +`; + +exports[`snippet: #2 [oxc] format 1`] = ` +"An export name cannot include a unicode lone surrogate (1:10) +> 1 | export { "\\uD83C" as b } from "./foo"; + | ^^^^^^^^" +`; + +exports[`snippet: #2 [oxc-ts] format 1`] = ` +"An export name cannot include a unicode lone surrogate (1:10) +> 1 | export { "\\uD83C" as b } from "./foo"; + | ^^^^^^^^" +`; + +exports[`snippet: #3 [__babel_estree] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:15) +> 1 | export { b as "\\uD83C" } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:14)" +`; + +exports[`snippet: #3 [acorn] format 1`] = ` +"An export name cannot include a lone surrogate. (1:15) +> 1 | export { b as "\\uD83C" } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate. (1:14)" +`; + +exports[`snippet: #3 [babel] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:15) +> 1 | export { b as "\\uD83C" } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:14)" +`; + +exports[`snippet: #3 [babel-flow] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:15) +> 1 | export { b as "\\uD83C" } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:14)" +`; + +exports[`snippet: #3 [babel-ts] format 1`] = ` +"An export name cannot include a lone surrogate, found '\\ud83c'. (1:15) +> 1 | export { b as "\\uD83C" } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate, found '\\ud83c'. (1:14)" +`; + +exports[`snippet: #3 [espree] format 1`] = ` +"An export name cannot include a lone surrogate. (1:15) +> 1 | export { b as "\\uD83C" } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate." +`; + +exports[`snippet: #3 [espree] format 2`] = ` +"An export name cannot include a lone surrogate. (1:15) +> 1 | export { b as "\\uD83C" } from "./foo"; + | ^ +Cause: An export name cannot include a lone surrogate." +`; + +exports[`snippet: #3 [flow] format 1`] = ` +"Unexpected string, expected an identifier (1:15) +> 1 | export { b as "\\uD83C" } from "./foo"; + | ^^^^^^^^" +`; + +exports[`snippet: #3 [hermes] format 1`] = ` +"'identifier' expected in export clause (1:14) +> 1 | export { b as "\\uD83C" } from "./foo"; + | ^ +Cause: 'identifier' expected in export clause (1:14) +export { b as "\\uD83C" } from "./foo"; + ~~~~~~~^" +`; + +exports[`snippet: #3 [meriyah] format 1`] = ` +"An export name cannot include a lone surrogate (1:15) +> 1 | export { b as "\\uD83C" } from "./foo"; + | ^^^^^^^^ +Cause: [1:14-1:22]: An export name cannot include a lone surrogate" +`; + +exports[`snippet: #3 [oxc] format 1`] = ` +"An export name cannot include a unicode lone surrogate (1:15) +> 1 | export { b as "\\uD83C" } from "./foo"; + | ^^^^^^^^" +`; + +exports[`snippet: #3 [oxc-ts] format 1`] = ` +"An export name cannot include a unicode lone surrogate (1:15) +> 1 | export { b as "\\uD83C" } from "./foo"; + | ^^^^^^^^" +`; diff --git a/tests/format/misc/errors/js/export-name/format.test.js b/tests/format/misc/errors/js/export-name/format.test.js new file mode 100644 index 000000000..23e2554a5 --- /dev/null +++ b/tests/format/misc/errors/js/export-name/format.test.js @@ -0,0 +1,26 @@ +runFormatTest( + { + importMeta: import.meta, + snippets: [ + String.raw`import { "\uD83C" as b } from "./foo";`, + String.raw`export { "\uD83C" } from "./foo";`, + String.raw`export { "\uD83C" as b } from "./foo";`, + String.raw`export { b as "\uD83C" } from "./foo";`, + ], + }, + [ + "babel", + "babel-flow", + "babel-ts", + "__babel_estree", + // "typescript", + "flow", + "meriyah", + "acorn", + "espree", + "espree", + "hermes", + "oxc", + "oxc-ts", + ], +); diff --git a/tests/unit/syntax-transform.js b/tests/unit/syntax-transform.js index 7ac716674..91324c077 100644 --- a/tests/unit/syntax-transform.js +++ b/tests/unit/syntax-transform.js @@ -15,6 +15,7 @@ const publicDocPath = url.fileURLToPath( const transform = (code) => "\n" + transformCode(code, file, { + __isSyntaxTransformUnitTest: true, reuseDocModule: true, }) .replaceAll( @@ -173,6 +174,28 @@ test("String.raw", () => { `); }); +test("String#isWellFormed", () => { + expect(transform("foo.isWellFormed()")).toMatchInlineSnapshot(` + " + import __isWellFormed from "/method-is-well-formed.js"; + + __isWellFormed(/* OPTIONAL_OBJECT: false */0, foo); + " + `); + expect(transform("foo?.isWellFormed()")).toMatchInlineSnapshot(` + " + import __isWellFormed from "/method-is-well-formed.js"; + + __isWellFormed(/* OPTIONAL_OBJECT: true */1, foo); + " + `); + expect(transform("foo?.isWellFormed(extraArgument)")).toMatchInlineSnapshot(` + " + foo?.isWellFormed(extraArgument) + " + `); +}); + test("public doc functionality", () => { expect(transform('import {align, line} from "./document/index.js"')) .toMatchInlineSnapshot(` @@ -233,6 +256,7 @@ test("All", () => { b = bar?.findLast(() => true), c = foo.findLastIndex(callback) const d = [a, b, foo].toReversed().join(String.raw\`\${d}\`) + const r = a.isWellFormed() } `), ).toMatchInlineSnapshot(` @@ -242,6 +266,7 @@ test("All", () => { import __findLast from "/method-find-last.js"; import __findLastIndex from "/method-find-last-index.js"; import __toReversed from "/method-to-reversed.js"; + import __isWellFormed from "/method-is-well-formed.js"; import { builders as __doc_builders } from ""; const { @@ -253,6 +278,7 @@ test("All", () => { b = __findLast(/* OPTIONAL_OBJECT: true */1, bar, () => true), c = __findLastIndex(/* OPTIONAL_OBJECT: false */0, foo, callback); const d = __toReversed(/* OPTIONAL_OBJECT: false */0, [a, b, foo]).join(\`\${d}\`); + const r = __isWellFormed(/* OPTIONAL_OBJECT: false */0, a); } " `); diff --git a/yarn.lock b/yarn.lock index 1519ad345..652c52f2d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6638,10 +6638,10 @@ __metadata: languageName: node linkType: hard -"meriyah@npm:6.1.4": - version: 6.1.4 - resolution: "meriyah@npm:6.1.4" - checksum: 10/16b841b9bf7cd6a815864f7054958207cacaa61a4a879477da38210132d30449d83accad822919c4c6f26697e113c9f6ced3620b4c5a1703c315ee183fe0bfc0 +"meriyah@npm:7.0.0": + version: 7.0.0 + resolution: "meriyah@npm:7.0.0" + checksum: 10/fa880d8dab039d9ba4ad4dbc2b6707d0088016fe492fee6e136ec1f157ee001fdaa9c9f1b43a4bbf4fcdce57fa97584d828811c1d436868f57ff072207f6bf10 languageName: node linkType: hard @@ -7568,7 +7568,7 @@ __metadata: linguist-languages: "npm:9.1.0" magic-string: "npm:0.30.21" make-synchronized: "npm:0.8.0" - meriyah: "npm:6.1.4" + meriyah: "npm:7.0.0" micromatch: "npm:4.0.8" minimist: "npm:1.2.8" n-readlines: "npm:1.0.1"