From bbff7403fbf46f9ad92240ac213df8d30ef31b64 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 20 Sep 2018 02:56:13 +0200 Subject: update packages --- node_modules/webpack/lib/NormalModule.js | 1098 +++++++++++++++--------------- 1 file changed, 542 insertions(+), 556 deletions(-) (limited to 'node_modules/webpack/lib/NormalModule.js') diff --git a/node_modules/webpack/lib/NormalModule.js b/node_modules/webpack/lib/NormalModule.js index c3288bf0d..fdabc3000 100644 --- a/node_modules/webpack/lib/NormalModule.js +++ b/node_modules/webpack/lib/NormalModule.js @@ -1,556 +1,542 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -const path = require("path"); -const NativeModule = require("module"); -const crypto = require("crypto"); - -const SourceMapSource = require("webpack-sources").SourceMapSource; -const OriginalSource = require("webpack-sources").OriginalSource; -const RawSource = require("webpack-sources").RawSource; -const ReplaceSource = require("webpack-sources").ReplaceSource; -const CachedSource = require("webpack-sources").CachedSource; -const LineToLineMappedSource = require("webpack-sources").LineToLineMappedSource; - -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 runLoaders = require("loader-runner").runLoaders; -const getContext = require("loader-runner").getContext; - -function asString(buf) { - if(Buffer.isBuffer(buf)) { - return buf.toString("utf-8"); - } - return buf; -} - -function contextify(context, request) { - return request.split("!").map(function(r) { - const splitPath = r.split("?"); - splitPath[0] = path.relative(context, splitPath[0]); - if(path.sep === "\\") - splitPath[0] = splitPath[0].replace(/\\/g, "/"); - if(splitPath[0].indexOf("../") !== 0) - splitPath[0] = "./" + splitPath[0]; - return splitPath.join("?"); - }).join("!"); -} - -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); - } -} - -const dependencyTemplatesHashMap = new WeakMap(); - -class NormalModule extends Module { - - constructor(request, userRequest, rawRequest, loaders, resource, parser) { - super(); - this.request = request; - this.userRequest = userRequest; - this.rawRequest = rawRequest; - this.parser = parser; - this.resource = resource; - this.context = getContext(resource); - this.loaders = loaders; - this.fileDependencies = []; - this.contextDependencies = []; - this.warnings = []; - this.errors = []; - this.error = null; - this._source = null; - this.assets = {}; - this.built = false; - this._cachedSource = null; - } - - identifier() { - return this.request; - } - - readableIdentifier(requestShortener) { - return requestShortener.shorten(this.userRequest); - } - - libIdent(options) { - return contextify(options.context, this.userRequest); - } - - nameForCondition() { - const idx = this.resource.indexOf("?"); - if(idx >= 0) return this.resource.substr(0, idx); - return this.resource; - } - - 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 loaderContext = { - version: 2, - emitWarning: (warning) => { - if(!(warning instanceof Error)) - warning = new NonErrorEmittedError(warning); - this.warnings.push(new ModuleWarning(this, warning)); - }, - emitError: (error) => { - if(!(error instanceof Error)) - error = new NonErrorEmittedError(error); - this.errors.push(new ModuleError(this, error)); - }, - exec: (code, filename) => { - const module = new NativeModule(filename, this); - 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); - }, - resolveSync(context, request) { - return resolver.resolveSync({}, context, request); - }, - emitFile: (name, content, sourceMap) => { - this.assets[name] = this.createSourceForAsset(name, content, sourceMap); - }, - options: options, - webpack: true, - sourceMap: !!this.useSourceMap, - _module: this, - _compilation: compilation, - _compiler: compilation.compiler, - fs: fs, - }; - - compilation.applyPlugins("normal-module-loader", loaderContext, this); - if(options.loader) - Object.assign(loaderContext, options.loader); - - return loaderContext; - } - - 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); - } - - return new OriginalSource(source, identifier); - } - - doBuild(options, compilation, resolver, fs, callback) { - this.cacheable = false; - 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.cacheable = result.cacheable; - this.fileDependencies = result.fileDependencies; - this.contextDependencies = result.contextDependencies; - } - - if(err) { - const error = new ModuleBuildError(this, err); - return callback(error); - } - - const resourceBuffer = result.resourceBuffer; - const source = result.result[0]; - const sourceMap = result.result[1]; - - if(!Buffer.isBuffer(source) && typeof source !== "string") { - const error = new ModuleBuildError(this, new Error("Final loader didn't return a Buffer or String")); - return callback(error); - } - - this._source = this.createSource(asString(source), resourceBuffer, sourceMap); - return callback(); - }); - } - - disconnect() { - this.built = false; - super.disconnect(); - } - - markModuleAsErrored(error) { - this.meta = null; - this.error = error; - this.errors.push(this.error); - this._source = new RawSource("throw new Error(" + JSON.stringify(this.error.message) + ");"); - } - - 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; - } - - build(options, compilation, resolver, fs, callback) { - this.buildTimestamp = Date.now(); - this.built = true; - this._source = null; - this.error = null; - this.errors.length = 0; - this.warnings.length = 0; - this.meta = {}; - - return this.doBuild(options, compilation, resolver, fs, (err) => { - this.dependencies.length = 0; - this.variables.length = 0; - this.blocks.length = 0; - this._cachedSource = null; - - // if we have an error mark module as failed and exit - if(err) { - this.markModuleAsErrored(err); - 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)) { - return callback(); - } - - try { - this.parser.parse(this._source.source(), { - current: this, - module: this, - compilation: compilation, - options: options - }); - } catch(e) { - const source = this._source.source(); - const error = new ModuleParseError(this, source, e); - this.markModuleAsErrored(error); - return callback(); - } - return callback(); - }); - } - - getHashDigest(dependencyTemplates) { - let dtHash = dependencyTemplatesHashMap.get("hash"); - const hash = crypto.createHash("md5"); - this.updateHash(hash); - hash.update(`${dtHash}`); - return hash.digest("hex"); - } - - sourceDependency(dependency, dependencyTemplates, source, outputOptions, requestShortener) { - const template = dependencyTemplates.get(dependency.constructor); - if(!template) throw new Error("No template for dependency: " + dependency.constructor.name); - template.apply(dependency, source, outputOptions, requestShortener, dependencyTemplates); - } - - sourceVariables(variable, availableVars, dependencyTemplates, outputOptions, requestShortener) { - const name = variable.name; - const expr = variable.expressionSource(dependencyTemplates, outputOptions, requestShortener); - - if(availableVars.some(v => v.name === name && v.expression.source() === expr.source())) { - return; - } - return { - name: name, - expression: expr - }; - } - - /* - * creates the start part of a IIFE around the module to inject a variable name - * (function(...){ <- this part - * }.call(...)) - */ - variableInjectionFunctionWrapperStartCode(varNames) { - const args = varNames.join(", "); - return `/* WEBPACK VAR INJECTION */(function(${args}) {`; - } - - contextArgument(block) { - if(this === block) { - return this.exportsArgument || "exports"; - } - return "this"; - } - - /* - * creates the end part of a IIFE around the module to inject a variable name - * (function(...){ - * }.call(...)) <- this part - */ - variableInjectionFunctionWrapperEndCode(varExpressions, block) { - const firstParam = this.contextArgument(block); - const furtherParams = varExpressions.map(e => e.source()).join(", "); - return `}.call(${firstParam}, ${furtherParams}))`; - } - - splitVariablesInUniqueNamedChunks(vars) { - const startState = [ - [] - ]; - return vars.reduce((chunks, variable) => { - const current = chunks[chunks.length - 1]; - // check if variable with same name exists already - // if so create a new chunk of variables. - const variableNameAlreadyExists = current.some(v => v.name === variable.name); - - if(variableNameAlreadyExists) { - // start new chunk with current variable - chunks.push([variable]); - } else { - // else add it to current chunk - current.push(variable); - } - return chunks; - }, startState); - } - - sourceBlock(block, availableVars, dependencyTemplates, source, outputOptions, requestShortener) { - block.dependencies.forEach((dependency) => this.sourceDependency( - dependency, dependencyTemplates, source, outputOptions, requestShortener)); - - /** - * Get the variables of all blocks that we need to inject. - * These will contain the variable name and its expression. - * The name will be added as a paramter in a IIFE the expression as its value. - */ - const vars = block.variables.reduce((result, value) => { - const variable = this.sourceVariables( - value, availableVars, dependencyTemplates, outputOptions, requestShortener); - - if(variable) { - result.push(variable); - } - - return result; - }, []); - - /** - * if we actually have variables - * this is important as how #splitVariablesInUniqueNamedChunks works - * it will always return an array in an array which would lead to a IIFE wrapper around - * a module if we do this with an empty vars array. - */ - if(vars.length > 0) { - /** - * Split all variables up into chunks of unique names. - * e.g. imagine you have the following variable names that need to be injected: - * [foo, bar, baz, foo, some, more] - * we can not inject "foo" twice, therefore we just make two IIFEs like so: - * (function(foo, bar, baz){ - * (function(foo, some, more){ - * ... - * }(...)); - * }(...)); - * - * "splitVariablesInUniqueNamedChunks" splits the variables shown above up to this: - * [[foo, bar, baz], [foo, some, more]] - */ - const injectionVariableChunks = this.splitVariablesInUniqueNamedChunks(vars); - - // create all the beginnings of IIFEs - const functionWrapperStarts = injectionVariableChunks.map((variableChunk) => { - return this.variableInjectionFunctionWrapperStartCode( - variableChunk.map(variable => variable.name) - ); - }); - - // and all the ends - const functionWrapperEnds = injectionVariableChunks.map((variableChunk) => { - return this.variableInjectionFunctionWrapperEndCode( - variableChunk.map(variable => variable.expression), block - ); - }); - - // join them to one big string - const varStartCode = functionWrapperStarts.join(""); - - // reverse the ends first before joining them, as the last added must be the inner most - const varEndCode = functionWrapperEnds.reverse().join(""); - - // if we have anything, add it to the source - if(varStartCode && varEndCode) { - const start = block.range ? block.range[0] : -10; - const end = block.range ? block.range[1] : (this._source.size() + 1); - source.insert(start + 0.5, varStartCode); - source.insert(end + 0.5, "\n/* WEBPACK VAR INJECTION */" + varEndCode); - } - } - - block.blocks.forEach((block) => - this.sourceBlock( - block, - availableVars.concat(vars), - dependencyTemplates, - source, - outputOptions, - requestShortener - ) - ); - } - - source(dependencyTemplates, outputOptions, requestShortener) { - const hashDigest = this.getHashDigest(dependencyTemplates); - if(this._cachedSource && this._cachedSource.hash === hashDigest) { - return this._cachedSource.source; - } - - if(!this._source) { - return new RawSource("throw new Error('No source available');"); - } - - const source = new ReplaceSource(this._source); - this._cachedSource = { - source: source, - hash: hashDigest - }; - - this.sourceBlock(this, [], dependencyTemplates, source, outputOptions, requestShortener); - return new CachedSource(source); - } - - originalSource() { - return this._source; - } - - getHighestTimestamp(keys, timestampsByKey) { - let highestTimestamp = 0; - for(let i = 0; i < keys.length; i++) { - const key = keys[i]; - const timestamp = timestampsByKey[key]; - // if there is no timestamp yet, early return with Infinity - if(!timestamp) return Infinity; - highestTimestamp = Math.max(highestTimestamp, timestamp); - } - return highestTimestamp; - } - - needRebuild(fileTimestamps, contextTimestamps) { - const highestFileDepTimestamp = this.getHighestTimestamp( - this.fileDependencies, fileTimestamps); - // if the hightest is Infinity, we need a rebuild - // exit early here. - if(highestFileDepTimestamp === Infinity) { - return true; - } - - const highestContextDepTimestamp = this.getHighestTimestamp( - this.contextDependencies, contextTimestamps); - - // Again if the hightest is Infinity, we need a rebuild - // exit early here. - if(highestContextDepTimestamp === Infinity) { - return true; - } - - // else take the highest of file and context timestamps and compare - // to last build timestamp - return Math.max(highestContextDepTimestamp, highestFileDepTimestamp) >= this.buildTimestamp; - } - - size() { - return this._source ? this._source.size() : -1; - } - - updateHashWithSource(hash) { - if(!this._source) { - hash.update("null"); - return; - } - hash.update("source"); - this._source.updateHash(hash); - } - - updateHashWithMeta(hash) { - hash.update("meta"); - hash.update(JSON.stringify(this.meta)); - } - - updateHash(hash) { - this.updateHashWithSource(hash); - this.updateHashWithMeta(hash); - super.updateHash(hash); - } - -} - -module.exports = NormalModule; +/* + 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._buildHash = ""; + this.buildTimestamp = undefined; + /** @private @type {Map} */ + 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 loaderContext = { + version: 2, + emitWarning: warning => { + if (!(warning instanceof Error)) { + warning = new NonErrorEmittedError(warning); + } + const currentLoader = this.getCurrentLoader(loaderContext); + this.warnings.push( + new ModuleWarning(this, warning, { + from: requestShortener.shorten(currentLoader.loader) + }) + ); + }, + emitError: error => { + if (!(error instanceof Error)) { + error = new NonErrorEmittedError(error); + } + const currentLoader = this.getCurrentLoader(loaderContext); + this.errors.push( + new ModuleError(this, error, { + from: requestShortener.shorten(currentLoader.loader) + }) + ); + }, + // 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); + }, + emitFile: (name, content, sourceMap) => { + if (!this.buildInfo.assets) { + this.buildInfo.assets = Object.create(null); + } + this.buildInfo.assets[name] = this.createSourceForAsset( + name, + content, + sourceMap + ); + }, + rootContext: options.context, + webpack: true, + sourceMap: !!this.useSourceMap, + _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._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._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 = hash.digest("hex"); + } + + build(options, compilation, resolver, fs, callback) { + this.buildTimestamp = Date.now(); + this.built = true; + this._source = 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() + }; + + 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 error = new ModuleParseError(this, source, e); + 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() { + return this._source ? this._source.size() : -1; + } + + /** + * @param {Hash} hash the hash used to track dependencies + * @returns {void} + */ + updateHash(hash) { + hash.update(this._buildHash); + super.updateHash(hash); + } +} + +module.exports = NormalModule; -- cgit v1.2.3