Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: mangle with destructuring #18319

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions lib/DefinePlugin.js
Expand Up @@ -27,6 +27,7 @@ const createHash = require("./util/createHash");
/** @typedef {import("./NormalModule")} NormalModule */
/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
/** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */
/** @typedef {import("./javascript/JavascriptParser").DestructuringAssignmentProperty} DestructuringAssignmentProperty */
/** @typedef {import("./javascript/JavascriptParser").Range} Range */
/** @typedef {import("./logging/Logger").Logger} Logger */

Expand Down Expand Up @@ -114,6 +115,15 @@ class RuntimeValue {
}
}

/**
* @param {Set<DestructuringAssignmentProperty> | undefined} properties properties
* @returns {Set<string> | undefined} used keys
*/
function getObjKeys(properties) {
if (!properties) return undefined;
return new Set([...properties].map(p => p.id));
}

/**
* @param {any[]|{[k: string]: any}} obj obj
* @param {JavascriptParser} parser Parser
Expand Down Expand Up @@ -491,7 +501,7 @@ class DefinePlugin {
runtimeTemplate,
logger,
!parser.isAsiPosition(/** @type {Range} */ (expr.range)[0]),
parser.destructuringAssignmentPropertiesFor(expr)
getObjKeys(parser.destructuringAssignmentPropertiesFor(expr))
);

