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 ConditionalInitFragment = require("../ConditionalInitFragment");
|
---|
9 | const Dependency = require("../Dependency");
|
---|
10 | const HarmonyLinkingError = require("../HarmonyLinkingError");
|
---|
11 | const InitFragment = require("../InitFragment");
|
---|
12 | const Template = require("../Template");
|
---|
13 | const AwaitDependenciesInitFragment = require("../async-modules/AwaitDependenciesInitFragment");
|
---|
14 | const { filterRuntime, mergeRuntime } = require("../util/runtime");
|
---|
15 | const ModuleDependency = require("./ModuleDependency");
|
---|
16 |
|
---|
17 | /** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
|
---|
18 | /** @typedef {import("webpack-sources").Source} Source */
|
---|
19 | /** @typedef {import("../ChunkGraph")} ChunkGraph */
|
---|
20 | /** @typedef {import("../Dependency").ReferencedExport} ReferencedExport */
|
---|
21 | /** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
|
---|
22 | /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
|
---|
23 | /** @typedef {import("../Module")} Module */
|
---|
24 | /** @typedef {import("../ModuleGraph")} ModuleGraph */
|
---|
25 | /** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
|
---|
26 | /** @typedef {import("../WebpackError")} WebpackError */
|
---|
27 | /** @typedef {import("../util/Hash")} Hash */
|
---|
28 | /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
|
---|
29 |
|
---|
30 | class HarmonyImportDependency extends ModuleDependency {
|
---|
31 | /**
|
---|
32 | *
|
---|
33 | * @param {string} request request string
|
---|
34 | * @param {number} sourceOrder source order
|
---|
35 | * @param {Record<string, any>=} assertions import assertions
|
---|
36 | */
|
---|
37 | constructor(request, sourceOrder, assertions) {
|
---|
38 | super(request);
|
---|
39 | this.sourceOrder = sourceOrder;
|
---|
40 | this.assertions = assertions;
|
---|
41 | }
|
---|
42 |
|
---|
43 | get category() {
|
---|
44 | return "esm";
|
---|
45 | }
|
---|
46 |
|
---|
47 | /**
|
---|
48 | * Returns list of exports referenced by this dependency
|
---|
49 | * @param {ModuleGraph} moduleGraph module graph
|
---|
50 | * @param {RuntimeSpec} runtime the runtime for which the module is analysed
|
---|
51 | * @returns {(string[] | ReferencedExport)[]} referenced exports
|
---|
52 | */
|
---|
53 | getReferencedExports(moduleGraph, runtime) {
|
---|
54 | return Dependency.NO_EXPORTS_REFERENCED;
|
---|
55 | }
|
---|
56 |
|
---|
57 | /**
|
---|
58 | * @param {ModuleGraph} moduleGraph the module graph
|
---|
59 | * @returns {string} name of the variable for the import
|
---|
60 | */
|
---|
61 | getImportVar(moduleGraph) {
|
---|
62 | const module = moduleGraph.getParentModule(this);
|
---|
63 | const meta = moduleGraph.getMeta(module);
|
---|
64 | let importVarMap = meta.importVarMap;
|
---|
65 | if (!importVarMap) meta.importVarMap = importVarMap = new Map();
|
---|
66 | let importVar = importVarMap.get(moduleGraph.getModule(this));
|
---|
67 | if (importVar) return importVar;
|
---|
68 | importVar = `${Template.toIdentifier(
|
---|
69 | `${this.userRequest}`
|
---|
70 | )}__WEBPACK_IMPORTED_MODULE_${importVarMap.size}__`;
|
---|
71 | importVarMap.set(moduleGraph.getModule(this), importVar);
|
---|
72 | return importVar;
|
---|
73 | }
|
---|
74 |
|
---|
75 | /**
|
---|
76 | * @param {boolean} update create new variables or update existing one
|
---|
77 | * @param {DependencyTemplateContext} templateContext the template context
|
---|
78 | * @returns {[string, string]} the import statement and the compat statement
|
---|
79 | */
|
---|
80 | getImportStatement(
|
---|
81 | update,
|
---|
82 | { runtimeTemplate, module, moduleGraph, chunkGraph, runtimeRequirements }
|
---|
83 | ) {
|
---|
84 | return runtimeTemplate.importStatement({
|
---|
85 | update,
|
---|
86 | module: moduleGraph.getModule(this),
|
---|
87 | chunkGraph,
|
---|
88 | importVar: this.getImportVar(moduleGraph),
|
---|
89 | request: this.request,
|
---|
90 | originModule: module,
|
---|
91 | runtimeRequirements
|
---|
92 | });
|
---|
93 | }
|
---|
94 |
|
---|
95 | /**
|
---|
96 | * @param {ModuleGraph} moduleGraph module graph
|
---|
97 | * @param {string[]} ids imported ids
|
---|
98 | * @param {string} additionalMessage extra info included in the error message
|
---|
99 | * @returns {WebpackError[] | undefined} errors
|
---|
100 | */
|
---|
101 | getLinkingErrors(moduleGraph, ids, additionalMessage) {
|
---|
102 | const importedModule = moduleGraph.getModule(this);
|
---|
103 | // ignore errors for missing or failed modules
|
---|
104 | if (!importedModule || importedModule.getNumberOfErrors() > 0) {
|
---|
105 | return;
|
---|
106 | }
|
---|
107 |
|
---|
108 | const parentModule = moduleGraph.getParentModule(this);
|
---|
109 | const exportsType = importedModule.getExportsType(
|
---|
110 | moduleGraph,
|
---|
111 | parentModule.buildMeta.strictHarmonyModule
|
---|
112 | );
|
---|
113 | if (exportsType === "namespace" || exportsType === "default-with-named") {
|
---|
114 | if (ids.length === 0) {
|
---|
115 | return;
|
---|
116 | }
|
---|
117 |
|
---|
118 | if (
|
---|
119 | (exportsType !== "default-with-named" || ids[0] !== "default") &&
|
---|
120 | moduleGraph.isExportProvided(importedModule, ids) === false
|
---|
121 | ) {
|
---|
122 | // We are sure that it's not provided
|
---|
123 |
|
---|
124 | // Try to provide detailed info in the error message
|
---|
125 | let pos = 0;
|
---|
126 | let exportsInfo = moduleGraph.getExportsInfo(importedModule);
|
---|
127 | while (pos < ids.length && exportsInfo) {
|
---|
128 | const id = ids[pos++];
|
---|
129 | const exportInfo = exportsInfo.getReadOnlyExportInfo(id);
|
---|
130 | if (exportInfo.provided === false) {
|
---|
131 | // We are sure that it's not provided
|
---|
132 | const providedExports = exportsInfo.getProvidedExports();
|
---|
133 | const moreInfo = !Array.isArray(providedExports)
|
---|
134 | ? " (possible exports unknown)"
|
---|
135 | : providedExports.length === 0
|
---|
136 | ? " (module has no exports)"
|
---|
137 | : ` (possible exports: ${providedExports.join(", ")})`;
|
---|
138 | return [
|
---|
139 | new HarmonyLinkingError(
|
---|
140 | `export ${ids
|
---|
141 | .slice(0, pos)
|
---|
142 | .map(id => `'${id}'`)
|
---|
143 | .join(".")} ${additionalMessage} was not found in '${
|
---|
144 | this.userRequest
|
---|
145 | }'${moreInfo}`
|
---|
146 | )
|
---|
147 | ];
|
---|
148 | }
|
---|
149 | exportsInfo = exportInfo.getNestedExportsInfo();
|
---|
150 | }
|
---|
151 |
|
---|
152 | // General error message
|
---|
153 | return [
|
---|
154 | new HarmonyLinkingError(
|
---|
155 | `export ${ids
|
---|
156 | .map(id => `'${id}'`)
|
---|
157 | .join(".")} ${additionalMessage} was not found in '${
|
---|
158 | this.userRequest
|
---|
159 | }'`
|
---|
160 | )
|
---|
161 | ];
|
---|
162 | }
|
---|
163 | }
|
---|
164 | switch (exportsType) {
|
---|
165 | case "default-only":
|
---|
166 | // It's has only a default export
|
---|
167 | if (ids.length > 0 && ids[0] !== "default") {
|
---|
168 | // In strict harmony modules we only support the default export
|
---|
169 | return [
|
---|
170 | new HarmonyLinkingError(
|
---|
171 | `Can't import the named export ${ids
|
---|
172 | .map(id => `'${id}'`)
|
---|
173 | .join(
|
---|
174 | "."
|
---|
175 | )} ${additionalMessage} from default-exporting module (only default export is available)`
|
---|
176 | )
|
---|
177 | ];
|
---|
178 | }
|
---|
179 | break;
|
---|
180 | case "default-with-named":
|
---|
181 | // It has a default export and named properties redirect
|
---|
182 | // In some cases we still want to warn here
|
---|
183 | if (
|
---|
184 | ids.length > 0 &&
|
---|
185 | ids[0] !== "default" &&
|
---|
186 | importedModule.buildMeta.defaultObject === "redirect-warn"
|
---|
187 | ) {
|
---|
188 | // For these modules only the default export is supported
|
---|
189 | return [
|
---|
190 | new HarmonyLinkingError(
|
---|
191 | `Should not import the named export ${ids
|
---|
192 | .map(id => `'${id}'`)
|
---|
193 | .join(
|
---|
194 | "."
|
---|
195 | )} ${additionalMessage} from default-exporting module (only default export is available soon)`
|
---|
196 | )
|
---|
197 | ];
|
---|
198 | }
|
---|
199 | break;
|
---|
200 | }
|
---|
201 | }
|
---|
202 |
|
---|
203 | serialize(context) {
|
---|
204 | const { write } = context;
|
---|
205 | write(this.sourceOrder);
|
---|
206 | write(this.assertions);
|
---|
207 | super.serialize(context);
|
---|
208 | }
|
---|
209 |
|
---|
210 | deserialize(context) {
|
---|
211 | const { read } = context;
|
---|
212 | this.sourceOrder = read();
|
---|
213 | this.assertions = read();
|
---|
214 | super.deserialize(context);
|
---|
215 | }
|
---|
216 | }
|
---|
217 |
|
---|
218 | module.exports = HarmonyImportDependency;
|
---|
219 |
|
---|
220 | /** @type {WeakMap<Module, WeakMap<Module, RuntimeSpec | boolean>>} */
|
---|
221 | const importEmittedMap = new WeakMap();
|
---|
222 |
|
---|
223 | HarmonyImportDependency.Template = class HarmonyImportDependencyTemplate extends (
|
---|
224 | ModuleDependency.Template
|
---|
225 | ) {
|
---|
226 | /**
|
---|
227 | * @param {Dependency} dependency the dependency for which the template should be applied
|
---|
228 | * @param {ReplaceSource} source the current replace source which can be modified
|
---|
229 | * @param {DependencyTemplateContext} templateContext the context object
|
---|
230 | * @returns {void}
|
---|
231 | */
|
---|
232 | apply(dependency, source, templateContext) {
|
---|
233 | const dep = /** @type {HarmonyImportDependency} */ (dependency);
|
---|
234 | const { module, chunkGraph, moduleGraph, runtime } = templateContext;
|
---|
235 |
|
---|
236 | const connection = moduleGraph.getConnection(dep);
|
---|
237 | if (connection && !connection.isTargetActive(runtime)) return;
|
---|
238 |
|
---|
239 | const referencedModule = connection && connection.module;
|
---|
240 |
|
---|
241 | if (
|
---|
242 | connection &&
|
---|
243 | connection.weak &&
|
---|
244 | referencedModule &&
|
---|
245 | chunkGraph.getModuleId(referencedModule) === null
|
---|
246 | ) {
|
---|
247 | // in weak references, module might not be in any chunk
|
---|
248 | // but that's ok, we don't need that logic in this case
|
---|
249 | return;
|
---|
250 | }
|
---|
251 |
|
---|
252 | const moduleKey = referencedModule
|
---|
253 | ? referencedModule.identifier()
|
---|
254 | : dep.request;
|
---|
255 | const key = `harmony import ${moduleKey}`;
|
---|
256 |
|
---|
257 | const runtimeCondition = dep.weak
|
---|
258 | ? false
|
---|
259 | : connection
|
---|
260 | ? filterRuntime(runtime, r => connection.isTargetActive(r))
|
---|
261 | : true;
|
---|
262 |
|
---|
263 | if (module && referencedModule) {
|
---|
264 | let emittedModules = importEmittedMap.get(module);
|
---|
265 | if (emittedModules === undefined) {
|
---|
266 | emittedModules = new WeakMap();
|
---|
267 | importEmittedMap.set(module, emittedModules);
|
---|
268 | }
|
---|
269 | let mergedRuntimeCondition = runtimeCondition;
|
---|
270 | const oldRuntimeCondition = emittedModules.get(referencedModule) || false;
|
---|
271 | if (oldRuntimeCondition !== false && mergedRuntimeCondition !== true) {
|
---|
272 | if (mergedRuntimeCondition === false || oldRuntimeCondition === true) {
|
---|
273 | mergedRuntimeCondition = oldRuntimeCondition;
|
---|
274 | } else {
|
---|
275 | mergedRuntimeCondition = mergeRuntime(
|
---|
276 | oldRuntimeCondition,
|
---|
277 | mergedRuntimeCondition
|
---|
278 | );
|
---|
279 | }
|
---|
280 | }
|
---|
281 | emittedModules.set(referencedModule, mergedRuntimeCondition);
|
---|
282 | }
|
---|
283 |
|
---|
284 | const importStatement = dep.getImportStatement(false, templateContext);
|
---|
285 | if (templateContext.moduleGraph.isAsync(referencedModule)) {
|
---|
286 | templateContext.initFragments.push(
|
---|
287 | new ConditionalInitFragment(
|
---|
288 | importStatement[0],
|
---|
289 | InitFragment.STAGE_HARMONY_IMPORTS,
|
---|
290 | dep.sourceOrder,
|
---|
291 | key,
|
---|
292 | runtimeCondition
|
---|
293 | )
|
---|
294 | );
|
---|
295 | templateContext.initFragments.push(
|
---|
296 | new AwaitDependenciesInitFragment(
|
---|
297 | new Set([dep.getImportVar(templateContext.moduleGraph)])
|
---|
298 | )
|
---|
299 | );
|
---|
300 | templateContext.initFragments.push(
|
---|
301 | new ConditionalInitFragment(
|
---|
302 | importStatement[1],
|
---|
303 | InitFragment.STAGE_ASYNC_HARMONY_IMPORTS,
|
---|
304 | dep.sourceOrder,
|
---|
305 | key + " compat",
|
---|
306 | runtimeCondition
|
---|
307 | )
|
---|
308 | );
|
---|
309 | } else {
|
---|
310 | templateContext.initFragments.push(
|
---|
311 | new ConditionalInitFragment(
|
---|
312 | importStatement[0] + importStatement[1],
|
---|
313 | InitFragment.STAGE_HARMONY_IMPORTS,
|
---|
314 | dep.sourceOrder,
|
---|
315 | key,
|
---|
316 | runtimeCondition
|
---|
317 | )
|
---|
318 | );
|
---|
319 | }
|
---|
320 | }
|
---|
321 |
|
---|
322 | /**
|
---|
323 | *
|
---|
324 | * @param {Module} module the module
|
---|
325 | * @param {Module} referencedModule the referenced module
|
---|
326 | * @returns {RuntimeSpec | boolean} runtimeCondition in which this import has been emitted
|
---|
327 | */
|
---|
328 | static getImportEmittedRuntime(module, referencedModule) {
|
---|
329 | const emittedModules = importEmittedMap.get(module);
|
---|
330 | if (emittedModules === undefined) return false;
|
---|
331 | return emittedModules.get(referencedModule) || false;
|
---|
332 | }
|
---|
333 | };
|
---|