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

Normalize property access for imports and exports #17137

Merged
merged 8 commits into from
May 16, 2023
Merged
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
5 changes: 3 additions & 2 deletions lib/dependencies/HarmonyExportExpressionDependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
const ConcatenationScope = require("../ConcatenationScope");
const RuntimeGlobals = require("../RuntimeGlobals");
const makeSerializable = require("../util/makeSerializable");
const propertyAccess = require("../util/propertyAccess");
const HarmonyExportInitFragment = require("./HarmonyExportInitFragment");
const NullDependency = require("./NullDependency");

Expand Down Expand Up @@ -172,9 +173,9 @@ HarmonyExportExpressionDependency.Template = class HarmonyExportDependencyTempla
if (used) {
runtimeRequirements.add(RuntimeGlobals.exports);
// This is a little bit incorrect as TDZ is not correct, but we can't use const.
content = `/* harmony default export */ ${exportsName}[${JSON.stringify(
content = `/* harmony default export */ ${exportsName}${propertyAccess(
used
)}] = `;
)} = `;
} else {
content = `/* unused harmony default export */ var ${name} = `;
}
Expand Down
3 changes: 2 additions & 1 deletion lib/dependencies/HarmonyExportImportedSpecifierDependency.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const { countIterable } = require("../util/IterableHelpers");
const { first, combine } = require("../util/SetHelpers");
const makeSerializable = require("../util/makeSerializable");
const propertyAccess = require("../util/propertyAccess");
const { propertyName } = require("../util/propertyName");
const { getRuntimeKey, keyToRuntime } = require("../util/runtime");
const HarmonyExportInitFragment = require("./HarmonyExportInitFragment");
const HarmonyImportDependency = require("./HarmonyImportDependency");
Expand Down Expand Up @@ -1219,7 +1220,7 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
valueKey[0]
)})) ${
RuntimeGlobals.definePropertyGetters
}(${exportsName}, { ${JSON.stringify(
}(${exportsName}, { ${propertyName(
key
)}: function() { return ${returnValue}; } });\n`;
}
Expand Down
3 changes: 2 additions & 1 deletion lib/dependencies/HarmonyExportInitFragment.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
const InitFragment = require("../InitFragment");
const RuntimeGlobals = require("../RuntimeGlobals");
const { first } = require("../util/SetHelpers");
const { propertyName } = require("../util/propertyName");

/** @typedef {import("webpack-sources").Source} Source */
/** @typedef {import("../Generator").GenerateContext} GenerateContext */
Expand Down Expand Up @@ -150,7 +151,7 @@ class HarmonyExportInitFragment extends InitFragment {
);
for (const [key, value] of orderedExportMap) {
definitions.push(
`\n/* harmony export */ ${JSON.stringify(
`\n/* harmony export */ ${propertyName(
key
)}: ${runtimeTemplate.returningFunction(value)}`
);
Expand Down
9 changes: 5 additions & 4 deletions lib/optimize/ConcatenatedModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const createHash = require("../util/createHash");
const { makePathsRelative } = require("../util/identifier");
const makeSerializable = require("../util/makeSerializable");
const propertyAccess = require("../util/propertyAccess");
const { propertyName } = require("../util/propertyName");
const {
filterRuntime,
intersectRuntime,
Expand Down Expand Up @@ -1484,7 +1485,7 @@ class ConcatenatedModule extends Module {
const definitions = [];
for (const [key, value] of exportsMap) {
definitions.push(
`\n ${JSON.stringify(key)}: ${runtimeTemplate.returningFunction(
`\n ${propertyName(key)}: ${runtimeTemplate.returningFunction(
value(requestShortener)
)}`
);
Expand Down Expand Up @@ -1529,9 +1530,9 @@ class ConcatenatedModule extends Module {
true
);
nsObj.push(
`\n ${JSON.stringify(
usedName
)}: ${runtimeTemplate.returningFunction(finalName)}`
`\n ${propertyName(usedName)}: ${runtimeTemplate.returningFunction(
finalName
)}`
);
}
}
Expand Down
58 changes: 4 additions & 54 deletions lib/util/propertyAccess.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,10 @@

"use strict";

const SAFE_IDENTIFIER = /^[_a-zA-Z$][_a-zA-Z$0-9]*$/;
const RESERVED_IDENTIFIER = new Set([
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"export",
"extends",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"new",
"return",
"super",
"switch",
"this",
"throw",
"try",
"typeof",
"var",
"void",
"while",
"with",
"enum",
// strict mode
"implements",
"interface",
"let",
"package",
"private",
"protected",
"public",
"static",
"yield",
"yield",
// module code
"await",
// skip future reserved keywords defined under ES1 till ES3
// additional
"null",
"true",
"false"
]);
const {
SAFE_IDENTIFIER,
RESERVED_IDENTIFIER
} = require("../util/propertyName");

/**
* @param {ArrayLike<string>} properties properties
Expand Down
79 changes: 79 additions & 0 deletions lib/util/propertyName.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/

"use strict";

const SAFE_IDENTIFIER = /^[_a-zA-Z$][_a-zA-Z$0-9]*$/;
const RESERVED_IDENTIFIER = new Set([
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"export",
"extends",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"new",
"return",
"super",
"switch",
"this",
"throw",
"try",
"typeof",
"var",
"void",
"while",
"with",
"enum",
// strict mode
"implements",
"interface",
"let",
"package",
"private",
"protected",
"public",
"static",
"yield",
"yield",
// module code
"await",
// skip future reserved keywords defined under ES1 till ES3
// additional
"null",
"true",
"false"
]);

/**
* @summary Returns a valid JS property name for the given property.
* Certain strings like "default", "null", and names with whitespace are not
* valid JS property names, so they are returned as strings.
*
* @param {string} prop property name to analyze
* @returns {string} valid JS property name
*/
const propertyName = prop => {
if (SAFE_IDENTIFIER.test(prop) && !RESERVED_IDENTIFIER.has(prop)) {
return prop;
} else {
return JSON.stringify(prop);
}
};

module.exports = { SAFE_IDENTIFIER, RESERVED_IDENTIFIER, propertyName };