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