UNPKG

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