From 35c56e67ae3d19778908835b7b245a7e6b20ea92 Mon Sep 17 00:00:00 2001 From: Ivan Kopeykin Date: Fri, 28 Aug 2020 19:46:56 +0300 Subject: [PATCH 1/4] refactor how asi handled - add asiSafeExpression helper for JavascriptParser - use asiSafe flag in DefinePlugin - try to use (...) instead of Object() --- lib/DefinePlugin.js | 80 +++++++++++++------ lib/RuntimeTemplate.js | 4 +- lib/dependencies/ImportMetaPlugin.js | 6 +- lib/javascript/JavascriptParserHelpers.js | 28 +++++++ lib/optimize/ConcatenatedModule.js | 6 +- test/cases/parsing/asi/a.js | 1 + test/cases/parsing/asi/b.json | 1 + test/cases/parsing/asi/index.js | 15 ++++ test/cases/parsing/asi/warnings.js | 3 + .../plugins/define-plugin/index.js | 11 ++- 10 files changed, 124 insertions(+), 31 deletions(-) create mode 100644 test/cases/parsing/asi/a.js create mode 100644 test/cases/parsing/asi/b.json create mode 100644 test/cases/parsing/asi/index.js create mode 100644 test/cases/parsing/asi/warnings.js diff --git a/lib/DefinePlugin.js b/lib/DefinePlugin.js index 5ea9a8fb1..8ca6a2149 100644 --- a/lib/DefinePlugin.js +++ b/lib/DefinePlugin.js @@ -15,6 +15,7 @@ const { } = require("./javascript/JavascriptParserHelpers"); /** @typedef {import("./Compiler")} Compiler */ +/** @typedef {import("estree").Expression} Expression */ /** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */ /** @typedef {null|undefined|RegExp|Function|string|number|boolean|bigint|undefined} CodeValuePrimitive */ /** @typedef {RecursiveArrayOrRecord} CodeValue */ @@ -39,25 +40,41 @@ class RuntimeValue { } } -const stringifyObj = (obj, parser, ecmaVersion) => { - if (Array.isArray(obj)) { - return ( - "Object([" + - obj.map(code => toCode(code, parser, ecmaVersion)).join(",") + - "])" - ); - } - - return ( - "Object({" + - Object.keys(obj) +/** + * @param {any[]|{[k: string]: any}} obj obj + * @param {JavascriptParser} parser Parser + * @param {number} ecmaVersion EcmaScript version + * @param {boolean|null=} asiSafe asi safe + * @returns {string} code converted to string that evaluates + */ +const stringifyObj = (obj, parser, ecmaVersion, asiSafe) => { + let code; + let arr = Array.isArray(obj); + if (arr) { + code = `[${obj + .map(code => toCode(code, parser, ecmaVersion, null)) + .join(",")}]`; + } else { + code = `{${Object.keys(obj) .map(key => { const code = obj[key]; - return JSON.stringify(key) + ":" + toCode(code, parser, ecmaVersion); + return ( + JSON.stringify(key) + ":" + toCode(code, parser, ecmaVersion, null) + ); }) - .join(",") + - "})" - ); + .join(",")}}`; + } + + switch (asiSafe) { + case null: + return code; + case true: + return arr ? code : `(${code})`; + case false: + return arr ? `;${code}` : `;(${code})`; + default: + return `Object(${code})`; + } }; /** @@ -65,9 +82,10 @@ const stringifyObj = (obj, parser, ecmaVersion) => { * @param {CodeValue} code Code to evaluate * @param {JavascriptParser} parser Parser * @param {number} ecmaVersion EcmaScript version + * @param {boolean|null=} asiSafe asi safe * @returns {string} code converted to string that evaluates */ -const toCode = (code, parser, ecmaVersion) => { +const toCode = (code, parser, ecmaVersion, asiSafe) => { if (code === null) { return "null"; } @@ -78,7 +96,7 @@ const toCode = (code, parser, ecmaVersion) => { return "-0"; } if (code instanceof RuntimeValue) { - return toCode(code.exec(parser), parser, ecmaVersion); + return toCode(code.exec(parser), parser, ecmaVersion, asiSafe); } if (code instanceof RegExp && code.toString) { return code.toString(); @@ -87,7 +105,7 @@ const toCode = (code, parser, ecmaVersion) => { return "(" + code.toString() + ")"; } if (typeof code === "object") { - return stringifyObj(code, parser, ecmaVersion); + return stringifyObj(code, parser, ecmaVersion, asiSafe); } if (typeof code === "bigint") { return ecmaVersion >= 11 ? `${code}n` : `BigInt("${code}")`; @@ -195,14 +213,19 @@ class DefinePlugin { if (recurse) return; recurse = true; const res = parser.evaluate( - toCode(code, parser, ecmaVersion) + toCode(code, parser, ecmaVersion, null) ); recurse = false; res.setRange(expr.range); return res; }); parser.hooks.expression.for(key).tap("DefinePlugin", expr => { - const strCode = toCode(code, parser, ecmaVersion); + const strCode = toCode( + code, + parser, + ecmaVersion, + !parser.isAsiPosition(expr.range[0]) + ); if (/__webpack_require__\s*(!?\.)/.test(strCode)) { return toConstantDependency(parser, strCode, [ RuntimeGlobals.require @@ -228,8 +251,8 @@ class DefinePlugin { if (recurseTypeof) return; recurseTypeof = true; const typeofCode = isTypeof - ? toCode(code, parser, ecmaVersion) - : "typeof (" + toCode(code, parser, ecmaVersion) + ")"; + ? toCode(code, parser, ecmaVersion, null) + : "typeof (" + toCode(code, parser, ecmaVersion, null) + ")"; const res = parser.evaluate(typeofCode); recurseTypeof = false; res.setRange(expr.range); @@ -237,8 +260,8 @@ class DefinePlugin { }); parser.hooks.typeof.for(key).tap("DefinePlugin", expr => { const typeofCode = isTypeof - ? toCode(code, parser, ecmaVersion) - : "typeof (" + toCode(code, parser, ecmaVersion) + ")"; + ? toCode(code, parser, ecmaVersion, null) + : "typeof (" + toCode(code, parser, ecmaVersion, null) + ")"; const res = parser.evaluate(typeofCode); if (!res.isString()) return; return toConstantDependency( @@ -268,7 +291,12 @@ class DefinePlugin { .for(key) .tap("DefinePlugin", evaluateToString("object")); parser.hooks.expression.for(key).tap("DefinePlugin", expr => { - const strCode = stringifyObj(obj, parser, ecmaVersion); + const strCode = stringifyObj( + obj, + parser, + ecmaVersion, + !parser.isAsiPosition(expr.range[0]) + ); if (/__webpack_require__\s*(!?\.)/.test(strCode)) { return toConstantDependency(parser, strCode, [ diff --git a/lib/RuntimeTemplate.js b/lib/RuntimeTemplate.js index 7392f2e8a..b7bd93e1e 100644 --- a/lib/RuntimeTemplate.js +++ b/lib/RuntimeTemplate.js @@ -700,7 +700,7 @@ class RuntimeTemplate { ) ); return `/*#__PURE__*/ ${ - asiSafe ? "" : "Object" + asiSafe ? "" : asiSafe === false ? ";" : "Object" }(${importVar}_namespace_cache || (${importVar}_namespace_cache = ${ RuntimeGlobals.createFakeNamespaceObject }(${importVar}${exportsType === "default-only" ? "" : ", 2"})))`; @@ -724,7 +724,7 @@ class RuntimeTemplate { if (asiSafe) { return `(0,${access})`; } else { - return `Object(${access})`; + return asiSafe === false ? `;(0,${access})` : `Object(${access})`; } } return access; diff --git a/lib/dependencies/ImportMetaPlugin.js b/lib/dependencies/ImportMetaPlugin.js index dc5084789..9ccfbe98b 100644 --- a/lib/dependencies/ImportMetaPlugin.js +++ b/lib/dependencies/ImportMetaPlugin.js @@ -10,6 +10,7 @@ const ModuleDependencyWarning = require("../ModuleDependencyWarning"); const Template = require("../Template"); const BasicEvaluatedExpression = require("../javascript/BasicEvaluatedExpression"); const { + asiSafeExpression, evaluateToIdentifier, toConstantDependency, evaluateToString, @@ -69,7 +70,10 @@ class ImportMetaPlugin { metaProperty.loc ) ); - const dep = new ConstDependency("Object()", metaProperty.range); + const dep = new ConstDependency( + asiSafeExpression(parser, metaProperty, "({})", "Object()"), + metaProperty.range + ); dep.loc = metaProperty.loc; parser.state.module.addPresentationalDependency(dep); return true; diff --git a/lib/javascript/JavascriptParserHelpers.js b/lib/javascript/JavascriptParserHelpers.js index e0ecc6dd0..d8e6608f0 100644 --- a/lib/javascript/JavascriptParserHelpers.js +++ b/lib/javascript/JavascriptParserHelpers.js @@ -10,6 +10,7 @@ const ConstDependency = require("../dependencies/ConstDependency"); const BasicEvaluatedExpression = require("./BasicEvaluatedExpression"); /** @typedef {import("estree").Expression} ExpressionNode */ +/** @typedef {import("estree").Node} Node */ /** @typedef {import("./JavascriptParser")} JavascriptParser */ /** @@ -103,6 +104,33 @@ exports.expressionIsUnsupported = (parser, message) => { }; }; +/** + * @param {JavascriptParser} parser parser + * @param {Node} node node + * @param {string} expression expression + * @param {string|(() => string)} unknownAsiStateExpression unknown asi state expression + * @returns {string} asi safe expression + */ +exports.asiSafeExpression = ( + parser, + node, + expression, + unknownAsiStateExpression +) => { + const asiSafe = !parser.isAsiPosition(node.range[0]); + + switch (asiSafe) { + case true: + return expression; + case false: + return `;${expression}`; + default: + return typeof unknownAsiStateExpression === "string" + ? unknownAsiStateExpression + : unknownAsiStateExpression(); + } +}; + exports.skipTraversal = () => true; exports.approve = () => true; diff --git a/lib/optimize/ConcatenatedModule.js b/lib/optimize/ConcatenatedModule.js index 148b10246..7dd6229cf 100644 --- a/lib/optimize/ConcatenatedModule.js +++ b/lib/optimize/ConcatenatedModule.js @@ -316,7 +316,11 @@ const getExternalImport = ( : Template.toNormalComment(`${exportName.join(".")}`); const reference = `${exprStart}${comment}${propertyAccess(used)}`; if (asCall && callContext === false) { - return asiSafe ? `(0,${reference})` : `Object(${reference})`; + return asiSafe + ? `(0,${reference})` + : asiSafe === false + ? `;(0,${reference})` + : `Object(${reference})`; } return reference; }; diff --git a/test/cases/parsing/asi/a.js b/test/cases/parsing/asi/a.js new file mode 100644 index 000000000..fa1736f9f --- /dev/null +++ b/test/cases/parsing/asi/a.js @@ -0,0 +1 @@ +export function a() {} diff --git a/test/cases/parsing/asi/b.json b/test/cases/parsing/asi/b.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/test/cases/parsing/asi/b.json @@ -0,0 +1 @@ +{} diff --git a/test/cases/parsing/asi/index.js b/test/cases/parsing/asi/index.js new file mode 100644 index 000000000..9fb0f1255 --- /dev/null +++ b/test/cases/parsing/asi/index.js @@ -0,0 +1,15 @@ +import {a as b} from "./a"; +import * as c from "./b"; + +function donotcallme() { + expect("asi unsafe call happened").toBe(false); +} + +it("should respect asi flag", () => { + (donotcallme) + import.meta; + (donotcallme) + b(); + (donotcallme) + c; +}); diff --git a/test/cases/parsing/asi/warnings.js b/test/cases/parsing/asi/warnings.js new file mode 100644 index 000000000..79f938e14 --- /dev/null +++ b/test/cases/parsing/asi/warnings.js @@ -0,0 +1,3 @@ +module.exports = [ + [/Critical dependency: Accessing import\.meta/] +]; diff --git a/test/configCases/plugins/define-plugin/index.js b/test/configCases/plugins/define-plugin/index.js index 7661f6fa6..e3cde2993 100644 --- a/test/configCases/plugins/define-plugin/index.js +++ b/test/configCases/plugins/define-plugin/index.js @@ -1,4 +1,7 @@ -/* globals it, should */ +function donotcallme() { + expect("asi unsafe call happened").toBe(false); +} + it("should define FALSE", function() { expect(FALSE).toBe(false); expect(typeof FALSE).toBe("boolean"); @@ -126,6 +129,10 @@ it("should define OBJECT", function() { expect(o.SUB.FUNCTION(10)).toBe(11); }); it("should define OBJECT.SUB.CODE", function() { + (donotcallme) + OBJECT; + (donotcallme) + OBJECT.SUB; expect(typeof OBJECT.SUB.CODE).toBe("number"); expect(OBJECT.SUB.CODE).toBe(3); if (OBJECT.SUB.CODE !== 3) require("fail"); @@ -148,6 +155,8 @@ it("should define OBJECT.SUB.STRING", function() { })(OBJECT.SUB); }); it("should define ARRAY", function() { + (donotcallme) + ARRAY; expect(Array.isArray(ARRAY)).toBeTruthy(); expect(ARRAY).toHaveLength(2); }); From a3fc25cadb3651993f5cadfcf935d5b035dc2126 Mon Sep 17 00:00:00 2001 From: Ivan Kopeykin Date: Sat, 29 Aug 2020 17:00:03 +0300 Subject: [PATCH 2/4] refactor asiSafe flag in dependencies --- lib/RuntimeTemplate.js | 8 +++++--- .../CommonJsExportRequireDependency.js | 4 +++- .../CommonJsFullRequireDependency.js | 2 +- .../HarmonyImportSpecifierDependency.js | 4 ++-- lib/optimize/ConcatenatedModule.js | 17 ++++++++++++----- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/RuntimeTemplate.js b/lib/RuntimeTemplate.js index b7bd93e1e..edcebef45 100644 --- a/lib/RuntimeTemplate.js +++ b/lib/RuntimeTemplate.js @@ -669,10 +669,12 @@ class RuntimeTemplate { case "dynamic": if (isCall) { return `${importVar}_default()${propertyAccess(exportName, 1)}`; - } else if (asiSafe) { - return `(${importVar}_default()${propertyAccess(exportName, 1)})`; } else { - return `${importVar}_default.a${propertyAccess(exportName, 1)}`; + return asiSafe + ? `(${importVar}_default()${propertyAccess(exportName, 1)})` + : asiSafe === false + ? `;(${importVar}_default()${propertyAccess(exportName, 1)})` + : `${importVar}_default.a${propertyAccess(exportName, 1)}`; } case "default-only": case "default-with-named": diff --git a/lib/dependencies/CommonJsExportRequireDependency.js b/lib/dependencies/CommonJsExportRequireDependency.js index 06d2c83e2..b5cb3bc35 100644 --- a/lib/dependencies/CommonJsExportRequireDependency.js +++ b/lib/dependencies/CommonJsExportRequireDependency.js @@ -37,7 +37,7 @@ class CommonJsExportRequireDependency extends ModuleDependency { this.names = names; this.ids = ids; this.resultUsed = resultUsed; - this.asiSafe = false; + this.asiSafe = undefined; } get type() { @@ -260,6 +260,7 @@ class CommonJsExportRequireDependency extends ModuleDependency { serialize(context) { const { write } = context; + write(this.asiSafe); write(this.range); write(this.valueRange); write(this.base); @@ -271,6 +272,7 @@ class CommonJsExportRequireDependency extends ModuleDependency { deserialize(context) { const { read } = context; + this.asiSafe = read(); this.range = read(); this.valueRange = read(); this.base = read(); diff --git a/lib/dependencies/CommonJsFullRequireDependency.js b/lib/dependencies/CommonJsFullRequireDependency.js index 31f74ce8e..854338edb 100644 --- a/lib/dependencies/CommonJsFullRequireDependency.js +++ b/lib/dependencies/CommonJsFullRequireDependency.js @@ -29,7 +29,7 @@ class CommonJsFullRequireDependency extends ModuleDependency { this.range = range; this.names = names; this.call = false; - this.asiSafe = false; + this.asiSafe = undefined; } /** diff --git a/lib/dependencies/HarmonyImportSpecifierDependency.js b/lib/dependencies/HarmonyImportSpecifierDependency.js index f2123e1bb..e563783ba 100644 --- a/lib/dependencies/HarmonyImportSpecifierDependency.js +++ b/lib/dependencies/HarmonyImportSpecifierDependency.js @@ -36,7 +36,7 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency { this.call = undefined; this.directImport = undefined; this.shorthand = undefined; - this.asiSafe = false; + this.asiSafe = undefined; /** @type {Set | boolean} */ this.usedByExports = undefined; } @@ -272,7 +272,7 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen request: dep.request, exportName: ids, originModule: module, - asiSafe: dep.asiSafe || dep.shorthand, + asiSafe: dep.shorthand ? true : dep.asiSafe, isCall: dep.call, callContext: !dep.directImport, defaultInterop: true, diff --git a/lib/optimize/ConcatenatedModule.js b/lib/optimize/ConcatenatedModule.js index 7dd6229cf..0b252fbef 100644 --- a/lib/optimize/ConcatenatedModule.js +++ b/lib/optimize/ConcatenatedModule.js @@ -241,7 +241,7 @@ const ensureNsObjSource = ( * @param {boolean} asCall asCall * @param {boolean} callContext callContext * @param {boolean} strictHarmonyModule strictHarmonyModule - * @param {boolean} asiSafe asiSafe + * @param {boolean | undefined} asiSafe asiSafe * @returns {string} expression to get value of external module */ const getExternalImport = ( @@ -299,6 +299,8 @@ const getExternalImport = ( ? `${info.interopDefaultAccessName}()` : asiSafe ? `(${info.interopDefaultAccessName}())` + : asiSafe === false + ? `;(${info.interopDefaultAccessName}())` : `${info.interopDefaultAccessName}.a`; exportName = exportName.slice(1); } @@ -411,7 +413,7 @@ const getFinalBinding = ( * @param {boolean} asCall asCall * @param {boolean} callContext callContext * @param {boolean} strictHarmonyModule strictHarmonyModule - * @param {boolean} asiSafe asiSafe + * @param {boolean | undefined} asiSafe asiSafe * @returns {string} the final name */ const getFinalName = ( @@ -585,14 +587,18 @@ const createModuleReference = ({ const callFlag = call ? "_call" : ""; const directImportFlag = directImport ? "_directImport" : ""; const strictFlag = strict ? "_strict" : ""; - const asiSafeFlag = asiSafe ? "_asiSafe" : ""; + const asiSafeFlag = asiSafe + ? "_asiSafe1" + : asiSafe === false + ? "_asiSafe0" + : ""; const exportData = ids ? Buffer.from(JSON.stringify(ids), "utf-8").toString("hex") : "ns"; return `__WEBPACK_MODULE_REFERENCE__${info.index}_${exportData}${callFlag}${directImportFlag}${strictFlag}${asiSafeFlag}__`; }; -const MODULE_REFERENCE_REGEXP = /^__WEBPACK_MODULE_REFERENCE__(\d+)_([\da-f]+|ns)(_call)?(_directImport)?(_strict)?(_asiSafe)?__$/; +const MODULE_REFERENCE_REGEXP = /^__WEBPACK_MODULE_REFERENCE__(\d+)_([\da-f]+|ns)(_call)?(_directImport)?(_strict)?(?:_asiSafe(\d))?__$/; const isModuleReference = name => { return MODULE_REFERENCE_REGEXP.test(name); @@ -602,6 +608,7 @@ const matchModuleReference = (name, modulesWithInfo) => { const match = MODULE_REFERENCE_REGEXP.exec(name); if (!match) return null; const index = +match[1]; + const asiSafe = match[6]; return { index, info: modulesWithInfo[index], @@ -612,7 +619,7 @@ const matchModuleReference = (name, modulesWithInfo) => { call: !!match[3], directImport: !!match[4], strict: !!match[5], - asiSafe: !!match[6] + asiSafe: asiSafe ? asiSafe === "1" : undefined }; }; From efb8211bcf06ef12f45fa54e5654ab3beb8d8ca0 Mon Sep 17 00:00:00 2001 From: Ivan Kopeykin Date: Sat, 29 Aug 2020 17:02:10 +0300 Subject: [PATCH 3/4] refactor: use ternary operator instead of if-else --- lib/RuntimeTemplate.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/RuntimeTemplate.js b/lib/RuntimeTemplate.js index edcebef45..8e91d144c 100644 --- a/lib/RuntimeTemplate.js +++ b/lib/RuntimeTemplate.js @@ -723,11 +723,11 @@ class RuntimeTemplate { : Template.toNormalComment(propertyAccess(exportName)) + " "; const access = `${importVar}${comment}${propertyAccess(used)}`; if (isCall && callContext === false) { - if (asiSafe) { - return `(0,${access})`; - } else { - return asiSafe === false ? `;(0,${access})` : `Object(${access})`; - } + return asiSafe + ? `(0,${access})` + : asiSafe === false + ? `;(0,${access})` + : `Object(${access})`; } return access; } else { From 962199c288f7af9798bf9bbcd90052e5243cc75e Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Tue, 1 Sep 2020 11:06:14 +0200 Subject: [PATCH 4/4] nitpicks --- lib/DefinePlugin.js | 4 ++-- lib/RuntimeTemplate.js | 2 +- lib/dependencies/ImportMetaPlugin.js | 3 +-- lib/javascript/JavascriptParser.js | 4 ++++ lib/javascript/JavascriptParserHelpers.js | 27 ----------------------- types.d.ts | 2 +- 6 files changed, 9 insertions(+), 33 deletions(-) diff --git a/lib/DefinePlugin.js b/lib/DefinePlugin.js index 8ca6a2149..3e78700d8 100644 --- a/lib/DefinePlugin.js +++ b/lib/DefinePlugin.js @@ -44,7 +44,7 @@ class RuntimeValue { * @param {any[]|{[k: string]: any}} obj obj * @param {JavascriptParser} parser Parser * @param {number} ecmaVersion EcmaScript version - * @param {boolean|null=} asiSafe asi safe + * @param {boolean|undefined|null=} asiSafe asi safe (undefined: unknown, null: unneeded) * @returns {string} code converted to string that evaluates */ const stringifyObj = (obj, parser, ecmaVersion, asiSafe) => { @@ -82,7 +82,7 @@ const stringifyObj = (obj, parser, ecmaVersion, asiSafe) => { * @param {CodeValue} code Code to evaluate * @param {JavascriptParser} parser Parser * @param {number} ecmaVersion EcmaScript version - * @param {boolean|null=} asiSafe asi safe + * @param {boolean|undefined|null=} asiSafe asi safe (undefined: unknown, null: unneeded) * @returns {string} code converted to string that evaluates */ const toCode = (code, parser, ecmaVersion, asiSafe) => { diff --git a/lib/RuntimeTemplate.js b/lib/RuntimeTemplate.js index 8e91d144c..a0259653c 100644 --- a/lib/RuntimeTemplate.js +++ b/lib/RuntimeTemplate.js @@ -625,7 +625,7 @@ class RuntimeTemplate { * @param {string} options.request the request * @param {string | string[]} options.exportName the export name * @param {Module} options.originModule the origin module - * @param {boolean} options.asiSafe true, if location is safe for ASI, a bracket can be emitted + * @param {boolean|undefined} options.asiSafe true, if location is safe for ASI, a bracket can be emitted * @param {boolean} options.isCall true, if expression will be called * @param {boolean} options.callContext when false, call context will not be preserved * @param {boolean} options.defaultInterop when true and accessing the default exports, interop code will be generated diff --git a/lib/dependencies/ImportMetaPlugin.js b/lib/dependencies/ImportMetaPlugin.js index 9ccfbe98b..69083185a 100644 --- a/lib/dependencies/ImportMetaPlugin.js +++ b/lib/dependencies/ImportMetaPlugin.js @@ -10,7 +10,6 @@ const ModuleDependencyWarning = require("../ModuleDependencyWarning"); const Template = require("../Template"); const BasicEvaluatedExpression = require("../javascript/BasicEvaluatedExpression"); const { - asiSafeExpression, evaluateToIdentifier, toConstantDependency, evaluateToString, @@ -71,7 +70,7 @@ class ImportMetaPlugin { ) ); const dep = new ConstDependency( - asiSafeExpression(parser, metaProperty, "({})", "Object()"), + `${parser.isAsiPosition(metaProperty.range[0]) ? ";" : ""}({})`, metaProperty.range ); dep.loc = metaProperty.loc; diff --git a/lib/javascript/JavascriptParser.js b/lib/javascript/JavascriptParser.js index d6a5525be..d60bfb34e 100644 --- a/lib/javascript/JavascriptParser.js +++ b/lib/javascript/JavascriptParser.js @@ -3255,6 +3255,10 @@ class JavascriptParser extends Parser { ); } + /** + * @param {number} pos source code position + * @returns {boolean} true when a semicolon has been inserted before this position, false if not + */ isAsiPosition(pos) { if (this.prevStatement === undefined) return false; const currentStatement = this.statementPath[this.statementPath.length - 1]; diff --git a/lib/javascript/JavascriptParserHelpers.js b/lib/javascript/JavascriptParserHelpers.js index d8e6608f0..3a960d69c 100644 --- a/lib/javascript/JavascriptParserHelpers.js +++ b/lib/javascript/JavascriptParserHelpers.js @@ -104,33 +104,6 @@ exports.expressionIsUnsupported = (parser, message) => { }; }; -/** - * @param {JavascriptParser} parser parser - * @param {Node} node node - * @param {string} expression expression - * @param {string|(() => string)} unknownAsiStateExpression unknown asi state expression - * @returns {string} asi safe expression - */ -exports.asiSafeExpression = ( - parser, - node, - expression, - unknownAsiStateExpression -) => { - const asiSafe = !parser.isAsiPosition(node.range[0]); - - switch (asiSafe) { - case true: - return expression; - case false: - return `;${expression}`; - default: - return typeof unknownAsiStateExpression === "string" - ? unknownAsiStateExpression - : unknownAsiStateExpression(); - } -}; - exports.skipTraversal = () => true; exports.approve = () => true; diff --git a/types.d.ts b/types.d.ts index 1492e75f0..77c1c0b50 100644 --- a/types.d.ts +++ b/types.d.ts @@ -3824,7 +3824,7 @@ declare class JavascriptParser extends Parser { parseCalculatedString(expression?: any): any; evaluate(source?: any): BasicEvaluatedExpression; getComments(range?: any): any; - isAsiPosition(pos?: any): any; + isAsiPosition(pos: number): boolean; isStatementLevelExpression(expr?: any): boolean; getTagData(name?: any, tag?: any): any; tagVariable(name?: any, tag?: any, data?: any): void;