UNPKG

39.2 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 parseJson = require("json-parse-better-errors");
9const { getContext, runLoaders } = require("loader-runner");
10const querystring = require("querystring");
11const { HookMap, SyncHook, AsyncSeriesBailHook } = require("tapable");
12const {
13 CachedSource,
14 OriginalSource,
15 RawSource,
16 SourceMapSource
17} = require("webpack-sources");
18const Compilation = require("./Compilation");
19const Module = require("./Module");
20const ModuleBuildError = require("./ModuleBuildError");
21const ModuleError = require("./ModuleError");
22const ModuleGraphConnection = require("./ModuleGraphConnection");
23const ModuleParseError = require("./ModuleParseError");
24const ModuleWarning = require("./ModuleWarning");
25const RuntimeGlobals = require("./RuntimeGlobals");
26const UnhandledSchemeError = require("./UnhandledSchemeError");
27const WebpackError = require("./WebpackError");
28const formatLocation = require("./formatLocation");
29const LazySet = require("./util/LazySet");
30const { isSubset } = require("./util/SetHelpers");
31const { getScheme } = require("./util/URLAbsoluteSpecifier");
32const {
33 compareLocations,
34 concatComparators,
35 compareSelect,
36 keepOriginalOrder
37} = require("./util/comparators");
38const createHash = require("./util/createHash");
39const { join } = require("./util/fs");
40const { contextify, absolutify } = require("./util/identifier");
41const makeSerializable = require("./util/makeSerializable");
42const memoize = require("./util/memoize");
43
44/** @typedef {import("webpack-sources").Source} Source */
45/** @typedef {import("../declarations/LoaderContext").NormalModuleLoaderContext} NormalModuleLoaderContext */
46/** @typedef {import("../declarations/WebpackOptions").Mode} Mode */
47/** @typedef {import("../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
48/** @typedef {import("./ChunkGraph")} ChunkGraph */
49/** @typedef {import("./Compiler")} Compiler */
50/** @typedef {import("./Dependency").UpdateHashContext} UpdateHashContext */
51/** @typedef {import("./DependencyTemplates")} DependencyTemplates */
52/** @typedef {import("./Generator")} Generator */
53/** @typedef {import("./Module").CodeGenerationContext} CodeGenerationContext */
54/** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */
55/** @typedef {import("./Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
56/** @typedef {import("./Module").LibIdentOptions} LibIdentOptions */
57/** @typedef {import("./Module").NeedBuildContext} NeedBuildContext */
58/** @typedef {import("./ModuleGraph")} ModuleGraph */
59/** @typedef {import("./ModuleGraphConnection").ConnectionState} ConnectionState */
60/** @typedef {import("./NormalModuleFactory")} NormalModuleFactory */
61/** @typedef {import("./Parser")} Parser */
62/** @typedef {import("./RequestShortener")} RequestShortener */
63/** @typedef {import("./ResolverFactory").ResolverWithOptions} ResolverWithOptions */
64/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
65/** @typedef {import("./logging/Logger").Logger} WebpackLogger */
66/** @typedef {import("./util/Hash")} Hash */
67/** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
68/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
69
70/**
71 * @typedef {Object} SourceMap
72 * @property {number} version
73 * @property {string[]} sources
74 * @property {string} mappings
75 * @property {string=} file
76 * @property {string=} sourceRoot
77 * @property {string[]=} sourcesContent
78 * @property {string[]=} names
79 */
80
81const getInvalidDependenciesModuleWarning = memoize(() =>
82 require("./InvalidDependenciesModuleWarning")
83);
84const getValidate = memoize(() => require("schema-utils").validate);
85
86const ABSOLUTE_PATH_REGEX = /^([a-zA-Z]:\\|\\\\|\/)/;
87
88/**
89 * @typedef {Object} LoaderItem
90 * @property {string} loader
91 * @property {any} options
92 * @property {string?} ident
93 * @property {string?} type
94 */
95
96/**
97 * @param {string} context absolute context path
98 * @param {string} source a source path
99 * @param {Object=} associatedObjectForCache an object to which the cache will be attached
100 * @returns {string} new source path
101 */
102const contextifySourceUrl = (context, source, associatedObjectForCache) => {
103 if (source.startsWith("webpack://")) return source;
104 return `webpack://${contextify(context, source, associatedObjectForCache)}`;
105};
106
107/**
108 * @param {string} context absolute context path
109 * @param {SourceMap} sourceMap a source map
110 * @param {Object=} associatedObjectForCache an object to which the cache will be attached
111 * @returns {SourceMap} new source map
112 */
113const contextifySourceMap = (context, sourceMap, associatedObjectForCache) => {
114 if (!Array.isArray(sourceMap.sources)) return sourceMap;
115 const { sourceRoot } = sourceMap;
116 /** @type {function(string): string} */
117 const mapper = !sourceRoot
118 ? source => source
119 : sourceRoot.endsWith("/")
120 ? source =>
121 source.startsWith("/")
122 ? `${sourceRoot.slice(0, -1)}${source}`
123 : `${sourceRoot}${source}`
124 : source =>
125 source.startsWith("/")
126 ? `${sourceRoot}${source}`
127 : `${sourceRoot}/${source}`;
128 const newSources = sourceMap.sources.map(source =>
129 contextifySourceUrl(context, mapper(source), associatedObjectForCache)
130 );
131 return {
132 ...sourceMap,
133 file: "x",
134 sourceRoot: undefined,
135 sources: newSources
136 };
137};
138
139/**
140 * @param {string | Buffer} input the input
141 * @returns {string} the converted string
142 */
143const asString = input => {
144 if (Buffer.isBuffer(input)) {
145 return input.toString("utf-8");
146 }
147 return input;
148};
149
150/**
151 * @param {string | Buffer} input the input
152 * @returns {Buffer} the converted buffer
153 */
154const asBuffer = input => {
155 if (!Buffer.isBuffer(input)) {
156 return Buffer.from(input, "utf-8");
157 }
158 return input;
159};
160
161class NonErrorEmittedError extends WebpackError {
162 constructor(error) {
163 super();
164
165 this.name = "NonErrorEmittedError";
166 this.message = "(Emitted value instead of an instance of Error) " + error;
167 }
168}
169
170makeSerializable(
171 NonErrorEmittedError,
172 "webpack/lib/NormalModule",
173 "NonErrorEmittedError"
174);
175
176/**
177 * @typedef {Object} NormalModuleCompilationHooks
178 * @property {SyncHook<[object, NormalModule]>} loader
179 * @property {SyncHook<[LoaderItem[], NormalModule, object]>} beforeLoaders
180 * @property {HookMap<AsyncSeriesBailHook<[string, NormalModule], string | Buffer>>} readResourceForScheme
181 */
182
183/** @type {WeakMap<Compilation, NormalModuleCompilationHooks>} */
184const compilationHooksMap = new WeakMap();
185
186class NormalModule extends Module {
187 /**
188 * @param {Compilation} compilation the compilation
189 * @returns {NormalModuleCompilationHooks} the attached hooks
190 */
191 static getCompilationHooks(compilation) {
192 if (!(compilation instanceof Compilation)) {
193 throw new TypeError(
194 "The 'compilation' argument must be an instance of Compilation"
195 );
196 }
197 let hooks = compilationHooksMap.get(compilation);
198 if (hooks === undefined) {
199 hooks = {
200 loader: new SyncHook(["loaderContext", "module"]),
201 beforeLoaders: new SyncHook(["loaders", "module", "loaderContext"]),
202 readResourceForScheme: new HookMap(
203 () => new AsyncSeriesBailHook(["resource", "module"])
204 )
205 };
206 compilationHooksMap.set(compilation, hooks);
207 }
208 return hooks;
209 }
210
211 /**
212 * @param {Object} options options object
213 * @param {string=} options.layer an optional layer in which the module is
214 * @param {string} options.type module type
215 * @param {string} options.request request string
216 * @param {string} options.userRequest request intended by user (without loaders from config)
217 * @param {string} options.rawRequest request without resolving
218 * @param {LoaderItem[]} options.loaders list of loaders
219 * @param {string} options.resource path + query of the real resource
220 * @param {Record<string, any>=} options.resourceResolveData resource resolve data
221 * @param {string | undefined} options.matchResource path + query of the matched resource (virtual)
222 * @param {Parser} options.parser the parser used
223 * @param {object} options.parserOptions the options of the parser used
224 * @param {Generator} options.generator the generator used
225 * @param {object} options.generatorOptions the options of the generator used
226 * @param {Object} options.resolveOptions options used for resolving requests from this module
227 */
228 constructor({
229 layer,
230 type,
231 request,
232 userRequest,
233 rawRequest,
234 loaders,
235 resource,
236 resourceResolveData,
237 matchResource,
238 parser,
239 parserOptions,
240 generator,
241 generatorOptions,
242 resolveOptions
243 }) {
244 super(type, getContext(resource), layer);
245
246 // Info from Factory
247 /** @type {string} */
248 this.request = request;
249 /** @type {string} */
250 this.userRequest = userRequest;
251 /** @type {string} */
252 this.rawRequest = rawRequest;
253 /** @type {boolean} */
254 this.binary = /^(asset|webassembly)\b/.test(type);
255 /** @type {Parser} */
256 this.parser = parser;
257 this.parserOptions = parserOptions;
258 /** @type {Generator} */
259 this.generator = generator;
260 this.generatorOptions = generatorOptions;
261 /** @type {string} */
262 this.resource = resource;
263 this.resourceResolveData = resourceResolveData;
264 /** @type {string | undefined} */
265 this.matchResource = matchResource;
266 /** @type {LoaderItem[]} */
267 this.loaders = loaders;
268 if (resolveOptions !== undefined) {
269 // already declared in super class
270 this.resolveOptions = resolveOptions;
271 }
272
273 // Info from Build
274 /** @type {WebpackError=} */
275 this.error = null;
276 /** @private @type {Source=} */
277 this._source = null;
278 /** @private @type {Map<string, number> | undefined} **/
279 this._sourceSizes = undefined;
280 /** @private @type {Set<string>} */
281 this._sourceTypes = undefined;
282
283 // Cache
284 this._lastSuccessfulBuildMeta = {};
285 this._forceBuild = true;
286 this._isEvaluatingSideEffects = false;
287 /** @type {WeakSet<ModuleGraph> | undefined} */
288 this._addedSideEffectsBailout = undefined;
289 }
290
291 /**
292 * @returns {string} a unique identifier of the module
293 */
294 identifier() {
295 if (this.layer === null) {
296 return this.request;
297 } else {
298 return `${this.request}|${this.layer}`;
299 }
300 }
301
302 /**
303 * @param {RequestShortener} requestShortener the request shortener
304 * @returns {string} a user readable identifier of the module
305 */
306 readableIdentifier(requestShortener) {
307 return requestShortener.shorten(this.userRequest);
308 }
309
310 /**
311 * @param {LibIdentOptions} options options
312 * @returns {string | null} an identifier for library inclusion
313 */
314 libIdent(options) {
315 return contextify(
316 options.context,
317 this.userRequest,
318 options.associatedObjectForCache
319 );
320 }
321
322 /**
323 * @returns {string | null} absolute path which should be used for condition matching (usually the resource path)
324 */
325 nameForCondition() {
326 const resource = this.matchResource || this.resource;
327 const idx = resource.indexOf("?");
328 if (idx >= 0) return resource.substr(0, idx);
329 return resource;
330 }
331
332 /**
333 * Assuming this module is in the cache. Update the (cached) module with
334 * the fresh module from the factory. Usually updates internal references
335 * and properties.
336 * @param {Module} module fresh module
337 * @returns {void}
338 */
339 updateCacheModule(module) {
340 super.updateCacheModule(module);
341 const m = /** @type {NormalModule} */ (module);
342 this.binary = m.binary;
343 this.request = m.request;
344 this.userRequest = m.userRequest;
345 this.rawRequest = m.rawRequest;
346 this.parser = m.parser;
347 this.parserOptions = m.parserOptions;
348 this.generator = m.generator;
349 this.generatorOptions = m.generatorOptions;
350 this.resource = m.resource;
351 this.matchResource = m.matchResource;
352 this.loaders = m.loaders;
353 this._sourceTypes = m._sourceTypes;
354 this._sourceSizes = m._sourceSizes;
355 }
356
357 /**
358 * Assuming this module is in the cache. Remove internal references to allow freeing some memory.
359 */
360 cleanupForCache() {
361 // Make sure to cache types and sizes before cleanup
362 if (this._sourceTypes === undefined) this.getSourceTypes();
363 for (const type of this._sourceTypes) {
364 this.size(type);
365 }
366 super.cleanupForCache();
367 this.parser = undefined;
368 this.parserOptions = undefined;
369 this.generator = undefined;
370 this.generatorOptions = undefined;
371 }
372
373 /**
374 * Module should be unsafe cached. Get data that's needed for that.
375 * This data will be passed to restoreFromUnsafeCache later.
376 * @returns {object} cached data
377 */
378 getUnsafeCacheData() {
379 const data = super.getUnsafeCacheData();
380 data.parserOptions = this.parserOptions;
381 data.generatorOptions = this.generatorOptions;
382 return data;
383 }
384
385 restoreFromUnsafeCache(unsafeCacheData, normalModuleFactory) {
386 this._restoreFromUnsafeCache(unsafeCacheData, normalModuleFactory);
387 }
388
389 /**
390 * restore unsafe cache data
391 * @param {object} unsafeCacheData data from getUnsafeCacheData
392 * @param {NormalModuleFactory} normalModuleFactory the normal module factory handling the unsafe caching
393 */
394 _restoreFromUnsafeCache(unsafeCacheData, normalModuleFactory) {
395 super._restoreFromUnsafeCache(unsafeCacheData, normalModuleFactory);
396 this.parserOptions = unsafeCacheData.parserOptions;
397 this.parser = normalModuleFactory.getParser(this.type, this.parserOptions);
398 this.generatorOptions = unsafeCacheData.generatorOptions;
399 this.generator = normalModuleFactory.getGenerator(
400 this.type,
401 this.generatorOptions
402 );
403 // we assume the generator behaves identically and keep cached sourceTypes/Sizes
404 }
405
406 /**
407 * @param {string} context the compilation context
408 * @param {string} name the asset name
409 * @param {string} content the content
410 * @param {string | TODO} sourceMap an optional source map
411 * @param {Object=} associatedObjectForCache object for caching
412 * @returns {Source} the created source
413 */
414 createSourceForAsset(
415 context,
416 name,
417 content,
418 sourceMap,
419 associatedObjectForCache
420 ) {
421 if (sourceMap) {
422 if (
423 typeof sourceMap === "string" &&
424 (this.useSourceMap || this.useSimpleSourceMap)
425 ) {
426 return new OriginalSource(
427 content,
428 contextifySourceUrl(context, sourceMap, associatedObjectForCache)
429 );
430 }
431
432 if (this.useSourceMap) {
433 return new SourceMapSource(
434 content,
435 name,
436 contextifySourceMap(context, sourceMap, associatedObjectForCache)
437 );
438 }
439 }
440
441 return new RawSource(content);
442 }
443
444 /**
445 * @param {ResolverWithOptions} resolver a resolver
446 * @param {WebpackOptions} options webpack options
447 * @param {Compilation} compilation the compilation
448 * @param {InputFileSystem} fs file system from reading
449 * @returns {NormalModuleLoaderContext} loader context
450 */
451 createLoaderContext(resolver, options, compilation, fs) {
452 const { requestShortener } = compilation.runtimeTemplate;
453 const getCurrentLoaderName = () => {
454 const currentLoader = this.getCurrentLoader(loaderContext);
455 if (!currentLoader) return "(not in loader scope)";
456 return requestShortener.shorten(currentLoader.loader);
457 };
458 const getResolveContext = () => {
459 return {
460 fileDependencies: {
461 add: d => loaderContext.addDependency(d)
462 },
463 contextDependencies: {
464 add: d => loaderContext.addContextDependency(d)
465 },
466 missingDependencies: {
467 add: d => loaderContext.addMissingDependency(d)
468 }
469 };
470 };
471 const getAbsolutify = memoize(() =>
472 absolutify.bindCache(compilation.compiler.root)
473 );
474 const getAbsolutifyInContext = memoize(() =>
475 absolutify.bindContextCache(this.context, compilation.compiler.root)
476 );
477 const getContextify = memoize(() =>
478 contextify.bindCache(compilation.compiler.root)
479 );
480 const getContextifyInContext = memoize(() =>
481 contextify.bindContextCache(this.context, compilation.compiler.root)
482 );
483 const utils = {
484 absolutify: (context, request) => {
485 return context === this.context
486 ? getAbsolutifyInContext()(request)
487 : getAbsolutify()(context, request);
488 },
489 contextify: (context, request) => {
490 return context === this.context
491 ? getContextifyInContext()(request)
492 : getContextify()(context, request);
493 }
494 };
495 const loaderContext = {
496 version: 2,
497 getOptions: schema => {
498 const loader = this.getCurrentLoader(loaderContext);
499
500 let { options } = loader;
501
502 if (typeof options === "string") {
503 if (options.substr(0, 1) === "{" && options.substr(-1) === "}") {
504 try {
505 options = parseJson(options);
506 } catch (e) {
507 throw new Error(`Cannot parse string options: ${e.message}`);
508 }
509 } else {
510 options = querystring.parse(options, "&", "=", {
511 maxKeys: 0
512 });
513 }
514 }
515
516 if (options === null || options === undefined) {
517 options = {};
518 }
519
520 if (schema) {
521 let name = "Loader";
522 let baseDataPath = "options";
523 let match;
524 if (schema.title && (match = /^(.+) (.+)$/.exec(schema.title))) {
525 [, name, baseDataPath] = match;
526 }
527 getValidate()(schema, options, {
528 name,
529 baseDataPath
530 });
531 }
532
533 return options;
534 },
535 emitWarning: warning => {
536 if (!(warning instanceof Error)) {
537 warning = new NonErrorEmittedError(warning);
538 }
539 this.addWarning(
540 new ModuleWarning(warning, {
541 from: getCurrentLoaderName()
542 })
543 );
544 },
545 emitError: error => {
546 if (!(error instanceof Error)) {
547 error = new NonErrorEmittedError(error);
548 }
549 this.addError(
550 new ModuleError(error, {
551 from: getCurrentLoaderName()
552 })
553 );
554 },
555 getLogger: name => {
556 const currentLoader = this.getCurrentLoader(loaderContext);
557 return compilation.getLogger(() =>
558 [currentLoader && currentLoader.loader, name, this.identifier()]
559 .filter(Boolean)
560 .join("|")
561 );
562 },
563 resolve(context, request, callback) {
564 resolver.resolve({}, context, request, getResolveContext(), callback);
565 },
566 getResolve(options) {
567 const child = options ? resolver.withOptions(options) : resolver;
568 return (context, request, callback) => {
569 if (callback) {
570 child.resolve({}, context, request, getResolveContext(), callback);
571 } else {
572 return new Promise((resolve, reject) => {
573 child.resolve(
574 {},
575 context,
576 request,
577 getResolveContext(),
578 (err, result) => {
579 if (err) reject(err);
580 else resolve(result);
581 }
582 );
583 });
584 }
585 };
586 },
587 emitFile: (name, content, sourceMap, assetInfo) => {
588 if (!this.buildInfo.assets) {
589 this.buildInfo.assets = Object.create(null);
590 this.buildInfo.assetsInfo = new Map();
591 }
592 this.buildInfo.assets[name] = this.createSourceForAsset(
593 options.context,
594 name,
595 content,
596 sourceMap,
597 compilation.compiler.root
598 );
599 this.buildInfo.assetsInfo.set(name, assetInfo);
600 },
601 addBuildDependency: dep => {
602 if (this.buildInfo.buildDependencies === undefined) {
603 this.buildInfo.buildDependencies = new LazySet();
604 }
605 this.buildInfo.buildDependencies.add(dep);
606 },
607 utils,
608 rootContext: options.context,
609 webpack: true,
610 sourceMap: !!this.useSourceMap,
611 mode: options.mode || "production",
612 _module: this,
613 _compilation: compilation,
614 _compiler: compilation.compiler,
615 fs: fs
616 };
617
618 Object.assign(loaderContext, options.loader);
619
620 NormalModule.getCompilationHooks(compilation).loader.call(
621 loaderContext,
622 this
623 );
624
625 return loaderContext;
626 }
627
628 getCurrentLoader(loaderContext, index = loaderContext.loaderIndex) {
629 if (
630 this.loaders &&
631 this.loaders.length &&
632 index < this.loaders.length &&
633 index >= 0 &&
634 this.loaders[index]
635 ) {
636 return this.loaders[index];
637 }
638 return null;
639 }
640
641 /**
642 * @param {string} context the compilation context
643 * @param {string | Buffer} content the content
644 * @param {string | TODO} sourceMap an optional source map
645 * @param {Object=} associatedObjectForCache object for caching
646 * @returns {Source} the created source
647 */
648 createSource(context, content, sourceMap, associatedObjectForCache) {
649 if (Buffer.isBuffer(content)) {
650 return new RawSource(content);
651 }
652
653 // if there is no identifier return raw source
654 if (!this.identifier) {
655 return new RawSource(content);
656 }
657
658 // from here on we assume we have an identifier
659 const identifier = this.identifier();
660
661 if (this.useSourceMap && sourceMap) {
662 return new SourceMapSource(
663 content,
664 contextifySourceUrl(context, identifier, associatedObjectForCache),
665 contextifySourceMap(context, sourceMap, associatedObjectForCache)
666 );
667 }
668
669 if (this.useSourceMap || this.useSimpleSourceMap) {
670 return new OriginalSource(
671 content,
672 contextifySourceUrl(context, identifier, associatedObjectForCache)
673 );
674 }
675
676 return new RawSource(content);
677 }
678
679 /**
680 * @param {WebpackOptions} options webpack options
681 * @param {Compilation} compilation the compilation
682 * @param {ResolverWithOptions} resolver the resolver
683 * @param {InputFileSystem} fs the file system
684 * @param {function(WebpackError=): void} callback callback function
685 * @returns {void}
686 */
687 doBuild(options, compilation, resolver, fs, callback) {
688 const loaderContext = this.createLoaderContext(
689 resolver,
690 options,
691 compilation,
692 fs
693 );
694
695 const processResult = (err, result) => {
696 if (err) {
697 if (!(err instanceof Error)) {
698 err = new NonErrorEmittedError(err);
699 }
700 const currentLoader = this.getCurrentLoader(loaderContext);
701 const error = new ModuleBuildError(err, {
702 from:
703 currentLoader &&
704 compilation.runtimeTemplate.requestShortener.shorten(
705 currentLoader.loader
706 )
707 });
708 return callback(error);
709 }
710
711 const source = result[0];
712 const sourceMap = result.length >= 1 ? result[1] : null;
713 const extraInfo = result.length >= 2 ? result[2] : null;
714
715 if (!Buffer.isBuffer(source) && typeof source !== "string") {
716 const currentLoader = this.getCurrentLoader(loaderContext, 0);
717 const err = new Error(
718 `Final loader (${
719 currentLoader
720 ? compilation.runtimeTemplate.requestShortener.shorten(
721 currentLoader.loader
722 )
723 : "unknown"
724 }) didn't return a Buffer or String`
725 );
726 const error = new ModuleBuildError(err);
727 return callback(error);
728 }
729
730 this._source = this.createSource(
731 options.context,
732 this.binary ? asBuffer(source) : asString(source),
733 sourceMap,
734 compilation.compiler.root
735 );
736 if (this._sourceSizes !== undefined) this._sourceSizes.clear();
737 this._ast =
738 typeof extraInfo === "object" &&
739 extraInfo !== null &&
740 extraInfo.webpackAST !== undefined
741 ? extraInfo.webpackAST
742 : null;
743 return callback();
744 };
745
746 const hooks = NormalModule.getCompilationHooks(compilation);
747
748 try {
749 hooks.beforeLoaders.call(this.loaders, this, loaderContext);
750 } catch (err) {
751 processResult(err);
752 return;
753 }
754 runLoaders(
755 {
756 resource: this.resource,
757 loaders: this.loaders,
758 context: loaderContext,
759 processResource: (loaderContext, resource, callback) => {
760 const scheme = getScheme(resource);
761 if (scheme) {
762 hooks.readResourceForScheme
763 .for(scheme)
764 .callAsync(resource, this, (err, result) => {
765 if (err) return callback(err);
766 if (typeof result !== "string" && !result) {
767 return callback(new UnhandledSchemeError(scheme, resource));
768 }
769 return callback(null, result);
770 });
771 } else {
772 loaderContext.addDependency(resource);
773 fs.readFile(resource, callback);
774 }
775 }
776 },
777 (err, result) => {
778 // Cleanup loaderContext to avoid leaking memory in ICs
779 loaderContext._compilation =
780 loaderContext._compiler =
781 loaderContext._module =
782 loaderContext.fs =
783 undefined;
784
785 if (!result) {
786 return processResult(
787 err || new Error("No result from loader-runner processing"),
788 null
789 );
790 }
791 this.buildInfo.fileDependencies = new LazySet();
792 this.buildInfo.fileDependencies.addAll(result.fileDependencies);
793 this.buildInfo.contextDependencies = new LazySet();
794 this.buildInfo.contextDependencies.addAll(result.contextDependencies);
795 this.buildInfo.missingDependencies = new LazySet();
796 this.buildInfo.missingDependencies.addAll(result.missingDependencies);
797 if (
798 this.loaders.length > 0 &&
799 this.buildInfo.buildDependencies === undefined
800 ) {
801 this.buildInfo.buildDependencies = new LazySet();
802 }
803 for (const loader of this.loaders) {
804 this.buildInfo.buildDependencies.add(loader.loader);
805 }
806 this.buildInfo.cacheable = result.cacheable;
807 processResult(err, result.result);
808 }
809 );
810 }
811
812 /**
813 * @param {WebpackError} error the error
814 * @returns {void}
815 */
816 markModuleAsErrored(error) {
817 // Restore build meta from successful build to keep importing state
818 this.buildMeta = { ...this._lastSuccessfulBuildMeta };
819 this.error = error;
820 this.addError(error);
821 }
822
823 applyNoParseRule(rule, content) {
824 // must start with "rule" if rule is a string
825 if (typeof rule === "string") {
826 return content.startsWith(rule);
827 }
828
829 if (typeof rule === "function") {
830 return rule(content);
831 }
832 // we assume rule is a regexp
833 return rule.test(content);
834 }
835
836 // check if module should not be parsed
837 // returns "true" if the module should !not! be parsed
838 // returns "false" if the module !must! be parsed
839 shouldPreventParsing(noParseRule, request) {
840 // if no noParseRule exists, return false
841 // the module !must! be parsed.
842 if (!noParseRule) {
843 return false;
844 }
845
846 // we only have one rule to check
847 if (!Array.isArray(noParseRule)) {
848 // returns "true" if the module is !not! to be parsed
849 return this.applyNoParseRule(noParseRule, request);
850 }
851
852 for (let i = 0; i < noParseRule.length; i++) {
853 const rule = noParseRule[i];
854 // early exit on first truthy match
855 // this module is !not! to be parsed
856 if (this.applyNoParseRule(rule, request)) {
857 return true;
858 }
859 }
860 // no match found, so this module !should! be parsed
861 return false;
862 }
863
864 _initBuildHash(compilation) {
865 const hash = createHash(compilation.outputOptions.hashFunction);
866 if (this._source) {
867 hash.update("source");
868 this._source.updateHash(hash);
869 }
870 hash.update("meta");
871 hash.update(JSON.stringify(this.buildMeta));
872 this.buildInfo.hash = /** @type {string} */ (hash.digest("hex"));
873 }
874
875 /**
876 * @param {WebpackOptions} options webpack options
877 * @param {Compilation} compilation the compilation
878 * @param {ResolverWithOptions} resolver the resolver
879 * @param {InputFileSystem} fs the file system
880 * @param {function(WebpackError=): void} callback callback function
881 * @returns {void}
882 */
883 build(options, compilation, resolver, fs, callback) {
884 this._forceBuild = false;
885 this._source = null;
886 if (this._sourceSizes !== undefined) this._sourceSizes.clear();
887 this._sourceTypes = undefined;
888 this._ast = null;
889 this.error = null;
890 this.clearWarningsAndErrors();
891 this.clearDependenciesAndBlocks();
892 this.buildMeta = {};
893 this.buildInfo = {
894 cacheable: false,
895 parsed: true,
896 fileDependencies: undefined,
897 contextDependencies: undefined,
898 missingDependencies: undefined,
899 buildDependencies: undefined,
900 valueDependencies: undefined,
901 hash: undefined,
902 assets: undefined,
903 assetsInfo: undefined
904 };
905
906 const startTime = compilation.compiler.fsStartTime || Date.now();
907
908 return this.doBuild(options, compilation, resolver, fs, err => {
909 // if we have an error mark module as failed and exit
910 if (err) {
911 this.markModuleAsErrored(err);
912 this._initBuildHash(compilation);
913 return callback();
914 }
915
916 const handleParseError = e => {
917 const source = this._source.source();
918 const loaders = this.loaders.map(item =>
919 contextify(options.context, item.loader, compilation.compiler.root)
920 );
921 const error = new ModuleParseError(source, e, loaders, this.type);
922 this.markModuleAsErrored(error);
923 this._initBuildHash(compilation);
924 return callback();
925 };
926
927 const handleParseResult = result => {
928 this.dependencies.sort(
929 concatComparators(
930 compareSelect(a => a.loc, compareLocations),
931 keepOriginalOrder(this.dependencies)
932 )
933 );
934 this._initBuildHash(compilation);
935 this._lastSuccessfulBuildMeta = this.buildMeta;
936 return handleBuildDone();
937 };
938
939 const handleBuildDone = () => {
940 const snapshotOptions = compilation.options.snapshot.module;
941 if (!this.buildInfo.cacheable || !snapshotOptions) {
942 return callback();
943 }
944 // add warning for all non-absolute paths in fileDependencies, etc
945 // This makes it easier to find problems with watching and/or caching
946 let nonAbsoluteDependencies = undefined;
947 const checkDependencies = deps => {
948 for (const dep of deps) {
949 if (!ABSOLUTE_PATH_REGEX.test(dep)) {
950 if (nonAbsoluteDependencies === undefined)
951 nonAbsoluteDependencies = new Set();
952 nonAbsoluteDependencies.add(dep);
953 deps.delete(dep);
954 try {
955 const depWithoutGlob = dep.replace(/[\\/]?\*.*$/, "");
956 const absolute = join(
957 compilation.fileSystemInfo.fs,
958 this.context,
959 depWithoutGlob
960 );
961 if (absolute !== dep && ABSOLUTE_PATH_REGEX.test(absolute)) {
962 (depWithoutGlob !== dep
963 ? this.buildInfo.contextDependencies
964 : deps
965 ).add(absolute);
966 }
967 } catch (e) {
968 // ignore
969 }
970 }
971 }
972 };
973 checkDependencies(this.buildInfo.fileDependencies);
974 checkDependencies(this.buildInfo.missingDependencies);
975 checkDependencies(this.buildInfo.contextDependencies);
976 if (nonAbsoluteDependencies !== undefined) {
977 const InvalidDependenciesModuleWarning =
978 getInvalidDependenciesModuleWarning();
979 this.addWarning(
980 new InvalidDependenciesModuleWarning(this, nonAbsoluteDependencies)
981 );
982 }
983 // convert file/context/missingDependencies into filesystem snapshot
984 compilation.fileSystemInfo.createSnapshot(
985 startTime,
986 this.buildInfo.fileDependencies,
987 this.buildInfo.contextDependencies,
988 this.buildInfo.missingDependencies,
989 snapshotOptions,
990 (err, snapshot) => {
991 if (err) {
992 this.markModuleAsErrored(err);
993 return;
994 }
995 this.buildInfo.fileDependencies = undefined;
996 this.buildInfo.contextDependencies = undefined;
997 this.buildInfo.missingDependencies = undefined;
998 this.buildInfo.snapshot = snapshot;
999 return callback();
1000 }
1001 );
1002 };
1003
1004 // check if this module should !not! be parsed.
1005 // if so, exit here;
1006 const noParseRule = options.module && options.module.noParse;
1007 if (this.shouldPreventParsing(noParseRule, this.request)) {
1008 // We assume that we need module and exports
1009 this.buildInfo.parsed = false;
1010 this._initBuildHash(compilation);
1011 return handleBuildDone();
1012 }
1013
1014 let result;
1015 try {
1016 result = this.parser.parse(this._ast || this._source.source(), {
1017 current: this,
1018 module: this,
1019 compilation: compilation,
1020 options: options
1021 });
1022 } catch (e) {
1023 handleParseError(e);
1024 return;
1025 }
1026 handleParseResult(result);
1027 });
1028 }
1029
1030 /**
1031 * @param {ConcatenationBailoutReasonContext} context context
1032 * @returns {string | undefined} reason why this module can't be concatenated, undefined when it can be concatenated
1033 */
1034 getConcatenationBailoutReason(context) {
1035 return this.generator.getConcatenationBailoutReason(this, context);
1036 }
1037
1038 /**
1039 * @param {ModuleGraph} moduleGraph the module graph
1040 * @returns {ConnectionState} how this module should be connected to referencing modules when consumed for side-effects only
1041 */
1042 getSideEffectsConnectionState(moduleGraph) {
1043 if (this.factoryMeta !== undefined) {
1044 if (this.factoryMeta.sideEffectFree) return false;
1045 if (this.factoryMeta.sideEffectFree === false) return true;
1046 }
1047 if (this.buildMeta !== undefined && this.buildMeta.sideEffectFree) {
1048 if (this._isEvaluatingSideEffects)
1049 return ModuleGraphConnection.CIRCULAR_CONNECTION;
1050 this._isEvaluatingSideEffects = true;
1051 /** @type {ConnectionState} */
1052 let current = false;
1053 for (const dep of this.dependencies) {
1054 const state = dep.getModuleEvaluationSideEffectsState(moduleGraph);
1055 if (state === true) {
1056 if (
1057 this._addedSideEffectsBailout === undefined
1058 ? ((this._addedSideEffectsBailout = new WeakSet()), true)
1059 : !this._addedSideEffectsBailout.has(moduleGraph)
1060 ) {
1061 this._addedSideEffectsBailout.add(moduleGraph);
1062 moduleGraph
1063 .getOptimizationBailout(this)
1064 .push(
1065 () =>
1066 `Dependency (${
1067 dep.type
1068 }) with side effects at ${formatLocation(dep.loc)}`
1069 );
1070 }
1071 this._isEvaluatingSideEffects = false;
1072 return true;
1073 } else if (state !== ModuleGraphConnection.CIRCULAR_CONNECTION) {
1074 current = ModuleGraphConnection.addConnectionStates(current, state);
1075 }
1076 }
1077 this._isEvaluatingSideEffects = false;
1078 // When caching is implemented here, make sure to not cache when
1079 // at least one circular connection was in the loop above
1080 return current;
1081 } else {
1082 return true;
1083 }
1084 }
1085
1086 /**
1087 * @returns {Set<string>} types available (do not mutate)
1088 */
1089 getSourceTypes() {
1090 if (this._sourceTypes === undefined) {
1091 this._sourceTypes = this.generator.getTypes(this);
1092 }
1093 return this._sourceTypes;
1094 }
1095
1096 /**
1097 * @param {CodeGenerationContext} context context for code generation
1098 * @returns {CodeGenerationResult} result
1099 */
1100 codeGeneration({
1101 dependencyTemplates,
1102 runtimeTemplate,
1103 moduleGraph,
1104 chunkGraph,
1105 runtime,
1106 concatenationScope
1107 }) {
1108 /** @type {Set<string>} */
1109 const runtimeRequirements = new Set();
1110
1111 if (!this.buildInfo.parsed) {
1112 runtimeRequirements.add(RuntimeGlobals.module);
1113 runtimeRequirements.add(RuntimeGlobals.exports);
1114 runtimeRequirements.add(RuntimeGlobals.thisAsExports);
1115 }
1116
1117 /** @type {Map<string, any>} */
1118 let data;
1119 const getData = () => {
1120 if (data === undefined) data = new Map();
1121 return data;
1122 };
1123
1124 const sources = new Map();
1125 for (const type of this.generator.getTypes(this)) {
1126 const source = this.error
1127 ? new RawSource(
1128 "throw new Error(" + JSON.stringify(this.error.message) + ");"
1129 )
1130 : this.generator.generate(this, {
1131 dependencyTemplates,
1132 runtimeTemplate,
1133 moduleGraph,
1134 chunkGraph,
1135 runtimeRequirements,
1136 runtime,
1137 concatenationScope,
1138 getData,
1139 type
1140 });
1141
1142 if (source) {
1143 sources.set(type, new CachedSource(source));
1144 }
1145 }
1146
1147 /** @type {CodeGenerationResult} */
1148 const resultEntry = {
1149 sources,
1150 runtimeRequirements,
1151 data
1152 };
1153 return resultEntry;
1154 }
1155
1156 /**
1157 * @returns {Source | null} the original source for the module before webpack transformation
1158 */
1159 originalSource() {
1160 return this._source;
1161 }
1162
1163 /**
1164 * @returns {void}
1165 */
1166 invalidateBuild() {
1167 this._forceBuild = true;
1168 }
1169
1170 /**
1171 * @param {NeedBuildContext} context context info
1172 * @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
1173 * @returns {void}
1174 */
1175 needBuild({ fileSystemInfo, valueCacheVersions }, callback) {
1176 // build if enforced
1177 if (this._forceBuild) return callback(null, true);
1178
1179 // always try to build in case of an error
1180 if (this.error) return callback(null, true);
1181
1182 // always build when module is not cacheable
1183 if (!this.buildInfo.cacheable) return callback(null, true);
1184
1185 // build when there is no snapshot to check
1186 if (!this.buildInfo.snapshot) return callback(null, true);
1187
1188 // build when valueDependencies have changed
1189 /** @type {Map<string, string | Set<string>>} */
1190 const valueDependencies = this.buildInfo.valueDependencies;
1191 if (valueDependencies) {
1192 if (!valueCacheVersions) return callback(null, true);
1193 for (const [key, value] of valueDependencies) {
1194 if (value === undefined) return callback(null, true);
1195 const current = valueCacheVersions.get(key);
1196 if (
1197 value !== current &&
1198 (typeof value === "string" ||
1199 typeof current === "string" ||
1200 current === undefined ||
1201 !isSubset(value, current))
1202 ) {
1203 return callback(null, true);
1204 }
1205 }
1206 }
1207
1208 // check snapshot for validity
1209 fileSystemInfo.checkSnapshotValid(this.buildInfo.snapshot, (err, valid) => {
1210 callback(err, !valid);
1211 });
1212 }
1213
1214 /**
1215 * @param {string=} type the source type for which the size should be estimated
1216 * @returns {number} the estimated size of the module (must be non-zero)
1217 */
1218 size(type) {
1219 const cachedSize =
1220 this._sourceSizes === undefined ? undefined : this._sourceSizes.get(type);
1221 if (cachedSize !== undefined) {
1222 return cachedSize;
1223 }
1224 const size = Math.max(1, this.generator.getSize(this, type));
1225 if (this._sourceSizes === undefined) {
1226 this._sourceSizes = new Map();
1227 }
1228 this._sourceSizes.set(type, size);
1229 return size;
1230 }
1231
1232 /**
1233 * @param {LazySet<string>} fileDependencies set where file dependencies are added to
1234 * @param {LazySet<string>} contextDependencies set where context dependencies are added to
1235 * @param {LazySet<string>} missingDependencies set where missing dependencies are added to
1236 * @param {LazySet<string>} buildDependencies set where build dependencies are added to
1237 */
1238 addCacheDependencies(
1239 fileDependencies,
1240 contextDependencies,
1241 missingDependencies,
1242 buildDependencies
1243 ) {
1244 const { snapshot, buildDependencies: buildDeps } = this.buildInfo;
1245 if (snapshot) {
1246 fileDependencies.addAll(snapshot.getFileIterable());
1247 contextDependencies.addAll(snapshot.getContextIterable());
1248 missingDependencies.addAll(snapshot.getMissingIterable());
1249 } else {
1250 const {
1251 fileDependencies: fileDeps,
1252 contextDependencies: contextDeps,
1253 missingDependencies: missingDeps
1254 } = this.buildInfo;
1255 if (fileDeps !== undefined) fileDependencies.addAll(fileDeps);
1256 if (contextDeps !== undefined) contextDependencies.addAll(contextDeps);
1257 if (missingDeps !== undefined) missingDependencies.addAll(missingDeps);
1258 }
1259 if (buildDeps !== undefined) {
1260 buildDependencies.addAll(buildDeps);
1261 }
1262 }
1263
1264 /**
1265 * @param {Hash} hash the hash used to track dependencies
1266 * @param {UpdateHashContext} context context
1267 * @returns {void}
1268 */
1269 updateHash(hash, context) {
1270 hash.update(this.buildInfo.hash);
1271 this.generator.updateHash(hash, {
1272 module: this,
1273 ...context
1274 });
1275 super.updateHash(hash, context);
1276 }
1277
1278 serialize(context) {
1279 const { write } = context;
1280 // deserialize
1281 write(this._source);
1282 write(this.error);
1283 write(this._lastSuccessfulBuildMeta);
1284 write(this._forceBuild);
1285 super.serialize(context);
1286 }
1287
1288 static deserialize(context) {
1289 const obj = new NormalModule({
1290 // will be deserialized by Module
1291 layer: null,
1292 type: "",
1293 // will be filled by updateCacheModule
1294 resource: "",
1295 request: null,
1296 userRequest: null,
1297 rawRequest: null,
1298 loaders: null,
1299 matchResource: null,
1300 parser: null,
1301 parserOptions: null,
1302 generator: null,
1303 generatorOptions: null,
1304 resolveOptions: null
1305 });
1306 obj.deserialize(context);
1307 return obj;
1308 }
1309
1310 deserialize(context) {
1311 const { read } = context;
1312 this._source = read();
1313 this.error = read();
1314 this._lastSuccessfulBuildMeta = read();
1315 this._forceBuild = read();
1316 super.deserialize(context);
1317 }
1318}
1319
1320makeSerializable(NormalModule, "webpack/lib/NormalModule");
1321
1322module.exports = NormalModule;