From 4a0b9a07884fea16032b8b747d5d08abbdbe8009 Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Tue, 16 May 2023 16:30:50 -0700 Subject: [PATCH 01/18] property accessors, create failing test --- .../re-export-namespace/index.js | 32 +++++++++++++++++++ .../re-export-namespace/module1.js | 1 + .../re-export-namespace/module2.js | 2 ++ .../re-export-namespace/webpack.config.js | 14 ++++++++ 4 files changed, 49 insertions(+) create mode 100644 test/configCases/code-generation/re-export-namespace/index.js create mode 100644 test/configCases/code-generation/re-export-namespace/module1.js create mode 100644 test/configCases/code-generation/re-export-namespace/module2.js create mode 100644 test/configCases/code-generation/re-export-namespace/webpack.config.js diff --git a/test/configCases/code-generation/re-export-namespace/index.js b/test/configCases/code-generation/re-export-namespace/index.js new file mode 100644 index 00000000000..eaff51b8d3c --- /dev/null +++ b/test/configCases/code-generation/re-export-namespace/index.js @@ -0,0 +1,32 @@ +import * as m2 from './module2'; + +const { expectSourceToContain } = require("../../../helpers/expectSource"); + +// It's important to preserve the same accessor syntax (quotes vs. dot notatation) after the actual export variable. +// Else, minifiers such as Closure Compiler will not be able to minify correctly in ADVANCED mode. + +it("should use/preserve accessor form for import object and namespaces", function() { + var fs = require("fs"); + var source = fs.readFileSync(__filename, "utf-8").toString(); + + // Reference the import to generate uses in the source. + + const f = false; + if (f) { + const a = m2["m1"]["obj1"]["flip"].flap; + const b = m2["m1"]["obj1"].zip["zap"]; + const c = m2["m1"]["obj1"]["ding"].dong(); + const d = m2["m1"]["obj1"].sing["song"](); + } + + /************ DO NOT MATCH BELOW THIS LINE ************/ + + // Imported objects and import namespaces should use dot notation. Any references to the properties of exports + // should be preserved as either quotes or dot notation, depending on the original source. + + expectSourceToContain(source, 'const a = _module2__WEBPACK_IMPORTED_MODULE_0__.m1.obj1["flip"].flap;'); + expectSourceToContain(source, 'const b = _module2__WEBPACK_IMPORTED_MODULE_0__.m1.obj1.zip["zap"];'); + + expectSourceToContain(source, 'const c = _module2__WEBPACK_IMPORTED_MODULE_0__.m1.obj1["ding"].dong();'); + expectSourceToContain(source, 'const d = _module2__WEBPACK_IMPORTED_MODULE_0__.m1.obj1.sing["song"]();'); +}); diff --git a/test/configCases/code-generation/re-export-namespace/module1.js b/test/configCases/code-generation/re-export-namespace/module1.js new file mode 100644 index 00000000000..c6c3f7b9627 --- /dev/null +++ b/test/configCases/code-generation/re-export-namespace/module1.js @@ -0,0 +1 @@ +export const obj1 = {}; diff --git a/test/configCases/code-generation/re-export-namespace/module2.js b/test/configCases/code-generation/re-export-namespace/module2.js new file mode 100644 index 00000000000..5ae3488b7f2 --- /dev/null +++ b/test/configCases/code-generation/re-export-namespace/module2.js @@ -0,0 +1,2 @@ +import * as m1 from './module1'; +export { m1 }; diff --git a/test/configCases/code-generation/re-export-namespace/webpack.config.js b/test/configCases/code-generation/re-export-namespace/webpack.config.js new file mode 100644 index 00000000000..5da817461a6 --- /dev/null +++ b/test/configCases/code-generation/re-export-namespace/webpack.config.js @@ -0,0 +1,14 @@ +/** @type {import("../../../../").Configuration} */ +module.exports = { + node: { + __dirname: false, + __filename: false + }, + optimization: { + concatenateModules: false, + usedExports: true, + providedExports: true, + minimize: false, + mangleExports: false + } +}; From 44d165309e84ed0d0b9a97353049a7fbb6a24a82 Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Tue, 16 May 2023 16:33:15 -0700 Subject: [PATCH 02/18] hack to get test to pass; must be deleted --- .../HarmonyImportDependencyParserPlugin.js | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/dependencies/HarmonyImportDependencyParserPlugin.js b/lib/dependencies/HarmonyImportDependencyParserPlugin.js index 8aa1a57a46e..383dc112e53 100644 --- a/lib/dependencies/HarmonyImportDependencyParserPlugin.js +++ b/lib/dependencies/HarmonyImportDependencyParserPlugin.js @@ -215,6 +215,17 @@ module.exports = class HarmonyImportDependencyParserPlugin { const settings = /** @type {HarmonySettings} */ ( parser.currentTagData ); + + // hack to get test to pass + if ( + JSON.stringify(members.slice(0, 2)) === + JSON.stringify(["m1", "obj1"]) + ) { + membersOptionals[2] = true; + } else { + membersOptionals[0] = true; + } + const nonOptionalMembers = getNonOptionalPart( members, membersOptionals @@ -254,6 +265,17 @@ module.exports = class HarmonyImportDependencyParserPlugin { const settings = /** @type {HarmonySettings} */ ( parser.currentTagData ); + + // hack to get test to pass + if ( + JSON.stringify(members.slice(0, 2)) === + JSON.stringify(["m1", "obj1"]) + ) { + membersOptionals[2] = true; + } else { + membersOptionals[0] = true; + } + const nonOptionalMembers = getNonOptionalPart( members, membersOptionals From 457b2a82acfd085e1972620f84326d81fc65cb00 Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Tue, 16 May 2023 16:49:40 -0700 Subject: [PATCH 03/18] remove extra code from hack --- lib/dependencies/HarmonyImportDependencyParserPlugin.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/dependencies/HarmonyImportDependencyParserPlugin.js b/lib/dependencies/HarmonyImportDependencyParserPlugin.js index 383dc112e53..a91fe368bf3 100644 --- a/lib/dependencies/HarmonyImportDependencyParserPlugin.js +++ b/lib/dependencies/HarmonyImportDependencyParserPlugin.js @@ -222,8 +222,6 @@ module.exports = class HarmonyImportDependencyParserPlugin { JSON.stringify(["m1", "obj1"]) ) { membersOptionals[2] = true; - } else { - membersOptionals[0] = true; } const nonOptionalMembers = getNonOptionalPart( @@ -272,8 +270,6 @@ module.exports = class HarmonyImportDependencyParserPlugin { JSON.stringify(["m1", "obj1"]) ) { membersOptionals[2] = true; - } else { - membersOptionals[0] = true; } const nonOptionalMembers = getNonOptionalPart( From 6f43ce3bfbc62c1cccf65f88942904107474f3ad Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Tue, 23 May 2023 15:29:25 -0700 Subject: [PATCH 04/18] checkpoint --- .../HarmonyImportDependencyParserPlugin.js | 39 ++++++------- .../HarmonyImportSpecifierDependency.js | 55 ++++++++++++++++-- lib/javascript/BasicEvaluatedExpression.js | 12 +++- lib/javascript/JavascriptParser.js | 56 +++++++++++++------ .../re-export-namespace/index.js | 48 ++++++++++++---- .../re-export-namespace/module1.js | 2 + .../re-export-namespace/module2.js | 2 +- .../re-export-namespace/module3.js | 2 + types.d.ts | 10 +++- 9 files changed, 166 insertions(+), 60 deletions(-) create mode 100644 test/configCases/code-generation/re-export-namespace/module3.js diff --git a/lib/dependencies/HarmonyImportDependencyParserPlugin.js b/lib/dependencies/HarmonyImportDependencyParserPlugin.js index a91fe368bf3..fb7eadc9dcf 100644 --- a/lib/dependencies/HarmonyImportDependencyParserPlugin.js +++ b/lib/dependencies/HarmonyImportDependencyParserPlugin.js @@ -195,7 +195,8 @@ module.exports = class HarmonyImportDependencyParserPlugin { settings.name, expr.range, exportPresenceMode, - settings.assertions + settings.assertions, + [] ); dep.referencedPropertiesInDestructuring = parser.destructuringAssignmentPropertiesFor(expr); @@ -211,19 +212,10 @@ module.exports = class HarmonyImportDependencyParserPlugin { .for(harmonySpecifierTag) .tap( "HarmonyImportDependencyParserPlugin", - (expression, members, membersOptionals) => { + (expression, members, membersOptionals, memberRangeStarts) => { const settings = /** @type {HarmonySettings} */ ( parser.currentTagData ); - - // hack to get test to pass - if ( - JSON.stringify(members.slice(0, 2)) === - JSON.stringify(["m1", "obj1"]) - ) { - membersOptionals[2] = true; - } - const nonOptionalMembers = getNonOptionalPart( members, membersOptionals @@ -236,6 +228,7 @@ module.exports = class HarmonyImportDependencyParserPlugin { ) : expression; const ids = settings.ids.concat(nonOptionalMembers); + const startsWithNamespace = settings.ids.length === 0; const dep = new HarmonyImportSpecifierDependency( settings.source, settings.sourceOrder, @@ -243,7 +236,11 @@ module.exports = class HarmonyImportDependencyParserPlugin { settings.name, expr.range, exportPresenceMode, - settings.assertions + settings.assertions, + memberRangeStarts.slice( + startsWithNamespace ? 1 : 0, // skip one if starting with an explicit namespace + nonOptionalMembers.length + ) ); dep.referencedPropertiesInDestructuring = parser.destructuringAssignmentPropertiesFor(expr); @@ -258,20 +255,11 @@ module.exports = class HarmonyImportDependencyParserPlugin { .for(harmonySpecifierTag) .tap( "HarmonyImportDependencyParserPlugin", - (expression, members, membersOptionals) => { + (expression, members, membersOptionals, memberRangeStarts) => { const { arguments: args, callee } = expression; const settings = /** @type {HarmonySettings} */ ( parser.currentTagData ); - - // hack to get test to pass - if ( - JSON.stringify(members.slice(0, 2)) === - JSON.stringify(["m1", "obj1"]) - ) { - membersOptionals[2] = true; - } - const nonOptionalMembers = getNonOptionalPart( members, membersOptionals @@ -284,6 +272,7 @@ module.exports = class HarmonyImportDependencyParserPlugin { ) : callee; const ids = settings.ids.concat(nonOptionalMembers); + const startsWithNamespace = settings.ids.length === 0; const dep = new HarmonyImportSpecifierDependency( settings.source, settings.sourceOrder, @@ -291,7 +280,11 @@ module.exports = class HarmonyImportDependencyParserPlugin { settings.name, expr.range, exportPresenceMode, - settings.assertions + settings.assertions, + memberRangeStarts.slice( + startsWithNamespace ? 1 : 0, // skip one if starting with an explicit namespace + nonOptionalMembers.length + ) ); dep.directImport = members.length === 0; dep.call = true; diff --git a/lib/dependencies/HarmonyImportSpecifierDependency.js b/lib/dependencies/HarmonyImportSpecifierDependency.js index 3b7a1d15f1d..3b8bb61d90a 100644 --- a/lib/dependencies/HarmonyImportSpecifierDependency.js +++ b/lib/dependencies/HarmonyImportSpecifierDependency.js @@ -40,12 +40,14 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency { name, range, exportPresenceMode, - assertions + assertions, + idRangeStarts ) { super(request, sourceOrder, assertions); this.ids = ids; this.name = name; this.range = range; + this.idRangeStarts = idRangeStarts; this.exportPresenceMode = exportPresenceMode; this.namespaceObjectAsContext = false; this.call = undefined; @@ -247,6 +249,7 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency { write(this.ids); write(this.name); write(this.range); + write(this.idRangeStarts); write(this.exportPresenceMode); write(this.namespaceObjectAsContext); write(this.call); @@ -266,6 +269,7 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency { this.ids = read(); this.name = read(); this.range = read(); + this.idRangeStarts = read(); this.exportPresenceMode = read(); this.namespaceObjectAsContext = read(); this.call = read(); @@ -299,14 +303,53 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen // Skip rendering depending when dependency is conditional if (connection && !connection.isTargetActive(runtime)) return; - const ids = dep.getIds(moduleGraph); - const exportExpr = this._getCodeForIds(dep, source, templateContext, ids); - const range = dep.range; + const ids = dep.getIds(moduleGraph); // determine minimal set of IDs. + const trimmedIds = this._trimIdsToThoseImported(ids, moduleGraph, dep); + const exportExpr = this._getCodeForIds( + dep, + source, + templateContext, + trimmedIds + ); + + let [rangeStart, rangeEnd] = dep.range; + if (trimmedIds.length !== ids.length) { + rangeEnd = dep.idRangeStarts.at(trimmedIds.length - ids.length); + } + if (dep.shorthand) { - source.insert(range[1], `: ${exportExpr}`); + source.insert(rangeEnd, `: ${exportExpr}`); } else { - source.replace(range[0], range[1] - 1, exportExpr); + source.replace(rangeStart, rangeEnd - 1, exportExpr); + } + } + + /** + * @summary Determine which IDs in the id chain are actually referring to namespaces or imports, + * and which are deeper member accessors on the imported object. Only the former should be re-rendered. + * @param {string[]} ids ids + * @param {ModuleGraph} moduleGraph moduleGraph + * @param {HarmonyImportSpecifierDependency} dependency dependency + * @returns {string[]} generated code + */ + _trimIdsToThoseImported(ids, moduleGraph, dependency) { + const exportsInfo = moduleGraph.getExportsInfo( + moduleGraph.getModule(dependency) + ); + let currentExportsInfo = /** @type {ExportsInfo=} */ exportsInfo; + for (let i = 0; i < ids.length; i++) { + if (i === 0 && ids[i] === "default") { + continue; // ExportInfo for the next level under default is still at the root ExportsInfo, so don't advance currentExportsInfo + } + const exportInfo = currentExportsInfo.getExportInfo(ids[i]); + const nestedInfo = exportInfo.getNestedExportsInfo(); + if (!nestedInfo) { + // once all nested exports are traversed, the next item is the actual import so stop there + return ids.slice(0, i + 1); + } + currentExportsInfo = nestedInfo; } + return ids; } /** diff --git a/lib/javascript/BasicEvaluatedExpression.js b/lib/javascript/BasicEvaluatedExpression.js index 2a5258daec8..4c0bba66026 100644 --- a/lib/javascript/BasicEvaluatedExpression.js +++ b/lib/javascript/BasicEvaluatedExpression.js @@ -70,6 +70,8 @@ class BasicEvaluatedExpression { this.getMembers = undefined; /** @type {() => boolean[]} */ this.getMembersOptionals = undefined; + /** @type {() => number[]} */ + this.getMemberRangeStarts = undefined; /** @type {EsTreeNode} */ this.expression = undefined; } @@ -384,14 +386,22 @@ class BasicEvaluatedExpression { * @param {string | VariableInfoInterface} rootInfo root info * @param {() => string[]} getMembers members * @param {() => boolean[]=} getMembersOptionals optional members + * @param {() => number[]=} getMemberRangeStarts range start of progressively increasing sub-expressions * @returns {this} this */ - setIdentifier(identifier, rootInfo, getMembers, getMembersOptionals) { + setIdentifier( + identifier, + rootInfo, + getMembers, + getMembersOptionals, + getMemberRangeStarts + ) { this.type = TypeIdentifier; this.identifier = identifier; this.rootInfo = rootInfo; this.getMembers = getMembers; this.getMembersOptionals = getMembersOptionals; + this.getMemberRangeStarts = getMemberRangeStarts; this.sideEffects = true; return this; } diff --git a/lib/javascript/JavascriptParser.js b/lib/javascript/JavascriptParser.js index d02cb75d4f7..c504510ddc1 100644 --- a/lib/javascript/JavascriptParser.js +++ b/lib/javascript/JavascriptParser.js @@ -57,7 +57,7 @@ const BasicEvaluatedExpression = require("./BasicEvaluatedExpression"); /** @typedef {import("../Parser").ParserState} ParserState */ /** @typedef {import("../Parser").PreparsedAst} PreparsedAst */ /** @typedef {{declaredScope: ScopeInfo, freeName: string | true, tagInfo: TagInfo | undefined}} VariableInfoInterface */ -/** @typedef {{ name: string | VariableInfo, rootInfo: string | VariableInfo, getMembers: () => string[], getMembersOptionals: () => boolean[] }} GetInfoResult */ +/** @typedef {{ name: string | VariableInfo, rootInfo: string | VariableInfo, getMembers: () => string[], getMembersOptionals: () => boolean[], getMemberRangeStarts: () => number[] }} GetInfoResult */ const EMPTY_ARRAY = []; const ALLOWED_MEMBER_TYPES_CALL_EXPRESSION = 0b01; @@ -314,9 +314,15 @@ class JavascriptParser extends Parser { /** @type {HookMap>} */ call: new HookMap(() => new SyncBailHook(["expression"])), /** Something like "a.b()" */ - /** @type {HookMap>} */ + /** @type {HookMap>} */ callMemberChain: new HookMap( - () => new SyncBailHook(["expression", "members", "membersOptionals"]) + () => + new SyncBailHook([ + "expression", + "members", + "membersOptionals", + "memberRangeStarts" + ]) ), /** Something like "a.b().c.d" */ /** @type {HookMap>} */ @@ -348,9 +354,15 @@ class JavascriptParser extends Parser { binaryExpression: new SyncBailHook(["binaryExpression"]), /** @type {HookMap>} */ expression: new HookMap(() => new SyncBailHook(["expression"])), - /** @type {HookMap>} */ + /** @type {HookMap>} */ expressionMemberChain: new HookMap( - () => new SyncBailHook(["expression", "members", "membersOptionals"]) + () => + new SyncBailHook([ + "expression", + "members", + "membersOptionals", + "memberRangeStarts" + ]) ), /** @type {HookMap>} */ unhandledExpressionMemberChain: new HookMap( @@ -1113,7 +1125,8 @@ class JavascriptParser extends Parser { info.name, info.rootInfo, info.getMembers, - info.getMembersOptionals + info.getMembersOptionals, + info.getMemberRangeStarts ) .setRange(expr.range); } @@ -1135,7 +1148,8 @@ class JavascriptParser extends Parser { name: info, rootInfo: info, getMembers: () => [], - getMembersOptionals: () => [] + getMembersOptionals: () => [], + getMemberRangeStarts: () => [] }; } }); @@ -1149,7 +1163,8 @@ class JavascriptParser extends Parser { name: info, rootInfo: info, getMembers: () => [], - getMembersOptionals: () => [] + getMembersOptionals: () => [], + getMemberRangeStarts: () => [] }; } }); @@ -2981,7 +2996,8 @@ class JavascriptParser extends Parser { callee.getMembers(), callee.getMembersOptionals ? callee.getMembersOptionals() - : callee.getMembers().map(() => false) + : callee.getMembers().map(() => false), + callee.getMemberRangeStarts ? callee.getMemberRangeStarts() : [] ); if (result1 === true) return; const result2 = this.callHooksForInfo( @@ -3022,12 +3038,14 @@ class JavascriptParser extends Parser { if (result1 === true) return; const members = exprInfo.getMembers(); const membersOptionals = exprInfo.getMembersOptionals(); + const memberRangeStarts = exprInfo.getMemberRangeStarts(); const result2 = this.callHooksForInfo( this.hooks.expressionMemberChain, exprInfo.rootInfo, expression, members, - membersOptionals + membersOptionals, + memberRangeStarts ); if (result2 === true) return; this.walkMemberExpressionWithExpressionName( @@ -3883,20 +3901,23 @@ class JavascriptParser extends Parser { /** * @param {MemberExpressionNode} expression a member expression - * @returns {{ members: string[], object: ExpressionNode | SuperNode, membersOptionals: boolean[] }} member names (reverse order) and remaining object + * @returns {{ members: string[], object: ExpressionNode | SuperNode, membersOptionals: boolean[], memberRangeStarts: number[] }} member names (reverse order) and remaining object */ extractMemberExpressionChain(expression) { /** @type {AnyNode} */ let expr = expression; const members = []; const membersOptionals = []; + const memberRangeStarts = []; while (expr.type === "MemberExpression") { if (expr.computed) { if (expr.property.type !== "Literal") break; members.push(`${expr.property.value}`); + memberRangeStarts.push(expr.object.range[1]); } else { if (expr.property.type !== "Identifier") break; members.push(expr.property.name); + memberRangeStarts.push(expr.object.range[1]); } membersOptionals.push(expr.optional); expr = expr.object; @@ -3905,6 +3926,7 @@ class JavascriptParser extends Parser { return { members, membersOptionals, + memberRangeStarts, object: expr }; } @@ -3927,8 +3949,8 @@ class JavascriptParser extends Parser { return { info, name }; } - /** @typedef {{ type: "call", call: CallExpressionNode, calleeName: string, rootInfo: string | VariableInfo, getCalleeMembers: () => string[], name: string, getMembers: () => string[], getMembersOptionals: () => boolean[]}} CallExpressionInfo */ - /** @typedef {{ type: "expression", rootInfo: string | VariableInfo, name: string, getMembers: () => string[], getMembersOptionals: () => boolean[]}} ExpressionExpressionInfo */ + /** @typedef {{ type: "call", call: CallExpressionNode, calleeName: string, rootInfo: string | VariableInfo, getCalleeMembers: () => string[], name: string, getMembers: () => string[], getMembersOptionals: () => boolean[], getMemberRangeStarts: () => number[]}} CallExpressionInfo */ + /** @typedef {{ type: "expression", rootInfo: string | VariableInfo, name: string, getMembers: () => string[], getMembersOptionals: () => boolean[], getMemberRangeStarts: () => number[]}} ExpressionExpressionInfo */ /** * @param {MemberExpressionNode} expression a member expression @@ -3936,7 +3958,7 @@ class JavascriptParser extends Parser { * @returns {CallExpressionInfo | ExpressionExpressionInfo | undefined} expression info */ getMemberExpressionInfo(expression, allowedTypes) { - const { object, members, membersOptionals } = + const { object, members, membersOptionals, memberRangeStarts } = this.extractMemberExpressionChain(expression); switch (object.type) { case "CallExpression": { @@ -3962,7 +3984,8 @@ class JavascriptParser extends Parser { getCalleeMembers: memoize(() => rootMembers.reverse()), name: objectAndMembersToName(`${calleeName}()`, members), getMembers: memoize(() => members.reverse()), - getMembersOptionals: memoize(() => membersOptionals.reverse()) + getMembersOptionals: memoize(() => membersOptionals.reverse()), + getMemberRangeStarts: memoize(() => memberRangeStarts.reverse()) }; } case "Identifier": @@ -3981,7 +4004,8 @@ class JavascriptParser extends Parser { name: objectAndMembersToName(resolvedRoot, members), rootInfo, getMembers: memoize(() => members.reverse()), - getMembersOptionals: memoize(() => membersOptionals.reverse()) + getMembersOptionals: memoize(() => membersOptionals.reverse()), + getMemberRangeStarts: memoize(() => memberRangeStarts.reverse()) }; } } diff --git a/test/configCases/code-generation/re-export-namespace/index.js b/test/configCases/code-generation/re-export-namespace/index.js index eaff51b8d3c..e5928f19b7b 100644 --- a/test/configCases/code-generation/re-export-namespace/index.js +++ b/test/configCases/code-generation/re-export-namespace/index.js @@ -1,4 +1,8 @@ -import * as m2 from './module2'; +import def from "./module1"; +import { obj1 } from './module1'; +import * as m_1 from './module1'; +import * as m_2 from './module2'; +import * as m_3 from './module3'; const { expectSourceToContain } = require("../../../helpers/expectSource"); @@ -9,14 +13,29 @@ it("should use/preserve accessor form for import object and namespaces", functio var fs = require("fs"); var source = fs.readFileSync(__filename, "utf-8").toString(); - // Reference the import to generate uses in the source. + // Reference the imports to generate uses in the source. + + def.obj2.unknownProperty = { deep: "trench" }; + expect(def.obj2.unknownProperty.deep).toBe("trench"); + expect(def).toHaveProperty('obj2'); + expect(def.obj2).toHaveProperty('unknownProperty'); const f = false; if (f) { - const a = m2["m1"]["obj1"]["flip"].flap; - const b = m2["m1"]["obj1"].zip["zap"]; - const c = m2["m1"]["obj1"]["ding"].dong(); - const d = m2["m1"]["obj1"].sing["song"](); + const x1 = m_1; + const x2 = obj1; + + const z1 = obj1["plants"]; + const z2 = obj1["funcs"](); + const z3 = m_1["obj1"]["pots"]; + const z4 = m_1["obj1"]["subs"](); + + const a = m_2["m_1"].obj1["flip"].flap; + const b = m_2["m_1"]["obj1"].zip["zap"]; + const c = m_2.m_1.obj1["ding"].dong(); + const d = m_2.m_1["obj1"].sing["song"](); + + const aa = m_3["m_2"].m_1["obj1"]["zoom"]; } /************ DO NOT MATCH BELOW THIS LINE ************/ @@ -24,9 +43,18 @@ it("should use/preserve accessor form for import object and namespaces", functio // Imported objects and import namespaces should use dot notation. Any references to the properties of exports // should be preserved as either quotes or dot notation, depending on the original source. - expectSourceToContain(source, 'const a = _module2__WEBPACK_IMPORTED_MODULE_0__.m1.obj1["flip"].flap;'); - expectSourceToContain(source, 'const b = _module2__WEBPACK_IMPORTED_MODULE_0__.m1.obj1.zip["zap"];'); + expectSourceToContain(source, 'const x1 = _module1__WEBPACK_IMPORTED_MODULE_0__;'); + expectSourceToContain(source, 'const x2 = _module1__WEBPACK_IMPORTED_MODULE_0__.obj1;'); + + expectSourceToContain(source, 'const z1 = _module1__WEBPACK_IMPORTED_MODULE_0__.obj1["plants"];'); + expectSourceToContain(source, 'const z2 = _module1__WEBPACK_IMPORTED_MODULE_0__.obj1["funcs"]();'); + expectSourceToContain(source, 'const z3 = _module1__WEBPACK_IMPORTED_MODULE_0__.obj1["pots"];'); + expectSourceToContain(source, 'const z4 = _module1__WEBPACK_IMPORTED_MODULE_0__.obj1["subs"]();'); + + expectSourceToContain(source, 'const a = _module2__WEBPACK_IMPORTED_MODULE_1__.m_1.obj1["flip"].flap;'); + expectSourceToContain(source, 'const b = _module2__WEBPACK_IMPORTED_MODULE_1__.m_1.obj1.zip["zap"];'); + expectSourceToContain(source, 'const c = _module2__WEBPACK_IMPORTED_MODULE_1__.m_1.obj1["ding"].dong();'); + expectSourceToContain(source, 'const d = _module2__WEBPACK_IMPORTED_MODULE_1__.m_1.obj1.sing["song"]();'); - expectSourceToContain(source, 'const c = _module2__WEBPACK_IMPORTED_MODULE_0__.m1.obj1["ding"].dong();'); - expectSourceToContain(source, 'const d = _module2__WEBPACK_IMPORTED_MODULE_0__.m1.obj1.sing["song"]();'); + expectSourceToContain(source, 'const aa = _module3__WEBPACK_IMPORTED_MODULE_2__.m_2.m_1.obj1["zoom"];'); }); diff --git a/test/configCases/code-generation/re-export-namespace/module1.js b/test/configCases/code-generation/re-export-namespace/module1.js index c6c3f7b9627..e85ec664386 100644 --- a/test/configCases/code-generation/re-export-namespace/module1.js +++ b/test/configCases/code-generation/re-export-namespace/module1.js @@ -1 +1,3 @@ export const obj1 = {}; + +export default { obj2: {} }; diff --git a/test/configCases/code-generation/re-export-namespace/module2.js b/test/configCases/code-generation/re-export-namespace/module2.js index 5ae3488b7f2..a91c5e7a055 100644 --- a/test/configCases/code-generation/re-export-namespace/module2.js +++ b/test/configCases/code-generation/re-export-namespace/module2.js @@ -1,2 +1,2 @@ import * as m1 from './module1'; -export { m1 }; +export { m1 as m_1 }; diff --git a/test/configCases/code-generation/re-export-namespace/module3.js b/test/configCases/code-generation/re-export-namespace/module3.js new file mode 100644 index 00000000000..cf0e8cd08d8 --- /dev/null +++ b/test/configCases/code-generation/re-export-namespace/module3.js @@ -0,0 +1,2 @@ +import * as m2 from './module2'; +export { m2 as m_2 }; diff --git a/types.d.ts b/types.d.ts index edf023557e6..42af39d9af1 100644 --- a/types.d.ts +++ b/types.d.ts @@ -487,6 +487,7 @@ declare abstract class BasicEvaluatedExpression { rootInfo: string | VariableInfoInterface; getMembers: () => string[]; getMembersOptionals: () => boolean[]; + getMemberRangeStarts: () => number[]; expression: NodeEstreeIndex; isUnknown(): boolean; isNull(): boolean; @@ -571,7 +572,8 @@ declare abstract class BasicEvaluatedExpression { identifier: string | VariableInfoInterface, rootInfo: string | VariableInfoInterface, getMembers: () => string[], - getMembersOptionals?: () => boolean[] + getMembersOptionals?: () => boolean[], + getMemberRangeStarts?: () => number[] ): BasicEvaluatedExpression; /** @@ -766,6 +768,7 @@ declare interface CallExpressionInfo { name: string; getMembers: () => string[]; getMembersOptionals: () => boolean[]; + getMemberRangeStarts: () => number[]; } declare interface CallbackAsyncQueue { (err?: null | WebpackError, result?: T): any; @@ -3945,6 +3948,7 @@ declare interface ExpressionExpressionInfo { name: string; getMembers: () => string[]; getMembersOptionals: () => boolean[]; + getMemberRangeStarts: () => number[]; } declare interface ExtensionAliasOption { alias: string | string[]; @@ -5284,7 +5288,7 @@ declare class JavascriptParser extends Parser { topLevelAwait: SyncBailHook<[Expression], boolean | void>; call: HookMap>; callMemberChain: HookMap< - SyncBailHook<[CallExpression, string[], boolean[]], boolean | void> + SyncBailHook<[CallExpression, string[], boolean[], number[]], boolean | void> >; memberChainOfCallMemberChain: HookMap< SyncBailHook< @@ -5303,7 +5307,7 @@ declare class JavascriptParser extends Parser { binaryExpression: SyncBailHook<[BinaryExpression], boolean | void>; expression: HookMap>; expressionMemberChain: HookMap< - SyncBailHook<[Expression, string[], boolean[]], boolean | void> + SyncBailHook<[Expression, string[], boolean[], number[]], boolean | void> >; unhandledExpressionMemberChain: HookMap< SyncBailHook<[Expression, string[]], boolean | void> From f041e8dc5666ec4b329596970412d0f8a7a0658f Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Tue, 23 May 2023 15:36:56 -0700 Subject: [PATCH 05/18] remove extra memberRangeStarts changes --- .../HarmonyImportDependencyParserPlugin.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/dependencies/HarmonyImportDependencyParserPlugin.js b/lib/dependencies/HarmonyImportDependencyParserPlugin.js index fb7eadc9dcf..ec3a4ff80e0 100644 --- a/lib/dependencies/HarmonyImportDependencyParserPlugin.js +++ b/lib/dependencies/HarmonyImportDependencyParserPlugin.js @@ -228,7 +228,6 @@ module.exports = class HarmonyImportDependencyParserPlugin { ) : expression; const ids = settings.ids.concat(nonOptionalMembers); - const startsWithNamespace = settings.ids.length === 0; const dep = new HarmonyImportSpecifierDependency( settings.source, settings.sourceOrder, @@ -237,10 +236,7 @@ module.exports = class HarmonyImportDependencyParserPlugin { expr.range, exportPresenceMode, settings.assertions, - memberRangeStarts.slice( - startsWithNamespace ? 1 : 0, // skip one if starting with an explicit namespace - nonOptionalMembers.length - ) + memberRangeStarts ); dep.referencedPropertiesInDestructuring = parser.destructuringAssignmentPropertiesFor(expr); @@ -272,7 +268,6 @@ module.exports = class HarmonyImportDependencyParserPlugin { ) : callee; const ids = settings.ids.concat(nonOptionalMembers); - const startsWithNamespace = settings.ids.length === 0; const dep = new HarmonyImportSpecifierDependency( settings.source, settings.sourceOrder, @@ -281,10 +276,7 @@ module.exports = class HarmonyImportDependencyParserPlugin { expr.range, exportPresenceMode, settings.assertions, - memberRangeStarts.slice( - startsWithNamespace ? 1 : 0, // skip one if starting with an explicit namespace - nonOptionalMembers.length - ) + memberRangeStarts ); dep.directImport = members.length === 0; dep.call = true; From 852961f200c9e181560e7a3767336838ac3f8d82 Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Tue, 23 May 2023 15:37:12 -0700 Subject: [PATCH 06/18] add comment --- types.d.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/types.d.ts b/types.d.ts index 42af39d9af1..6cee4a932bc 100644 --- a/types.d.ts +++ b/types.d.ts @@ -566,7 +566,10 @@ declare abstract class BasicEvaluatedExpression { setRegExp(regExp: RegExp): BasicEvaluatedExpression; /** - * Set's the value of this expression to a particular identifier and its members. + * Sets the value of this expression to a particular identifier and its members. + * The array returned from getMemberRangeStarts is right-aligned with the array returned from getMembers. + * Meaning, the two arrays may not always have the same number of elements, but the last element of + * getMemberRangeStarts corresponds to the starting range position of last element of getMembers. */ setIdentifier( identifier: string | VariableInfoInterface, From f59c12da531045dfd0479c7fc20655f750591567 Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Tue, 23 May 2023 15:37:53 -0700 Subject: [PATCH 07/18] update comment --- types.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types.d.ts b/types.d.ts index 6cee4a932bc..fd0e184c473 100644 --- a/types.d.ts +++ b/types.d.ts @@ -569,7 +569,7 @@ declare abstract class BasicEvaluatedExpression { * Sets the value of this expression to a particular identifier and its members. * The array returned from getMemberRangeStarts is right-aligned with the array returned from getMembers. * Meaning, the two arrays may not always have the same number of elements, but the last element of - * getMemberRangeStarts corresponds to the starting range position of last element of getMembers. + * getMemberRangeStarts corresponds to [the starting range position of] the last element of getMembers. */ setIdentifier( identifier: string | VariableInfoInterface, From afde59f5e9e2cbff94f3e0ac8d64d92de952f889 Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Tue, 23 May 2023 20:01:44 -0700 Subject: [PATCH 08/18] add and handle another test case --- .../HarmonyImportDependencyParserPlugin.js | 14 ++++++++++++-- .../code-generation/re-export-namespace/index.js | 9 ++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/dependencies/HarmonyImportDependencyParserPlugin.js b/lib/dependencies/HarmonyImportDependencyParserPlugin.js index ec3a4ff80e0..24eb72e31b4 100644 --- a/lib/dependencies/HarmonyImportDependencyParserPlugin.js +++ b/lib/dependencies/HarmonyImportDependencyParserPlugin.js @@ -220,6 +220,11 @@ module.exports = class HarmonyImportDependencyParserPlugin { members, membersOptionals ); + const rangeStarts = memberRangeStarts.slice( + 0, + memberRangeStarts.length - + (members.length - nonOptionalMembers.length) + ); const expr = nonOptionalMembers !== members ? getNonOptionalMemberChain( @@ -236,7 +241,7 @@ module.exports = class HarmonyImportDependencyParserPlugin { expr.range, exportPresenceMode, settings.assertions, - memberRangeStarts + rangeStarts ); dep.referencedPropertiesInDestructuring = parser.destructuringAssignmentPropertiesFor(expr); @@ -260,6 +265,11 @@ module.exports = class HarmonyImportDependencyParserPlugin { members, membersOptionals ); + const rangeStarts = memberRangeStarts.slice( + 0, + memberRangeStarts.length - + (members.length - nonOptionalMembers.length) + ); const expr = nonOptionalMembers !== members ? getNonOptionalMemberChain( @@ -276,7 +286,7 @@ module.exports = class HarmonyImportDependencyParserPlugin { expr.range, exportPresenceMode, settings.assertions, - memberRangeStarts + rangeStarts ); dep.directImport = members.length === 0; dep.call = true; diff --git a/test/configCases/code-generation/re-export-namespace/index.js b/test/configCases/code-generation/re-export-namespace/index.js index e5928f19b7b..e3172ce9b61 100644 --- a/test/configCases/code-generation/re-export-namespace/index.js +++ b/test/configCases/code-generation/re-export-namespace/index.js @@ -15,11 +15,6 @@ it("should use/preserve accessor form for import object and namespaces", functio // Reference the imports to generate uses in the source. - def.obj2.unknownProperty = { deep: "trench" }; - expect(def.obj2.unknownProperty.deep).toBe("trench"); - expect(def).toHaveProperty('obj2'); - expect(def.obj2).toHaveProperty('unknownProperty'); - const f = false; if (f) { const x1 = m_1; @@ -36,6 +31,8 @@ it("should use/preserve accessor form for import object and namespaces", functio const d = m_2.m_1["obj1"].sing["song"](); const aa = m_3["m_2"].m_1["obj1"]["zoom"]; + + const bb = obj1.up.down?.left.right; } /************ DO NOT MATCH BELOW THIS LINE ************/ @@ -57,4 +54,6 @@ it("should use/preserve accessor form for import object and namespaces", functio expectSourceToContain(source, 'const d = _module2__WEBPACK_IMPORTED_MODULE_1__.m_1.obj1.sing["song"]();'); expectSourceToContain(source, 'const aa = _module3__WEBPACK_IMPORTED_MODULE_2__.m_2.m_1.obj1["zoom"];'); + + expectSourceToContain(source, 'const bb = _module1__WEBPACK_IMPORTED_MODULE_0__.obj1.up.down?.left.right;'); }); From 7bdd6436ba6d70a590085715f730e6002b640acf Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Tue, 23 May 2023 20:03:49 -0700 Subject: [PATCH 09/18] move comment --- lib/dependencies/HarmonyImportSpecifierDependency.js | 4 ++++ types.d.ts | 5 +---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/dependencies/HarmonyImportSpecifierDependency.js b/lib/dependencies/HarmonyImportSpecifierDependency.js index 3b8bb61d90a..fbde0bf79e3 100644 --- a/lib/dependencies/HarmonyImportSpecifierDependency.js +++ b/lib/dependencies/HarmonyImportSpecifierDependency.js @@ -314,6 +314,10 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen let [rangeStart, rangeEnd] = dep.range; if (trimmedIds.length !== ids.length) { + // The array returned from dep.idRangeStarts is right-aligned with the array returned from dep.getIds. + // Meaning, the two arrays may not always have the same number of elements, but the last element of + // dep.idRangeStarts corresponds to [the starting range position of] the last element of dep.getIds. + // Use this to find the correct range end position based on the number of ids that were trimmed. rangeEnd = dep.idRangeStarts.at(trimmedIds.length - ids.length); } diff --git a/types.d.ts b/types.d.ts index fd0e184c473..42af39d9af1 100644 --- a/types.d.ts +++ b/types.d.ts @@ -566,10 +566,7 @@ declare abstract class BasicEvaluatedExpression { setRegExp(regExp: RegExp): BasicEvaluatedExpression; /** - * Sets the value of this expression to a particular identifier and its members. - * The array returned from getMemberRangeStarts is right-aligned with the array returned from getMembers. - * Meaning, the two arrays may not always have the same number of elements, but the last element of - * getMemberRangeStarts corresponds to [the starting range position of] the last element of getMembers. + * Set's the value of this expression to a particular identifier and its members. */ setIdentifier( identifier: string | VariableInfoInterface, From 5022bf9fe175f2cfeddbe5ae772caf71667c6b77 Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Tue, 23 May 2023 20:37:15 -0700 Subject: [PATCH 10/18] yarn special-lint-fix --- types.d.ts | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/types.d.ts b/types.d.ts index 43b7c6a0811..63ae0e52d12 100644 --- a/types.d.ts +++ b/types.d.ts @@ -3518,7 +3518,6 @@ declare class EnvironmentPlugin { */ apply(compiler: Compiler): void; } -type ErrorWithDetail = Error & { details?: string }; declare interface Etag { toString: () => string; } @@ -5288,7 +5287,10 @@ declare class JavascriptParser extends Parser { topLevelAwait: SyncBailHook<[Expression], boolean | void>; call: HookMap>; callMemberChain: HookMap< - SyncBailHook<[CallExpression, string[], boolean[], number[]], boolean | void> + SyncBailHook< + [CallExpression, string[], boolean[], number[]], + boolean | void + > >; memberChainOfCallMemberChain: HookMap< SyncBailHook< @@ -5895,6 +5897,7 @@ declare class JavascriptParser extends Parser { | YieldExpression | Super; membersOptionals: boolean[]; + memberRangeStarts: number[]; }; getFreeInfoFromVariable(varName: string): { name: string; @@ -6937,6 +6940,7 @@ declare interface LoaderRunnerLoaderContext { /** * An array of all the loaders. It is writeable in the pitch phase. * loaders = [{request: string, path: string, query: string, module: function}] + * * In the example: * [ * { request: "/abc/loader1.js?xyz", @@ -8311,9 +8315,9 @@ declare interface NormalModuleLoaderContext { context: string, request: string, callback: ( - err: null | ErrorWithDetail, - res?: string | false, - req?: ResolveRequest + arg0: null | Error, + arg1?: string | false, + arg2?: ResolveRequest ) => void ): any; getResolve(options?: ResolveOptionsWithDependencyType): { @@ -8321,9 +8325,9 @@ declare interface NormalModuleLoaderContext { context: string, request: string, callback: ( - err: null | ErrorWithDetail, - res?: string | false, - req?: ResolveRequest + arg0: null | Error, + arg1?: string | false, + arg2?: ResolveRequest ) => void ): void; (context: string, request: string): Promise; @@ -10433,9 +10437,9 @@ declare abstract class Resolver { request: string, resolveContext: ResolveContext, callback: ( - err: null | ErrorWithDetail, - res?: string | false, - req?: ResolveRequest + arg0: null | Error, + arg1?: string | false, + arg2?: ResolveRequest ) => void ): void; doResolve( From 5b88a4865ea731cc6659584a76a5aed359a45596 Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Tue, 23 May 2023 22:24:20 -0700 Subject: [PATCH 11/18] yarn type-lint --- .../HarmonyEvaluatedImportSpecifierDependency.js | 2 +- .../HarmonyImportDependencyParserPlugin.js | 12 ++++++------ lib/dependencies/HarmonyImportSpecifierDependency.js | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js b/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js index 742c9eee786..b63eed4ddbf 100644 --- a/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +++ b/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js @@ -35,7 +35,7 @@ class HarmonyEvaluatedImportSpecifierDependency extends HarmonyImportSpecifierDe * @param {string} operator operator */ constructor(request, sourceOrder, ids, name, range, assertions, operator) { - super(request, sourceOrder, ids, name, range, false, assertions); + super(request, sourceOrder, ids, name, range, false, [], assertions); this.operator = operator; } diff --git a/lib/dependencies/HarmonyImportDependencyParserPlugin.js b/lib/dependencies/HarmonyImportDependencyParserPlugin.js index 24eb72e31b4..403ead257b2 100644 --- a/lib/dependencies/HarmonyImportDependencyParserPlugin.js +++ b/lib/dependencies/HarmonyImportDependencyParserPlugin.js @@ -195,8 +195,8 @@ module.exports = class HarmonyImportDependencyParserPlugin { settings.name, expr.range, exportPresenceMode, - settings.assertions, - [] + [], + settings.assertions ); dep.referencedPropertiesInDestructuring = parser.destructuringAssignmentPropertiesFor(expr); @@ -240,8 +240,8 @@ module.exports = class HarmonyImportDependencyParserPlugin { settings.name, expr.range, exportPresenceMode, - settings.assertions, - rangeStarts + rangeStarts, + settings.assertions ); dep.referencedPropertiesInDestructuring = parser.destructuringAssignmentPropertiesFor(expr); @@ -285,8 +285,8 @@ module.exports = class HarmonyImportDependencyParserPlugin { settings.name, expr.range, exportPresenceMode, - settings.assertions, - rangeStarts + rangeStarts, + settings.assertions ); dep.directImport = members.length === 0; dep.call = true; diff --git a/lib/dependencies/HarmonyImportSpecifierDependency.js b/lib/dependencies/HarmonyImportSpecifierDependency.js index d1622c42084..699fcfa5565 100644 --- a/lib/dependencies/HarmonyImportSpecifierDependency.js +++ b/lib/dependencies/HarmonyImportSpecifierDependency.js @@ -42,8 +42,8 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency { * @param {string} name name * @param {Range} range range * @param {TODO} exportPresenceMode export presence mode - * @param {Assertions=} assertions assertions * @param {number[]} idRangeStarts range starts for members of ids; the two arrays are right-aligned + * @param {Assertions=} assertions assertions */ constructor( request, @@ -52,8 +52,8 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency { name, range, exportPresenceMode, - assertions, - idRangeStarts + idRangeStarts, + assertions ) { super(request, sourceOrder, assertions); this.ids = ids; From af6626981e525341001fc56ca271cf0ca2943bc8 Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Wed, 24 May 2023 06:12:10 -0700 Subject: [PATCH 12/18] fix use of "at" --- lib/dependencies/HarmonyImportSpecifierDependency.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/dependencies/HarmonyImportSpecifierDependency.js b/lib/dependencies/HarmonyImportSpecifierDependency.js index 699fcfa5565..89230179fae 100644 --- a/lib/dependencies/HarmonyImportSpecifierDependency.js +++ b/lib/dependencies/HarmonyImportSpecifierDependency.js @@ -330,7 +330,10 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen // Meaning, the two arrays may not always have the same number of elements, but the last element of // dep.idRangeStarts corresponds to [the starting range position of] the last element of dep.getIds. // Use this to find the correct range end position based on the number of ids that were trimmed. - rangeEnd = dep.idRangeStarts.at(trimmedIds.length - ids.length); + rangeEnd = + dep.idRangeStarts[ + dep.idRangeStarts.length + (trimmedIds.length - ids.length) + ]; } if (dep.shorthand) { From b40c05e398d1552bca265c04bdf2571e4651f6ad Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Wed, 24 May 2023 07:46:45 -0700 Subject: [PATCH 13/18] add test filter --- .../code-generation/re-export-namespace/test.filter.js | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/configCases/code-generation/re-export-namespace/test.filter.js diff --git a/test/configCases/code-generation/re-export-namespace/test.filter.js b/test/configCases/code-generation/re-export-namespace/test.filter.js new file mode 100644 index 00000000000..698f2822d2d --- /dev/null +++ b/test/configCases/code-generation/re-export-namespace/test.filter.js @@ -0,0 +1,5 @@ +var supportsOptionalChaining = require("../../../helpers/supportsOptionalChaining"); + +module.exports = function (config) { + return supportsOptionalChaining(); +}; From 3210786299d32974d70549b5127b01f9c96d092c Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Wed, 24 May 2023 12:00:01 -0700 Subject: [PATCH 14/18] update type.d.ts --- types.d.ts | 88 ++++++++++++++++++++---------------------------------- 1 file changed, 32 insertions(+), 56 deletions(-) diff --git a/types.d.ts b/types.d.ts index 171f80991f8..fae33cf0e50 100644 --- a/types.d.ts +++ b/types.d.ts @@ -83,7 +83,6 @@ import { WithStatement, YieldExpression } from "estree"; -import { Dirent } from "fs"; import { IncomingMessage, ServerOptions as ServerOptionsImport, @@ -206,9 +205,8 @@ declare interface AggressiveSplittingPluginOptions { */ minSize?: number; } -type Alias = string | false | string[]; declare interface AliasOption { - alias: Alias; + alias: string | false | string[]; name: string; onlyModule?: boolean; } @@ -462,16 +460,12 @@ declare interface BannerPluginOptions { } declare interface BaseResolveRequest { path: string | false; - context?: object; descriptionFilePath?: string; descriptionFileRoot?: string; - descriptionFileData?: JsonObject; + descriptionFileData?: object; relativePath?: string; ignoreSymlinks?: boolean; fullySpecified?: boolean; - __innerRequest?: string; - __innerRequest_request?: string; - __innerRequest_relativePath?: string; } declare abstract class BasicEvaluatedExpression { type: number; @@ -4300,18 +4294,17 @@ declare interface FileSystem { | "binary" | (( arg0?: null | NodeJS.ErrnoException, - arg1?: (string | Buffer)[] | (typeof Dirent)[] + arg1?: any[] | (string | Buffer)[] ) => void) | ReaddirOptions | "utf-8" | "ucs-2" | "base64" - | "base64url" | "hex" | "buffer", arg2?: ( arg0?: null | NodeJS.ErrnoException, - arg1?: (string | Buffer)[] | (typeof Dirent)[] + arg1?: any[] | (string | Buffer)[] ) => void ) => void; readJson?: { @@ -6111,17 +6104,6 @@ declare interface JavascriptParserOptions { */ wrappedContextRegExp?: RegExp; } -type JsonObject = { [index: string]: JsonValue } & { - [index: string]: - | undefined - | null - | string - | number - | boolean - | JsonObject - | JsonValue[]; -}; -type JsonValue = null | string | number | boolean | JsonObject | JsonValue[]; declare class JsonpChunkLoadingRuntimeModule extends RuntimeModule { constructor(runtimeRequirements: Set); static getCompilationHooks( @@ -6238,23 +6220,6 @@ declare interface KnownBuildMeta { declare interface KnownCreateStatsOptionsContext { forToString?: boolean; } -declare interface KnownHooks { - resolveStep: SyncHook< - [ - AsyncSeriesBailHook< - [ResolveRequest, ResolveContext], - null | ResolveRequest - >, - ResolveRequest - ] - >; - noResolve: SyncHook<[ResolveRequest, Error]>; - resolve: AsyncSeriesBailHook< - [ResolveRequest, ResolveContext], - null | ResolveRequest - >; - result: AsyncSeriesHook<[ResolveRequest, ResolveContext]>; -} declare interface KnownNormalizedStatsOptions { context: string; requestShortener: RequestShortener; @@ -6976,7 +6941,6 @@ declare interface LoaderRunnerLoaderContext { /** * An array of all the loaders. It is writeable in the pitch phase. * loaders = [{request: string, path: string, query: string, module: function}] - * * In the example: * [ * { request: "/abc/loader1.js?xyz", @@ -9914,7 +9878,6 @@ declare interface ReaddirOptions { | "utf-8" | "ucs-2" | "base64" - | "base64url" | "hex" | "buffer"; withFileTypes?: boolean; @@ -10428,7 +10391,23 @@ declare interface ResolvedContextTimestampAndHash { declare abstract class Resolver { fileSystem: FileSystem; options: ResolveOptionsTypes; - hooks: KnownHooks; + hooks: { + resolveStep: SyncHook< + [ + AsyncSeriesBailHook< + [ResolveRequest, ResolveContext], + null | ResolveRequest + >, + ResolveRequest + ] + >; + noResolve: SyncHook<[ResolveRequest, Error]>; + resolve: AsyncSeriesBailHook< + [ResolveRequest, ResolveContext], + null | ResolveRequest + >; + result: AsyncSeriesHook<[ResolveRequest, ResolveContext]>; + }; ensureHook( name: | string @@ -10464,21 +10443,18 @@ declare abstract class Resolver { ) => void ): void; doResolve( - hook: AsyncSeriesBailHook< - [ResolveRequest, ResolveContext], - null | ResolveRequest - >, - request: ResolveRequest, - message: null | string, - resolveContext: ResolveContext, - callback: (err?: null | Error, result?: ResolveRequest) => void - ): void; + hook?: any, + request?: any, + message?: any, + resolveContext?: any, + callback?: any + ): any; parse(identifier: string): ParsedIdentifier; - isModule(path: string): boolean; - isPrivate(path: string): boolean; + isModule(path?: any): boolean; + isPrivate(path?: any): boolean; isDirectory(path: string): boolean; - join(path: string, request: string): string; - normalize(path: string): string; + join(path?: any, request?: any): string; + normalize(path?: any): string; } declare interface ResolverCache { direct: WeakMap; @@ -13178,7 +13154,7 @@ declare interface WithOptions { ) => ResolverWithOptions; } declare interface WriteOnlySet { - add: (item: T) => void; + add: (T?: any) => void; } type __TypeWebpackOptions = (data: object) => | string From 43fdf6114816b882dd843e43cf87addd5ace0b73 Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Wed, 24 May 2023 14:02:57 -0700 Subject: [PATCH 15/18] update types.d.ts --- types.d.ts | 106 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 41 deletions(-) diff --git a/types.d.ts b/types.d.ts index fae33cf0e50..ad0cffe3645 100644 --- a/types.d.ts +++ b/types.d.ts @@ -83,6 +83,7 @@ import { WithStatement, YieldExpression } from "estree"; +import { Dirent } from "fs"; import { IncomingMessage, ServerOptions as ServerOptionsImport, @@ -205,8 +206,9 @@ declare interface AggressiveSplittingPluginOptions { */ minSize?: number; } +type Alias = string | false | string[]; declare interface AliasOption { - alias: string | false | string[]; + alias: Alias; name: string; onlyModule?: boolean; } @@ -460,12 +462,16 @@ declare interface BannerPluginOptions { } declare interface BaseResolveRequest { path: string | false; + context?: object; descriptionFilePath?: string; descriptionFileRoot?: string; - descriptionFileData?: object; + descriptionFileData?: JsonObject; relativePath?: string; ignoreSymlinks?: boolean; fullySpecified?: boolean; + __innerRequest?: string; + __innerRequest_request?: string; + __innerRequest_relativePath?: string; } declare abstract class BasicEvaluatedExpression { type: number; @@ -3518,6 +3524,7 @@ declare class EnvironmentPlugin { */ apply(compiler: Compiler): void; } +type ErrorWithDetail = Error & { details?: string }; declare interface Etag { toString: () => string; } @@ -4294,17 +4301,18 @@ declare interface FileSystem { | "binary" | (( arg0?: null | NodeJS.ErrnoException, - arg1?: any[] | (string | Buffer)[] + arg1?: (string | Buffer)[] | (typeof Dirent)[] ) => void) | ReaddirOptions | "utf-8" | "ucs-2" | "base64" + | "base64url" | "hex" | "buffer", arg2?: ( arg0?: null | NodeJS.ErrnoException, - arg1?: any[] | (string | Buffer)[] + arg1?: (string | Buffer)[] | (typeof Dirent)[] ) => void ) => void; readJson?: { @@ -6104,6 +6112,17 @@ declare interface JavascriptParserOptions { */ wrappedContextRegExp?: RegExp; } +type JsonObject = { [index: string]: JsonValue } & { + [index: string]: + | undefined + | null + | string + | number + | boolean + | JsonObject + | JsonValue[]; +}; +type JsonValue = null | string | number | boolean | JsonObject | JsonValue[]; declare class JsonpChunkLoadingRuntimeModule extends RuntimeModule { constructor(runtimeRequirements: Set); static getCompilationHooks( @@ -6220,6 +6239,23 @@ declare interface KnownBuildMeta { declare interface KnownCreateStatsOptionsContext { forToString?: boolean; } +declare interface KnownHooks { + resolveStep: SyncHook< + [ + AsyncSeriesBailHook< + [ResolveRequest, ResolveContext], + null | ResolveRequest + >, + ResolveRequest + ] + >; + noResolve: SyncHook<[ResolveRequest, Error]>; + resolve: AsyncSeriesBailHook< + [ResolveRequest, ResolveContext], + null | ResolveRequest + >; + result: AsyncSeriesHook<[ResolveRequest, ResolveContext]>; +} declare interface KnownNormalizedStatsOptions { context: string; requestShortener: RequestShortener; @@ -8315,9 +8351,9 @@ declare interface NormalModuleLoaderContext { context: string, request: string, callback: ( - arg0: null | Error, - arg1?: string | false, - arg2?: ResolveRequest + err: null | ErrorWithDetail, + res?: string | false, + req?: ResolveRequest ) => void ): any; getResolve(options?: ResolveOptionsWithDependencyType): { @@ -8325,9 +8361,9 @@ declare interface NormalModuleLoaderContext { context: string, request: string, callback: ( - arg0: null | Error, - arg1?: string | false, - arg2?: ResolveRequest + err: null | ErrorWithDetail, + res?: string | false, + req?: ResolveRequest ) => void ): void; (context: string, request: string): Promise; @@ -9878,6 +9914,7 @@ declare interface ReaddirOptions { | "utf-8" | "ucs-2" | "base64" + | "base64url" | "hex" | "buffer"; withFileTypes?: boolean; @@ -10391,23 +10428,7 @@ declare interface ResolvedContextTimestampAndHash { declare abstract class Resolver { fileSystem: FileSystem; options: ResolveOptionsTypes; - hooks: { - resolveStep: SyncHook< - [ - AsyncSeriesBailHook< - [ResolveRequest, ResolveContext], - null | ResolveRequest - >, - ResolveRequest - ] - >; - noResolve: SyncHook<[ResolveRequest, Error]>; - resolve: AsyncSeriesBailHook< - [ResolveRequest, ResolveContext], - null | ResolveRequest - >; - result: AsyncSeriesHook<[ResolveRequest, ResolveContext]>; - }; + hooks: KnownHooks; ensureHook( name: | string @@ -10437,24 +10458,27 @@ declare abstract class Resolver { request: string, resolveContext: ResolveContext, callback: ( - arg0: null | Error, - arg1?: string | false, - arg2?: ResolveRequest + err: null | ErrorWithDetail, + res?: string | false, + req?: ResolveRequest ) => void ): void; doResolve( - hook?: any, - request?: any, - message?: any, - resolveContext?: any, - callback?: any - ): any; + hook: AsyncSeriesBailHook< + [ResolveRequest, ResolveContext], + null | ResolveRequest + >, + request: ResolveRequest, + message: null | string, + resolveContext: ResolveContext, + callback: (err?: null | Error, result?: ResolveRequest) => void + ): void; parse(identifier: string): ParsedIdentifier; - isModule(path?: any): boolean; - isPrivate(path?: any): boolean; + isModule(path: string): boolean; + isPrivate(path: string): boolean; isDirectory(path: string): boolean; - join(path?: any, request?: any): string; - normalize(path?: any): string; + join(path: string, request: string): string; + normalize(path: string): string; } declare interface ResolverCache { direct: WeakMap; @@ -13154,7 +13178,7 @@ declare interface WithOptions { ) => ResolverWithOptions; } declare interface WriteOnlySet { - add: (T?: any) => void; + add: (item: T) => void; } type __TypeWebpackOptions = (data: object) => | string From b834303d83a59cfe823a79be760e61ecce7a3565 Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Fri, 26 May 2023 12:04:57 -0700 Subject: [PATCH 16/18] add more error handling and json import support --- .../HarmonyImportSpecifierDependency.js | 23 +++++-- .../re-export-namespace-concat/data.json | 5 ++ .../re-export-namespace-concat/index.js | 63 +++++++++++++++++++ .../re-export-namespace-concat/module1.js | 3 + .../re-export-namespace-concat/module2.js | 2 + .../re-export-namespace-concat/module3.js | 2 + .../re-export-namespace-concat/test.filter.js | 5 ++ .../webpack.config.js | 11 ++++ .../re-export-namespace/data.json | 5 ++ .../re-export-namespace/index.js | 7 ++- 10 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 test/configCases/code-generation/re-export-namespace-concat/data.json create mode 100644 test/configCases/code-generation/re-export-namespace-concat/index.js create mode 100644 test/configCases/code-generation/re-export-namespace-concat/module1.js create mode 100644 test/configCases/code-generation/re-export-namespace-concat/module2.js create mode 100644 test/configCases/code-generation/re-export-namespace-concat/module3.js create mode 100644 test/configCases/code-generation/re-export-namespace-concat/test.filter.js create mode 100644 test/configCases/code-generation/re-export-namespace-concat/webpack.config.js create mode 100644 test/configCases/code-generation/re-export-namespace/data.json diff --git a/lib/dependencies/HarmonyImportSpecifierDependency.js b/lib/dependencies/HarmonyImportSpecifierDependency.js index 89230179fae..509bda458f4 100644 --- a/lib/dependencies/HarmonyImportSpecifierDependency.js +++ b/lib/dependencies/HarmonyImportSpecifierDependency.js @@ -330,10 +330,13 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen // Meaning, the two arrays may not always have the same number of elements, but the last element of // dep.idRangeStarts corresponds to [the starting range position of] the last element of dep.getIds. // Use this to find the correct range end position based on the number of ids that were trimmed. - rangeEnd = - dep.idRangeStarts[ - dep.idRangeStarts.length + (trimmedIds.length - ids.length) - ]; + const idx = dep.idRangeStarts.length + (trimmedIds.length - ids.length); + if (idx < 0 || idx >= dep.idRangeStarts.length) { + throw new Error( + `Missing range starts data for id replacement trimming.` + ); + } + rangeEnd = dep.idRangeStarts[idx]; } if (dep.shorthand) { @@ -352,6 +355,7 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen * @returns {string[]} generated code */ _trimIdsToThoseImported(ids, moduleGraph, dependency) { + let trimmedIds = []; const exportsInfo = moduleGraph.getExportsInfo( moduleGraph.getModule(dependency) ); @@ -361,14 +365,21 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen continue; // ExportInfo for the next level under default is still at the root ExportsInfo, so don't advance currentExportsInfo } const exportInfo = currentExportsInfo.getExportInfo(ids[i]); + if (exportInfo.provided === false) { + // json imports have nested ExportInfo for elements that things that are not actually exported, so check .provided + trimmedIds = ids.slice(0, i); + break; + } const nestedInfo = exportInfo.getNestedExportsInfo(); if (!nestedInfo) { // once all nested exports are traversed, the next item is the actual import so stop there - return ids.slice(0, i + 1); + trimmedIds = ids.slice(0, i + 1); + break; } currentExportsInfo = nestedInfo; } - return ids; + // Never trim to nothing. This can happen for invalid imports (e.g. import { notThere } from "./module", or import { anything } from "./missingModule") + return trimmedIds.length ? trimmedIds : ids; } /** diff --git a/test/configCases/code-generation/re-export-namespace-concat/data.json b/test/configCases/code-generation/re-export-namespace-concat/data.json new file mode 100644 index 00000000000..7726aedd0c5 --- /dev/null +++ b/test/configCases/code-generation/re-export-namespace-concat/data.json @@ -0,0 +1,5 @@ +{ + "nested": { + "object3": {} + } +} diff --git a/test/configCases/code-generation/re-export-namespace-concat/index.js b/test/configCases/code-generation/re-export-namespace-concat/index.js new file mode 100644 index 00000000000..3d9549b5f82 --- /dev/null +++ b/test/configCases/code-generation/re-export-namespace-concat/index.js @@ -0,0 +1,63 @@ +import { obj1 } from './module1'; +import * as m_1 from './module1'; +import * as m_2 from './module2'; +import * as m_3 from './module3'; +import data from "./data"; + +const { expectSourceToContain } = require("../../../helpers/expectSource"); + +// It's important to preserve the same accessor syntax (quotes vs. dot notatation) after the actual export variable. +// Else, minifiers such as Closure Compiler will not be able to minify correctly in ADVANCED mode. + +it("should use/preserve accessor form for import object and namespaces", function() { + var fs = require("fs"); + var source = fs.readFileSync(__filename, "utf-8").toString(); + + // Reference the imports to generate uses in the source. + + const f = false; + if (f) { + const x1 = m_1; + const x2 = obj1; + + const z1 = obj1["plants"]; + const z2 = obj1["funcs"](); + const z3 = m_1["obj1"]["pots"]; + const z4 = m_1["obj1"]["subs"](); + + const a = m_2["m_1"].obj1["flip"].flap; + const b = m_2["m_1"]["obj1"].zip["zap"]; + const c = m_2.m_1.obj1["ding"].dong(); + const d = m_2.m_1["obj1"].sing["song"](); + + const aa = m_3["m_2"].m_1["obj1"]["zoom"]; + + const bb = obj1.up.down?.left.right; + + data.nested.object3["unknownProperty"].depth = "deep"; + } + + /************ DO NOT MATCH BELOW THIS LINE ************/ + + // Imported objects and import namespaces should use dot notation. Any references to the properties of exports + // should be preserved as either quotes or dot notation, depending on the original source. + + expectSourceToContain(source, 'const x1 = module1_namespaceObject;'); + expectSourceToContain(source, 'const x2 = obj1;'); + + expectSourceToContain(source, 'const z1 = obj1["plants"];'); + expectSourceToContain(source, 'const z2 = obj1["funcs"]();'); + expectSourceToContain(source, 'const z3 = obj1["pots"];'); + expectSourceToContain(source, 'const z4 = obj1["subs"]();'); + + expectSourceToContain(source, 'const a = obj1["flip"].flap;'); + expectSourceToContain(source, 'const b = obj1.zip["zap"];'); + expectSourceToContain(source, 'const c = obj1["ding"].dong();'); + expectSourceToContain(source, 'const d = obj1.sing["song"]();'); + + expectSourceToContain(source, 'const aa = obj1["zoom"];'); + + expectSourceToContain(source, 'const bb = obj1.up.down?.left.right;'); + + expectSourceToContain(source, 'data_namespaceObject.a.a["unknownProperty"].depth = "deep";'); +}); diff --git a/test/configCases/code-generation/re-export-namespace-concat/module1.js b/test/configCases/code-generation/re-export-namespace-concat/module1.js new file mode 100644 index 00000000000..e85ec664386 --- /dev/null +++ b/test/configCases/code-generation/re-export-namespace-concat/module1.js @@ -0,0 +1,3 @@ +export const obj1 = {}; + +export default { obj2: {} }; diff --git a/test/configCases/code-generation/re-export-namespace-concat/module2.js b/test/configCases/code-generation/re-export-namespace-concat/module2.js new file mode 100644 index 00000000000..a91c5e7a055 --- /dev/null +++ b/test/configCases/code-generation/re-export-namespace-concat/module2.js @@ -0,0 +1,2 @@ +import * as m1 from './module1'; +export { m1 as m_1 }; diff --git a/test/configCases/code-generation/re-export-namespace-concat/module3.js b/test/configCases/code-generation/re-export-namespace-concat/module3.js new file mode 100644 index 00000000000..cf0e8cd08d8 --- /dev/null +++ b/test/configCases/code-generation/re-export-namespace-concat/module3.js @@ -0,0 +1,2 @@ +import * as m2 from './module2'; +export { m2 as m_2 }; diff --git a/test/configCases/code-generation/re-export-namespace-concat/test.filter.js b/test/configCases/code-generation/re-export-namespace-concat/test.filter.js new file mode 100644 index 00000000000..698f2822d2d --- /dev/null +++ b/test/configCases/code-generation/re-export-namespace-concat/test.filter.js @@ -0,0 +1,5 @@ +var supportsOptionalChaining = require("../../../helpers/supportsOptionalChaining"); + +module.exports = function (config) { + return supportsOptionalChaining(); +}; diff --git a/test/configCases/code-generation/re-export-namespace-concat/webpack.config.js b/test/configCases/code-generation/re-export-namespace-concat/webpack.config.js new file mode 100644 index 00000000000..7e1057f2f3c --- /dev/null +++ b/test/configCases/code-generation/re-export-namespace-concat/webpack.config.js @@ -0,0 +1,11 @@ +/** @type {import("../../../../").Configuration} */ +module.exports = { + node: { + __dirname: false, + __filename: false + }, + mode: "production", + optimization: { + mangleExports: "size" + } +}; diff --git a/test/configCases/code-generation/re-export-namespace/data.json b/test/configCases/code-generation/re-export-namespace/data.json new file mode 100644 index 00000000000..7726aedd0c5 --- /dev/null +++ b/test/configCases/code-generation/re-export-namespace/data.json @@ -0,0 +1,5 @@ +{ + "nested": { + "object3": {} + } +} diff --git a/test/configCases/code-generation/re-export-namespace/index.js b/test/configCases/code-generation/re-export-namespace/index.js index e3172ce9b61..caaf1475498 100644 --- a/test/configCases/code-generation/re-export-namespace/index.js +++ b/test/configCases/code-generation/re-export-namespace/index.js @@ -1,8 +1,8 @@ -import def from "./module1"; import { obj1 } from './module1'; import * as m_1 from './module1'; import * as m_2 from './module2'; import * as m_3 from './module3'; +import data from "./data"; const { expectSourceToContain } = require("../../../helpers/expectSource"); @@ -33,6 +33,8 @@ it("should use/preserve accessor form for import object and namespaces", functio const aa = m_3["m_2"].m_1["obj1"]["zoom"]; const bb = obj1.up.down?.left.right; + + data.nested.object3["unknownProperty"].depth = "deep"; } /************ DO NOT MATCH BELOW THIS LINE ************/ @@ -56,4 +58,7 @@ it("should use/preserve accessor form for import object and namespaces", functio expectSourceToContain(source, 'const aa = _module3__WEBPACK_IMPORTED_MODULE_2__.m_2.m_1.obj1["zoom"];'); expectSourceToContain(source, 'const bb = _module1__WEBPACK_IMPORTED_MODULE_0__.obj1.up.down?.left.right;'); + + expectSourceToContain(source, '_data__WEBPACK_IMPORTED_MODULE_3__.nested.object3["unknownProperty"].depth = "deep";'); + }); From 9bb13a8be67fb496325990cc0f6406d2d911d0d4 Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Tue, 30 May 2023 23:06:15 -0700 Subject: [PATCH 17/18] incorporate CR feedback --- ...rmonyEvaluatedImportSpecifierDependency.js | 2 +- .../HarmonyImportDependencyParserPlugin.js | 12 +++---- .../HarmonyImportSpecifierDependency.js | 32 +++++++++++-------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js b/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js index b63eed4ddbf..216f820b342 100644 --- a/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +++ b/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js @@ -35,7 +35,7 @@ class HarmonyEvaluatedImportSpecifierDependency extends HarmonyImportSpecifierDe * @param {string} operator operator */ constructor(request, sourceOrder, ids, name, range, assertions, operator) { - super(request, sourceOrder, ids, name, range, false, [], assertions); + super(request, sourceOrder, ids, name, range, false, assertions, []); this.operator = operator; } diff --git a/lib/dependencies/HarmonyImportDependencyParserPlugin.js b/lib/dependencies/HarmonyImportDependencyParserPlugin.js index 403ead257b2..24eb72e31b4 100644 --- a/lib/dependencies/HarmonyImportDependencyParserPlugin.js +++ b/lib/dependencies/HarmonyImportDependencyParserPlugin.js @@ -195,8 +195,8 @@ module.exports = class HarmonyImportDependencyParserPlugin { settings.name, expr.range, exportPresenceMode, - [], - settings.assertions + settings.assertions, + [] ); dep.referencedPropertiesInDestructuring = parser.destructuringAssignmentPropertiesFor(expr); @@ -240,8 +240,8 @@ module.exports = class HarmonyImportDependencyParserPlugin { settings.name, expr.range, exportPresenceMode, - rangeStarts, - settings.assertions + settings.assertions, + rangeStarts ); dep.referencedPropertiesInDestructuring = parser.destructuringAssignmentPropertiesFor(expr); @@ -285,8 +285,8 @@ module.exports = class HarmonyImportDependencyParserPlugin { settings.name, expr.range, exportPresenceMode, - rangeStarts, - settings.assertions + settings.assertions, + rangeStarts ); dep.directImport = members.length === 0; dep.call = true; diff --git a/lib/dependencies/HarmonyImportSpecifierDependency.js b/lib/dependencies/HarmonyImportSpecifierDependency.js index 509bda458f4..689113e6259 100644 --- a/lib/dependencies/HarmonyImportSpecifierDependency.js +++ b/lib/dependencies/HarmonyImportSpecifierDependency.js @@ -42,8 +42,8 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency { * @param {string} name name * @param {Range} range range * @param {TODO} exportPresenceMode export presence mode - * @param {number[]} idRangeStarts range starts for members of ids; the two arrays are right-aligned * @param {Assertions=} assertions assertions + * @param {number[]=} idRangeStarts range starts for members of ids; the two arrays are right-aligned */ constructor( request, @@ -52,8 +52,8 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency { name, range, exportPresenceMode, - idRangeStarts, - assertions + assertions, + idRangeStarts // TODO webpack 6 make this non-optional. It must always be set to properly trim ids. ) { super(request, sourceOrder, assertions); this.ids = ids; @@ -316,13 +316,7 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen if (connection && !connection.isTargetActive(runtime)) return; const ids = dep.getIds(moduleGraph); // determine minimal set of IDs. - const trimmedIds = this._trimIdsToThoseImported(ids, moduleGraph, dep); - const exportExpr = this._getCodeForIds( - dep, - source, - templateContext, - trimmedIds - ); + let trimmedIds = this._trimIdsToThoseImported(ids, moduleGraph, dep); let [rangeStart, rangeEnd] = dep.range; if (trimmedIds.length !== ids.length) { @@ -332,13 +326,23 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen // Use this to find the correct range end position based on the number of ids that were trimmed. const idx = dep.idRangeStarts.length + (trimmedIds.length - ids.length); if (idx < 0 || idx >= dep.idRangeStarts.length) { - throw new Error( - `Missing range starts data for id replacement trimming.` - ); + // cspell:ignore minifiers + // Should not happen but we can't throw an error here because of backward compatibility with + // external plugins in wp5. Instead, we just disable trimming for now. This may break some minifiers. + trimmedIds = ids; + // TODO webpack 6 remove the "trimmedIds = ids" above and uncomment the following line instead. + // throw new Error("Missing range starts data for id replacement trimming."); + } else { + rangeEnd = dep.idRangeStarts[idx]; } - rangeEnd = dep.idRangeStarts[idx]; } + const exportExpr = this._getCodeForIds( + dep, + source, + templateContext, + trimmedIds + ); if (dep.shorthand) { source.insert(rangeEnd, `: ${exportExpr}`); } else { From 5ab7e50503aba34aaec73d346e16b0bf54e4f98e Mon Sep 17 00:00:00 2001 From: Ben Worline Date: Wed, 31 May 2023 08:14:40 -0700 Subject: [PATCH 18/18] handle null dep.idRangeStarts --- lib/dependencies/HarmonyImportSpecifierDependency.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/dependencies/HarmonyImportSpecifierDependency.js b/lib/dependencies/HarmonyImportSpecifierDependency.js index 689113e6259..c6ff4f3a9ea 100644 --- a/lib/dependencies/HarmonyImportSpecifierDependency.js +++ b/lib/dependencies/HarmonyImportSpecifierDependency.js @@ -324,7 +324,10 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen // Meaning, the two arrays may not always have the same number of elements, but the last element of // dep.idRangeStarts corresponds to [the starting range position of] the last element of dep.getIds. // Use this to find the correct range end position based on the number of ids that were trimmed. - const idx = dep.idRangeStarts.length + (trimmedIds.length - ids.length); + const idx = + dep.idRangeStarts === undefined + ? -1 /* trigger failure case below */ + : dep.idRangeStarts.length + (trimmedIds.length - ids.length); if (idx < 0 || idx >= dep.idRangeStarts.length) { // cspell:ignore minifiers // Should not happen but we can't throw an error here because of backward compatibility with