UNPKG

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