123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
- const HotModuleReplacementPlugin = require("../HotModuleReplacementPlugin");
- const InnerGraph = require("../optimize/InnerGraph");
- const ConstDependency = require("./ConstDependency");
- const HarmonyAcceptDependency = require("./HarmonyAcceptDependency");
- const HarmonyAcceptImportDependency = require("./HarmonyAcceptImportDependency");
- const HarmonyEvaluatedImportSpecifierDependency = require("./HarmonyEvaluatedImportSpecifierDependency");
- const HarmonyExports = require("./HarmonyExports");
- const { ExportPresenceModes } = require("./HarmonyImportDependency");
- const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");
- const HarmonyImportSpecifierDependency = require("./HarmonyImportSpecifierDependency");
- /** @typedef {import("estree").ExportAllDeclaration} ExportAllDeclaration */
- /** @typedef {import("estree").ExportNamedDeclaration} ExportNamedDeclaration */
- /** @typedef {import("estree").Identifier} Identifier */
- /** @typedef {import("estree").ImportDeclaration} ImportDeclaration */
- /** @typedef {import("estree").ImportExpression} ImportExpression */
- /** @typedef {import("estree").Literal} Literal */
- /** @typedef {import("estree").MemberExpression} MemberExpression */
- /** @typedef {import("estree").ObjectExpression} ObjectExpression */
- /** @typedef {import("estree").Property} Property */
- /** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
- /** @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").ImportAttributes} ImportAttributes */
- /** @typedef {import("../javascript/JavascriptParser").Range} Range */
- /** @typedef {import("../optimize/InnerGraph").InnerGraph} InnerGraph */
- /** @typedef {import("../optimize/InnerGraph").TopLevelSymbol} TopLevelSymbol */
- /** @typedef {import("./HarmonyImportDependency")} HarmonyImportDependency */
- const harmonySpecifierTag = Symbol("harmony import");
- /**
- * @typedef {object} HarmonySettings
- * @property {string[]} ids
- * @property {string} source
- * @property {number} sourceOrder
- * @property {string} name
- * @property {boolean} await
- * @property {Record<string, any> | undefined} assertions
- */
- /**
- * @param {ImportDeclaration | ExportNamedDeclaration | ExportAllDeclaration | (ImportExpression & { arguments?: ObjectExpression[] })} node node with assertions
- * @returns {ImportAttributes} import attributes
- */
- function getAttributes(node) {
- if (
- node.type === "ImportExpression" &&
- node.arguments &&
- node.arguments[0] &&
- node.arguments[0].type === "ObjectExpression" &&
- node.arguments[0].properties[0] &&
- node.arguments[0].properties[0].type === "Property" &&
- node.arguments[0].properties[0].value.type === "ObjectExpression" &&
- node.arguments[0].properties[0].value.properties
- ) {
- const properties =
- /** @type {Property[]} */
- (node.arguments[0].properties[0].value.properties);
- const result = /** @type {ImportAttributes} */ ({});
- for (const property of properties) {
- const key =
- /** @type {string} */
- (
- property.key.type === "Identifier"
- ? property.key.name
- : /** @type {Literal} */ (property.key).value
- );
- result[key] =
- /** @type {string} */
- (/** @type {Literal} */ (property.value).value);
- }
- const key =
- node.arguments[0].properties[0].key.type === "Identifier"
- ? node.arguments[0].properties[0].key.name
- : /** @type {Literal} */ (node.arguments[0].properties[0].key).value;
- if (key === "assert") {
- result._isLegacyAssert = true;
- }
- return result;
- }
- // TODO remove cast when @types/estree has been updated to import assertions
- const isImportAssertion =
- /** @type {{ assertions?: ImportAttributeNode[] }} */ (node).assertions !==
- undefined;
- const attributes = isImportAssertion
- ? /** @type {{ assertions?: ImportAttributeNode[] }} */ (node).assertions
- : /** @type {{ attributes?: ImportAttributeNode[] }} */ (node).attributes;
- if (attributes === undefined) {
- return undefined;
- }
- const result = /** @type {ImportAttributes} */ ({});
- for (const attribute of attributes) {
- const key =
- /** @type {string} */
- (
- attribute.key.type === "Identifier"
- ? attribute.key.name
- : attribute.key.value
- );
- result[key] = /** @type {string} */ (attribute.value.value);
- }
- if (isImportAssertion) {
- result._isLegacyAssert = true;
- }
- return result;
- }
- module.exports = class HarmonyImportDependencyParserPlugin {
- /**
- * @param {JavascriptParserOptions} options options
- */
- constructor(options) {
- this.exportPresenceMode =
- options.importExportsPresence !== undefined
- ? ExportPresenceModes.fromUserOption(options.importExportsPresence)
- : options.exportsPresence !== undefined
- ? ExportPresenceModes.fromUserOption(options.exportsPresence)
- : options.strictExportPresence
- ? ExportPresenceModes.ERROR
- : ExportPresenceModes.AUTO;
- this.strictThisContextOnImports = options.strictThisContextOnImports;
- }
- /**
- * @param {JavascriptParser} parser the parser
- * @returns {void}
- */
- apply(parser) {
- const { exportPresenceMode } = this;
- function getNonOptionalPart(members, membersOptionals) {
- let i = 0;
- while (i < members.length && membersOptionals[i] === false) i++;
- return i !== members.length ? members.slice(0, i) : members;
- }
- function getNonOptionalMemberChain(node, count) {
- while (count--) node = node.object;
- return node;
- }
- parser.hooks.isPure
- .for("Identifier")
- .tap("HarmonyImportDependencyParserPlugin", expression => {
- const expr = /** @type {Identifier} */ (expression);
- if (
- parser.isVariableDefined(expr.name) ||
- parser.getTagData(expr.name, harmonySpecifierTag)
- ) {
- return true;
- }
- });
- parser.hooks.import.tap(
- "HarmonyImportDependencyParserPlugin",
- (statement, source) => {
- parser.state.lastHarmonyImportOrder =
- (parser.state.lastHarmonyImportOrder || 0) + 1;
- const clearDep = new ConstDependency(
- parser.isAsiPosition(/** @type {Range} */ (statement.range)[0])
- ? ";"
- : "",
- /** @type {Range} */ (statement.range)
- );
- clearDep.loc = /** @type {DependencyLocation} */ (statement.loc);
- parser.state.module.addPresentationalDependency(clearDep);
- parser.unsetAsiPosition(/** @type {Range} */ (statement.range)[1]);
- const attributes = getAttributes(statement);
- const sideEffectDep = new HarmonyImportSideEffectDependency(
- /** @type {string} */ (source),
- parser.state.lastHarmonyImportOrder,
- attributes
- );
- sideEffectDep.loc = /** @type {DependencyLocation} */ (statement.loc);
- parser.state.module.addDependency(sideEffectDep);
- return true;
- }
- );
- parser.hooks.importSpecifier.tap(
- "HarmonyImportDependencyParserPlugin",
- (statement, source, id, name) => {
- const ids = id === null ? [] : [id];
- parser.tagVariable(name, harmonySpecifierTag, {
- name,
- source,
- ids,
- sourceOrder: parser.state.lastHarmonyImportOrder,
- assertions: getAttributes(statement)
- });
- return true;
- }
- );
- parser.hooks.binaryExpression.tap(
- "HarmonyImportDependencyParserPlugin",
- expression => {
- if (expression.operator !== "in") return;
- const leftPartEvaluated = parser.evaluateExpression(expression.left);
- if (leftPartEvaluated.couldHaveSideEffects()) return;
- const leftPart = leftPartEvaluated.asString();
- if (!leftPart) return;
- const rightPart = parser.evaluateExpression(expression.right);
- if (!rightPart.isIdentifier()) return;
- const rootInfo = rightPart.rootInfo;
- if (
- typeof rootInfo === "string" ||
- !rootInfo ||
- !rootInfo.tagInfo ||
- rootInfo.tagInfo.tag !== harmonySpecifierTag
- )
- return;
- const settings = rootInfo.tagInfo.data;
- const members = rightPart.getMembers();
- const dep = new HarmonyEvaluatedImportSpecifierDependency(
- settings.source,
- settings.sourceOrder,
- settings.ids.concat(members).concat([leftPart]),
- settings.name,
- /** @type {Range} */ (expression.range),
- settings.assertions,
- "in"
- );
- dep.directImport = members.length === 0;
- dep.asiSafe = !parser.isAsiPosition(
- /** @type {Range} */ (expression.range)[0]
- );
- dep.loc = /** @type {DependencyLocation} */ (expression.loc);
- parser.state.module.addDependency(dep);
- InnerGraph.onUsage(parser.state, e => (dep.usedByExports = e));
- return true;
- }
- );
- parser.hooks.expression
- .for(harmonySpecifierTag)
- .tap("HarmonyImportDependencyParserPlugin", expr => {
- const settings = /** @type {HarmonySettings} */ (parser.currentTagData);
- const dep = new HarmonyImportSpecifierDependency(
- settings.source,
- settings.sourceOrder,
- settings.ids,
- settings.name,
- /** @type {Range} */ (expr.range),
- exportPresenceMode,
- settings.assertions,
- []
- );
- dep.referencedPropertiesInDestructuring =
- parser.destructuringAssignmentPropertiesFor(expr);
- dep.shorthand = parser.scope.inShorthand;
- dep.directImport = true;
- dep.asiSafe = !parser.isAsiPosition(
- /** @type {Range} */ (expr.range)[0]
- );
- dep.loc = /** @type {DependencyLocation} */ (expr.loc);
- dep.call = parser.scope.inTaggedTemplateTag;
- parser.state.module.addDependency(dep);
- InnerGraph.onUsage(parser.state, e => (dep.usedByExports = e));
- return true;
- });
- parser.hooks.expressionMemberChain
- .for(harmonySpecifierTag)
- .tap(
- "HarmonyImportDependencyParserPlugin",
- (expression, members, membersOptionals, memberRanges) => {
- const settings = /** @type {HarmonySettings} */ (
- parser.currentTagData
- );
- const nonOptionalMembers = getNonOptionalPart(
- members,
- membersOptionals
- );
- const ranges = memberRanges.slice(
- 0,
- memberRanges.length - (members.length - nonOptionalMembers.length)
- );
- const expr =
- nonOptionalMembers !== members
- ? getNonOptionalMemberChain(
- expression,
- members.length - nonOptionalMembers.length
- )
- : expression;
- const ids = settings.ids.concat(nonOptionalMembers);
- const dep = new HarmonyImportSpecifierDependency(
- settings.source,
- settings.sourceOrder,
- ids,
- settings.name,
- /** @type {Range} */ (expr.range),
- exportPresenceMode,
- settings.assertions,
- ranges
- );
- dep.referencedPropertiesInDestructuring =
- parser.destructuringAssignmentPropertiesFor(expr);
- dep.asiSafe = !parser.isAsiPosition(
- /** @type {Range} */ (expr.range)[0]
- );
- dep.loc = /** @type {DependencyLocation} */ (expr.loc);
- parser.state.module.addDependency(dep);
- InnerGraph.onUsage(parser.state, e => (dep.usedByExports = e));
- return true;
- }
- );
- parser.hooks.callMemberChain
- .for(harmonySpecifierTag)
- .tap(
- "HarmonyImportDependencyParserPlugin",
- (expression, members, membersOptionals, memberRanges) => {
- const { arguments: args, callee } = expression;
- const settings = /** @type {HarmonySettings} */ (
- parser.currentTagData
- );
- const nonOptionalMembers = getNonOptionalPart(
- members,
- membersOptionals
- );
- const ranges = memberRanges.slice(
- 0,
- memberRanges.length - (members.length - nonOptionalMembers.length)
- );
- const expr =
- nonOptionalMembers !== members
- ? getNonOptionalMemberChain(
- callee,
- members.length - nonOptionalMembers.length
- )
- : callee;
- const ids = settings.ids.concat(nonOptionalMembers);
- const dep = new HarmonyImportSpecifierDependency(
- settings.source,
- settings.sourceOrder,
- ids,
- settings.name,
- /** @type {Range} */ (expr.range),
- exportPresenceMode,
- settings.assertions,
- ranges
- );
- dep.directImport = members.length === 0;
- dep.call = true;
- dep.asiSafe = !parser.isAsiPosition(
- /** @type {Range} */ (expr.range)[0]
- );
- // only in case when we strictly follow the spec we need a special case here
- dep.namespaceObjectAsContext =
- members.length > 0 && this.strictThisContextOnImports;
- dep.loc = /** @type {DependencyLocation} */ (expr.loc);
- parser.state.module.addDependency(dep);
- if (args) parser.walkExpressions(args);
- InnerGraph.onUsage(parser.state, e => (dep.usedByExports = e));
- return true;
- }
- );
- const { hotAcceptCallback, hotAcceptWithoutCallback } =
- HotModuleReplacementPlugin.getParserHooks(parser);
- hotAcceptCallback.tap(
- "HarmonyImportDependencyParserPlugin",
- (expr, requests) => {
- if (!HarmonyExports.isEnabled(parser.state)) {
- // This is not a harmony module, skip it
- return;
- }
- const dependencies = requests.map(request => {
- const dep = new HarmonyAcceptImportDependency(request);
- dep.loc = expr.loc;
- parser.state.module.addDependency(dep);
- return dep;
- });
- if (dependencies.length > 0) {
- const dep = new HarmonyAcceptDependency(
- expr.range,
- dependencies,
- true
- );
- dep.loc = expr.loc;
- parser.state.module.addDependency(dep);
- }
- }
- );
- hotAcceptWithoutCallback.tap(
- "HarmonyImportDependencyParserPlugin",
- (expr, requests) => {
- if (!HarmonyExports.isEnabled(parser.state)) {
- // This is not a harmony module, skip it
- return;
- }
- const dependencies = requests.map(request => {
- const dep = new HarmonyAcceptImportDependency(request);
- dep.loc = expr.loc;
- parser.state.module.addDependency(dep);
- return dep;
- });
- if (dependencies.length > 0) {
- const dep = new HarmonyAcceptDependency(
- expr.range,
- dependencies,
- false
- );
- dep.loc = expr.loc;
- parser.state.module.addDependency(dep);
- }
- }
- );
- }
- };
- module.exports.harmonySpecifierTag = harmonySpecifierTag;
- // TODO remove it in webpack@6 in favor getAttributes
- module.exports.getAssertions = getAttributes;
- module.exports.getAttributes = getAttributes;
|