Add semantic token highlighting for 'that' variable and comment file references in VS Code extension Add Phone_Text_Input and Currency_Input components with formatting utilities Implement client widgets, form standardization, and soft delete functionality Add modal scroll lock and update documentation Implement comprehensive modal system with form integration and validation Fix modal component instantiation using jQuery plugin API Implement modal system with responsive sizing, queuing, and validation support Implement form submission with validation, error handling, and loading states Implement country/state selectors with dynamic data loading and Bootstrap styling Revert Rsx::Route() highlighting in Blade/PHP files Target specific PHP scopes for Rsx::Route() highlighting in Blade Expand injection selector for Rsx::Route() highlighting Add custom syntax highlighting for Rsx::Route() and Rsx.Route() calls Update jqhtml packages to v2.2.165 Add bundle path validation for common mistakes (development mode only) Create Ajax_Select_Input widget and Rsx_Reference_Data controller Create Country_Select_Input widget with default country support Initialize Tom Select on Select_Input widgets Add Tom Select bundle for enhanced select dropdowns Implement ISO 3166 geographic data system for country/region selection Implement widget-based form system with disabled state support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2233 lines
66 KiB
JavaScript
Executable File
2233 lines
66 KiB
JavaScript
Executable File
/*
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
Author Tobias Koppers @sokra
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const eslintScope = require("eslint-scope");
|
|
const Referencer = require("eslint-scope/lib/referencer");
|
|
const { SyncBailHook } = require("tapable");
|
|
const {
|
|
CachedSource,
|
|
ConcatSource,
|
|
ReplaceSource
|
|
} = require("webpack-sources");
|
|
const ConcatenationScope = require("../ConcatenationScope");
|
|
const { UsageState } = require("../ExportsInfo");
|
|
const Module = require("../Module");
|
|
const { JS_TYPES } = require("../ModuleSourceTypesConstants");
|
|
const { JAVASCRIPT_MODULE_TYPE_ESM } = require("../ModuleTypeConstants");
|
|
const RuntimeGlobals = require("../RuntimeGlobals");
|
|
const Template = require("../Template");
|
|
const { DEFAULTS } = require("../config/defaults");
|
|
const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
|
|
const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency");
|
|
const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency");
|
|
const JavascriptParser = require("../javascript/JavascriptParser");
|
|
const {
|
|
getMakeDeferredNamespaceModeFromExportsType,
|
|
getOptimizedDeferredModule
|
|
} = require("../runtime/MakeDeferredNamespaceObjectRuntime");
|
|
const { equals } = require("../util/ArrayHelpers");
|
|
const LazySet = require("../util/LazySet");
|
|
const { concatComparators } = require("../util/comparators");
|
|
const {
|
|
RESERVED_NAMES,
|
|
addScopeSymbols,
|
|
findNewName,
|
|
getAllReferences,
|
|
getPathInAst,
|
|
getUsedNamesInScopeInfo
|
|
} = require("../util/concatenate");
|
|
const createHash = require("../util/createHash");
|
|
const { makePathsRelative } = require("../util/identifier");
|
|
const makeSerializable = require("../util/makeSerializable");
|
|
const propertyAccess = require("../util/propertyAccess");
|
|
const { propertyName } = require("../util/propertyName");
|
|
const {
|
|
filterRuntime,
|
|
intersectRuntime,
|
|
mergeRuntimeCondition,
|
|
mergeRuntimeConditionNonFalse,
|
|
runtimeConditionToString,
|
|
subtractRuntimeCondition
|
|
} = require("../util/runtime");
|
|
|
|
/** @typedef {import("eslint-scope").Reference} Reference */
|
|
/** @typedef {import("eslint-scope").Scope} Scope */
|
|
/** @typedef {import("eslint-scope").Variable} Variable */
|
|
/** @typedef {import("webpack-sources").Source} Source */
|
|
/** @typedef {import("../config/defaults").WebpackOptionsNormalizedWithDefaults} WebpackOptions */
|
|
/** @typedef {import("../ChunkGraph")} ChunkGraph */
|
|
/** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
|
|
/** @typedef {import("../Compilation")} Compilation */
|
|
/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
|
|
/** @typedef {import("../DependencyTemplates")} DependencyTemplates */
|
|
/** @typedef {import("../ExportsInfo").ExportInfo} ExportInfo */
|
|
/** @typedef {import("../Module").BuildCallback} BuildCallback */
|
|
/** @typedef {import("../Module").BuildInfo} BuildInfo */
|
|
/** @typedef {import("../Module").FileSystemDependencies} FileSystemDependencies */
|
|
/** @typedef {import("../Module").BuildMeta} BuildMeta */
|
|
/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
|
|
/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
|
|
/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */
|
|
/** @typedef {import("../Module").LibIdent} LibIdent */
|
|
/** @typedef {import("../Module").NameForCondition} NameForCondition */
|
|
/** @typedef {import("../Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
|
|
/** @typedef {import("../Module").RuntimeRequirements} RuntimeRequirements */
|
|
/** @typedef {import("../Module").SourceTypes} SourceTypes */
|
|
/** @typedef {import("../ModuleGraph")} ModuleGraph */
|
|
/** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */
|
|
/** @typedef {import("../ModuleGraphConnection").ConnectionState} ConnectionState */
|
|
/** @typedef {import("../RequestShortener")} RequestShortener */
|
|
/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
|
|
/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
|
|
/** @typedef {import("../javascript/JavascriptModulesPlugin").ChunkRenderContext} ChunkRenderContext */
|
|
/** @typedef {import("../javascript/JavascriptParser").Program} Program */
|
|
/** @typedef {import("../javascript/JavascriptParser").Range} Range */
|
|
/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
|
|
/** @typedef {import("../util/Hash")} Hash */
|
|
/** @typedef {typeof import("../util/Hash")} HashConstructor */
|
|
/** @typedef {import("../util/concatenate").ScopeInfo} ScopeInfo */
|
|
/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
|
|
/** @typedef {import("../util/identifier").AssociatedObjectForCache} AssociatedObjectForCache */
|
|
/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
|
|
/**
|
|
* @template T
|
|
* @typedef {import("../InitFragment")<T>} InitFragment
|
|
*/
|
|
|
|
/**
|
|
* @template T
|
|
* @typedef {import("../util/comparators").Comparator<T>} Comparator
|
|
*/
|
|
|
|
// fix eslint-scope to support class properties correctly
|
|
// cspell:word Referencer
|
|
const ReferencerClass = Referencer;
|
|
if (!ReferencerClass.prototype.PropertyDefinition) {
|
|
ReferencerClass.prototype.PropertyDefinition =
|
|
ReferencerClass.prototype.Property;
|
|
}
|
|
|
|
/** @typedef {RawBinding | SymbolBinding} Binding */
|
|
|
|
/** @typedef {string[]} ExportName */
|
|
|
|
/**
|
|
* @typedef {object} RawBinding
|
|
* @property {ModuleInfo} info
|
|
* @property {string} rawName
|
|
* @property {string=} comment
|
|
* @property {ExportName} ids
|
|
* @property {ExportName} exportName
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} SymbolBinding
|
|
* @property {ConcatenatedModuleInfo} info
|
|
* @property {string} name
|
|
* @property {string=} comment
|
|
* @property {ExportName} ids
|
|
* @property {ExportName} exportName
|
|
*/
|
|
|
|
/** @typedef {ConcatenatedModuleInfo | ExternalModuleInfo } ModuleInfo */
|
|
/** @typedef {ConcatenatedModuleInfo | ExternalModuleInfo | ReferenceToModuleInfo } ModuleInfoOrReference */
|
|
|
|
/**
|
|
* @typedef {object} ConcatenatedModuleInfo
|
|
* @property {"concatenated"} type
|
|
* @property {Module} module
|
|
* @property {number} index
|
|
* @property {Program | undefined} ast
|
|
* @property {Source | undefined} internalSource
|
|
* @property {ReplaceSource | undefined} source
|
|
* @property {InitFragment<ChunkRenderContext>[]=} chunkInitFragments
|
|
* @property {ReadOnlyRuntimeRequirements | undefined} runtimeRequirements
|
|
* @property {Scope | undefined} globalScope
|
|
* @property {Scope | undefined} moduleScope
|
|
* @property {Map<string, string>} internalNames
|
|
* @property {Map<string, string> | undefined} exportMap
|
|
* @property {Map<string, string> | undefined} rawExportMap
|
|
* @property {string=} namespaceExportSymbol
|
|
* @property {string | undefined} namespaceObjectName
|
|
* @property {ConcatenationScope | undefined} concatenationScope
|
|
* @property {boolean} interopNamespaceObjectUsed "default-with-named" namespace
|
|
* @property {string | undefined} interopNamespaceObjectName "default-with-named" namespace
|
|
* @property {boolean} interopNamespaceObject2Used "default-only" namespace
|
|
* @property {string | undefined} interopNamespaceObject2Name "default-only" namespace
|
|
* @property {boolean} interopDefaultAccessUsed runtime namespace object that detects "__esModule"
|
|
* @property {string | undefined} interopDefaultAccessName runtime namespace object that detects "__esModule"
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} ExternalModuleInfo
|
|
* @property {"external"} type
|
|
* @property {Module} module
|
|
* @property {RuntimeSpec | boolean} runtimeCondition
|
|
* @property {number} index
|
|
* @property {string | undefined} name module.exports / harmony namespace object
|
|
* @property {string | undefined} deferredName deferred module.exports / harmony namespace object
|
|
* @property {boolean} deferred the module is deferred at least once
|
|
* @property {boolean} deferredNamespaceObjectUsed deferred namespace object that being used in a not-analyzable way so it must be materialized
|
|
* @property {string | undefined} deferredNamespaceObjectName deferred namespace object that being used in a not-analyzable way so it must be materialized
|
|
* @property {boolean} interopNamespaceObjectUsed "default-with-named" namespace
|
|
* @property {string | undefined} interopNamespaceObjectName "default-with-named" namespace
|
|
* @property {boolean} interopNamespaceObject2Used "default-only" namespace
|
|
* @property {string | undefined} interopNamespaceObject2Name "default-only" namespace
|
|
* @property {boolean} interopDefaultAccessUsed runtime namespace object that detects "__esModule"
|
|
* @property {string | undefined} interopDefaultAccessName runtime namespace object that detects "__esModule"
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} ReferenceToModuleInfo
|
|
* @property {"reference"} type
|
|
* @property {RuntimeSpec | boolean} runtimeCondition
|
|
* @property {ModuleInfo} target
|
|
*/
|
|
|
|
/**
|
|
* @template T
|
|
* @param {string} property property
|
|
* @param {function(T[keyof T], T[keyof T]): 0 | 1 | -1} comparator comparator
|
|
* @returns {Comparator<T>} comparator
|
|
*/
|
|
|
|
const createComparator = (property, comparator) => (a, b) =>
|
|
comparator(
|
|
a[/** @type {keyof T} */ (property)],
|
|
b[/** @type {keyof T} */ (property)]
|
|
);
|
|
|
|
/**
|
|
* @param {number} a a
|
|
* @param {number} b b
|
|
* @returns {0 | 1 | -1} result
|
|
*/
|
|
const compareNumbers = (a, b) => {
|
|
if (Number.isNaN(a)) {
|
|
if (!Number.isNaN(b)) {
|
|
return 1;
|
|
}
|
|
} else {
|
|
if (Number.isNaN(b)) {
|
|
return -1;
|
|
}
|
|
if (a !== b) {
|
|
return a < b ? -1 : 1;
|
|
}
|
|
}
|
|
return 0;
|
|
};
|
|
const bySourceOrder = createComparator("sourceOrder", compareNumbers);
|
|
const byRangeStart = createComparator("rangeStart", compareNumbers);
|
|
const moveDeferToLast = (
|
|
/** @type {{ defer?: boolean }} */ a,
|
|
/** @type {{ defer?: boolean }} */ b
|
|
) => {
|
|
if (a.defer === b.defer) return 0;
|
|
if (a.defer) return 1;
|
|
return -1;
|
|
};
|
|
|
|
const INITIAL_USED_NAMES = new Set(RESERVED_NAMES);
|
|
|
|
/**
|
|
* @param {Iterable<string>} iterable iterable object
|
|
* @returns {string} joined iterable object
|
|
*/
|
|
const joinIterableWithComma = (iterable) => {
|
|
// This is more performant than Array.from().join(", ")
|
|
// as it doesn't create an array
|
|
let str = "";
|
|
let first = true;
|
|
for (const item of iterable) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
str += ", ";
|
|
}
|
|
str += item;
|
|
}
|
|
return str;
|
|
};
|
|
|
|
/**
|
|
* @typedef {object} ConcatenationEntry
|
|
* @property {"concatenated" | "external"} type
|
|
* @property {Module} module
|
|
* @property {RuntimeSpec | boolean} runtimeCondition
|
|
*/
|
|
|
|
/** @typedef {Set<ConcatenatedModuleInfo>} NeededNamespaceObjects */
|
|
|
|
/** @typedef {Map<Module, ModuleInfo>} ModuleToInfoMap */
|
|
|
|
/**
|
|
* @param {ModuleGraph} moduleGraph the module graph
|
|
* @param {ModuleInfo} info module info
|
|
* @param {ExportName} exportName exportName
|
|
* @param {ModuleToInfoMap} moduleToInfoMap moduleToInfoMap
|
|
* @param {RuntimeSpec} runtime for which runtime
|
|
* @param {RequestShortener} requestShortener the request shortener
|
|
* @param {RuntimeTemplate} runtimeTemplate the runtime template
|
|
* @param {NeededNamespaceObjects} neededNamespaceObjects modules for which a namespace object should be generated
|
|
* @param {boolean} asCall asCall
|
|
* @param {boolean} depDeferred the dependency is deferred
|
|
* @param {boolean | undefined} strictHarmonyModule strictHarmonyModule
|
|
* @param {boolean | undefined} asiSafe asiSafe
|
|
* @param {Set<ExportInfo>} alreadyVisited alreadyVisited
|
|
* @returns {Binding} the final variable
|
|
*/
|
|
const getFinalBinding = (
|
|
moduleGraph,
|
|
info,
|
|
exportName,
|
|
moduleToInfoMap,
|
|
runtime,
|
|
requestShortener,
|
|
runtimeTemplate,
|
|
neededNamespaceObjects,
|
|
asCall,
|
|
depDeferred,
|
|
strictHarmonyModule,
|
|
asiSafe,
|
|
alreadyVisited = new Set()
|
|
) => {
|
|
const exportsType = info.module.getExportsType(
|
|
moduleGraph,
|
|
strictHarmonyModule
|
|
);
|
|
const deferred =
|
|
depDeferred &&
|
|
info.type === "external" &&
|
|
info.deferred &&
|
|
!moduleGraph.isAsync(info.module);
|
|
if (exportName.length === 0) {
|
|
switch (exportsType) {
|
|
case "default-only":
|
|
if (deferred) info.deferredNamespaceObjectUsed = true;
|
|
else info.interopNamespaceObject2Used = true;
|
|
return {
|
|
info,
|
|
rawName: /** @type {string} */ (
|
|
deferred
|
|
? info.deferredNamespaceObjectName
|
|
: info.interopNamespaceObject2Name
|
|
),
|
|
ids: exportName,
|
|
exportName
|
|
};
|
|
case "default-with-named":
|
|
if (deferred) info.deferredNamespaceObjectUsed = true;
|
|
else info.interopNamespaceObjectUsed = true;
|
|
return {
|
|
info,
|
|
rawName: /** @type {string} */ (
|
|
deferred
|
|
? info.deferredNamespaceObjectName
|
|
: info.interopNamespaceObjectName
|
|
),
|
|
ids: exportName,
|
|
exportName
|
|
};
|
|
case "namespace":
|
|
case "dynamic":
|
|
break;
|
|
default:
|
|
throw new Error(`Unexpected exportsType ${exportsType}`);
|
|
}
|
|
} else {
|
|
switch (exportsType) {
|
|
case "namespace":
|
|
break;
|
|
case "default-with-named":
|
|
switch (exportName[0]) {
|
|
case "default":
|
|
exportName = exportName.slice(1);
|
|
break;
|
|
case "__esModule":
|
|
return {
|
|
info,
|
|
rawName: "/* __esModule */true",
|
|
ids: exportName.slice(1),
|
|
exportName
|
|
};
|
|
}
|
|
break;
|
|
case "default-only": {
|
|
const exportId = exportName[0];
|
|
if (exportId === "__esModule") {
|
|
return {
|
|
info,
|
|
rawName: "/* __esModule */true",
|
|
ids: exportName.slice(1),
|
|
exportName
|
|
};
|
|
}
|
|
exportName = exportName.slice(1);
|
|
if (exportId !== "default") {
|
|
return {
|
|
info,
|
|
rawName:
|
|
"/* non-default import from default-exporting module */undefined",
|
|
ids: exportName,
|
|
exportName
|
|
};
|
|
}
|
|
break;
|
|
}
|
|
case "dynamic":
|
|
switch (exportName[0]) {
|
|
case "default": {
|
|
exportName = exportName.slice(1);
|
|
if (deferred) {
|
|
return {
|
|
info,
|
|
rawName: `${info.deferredName}.a`,
|
|
ids: exportName,
|
|
exportName
|
|
};
|
|
}
|
|
info.interopDefaultAccessUsed = true;
|
|
const defaultExport = asCall
|
|
? `${info.interopDefaultAccessName}()`
|
|
: asiSafe
|
|
? `(${info.interopDefaultAccessName}())`
|
|
: asiSafe === false
|
|
? `;(${info.interopDefaultAccessName}())`
|
|
: `${info.interopDefaultAccessName}.a`;
|
|
return {
|
|
info,
|
|
rawName: defaultExport,
|
|
ids: exportName,
|
|
exportName
|
|
};
|
|
}
|
|
case "__esModule":
|
|
return {
|
|
info,
|
|
rawName: "/* __esModule */true",
|
|
ids: exportName.slice(1),
|
|
exportName
|
|
};
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error(`Unexpected exportsType ${exportsType}`);
|
|
}
|
|
}
|
|
if (exportName.length === 0) {
|
|
switch (info.type) {
|
|
case "concatenated":
|
|
neededNamespaceObjects.add(info);
|
|
return {
|
|
info,
|
|
rawName:
|
|
/** @type {NonNullable<ConcatenatedModuleInfo["namespaceObjectName"]>} */
|
|
(info.namespaceObjectName),
|
|
ids: exportName,
|
|
exportName
|
|
};
|
|
case "external":
|
|
if (deferred) {
|
|
info.deferredNamespaceObjectUsed = true;
|
|
return {
|
|
info,
|
|
rawName: /** @type {string} */ (info.deferredNamespaceObjectName),
|
|
ids: exportName,
|
|
exportName
|
|
};
|
|
}
|
|
return {
|
|
info,
|
|
rawName:
|
|
/** @type {NonNullable<ExternalModuleInfo["name"]>} */
|
|
(info.name),
|
|
ids: exportName,
|
|
exportName
|
|
};
|
|
}
|
|
}
|
|
const exportsInfo = moduleGraph.getExportsInfo(info.module);
|
|
const exportInfo = exportsInfo.getExportInfo(exportName[0]);
|
|
if (alreadyVisited.has(exportInfo)) {
|
|
return {
|
|
info,
|
|
rawName: "/* circular reexport */ Object(function x() { x() }())",
|
|
ids: [],
|
|
exportName
|
|
};
|
|
}
|
|
alreadyVisited.add(exportInfo);
|
|
switch (info.type) {
|
|
case "concatenated": {
|
|
const exportId = exportName[0];
|
|
if (exportInfo.provided === false) {
|
|
// It's not provided, but it could be on the prototype
|
|
neededNamespaceObjects.add(info);
|
|
return {
|
|
info,
|
|
rawName: /** @type {string} */ (info.namespaceObjectName),
|
|
ids: exportName,
|
|
exportName
|
|
};
|
|
}
|
|
const directExport = info.exportMap && info.exportMap.get(exportId);
|
|
if (directExport) {
|
|
const usedName = /** @type {ExportName} */ (
|
|
exportsInfo.getUsedName(exportName, runtime)
|
|
);
|
|
if (!usedName) {
|
|
return {
|
|
info,
|
|
rawName: "/* unused export */ undefined",
|
|
ids: exportName.slice(1),
|
|
exportName
|
|
};
|
|
}
|
|
return {
|
|
info,
|
|
name: directExport,
|
|
ids: usedName.slice(1),
|
|
exportName
|
|
};
|
|
}
|
|
const rawExport = info.rawExportMap && info.rawExportMap.get(exportId);
|
|
if (rawExport) {
|
|
return {
|
|
info,
|
|
rawName: rawExport,
|
|
ids: exportName.slice(1),
|
|
exportName
|
|
};
|
|
}
|
|
const reexport = exportInfo.findTarget(moduleGraph, (module) =>
|
|
moduleToInfoMap.has(module)
|
|
);
|
|
if (reexport === false) {
|
|
throw new Error(
|
|
`Target module of reexport from '${info.module.readableIdentifier(
|
|
requestShortener
|
|
)}' is not part of the concatenation (export '${exportId}')\nModules in the concatenation:\n${Array.from(
|
|
moduleToInfoMap,
|
|
([m, info]) =>
|
|
` * ${info.type} ${m.readableIdentifier(requestShortener)}`
|
|
).join("\n")}`
|
|
);
|
|
}
|
|
if (reexport) {
|
|
const refInfo = moduleToInfoMap.get(reexport.module);
|
|
return getFinalBinding(
|
|
moduleGraph,
|
|
/** @type {ModuleInfo} */ (refInfo),
|
|
reexport.export
|
|
? [...reexport.export, ...exportName.slice(1)]
|
|
: exportName.slice(1),
|
|
moduleToInfoMap,
|
|
runtime,
|
|
requestShortener,
|
|
runtimeTemplate,
|
|
neededNamespaceObjects,
|
|
asCall,
|
|
reexport.deferred,
|
|
/** @type {BuildMeta} */
|
|
(info.module.buildMeta).strictHarmonyModule,
|
|
asiSafe,
|
|
alreadyVisited
|
|
);
|
|
}
|
|
if (info.namespaceExportSymbol) {
|
|
const usedName = /** @type {ExportName} */ (
|
|
exportsInfo.getUsedName(exportName, runtime)
|
|
);
|
|
return {
|
|
info,
|
|
rawName: /** @type {string} */ (info.namespaceObjectName),
|
|
ids: usedName,
|
|
exportName
|
|
};
|
|
}
|
|
throw new Error(
|
|
`Cannot get final name for export '${exportName.join(
|
|
"."
|
|
)}' of ${info.module.readableIdentifier(requestShortener)}`
|
|
);
|
|
}
|
|
|
|
case "external": {
|
|
const used = /** @type {ExportName} */ (
|
|
exportsInfo.getUsedName(exportName, runtime)
|
|
);
|
|
if (!used) {
|
|
return {
|
|
info,
|
|
rawName: "/* unused export */ undefined",
|
|
ids: exportName.slice(1),
|
|
exportName
|
|
};
|
|
}
|
|
const comment = equals(used, exportName)
|
|
? ""
|
|
: Template.toNormalComment(`${exportName.join(".")}`);
|
|
return {
|
|
info,
|
|
rawName:
|
|
(deferred ? info.deferredName : info.name) +
|
|
(deferred ? ".a" : "") +
|
|
comment,
|
|
ids: used,
|
|
exportName
|
|
};
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {ModuleGraph} moduleGraph the module graph
|
|
* @param {ModuleInfo} info module info
|
|
* @param {ExportName} exportName exportName
|
|
* @param {ModuleToInfoMap} moduleToInfoMap moduleToInfoMap
|
|
* @param {RuntimeSpec} runtime for which runtime
|
|
* @param {RequestShortener} requestShortener the request shortener
|
|
* @param {RuntimeTemplate} runtimeTemplate the runtime template
|
|
* @param {NeededNamespaceObjects} neededNamespaceObjects modules for which a namespace object should be generated
|
|
* @param {boolean} asCall asCall
|
|
* @param {boolean} depDeferred the dependency is deferred
|
|
* @param {boolean | undefined} callContext callContext
|
|
* @param {boolean | undefined} strictHarmonyModule strictHarmonyModule
|
|
* @param {boolean | undefined} asiSafe asiSafe
|
|
* @returns {string} the final name
|
|
*/
|
|
const getFinalName = (
|
|
moduleGraph,
|
|
info,
|
|
exportName,
|
|
moduleToInfoMap,
|
|
runtime,
|
|
requestShortener,
|
|
runtimeTemplate,
|
|
neededNamespaceObjects,
|
|
asCall,
|
|
depDeferred,
|
|
callContext,
|
|
strictHarmonyModule,
|
|
asiSafe
|
|
) => {
|
|
const binding = getFinalBinding(
|
|
moduleGraph,
|
|
info,
|
|
exportName,
|
|
moduleToInfoMap,
|
|
runtime,
|
|
requestShortener,
|
|
runtimeTemplate,
|
|
neededNamespaceObjects,
|
|
asCall,
|
|
depDeferred,
|
|
strictHarmonyModule,
|
|
asiSafe
|
|
);
|
|
{
|
|
const { ids, comment } = binding;
|
|
let reference;
|
|
let isPropertyAccess;
|
|
if ("rawName" in binding) {
|
|
reference = `${binding.rawName}${comment || ""}${propertyAccess(ids)}`;
|
|
isPropertyAccess = ids.length > 0;
|
|
} else {
|
|
const { info, name: exportId } = binding;
|
|
const name = info.internalNames.get(exportId);
|
|
if (!name) {
|
|
throw new Error(
|
|
`The export "${exportId}" in "${info.module.readableIdentifier(
|
|
requestShortener
|
|
)}" has no internal name (existing names: ${
|
|
Array.from(
|
|
info.internalNames,
|
|
([name, symbol]) => `${name}: ${symbol}`
|
|
).join(", ") || "none"
|
|
})`
|
|
);
|
|
}
|
|
reference = `${name}${comment || ""}${propertyAccess(ids)}`;
|
|
isPropertyAccess = ids.length > 1;
|
|
}
|
|
if (isPropertyAccess && asCall && callContext === false) {
|
|
return asiSafe
|
|
? `(0,${reference})`
|
|
: asiSafe === false
|
|
? `;(0,${reference})`
|
|
: `/*#__PURE__*/Object(${reference})`;
|
|
}
|
|
return reference;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @typedef {object} ConcatenateModuleHooks
|
|
* @property {SyncBailHook<[ConcatenatedModule], boolean>} onDemandExportsGeneration
|
|
* @property {SyncBailHook<[Partial<ConcatenatedModuleInfo>, ConcatenatedModuleInfo], boolean | void>} concatenatedModuleInfo
|
|
*/
|
|
|
|
/** @typedef {BuildInfo["topLevelDeclarations"]} TopLevelDeclarations */
|
|
|
|
/** @type {WeakMap<Compilation, ConcatenateModuleHooks>} */
|
|
const compilationHooksMap = new WeakMap();
|
|
|
|
class ConcatenatedModule extends Module {
|
|
/**
|
|
* @param {Module} rootModule the root module of the concatenation
|
|
* @param {Set<Module>} modules all modules in the concatenation (including the root module)
|
|
* @param {RuntimeSpec} runtime the runtime
|
|
* @param {Compilation} compilation the compilation
|
|
* @param {AssociatedObjectForCache=} associatedObjectForCache object for caching
|
|
* @param {string | HashConstructor=} hashFunction hash function to use
|
|
* @returns {ConcatenatedModule} the module
|
|
*/
|
|
static create(
|
|
rootModule,
|
|
modules,
|
|
runtime,
|
|
compilation,
|
|
associatedObjectForCache,
|
|
hashFunction = DEFAULTS.HASH_FUNCTION
|
|
) {
|
|
const identifier = ConcatenatedModule._createIdentifier(
|
|
rootModule,
|
|
modules,
|
|
associatedObjectForCache,
|
|
hashFunction
|
|
);
|
|
return new ConcatenatedModule({
|
|
identifier,
|
|
rootModule,
|
|
modules,
|
|
runtime,
|
|
compilation
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {Compilation} compilation the compilation
|
|
* @returns {ConcatenateModuleHooks} the attached hooks
|
|
*/
|
|
static getCompilationHooks(compilation) {
|
|
let hooks = compilationHooksMap.get(compilation);
|
|
if (hooks === undefined) {
|
|
hooks = {
|
|
onDemandExportsGeneration: new SyncBailHook(["module"]),
|
|
concatenatedModuleInfo: new SyncBailHook([
|
|
"updatedInfo",
|
|
"concatenatedModuleInfo"
|
|
])
|
|
};
|
|
compilationHooksMap.set(compilation, hooks);
|
|
}
|
|
return hooks;
|
|
}
|
|
|
|
/**
|
|
* @param {object} options options
|
|
* @param {string} options.identifier the identifier of the module
|
|
* @param {Module} options.rootModule the root module of the concatenation
|
|
* @param {RuntimeSpec} options.runtime the selected runtime
|
|
* @param {Set<Module>} options.modules all concatenated modules
|
|
* @param {Compilation} options.compilation the compilation
|
|
*/
|
|
constructor({ identifier, rootModule, modules, runtime, compilation }) {
|
|
super(JAVASCRIPT_MODULE_TYPE_ESM, null, rootModule && rootModule.layer);
|
|
|
|
// Info from Factory
|
|
/** @type {string} */
|
|
this._identifier = identifier;
|
|
/** @type {Module} */
|
|
this.rootModule = rootModule;
|
|
/** @type {Set<Module>} */
|
|
this._modules = modules;
|
|
this._runtime = runtime;
|
|
this.factoryMeta = rootModule && rootModule.factoryMeta;
|
|
/** @type {Compilation} */
|
|
this.compilation = compilation;
|
|
}
|
|
|
|
/**
|
|
* Assuming this module is in the cache. Update the (cached) module with
|
|
* the fresh module from the factory. Usually updates internal references
|
|
* and properties.
|
|
* @param {Module} module fresh module
|
|
* @returns {void}
|
|
*/
|
|
updateCacheModule(module) {
|
|
throw new Error("Must not be called");
|
|
}
|
|
|
|
/**
|
|
* @returns {SourceTypes} types available (do not mutate)
|
|
*/
|
|
getSourceTypes() {
|
|
return JS_TYPES;
|
|
}
|
|
|
|
get modules() {
|
|
return [...this._modules];
|
|
}
|
|
|
|
/**
|
|
* @returns {string} a unique identifier of the module
|
|
*/
|
|
identifier() {
|
|
return this._identifier;
|
|
}
|
|
|
|
/**
|
|
* @param {RequestShortener} requestShortener the request shortener
|
|
* @returns {string} a user readable identifier of the module
|
|
*/
|
|
readableIdentifier(requestShortener) {
|
|
return `${this.rootModule.readableIdentifier(
|
|
requestShortener
|
|
)} + ${this._modules.size - 1} modules`;
|
|
}
|
|
|
|
/**
|
|
* @param {LibIdentOptions} options options
|
|
* @returns {LibIdent | null} an identifier for library inclusion
|
|
*/
|
|
libIdent(options) {
|
|
return this.rootModule.libIdent(options);
|
|
}
|
|
|
|
/**
|
|
* @returns {NameForCondition | null} absolute path which should be used for condition matching (usually the resource path)
|
|
*/
|
|
nameForCondition() {
|
|
return this.rootModule.nameForCondition();
|
|
}
|
|
|
|
/**
|
|
* @param {ModuleGraph} moduleGraph the module graph
|
|
* @returns {ConnectionState} how this module should be connected to referencing modules when consumed for side-effects only
|
|
*/
|
|
getSideEffectsConnectionState(moduleGraph) {
|
|
return this.rootModule.getSideEffectsConnectionState(moduleGraph);
|
|
}
|
|
|
|
/**
|
|
* @param {WebpackOptions} options webpack options
|
|
* @param {Compilation} compilation the compilation
|
|
* @param {ResolverWithOptions} resolver the resolver
|
|
* @param {InputFileSystem} fs the file system
|
|
* @param {BuildCallback} callback callback function
|
|
* @returns {void}
|
|
*/
|
|
build(options, compilation, resolver, fs, callback) {
|
|
const { rootModule } = this;
|
|
const { moduleArgument, exportsArgument } =
|
|
/** @type {BuildInfo} */
|
|
(rootModule.buildInfo);
|
|
this.buildInfo = {
|
|
strict: true,
|
|
cacheable: true,
|
|
moduleArgument,
|
|
exportsArgument,
|
|
fileDependencies: new LazySet(),
|
|
contextDependencies: new LazySet(),
|
|
missingDependencies: new LazySet(),
|
|
topLevelDeclarations: new Set(),
|
|
assets: undefined
|
|
};
|
|
this.buildMeta = rootModule.buildMeta;
|
|
this.clearDependenciesAndBlocks();
|
|
this.clearWarningsAndErrors();
|
|
|
|
for (const m of this._modules) {
|
|
// populate cacheable
|
|
if (!(/** @type {BuildInfo} */ (m.buildInfo).cacheable)) {
|
|
/** @type {BuildInfo} */
|
|
(this.buildInfo).cacheable = false;
|
|
}
|
|
|
|
// populate dependencies
|
|
for (const d of m.dependencies.filter(
|
|
(dep) =>
|
|
!(dep instanceof HarmonyImportDependency) ||
|
|
!this._modules.has(
|
|
/** @type {Module} */
|
|
(compilation.moduleGraph.getModule(dep))
|
|
)
|
|
)) {
|
|
this.dependencies.push(d);
|
|
}
|
|
// populate blocks
|
|
for (const d of m.blocks) {
|
|
this.blocks.push(d);
|
|
}
|
|
|
|
// populate warnings
|
|
const warnings = m.getWarnings();
|
|
if (warnings !== undefined) {
|
|
for (const warning of warnings) {
|
|
this.addWarning(warning);
|
|
}
|
|
}
|
|
|
|
// populate errors
|
|
const errors = m.getErrors();
|
|
if (errors !== undefined) {
|
|
for (const error of errors) {
|
|
this.addError(error);
|
|
}
|
|
}
|
|
|
|
const { assets, assetsInfo, topLevelDeclarations, needCreateRequire } =
|
|
/** @type {BuildInfo} */ (m.buildInfo);
|
|
|
|
const buildInfo = /** @type {BuildInfo} */ (this.buildInfo);
|
|
|
|
// populate topLevelDeclarations
|
|
if (topLevelDeclarations) {
|
|
const topLevelDeclarations = buildInfo.topLevelDeclarations;
|
|
if (topLevelDeclarations !== undefined) {
|
|
for (const decl of topLevelDeclarations) {
|
|
topLevelDeclarations.add(decl);
|
|
}
|
|
}
|
|
} else {
|
|
buildInfo.topLevelDeclarations = undefined;
|
|
}
|
|
|
|
// populate needCreateRequire
|
|
if (needCreateRequire) {
|
|
this.buildInfo.needCreateRequire = true;
|
|
}
|
|
|
|
// populate assets
|
|
if (assets) {
|
|
if (buildInfo.assets === undefined) {
|
|
buildInfo.assets = Object.create(null);
|
|
}
|
|
Object.assign(
|
|
/** @type {NonNullable<BuildInfo["assets"]>} */
|
|
(buildInfo.assets),
|
|
assets
|
|
);
|
|
}
|
|
if (assetsInfo) {
|
|
if (buildInfo.assetsInfo === undefined) {
|
|
buildInfo.assetsInfo = new Map();
|
|
}
|
|
for (const [key, value] of assetsInfo) {
|
|
buildInfo.assetsInfo.set(key, value);
|
|
}
|
|
}
|
|
}
|
|
callback();
|
|
}
|
|
|
|
/**
|
|
* @param {string=} type the source type for which the size should be estimated
|
|
* @returns {number} the estimated size of the module (must be non-zero)
|
|
*/
|
|
size(type) {
|
|
// Guess size from embedded modules
|
|
let size = 0;
|
|
for (const module of this._modules) {
|
|
size += module.size(type);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* @param {Module} rootModule the root of the concatenation
|
|
* @param {Set<Module>} modulesSet a set of modules which should be concatenated
|
|
* @param {RuntimeSpec} runtime for this runtime
|
|
* @param {ModuleGraph} moduleGraph the module graph
|
|
* @returns {ConcatenationEntry[]} concatenation list
|
|
*/
|
|
_createConcatenationList(rootModule, modulesSet, runtime, moduleGraph) {
|
|
/** @type {ConcatenationEntry[]} */
|
|
const list = [];
|
|
/** @type {Map<Module, RuntimeSpec | true>} */
|
|
const existingEntries = new Map();
|
|
const deferEnabled = this.compilation.options.experiments.deferImport;
|
|
|
|
/**
|
|
* @param {Module} module a module
|
|
* @returns {Iterable<{ connection: ModuleGraphConnection, runtimeCondition: RuntimeSpec | true }>} imported modules in order
|
|
*/
|
|
const getConcatenatedImports = (module) => {
|
|
const connections = [...moduleGraph.getOutgoingConnections(module)];
|
|
if (module === rootModule) {
|
|
for (const c of moduleGraph.getOutgoingConnections(this)) {
|
|
connections.push(c);
|
|
}
|
|
}
|
|
/**
|
|
* @type {{ connection: ModuleGraphConnection, sourceOrder: number, rangeStart: number | undefined, defer?: boolean }[]}
|
|
*/
|
|
const references = connections
|
|
.filter((connection) => {
|
|
if (!(connection.dependency instanceof HarmonyImportDependency)) {
|
|
return false;
|
|
}
|
|
return (
|
|
connection &&
|
|
connection.resolvedOriginModule === module &&
|
|
connection.module &&
|
|
connection.isTargetActive(runtime)
|
|
);
|
|
})
|
|
.map((connection) => {
|
|
const dep =
|
|
/** @type {HarmonyImportDependency} */
|
|
(connection.dependency);
|
|
return {
|
|
connection,
|
|
sourceOrder: dep.sourceOrder,
|
|
rangeStart: dep.range && dep.range[0],
|
|
defer: dep.defer
|
|
};
|
|
});
|
|
/**
|
|
* bySourceOrder
|
|
* @example
|
|
* import a from "a"; // sourceOrder=1
|
|
* import b from "b"; // sourceOrder=2
|
|
*
|
|
* byRangeStart
|
|
* @example
|
|
* import {a, b} from "a"; // sourceOrder=1
|
|
* a.a(); // first range
|
|
* b.b(); // second range
|
|
*
|
|
* If the import is deferred, we always move it to the last.
|
|
* If there is no reexport, we have the same source.
|
|
* If there is reexport, but module has side effects, this will lead to reexport module only.
|
|
* If there is side-effects-free reexport, we can get simple deterministic result with range start comparison.
|
|
*/
|
|
references.sort(concatComparators(bySourceOrder, byRangeStart));
|
|
if (deferEnabled) {
|
|
// do not combine those two sorts. defer is not the same as source or range which has a comparable number, defer is only moving them.
|
|
references.sort(moveDeferToLast);
|
|
}
|
|
/** @type {Map<Module, { connection: ModuleGraphConnection, runtimeCondition: RuntimeSpec | true }>} */
|
|
const referencesMap = new Map();
|
|
for (const { connection } of references) {
|
|
const runtimeCondition = filterRuntime(runtime, (r) =>
|
|
connection.isTargetActive(r)
|
|
);
|
|
if (runtimeCondition === false) continue;
|
|
const module = connection.module;
|
|
const entry = referencesMap.get(module);
|
|
if (entry === undefined) {
|
|
referencesMap.set(module, { connection, runtimeCondition });
|
|
continue;
|
|
}
|
|
entry.runtimeCondition = mergeRuntimeConditionNonFalse(
|
|
entry.runtimeCondition,
|
|
runtimeCondition,
|
|
runtime
|
|
);
|
|
}
|
|
return referencesMap.values();
|
|
};
|
|
|
|
/**
|
|
* @param {ModuleGraphConnection} connection graph connection
|
|
* @param {RuntimeSpec | true} runtimeCondition runtime condition
|
|
* @returns {void}
|
|
*/
|
|
const enterModule = (connection, runtimeCondition) => {
|
|
const module = connection.module;
|
|
if (!module) return;
|
|
const existingEntry = existingEntries.get(module);
|
|
if (existingEntry === true) {
|
|
return;
|
|
}
|
|
if (modulesSet.has(module)) {
|
|
existingEntries.set(module, true);
|
|
if (runtimeCondition !== true) {
|
|
throw new Error(
|
|
`Cannot runtime-conditional concatenate a module (${module.identifier()} in ${this.rootModule.identifier()}, ${runtimeConditionToString(
|
|
runtimeCondition
|
|
)}). This should not happen.`
|
|
);
|
|
}
|
|
const imports = getConcatenatedImports(module);
|
|
for (const { connection, runtimeCondition } of imports) {
|
|
enterModule(connection, runtimeCondition);
|
|
}
|
|
list.push({
|
|
type: "concatenated",
|
|
module: connection.module,
|
|
runtimeCondition
|
|
});
|
|
} else {
|
|
if (existingEntry !== undefined) {
|
|
const reducedRuntimeCondition = subtractRuntimeCondition(
|
|
runtimeCondition,
|
|
existingEntry,
|
|
runtime
|
|
);
|
|
if (reducedRuntimeCondition === false) return;
|
|
runtimeCondition = reducedRuntimeCondition;
|
|
existingEntries.set(
|
|
connection.module,
|
|
mergeRuntimeConditionNonFalse(
|
|
existingEntry,
|
|
runtimeCondition,
|
|
runtime
|
|
)
|
|
);
|
|
} else {
|
|
existingEntries.set(connection.module, runtimeCondition);
|
|
}
|
|
if (list.length > 0) {
|
|
const lastItem = list[list.length - 1];
|
|
if (
|
|
lastItem.type === "external" &&
|
|
lastItem.module === connection.module
|
|
) {
|
|
lastItem.runtimeCondition = mergeRuntimeCondition(
|
|
lastItem.runtimeCondition,
|
|
runtimeCondition,
|
|
runtime
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
list.push({
|
|
type: "external",
|
|
get module() {
|
|
// We need to use a getter here, because the module in the dependency
|
|
// could be replaced by some other process (i. e. also replaced with a
|
|
// concatenated module)
|
|
return connection.module;
|
|
},
|
|
runtimeCondition
|
|
});
|
|
}
|
|
};
|
|
|
|
existingEntries.set(rootModule, true);
|
|
const imports = getConcatenatedImports(rootModule);
|
|
for (const { connection, runtimeCondition } of imports) {
|
|
enterModule(connection, runtimeCondition);
|
|
}
|
|
list.push({
|
|
type: "concatenated",
|
|
module: rootModule,
|
|
runtimeCondition: true
|
|
});
|
|
|
|
return list;
|
|
}
|
|
|
|
/**
|
|
* @param {Module} rootModule the root module of the concatenation
|
|
* @param {Set<Module>} modules all modules in the concatenation (including the root module)
|
|
* @param {AssociatedObjectForCache=} associatedObjectForCache object for caching
|
|
* @param {string | HashConstructor=} hashFunction hash function to use
|
|
* @returns {string} the identifier
|
|
*/
|
|
static _createIdentifier(
|
|
rootModule,
|
|
modules,
|
|
associatedObjectForCache,
|
|
hashFunction = DEFAULTS.HASH_FUNCTION
|
|
) {
|
|
const cachedMakePathsRelative = makePathsRelative.bindContextCache(
|
|
/** @type {string} */ (rootModule.context),
|
|
associatedObjectForCache
|
|
);
|
|
const identifiers = [];
|
|
for (const module of modules) {
|
|
identifiers.push(cachedMakePathsRelative(module.identifier()));
|
|
}
|
|
identifiers.sort();
|
|
const hash = createHash(hashFunction);
|
|
hash.update(identifiers.join(" "));
|
|
return `${rootModule.identifier()}|${hash.digest("hex")}`;
|
|
}
|
|
|
|
/**
|
|
* @param {FileSystemDependencies} fileDependencies set where file dependencies are added to
|
|
* @param {FileSystemDependencies} contextDependencies set where context dependencies are added to
|
|
* @param {FileSystemDependencies} missingDependencies set where missing dependencies are added to
|
|
* @param {FileSystemDependencies} buildDependencies set where build dependencies are added to
|
|
*/
|
|
addCacheDependencies(
|
|
fileDependencies,
|
|
contextDependencies,
|
|
missingDependencies,
|
|
buildDependencies
|
|
) {
|
|
for (const module of this._modules) {
|
|
module.addCacheDependencies(
|
|
fileDependencies,
|
|
contextDependencies,
|
|
missingDependencies,
|
|
buildDependencies
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {CodeGenerationContext} context context for code generation
|
|
* @returns {CodeGenerationResult} result
|
|
*/
|
|
codeGeneration({
|
|
dependencyTemplates,
|
|
runtimeTemplate,
|
|
moduleGraph,
|
|
chunkGraph,
|
|
runtime: generationRuntime,
|
|
codeGenerationResults
|
|
}) {
|
|
const { concatenatedModuleInfo } = ConcatenatedModule.getCompilationHooks(
|
|
this.compilation
|
|
);
|
|
|
|
/** @type {RuntimeRequirements} */
|
|
const runtimeRequirements = new Set();
|
|
const runtime = intersectRuntime(generationRuntime, this._runtime);
|
|
|
|
const requestShortener = runtimeTemplate.requestShortener;
|
|
// Meta info for each module
|
|
const [modulesWithInfo, moduleToInfoMap] = this._getModulesWithInfo(
|
|
moduleGraph,
|
|
runtime
|
|
);
|
|
|
|
// Set with modules that need a generated namespace object
|
|
/** @type {NeededNamespaceObjects} */
|
|
const neededNamespaceObjects = new Set();
|
|
|
|
// Default disallowed names
|
|
const allUsedNames = new Set(INITIAL_USED_NAMES);
|
|
const chunks = chunkGraph.getModuleChunks(this);
|
|
|
|
// Add names already used in the current chunk scope
|
|
for (const chunk of chunks) {
|
|
if (ConcatenationScope.chunkUsedNames.has(chunk)) {
|
|
for (const name of ConcatenationScope.chunkUsedNames.get(chunk) || []) {
|
|
allUsedNames.add(name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate source code and analyse scopes
|
|
// Prepare a ReplaceSource for the final source
|
|
for (const info of moduleToInfoMap.values()) {
|
|
this._analyseModule(
|
|
moduleToInfoMap,
|
|
info,
|
|
dependencyTemplates,
|
|
runtimeTemplate,
|
|
moduleGraph,
|
|
chunkGraph,
|
|
runtime,
|
|
/** @type {CodeGenerationResults} */
|
|
(codeGenerationResults),
|
|
allUsedNames
|
|
);
|
|
}
|
|
|
|
// Record the names registered by the current ConcatenatedModule into the chunk scope
|
|
if (INITIAL_USED_NAMES.size !== allUsedNames.size) {
|
|
for (const name of allUsedNames) {
|
|
if (INITIAL_USED_NAMES.has(name)) continue;
|
|
|
|
for (const chunk of chunks) {
|
|
if (!ConcatenationScope.chunkUsedNames.has(chunk)) {
|
|
ConcatenationScope.chunkUsedNames.set(chunk, new Set([name]));
|
|
} else {
|
|
/** @type {Set<string>} */ (
|
|
ConcatenationScope.chunkUsedNames.get(chunk)
|
|
).add(name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Updated Top level declarations are created by renaming
|
|
/** @type {TopLevelDeclarations} */
|
|
const topLevelDeclarations = new Set();
|
|
|
|
// List of additional names in scope for module references
|
|
/** @type {Map<string, ScopeInfo>} */
|
|
const usedNamesInScopeInfo = new Map();
|
|
|
|
// Set of already checked scopes
|
|
const ignoredScopes = new Set();
|
|
|
|
// get all global names
|
|
for (const info of modulesWithInfo) {
|
|
if (info.type === "concatenated") {
|
|
// ignore symbols from moduleScope
|
|
if (info.moduleScope) {
|
|
ignoredScopes.add(info.moduleScope);
|
|
}
|
|
|
|
// The super class expression in class scopes behaves weird
|
|
// We get ranges of all super class expressions to make
|
|
// renaming to work correctly
|
|
const superClassCache = new WeakMap();
|
|
/**
|
|
* @param {Scope} scope scope
|
|
* @returns {{ range: Range, variables: Variable[] }[]} result
|
|
*/
|
|
const getSuperClassExpressions = (scope) => {
|
|
const cacheEntry = superClassCache.get(scope);
|
|
if (cacheEntry !== undefined) return cacheEntry;
|
|
const superClassExpressions = [];
|
|
for (const childScope of scope.childScopes) {
|
|
if (childScope.type !== "class") continue;
|
|
const block = childScope.block;
|
|
if (
|
|
(block.type === "ClassDeclaration" ||
|
|
block.type === "ClassExpression") &&
|
|
block.superClass
|
|
) {
|
|
superClassExpressions.push({
|
|
range: /** @type {Range} */ (block.superClass.range),
|
|
variables: childScope.variables
|
|
});
|
|
}
|
|
}
|
|
superClassCache.set(scope, superClassExpressions);
|
|
return superClassExpressions;
|
|
};
|
|
|
|
// add global symbols
|
|
if (info.globalScope) {
|
|
for (const reference of info.globalScope.through) {
|
|
const name = reference.identifier.name;
|
|
if (ConcatenationScope.isModuleReference(name)) {
|
|
const match = ConcatenationScope.matchModuleReference(name);
|
|
if (!match) continue;
|
|
const referencedInfo = modulesWithInfo[match.index];
|
|
if (referencedInfo.type === "reference") {
|
|
throw new Error("Module reference can't point to a reference");
|
|
}
|
|
const binding = getFinalBinding(
|
|
moduleGraph,
|
|
referencedInfo,
|
|
match.ids,
|
|
moduleToInfoMap,
|
|
runtime,
|
|
requestShortener,
|
|
runtimeTemplate,
|
|
neededNamespaceObjects,
|
|
false,
|
|
match.deferredImport,
|
|
/** @type {BuildMeta} */
|
|
(info.module.buildMeta).strictHarmonyModule,
|
|
true
|
|
);
|
|
if (!binding.ids) continue;
|
|
const { usedNames, alreadyCheckedScopes } =
|
|
getUsedNamesInScopeInfo(
|
|
usedNamesInScopeInfo,
|
|
binding.info.module.identifier(),
|
|
"name" in binding ? binding.name : ""
|
|
);
|
|
for (const expr of getSuperClassExpressions(reference.from)) {
|
|
if (
|
|
expr.range[0] <=
|
|
/** @type {Range} */ (reference.identifier.range)[0] &&
|
|
expr.range[1] >=
|
|
/** @type {Range} */ (reference.identifier.range)[1]
|
|
) {
|
|
for (const variable of expr.variables) {
|
|
usedNames.add(variable.name);
|
|
}
|
|
}
|
|
}
|
|
addScopeSymbols(
|
|
reference.from,
|
|
usedNames,
|
|
alreadyCheckedScopes,
|
|
ignoredScopes
|
|
);
|
|
} else {
|
|
allUsedNames.add(name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} name the name to find a new name for
|
|
* @param {ConcatenatedModuleInfo} info the info of the module
|
|
* @param {Reference[]} references the references to the name
|
|
* @returns {string|undefined} the new name or undefined if the name is not found
|
|
*/
|
|
const _findNewName = (name, info, references) => {
|
|
const { usedNames, alreadyCheckedScopes } = getUsedNamesInScopeInfo(
|
|
usedNamesInScopeInfo,
|
|
info.module.identifier(),
|
|
name
|
|
);
|
|
if (allUsedNames.has(name) || usedNames.has(name)) {
|
|
for (const ref of references) {
|
|
addScopeSymbols(
|
|
ref.from,
|
|
usedNames,
|
|
alreadyCheckedScopes,
|
|
ignoredScopes
|
|
);
|
|
}
|
|
const newName = findNewName(
|
|
name,
|
|
allUsedNames,
|
|
usedNames,
|
|
info.module.readableIdentifier(requestShortener)
|
|
);
|
|
allUsedNames.add(newName);
|
|
info.internalNames.set(name, newName);
|
|
topLevelDeclarations.add(newName);
|
|
return newName;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {string} name the name to find a new name for
|
|
* @param {ConcatenatedModuleInfo} info the info of the module
|
|
* @param {Reference[]} references the references to the name
|
|
* @returns {string|undefined} the new name or undefined if the name is not found
|
|
*/
|
|
const _findNewNameForSpecifier = (name, info, references) => {
|
|
const { usedNames: moduleUsedNames, alreadyCheckedScopes } =
|
|
getUsedNamesInScopeInfo(
|
|
usedNamesInScopeInfo,
|
|
info.module.identifier(),
|
|
name
|
|
);
|
|
const referencesUsedNames = new Set();
|
|
for (const ref of references) {
|
|
addScopeSymbols(
|
|
ref.from,
|
|
referencesUsedNames,
|
|
alreadyCheckedScopes,
|
|
ignoredScopes
|
|
);
|
|
}
|
|
if (moduleUsedNames.has(name) || referencesUsedNames.has(name)) {
|
|
const newName = findNewName(
|
|
name,
|
|
allUsedNames,
|
|
new Set([...moduleUsedNames, ...referencesUsedNames]),
|
|
info.module.readableIdentifier(requestShortener)
|
|
);
|
|
allUsedNames.add(newName);
|
|
topLevelDeclarations.add(newName);
|
|
return newName;
|
|
}
|
|
};
|
|
|
|
// generate names for symbols
|
|
for (const info of moduleToInfoMap.values()) {
|
|
const { usedNames: namespaceObjectUsedNames } = getUsedNamesInScopeInfo(
|
|
usedNamesInScopeInfo,
|
|
info.module.identifier(),
|
|
""
|
|
);
|
|
switch (info.type) {
|
|
case "concatenated": {
|
|
const variables = /** @type {Scope} */ (info.moduleScope).variables;
|
|
for (const variable of variables) {
|
|
const name = variable.name;
|
|
const references = getAllReferences(variable);
|
|
const newName = _findNewName(name, info, references);
|
|
if (newName) {
|
|
const source = /** @type {ReplaceSource} */ (info.source);
|
|
const allIdentifiers = new Set([
|
|
...references.map((r) => r.identifier),
|
|
...variable.identifiers
|
|
]);
|
|
for (const identifier of allIdentifiers) {
|
|
const r = /** @type {Range} */ (identifier.range);
|
|
const path = getPathInAst(
|
|
/** @type {NonNullable<ConcatenatedModuleInfo["ast"]>} */
|
|
(info.ast),
|
|
identifier
|
|
);
|
|
if (path && path.length > 1) {
|
|
const maybeProperty =
|
|
path[1].type === "AssignmentPattern" &&
|
|
path[1].left === path[0]
|
|
? path[2]
|
|
: path[1];
|
|
if (
|
|
maybeProperty.type === "Property" &&
|
|
maybeProperty.shorthand
|
|
) {
|
|
source.insert(r[1], `: ${newName}`);
|
|
continue;
|
|
}
|
|
}
|
|
source.replace(r[0], r[1] - 1, newName);
|
|
}
|
|
} else {
|
|
allUsedNames.add(name);
|
|
info.internalNames.set(name, name);
|
|
topLevelDeclarations.add(name);
|
|
}
|
|
}
|
|
let namespaceObjectName;
|
|
if (info.namespaceExportSymbol) {
|
|
namespaceObjectName = info.internalNames.get(
|
|
info.namespaceExportSymbol
|
|
);
|
|
} else {
|
|
namespaceObjectName = findNewName(
|
|
"namespaceObject",
|
|
allUsedNames,
|
|
namespaceObjectUsedNames,
|
|
info.module.readableIdentifier(requestShortener)
|
|
);
|
|
allUsedNames.add(namespaceObjectName);
|
|
}
|
|
info.namespaceObjectName =
|
|
/** @type {string} */
|
|
(namespaceObjectName);
|
|
topLevelDeclarations.add(
|
|
/** @type {string} */
|
|
(namespaceObjectName)
|
|
);
|
|
break;
|
|
}
|
|
case "external": {
|
|
const externalName = findNewName(
|
|
"",
|
|
allUsedNames,
|
|
namespaceObjectUsedNames,
|
|
info.module.readableIdentifier(requestShortener)
|
|
);
|
|
allUsedNames.add(externalName);
|
|
info.name = externalName;
|
|
topLevelDeclarations.add(externalName);
|
|
|
|
if (info.deferred) {
|
|
const externalName = findNewName(
|
|
"deferred",
|
|
allUsedNames,
|
|
namespaceObjectUsedNames,
|
|
info.module.readableIdentifier(requestShortener)
|
|
);
|
|
allUsedNames.add(externalName);
|
|
info.deferredName = externalName;
|
|
topLevelDeclarations.add(externalName);
|
|
|
|
const externalNameInterop = findNewName(
|
|
"deferredNamespaceObject",
|
|
allUsedNames,
|
|
namespaceObjectUsedNames,
|
|
info.module.readableIdentifier(requestShortener)
|
|
);
|
|
allUsedNames.add(externalNameInterop);
|
|
info.deferredNamespaceObjectName = externalNameInterop;
|
|
topLevelDeclarations.add(externalNameInterop);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
const buildMeta = /** @type {BuildMeta} */ (info.module.buildMeta);
|
|
if (buildMeta.exportsType !== "namespace") {
|
|
const externalNameInterop = findNewName(
|
|
"namespaceObject",
|
|
allUsedNames,
|
|
namespaceObjectUsedNames,
|
|
info.module.readableIdentifier(requestShortener)
|
|
);
|
|
allUsedNames.add(externalNameInterop);
|
|
info.interopNamespaceObjectName = externalNameInterop;
|
|
topLevelDeclarations.add(externalNameInterop);
|
|
}
|
|
if (
|
|
buildMeta.exportsType === "default" &&
|
|
buildMeta.defaultObject !== "redirect" &&
|
|
info.interopNamespaceObject2Used
|
|
) {
|
|
const externalNameInterop = findNewName(
|
|
"namespaceObject2",
|
|
allUsedNames,
|
|
namespaceObjectUsedNames,
|
|
info.module.readableIdentifier(requestShortener)
|
|
);
|
|
allUsedNames.add(externalNameInterop);
|
|
info.interopNamespaceObject2Name = externalNameInterop;
|
|
topLevelDeclarations.add(externalNameInterop);
|
|
}
|
|
if (buildMeta.exportsType === "dynamic" || !buildMeta.exportsType) {
|
|
const externalNameInterop = findNewName(
|
|
"default",
|
|
allUsedNames,
|
|
namespaceObjectUsedNames,
|
|
info.module.readableIdentifier(requestShortener)
|
|
);
|
|
allUsedNames.add(externalNameInterop);
|
|
info.interopDefaultAccessName = externalNameInterop;
|
|
topLevelDeclarations.add(externalNameInterop);
|
|
}
|
|
}
|
|
|
|
// Find and replace references to modules
|
|
for (const info of moduleToInfoMap.values()) {
|
|
if (info.type === "concatenated") {
|
|
const globalScope = /** @type {Scope} */ (info.globalScope);
|
|
// group references by name
|
|
const referencesByName = new Map();
|
|
for (const reference of globalScope.through) {
|
|
const name = reference.identifier.name;
|
|
if (!referencesByName.has(name)) {
|
|
referencesByName.set(name, []);
|
|
}
|
|
referencesByName.get(name).push(reference);
|
|
}
|
|
for (const [name, references] of referencesByName) {
|
|
const match = ConcatenationScope.matchModuleReference(name);
|
|
if (match) {
|
|
const referencedInfo = modulesWithInfo[match.index];
|
|
if (referencedInfo.type === "reference") {
|
|
throw new Error("Module reference can't point to a reference");
|
|
}
|
|
const concatenationScope = /** @type {ConcatenatedModuleInfo} */ (
|
|
referencedInfo
|
|
).concatenationScope;
|
|
const exportId = match.ids[0];
|
|
const specifier =
|
|
concatenationScope && concatenationScope.getRawExport(exportId);
|
|
if (specifier) {
|
|
const newName = _findNewNameForSpecifier(
|
|
specifier,
|
|
info,
|
|
references
|
|
);
|
|
const initFragmentChanged =
|
|
newName &&
|
|
concatenatedModuleInfo.call(
|
|
{
|
|
rawExportMap: new Map([
|
|
[exportId, /** @type {string} */ (newName)]
|
|
])
|
|
},
|
|
/** @type {ConcatenatedModuleInfo} */ (referencedInfo)
|
|
);
|
|
if (initFragmentChanged) {
|
|
concatenationScope.setRawExportMap(exportId, newName);
|
|
}
|
|
}
|
|
const finalName = getFinalName(
|
|
moduleGraph,
|
|
referencedInfo,
|
|
match.ids,
|
|
moduleToInfoMap,
|
|
runtime,
|
|
requestShortener,
|
|
runtimeTemplate,
|
|
neededNamespaceObjects,
|
|
match.call,
|
|
match.deferredImport,
|
|
!match.directImport,
|
|
/** @type {BuildMeta} */
|
|
(info.module.buildMeta).strictHarmonyModule,
|
|
match.asiSafe
|
|
);
|
|
|
|
for (const reference of references) {
|
|
const r = /** @type {Range} */ (reference.identifier.range);
|
|
const source = /** @type {ReplaceSource} */ (info.source);
|
|
// range is extended by 2 chars to cover the appended "._"
|
|
source.replace(r[0], r[1] + 1, finalName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Map with all root exposed used exports
|
|
/** @type {Map<string, (requestShortener: RequestShortener) => string>} */
|
|
const exportsMap = new Map();
|
|
|
|
// Set with all root exposed unused exports
|
|
/** @type {Set<string>} */
|
|
const unusedExports = new Set();
|
|
|
|
const rootInfo =
|
|
/** @type {ConcatenatedModuleInfo} */
|
|
(moduleToInfoMap.get(this.rootModule));
|
|
const strictHarmonyModule =
|
|
/** @type {BuildMeta} */
|
|
(rootInfo.module.buildMeta).strictHarmonyModule;
|
|
const exportsInfo = moduleGraph.getExportsInfo(rootInfo.module);
|
|
/** @type {Record<string, string>} */
|
|
const exportsFinalName = {};
|
|
for (const exportInfo of exportsInfo.orderedExports) {
|
|
const name = exportInfo.name;
|
|
if (exportInfo.provided === false) continue;
|
|
const used = exportInfo.getUsedName(undefined, runtime);
|
|
if (!used) {
|
|
unusedExports.add(name);
|
|
continue;
|
|
}
|
|
exportsMap.set(used, (requestShortener) => {
|
|
try {
|
|
const finalName = getFinalName(
|
|
moduleGraph,
|
|
rootInfo,
|
|
[name],
|
|
moduleToInfoMap,
|
|
runtime,
|
|
requestShortener,
|
|
runtimeTemplate,
|
|
neededNamespaceObjects,
|
|
false,
|
|
false,
|
|
false,
|
|
strictHarmonyModule,
|
|
true
|
|
);
|
|
exportsFinalName[used] = finalName;
|
|
return `/* ${
|
|
exportInfo.isReexport() ? "reexport" : "binding"
|
|
} */ ${finalName}`;
|
|
} catch (err) {
|
|
/** @type {Error} */
|
|
(err).message +=
|
|
`\nwhile generating the root export '${name}' (used name: '${used}')`;
|
|
throw err;
|
|
}
|
|
});
|
|
}
|
|
|
|
const result = new ConcatSource();
|
|
|
|
// add harmony compatibility flag (must be first because of possible circular dependencies)
|
|
let shouldAddHarmonyFlag = false;
|
|
const rootExportsInfo = moduleGraph.getExportsInfo(this);
|
|
if (
|
|
rootExportsInfo.otherExportsInfo.getUsed(runtime) !== UsageState.Unused ||
|
|
rootExportsInfo.getReadOnlyExportInfo("__esModule").getUsed(runtime) !==
|
|
UsageState.Unused
|
|
) {
|
|
shouldAddHarmonyFlag = true;
|
|
}
|
|
|
|
// define exports
|
|
if (exportsMap.size > 0) {
|
|
const definitions = [];
|
|
for (const [key, value] of exportsMap) {
|
|
definitions.push(
|
|
`\n ${propertyName(key)}: ${runtimeTemplate.returningFunction(
|
|
value(requestShortener)
|
|
)}`
|
|
);
|
|
}
|
|
|
|
const { onDemandExportsGeneration } =
|
|
ConcatenatedModule.getCompilationHooks(this.compilation);
|
|
|
|
runtimeRequirements.add(RuntimeGlobals.exports);
|
|
runtimeRequirements.add(RuntimeGlobals.definePropertyGetters);
|
|
|
|
if (shouldAddHarmonyFlag) {
|
|
result.add("// ESM COMPAT FLAG\n");
|
|
result.add(
|
|
runtimeTemplate.defineEsModuleFlagStatement({
|
|
exportsArgument: this.exportsArgument,
|
|
runtimeRequirements
|
|
})
|
|
);
|
|
}
|
|
|
|
if (onDemandExportsGeneration.call(this)) {
|
|
/** @type {BuildMeta} */ (this.buildMeta).factoryExportsBinding =
|
|
"\n// EXPORTS\n" +
|
|
`${RuntimeGlobals.definePropertyGetters}(${
|
|
this.exportsArgument
|
|
}, {${definitions.join(",")}\n});\n`;
|
|
/** @type {BuildMeta} */ (this.buildMeta).exportsFinalName =
|
|
exportsFinalName;
|
|
} else {
|
|
result.add("\n// EXPORTS\n");
|
|
result.add(
|
|
`${RuntimeGlobals.definePropertyGetters}(${
|
|
this.exportsArgument
|
|
}, {${definitions.join(",")}\n});\n`
|
|
);
|
|
}
|
|
}
|
|
|
|
// list unused exports
|
|
if (unusedExports.size > 0) {
|
|
result.add(
|
|
`\n// UNUSED EXPORTS: ${joinIterableWithComma(unusedExports)}\n`
|
|
);
|
|
}
|
|
|
|
// generate namespace objects
|
|
const namespaceObjectSources = new Map();
|
|
for (const info of neededNamespaceObjects) {
|
|
if (info.namespaceExportSymbol) continue;
|
|
const nsObj = [];
|
|
const exportsInfo = moduleGraph.getExportsInfo(info.module);
|
|
for (const exportInfo of exportsInfo.orderedExports) {
|
|
if (exportInfo.provided === false) continue;
|
|
const usedName = exportInfo.getUsedName(undefined, runtime);
|
|
if (usedName) {
|
|
const finalName = getFinalName(
|
|
moduleGraph,
|
|
info,
|
|
[exportInfo.name],
|
|
moduleToInfoMap,
|
|
runtime,
|
|
requestShortener,
|
|
runtimeTemplate,
|
|
neededNamespaceObjects,
|
|
false,
|
|
false,
|
|
undefined,
|
|
/** @type {BuildMeta} */
|
|
(info.module.buildMeta).strictHarmonyModule,
|
|
true
|
|
);
|
|
nsObj.push(
|
|
`\n ${propertyName(usedName)}: ${runtimeTemplate.returningFunction(
|
|
finalName
|
|
)}`
|
|
);
|
|
}
|
|
}
|
|
const name = info.namespaceObjectName;
|
|
const defineGetters =
|
|
nsObj.length > 0
|
|
? `${RuntimeGlobals.definePropertyGetters}(${name}, {${nsObj.join(
|
|
","
|
|
)}\n});\n`
|
|
: "";
|
|
if (nsObj.length > 0) {
|
|
runtimeRequirements.add(RuntimeGlobals.definePropertyGetters);
|
|
}
|
|
namespaceObjectSources.set(
|
|
info,
|
|
`
|
|
// NAMESPACE OBJECT: ${info.module.readableIdentifier(requestShortener)}
|
|
var ${name} = {};
|
|
${RuntimeGlobals.makeNamespaceObject}(${name});
|
|
${defineGetters}`
|
|
);
|
|
runtimeRequirements.add(RuntimeGlobals.makeNamespaceObject);
|
|
}
|
|
|
|
// define required namespace objects (must be before evaluation modules)
|
|
for (const info of modulesWithInfo) {
|
|
if (info.type === "concatenated") {
|
|
const source = namespaceObjectSources.get(info);
|
|
if (!source) continue;
|
|
result.add(source);
|
|
}
|
|
|
|
if (info.type === "external" && info.deferred) {
|
|
const moduleId = JSON.stringify(chunkGraph.getModuleId(info.module));
|
|
const loader = getOptimizedDeferredModule(
|
|
runtimeTemplate,
|
|
info.module.getExportsType(
|
|
moduleGraph,
|
|
/** @type {BuildMeta} */
|
|
(this.rootModule.buildMeta).strictHarmonyModule
|
|
),
|
|
moduleId,
|
|
// an async module will opt-out of the concat module optimization.
|
|
[]
|
|
);
|
|
runtimeRequirements.add(RuntimeGlobals.require);
|
|
result.add(
|
|
`\n// DEFERRED EXTERNAL MODULE: ${info.module.readableIdentifier(requestShortener)}\nvar ${info.deferredName} = ${loader};`
|
|
);
|
|
}
|
|
}
|
|
|
|
/** @type {InitFragment<ChunkRenderContext>[]} */
|
|
const chunkInitFragments = [];
|
|
const deferEnabled = this.compilation.options.experiments.deferImport;
|
|
|
|
// evaluate modules in order
|
|
for (const rawInfo of modulesWithInfo) {
|
|
let name;
|
|
let isConditional = false;
|
|
const info = rawInfo.type === "reference" ? rawInfo.target : rawInfo;
|
|
switch (info.type) {
|
|
case "concatenated": {
|
|
result.add(
|
|
`\n;// ${info.module.readableIdentifier(requestShortener)}\n`
|
|
);
|
|
// If a module is deferred in other places, but used as non-deferred here,
|
|
// the module itself will be emitted as mod_deferred (in the case "external"),
|
|
// we need to emit an extra import declaration to evaluate it in order.
|
|
if (deferEnabled) {
|
|
for (const dep of info.module.dependencies) {
|
|
if (
|
|
!(/** @type {HarmonyImportDependency} */ (dep).defer) &&
|
|
(dep instanceof HarmonyImportSideEffectDependency ||
|
|
dep instanceof HarmonyImportSpecifierDependency)
|
|
) {
|
|
const referredModule = moduleGraph.getModule(dep);
|
|
if (!referredModule) {
|
|
if (dep instanceof HarmonyImportSideEffectDependency) {
|
|
continue;
|
|
} else {
|
|
throw new Error(
|
|
"Deferred module used, but no module in the graph."
|
|
);
|
|
}
|
|
}
|
|
if (moduleGraph.isDeferred(referredModule)) {
|
|
const deferredModuleInfo = /** @type {ExternalModuleInfo} */ (
|
|
modulesWithInfo.find(
|
|
(i) =>
|
|
i.type === "external" && i.module === referredModule
|
|
)
|
|
);
|
|
if (!deferredModuleInfo) continue;
|
|
result.add(
|
|
`\n// non-deferred import to a deferred module (${referredModule.readableIdentifier(requestShortener)})\nvar ${deferredModuleInfo.name} = ${deferredModuleInfo.deferredName}.a;`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
result.add(/** @type {ReplaceSource} */ (info.source));
|
|
if (info.chunkInitFragments) {
|
|
for (const f of info.chunkInitFragments) chunkInitFragments.push(f);
|
|
}
|
|
if (info.runtimeRequirements) {
|
|
for (const r of info.runtimeRequirements) {
|
|
runtimeRequirements.add(r);
|
|
}
|
|
}
|
|
name = info.namespaceObjectName;
|
|
break;
|
|
}
|
|
case "external": {
|
|
// deferred case is handled in the "const info of modulesWithInfo" loop above
|
|
if (!info.deferred) {
|
|
result.add(
|
|
`\n// EXTERNAL MODULE: ${info.module.readableIdentifier(
|
|
requestShortener
|
|
)}\n`
|
|
);
|
|
runtimeRequirements.add(RuntimeGlobals.require);
|
|
const { runtimeCondition } =
|
|
/** @type {ExternalModuleInfo | ReferenceToModuleInfo} */
|
|
(rawInfo);
|
|
const condition = runtimeTemplate.runtimeConditionExpression({
|
|
chunkGraph,
|
|
runtimeCondition,
|
|
runtime,
|
|
runtimeRequirements
|
|
});
|
|
if (condition !== "true") {
|
|
isConditional = true;
|
|
result.add(`if (${condition}) {\n`);
|
|
}
|
|
const moduleId = JSON.stringify(
|
|
chunkGraph.getModuleId(info.module)
|
|
);
|
|
result.add(`var ${info.name} = __webpack_require__(${moduleId});`);
|
|
name = info.name;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
// @ts-expect-error never is expected here
|
|
throw new Error(`Unsupported concatenation entry type ${info.type}`);
|
|
}
|
|
if (info.type === "external" && info.deferredNamespaceObjectUsed) {
|
|
runtimeRequirements.add(RuntimeGlobals.makeDeferredNamespaceObject);
|
|
result.add(
|
|
`\nvar ${info.deferredNamespaceObjectName} = /*#__PURE__*/${
|
|
RuntimeGlobals.makeDeferredNamespaceObject
|
|
}(${JSON.stringify(
|
|
chunkGraph.getModuleId(info.module)
|
|
)}, ${getMakeDeferredNamespaceModeFromExportsType(
|
|
info.module.getExportsType(moduleGraph, strictHarmonyModule)
|
|
)});`
|
|
);
|
|
}
|
|
if (info.interopNamespaceObjectUsed) {
|
|
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
|
|
result.add(
|
|
`\nvar ${info.interopNamespaceObjectName} = /*#__PURE__*/${RuntimeGlobals.createFakeNamespaceObject}(${name}, 2);`
|
|
);
|
|
}
|
|
if (info.interopNamespaceObject2Used) {
|
|
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
|
|
result.add(
|
|
`\nvar ${info.interopNamespaceObject2Name} = /*#__PURE__*/${RuntimeGlobals.createFakeNamespaceObject}(${name});`
|
|
);
|
|
}
|
|
if (info.interopDefaultAccessUsed) {
|
|
runtimeRequirements.add(RuntimeGlobals.compatGetDefaultExport);
|
|
result.add(
|
|
`\nvar ${info.interopDefaultAccessName} = /*#__PURE__*/${RuntimeGlobals.compatGetDefaultExport}(${name});`
|
|
);
|
|
}
|
|
if (isConditional) {
|
|
result.add("\n}");
|
|
}
|
|
}
|
|
|
|
const data = new Map();
|
|
if (chunkInitFragments.length > 0) {
|
|
data.set("chunkInitFragments", chunkInitFragments);
|
|
}
|
|
data.set("topLevelDeclarations", topLevelDeclarations);
|
|
|
|
/** @type {CodeGenerationResult} */
|
|
const resultEntry = {
|
|
sources: new Map([["javascript", new CachedSource(result)]]),
|
|
data,
|
|
runtimeRequirements
|
|
};
|
|
|
|
return resultEntry;
|
|
}
|
|
|
|
/**
|
|
* @param {ModuleToInfoMap} modulesMap modulesMap
|
|
* @param {ModuleInfo} info info
|
|
* @param {DependencyTemplates} dependencyTemplates dependencyTemplates
|
|
* @param {RuntimeTemplate} runtimeTemplate runtimeTemplate
|
|
* @param {ModuleGraph} moduleGraph moduleGraph
|
|
* @param {ChunkGraph} chunkGraph chunkGraph
|
|
* @param {RuntimeSpec} runtime runtime
|
|
* @param {CodeGenerationResults} codeGenerationResults codeGenerationResults
|
|
* @param {Set<string>} usedNames used names
|
|
*/
|
|
_analyseModule(
|
|
modulesMap,
|
|
info,
|
|
dependencyTemplates,
|
|
runtimeTemplate,
|
|
moduleGraph,
|
|
chunkGraph,
|
|
runtime,
|
|
codeGenerationResults,
|
|
usedNames
|
|
) {
|
|
if (info.type === "concatenated") {
|
|
const m = info.module;
|
|
try {
|
|
// Create a concatenation scope to track and capture information
|
|
const concatenationScope = new ConcatenationScope(
|
|
modulesMap,
|
|
info,
|
|
usedNames
|
|
);
|
|
|
|
// TODO cache codeGeneration results
|
|
const codeGenResult = m.codeGeneration({
|
|
dependencyTemplates,
|
|
runtimeTemplate,
|
|
moduleGraph,
|
|
chunkGraph,
|
|
runtime,
|
|
concatenationScope,
|
|
codeGenerationResults,
|
|
sourceTypes: JS_TYPES
|
|
});
|
|
const source =
|
|
/** @type {Source} */
|
|
(codeGenResult.sources.get("javascript"));
|
|
const data = codeGenResult.data;
|
|
const chunkInitFragments = data && data.get("chunkInitFragments");
|
|
const code = source.source().toString();
|
|
let ast;
|
|
try {
|
|
ast = JavascriptParser._parse(code, {
|
|
sourceType: "module"
|
|
});
|
|
} catch (_err) {
|
|
const err =
|
|
/** @type {Error & { loc?: { line: number, column: number } }} */
|
|
(_err);
|
|
if (
|
|
err.loc &&
|
|
typeof err.loc === "object" &&
|
|
typeof err.loc.line === "number"
|
|
) {
|
|
const lineNumber = err.loc.line;
|
|
const lines = code.split("\n");
|
|
err.message += `\n| ${lines
|
|
.slice(Math.max(0, lineNumber - 3), lineNumber + 2)
|
|
.join("\n| ")}`;
|
|
}
|
|
throw err;
|
|
}
|
|
const scopeManager = eslintScope.analyze(ast, {
|
|
ecmaVersion: 6,
|
|
sourceType: "module",
|
|
optimistic: true,
|
|
ignoreEval: true,
|
|
impliedStrict: true
|
|
});
|
|
const globalScope = /** @type {Scope} */ (scopeManager.acquire(ast));
|
|
const moduleScope = globalScope.childScopes[0];
|
|
const resultSource = new ReplaceSource(source);
|
|
info.runtimeRequirements =
|
|
/** @type {ReadOnlyRuntimeRequirements} */
|
|
(codeGenResult.runtimeRequirements);
|
|
info.ast = ast;
|
|
info.internalSource = source;
|
|
info.source = resultSource;
|
|
info.chunkInitFragments = chunkInitFragments;
|
|
info.globalScope = globalScope;
|
|
info.moduleScope = moduleScope;
|
|
info.concatenationScope = concatenationScope;
|
|
} catch (err) {
|
|
/** @type {Error} */
|
|
(err).message +=
|
|
`\nwhile analyzing module ${m.identifier()} for concatenation`;
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {ModuleGraph} moduleGraph the module graph
|
|
* @param {RuntimeSpec} runtime the runtime
|
|
* @returns {[ModuleInfoOrReference[], ModuleToInfoMap]} module info items
|
|
*/
|
|
_getModulesWithInfo(moduleGraph, runtime) {
|
|
const orderedConcatenationList = this._createConcatenationList(
|
|
this.rootModule,
|
|
this._modules,
|
|
runtime,
|
|
moduleGraph
|
|
);
|
|
/** @type {ModuleToInfoMap} */
|
|
const map = new Map();
|
|
const list = orderedConcatenationList.map((info, index) => {
|
|
let item = map.get(info.module);
|
|
if (item === undefined) {
|
|
switch (info.type) {
|
|
case "concatenated":
|
|
item = {
|
|
type: "concatenated",
|
|
module: info.module,
|
|
index,
|
|
ast: undefined,
|
|
internalSource: undefined,
|
|
runtimeRequirements: undefined,
|
|
source: undefined,
|
|
globalScope: undefined,
|
|
moduleScope: undefined,
|
|
internalNames: new Map(),
|
|
exportMap: undefined,
|
|
rawExportMap: undefined,
|
|
namespaceExportSymbol: undefined,
|
|
namespaceObjectName: undefined,
|
|
interopNamespaceObjectUsed: false,
|
|
interopNamespaceObjectName: undefined,
|
|
interopNamespaceObject2Used: false,
|
|
interopNamespaceObject2Name: undefined,
|
|
interopDefaultAccessUsed: false,
|
|
interopDefaultAccessName: undefined,
|
|
concatenationScope: undefined
|
|
};
|
|
break;
|
|
case "external":
|
|
item = {
|
|
type: "external",
|
|
module: info.module,
|
|
runtimeCondition: info.runtimeCondition,
|
|
index,
|
|
name: undefined,
|
|
deferredName: undefined,
|
|
interopNamespaceObjectUsed: false,
|
|
interopNamespaceObjectName: undefined,
|
|
interopNamespaceObject2Used: false,
|
|
interopNamespaceObject2Name: undefined,
|
|
interopDefaultAccessUsed: false,
|
|
interopDefaultAccessName: undefined,
|
|
deferred: moduleGraph.isDeferred(info.module),
|
|
deferredNamespaceObjectName: undefined,
|
|
deferredNamespaceObjectUsed: false
|
|
};
|
|
break;
|
|
default:
|
|
throw new Error(
|
|
`Unsupported concatenation entry type ${info.type}`
|
|
);
|
|
}
|
|
map.set(
|
|
/** @type {ModuleInfo} */ (item).module,
|
|
/** @type {ModuleInfo} */ (item)
|
|
);
|
|
return /** @type {ModuleInfo} */ (item);
|
|
}
|
|
/** @type {ReferenceToModuleInfo} */
|
|
const ref = {
|
|
type: "reference",
|
|
runtimeCondition: info.runtimeCondition,
|
|
target: item
|
|
};
|
|
return ref;
|
|
});
|
|
return [list, map];
|
|
}
|
|
|
|
/**
|
|
* @param {Hash} hash the hash used to track dependencies
|
|
* @param {UpdateHashContext} context context
|
|
* @returns {void}
|
|
*/
|
|
updateHash(hash, context) {
|
|
const { chunkGraph, runtime } = context;
|
|
for (const info of this._createConcatenationList(
|
|
this.rootModule,
|
|
this._modules,
|
|
intersectRuntime(runtime, this._runtime),
|
|
chunkGraph.moduleGraph
|
|
)) {
|
|
switch (info.type) {
|
|
case "concatenated":
|
|
info.module.updateHash(hash, context);
|
|
break;
|
|
case "external":
|
|
hash.update(`${chunkGraph.getModuleId(info.module)}`);
|
|
// TODO runtimeCondition
|
|
break;
|
|
}
|
|
}
|
|
super.updateHash(hash, context);
|
|
}
|
|
|
|
/**
|
|
* @param {ObjectDeserializerContext} context context
|
|
* @returns {ConcatenatedModule} ConcatenatedModule
|
|
*/
|
|
static deserialize(context) {
|
|
const obj = new ConcatenatedModule({
|
|
identifier: /** @type {EXPECTED_ANY} */ (undefined),
|
|
rootModule: /** @type {EXPECTED_ANY} */ (undefined),
|
|
modules: /** @type {EXPECTED_ANY} */ (undefined),
|
|
runtime: undefined,
|
|
compilation: /** @type {EXPECTED_ANY} */ (undefined)
|
|
});
|
|
obj.deserialize(context);
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
makeSerializable(ConcatenatedModule, "webpack/lib/optimize/ConcatenatedModule");
|
|
|
|
module.exports = ConcatenatedModule;
|