UNPKG

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