UNPKG

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