Infer source type from filepath (#13772)
This commit is contained in:
1 parent
bf6cf1c96c
commit
6c688b7e28
9 files changed
+130
-27
No files matched your search
@@ -3,15 +3,16 @@ import createError from "../../common/parser-create-error.js";
|
||||
import tryCombinations from "../../utils/try-combinations.js";
|
||||
import createParser from "./utils/create-parser.js";
|
||||
import postprocess from "./postprocess/index.js";
|
||||
import getSourceType from "./utils/get-source-type.js";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
/** @type {import("acorn").Options} */
|
||||
const parseOptions = {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
// sourceType: "module",
|
||||
allowReturnOutsideFunction: true,
|
||||
allowImportExportEverywhere: true,
|
||||
// allowImportExportEverywhere: true,
|
||||
allowSuperOutsideMethod: true,
|
||||
locations: true,
|
||||
ranges: true,
|
||||
@@ -55,6 +56,7 @@ function parseWithOptions(text, sourceType) {
|
||||
const ast = parser.parse(text, {
|
||||
...parseOptions,
|
||||
sourceType,
|
||||
allowImportExportEverywhere: sourceType === "module",
|
||||
onComment: comments,
|
||||
onToken: tokens,
|
||||
});
|
||||
@@ -65,14 +67,15 @@ function parseWithOptions(text, sourceType) {
|
||||
}
|
||||
|
||||
function parse(text, options = {}) {
|
||||
const { result: ast, error: moduleParseError } = tryCombinations(
|
||||
() => parseWithOptions(text, /* sourceType */ "module"),
|
||||
() => parseWithOptions(text, /* sourceType */ "script")
|
||||
const sourceType = getSourceType(options);
|
||||
const combinations = (sourceType ? [sourceType] : ["module", "script"]).map(
|
||||
(sourceType) => () => parseWithOptions(text, sourceType)
|
||||
);
|
||||
|
||||
const { result: ast, error } = tryCombinations(combinations);
|
||||
|
||||
if (!ast) {
|
||||
// throw the error for `module` parsing
|
||||
throw createParseError(moduleParseError);
|
||||
throw createParseError(error);
|
||||
}
|
||||
|
||||
options.originalText = text;
|
||||
|
||||
@@ -6,6 +6,7 @@ import jsonParsers from "../../language-json/parser-json.js";
|
||||
import createParser from "./utils/create-parser.js";
|
||||
import createBabelParseError from "./utils/create-babel-parse-error.js";
|
||||
import postprocess from "./postprocess/index.js";
|
||||
import getSourceType from "./utils/get-source-type.js";
|
||||
|
||||
/**
|
||||
* @typedef {import("@babel/parser").parse | import("@babel/parser").parseExpression} Parse
|
||||
@@ -106,7 +107,8 @@ function createParse(parseMethod, ...optionsCombinations) {
|
||||
}
|
||||
|
||||
let combinations = optionsCombinations;
|
||||
if (opts.__babelSourceType === "script") {
|
||||
const sourceType = opts.__babelSourceType ?? getSourceType(opts);
|
||||
if (sourceType === "script") {
|
||||
combinations = combinations.map((options) => ({
|
||||
...options,
|
||||
sourceType: "script",
|
||||
@@ -143,7 +145,7 @@ function createParse(parseMethod, ...optionsCombinations) {
|
||||
const parseFunction =
|
||||
parseMethod === "parseExpression" ? parseExpression : babelParse;
|
||||
const { result: ast, error } = tryCombinations(
|
||||
...combinations.map(
|
||||
combinations.map(
|
||||
(options) => () => parseWithOptions(parseFunction, text, options)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import createError from "../../common/parser-create-error.js";
|
||||
import tryCombinations from "../../utils/try-combinations.js";
|
||||
import createParser from "./utils/create-parser.js";
|
||||
import postprocess from "./postprocess/index.js";
|
||||
import getSourceType from "./utils/get-source-type.js";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
@@ -38,14 +39,16 @@ function createParseError(error) {
|
||||
function parse(text, options = {}) {
|
||||
const { parse: espreeParse } = require("espree");
|
||||
|
||||
const { result: ast, error: moduleParseError } = tryCombinations(
|
||||
() => espreeParse(text, { ...parseOptions, sourceType: "module" }),
|
||||
() => espreeParse(text, { ...parseOptions, sourceType: "script" })
|
||||
const sourceType = getSourceType(options);
|
||||
const combinations = (sourceType ? [sourceType] : ["module", "script"]).map(
|
||||
(/** @type {"module"|"script"} */ sourceType) => () =>
|
||||
espreeParse(text, { ...parseOptions, sourceType })
|
||||
);
|
||||
|
||||
const { result: ast, error } = tryCombinations(combinations);
|
||||
|
||||
if (!ast) {
|
||||
// throw the error for `module` parsing
|
||||
throw createParseError(moduleParseError);
|
||||
throw createParseError(error);
|
||||
}
|
||||
|
||||
options.originalText = text;
|
||||
|
||||
@@ -2,11 +2,12 @@ import createError from "../../common/parser-create-error.js";
|
||||
import tryCombinations from "../../utils/try-combinations.js";
|
||||
import createParser from "./utils/create-parser.js";
|
||||
import postprocess from "./postprocess/index.js";
|
||||
import getSourceType from "./utils/get-source-type.js";
|
||||
|
||||
// https://github.com/meriyah/meriyah/blob/4676f60b6c149d7082bde2c9147f9ae2359c8075/src/parser.ts#L185
|
||||
const parseOptions = {
|
||||
// Allow module code
|
||||
module: true,
|
||||
// module: true,
|
||||
// Enable stage 3 support (ESNext)
|
||||
next: true,
|
||||
// Enable start and end offsets to each node
|
||||
@@ -39,14 +40,14 @@ const parseOptions = {
|
||||
uniqueKeyInPattern: false,
|
||||
};
|
||||
|
||||
function parseWithOptions(parse, text, module) {
|
||||
function parseWithOptions(parse, text, sourceType) {
|
||||
const comments = [];
|
||||
const tokens = [];
|
||||
|
||||
/** @type {any} */
|
||||
const ast = parse(text, {
|
||||
...parseOptions,
|
||||
module,
|
||||
module: sourceType === "module",
|
||||
onComment: comments,
|
||||
onToken: tokens,
|
||||
});
|
||||
@@ -86,14 +87,15 @@ function createParseError(error) {
|
||||
|
||||
async function parse(text, options = {}) {
|
||||
const { parse } = await import("meriyah");
|
||||
const { result: ast, error: moduleParseError } = tryCombinations(
|
||||
() => parseWithOptions(parse, text, /* module */ true),
|
||||
() => parseWithOptions(parse, text, /* module */ false)
|
||||
|
||||
const sourceType = getSourceType(options);
|
||||
const combinations = (sourceType ? [sourceType] : ["module", "script"]).map(
|
||||
(sourceType) => () => parseWithOptions(parse, text, sourceType)
|
||||
);
|
||||
const { result: ast, error } = tryCombinations(combinations);
|
||||
|
||||
if (!ast) {
|
||||
// Throw the error for `module` parsing
|
||||
throw createParseError(moduleParseError);
|
||||
throw createParseError(error);
|
||||
}
|
||||
|
||||
options.originalText = text;
|
||||
|
||||
@@ -42,16 +42,16 @@ async function parse(text, options = {}) {
|
||||
const { parseWithNodeMaps } = await import(
|
||||
"@typescript-eslint/typescript-estree/dist/parser.js"
|
||||
);
|
||||
const { result, error: firstError } = tryCombinations(
|
||||
const { result, error } = tryCombinations([
|
||||
// Try passing with our best guess first.
|
||||
() => parseWithNodeMaps(textToParse, { ...parseOptions, jsx }),
|
||||
// But if we get it wrong, try the opposite.
|
||||
() => parseWithNodeMaps(textToParse, { ...parseOptions, jsx: !jsx })
|
||||
);
|
||||
() => parseWithNodeMaps(textToParse, { ...parseOptions, jsx: !jsx }),
|
||||
]);
|
||||
|
||||
if (!result) {
|
||||
// Suppose our guess is correct, throw the first error
|
||||
throw createParseError(firstError);
|
||||
throw createParseError(error);
|
||||
}
|
||||
|
||||
options.originalText = text;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/** @returns {"module" | "script" | undefined} */
|
||||
function getSourceType(options) {
|
||||
let { filepath } = options;
|
||||
if (!filepath) {
|
||||
return;
|
||||
}
|
||||
filepath = filepath.toLowerCase();
|
||||
|
||||
if (filepath.endsWith(".cjs")) {
|
||||
return "script";
|
||||
}
|
||||
|
||||
if (filepath.endsWith(".mjs")) {
|
||||
return "module";
|
||||
}
|
||||
}
|
||||
|
||||
export default getSourceType;
|
||||
@@ -1,4 +1,4 @@
|
||||
function tryCombinations(...combinations) {
|
||||
function tryCombinations(combinations) {
|
||||
let firstError;
|
||||
for (const [index, fn] of combinations.entries()) {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`snippet: #0 [acorn] format 1`] = `
|
||||
"'import' and 'export' may appear only with 'sourceType: module' (1:1)
|
||||
> 1 | import "foo"
|
||||
| ^"
|
||||
`;
|
||||
|
||||
exports[`snippet: #0 [espree] format 1`] = `
|
||||
"'import' and 'export' may appear only with 'sourceType: module' (1:1)
|
||||
> 1 | import "foo"
|
||||
| ^"
|
||||
`;
|
||||
|
||||
exports[`snippet: #0 [meriyah] format 1`] = `
|
||||
"The import keyword can only be used with the module goal (1:12)
|
||||
> 1 | import "foo"
|
||||
| ^"
|
||||
`;
|
||||
|
||||
exports[`snippet: #1 [acorn] format 1`] = `
|
||||
"'with' in strict mode (1:1)
|
||||
> 1 | with(foo) {}
|
||||
| ^"
|
||||
`;
|
||||
|
||||
exports[`snippet: #1 [espree] format 1`] = `
|
||||
"'with' in strict mode (1:1)
|
||||
> 1 | with(foo) {}
|
||||
| ^"
|
||||
`;
|
||||
|
||||
exports[`snippet: #1 [meriyah] format 1`] = `
|
||||
"Strict mode code may not include a with statement (1:5)
|
||||
> 1 | with(foo) {}
|
||||
| ^"
|
||||
`;
|
||||
|
||||
exports[`snippet: #2 [acorn] format 1`] = `
|
||||
"Deleting local variable in strict mode (1:1)
|
||||
> 1 | delete foo
|
||||
| ^"
|
||||
`;
|
||||
|
||||
exports[`snippet: #2 [espree] format 1`] = `
|
||||
"Deleting local variable in strict mode (1:1)
|
||||
> 1 | delete foo
|
||||
| ^"
|
||||
`;
|
||||
|
||||
exports[`snippet: #2 [meriyah] format 1`] = `
|
||||
"Calling delete on expression not allowed in strict mode (1:10)
|
||||
> 1 | delete foo
|
||||
| ^"
|
||||
`;
|
||||
@@ -0,0 +1,20 @@
|
||||
run_spec(
|
||||
{
|
||||
importMeta: import.meta,
|
||||
snippets: [
|
||||
{
|
||||
code: 'import "foo"',
|
||||
filename: "script.cjs",
|
||||
},
|
||||
{
|
||||
code: "with(foo) {}",
|
||||
filename: "module.mjs",
|
||||
},
|
||||
{
|
||||
code: "delete foo",
|
||||
filename: "module.mJs",
|
||||
},
|
||||
],
|
||||
},
|
||||
["acorn", "espree", "meriyah"]
|
||||
);
|
||||
Reference in new issue
Block a user