UNPKG

37.5 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 if (!result) {
753 return processResult(
754 err || new Error("No result from loader-runner processing"),
755 null
756 );
757 }
758 this.buildInfo.fileDependencies = new LazySet();
759 this.buildInfo.fileDependencies.addAll(result.fileDependencies);
760 this.buildInfo.contextDependencies = new LazySet();
761 this.buildInfo.contextDependencies.addAll(result.contextDependencies);
762 this.buildInfo.missingDependencies = new LazySet();
763 this.buildInfo.missingDependencies.addAll(result.missingDependencies);
764 if (
765 this.loaders.length > 0 &&
766 this.buildInfo.buildDependencies === undefined
767 ) {
768 this.buildInfo.buildDependencies = new LazySet();
769 }
770 for (const loader of this.loaders) {
771 this.buildInfo.buildDependencies.add(loader.loader);
772 }
773 this.buildInfo.cacheable = result.cacheable;
774 processResult(err, result.result);
775 }
776 );
777 }
778
779 /**
780 * @param {WebpackError} error the error
781 * @returns {void}
782 */
783 markModuleAsErrored(error) {
784 // Restore build meta from successful build to keep importing state
785 this.buildMeta = { ...this._lastSuccessfulBuildMeta };
786 this.error = error;
787 this.addError(error);
788 }
789
790 applyNoParseRule(rule, content) {
791 // must start with "rule" if rule is a string
792 if (typeof rule === "string") {
793 return content.startsWith(rule);
794 }
795
796 if (typeof rule === "function") {
797 return rule(content);
798 }
799 // we assume rule is a regexp
800 return rule.test(content);
801 }
802
803 // check if module should not be parsed
804 // returns "true" if the module should !not! be parsed
805 // returns "false" if the module !must! be parsed
806 shouldPreventParsing(noParseRule, request) {
807 // if no noParseRule exists, return false
808 // the module !must! be parsed.
809 if (!noParseRule) {
810 return false;
811 }
812
813 // we only have one rule to check
814 if (!Array.isArray(noParseRule)) {
815 // returns "true" if the module is !not! to be parsed
816 return this.applyNoParseRule(noParseRule, request);
817 }
818
819 for (let i = 0; i < noParseRule.length; i++) {
820 const rule = noParseRule[i];
821 // early exit on first truthy match
822 // this module is !not! to be parsed
823 if (this.applyNoParseRule(rule, request)) {
824 return true;
825 }
826 }
827 // no match found, so this module !should! be parsed
828 return false;
829 }
830
831 _initBuildHash(compilation) {
832 const hash = createHash(compilation.outputOptions.hashFunction);
833 if (this._source) {
834 hash.update("source");
835 this._source.updateHash(hash);
836 }
837 hash.update("meta");
838 hash.update(JSON.stringify(this.buildMeta));
839 this.buildInfo.hash = /** @type {string} */ (hash.digest("hex"));
840 }
841
842 /**
843 * @param {WebpackOptions} options webpack options
844 * @param {Compilation} compilation the compilation
845 * @param {ResolverWithOptions} resolver the resolver
846 * @param {InputFileSystem} fs the file system
847 * @param {function(WebpackError=): void} callback callback function
848 * @returns {void}
849 */
850 build(options, compilation, resolver, fs, callback) {
851 this._forceBuild = false;
852 this._source = null;
853 if (this._sourceSizes !== undefined) this._sourceSizes.clear();
854 this._ast = null;
855 this.error = null;
856 this.clearWarningsAndErrors();
857 this.clearDependenciesAndBlocks();
858 this.buildMeta = {};
859 this.buildInfo = {
860 cacheable: false,
861 parsed: true,
862 fileDependencies: undefined,
863 contextDependencies: undefined,
864 missingDependencies: undefined,
865 buildDependencies: undefined,
866 valueDependencies: undefined,
867 hash: undefined,
868 assets: undefined,
869 assetsInfo: undefined
870 };
871
872 const startTime = Date.now();
873
874 return this.doBuild(options, compilation, resolver, fs, err => {
875 // if we have an error mark module as failed and exit
876 if (err) {
877 this.markModuleAsErrored(err);
878 this._initBuildHash(compilation);
879 return callback();
880 }
881
882 const handleParseError = e => {
883 const source = this._source.source();
884 const loaders = this.loaders.map(item =>
885 contextify(options.context, item.loader, compilation.compiler.root)
886 );
887 const error = new ModuleParseError(source, e, loaders, this.type);
888 this.markModuleAsErrored(error);
889 this._initBuildHash(compilation);
890 return callback();
891 };
892
893 const handleParseResult = result => {
894 this.dependencies.sort(
895 concatComparators(
896 compareSelect(a => a.loc, compareLocations),
897 keepOriginalOrder(this.dependencies)
898 )
899 );
900 this._initBuildHash(compilation);
901 this._lastSuccessfulBuildMeta = this.buildMeta;
902 return handleBuildDone();
903 };
904
905 const handleBuildDone = () => {
906 const snapshotOptions = compilation.options.snapshot.module;
907 if (!this.buildInfo.cacheable || !snapshotOptions) {
908 return callback();
909 }
910 // add warning for all non-absolute paths in fileDependencies, etc
911 // This makes it easier to find problems with watching and/or caching
912 let nonAbsoluteDependencies = undefined;
913 const checkDependencies = deps => {
914 for (const dep of deps) {
915 if (!ABSOLUTE_PATH_REGEX.test(dep)) {
916 if (nonAbsoluteDependencies === undefined)
917 nonAbsoluteDependencies = new Set();
918 nonAbsoluteDependencies.add(dep);
919 deps.delete(dep);
920 try {
921 const depWithoutGlob = dep.replace(/[\\/]?\*.*$/, "");
922 const absolute = join(
923 compilation.fileSystemInfo.fs,
924 this.context,
925 depWithoutGlob
926 );
927 if (absolute !== dep && ABSOLUTE_PATH_REGEX.test(absolute)) {
928 (depWithoutGlob !== dep
929 ? this.buildInfo.contextDependencies
930 : deps
931 ).add(absolute);
932 }
933 } catch (e) {
934 // ignore
935 }
936 }
937 }
938 };
939 checkDependencies(this.buildInfo.fileDependencies);
940 checkDependencies(this.buildInfo.missingDependencies);
941 checkDependencies(this.buildInfo.contextDependencies);
942 if (nonAbsoluteDependencies !== undefined) {
943 const InvalidDependenciesModuleWarning = getInvalidDependenciesModuleWarning();
944 this.addWarning(
945 new InvalidDependenciesModuleWarning(this, nonAbsoluteDependencies)
946 );
947 }
948 // convert file/context/missingDependencies into filesystem snapshot
949 compilation.fileSystemInfo.createSnapshot(
950 startTime,
951 this.buildInfo.fileDependencies,
952 this.buildInfo.contextDependencies,
953 this.buildInfo.missingDependencies,
954 snapshotOptions,
955 (err, snapshot) => {
956 if (err) {
957 this.markModuleAsErrored(err);
958 return;
959 }
960 this.buildInfo.fileDependencies = undefined;
961 this.buildInfo.contextDependencies = undefined;
962 this.buildInfo.missingDependencies = undefined;
963 this.buildInfo.snapshot = snapshot;
964 return callback();
965 }
966 );
967 };
968
969 // check if this module should !not! be parsed.
970 // if so, exit here;
971 const noParseRule = options.module && options.module.noParse;
972 if (this.shouldPreventParsing(noParseRule, this.request)) {
973 // We assume that we need module and exports
974 this.buildInfo.parsed = false;
975 this._initBuildHash(compilation);
976 return handleBuildDone();
977 }
978
979 let result;
980 try {
981 result = this.parser.parse(this._ast || this._source.source(), {
982 current: this,
983 module: this,
984 compilation: compilation,
985 options: options
986 });
987 } catch (e) {
988 handleParseError(e);
989 return;
990 }
991 handleParseResult(result);
992 });
993 }
994
995 /**
996 * @param {ConcatenationBailoutReasonContext} context context
997 * @returns {string | undefined} reason why this module can't be concatenated, undefined when it can be concatenated
998 */
999 getConcatenationBailoutReason(context) {
1000 return this.generator.getConcatenationBailoutReason(this, context);
1001 }
1002
1003 /**
1004 * @param {ModuleGraph} moduleGraph the module graph
1005 * @returns {ConnectionState} how this module should be connected to referencing modules when consumed for side-effects only
1006 */
1007 getSideEffectsConnectionState(moduleGraph) {
1008 if (this.factoryMeta !== undefined) {
1009 if (this.factoryMeta.sideEffectFree) return false;
1010 if (this.factoryMeta.sideEffectFree === false) return true;
1011 }
1012 if (this.buildMeta !== undefined && this.buildMeta.sideEffectFree) {
1013 if (this._isEvaluatingSideEffects)
1014 return ModuleGraphConnection.CIRCULAR_CONNECTION;
1015 this._isEvaluatingSideEffects = true;
1016 /** @type {ConnectionState} */
1017 let current = false;
1018 for (const dep of this.dependencies) {
1019 const state = dep.getModuleEvaluationSideEffectsState(moduleGraph);
1020 if (state === true) {
1021 if (
1022 this._addedSideEffectsBailout === undefined
1023 ? ((this._addedSideEffectsBailout = new WeakSet()), true)
1024 : !this._addedSideEffectsBailout.has(moduleGraph)
1025 ) {
1026 this._addedSideEffectsBailout.add(moduleGraph);
1027 moduleGraph
1028 .getOptimizationBailout(this)
1029 .push(
1030 () =>
1031 `Dependency (${
1032 dep.type
1033 }) with side effects at ${formatLocation(dep.loc)}`
1034 );
1035 }
1036 this._isEvaluatingSideEffects = false;
1037 return true;
1038 } else if (state !== ModuleGraphConnection.CIRCULAR_CONNECTION) {
1039 current = ModuleGraphConnection.addConnectionStates(current, state);
1040 }
1041 }
1042 this._isEvaluatingSideEffects = false;
1043 // When caching is implemented here, make sure to not cache when
1044 // at least one circular connection was in the loop above
1045 return current;
1046 } else {
1047 return true;
1048 }
1049 }
1050
1051 /**
1052 * @returns {Set<string>} types available (do not mutate)
1053 */
1054 getSourceTypes() {
1055 return this.generator.getTypes(this);
1056 }
1057
1058 /**
1059 * @param {CodeGenerationContext} context context for code generation
1060 * @returns {CodeGenerationResult} result
1061 */
1062 codeGeneration({
1063 dependencyTemplates,
1064 runtimeTemplate,
1065 moduleGraph,
1066 chunkGraph,
1067 runtime,
1068 concatenationScope
1069 }) {
1070 /** @type {Set<string>} */
1071 const runtimeRequirements = new Set();
1072
1073 if (!this.buildInfo.parsed) {
1074 runtimeRequirements.add(RuntimeGlobals.module);
1075 runtimeRequirements.add(RuntimeGlobals.exports);
1076 runtimeRequirements.add(RuntimeGlobals.thisAsExports);
1077 }
1078
1079 /** @type {Map<string, any>} */
1080 let data;
1081 const getData = () => {
1082 if (data === undefined) data = new Map();
1083 return data;
1084 };
1085
1086 const sources = new Map();
1087 for (const type of this.generator.getTypes(this)) {
1088 const source = this.error
1089 ? new RawSource(
1090 "throw new Error(" + JSON.stringify(this.error.message) + ");"
1091 )
1092 : this.generator.generate(this, {
1093 dependencyTemplates,
1094 runtimeTemplate,
1095 moduleGraph,
1096 chunkGraph,
1097 runtimeRequirements,
1098 runtime,
1099 concatenationScope,
1100 getData,
1101 type
1102 });
1103
1104 if (source) {
1105 sources.set(type, new CachedSource(source));
1106 }
1107 }
1108
1109 /** @type {CodeGenerationResult} */
1110 const resultEntry = {
1111 sources,
1112 runtimeRequirements,
1113 data
1114 };
1115 return resultEntry;
1116 }
1117
1118 /**
1119 * @returns {Source | null} the original source for the module before webpack transformation
1120 */
1121 originalSource() {
1122 return this._source;
1123 }
1124
1125 /**
1126 * @returns {void}
1127 */
1128 invalidateBuild() {
1129 this._forceBuild = true;
1130 }
1131
1132 /**
1133 * @param {NeedBuildContext} context context info
1134 * @param {function(WebpackError=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
1135 * @returns {void}
1136 */
1137 needBuild({ fileSystemInfo, valueCacheVersions }, callback) {
1138 // build if enforced
1139 if (this._forceBuild) return callback(null, true);
1140
1141 // always try to build in case of an error
1142 if (this.error) return callback(null, true);
1143
1144 // always build when module is not cacheable
1145 if (!this.buildInfo.cacheable) return callback(null, true);
1146
1147 // build when there is no snapshot to check
1148 if (!this.buildInfo.snapshot) return callback(null, true);
1149
1150 // build when valueDependencies have changed
1151 if (this.buildInfo.valueDependencies) {
1152 if (!valueCacheVersions) return callback(null, true);
1153 for (const [key, value] of this.buildInfo.valueDependencies) {
1154 if (value === undefined) return callback(null, true);
1155 const current = valueCacheVersions.get(key);
1156 if (value !== current) return callback(null, true);
1157 }
1158 }
1159
1160 // check snapshot for validity
1161 fileSystemInfo.checkSnapshotValid(this.buildInfo.snapshot, (err, valid) => {
1162 callback(err, !valid);
1163 });
1164 }
1165
1166 /**
1167 * @param {string=} type the source type for which the size should be estimated
1168 * @returns {number} the estimated size of the module (must be non-zero)
1169 */
1170 size(type) {
1171 const cachedSize =
1172 this._sourceSizes === undefined ? undefined : this._sourceSizes.get(type);
1173 if (cachedSize !== undefined) {
1174 return cachedSize;
1175 }
1176 const size = Math.max(1, this.generator.getSize(this, type));
1177 if (this._sourceSizes === undefined) {
1178 this._sourceSizes = new Map();
1179 }
1180 this._sourceSizes.set(type, size);
1181 return size;
1182 }
1183
1184 /**
1185 * @param {LazySet<string>} fileDependencies set where file dependencies are added to
1186 * @param {LazySet<string>} contextDependencies set where context dependencies are added to
1187 * @param {LazySet<string>} missingDependencies set where missing dependencies are added to
1188 * @param {LazySet<string>} buildDependencies set where build dependencies are added to
1189 */
1190 addCacheDependencies(
1191 fileDependencies,
1192 contextDependencies,
1193 missingDependencies,
1194 buildDependencies
1195 ) {
1196 const { snapshot, buildDependencies: buildDeps } = this.buildInfo;
1197 if (snapshot) {
1198 fileDependencies.addAll(snapshot.getFileIterable());
1199 contextDependencies.addAll(snapshot.getContextIterable());
1200 missingDependencies.addAll(snapshot.getMissingIterable());
1201 } else {
1202 const {
1203 fileDependencies: fileDeps,
1204 contextDependencies: contextDeps,
1205 missingDependencies: missingDeps
1206 } = this.buildInfo;
1207 if (fileDeps !== undefined) fileDependencies.addAll(fileDeps);
1208 if (contextDeps !== undefined) contextDependencies.addAll(contextDeps);
1209 if (missingDeps !== undefined) missingDependencies.addAll(missingDeps);
1210 }
1211 if (buildDeps !== undefined) {
1212 buildDependencies.addAll(buildDeps);
1213 }
1214 }
1215
1216 /**
1217 * @param {Hash} hash the hash used to track dependencies
1218 * @param {UpdateHashContext} context context
1219 * @returns {void}
1220 */
1221 updateHash(hash, context) {
1222 hash.update(this.buildInfo.hash);
1223 this.generator.updateHash(hash, {
1224 module: this,
1225 ...context
1226 });
1227 super.updateHash(hash, context);
1228 }
1229
1230 serialize(context) {
1231 const { write } = context;
1232 // deserialize
1233 write(this._source);
1234 write(this._sourceSizes);
1235 write(this.error);
1236 write(this._lastSuccessfulBuildMeta);
1237 write(this._forceBuild);
1238 super.serialize(context);
1239 }
1240
1241 static deserialize(context) {
1242 const obj = new NormalModule({
1243 // will be deserialized by Module
1244 layer: null,
1245 type: "",
1246 // will be filled by updateCacheModule
1247 resource: "",
1248 request: null,
1249 userRequest: null,
1250 rawRequest: null,
1251 loaders: null,
1252 matchResource: null,
1253 parser: null,
1254 parserOptions: null,
1255 generator: null,
1256 generatorOptions: null,
1257 resolveOptions: null
1258 });
1259 obj.deserialize(context);
1260 return obj;
1261 }
1262
1263 deserialize(context) {
1264 const { read } = context;
1265 this._source = read();
1266 this._sourceSizes = read();
1267 this.error = read();
1268 this._lastSuccessfulBuildMeta = read();
1269 this._forceBuild = read();
1270 super.deserialize(context);
1271 }
1272}
1273
1274makeSerializable(NormalModule, "webpack/lib/NormalModule");
1275
1276module.exports = NormalModule;