| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";const NativeModule = require("module");const {	CachedSource,	LineToLineMappedSource,	OriginalSource,	RawSource,	SourceMapSource} = require("webpack-sources");const { getContext, runLoaders } = require("loader-runner");const WebpackError = require("./WebpackError");const Module = require("./Module");const ModuleParseError = require("./ModuleParseError");const ModuleBuildError = require("./ModuleBuildError");const ModuleError = require("./ModuleError");const ModuleWarning = require("./ModuleWarning");const createHash = require("./util/createHash");const contextify = require("./util/identifier").contextify;/** @typedef {import("./util/createHash").Hash} Hash */const asString = buf => {	if (Buffer.isBuffer(buf)) {		return buf.toString("utf-8");	}	return buf;};const asBuffer = str => {	if (!Buffer.isBuffer(str)) {		return Buffer.from(str, "utf-8");	}	return str;};class NonErrorEmittedError extends WebpackError {	constructor(error) {		super();		this.name = "NonErrorEmittedError";		this.message = "(Emitted value instead of an instance of Error) " + error;		Error.captureStackTrace(this, this.constructor);	}}/** * @typedef {Object} CachedSourceEntry * @property {TODO} source the generated source * @property {string} hash the hash value */class NormalModule extends Module {	constructor({		type,		request,		userRequest,		rawRequest,		loaders,		resource,		matchResource,		parser,		generator,		resolveOptions	}) {		super(type, getContext(resource));		// Info from Factory		this.request = request;		this.userRequest = userRequest;		this.rawRequest = rawRequest;		this.binary = type.startsWith("webassembly");		this.parser = parser;		this.generator = generator;		this.resource = resource;		this.matchResource = matchResource;		this.loaders = loaders;		if (resolveOptions !== undefined) this.resolveOptions = resolveOptions;		// Info from Build		this.error = null;		this._source = null;		this._sourceSize = null;		this._buildHash = "";		this.buildTimestamp = undefined;		/** @private @type {Map<string, CachedSourceEntry>} */		this._cachedSources = new Map();		// Options for the NormalModule set by plugins		// TODO refactor this -> options object filled from Factory		this.useSourceMap = false;		this.lineToLine = false;		// Cache		this._lastSuccessfulBuildMeta = {};	}	identifier() {		return this.request;	}	readableIdentifier(requestShortener) {		return requestShortener.shorten(this.userRequest);	}	libIdent(options) {		return contextify(options.context, this.userRequest);	}	nameForCondition() {		const resource = this.matchResource || this.resource;		const idx = resource.indexOf("?");		if (idx >= 0) return resource.substr(0, idx);		return resource;	}	updateCacheModule(module) {		this.type = module.type;		this.request = module.request;		this.userRequest = module.userRequest;		this.rawRequest = module.rawRequest;		this.parser = module.parser;		this.generator = module.generator;		this.resource = module.resource;		this.matchResource = module.matchResource;		this.loaders = module.loaders;		this.resolveOptions = module.resolveOptions;	}	createSourceForAsset(name, content, sourceMap) {		if (!sourceMap) {			return new RawSource(content);		}		if (typeof sourceMap === "string") {			return new OriginalSource(content, sourceMap);		}		return new SourceMapSource(content, name, sourceMap);	}	createLoaderContext(resolver, options, compilation, fs) {		const requestShortener = compilation.runtimeTemplate.requestShortener;		const getCurrentLoaderName = () => {			const currentLoader = this.getCurrentLoader(loaderContext);			if (!currentLoader) return "(not in loader scope)";			return requestShortener.shorten(currentLoader.loader);		};		const loaderContext = {			version: 2,			emitWarning: warning => {				if (!(warning instanceof Error)) {					warning = new NonErrorEmittedError(warning);				}				this.warnings.push(					new ModuleWarning(this, warning, {						from: getCurrentLoaderName()					})				);			},			emitError: error => {				if (!(error instanceof Error)) {					error = new NonErrorEmittedError(error);				}				this.errors.push(					new ModuleError(this, error, {						from: getCurrentLoaderName()					})				);			},			getLogger: name => {				const currentLoader = this.getCurrentLoader(loaderContext);				return compilation.getLogger(() =>					[currentLoader && currentLoader.loader, name, this.identifier()]						.filter(Boolean)						.join("|")				);			},			// TODO remove in webpack 5			exec: (code, filename) => {				// @ts-ignore Argument of type 'this' is not assignable to parameter of type 'Module'.				const module = new NativeModule(filename, this);				// @ts-ignore _nodeModulePaths is deprecated and undocumented Node.js API				module.paths = NativeModule._nodeModulePaths(this.context);				module.filename = filename;				module._compile(code, filename);				return module.exports;			},			resolve(context, request, callback) {				resolver.resolve({}, context, request, {}, callback);			},			getResolve(options) {				const child = options ? resolver.withOptions(options) : resolver;				return (context, request, callback) => {					if (callback) {						child.resolve({}, context, request, {}, callback);					} else {						return new Promise((resolve, reject) => {							child.resolve({}, context, request, {}, (err, result) => {								if (err) reject(err);								else resolve(result);							});						});					}				};			},			emitFile: (name, content, sourceMap, assetInfo) => {				if (!this.buildInfo.assets) {					this.buildInfo.assets = Object.create(null);					this.buildInfo.assetsInfo = new Map();				}				this.buildInfo.assets[name] = this.createSourceForAsset(					name,					content,					sourceMap				);				this.buildInfo.assetsInfo.set(name, assetInfo);			},			rootContext: options.context,			webpack: true,			sourceMap: !!this.useSourceMap,			mode: options.mode || "production",			_module: this,			_compilation: compilation,			_compiler: compilation.compiler,			fs: fs		};		compilation.hooks.normalModuleLoader.call(loaderContext, this);		if (options.loader) {			Object.assign(loaderContext, options.loader);		}		return loaderContext;	}	getCurrentLoader(loaderContext, index = loaderContext.loaderIndex) {		if (			this.loaders &&			this.loaders.length &&			index < this.loaders.length &&			index >= 0 &&			this.loaders[index]		) {			return this.loaders[index];		}		return null;	}	createSource(source, resourceBuffer, sourceMap) {		// if there is no identifier return raw source		if (!this.identifier) {			return new RawSource(source);		}		// from here on we assume we have an identifier		const identifier = this.identifier();		if (this.lineToLine && resourceBuffer) {			return new LineToLineMappedSource(				source,				identifier,				asString(resourceBuffer)			);		}		if (this.useSourceMap && sourceMap) {			return new SourceMapSource(source, identifier, sourceMap);		}		if (Buffer.isBuffer(source)) {			// @ts-ignore			// TODO We need to fix @types/webpack-sources to allow RawSource to take a Buffer | string			return new RawSource(source);		}		return new OriginalSource(source, identifier);	}	doBuild(options, compilation, resolver, fs, callback) {		const loaderContext = this.createLoaderContext(			resolver,			options,			compilation,			fs		);		runLoaders(			{				resource: this.resource,				loaders: this.loaders,				context: loaderContext,				readResource: fs.readFile.bind(fs)			},			(err, result) => {				if (result) {					this.buildInfo.cacheable = result.cacheable;					this.buildInfo.fileDependencies = new Set(result.fileDependencies);					this.buildInfo.contextDependencies = new Set(						result.contextDependencies					);				}				if (err) {					if (!(err instanceof Error)) {						err = new NonErrorEmittedError(err);					}					const currentLoader = this.getCurrentLoader(loaderContext);					const error = new ModuleBuildError(this, err, {						from:							currentLoader &&							compilation.runtimeTemplate.requestShortener.shorten(								currentLoader.loader							)					});					return callback(error);				}				const resourceBuffer = result.resourceBuffer;				const source = result.result[0];				const sourceMap = result.result.length >= 1 ? result.result[1] : null;				const extraInfo = result.result.length >= 2 ? result.result[2] : null;				if (!Buffer.isBuffer(source) && typeof source !== "string") {					const currentLoader = this.getCurrentLoader(loaderContext, 0);					const err = new Error(						`Final loader (${							currentLoader								? compilation.runtimeTemplate.requestShortener.shorten(										currentLoader.loader								  )								: "unknown"						}) didn't return a Buffer or String`					);					const error = new ModuleBuildError(this, err);					return callback(error);				}				this._source = this.createSource(					this.binary ? asBuffer(source) : asString(source),					resourceBuffer,					sourceMap				);				this._sourceSize = null;				this._ast =					typeof extraInfo === "object" &&					extraInfo !== null &&					extraInfo.webpackAST !== undefined						? extraInfo.webpackAST						: null;				return callback();			}		);	}	markModuleAsErrored(error) {		// Restore build meta from successful build to keep importing state		this.buildMeta = Object.assign({}, this._lastSuccessfulBuildMeta);		this.error = error;		this.errors.push(this.error);		this._source = new RawSource(			"throw new Error(" + JSON.stringify(this.error.message) + ");"		);		this._sourceSize = null;		this._ast = null;	}	applyNoParseRule(rule, content) {		// must start with "rule" if rule is a string		if (typeof rule === "string") {			return content.indexOf(rule) === 0;		}		if (typeof rule === "function") {			return rule(content);		}		// we assume rule is a regexp		return rule.test(content);	}	// check if module should not be parsed	// returns "true" if the module should !not! be parsed	// returns "false" if the module !must! be parsed	shouldPreventParsing(noParseRule, request) {		// if no noParseRule exists, return false		// the module !must! be parsed.		if (!noParseRule) {			return false;		}		// we only have one rule to check		if (!Array.isArray(noParseRule)) {			// returns "true" if the module is !not! to be parsed			return this.applyNoParseRule(noParseRule, request);		}		for (let i = 0; i < noParseRule.length; i++) {			const rule = noParseRule[i];			// early exit on first truthy match			// this module is !not! to be parsed			if (this.applyNoParseRule(rule, request)) {				return true;			}		}		// no match found, so this module !should! be parsed		return false;	}	_initBuildHash(compilation) {		const hash = createHash(compilation.outputOptions.hashFunction);		if (this._source) {			hash.update("source");			this._source.updateHash(hash);		}		hash.update("meta");		hash.update(JSON.stringify(this.buildMeta));		this._buildHash = /** @type {string} */ (hash.digest("hex"));	}	build(options, compilation, resolver, fs, callback) {		this.buildTimestamp = Date.now();		this.built = true;		this._source = null;		this._sourceSize = null;		this._ast = null;		this._buildHash = "";		this.error = null;		this.errors.length = 0;		this.warnings.length = 0;		this.buildMeta = {};		this.buildInfo = {			cacheable: false,			fileDependencies: new Set(),			contextDependencies: new Set(),			assets: undefined,			assetsInfo: undefined		};		return this.doBuild(options, compilation, resolver, fs, err => {			this._cachedSources.clear();			// if we have an error mark module as failed and exit			if (err) {				this.markModuleAsErrored(err);				this._initBuildHash(compilation);				return callback();			}			// check if this module should !not! be parsed.			// if so, exit here;			const noParseRule = options.module && options.module.noParse;			if (this.shouldPreventParsing(noParseRule, this.request)) {				this._initBuildHash(compilation);				return callback();			}			const handleParseError = e => {				const source = this._source.source();				const loaders = this.loaders.map(item =>					contextify(options.context, item.loader)				);				const error = new ModuleParseError(this, source, e, loaders);				this.markModuleAsErrored(error);				this._initBuildHash(compilation);				return callback();			};			const handleParseResult = result => {				this._lastSuccessfulBuildMeta = this.buildMeta;				this._initBuildHash(compilation);				return callback();			};			try {				const result = this.parser.parse(					this._ast || this._source.source(),					{						current: this,						module: this,						compilation: compilation,						options: options					},					(err, result) => {						if (err) {							handleParseError(err);						} else {							handleParseResult(result);						}					}				);				if (result !== undefined) {					// parse is sync					handleParseResult(result);				}			} catch (e) {				handleParseError(e);			}		});	}	getHashDigest(dependencyTemplates) {		// TODO webpack 5 refactor		let dtHash = dependencyTemplates.get("hash");		return `${this.hash}-${dtHash}`;	}	source(dependencyTemplates, runtimeTemplate, type = "javascript") {		const hashDigest = this.getHashDigest(dependencyTemplates);		const cacheEntry = this._cachedSources.get(type);		if (cacheEntry !== undefined && cacheEntry.hash === hashDigest) {			// We can reuse the cached source			return cacheEntry.source;		}		const source = this.generator.generate(			this,			dependencyTemplates,			runtimeTemplate,			type		);		const cachedSource = new CachedSource(source);		this._cachedSources.set(type, {			source: cachedSource,			hash: hashDigest		});		return cachedSource;	}	originalSource() {		return this._source;	}	needRebuild(fileTimestamps, contextTimestamps) {		// always try to rebuild in case of an error		if (this.error) return true;		// always rebuild when module is not cacheable		if (!this.buildInfo.cacheable) return true;		// Check timestamps of all dependencies		// Missing timestamp -> need rebuild		// Timestamp bigger than buildTimestamp -> need rebuild		for (const file of this.buildInfo.fileDependencies) {			const timestamp = fileTimestamps.get(file);			if (!timestamp) return true;			if (timestamp >= this.buildTimestamp) return true;		}		for (const file of this.buildInfo.contextDependencies) {			const timestamp = contextTimestamps.get(file);			if (!timestamp) return true;			if (timestamp >= this.buildTimestamp) return true;		}		// elsewise -> no rebuild needed		return false;	}	size() {		if (this._sourceSize === null) {			this._sourceSize = this._source ? this._source.size() : -1;		}		return this._sourceSize;	}	/**	 * @param {Hash} hash the hash used to track dependencies	 * @returns {void}	 */	updateHash(hash) {		hash.update(this._buildHash);		super.updateHash(hash);	}}module.exports = NormalModule;
 |