if (parser.scope.inShorthand) {
Expand Down Expand Up @@ -597,7 +607,7 @@ class DefinePlugin {
runtimeTemplate,
logger,
!parser.isAsiPosition(/** @type {Range} */ (expr.range)[0]),
parser.destructuringAssignmentPropertiesFor(expr)
getObjKeys(parser.destructuringAssignmentPropertiesFor(expr))
);

if (parser.scope.inShorthand) {
Expand Down
188 changes: 188 additions & 0 deletions lib/dependencies/HarmonyDestructuredImportSpecifierDependency.js
@@ -0,0 +1,188 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Gengkun He @ahabhgk
*/

"use strict";

const Template = require("../Template");
const makeSerializable = require("../util/makeSerializable");
const HarmonyImportSpecifierDependency = require("./HarmonyImportSpecifierDependency");

/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
/** @typedef {import("../javascript/JavascriptParser").Attributes} Attributes */
/** @typedef {import("../javascript/JavascriptParser").DestructuringAssignmentProperty} DestructuringAssignmentProperty */
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */

/**
* @param {DestructuringAssignmentProperty} property property
* @param {((property: DestructuringAssignmentProperty) => void) | undefined=} onProperty on property
* @param {((property: DestructuringAssignmentProperty) => void) | undefined=} onClean on property end
* @param {(() => void) | undefined=} onTerminate on a terminal property
*/
function traversePropertyInDestructuring(
property,
onProperty,
onClean,
onTerminate
) {
onProperty && onProperty(property);
if (!property.pattern) {
onTerminate && onTerminate();
} else {
for (const p of property.pattern) {
traversePropertyInDestructuring(p, onProperty, onClean, onTerminate);
}
}
onClean && onClean(property);
}

class HarmonyDestructuredImportSpecifierDependency extends HarmonyImportSpecifierDependency {
/**
* @param {string} request the request string
* @param {number} sourceOrder source order
* @param {string[]} ids ids
* @param {string} name name
* @param {Range} range range
* @param {TODO} exportPresenceMode export presence mode
* @param {Attributes | undefined} attributes assertions
* @param {Range[] | undefined} idRanges ranges for members of ids; the two arrays are right-aligned
*/
constructor(
request,
sourceOrder,
ids,
name,
range,
exportPresenceMode,
attributes,
idRanges
) {
super(
request,
sourceOrder,
ids,
name,
range,
exportPresenceMode,
attributes,
idRanges
);
/** @type {{ ids: string[], range: Range | undefined, shorthand: boolean | string }[]} */
this.flattenedProperties = [];
/** @type {string[][]} */
this.allIdsInDestructuring = [];
}

get type() {
return "destructured harmony import specifier";
}

/**
* @param {Set<DestructuringAssignmentProperty>} properties destructuring assignment properties
*/
setDestructuringAssignmentProperties(properties) {
for (const property of properties) {
const ids = [];
traversePropertyInDestructuring(
property,
({ id, range, shorthand }) => {
ids.push(id);
this.flattenedProperties.push({
ids: [...ids],
range,
shorthand
});
},
() => ids.pop(),
() => this.allIdsInDestructuring.push([...ids])
);
}
}

/**
* @param {string[]=} ids ids
* @returns {string[][]} referenced exports
*/
_getReferencedExportsInDestructuring(ids) {
return this.allIdsInDestructuring.map(idsInDestructuring =>
ids ? ids.concat(idsInDestructuring) : idsInDestructuring
);
}

/**
* @param {ObjectSerializerContext} context context
*/
serialize(context) {
const { write } = context;
write(this.properties);
write(this.flattenedProperties);
write(this.allIdsInDestructuring);
super.serialize(context);
}

/**
* @param {ObjectDeserializerContext} context context
*/
deserialize(context) {
const { read } = context;
this.properties = read();
this.flattenedProperties = read();
this.allIdsInDestructuring = read();
super.deserialize(context);
}
}

HarmonyDestructuredImportSpecifierDependency.Template = class HarmonyDestructuredImportSpecifierDependencyTemplate extends (
HarmonyImportSpecifierDependency.Template
) {
/**
* @param {Dependency} dependency the dependency for which the template should be applied
* @param {ReplaceSource} source the current replace source which can be modified
* @param {DependencyTemplateContext} templateContext the context object
* @returns {void}
*/
apply(dependency, source, templateContext) {
super.apply(dependency, source, templateContext);

const { moduleGraph, runtime } = templateContext;
const dep = /** @type {HarmonyDestructuredImportSpecifierDependency} */ (
dependency
);
const { flattenedProperties } = dep;
for (const {
ids: idsInDestructuring,
range,
shorthand
} of flattenedProperties) {
const ids = dep.ids.concat(idsInDestructuring);
if (ids[0] === "default") ids.shift();
const module = moduleGraph.getModule(dep);
const used = moduleGraph.getExportsInfo(module).getUsedName(ids, runtime);
if (!used) return;
const newName = used[used.length - 1];
const name = ids[ids.length - 1];
if (newName === name) continue;

const comment = Template.toNormalComment(name) + " ";
const key = comment + JSON.stringify(newName);
source.replace(
range[0],
range[1] - 1,
shorthand ? `${key}: ${name}` : `${key}`
);
}
}
};

makeSerializable(
HarmonyDestructuredImportSpecifierDependency,
"webpack/lib/dependencies/HarmonyDestructuredImportSpecifierDependency"
);

module.exports = HarmonyDestructuredImportSpecifierDependency;
28 changes: 22 additions & 6 deletions lib/dependencies/HarmonyImportDependencyParserPlugin.js
Expand Up @@ -10,6 +10,7 @@ const InnerGraph = require("../optimize/InnerGraph");
const ConstDependency = require("./ConstDependency");
const HarmonyAcceptDependency = require("./HarmonyAcceptDependency");
const HarmonyAcceptImportDependency = require("./HarmonyAcceptImportDependency");
const HarmonyDestructuredImportSpecifierDependency = require("./HarmonyDestructuredImportSpecifierDependency");
const HarmonyEvaluatedImportSpecifierDependency = require("./HarmonyEvaluatedImportSpecifierDependency");
const HarmonyExports = require("./HarmonyExports");
const { ExportPresenceModes } = require("./HarmonyImportDependency");
Expand All @@ -29,6 +30,7 @@ const HarmonyImportSpecifierDependency = require("./HarmonyImportSpecifierDepend
/** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
/** @typedef {import("../javascript/BasicEvaluatedExpression")} BasicEvaluatedExpression */
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
/** @typedef {import("../javascript/JavascriptParser").DestructuringAssignmentProperty} DestructuringAssignmentProperty */
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
/** @typedef {import("../optimize/InnerGraph").InnerGraph} InnerGraph */
/** @typedef {import("../optimize/InnerGraph").TopLevelSymbol} TopLevelSymbol */
Expand Down Expand Up @@ -222,7 +224,11 @@ module.exports = class HarmonyImportDependencyParserPlugin {
.for(harmonySpecifierTag)
.tap("HarmonyImportDependencyParserPlugin", expr => {
const settings = /** @type {HarmonySettings} */ (parser.currentTagData);
const dep = new HarmonyImportSpecifierDependency(
const properties = parser.destructuringAssignmentPropertiesFor(expr);
const Dep = properties
? HarmonyDestructuredImportSpecifierDependency
: HarmonyImportSpecifierDependency;
const dep = new Dep(
settings.source,
settings.sourceOrder,
settings.ids,
Expand All @@ -232,8 +238,11 @@ module.exports = class HarmonyImportDependencyParserPlugin {
settings.assertions,
[]
);
dep.referencedPropertiesInDestructuring =
parser.destructuringAssignmentPropertiesFor(expr);
if (properties) {
/** @type {HarmonyDestructuredImportSpecifierDependency} */ (
dep
).setDestructuringAssignmentProperties(properties);
}
dep.shorthand = parser.scope.inShorthand;
dep.directImport = true;
dep.asiSafe = !parser.isAsiPosition(
Expand Down Expand Up @@ -269,7 +278,11 @@ module.exports = class HarmonyImportDependencyParserPlugin {
)
: expression;
const ids = settings.ids.concat(nonOptionalMembers);
const dep = new HarmonyImportSpecifierDependency(
const properties = parser.destructuringAssignmentPropertiesFor(expr);
const Dep = properties
? HarmonyDestructuredImportSpecifierDependency
: HarmonyImportSpecifierDependency;
const dep = new Dep(
settings.source,
settings.sourceOrder,
ids,
Expand All @@ -279,8 +292,11 @@ module.exports = class HarmonyImportDependencyParserPlugin {
settings.assertions,
ranges
);
dep.referencedPropertiesInDestructuring =
parser.destructuringAssignmentPropertiesFor(expr);
if (properties) {
/** @type {HarmonyDestructuredImportSpecifierDependency} */ (
dep
).setDestructuringAssignmentProperties(properties);
}
dep.asiSafe = !parser.isAsiPosition(
/** @type {Range} */ (expr.range)[0]
);
Expand Down