From de98e0b232509d5f40c135d540a70e415272ff85 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 3 May 2017 15:35:00 +0200 Subject: node_modules --- node_modules/webpack/lib/NormalModule.js | 524 +++++++++++++++++++++++++++++++ 1 file changed, 524 insertions(+) create mode 100644 node_modules/webpack/lib/NormalModule.js (limited to 'node_modules/webpack/lib/NormalModule.js') diff --git a/node_modules/webpack/lib/NormalModule.js b/node_modules/webpack/lib/NormalModule.js new file mode 100644 index 000000000..949bfef87 --- /dev/null +++ b/node_modules/webpack/lib/NormalModule.js @@ -0,0 +1,524 @@ +/* + 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) { + let rp = path.relative(context, r); + if(path.sep === "\\") + rp = rp.replace(/\\/g, "/"); + if(rp.indexOf("../") !== 0) + rp = "./" + rp; + return rp; + }).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); + } +} + +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; + } + // 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 = new Date().getTime(); + 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() { + const hash = crypto.createHash("md5"); + this.updateHash(hash); + 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.map((variable) => this.sourceVariables( + variable, availableVars, dependencyTemplates, outputOptions, requestShortener)) + .filter(Boolean); + + /** + * 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) => variableChunk.map(variable => variable.name)) + .map(names => this.variableInjectionFunctionWrapperStartCode(names)); + + // and all the ends + const functionWrapperEnds = injectionVariableChunks.map((variableChunk) => variableChunk.map(variable => variable.expression)) + .map(expressions => this.variableInjectionFunctionWrapperEndCode(expressions, 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(); + 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; -- cgit v1.2.3