| /* |
| MIT License http://www.opensource.org/licenses/mit-license.php |
| Author Tobias Koppers @sokra |
| */ |
| "use strict"; |
| |
| const DependencyReference = require("./DependencyReference"); |
| const HarmonyImportDependency = require("./HarmonyImportDependency"); |
| const Template = require("../Template"); |
| const HarmonyLinkingError = require("../HarmonyLinkingError"); |
| |
| /** @typedef {import("../Module")} Module */ |
| |
| /** @typedef {"missing"|"unused"|"empty-star"|"reexport-non-harmony-default"|"reexport-named-default"|"reexport-namespace-object"|"reexport-non-harmony-default-strict"|"reexport-fake-namespace-object"|"rexport-non-harmony-undefined"|"safe-reexport"|"checked-reexport"|"dynamic-reexport"} ExportModeType */ |
| |
| /** @type {Map<string, string>} */ |
| const EMPTY_MAP = new Map(); |
| |
| class ExportMode { |
| /** |
| * @param {ExportModeType} type type of the mode |
| */ |
| constructor(type) { |
| /** @type {ExportModeType} */ |
| this.type = type; |
| /** @type {string|null} */ |
| this.name = null; |
| /** @type {Map<string, string>} */ |
| this.map = EMPTY_MAP; |
| /** @type {Module|null} */ |
| this.module = null; |
| /** @type {string|null} */ |
| this.userRequest = null; |
| } |
| } |
| |
| const EMPTY_STAR_MODE = new ExportMode("empty-star"); |
| |
| class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency { |
| constructor( |
| request, |
| originModule, |
| sourceOrder, |
| parserScope, |
| id, |
| name, |
| activeExports, |
| otherStarExports, |
| strictExportPresence |
| ) { |
| super(request, originModule, sourceOrder, parserScope); |
| this.id = id; |
| this.redirectedId = undefined; |
| this.name = name; |
| this.activeExports = activeExports; |
| this.otherStarExports = otherStarExports; |
| this.strictExportPresence = strictExportPresence; |
| } |
| |
| get type() { |
| return "harmony export imported specifier"; |
| } |
| |
| get _id() { |
| return this.redirectedId || this.id; |
| } |
| |
| getMode(ignoreUnused) { |
| const name = this.name; |
| const id = this._id; |
| const used = this.originModule.isUsed(name); |
| const importedModule = this._module; |
| |
| if (!importedModule) { |
| const mode = new ExportMode("missing"); |
| mode.userRequest = this.userRequest; |
| return mode; |
| } |
| |
| if ( |
| !ignoreUnused && |
| (name ? !used : this.originModule.usedExports === false) |
| ) { |
| const mode = new ExportMode("unused"); |
| mode.name = name || "*"; |
| return mode; |
| } |
| |
| const strictHarmonyModule = this.originModule.buildMeta.strictHarmonyModule; |
| if (name && id === "default" && importedModule.buildMeta) { |
| if (!importedModule.buildMeta.exportsType) { |
| const mode = new ExportMode( |
| strictHarmonyModule |
| ? "reexport-non-harmony-default-strict" |
| : "reexport-non-harmony-default" |
| ); |
| mode.name = name; |
| mode.module = importedModule; |
| return mode; |
| } else if (importedModule.buildMeta.exportsType === "named") { |
| const mode = new ExportMode("reexport-named-default"); |
| mode.name = name; |
| mode.module = importedModule; |
| return mode; |
| } |
| } |
| |
| const isNotAHarmonyModule = |
| importedModule.buildMeta && !importedModule.buildMeta.exportsType; |
| if (name) { |
| let mode; |
| if (id) { |
| // export { name as name } |
| if (isNotAHarmonyModule && strictHarmonyModule) { |
| mode = new ExportMode("rexport-non-harmony-undefined"); |
| mode.name = name; |
| } else { |
| mode = new ExportMode("safe-reexport"); |
| mode.map = new Map([[name, id]]); |
| } |
| } else { |
| // export { * as name } |
| if (isNotAHarmonyModule && strictHarmonyModule) { |
| mode = new ExportMode("reexport-fake-namespace-object"); |
| mode.name = name; |
| } else { |
| mode = new ExportMode("reexport-namespace-object"); |
| mode.name = name; |
| } |
| } |
| mode.module = importedModule; |
| return mode; |
| } |
| |
| const hasUsedExports = Array.isArray(this.originModule.usedExports); |
| const hasProvidedExports = Array.isArray( |
| importedModule.buildMeta.providedExports |
| ); |
| const activeFromOtherStarExports = this._discoverActiveExportsFromOtherStartExports(); |
| |
| // export * |
| if (hasUsedExports) { |
| // reexport * with known used exports |
| if (hasProvidedExports) { |
| const map = new Map( |
| this.originModule.usedExports |
| .filter(id => { |
| if (id === "default") return false; |
| if (this.activeExports.has(id)) return false; |
| if (activeFromOtherStarExports.has(id)) return false; |
| if (!importedModule.buildMeta.providedExports.includes(id)) |
| return false; |
| return true; |
| }) |
| .map(item => [item, item]) |
| ); |
| |
| if (map.size === 0) { |
| return EMPTY_STAR_MODE; |
| } |
| |
| const mode = new ExportMode("safe-reexport"); |
| mode.module = importedModule; |
| mode.map = map; |
| return mode; |
| } |
| |
| const map = new Map( |
| this.originModule.usedExports |
| .filter(id => { |
| if (id === "default") return false; |
| if (this.activeExports.has(id)) return false; |
| if (activeFromOtherStarExports.has(id)) return false; |
| |
| return true; |
| }) |
| .map(item => [item, item]) |
| ); |
| |
| if (map.size === 0) { |
| return EMPTY_STAR_MODE; |
| } |
| |
| const mode = new ExportMode("checked-reexport"); |
| mode.module = importedModule; |
| mode.map = map; |
| return mode; |
| } |
| |
| if (hasProvidedExports) { |
| const map = new Map( |
| importedModule.buildMeta.providedExports |
| .filter(id => { |
| if (id === "default") return false; |
| if (this.activeExports.has(id)) return false; |
| if (activeFromOtherStarExports.has(id)) return false; |
| |
| return true; |
| }) |
| .map(item => [item, item]) |
| ); |
| |
| if (map.size === 0) { |
| return EMPTY_STAR_MODE; |
| } |
| |
| const mode = new ExportMode("safe-reexport"); |
| mode.module = importedModule; |
| mode.map = map; |
| return mode; |
| } |
| |
| const mode = new ExportMode("dynamic-reexport"); |
| mode.module = importedModule; |
| return mode; |
| } |
| |
| getReference() { |
| const mode = this.getMode(false); |
| |
| switch (mode.type) { |
| case "missing": |
| case "unused": |
| case "empty-star": |
| return null; |
| |
| case "reexport-non-harmony-default": |
| case "reexport-named-default": |
| return new DependencyReference( |
| mode.module, |
| ["default"], |
| false, |
| this.sourceOrder |
| ); |
| |
| case "reexport-namespace-object": |
| case "reexport-non-harmony-default-strict": |
| case "reexport-fake-namespace-object": |
| case "rexport-non-harmony-undefined": |
| return new DependencyReference( |
| mode.module, |
| true, |
| false, |
| this.sourceOrder |
| ); |
| |
| case "safe-reexport": |
| case "checked-reexport": |
| return new DependencyReference( |
| mode.module, |
| Array.from(mode.map.values()), |
| false, |
| this.sourceOrder |
| ); |
| |
| case "dynamic-reexport": |
| return new DependencyReference( |
| mode.module, |
| true, |
| false, |
| this.sourceOrder |
| ); |
| |
| default: |
| throw new Error(`Unknown mode ${mode.type}`); |
| } |
| } |
| |
| _discoverActiveExportsFromOtherStartExports() { |
| if (!this.otherStarExports) return new Set(); |
| const result = new Set(); |
| // try to learn impossible exports from other star exports with provided exports |
| for (const otherStarExport of this.otherStarExports) { |
| const otherImportedModule = otherStarExport._module; |
| if ( |
| otherImportedModule && |
| Array.isArray(otherImportedModule.buildMeta.providedExports) |
| ) { |
| for (const exportName of otherImportedModule.buildMeta |
| .providedExports) { |
| result.add(exportName); |
| } |
| } |
| } |
| return result; |
| } |
| |
| getExports() { |
| if (this.name) { |
| return { |
| exports: [this.name], |
| dependencies: undefined |
| }; |
| } |
| |
| const importedModule = this._module; |
| |
| if (!importedModule) { |
| // no imported module available |
| return { |
| exports: null, |
| dependencies: undefined |
| }; |
| } |
| |
| if (Array.isArray(importedModule.buildMeta.providedExports)) { |
| return { |
| exports: importedModule.buildMeta.providedExports.filter( |
| id => id !== "default" |
| ), |
| dependencies: [importedModule] |
| }; |
| } |
| |
| if (importedModule.buildMeta.providedExports) { |
| return { |
| exports: true, |
| dependencies: undefined |
| }; |
| } |
| |
| return { |
| exports: null, |
| dependencies: [importedModule] |
| }; |
| } |
| |
| getWarnings() { |
| if ( |
| this.strictExportPresence || |
| this.originModule.buildMeta.strictHarmonyModule |
| ) { |
| return []; |
| } |
| return this._getErrors(); |
| } |
| |
| getErrors() { |
| if ( |
| this.strictExportPresence || |
| this.originModule.buildMeta.strictHarmonyModule |
| ) { |
| return this._getErrors(); |
| } |
| return []; |
| } |
| |
| _getErrors() { |
| const importedModule = this._module; |
| if (!importedModule) { |
| return; |
| } |
| |
| if (!importedModule.buildMeta || !importedModule.buildMeta.exportsType) { |
| // It's not an harmony module |
| if ( |
| this.originModule.buildMeta.strictHarmonyModule && |
| this._id && |
| this._id !== "default" |
| ) { |
| // In strict harmony modules we only support the default export |
| return [ |
| new HarmonyLinkingError( |
| `Can't reexport the named export '${this._id}' from non EcmaScript module (only default export is available)` |
| ) |
| ]; |
| } |
| return; |
| } |
| |
| if (!this._id) { |
| return; |
| } |
| |
| if (importedModule.isProvided(this._id) !== false) { |
| // It's provided or we are not sure |
| return; |
| } |
| |
| // We are sure that it's not provided |
| const idIsNotNameMessage = |
| this._id !== this.name ? ` (reexported as '${this.name}')` : ""; |
| const errorMessage = `"export '${this._id}'${idIsNotNameMessage} was not found in '${this.userRequest}'`; |
| return [new HarmonyLinkingError(errorMessage)]; |
| } |
| |
| updateHash(hash) { |
| super.updateHash(hash); |
| const hashValue = this.getHashValue(this._module); |
| hash.update(hashValue); |
| } |
| |
| getHashValue(importedModule) { |
| if (!importedModule) { |
| return ""; |
| } |
| |
| const stringifiedUsedExport = JSON.stringify(importedModule.usedExports); |
| const stringifiedProvidedExport = JSON.stringify( |
| importedModule.buildMeta.providedExports |
| ); |
| return ( |
| importedModule.used + stringifiedUsedExport + stringifiedProvidedExport |
| ); |
| } |
| |
| disconnect() { |
| super.disconnect(); |
| this.redirectedId = undefined; |
| } |
| } |
| |
| module.exports = HarmonyExportImportedSpecifierDependency; |
| |
| HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedSpecifierDependencyTemplate extends HarmonyImportDependency.Template { |
| harmonyInit(dep, source, runtime, dependencyTemplates) { |
| super.harmonyInit(dep, source, runtime, dependencyTemplates); |
| const content = this.getContent(dep); |
| source.insert(-1, content); |
| } |
| |
| getHarmonyInitOrder(dep) { |
| if (dep.name) { |
| const used = dep.originModule.isUsed(dep.name); |
| if (!used) return NaN; |
| } else { |
| const importedModule = dep._module; |
| |
| const activeFromOtherStarExports = dep._discoverActiveExportsFromOtherStartExports(); |
| |
| if (Array.isArray(dep.originModule.usedExports)) { |
| // we know which exports are used |
| |
| const unused = dep.originModule.usedExports.every(id => { |
| if (id === "default") return true; |
| if (dep.activeExports.has(id)) return true; |
| if (importedModule.isProvided(id) === false) return true; |
| if (activeFromOtherStarExports.has(id)) return true; |
| return false; |
| }); |
| if (unused) return NaN; |
| } else if ( |
| dep.originModule.usedExports && |
| importedModule && |
| Array.isArray(importedModule.buildMeta.providedExports) |
| ) { |
| // not sure which exports are used, but we know which are provided |
| |
| const unused = importedModule.buildMeta.providedExports.every(id => { |
| if (id === "default") return true; |
| if (dep.activeExports.has(id)) return true; |
| if (activeFromOtherStarExports.has(id)) return true; |
| return false; |
| }); |
| if (unused) return NaN; |
| } |
| } |
| return super.getHarmonyInitOrder(dep); |
| } |
| |
| getContent(dep) { |
| const mode = dep.getMode(false); |
| const module = dep.originModule; |
| const importedModule = dep._module; |
| const importVar = dep.getImportVar(); |
| |
| switch (mode.type) { |
| case "missing": |
| return `throw new Error(${JSON.stringify( |
| `Cannot find module '${mode.userRequest}'` |
| )});\n`; |
| |
| case "unused": |
| return `${Template.toNormalComment( |
| `unused harmony reexport ${mode.name}` |
| )}\n`; |
| |
| case "reexport-non-harmony-default": |
| return ( |
| "/* harmony reexport (default from non-harmony) */ " + |
| this.getReexportStatement( |
| module, |
| module.isUsed(mode.name), |
| importVar, |
| null |
| ) |
| ); |
| |
| case "reexport-named-default": |
| return ( |
| "/* harmony reexport (default from named exports) */ " + |
| this.getReexportStatement( |
| module, |
| module.isUsed(mode.name), |
| importVar, |
| "" |
| ) |
| ); |
| |
| case "reexport-fake-namespace-object": |
| return ( |
| "/* harmony reexport (fake namespace object from non-harmony) */ " + |
| this.getReexportFakeNamespaceObjectStatement( |
| module, |
| module.isUsed(mode.name), |
| importVar |
| ) |
| ); |
| |
| case "rexport-non-harmony-undefined": |
| return ( |
| "/* harmony reexport (non default export from non-harmony) */ " + |
| this.getReexportStatement( |
| module, |
| module.isUsed(mode.name), |
| "undefined", |
| "" |
| ) |
| ); |
| |
| case "reexport-non-harmony-default-strict": |
| return ( |
| "/* harmony reexport (default from non-harmony) */ " + |
| this.getReexportStatement( |
| module, |
| module.isUsed(mode.name), |
| importVar, |
| "" |
| ) |
| ); |
| |
| case "reexport-namespace-object": |
| return ( |
| "/* harmony reexport (module object) */ " + |
| this.getReexportStatement( |
| module, |
| module.isUsed(mode.name), |
| importVar, |
| "" |
| ) |
| ); |
| |
| case "empty-star": |
| return "/* empty/unused harmony star reexport */"; |
| |
| case "safe-reexport": |
| return Array.from(mode.map.entries()) |
| .map(item => { |
| return ( |
| "/* harmony reexport (safe) */ " + |
| this.getReexportStatement( |
| module, |
| module.isUsed(item[0]), |
| importVar, |
| importedModule.isUsed(item[1]) |
| ) + |
| "\n" |
| ); |
| }) |
| .join(""); |
| |
| case "checked-reexport": |
| return Array.from(mode.map.entries()) |
| .map(item => { |
| return ( |
| "/* harmony reexport (checked) */ " + |
| this.getConditionalReexportStatement( |
| module, |
| item[0], |
| importVar, |
| item[1] |
| ) + |
| "\n" |
| ); |
| }) |
| .join(""); |
| |
| case "dynamic-reexport": { |
| const activeExports = new Set([ |
| ...dep.activeExports, |
| ...dep._discoverActiveExportsFromOtherStartExports() |
| ]); |
| let content = |
| "/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in " + |
| importVar + |
| ") "; |
| |
| // Filter out exports which are defined by other exports |
| // and filter out default export because it cannot be reexported with * |
| if (activeExports.size > 0) { |
| content += |
| "if(" + |
| JSON.stringify(Array.from(activeExports).concat("default")) + |
| ".indexOf(__WEBPACK_IMPORT_KEY__) < 0) "; |
| } else { |
| content += "if(__WEBPACK_IMPORT_KEY__ !== 'default') "; |
| } |
| const exportsName = dep.originModule.exportsArgument; |
| return ( |
| content + |
| `(function(key) { __webpack_require__.d(${exportsName}, key, function() { return ${importVar}[key]; }) }(__WEBPACK_IMPORT_KEY__));\n` |
| ); |
| } |
| |
| default: |
| throw new Error(`Unknown mode ${mode.type}`); |
| } |
| } |
| |
| getReexportStatement(module, key, name, valueKey) { |
| const exportsName = module.exportsArgument; |
| const returnValue = this.getReturnValue(name, valueKey); |
| return `__webpack_require__.d(${exportsName}, ${JSON.stringify( |
| key |
| )}, function() { return ${returnValue}; });\n`; |
| } |
| |
| getReexportFakeNamespaceObjectStatement(module, key, name) { |
| const exportsName = module.exportsArgument; |
| return `__webpack_require__.d(${exportsName}, ${JSON.stringify( |
| key |
| )}, function() { return __webpack_require__.t(${name}); });\n`; |
| } |
| |
| getConditionalReexportStatement(module, key, name, valueKey) { |
| if (valueKey === false) { |
| return "/* unused export */\n"; |
| } |
| const exportsName = module.exportsArgument; |
| const returnValue = this.getReturnValue(name, valueKey); |
| return `if(__webpack_require__.o(${name}, ${JSON.stringify( |
| valueKey |
| )})) __webpack_require__.d(${exportsName}, ${JSON.stringify( |
| key |
| )}, function() { return ${returnValue}; });\n`; |
| } |
| |
| getReturnValue(name, valueKey) { |
| if (valueKey === null) { |
| return `${name}_default.a`; |
| } |
| if (valueKey === "") { |
| return name; |
| } |
| if (valueKey === false) { |
| return "/* unused export */ undefined"; |
| } |
| |
| return `${name}[${JSON.stringify(valueKey)}]`; |
| } |
| }; |