| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
- const CompatibilityPlugin = require("../CompatibilityPlugin");
- const WebpackError = require("../errors/WebpackError");
- const { getImportAttributes } = require("../javascript/JavascriptParser");
- const { CONST_BINDING_TAG } = require("../optimize/ConstExportsPlugin");
- const { toInlinedValue } = require("../optimize/InlineExports");
- const { getInnerGraphUtils } = require("../optimize/InnerGraph");
- const ConstDependency = require("./ConstDependency");
- const HarmonyExportExpressionDependency = require("./HarmonyExportExpressionDependency");
- const HarmonyExportHeaderDependency = require("./HarmonyExportHeaderDependency");
- const HarmonyExportImportedSpecifierDependency = require("./HarmonyExportImportedSpecifierDependency");
- const HarmonyExportSpecifierDependency = require("./HarmonyExportSpecifierDependency");
- const { ExportPresenceModes } = require("./HarmonyImportDependency");
- const {
- harmonySpecifierTag
- } = require("./HarmonyImportDependencyParserPlugin");
- const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");
- const { ImportPhaseUtils, createGetImportPhase } = require("./ImportPhase");
- /**
- * @import {
- * Expression,
- * ClassDeclaration,
- * FunctionDeclaration
- * } from "estree"
- */
- /**
- * @import {
- * JavascriptParserOptions
- * } from "../../declarations/WebpackOptions"
- */
- /** @import JavascriptParser, { Range } from "../javascript/JavascriptParser" */
- /** @import { HarmonySettings } from "./HarmonyImportDependencyParserPlugin" */
- /** @import { CompatibilitySettings } from "../CompatibilityPlugin" */
- const { HarmonyStarExportsList } = HarmonyExportImportedSpecifierDependency;
- const PLUGIN_NAME = "HarmonyExportDependencyParserPlugin";
- module.exports = class HarmonyExportDependencyParserPlugin {
- /**
- * Creates an instance of HarmonyExportDependencyParserPlugin.
- * @param {JavascriptParserOptions} options options
- */
- constructor(options) {
- /** @type {JavascriptParserOptions} */
- this.options = options;
- this.exportPresenceMode = ExportPresenceModes.resolveFromOptions(
- options.reexportExportsPresence,
- options
- );
- }
- /**
- * Applies the plugin by registering its hooks on the compiler.
- * @param {JavascriptParser} parser the parser
- * @returns {void}
- */
- apply(parser) {
- const { exportPresenceMode } = this;
- const getImportPhase = createGetImportPhase(
- this.options.deferImport,
- false
- );
- parser.hooks.export.tap(PLUGIN_NAME, (statement) => {
- const dep = new HarmonyExportHeaderDependency(
- statement.declaration && statement.declaration.range,
- /** @type {Range} */ (statement.range)
- );
- dep.setLocWithIndex(parser.getLocation(statement), -1);
- parser.state.module.addPresentationalDependency(dep);
- return true;
- });
- parser.hooks.exportImport.tap(PLUGIN_NAME, (statement, source) => {
- parser.state.lastHarmonyImportOrder =
- (parser.state.lastHarmonyImportOrder || 0) + 1;
- const clearDep = new ConstDependency(
- "",
- /** @type {Range} */ (statement.range)
- );
- clearDep.setLocWithIndex(parser.getLocation(statement), -1);
- parser.state.module.addPresentationalDependency(clearDep);
- const phase = getImportPhase(parser, statement);
- if (phase && ImportPhaseUtils.isDefer(phase)) {
- const error = new WebpackError(
- "Deferred re-export (`export defer * as namespace from '...'`) is not a part of the Import Defer proposal.\nUse the following code instead:\n import defer * as namespace from '...';\n export { namespace };"
- );
- error.loc = parser.getLocation(statement) || undefined;
- parser.state.current.addError(error);
- }
- const sideEffectDep = new HarmonyImportSideEffectDependency(
- /** @type {string} */ (source),
- parser.state.lastHarmonyImportOrder,
- phase,
- getImportAttributes(statement)
- );
- sideEffectDep.setLocWithIndex(parser.getLocation(statement), -1);
- // lazy barrel: defer at parse time when the importing module is side-effect-free
- const moduleFactoryMeta = parser.state.module.factoryMeta;
- if (moduleFactoryMeta !== undefined && moduleFactoryMeta.sideEffectFree) {
- sideEffectDep.setLazy(true);
- }
- parser.state.current.addDependency(sideEffectDep);
- return true;
- });
- // `export default <namespace binding>` is the same export as
- // `export { ns as default }`, so it is claimed here and rewritten into the
- // same re-export. Stage -10 runs before the inner-graph tap, which would
- // otherwise wrap the identifier in a pure-expression guard whose insertions
- // outlive the statement this removes.
- parser.hooks.statement.tap(
- { name: PLUGIN_NAME, stage: -10 },
- (statement) => {
- if (
- statement.type !== "ExportDefaultDeclaration" ||
- statement.declaration.type !== "Identifier"
- ) {
- return;
- }
- const name = statement.declaration.name;
- const settings =
- /** @type {HarmonySettings | undefined} */
- (parser.getTagData(name, harmonySpecifierTag));
- if (!settings || settings.ids.length !== 0) return;
- if (ImportPhaseUtils.isDefer(settings.phase)) return;
- const harmonyNamedExports = (parser.state.harmonyNamedExports =
- parser.state.harmonyNamedExports || new Set());
- harmonyNamedExports.add("default");
- getInnerGraphUtils(parser.state.compilation).addVariableUsage(
- parser,
- name,
- "default"
- );
- const dep = new HarmonyExportImportedSpecifierDependency(
- settings.source,
- settings.sourceOrder,
- settings.ids,
- "default",
- harmonyNamedExports,
- null,
- exportPresenceMode,
- null,
- settings.phase,
- settings.attributes
- );
- // lazy barrel: defer the re-export at parse time when the importing module is side-effect-free
- const moduleFactoryMeta = parser.state.module.factoryMeta;
- if (
- moduleFactoryMeta !== undefined &&
- moduleFactoryMeta.sideEffectFree
- ) {
- dep.setLazy(true);
- }
- dep.setLocWithIndex(parser.getLocation(statement), -1);
- parser.state.current.addDependency(dep);
- // no range erases the whole statement, as for `export { ns as default }`
- const headerDep = new HarmonyExportHeaderDependency(
- undefined,
- /** @type {Range} */ (statement.range)
- );
- headerDep.setLocWithIndex(parser.getLocation(statement), -1);
- parser.state.module.addPresentationalDependency(headerDep);
- return true;
- }
- );
- parser.hooks.exportExpression.tap(PLUGIN_NAME, (statement, node) => {
- const isFunctionDeclaration = node.type === "FunctionDeclaration";
- const exprRange = /** @type {Range} */ (node.range);
- const statementRange = /** @type {Range} */ (statement.range);
- const comments = parser.getComments([statementRange[0], exprRange[0]]);
- const constValue = node.type.endsWith("Declaration")
- ? undefined
- : toInlinedValue(
- parser.evaluateExpression(/** @type {Expression} */ (node))
- );
- const dep = new HarmonyExportExpressionDependency(
- exprRange,
- statementRange,
- comments
- .map((c) => {
- switch (c.type) {
- case "Block":
- return `/*${c.value}*/`;
- case "Line":
- return `//${c.value}\n`;
- }
- return "";
- })
- .join(""),
- node.type.endsWith("Declaration") &&
- /** @type {FunctionDeclaration | ClassDeclaration} */ (node).id
- ? /** @type {FunctionDeclaration | ClassDeclaration} */
- (node).id.name
- : isFunctionDeclaration
- ? {
- range: [
- exprRange[0],
- node.params.length > 0
- ? /** @type {Range} */ (node.params[0].range)[0]
- : /** @type {Range} */ (node.body.range)[0]
- ],
- prefix: `${node.async ? "async " : ""}function${
- node.generator ? "*" : ""
- } `,
- suffix: `(${node.params.length > 0 ? "" : ") "}`
- }
- : undefined,
- constValue
- );
- dep.isAnonymousDefault =
- this.options.anonymousDefaultExportName !== false &&
- (node.type === "ArrowFunctionExpression" ||
- ((node.type === "FunctionDeclaration" ||
- node.type === "FunctionExpression" ||
- node.type === "ClassDeclaration" ||
- node.type === "ClassExpression") &&
- !node.id));
- dep.setLocWithIndex(parser.getLocation(statement), -1);
- parser.state.current.addDependency(dep);
- getInnerGraphUtils(parser.state.compilation).addVariableUsage(
- parser,
- node.type.endsWith("Declaration") &&
- /** @type {FunctionDeclaration | ClassDeclaration} */ (node).id
- ? /** @type {FunctionDeclaration | ClassDeclaration} */ (node).id.name
- : "*default*",
- "default"
- );
- return true;
- });
- parser.hooks.exportSpecifier.tap(
- PLUGIN_NAME,
- (statement, id, name, idx) => {
- // CompatibilityPlugin may change exports name
- // not handle re-export or import then export situation as current CompatibilityPlugin only
- // rename symbol in declaration module, not change exported symbol
- const variable = parser.getTagData(
- id,
- CompatibilityPlugin.nestedWebpackIdentifierTag
- );
- if (variable && /** @type {CompatibilitySettings} */ (variable).name) {
- // CompatibilityPlugin changes exports to a new name, should updates exports name
- id = /** @type {CompatibilitySettings} */ (variable).name;
- }
- const settings =
- /** @type {HarmonySettings} */
- (parser.getTagData(id, harmonySpecifierTag));
- const harmonyNamedExports = (parser.state.harmonyNamedExports =
- parser.state.harmonyNamedExports || new Set());
- harmonyNamedExports.add(name);
- getInnerGraphUtils(parser.state.compilation).addVariableUsage(
- parser,
- id,
- name
- );
- const tagData = settings
- ? undefined
- : /** @type {{ value?: import("../optimize/InlineExports").InlinedValue } | undefined} */
- (parser.getTagData(id, CONST_BINDING_TAG));
- const dep = settings
- ? new HarmonyExportImportedSpecifierDependency(
- settings.source,
- settings.sourceOrder,
- settings.ids,
- name,
- harmonyNamedExports,
- null,
- exportPresenceMode,
- null,
- settings.phase,
- settings.attributes
- )
- : new HarmonyExportSpecifierDependency(
- id,
- name,
- tagData ? tagData.value || null : undefined
- );
- // lazy barrel: defer the re-export at parse time when the importing module is side-effect-free
- const moduleFactoryMeta = parser.state.module.factoryMeta;
- if (
- settings &&
- moduleFactoryMeta !== undefined &&
- moduleFactoryMeta.sideEffectFree
- ) {
- dep.setLazy(true);
- }
- dep.setLocWithIndex(
- parser.getLocation(statement),
- /** @type {number} */ (idx)
- );
- const isAsiSafe = !parser.isAsiPosition(
- /** @type {Range} */
- (statement.range)[0]
- );
- if (!isAsiSafe) {
- parser.setAsiPosition(/** @type {Range} */ (statement.range)[1]);
- }
- parser.state.current.addDependency(dep);
- return true;
- }
- );
- parser.hooks.exportImportSpecifier.tap(
- PLUGIN_NAME,
- (statement, source, id, name, idx) => {
- const harmonyNamedExports = (parser.state.harmonyNamedExports =
- parser.state.harmonyNamedExports || new Set());
- /** @type {InstanceType<HarmonyStarExportsList> | null} */
- let harmonyStarExports = null;
- if (name) {
- harmonyNamedExports.add(name);
- } else {
- harmonyStarExports = parser.state.harmonyStarExports =
- parser.state.harmonyStarExports || new HarmonyStarExportsList();
- }
- const attributes = getImportAttributes(statement);
- const dep = new HarmonyExportImportedSpecifierDependency(
- /** @type {string} */
- (source),
- /** @type {number} */
- (parser.state.lastHarmonyImportOrder),
- id ? [id] : [],
- name,
- harmonyNamedExports,
- // eslint-disable-next-line unicorn/prefer-spread
- harmonyStarExports && harmonyStarExports.slice(),
- exportPresenceMode,
- harmonyStarExports,
- getImportPhase(parser, statement),
- attributes
- );
- if (harmonyStarExports) {
- harmonyStarExports.push(dep);
- }
- // lazy barrel: defer the re-export at parse time when the importing module is side-effect-free
- const moduleFactoryMeta = parser.state.module.factoryMeta;
- if (
- moduleFactoryMeta !== undefined &&
- moduleFactoryMeta.sideEffectFree
- ) {
- dep.setLazy(true);
- }
- dep.setLocWithIndex(
- parser.getLocation(statement),
- /** @type {number} */ (idx)
- );
- const isAsiSafe = !parser.isAsiPosition(
- /** @type {Range} */
- (statement.range)[0]
- );
- if (!isAsiSafe) {
- parser.setAsiPosition(/** @type {Range} */ (statement.range)[1]);
- }
- parser.state.current.addDependency(dep);
- return true;
- }
- );
- }
- };
|