| 1 | /*
|
|---|
| 2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
|---|
| 3 | Author Tobias Koppers @sokra
|
|---|
| 4 | */
|
|---|
| 5 |
|
|---|
| 6 | "use strict";
|
|---|
| 7 |
|
|---|
| 8 | const CompatibilityPlugin = require("../CompatibilityPlugin");
|
|---|
| 9 | const WebpackError = require("../errors/WebpackError");
|
|---|
| 10 | const { getImportAttributes } = require("../javascript/JavascriptParser");
|
|---|
| 11 | const InnerGraph = require("../optimize/InnerGraph");
|
|---|
| 12 | const ConstDependency = require("./ConstDependency");
|
|---|
| 13 | const HarmonyExportExpressionDependency = require("./HarmonyExportExpressionDependency");
|
|---|
| 14 | const HarmonyExportHeaderDependency = require("./HarmonyExportHeaderDependency");
|
|---|
| 15 | const HarmonyExportImportedSpecifierDependency = require("./HarmonyExportImportedSpecifierDependency");
|
|---|
| 16 | const HarmonyExportSpecifierDependency = require("./HarmonyExportSpecifierDependency");
|
|---|
| 17 | const { ExportPresenceModes } = require("./HarmonyImportDependency");
|
|---|
| 18 | const {
|
|---|
| 19 | harmonySpecifierTag
|
|---|
| 20 | } = require("./HarmonyImportDependencyParserPlugin");
|
|---|
| 21 | const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");
|
|---|
| 22 | const { ImportPhaseUtils, createGetImportPhase } = require("./ImportPhase");
|
|---|
| 23 |
|
|---|
| 24 | /** @typedef {import("../../declarations/WebpackOptions").JavascriptParserOptions} JavascriptParserOptions */
|
|---|
| 25 | /** @typedef {import("../Dependency").DependencyLocation} DependencyLocation */
|
|---|
| 26 | /** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
|
|---|
| 27 | /** @typedef {import("../javascript/JavascriptParser").ClassDeclaration} ClassDeclaration */
|
|---|
| 28 | /** @typedef {import("../javascript/JavascriptParser").FunctionDeclaration} FunctionDeclaration */
|
|---|
| 29 | /** @typedef {import("../javascript/JavascriptParser").Range} Range */
|
|---|
| 30 | /** @typedef {import("./HarmonyImportDependencyParserPlugin").HarmonySettings} HarmonySettings */
|
|---|
| 31 | /** @typedef {import("../CompatibilityPlugin").CompatibilitySettings} CompatibilitySettings */
|
|---|
| 32 |
|
|---|
| 33 | const { HarmonyStarExportsList } = HarmonyExportImportedSpecifierDependency;
|
|---|
| 34 |
|
|---|
| 35 | const PLUGIN_NAME = "HarmonyExportDependencyParserPlugin";
|
|---|
| 36 |
|
|---|
| 37 | module.exports = class HarmonyExportDependencyParserPlugin {
|
|---|
| 38 | /**
|
|---|
| 39 | * Creates an instance of HarmonyExportDependencyParserPlugin.
|
|---|
| 40 | * @param {JavascriptParserOptions} options options
|
|---|
| 41 | */
|
|---|
| 42 | constructor(options) {
|
|---|
| 43 | this.options = options;
|
|---|
| 44 | this.exportPresenceMode = ExportPresenceModes.resolveFromOptions(
|
|---|
| 45 | options.reexportExportsPresence,
|
|---|
| 46 | options
|
|---|
| 47 | );
|
|---|
| 48 | }
|
|---|
| 49 |
|
|---|
| 50 | /**
|
|---|
| 51 | * Applies the plugin by registering its hooks on the compiler.
|
|---|
| 52 | * @param {JavascriptParser} parser the parser
|
|---|
| 53 | * @returns {void}
|
|---|
| 54 | */
|
|---|
| 55 | apply(parser) {
|
|---|
| 56 | const { exportPresenceMode } = this;
|
|---|
| 57 | const getImportPhase = createGetImportPhase(
|
|---|
| 58 | this.options.deferImport,
|
|---|
| 59 | false
|
|---|
| 60 | );
|
|---|
| 61 |
|
|---|
| 62 | parser.hooks.export.tap(PLUGIN_NAME, (statement) => {
|
|---|
| 63 | const dep = new HarmonyExportHeaderDependency(
|
|---|
| 64 | /** @type {Range | false} */ (
|
|---|
| 65 | statement.declaration && statement.declaration.range
|
|---|
| 66 | ),
|
|---|
| 67 | /** @type {Range} */ (statement.range)
|
|---|
| 68 | );
|
|---|
| 69 | dep.loc = Object.create(
|
|---|
| 70 | /** @type {DependencyLocation} */ (statement.loc)
|
|---|
| 71 | );
|
|---|
| 72 | dep.loc.index = -1;
|
|---|
| 73 | parser.state.module.addPresentationalDependency(dep);
|
|---|
| 74 | return true;
|
|---|
| 75 | });
|
|---|
| 76 | parser.hooks.exportImport.tap(PLUGIN_NAME, (statement, source) => {
|
|---|
| 77 | parser.state.lastHarmonyImportOrder =
|
|---|
| 78 | (parser.state.lastHarmonyImportOrder || 0) + 1;
|
|---|
| 79 | const clearDep = new ConstDependency(
|
|---|
| 80 | "",
|
|---|
| 81 | /** @type {Range} */ (statement.range)
|
|---|
| 82 | );
|
|---|
| 83 | clearDep.loc = /** @type {DependencyLocation} */ (statement.loc);
|
|---|
| 84 | clearDep.loc.index = -1;
|
|---|
| 85 | parser.state.module.addPresentationalDependency(clearDep);
|
|---|
| 86 |
|
|---|
| 87 | const phase = getImportPhase(parser, statement);
|
|---|
| 88 | if (phase && ImportPhaseUtils.isDefer(phase)) {
|
|---|
| 89 | const error = new WebpackError(
|
|---|
| 90 | "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 };"
|
|---|
| 91 | );
|
|---|
| 92 | error.loc = statement.loc || undefined;
|
|---|
| 93 | parser.state.current.addError(error);
|
|---|
| 94 | }
|
|---|
| 95 | const sideEffectDep = new HarmonyImportSideEffectDependency(
|
|---|
| 96 | /** @type {string} */ (source),
|
|---|
| 97 | parser.state.lastHarmonyImportOrder,
|
|---|
| 98 | phase,
|
|---|
| 99 | getImportAttributes(statement)
|
|---|
| 100 | );
|
|---|
| 101 | sideEffectDep.loc = Object.create(
|
|---|
| 102 | /** @type {DependencyLocation} */ (statement.loc)
|
|---|
| 103 | );
|
|---|
| 104 | sideEffectDep.loc.index = -1;
|
|---|
| 105 | parser.state.current.addDependency(sideEffectDep);
|
|---|
| 106 | return true;
|
|---|
| 107 | });
|
|---|
| 108 | parser.hooks.exportExpression.tap(PLUGIN_NAME, (statement, node) => {
|
|---|
| 109 | const isFunctionDeclaration = node.type === "FunctionDeclaration";
|
|---|
| 110 | const exprRange = /** @type {Range} */ (node.range);
|
|---|
| 111 | const statementRange = /** @type {Range} */ (statement.range);
|
|---|
| 112 | const comments = parser.getComments([statementRange[0], exprRange[0]]);
|
|---|
| 113 | const dep = new HarmonyExportExpressionDependency(
|
|---|
| 114 | exprRange,
|
|---|
| 115 | statementRange,
|
|---|
| 116 | comments
|
|---|
| 117 | .map((c) => {
|
|---|
| 118 | switch (c.type) {
|
|---|
| 119 | case "Block":
|
|---|
| 120 | return `/*${c.value}*/`;
|
|---|
| 121 | case "Line":
|
|---|
| 122 | return `//${c.value}\n`;
|
|---|
| 123 | }
|
|---|
| 124 | return "";
|
|---|
| 125 | })
|
|---|
| 126 | .join(""),
|
|---|
| 127 | node.type.endsWith("Declaration") &&
|
|---|
| 128 | /** @type {FunctionDeclaration | ClassDeclaration} */ (node).id
|
|---|
| 129 | ? /** @type {FunctionDeclaration | ClassDeclaration} */
|
|---|
| 130 | (node).id.name
|
|---|
| 131 | : isFunctionDeclaration
|
|---|
| 132 | ? {
|
|---|
| 133 | range: [
|
|---|
| 134 | exprRange[0],
|
|---|
| 135 | node.params.length > 0
|
|---|
| 136 | ? /** @type {Range} */ (node.params[0].range)[0]
|
|---|
| 137 | : /** @type {Range} */ (node.body.range)[0]
|
|---|
| 138 | ],
|
|---|
| 139 | prefix: `${node.async ? "async " : ""}function${
|
|---|
| 140 | node.generator ? "*" : ""
|
|---|
| 141 | } `,
|
|---|
| 142 | suffix: `(${node.params.length > 0 ? "" : ") "}`
|
|---|
| 143 | }
|
|---|
| 144 | : undefined
|
|---|
| 145 | );
|
|---|
| 146 | dep.isAnonymousDefault =
|
|---|
| 147 | this.options.anonymousDefaultExportName !== false &&
|
|---|
| 148 | (node.type === "ArrowFunctionExpression" ||
|
|---|
| 149 | ((node.type === "FunctionDeclaration" ||
|
|---|
| 150 | node.type === "FunctionExpression" ||
|
|---|
| 151 | node.type === "ClassDeclaration" ||
|
|---|
| 152 | node.type === "ClassExpression") &&
|
|---|
| 153 | !node.id));
|
|---|
| 154 | dep.loc = Object.create(
|
|---|
| 155 | /** @type {DependencyLocation} */ (statement.loc)
|
|---|
| 156 | );
|
|---|
| 157 | dep.loc.index = -1;
|
|---|
| 158 | parser.state.current.addDependency(dep);
|
|---|
| 159 | InnerGraph.addVariableUsage(
|
|---|
| 160 | parser,
|
|---|
| 161 | node.type.endsWith("Declaration") &&
|
|---|
| 162 | /** @type {FunctionDeclaration | ClassDeclaration} */ (node).id
|
|---|
| 163 | ? /** @type {FunctionDeclaration | ClassDeclaration} */ (node).id.name
|
|---|
| 164 | : "*default*",
|
|---|
| 165 | "default"
|
|---|
| 166 | );
|
|---|
| 167 | return true;
|
|---|
| 168 | });
|
|---|
| 169 | parser.hooks.exportSpecifier.tap(
|
|---|
| 170 | PLUGIN_NAME,
|
|---|
| 171 | (statement, id, name, idx) => {
|
|---|
| 172 | // CompatibilityPlugin may change exports name
|
|---|
| 173 | // not handle re-export or import then export situation as current CompatibilityPlugin only
|
|---|
| 174 | // rename symbol in declaration module, not change exported symbol
|
|---|
| 175 | const variable = parser.getTagData(
|
|---|
| 176 | id,
|
|---|
| 177 | CompatibilityPlugin.nestedWebpackIdentifierTag
|
|---|
| 178 | );
|
|---|
| 179 | if (variable && /** @type {CompatibilitySettings} */ (variable).name) {
|
|---|
| 180 | // CompatibilityPlugin changes exports to a new name, should updates exports name
|
|---|
| 181 | id = /** @type {CompatibilitySettings} */ (variable).name;
|
|---|
| 182 | }
|
|---|
| 183 |
|
|---|
| 184 | const settings =
|
|---|
| 185 | /** @type {HarmonySettings} */
|
|---|
| 186 | (parser.getTagData(id, harmonySpecifierTag));
|
|---|
| 187 | const harmonyNamedExports = (parser.state.harmonyNamedExports =
|
|---|
| 188 | parser.state.harmonyNamedExports || new Set());
|
|---|
| 189 | harmonyNamedExports.add(name);
|
|---|
| 190 | InnerGraph.addVariableUsage(parser, id, name);
|
|---|
| 191 | const dep = settings
|
|---|
| 192 | ? new HarmonyExportImportedSpecifierDependency(
|
|---|
| 193 | settings.source,
|
|---|
| 194 | settings.sourceOrder,
|
|---|
| 195 | settings.ids,
|
|---|
| 196 | name,
|
|---|
| 197 | harmonyNamedExports,
|
|---|
| 198 | null,
|
|---|
| 199 | exportPresenceMode,
|
|---|
| 200 | null,
|
|---|
| 201 | settings.phase,
|
|---|
| 202 | settings.attributes
|
|---|
| 203 | )
|
|---|
| 204 | : new HarmonyExportSpecifierDependency(id, name);
|
|---|
| 205 | dep.loc = Object.create(
|
|---|
| 206 | /** @type {DependencyLocation} */ (statement.loc)
|
|---|
| 207 | );
|
|---|
| 208 | dep.loc.index = idx;
|
|---|
| 209 | const isAsiSafe = !parser.isAsiPosition(
|
|---|
| 210 | /** @type {Range} */
|
|---|
| 211 | (statement.range)[0]
|
|---|
| 212 | );
|
|---|
| 213 | if (!isAsiSafe) {
|
|---|
| 214 | parser.setAsiPosition(/** @type {Range} */ (statement.range)[1]);
|
|---|
| 215 | }
|
|---|
| 216 | parser.state.current.addDependency(dep);
|
|---|
| 217 | return true;
|
|---|
| 218 | }
|
|---|
| 219 | );
|
|---|
| 220 | parser.hooks.exportImportSpecifier.tap(
|
|---|
| 221 | PLUGIN_NAME,
|
|---|
| 222 | (statement, source, id, name, idx) => {
|
|---|
| 223 | const harmonyNamedExports = (parser.state.harmonyNamedExports =
|
|---|
| 224 | parser.state.harmonyNamedExports || new Set());
|
|---|
| 225 | /** @type {InstanceType<HarmonyStarExportsList> | null} */
|
|---|
| 226 | let harmonyStarExports = null;
|
|---|
| 227 | if (name) {
|
|---|
| 228 | harmonyNamedExports.add(name);
|
|---|
| 229 | } else {
|
|---|
| 230 | harmonyStarExports = parser.state.harmonyStarExports =
|
|---|
| 231 | parser.state.harmonyStarExports || new HarmonyStarExportsList();
|
|---|
| 232 | }
|
|---|
| 233 | const attributes = getImportAttributes(statement);
|
|---|
| 234 | const dep = new HarmonyExportImportedSpecifierDependency(
|
|---|
| 235 | /** @type {string} */
|
|---|
| 236 | (source),
|
|---|
| 237 | /** @type {number} */
|
|---|
| 238 | (parser.state.lastHarmonyImportOrder),
|
|---|
| 239 | id ? [id] : [],
|
|---|
| 240 | name,
|
|---|
| 241 | harmonyNamedExports,
|
|---|
| 242 | // eslint-disable-next-line unicorn/prefer-spread
|
|---|
| 243 | harmonyStarExports && harmonyStarExports.slice(),
|
|---|
| 244 | exportPresenceMode,
|
|---|
| 245 | harmonyStarExports,
|
|---|
| 246 | getImportPhase(parser, statement),
|
|---|
| 247 | attributes
|
|---|
| 248 | );
|
|---|
| 249 | if (harmonyStarExports) {
|
|---|
| 250 | harmonyStarExports.push(dep);
|
|---|
| 251 | }
|
|---|
| 252 | dep.loc = Object.create(
|
|---|
| 253 | /** @type {DependencyLocation} */ (statement.loc)
|
|---|
| 254 | );
|
|---|
| 255 | dep.loc.index = idx;
|
|---|
| 256 | const isAsiSafe = !parser.isAsiPosition(
|
|---|
| 257 | /** @type {Range} */
|
|---|
| 258 | (statement.range)[0]
|
|---|
| 259 | );
|
|---|
| 260 | if (!isAsiSafe) {
|
|---|
| 261 | parser.setAsiPosition(/** @type {Range} */ (statement.range)[1]);
|
|---|
| 262 | }
|
|---|
| 263 | parser.state.current.addDependency(dep);
|
|---|
| 264 | return true;
|
|---|
| 265 | }
|
|---|
| 266 | );
|
|---|
| 267 | }
|
|---|
| 268 | };
|
|---|