123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- /*
- 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("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
- /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
- /** @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} node node with assertions
- * @returns {Record<string, any> | undefined} assertions
- */
- function getAssertions(node) {
- // TODO remove cast when @types/estree has been updated to import assertions
- const assertions = /** @type {{ assertions?: ImportAttributeNode[] }} */ (
- node
- ).assertions;
- if (assertions === undefined) {
- return undefined;
- }
- const result = {};
- for (const assertion of assertions) {
- const key =
- assertion.key.type === "Identifier"
- ? assertion.key.name
- : assertion.key.value;
- result[key] = assertion.value.value;
- }
- 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(statement.range[0]) ? ";" : "",
- statement.range
- );
- clearDep.loc = statement.loc;
- parser.state.module.addPresentationalDependency(clearDep);
- parser.unsetAsiPosition(statement.range[1]);
- const assertions = getAssertions(statement);
- const sideEffectDep = new HarmonyImportSideEffectDependency(
- source,
- parser.state.lastHarmonyImportOrder,
- assertions
- );
- sideEffectDep.loc = 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: getAssertions(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 (
- !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,
- expression.range,
- settings.assertions,
- "in"
- );
- dep.directImport = members.length === 0;
- dep.asiSafe = !parser.isAsiPosition(expression.range[0]);
- dep.loc = 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,
- expr.range,
- exportPresenceMode,
- settings.assertions
- );
- dep.shorthand = parser.scope.inShorthand;
- dep.directImport = true;
- dep.asiSafe = !parser.isAsiPosition(expr.range[0]);
- dep.loc = expr.loc;
- 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) => {
- const settings = /** @type {HarmonySettings} */ (
- parser.currentTagData
- );
- const nonOptionalMembers = getNonOptionalPart(
- members,
- membersOptionals
- );
- 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,
- expr.range,
- exportPresenceMode,
- settings.assertions
- );
- dep.asiSafe = !parser.isAsiPosition(expr.range[0]);
- dep.loc = 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) => {
- const { arguments: args, callee } = expression;
- const settings = /** @type {HarmonySettings} */ (
- parser.currentTagData
- );
- const nonOptionalMembers = getNonOptionalPart(
- members,
- membersOptionals
- );
- 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,
- expr.range,
- exportPresenceMode,
- settings.assertions
- );
- dep.directImport = members.length === 0;
- dep.call = true;
- dep.asiSafe = !parser.isAsiPosition(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 = 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;
- module.exports.getAssertions = getAssertions;
|