/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ var path = require("path"); var Module = require("./Module"); var SourceMapSource = require("webpack-sources").SourceMapSource; var OriginalSource = require("webpack-sources").OriginalSource; var RawSource = require("webpack-sources").RawSource; var ReplaceSource = require("webpack-sources").ReplaceSource; var CachedSource = require("webpack-sources").CachedSource; var LineToLineMappedSource = require("webpack-sources").LineToLineMappedSource; var ModuleParseError = require("./ModuleParseError"); var ModuleBuildError = require("./ModuleBuildError"); var ModuleError = require("./ModuleError"); var ModuleWarning = require("./ModuleWarning"); var runLoaders = require("loader-runner").runLoaders; var getContext = require("loader-runner").getContext; function asString(buf) { if(Buffer.isBuffer(buf)) { return buf.toString("utf-8"); } return buf; } function NormalModule(request, userRequest, rawRequest, loaders, resource, parser) { Module.call(this); 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; } module.exports = NormalModule; NormalModule.prototype = Object.create(Module.prototype); NormalModule.prototype.constructor = NormalModule; NormalModule.prototype.identifier = function() { return this.request; }; NormalModule.prototype.readableIdentifier = function(requestShortener) { return requestShortener.shorten(this.userRequest); }; function contextify(options, request) { return request.split("!").map(function(r) { var rp = path.relative(options.context, r); if(path.sep === "\\") rp = rp.replace(/\\/g, "/"); if(rp.indexOf("../") !== 0) rp = "./" + rp; return rp; }).join("!"); } NormalModule.prototype.libIdent = function(options) { return contextify(options, this.userRequest); }; NormalModule.prototype.nameForCondition = function() { var idx = this.resource.indexOf("?"); if(idx >= 0) return this.resource.substr(0, idx); return this.resource; }; NormalModule.prototype.doBuild = function doBuild(options, compilation, resolver, fs, callback) { this.cacheable = false; var module = this; var loaderContext = { version: 2, emitWarning: function(warning) { module.warnings.push(new ModuleWarning(module, warning)); }, emitError: function(error) { module.errors.push(new ModuleError(module, error)); }, exec: function(code, filename) { var Module = require("module"); var m = new Module(filename, module); m.paths = Module._nodeModulePaths(module.context); m.filename = filename; m._compile(code, filename); return m.exports; }, resolve: function(context, request, callback) { resolver.resolve({}, context, request, callback); }, resolveSync: function(context, request) { return resolver.resolveSync({}, context, request); }, options: options }; loaderContext.webpack = true; loaderContext.sourceMap = !!this.useSourceMap; loaderContext.emitFile = function(name, content, sourceMap) { if(typeof sourceMap === "string") { this.assets[name] = new OriginalSource(content, sourceMap); } else if(sourceMap) { this.assets[name] = new SourceMapSource(content, name, sourceMap); } else { this.assets[name] = new RawSource(content); } }.bind(this); loaderContext._module = this; loaderContext._compilation = compilation; loaderContext._compiler = compilation.compiler; loaderContext.fs = fs; compilation.applyPlugins("normal-module-loader", loaderContext, this); if(options.loader) for(var key in options.loader) loaderContext[key] = options.loader[key]; runLoaders({ resource: this.resource, loaders: this.loaders, context: loaderContext, readResource: fs.readFile.bind(fs) }, function(err, result) { if(result) { module.cacheable = result.cacheable; module.fileDependencies = result.fileDependencies; module.contextDependencies = result.contextDependencies; } if(err) { return callback(module.error = new ModuleBuildError(module, err)); } var resourceBuffer = result.resourceBuffer; var source = result.result[0]; var sourceMap = result.result[1]; if(!Buffer.isBuffer(source) && typeof source !== "string") { return callback(module.error = new ModuleBuildError(module, new Error("Final loader didn't return a Buffer or String"))); } source = asString(source); if(module.identifier && module.lineToLine && resourceBuffer) { module._source = new LineToLineMappedSource(source, module.identifier(), asString(resourceBuffer)); } else if(module.identifier && module.useSourceMap && sourceMap) { module._source = new SourceMapSource(source, module.identifier(), sourceMap); } else if(module.identifier) { module._source = new OriginalSource(source, module.identifier()); } else { module._source = new RawSource(source); } return callback(); }); }; NormalModule.prototype.disconnect = function disconnect() { this.built = false; Module.prototype.disconnect.call(this); }; NormalModule.prototype.build = function build(options, compilation, resolver, fs, callback) { var _this = this; _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, function(err) { _this.dependencies.length = 0; _this.variables.length = 0; _this.blocks.length = 0; _this._cachedSource = null; if(err) return setError(err); if(options.module && options.module.noParse) { var testRegExp = function testRegExp(regExp) { return typeof regExp === "string" ? _this.request.indexOf(regExp) === 0 : regExp.test(_this.request); }; if(Array.isArray(options.module.noParse)) { if(options.module.noParse.some(testRegExp, _this)) return callback(); } else if(testRegExp.call(_this, options.module.noParse)) { return callback(); } } try { _this.parser.parse(_this._source.source(), { current: _this, module: _this, compilation: compilation, options: options }); } catch(e) { var source = _this._source.source(); return setError(_this.error = new ModuleParseError(_this, source, e)); } return callback(); }); function setError(err) { _this.meta = null; if(_this.error) { _this.errors.push(_this.error); _this._source = new RawSource("throw new Error(" + JSON.stringify(_this.error.message) + ");"); } else { _this._source = new RawSource("throw new Error('Module build failed');"); } callback(); } }; NormalModule.prototype.source = function(dependencyTemplates, outputOptions, requestShortener) { var hash = require("crypto").createHash("md5"); this.updateHash(hash); hash = hash.digest("hex"); if(this._cachedSource && this._cachedSource.hash === hash) { return this._cachedSource.source; } var _source = this._source; if(!_source) return new RawSource("throw new Error('No source available');"); var source = new ReplaceSource(_source); this._cachedSource = { source: source, hash: hash }; var topLevelBlock = this; function doDep(dep) { var template = dependencyTemplates.get(dep.constructor); if(!template) throw new Error("No template for dependency: " + dep.constructor.name); template.apply(dep, source, outputOptions, requestShortener, dependencyTemplates); } function doVariable(availableVars, vars, variable) { var name = variable.name; var expr = variable.expressionSource(dependencyTemplates, outputOptions, requestShortener); function isEqual(v) { return v.name === name && v.expression.source() === expr.source(); } if(availableVars.some(isEqual)) return; vars.push({ name: name, expression: expr }); } function doBlock(availableVars, block) { block.dependencies.forEach(doDep); var vars = []; if(block.variables.length > 0) { block.variables.forEach(doVariable.bind(null, availableVars, vars)); var varNames = []; var varExpressions = []; var varStartCode = ""; var varEndCode = ""; var emitFunction = function emitFunction() { if(varNames.length === 0) return; varStartCode += "/* WEBPACK VAR INJECTION */(function(" + varNames.join(", ") + ") {"; // exports === this in the topLevelBlock, but exports do compress better... varEndCode = (topLevelBlock === block ? "}.call(" + (topLevelBlock.exportsArgument || "exports") + ", " : "}.call(this, ") + varExpressions.map(function(e) { return e.source(); }).join(", ") + "))" + varEndCode; varNames.length = 0; varExpressions.length = 0; }; vars.forEach(function(v) { if(varNames.indexOf(v.name) >= 0) emitFunction(); varNames.push(v.name); varExpressions.push(v.expression); }); emitFunction(); var start = block.range ? block.range[0] : -10; var end = block.range ? block.range[1] : (_source.size() + 1); if(varStartCode) source.insert(start + 0.5, varStartCode); if(varEndCode) source.insert(end + 0.5, "\n/* WEBPACK VAR INJECTION */" + varEndCode); } block.blocks.forEach(doBlock.bind(null, availableVars.concat(vars))); } doBlock([], this); return new CachedSource(source); }; NormalModule.prototype.needRebuild = function needRebuild(fileTimestamps, contextTimestamps) { var timestamp = 0; this.fileDependencies.forEach(function(file) { var ts = fileTimestamps[file]; if(!ts) timestamp = Infinity; if(ts > timestamp) timestamp = ts; }); this.contextDependencies.forEach(function(context) { var ts = contextTimestamps[context]; if(!ts) timestamp = Infinity; if(ts > timestamp) timestamp = ts; }); return timestamp >= this.buildTimestamp; }; NormalModule.prototype.size = function() { return this._source ? this._source.size() : -1; }; NormalModule.prototype.updateHash = function(hash) { if(this._source) { hash.update("source"); this._source.updateHash(hash); } else hash.update("null"); hash.update("meta"); hash.update(JSON.stringify(this.meta)); Module.prototype.updateHash.call(this, hash); };