diff --git a/changelog_unreleased/javascript/17882.md b/changelog_unreleased/javascript/17882.md index 4d529a0e6..8ef23474f 100644 --- a/changelog_unreleased/javascript/17882.md +++ b/changelog_unreleased/javascript/17882.md @@ -1,16 +1,40 @@ -#### Avoid break `import()` with long module name (#17882 by @kovsu) +#### Avoid break `{import,require.resolve,require.resolve.paths,import.meta.resolve}()` with long module name (#17882, #17908 by @kovsu & @fisker) ```js // Input -const module = await import("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +const a = require("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +const b = require.resolve("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +const c = require.resolve.paths("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + +const d = await import("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module") +const e = import.meta.resolve("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); // Prettier stable -const module = await import( - "./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +const a = require("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +const b = require.resolve( + "./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module", +); +const c = require.resolve.paths( + "./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module", ); -// Prettier mainconst module = -const module = +const d = await import( + "./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +); +const e = import.meta.resolve( + "./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module", +); + +// Prettier main +const a = require("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +const b = + require.resolve("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +const c = require.resolve + .paths("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + +const d = await import("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +const e = import.meta + .resolve("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); ``` diff --git a/src/language-js/print/call-expression.js b/src/language-js/print/call-expression.js index 7678db258..4a70dd5a9 100644 --- a/src/language-js/print/call-expression.js +++ b/src/language-js/print/call-expression.js @@ -5,6 +5,7 @@ import { hasComment, isCallExpression, isMemberish, + isNodeMatches, isStringLiteral, isTemplateOnItsOwnLine, isTestCall, @@ -99,6 +100,12 @@ function printCallee(path, print) { return print("callee"); } +const moduleImportCallees = [ + "require", + "require.resolve", + "require.resolve.paths", + "import.meta.resolve", +]; function isSimpleModuleImport(path) { const { node } = path; @@ -110,10 +117,12 @@ function isSimpleModuleImport(path) { // `type foo = import("foo")` node.type === "TSImportType" || // `require("foo")` + // `require.resolve("foo")` + // `require.resolve.paths("foo")` + // `import.meta.resolve("foo")` (node.type === "CallExpression" && !node.optional && - node.callee.type === "Identifier" && - node.callee.name === "require") + isNodeMatches(node.callee, moduleImportCallees)) ) ) ) { diff --git a/src/language-js/utils/index.js b/src/language-js/utils/index.js index 69853eb22..f2ac298e7 100644 --- a/src/language-js/utils/index.js +++ b/src/language-js/utils/index.js @@ -1141,3 +1141,4 @@ export { shouldPrintComma, startsWithNoLookaheadToken, }; +export { default as isNodeMatches } from "./is-node-matches.js"; diff --git a/src/language-js/utils/is-node-matches.js b/src/language-js/utils/is-node-matches.js index e7136afc5..ac4e99c71 100644 --- a/src/language-js/utils/is-node-matches.js +++ b/src/language-js/utils/is-node-matches.js @@ -21,16 +21,27 @@ function isNodeMatchesNameOrPath(node, nameOrPath) { } if ( - node.type !== "MemberExpression" || - node.optional || - node.computed || - node.property.type !== "Identifier" || - node.property.name !== name + index === 1 && + node.type === "MetaProperty" && + node.property.type === "Identifier" && + node.property.name === name ) { - return false; + node = node.meta; + continue; } - node = node.object; + if ( + node.type === "MemberExpression" && + !node.optional && + !node.computed && + node.property.type === "Identifier" && + node.property.name === name + ) { + node = node.object; + continue; + } + + return false; } } diff --git a/tests/config/run-format-test.js b/tests/config/run-format-test.js index e98a33e58..20484fbfb 100644 --- a/tests/config/run-format-test.js +++ b/tests/config/run-format-test.js @@ -100,6 +100,13 @@ const flowDisabledTests = new Set( "js/decorators/member-expression.js", ].map((file) => path.join(__dirname, "../format", file)), ); +const typescriptDisabledTests = new Set( + [ + // https://github.com/typescript-eslint/typescript-eslint/issues/11389 + "js/import/long-module-name/import-defer.js", + "js/import/long-module-name/import-source.js", + ].map((file) => path.join(__dirname, "../format", file)), +); const isUnstable = (filename, options) => { const testFunction = unstableTests.get(filename); @@ -317,7 +324,10 @@ function runFormatTest(fixtures, parsers, options) { (currentParser === "oxc-ts" && oxcTsDisabledTests.has(filename)) || (currentParser === "hermes" && hermesDisabledTests.has(filename)) || (currentParser === "flow" && flowDisabledTests.has(filename)) || - (currentParser === "babel-ts" && babelTsDisabledTests.has(filename)) + (currentParser === "babel-ts" && + babelTsDisabledTests.has(filename)) || + (currentParser === "typescript" && + typescriptDisabledTests.has(filename)) ) { continue; } diff --git a/tests/format/js/import/__snapshots__/format.test.js.snap b/tests/format/js/import/__snapshots__/format.test.js.snap index 221ed13fb..0efcf715f 100644 --- a/tests/format/js/import/__snapshots__/format.test.js.snap +++ b/tests/format/js/import/__snapshots__/format.test.js.snap @@ -524,51 +524,6 @@ import { ================================================================================ `; -exports[`log-module-name.js - {"bracketSpacing":false} format 1`] = ` -====================================options===================================== -bracketSpacing: false -parsers: ["babel", "flow", "typescript"] -printWidth: 80 - | printWidth -=====================================input====================================== -import("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module") -import( - // Comment - "./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module" -) - -=====================================output===================================== -import("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); -import( - // Comment - "./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module" -); - -================================================================================ -`; - -exports[`log-module-name.js format 1`] = ` -====================================options===================================== -parsers: ["babel", "flow", "typescript"] -printWidth: 80 - | printWidth -=====================================input====================================== -import("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module") -import( - // Comment - "./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module" -) - -=====================================output===================================== -import("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); -import( - // Comment - "./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module" -); - -================================================================================ -`; - exports[`long-line.js - {"bracketSpacing":false} format 1`] = ` ====================================options===================================== bracketSpacing: false diff --git a/tests/format/js/import/long-module-name/__snapshots__/format.test.js.snap b/tests/format/js/import/long-module-name/__snapshots__/format.test.js.snap new file mode 100644 index 000000000..b0063a9c0 --- /dev/null +++ b/tests/format/js/import/long-module-name/__snapshots__/format.test.js.snap @@ -0,0 +1,178 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`import-defer.js [acorn] format 1`] = ` +"The only valid meta property for import is 'import.meta' (1:8) +> 1 | import.defer("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + | ^ + 2 | import.defer( + 3 | // Comment + 4 | "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +Cause: The only valid meta property for import is 'import.meta' (1:7)" +`; + +exports[`import-defer.js [espree] format 1`] = ` +"The only valid meta property for import is 'import.meta' (1:8) +> 1 | import.defer("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + | ^ + 2 | import.defer( + 3 | // Comment + 4 | "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +Cause: The only valid meta property for import is 'import.meta'" +`; + +exports[`import-defer.js [flow] format 1`] = ` +"Unexpected identifier, expected the identifier \`meta\` (1:8) +> 1 | import.defer("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + | ^^^^^ + 2 | import.defer( + 3 | // Comment + 4 | "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"" +`; + +exports[`import-defer.js [hermes] format 1`] = ` +"'meta' expected in member expression (1:7) +> 1 | import.defer("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + | ^ + 2 | import.defer( + 3 | // Comment + 4 | "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +Cause: 'meta' expected in member expression (1:7) +import.defer("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + ^~~~~ +note: start of member expression (1:0) +import.defer("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +^" +`; + +exports[`import-defer.js [meriyah] format 1`] = ` +"The only valid meta property for import is 'import.meta' (1:8) +> 1 | import.defer("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + | ^^^^^ + 2 | import.defer( + 3 | // Comment + 4 | "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +Cause: [1:7-1:12]: The only valid meta property for import is 'import.meta'" +`; + +exports[`import-defer.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "flow", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +import.defer("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +import.defer( + // Comment + "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +) + +=====================================output===================================== +import.defer("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +import.defer( + // Comment + "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +); + +================================================================================ +`; + +exports[`import-expression.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "flow", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +import("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module") +import( + // Comment + "./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +) +import.meta.resolve("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + +=====================================output===================================== +import("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +import( + // Comment + "./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +); +import.meta + .resolve("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + +================================================================================ +`; + +exports[`import-source.js [acorn] format 1`] = ` +"The only valid meta property for import is 'import.meta' (1:8) +> 1 | import.source("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + | ^ + 2 | import.source( + 3 | // Comment + 4 | "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +Cause: The only valid meta property for import is 'import.meta' (1:7)" +`; + +exports[`import-source.js [espree] format 1`] = ` +"The only valid meta property for import is 'import.meta' (1:8) +> 1 | import.source("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + | ^ + 2 | import.source( + 3 | // Comment + 4 | "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +Cause: The only valid meta property for import is 'import.meta'" +`; + +exports[`import-source.js [flow] format 1`] = ` +"Unexpected identifier, expected the identifier \`meta\` (1:8) +> 1 | import.source("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + | ^^^^^^ + 2 | import.source( + 3 | // Comment + 4 | "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"" +`; + +exports[`import-source.js [hermes] format 1`] = ` +"'meta' expected in member expression (1:7) +> 1 | import.source("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + | ^ + 2 | import.source( + 3 | // Comment + 4 | "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +Cause: 'meta' expected in member expression (1:7) +import.source("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + ^~~~~~ +note: start of member expression (1:0) +import.source("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +^" +`; + +exports[`import-source.js [meriyah] format 1`] = ` +"The only valid meta property for import is 'import.meta' (1:8) +> 1 | import.source("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + | ^^^^^^ + 2 | import.source( + 3 | // Comment + 4 | "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +Cause: [1:7-1:13]: The only valid meta property for import is 'import.meta'" +`; + +exports[`import-source.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "flow", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +import.source("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +import.source( + // Comment + "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +) + +=====================================output===================================== +import.source("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +import.source( + // Comment + "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +); + +================================================================================ +`; diff --git a/tests/format/js/import/long-module-name/format.test.js b/tests/format/js/import/long-module-name/format.test.js new file mode 100644 index 000000000..f4444ee96 --- /dev/null +++ b/tests/format/js/import/long-module-name/format.test.js @@ -0,0 +1,9 @@ +runFormatTest(import.meta, ["babel", "flow", "typescript"], { + errors: { + acorn: ["import-defer.js", "import-source.js"], + espree: ["import-defer.js", "import-source.js"], + meriyah: ["import-defer.js", "import-source.js"], + flow: ["import-defer.js", "import-source.js"], + hermes: ["import-defer.js", "import-source.js"], + }, +}); diff --git a/tests/format/js/import/long-module-name/import-defer.js b/tests/format/js/import/long-module-name/import-defer.js new file mode 100644 index 000000000..c674c2b90 --- /dev/null +++ b/tests/format/js/import/long-module-name/import-defer.js @@ -0,0 +1,5 @@ +import.defer("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +import.defer( + // Comment + "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +) diff --git a/tests/format/js/import/log-module-name.js b/tests/format/js/import/long-module-name/import-expression.js similarity index 65% rename from tests/format/js/import/log-module-name.js rename to tests/format/js/import/long-module-name/import-expression.js index 0be8d19ad..3c27e3a4e 100644 --- a/tests/format/js/import/log-module-name.js +++ b/tests/format/js/import/long-module-name/import-expression.js @@ -3,3 +3,4 @@ import( // Comment "./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module" ) +import.meta.resolve("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); diff --git a/tests/format/js/import/long-module-name/import-source.js b/tests/format/js/import/long-module-name/import-source.js new file mode 100644 index 000000000..c13c96a67 --- /dev/null +++ b/tests/format/js/import/long-module-name/import-source.js @@ -0,0 +1,5 @@ +import.source("./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +import.source( + // Comment + "./a/long/long/long/long/long/long/long/long/long/long/long/long/path/to/module" +) diff --git a/tests/format/js/require/__snapshots__/format.test.js.snap b/tests/format/js/require/__snapshots__/format.test.js.snap index 5b8451af3..2241d3609 100644 --- a/tests/format/js/require/__snapshots__/format.test.js.snap +++ b/tests/format/js/require/__snapshots__/format.test.js.snap @@ -1,5 +1,24 @@ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +exports[`long-module-name.js format 1`] = ` +====================================options===================================== +parsers: ["babel", "flow", "typescript"] +printWidth: 80 + | printWidth +=====================================input====================================== +require("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +require.resolve("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +require.resolve.paths("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + +=====================================output===================================== +require("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +require.resolve("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +require.resolve + .paths("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); + +================================================================================ +`; + exports[`require.js format 1`] = ` ====================================options===================================== parsers: ["babel", "flow", "typescript"] diff --git a/tests/format/js/require/long-module-name.js b/tests/format/js/require/long-module-name.js new file mode 100644 index 000000000..e3effb4fa --- /dev/null +++ b/tests/format/js/require/long-module-name.js @@ -0,0 +1,3 @@ +require("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +require.resolve("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module"); +require.resolve.paths("./a/long/long/long/long/long/long/long/long/long/long/long/path/to/module");