UNPKG

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