<commit_before>/*
	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(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);
};
<commit_msg>fix(nmf): Fix exports for var injection to include free glob exports or arguments<commit_after>/*
	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(module.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);
};
<|endoftext|>


<commit_before>/*
	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 {
	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 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;
};

const contextify = (context, request) => {
	return request
		.split("!")
		.map(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);
	}
}

class NormalModule extends Module {
	constructor({
		type,
		request,
		userRequest,
		rawRequest,
		loaders,
		resource,
		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.loaders = loaders;
		if (resolveOptions !== undefined) this.resolveOptions = resolveOptions;

		// Info from Build
		this.error = null;
		this._source = null;
		this.buildTimestamp = undefined;
		this._cachedSource = undefined;
		this._cachedSourceHash = undefined;

		// 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 idx = this.resource.indexOf("?");
		if (idx >= 0) return this.resource.substr(0, idx);
		return this.resource;
	}

	updateCacheModule(module) {
		this.userRequest = module.userRequest;
		this.parser = module.parser;
		this.generator = module.generator;
		this.resource = module.resource;
		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 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);
				// @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;
	}

	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)) {
			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) {
					const error = new ModuleBuildError(this, err);
					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 error = new ModuleBuildError(
						this,
						new Error("Final loader didn't return a Buffer or String")
					);
					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;
	}

	build(options, compilation, resolver, fs, callback) {
		this.buildTimestamp = Date.now();
		this.built = true;
		this._source = null;
		this._ast = null;
		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._cachedSource = undefined;
			this._cachedSourceHash = undefined;

			// 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();
			}

			const handleParseError = e => {
				const source = this._source.source();
				const error = new ModuleParseError(this, source, e);
				this.markModuleAsErrored(error);
				return callback();
			};

			const handleParseResult = result => {
				this._lastSuccessfulBuildMeta = this.buildMeta;
				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) {
		let dtHash = dependencyTemplates.get("hash");
		return `${this.hash}-${dtHash}`;
	}

	source(dependencyTemplates, runtimeTemplate) {
		const hashDigest = this.getHashDigest(dependencyTemplates);
		if (this._cachedSourceHash === hashDigest) {
			// We can reuse the cached source
			return this._cachedSource;
		}

		const source = this.generator.generate(
			this,
			dependencyTemplates,
			runtimeTemplate
		);

		const cachedSource = new CachedSource(source);
		this._cachedSource = cachedSource;
		this._cachedSourceHash = 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;
	}

	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.buildMeta));
	}

	updateHash(hash) {
		this.updateHashWithSource(hash);
		this.updateHashWithMeta(hash);
		super.updateHash(hash);
	}
}

module.exports = NormalModule;
<commit_msg>Fixed all type errors!<commit_after>/*
	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 {
	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 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;
};

const contextify = (context, request) => {
	return request
		.split("!")
		.map(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);
	}
}

class NormalModule extends Module {
	constructor({
		type,
		request,
		userRequest,
		rawRequest,
		loaders,
		resource,
		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.loaders = loaders;
		if (resolveOptions !== undefined) this.resolveOptions = resolveOptions;

		// Info from Build
		this.error = null;
		this._source = null;
		this.buildTimestamp = undefined;
		this._cachedSource = undefined;
		this._cachedSourceHash = undefined;

		// 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 idx = this.resource.indexOf("?");
		if (idx >= 0) return this.resource.substr(0, idx);
		return this.resource;
	}

	updateCacheModule(module) {
		this.userRequest = module.userRequest;
		this.parser = module.parser;
		this.generator = module.generator;
		this.resource = module.resource;
		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 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) => {
				// @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;
	}

	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)) {
			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) {
					const error = new ModuleBuildError(this, err);
					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 error = new ModuleBuildError(
						this,
						new Error("Final loader didn't return a Buffer or String")
					);
					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;
	}

	build(options, compilation, resolver, fs, callback) {
		this.buildTimestamp = Date.now();
		this.built = true;
		this._source = null;
		this._ast = null;
		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._cachedSource = undefined;
			this._cachedSourceHash = undefined;

			// 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();
			}

			const handleParseError = e => {
				const source = this._source.source();
				const error = new ModuleParseError(this, source, e);
				this.markModuleAsErrored(error);
				return callback();
			};

			const handleParseResult = result => {
				this._lastSuccessfulBuildMeta = this.buildMeta;
				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) {
		let dtHash = dependencyTemplates.get("hash");
		return `${this.hash}-${dtHash}`;
	}

	source(dependencyTemplates, runtimeTemplate) {
		const hashDigest = this.getHashDigest(dependencyTemplates);
		if (this._cachedSourceHash === hashDigest) {
			// We can reuse the cached source
			return this._cachedSource;
		}

		const source = this.generator.generate(
			this,
			dependencyTemplates,
			runtimeTemplate
		);

		const cachedSource = new CachedSource(source);
		this._cachedSource = cachedSource;
		this._cachedSourceHash = 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;
	}

	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.buildMeta));
	}

	updateHash(hash) {
		this.updateHashWithSource(hash);
		this.updateHashWithMeta(hash);
		super.updateHash(hash);
	}
}

module.exports = NormalModule;
<|endoftext|>


<commit_before>/*
	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 Module = require("./Module");
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 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(options, request) {
	return request.split("!").map(function(r) {
		let rp = path.relative(options.context, r);
		if(path.sep === "\\")
			rp = rp.replace(/\\/g, "/");
		if(rp.indexOf("../") !== 0)
			rp = "./" + rp;
		return rp;
	}).join("!");
}

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, this.userRequest);
	}

	nameForCondition() {
		const idx = this.resource.indexOf("?");
		if(idx >= 0) return this.resource.substr(0, idx);
		return this.resource;
	}

	doBuild(options, compilation, resolver, fs, callback) {
		this.cacheable = false;
		const module = this;
		const loaderContext = {
			version: 2,
			emitWarning: (warning) => {
				module.warnings.push(new ModuleWarning(module, warning));
			},
			emitError: function(error) {
				module.errors.push(new ModuleError(module, error));
			},
			exec: function(code, filename) {
				const m = new NativeModule(filename, module);
				m.paths = NativeModule._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(const 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));
			}

			const resourceBuffer = result.resourceBuffer;
			let source = result.result[0];
			const 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();
		});
	}

	disconnect() {
		this.built = false;
		super.disconnect();
	}

	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 = {};

		const 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();
		};

		return this.doBuild(options, compilation, resolver, fs, (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) {
				const 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) {
				const source = this._source.source();
				return setError(this.error = new ModuleParseError(this, source, e));
			}
			return callback();
		});
	}

	source(dependencyTemplates, outputOptions, requestShortener) {
		let hash = require("crypto").createHash("md5");
		this.updateHash(hash);
		hash = hash.digest("hex");
		if(this._cachedSource && this._cachedSource.hash === hash) {
			return this._cachedSource.source;
		}
		const _source = this._source;
		if(!_source) return new RawSource("throw new Error('No source available');");
		const source = new ReplaceSource(_source);
		this._cachedSource = {
			source: source,
			hash: hash
		};
		const topLevelBlock = this;

		function doDep(dep) {
			const 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) {
			const name = variable.name;
			const 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);
			const vars = [];
			if(block.variables.length > 0) {
				block.variables.forEach(doVariable.bind(null, availableVars, vars));
				const varNames = [];
				const varExpressions = [];
				let varStartCode = "";
				let varEndCode = "";

				const 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();
				const start = block.range ? block.range[0] : -10;
				const 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);
	}

	needRebuild(fileTimestamps, contextTimestamps) {
		let timestamp = 0;
		this.fileDependencies.forEach(function(file) {
			const ts = fileTimestamps[file];
			if(!ts) timestamp = Infinity;
			if(ts > timestamp) timestamp = ts;
		});
		this.contextDependencies.forEach(function(context) {
			const ts = contextTimestamps[context];
			if(!ts) timestamp = Infinity;
			if(ts > timestamp) timestamp = ts;
		});
		return timestamp >= 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;
<commit_msg>rename setError to "markModuleAsErrored" and move it to prototype<commit_after>/*
	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 Module = require("./Module");
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 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(options, request) {
	return request.split("!").map(function(r) {
		let rp = path.relative(options.context, r);
		if(path.sep === "\\")
			rp = rp.replace(/\\/g, "/");
		if(rp.indexOf("../") !== 0)
			rp = "./" + rp;
		return rp;
	}).join("!");
}

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, this.userRequest);
	}

	nameForCondition() {
		const idx = this.resource.indexOf("?");
		if(idx >= 0) return this.resource.substr(0, idx);
		return this.resource;
	}

	doBuild(options, compilation, resolver, fs, callback) {
		this.cacheable = false;
		const module = this;
		const loaderContext = {
			version: 2,
			emitWarning: (warning) => {
				module.warnings.push(new ModuleWarning(module, warning));
			},
			emitError: function(error) {
				module.errors.push(new ModuleError(module, error));
			},
			exec: function(code, filename) {
				const m = new NativeModule(filename, module);
				m.paths = NativeModule._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(const 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));
			}

			const resourceBuffer = result.resourceBuffer;
			let source = result.result[0];
			const 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();
		});
	}

	disconnect() {
		this.built = false;
		super.disconnect();
	}

	markModuleAsErrored() {
		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');");
		}
	}

	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(err) {
				this.markModuleAsErrored();
				return callback();
			}
			if(options.module && options.module.noParse) {
				const 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) {
				const source = this._source.source();
				this.error = new ModuleParseError(this, source, e);
				this.markModuleAsErrored();
				return callback();
			}
			return callback();
		});
	}

	source(dependencyTemplates, outputOptions, requestShortener) {
		let hash = require("crypto").createHash("md5");
		this.updateHash(hash);
		hash = hash.digest("hex");
		if(this._cachedSource && this._cachedSource.hash === hash) {
			return this._cachedSource.source;
		}
		const _source = this._source;
		if(!_source) return new RawSource("throw new Error('No source available');");
		const source = new ReplaceSource(_source);
		this._cachedSource = {
			source: source,
			hash: hash
		};
		const topLevelBlock = this;

		function doDep(dep) {
			const 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) {
			const name = variable.name;
			const 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);
			const vars = [];
			if(block.variables.length > 0) {
				block.variables.forEach(doVariable.bind(null, availableVars, vars));
				const varNames = [];
				const varExpressions = [];
				let varStartCode = "";
				let varEndCode = "";

				const 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();
				const start = block.range ? block.range[0] : -10;
				const 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);
	}

	needRebuild(fileTimestamps, contextTimestamps) {
		let timestamp = 0;
		this.fileDependencies.forEach(function(file) {
			const ts = fileTimestamps[file];
			if(!ts) timestamp = Infinity;
			if(ts > timestamp) timestamp = ts;
		});
		this.contextDependencies.forEach(function(context) {
			const ts = contextTimestamps[context];
			if(!ts) timestamp = Infinity;
			if(ts > timestamp) timestamp = ts;
		});
		return timestamp >= 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;
<|endoftext|>


<commit_before>var Package = require('./Package');

describe("Package", function() {
  describe("constructor()", function() {

    it("should complain if no name is given", function() {
      expect(function() {
        new Package();
      }).toThrow();
      expect(function() {
        new Package(['dep1', 'dep2']);
      }).toThrow();
    });

    it("should add dependencies, if provided", function() {
      var package = new Package('packageName', ['dep1', 'dep2']);
      expect(package.dependencies).toEqual(['dep1', 'dep2']);
    });

    it("should create an empty dependencies collection if no dependencies are provided", function() {
      var package = new Package('packageName');
      expect(package.dependencies).toEqual([]);
    });

    it("should complain if dependencies is not an array", function() {
      expect(function() {
        new Package('somePackage', {});
      }).toThrow();
    });
  });

  describe("isPackage", function() {
    it("should return true for instances of Package", function() {
      var realPackage = new Package('realPackage', ['dep1']);
      expect(Package.isPackage(realPackage)).toEqual(true);
    });
    it("should return true for package-like objects", function() {
      var duckPackage = {
        name: 'duckPackage',
        module: {},
        dependencies: ['dep1']
      };
      expect(Package.isPackage(duckPackage)).toEqual(true);
    });
    it("should return false for non-package-like objects", function() {
      var nonPackage = {
        name: 'nonPackage',
        //module: {},
        dependencies: ['dep1']
      };
      expect(Package.isPackage(nonPackage)).toEqual(false);
    });
    it("should return false if passed a non-object", function() {
      var nonPackage = 'packageName';
      expect(Package.isPackage(nonPackage)).toEqual(false);
    });
  });

  describe("processor()", function() {

    it("should add processors defined by an object to the processors property", function() {
      var package = new Package('packageName');
      package.processor({ name: 'testProcessor'});
      expect(package.processors[0]).toEqual('testProcessor');
    });

    it("should add processors defined by a factory function to the processors property", function() {
      var package = new Package('packageName');
      package.processor(function testProcessor() {});
      expect(package.processors[0]).toEqual('testProcessor');
    });

    it("should complain if the processorFactory does not have a name", function() {
      var package = new Package('packageName');
      expect(function() {
        package.processor(function() {});
      }).toThrow();

      expect(function() {
        package.processor({ missing: 'name'});
      }).toThrow();
    });

    it("should use the first param as the name if it is a string", function() {
      var package = new Package('packageName');
      package.processor('testProcessor', { $process: function(docs) { } });
      expect(package.processors[0]).toEqual('testProcessor');
    });

    it("should add the processor to the DI module", function() {
      var package = new Package('packageName');
      var testProcessor = function testProcessor() {};
      package.processor(testProcessor);
      expect(package.module.testProcessor).toEqual(['factory', testProcessor]);
    });
  });

  describe("factory()", function() {
    it("should complain if the factory is not a function", function() {
      var package = new Package('packageName');
      expect(function() {
        package.factory({ name: 'bad factory'});
      }).toThrow();
    });

    it("should complain if the factory does not have a name", function() {
      var package = new Package('packageName');
      expect(function() {
        package.factory(function() {});
      }).toThrow();
    });

    it("should use the first param as the name if it is a string", function() {
      var package = new Package('packageName');

      var testServiceFactory = function() {};
      package.factory('testService', testServiceFactory);
      expect(package.module.testService).toEqual(['factory', testServiceFactory]);
    });

    it("should add the service to the DI module", function() {
      var package = new Package('packageName');
      var testService = function testService() {};
      package.factory(testService);
      expect(package.module.testService).toEqual(['factory', testService]);
    });
  });

  describe("type()", function() {
    it("should complain if the constructor is not a function", function() {
      var package = new Package('packageName');
      expect(function() {
        package.type({ name: 'bad type'});
      }).toThrow();
    });

    it("should complain if the constructor does not have a name", function() {
      var package = new Package('packageName');
      expect(function() {
        package.type(function() {});
      }).toThrow();
    });

    it("should use the first param as the name if it is a string", function() {
      var package = new Package('packageName');
      var TestService = function() {};
      package.type('testService', TestService);
      expect(package.module.testService).toEqual(['type', TestService]);
    });

    it("should add the service to the DI module", function() {
      var package = new Package('packageName');

      var TestService = function TestService() {};
      package.type(TestService);
      expect(package.module.TestService).toEqual(['type', TestService]);
    });
  });

  describe("config()", function() {
    it("should add the function to the configFns property", function() {
      var package = new Package('packageName');
      var testFn = function() {};
      package.config(testFn);
      expect(package.configFns[0]).toEqual(testFn);
    });

    it("should complain if configFn is not a function", function() {
      var package = new Package('packageName');
      expect(function() {
        package.config({ some: 'non-function'});
      }).toThrow();
    });
  });

  describe("eventHandlers()", function() {

    it("should add eventHandler name defined by a factory function to the handlers property", function() {
      var package = new Package('packageName');
      package.eventHandler('testEvent', function testHandler() {});
      expect(package.handlers['testEvent']).toEqual(['testHandler']);
    });

    it("should compute a unique name for the handler if it doesn't have one", function() {
      var package = new Package('packageName');
      package.eventHandler('testEvent', function() {});
      expect(package.handlers['testEvent'][0]).toEqual('packageName_testEvent_0');
      package.eventHandler('testEvent', function() {});
      expect(package.handlers['testEvent'][1]).toEqual('packageName_testEvent_1');
    });

    it("should complain if the eventType is not a string", function() {
      var package = new Package('packageName');
      expect(function() {
        package.eventHandler(function() {});
      }).toThrow();
    });

    it("should complain if the handler is not a function", function() {
      var package = new Package('packageName');
      expect(function() {
        package.eventHandler('testEvent', { name: 'bad handler'});
      }).toThrow();
    });

    it("should add the eventHandler to the DI module", function() {
      var package = new Package('packageName');
      var testHandler = function testHandler() {};
      package.eventHandler('testEvent', testHandler);
      expect(package.module.testHandler).toEqual(['factory', testHandler]);

      var anonymousHandler = function() {};
      package.eventHandler('testEvent', anonymousHandler);
      expect(package.module.packageName_testEvent_1).toEqual(['factory', anonymousHandler]);
    });
  });

});<commit_msg>style(Package): fix linting issues<commit_after>var Package = require('./Package');

describe('Package', function() {
  describe('constructor()', function() {

    it('should complain if no name is given', function() {
      expect(function() {
        new Package();
      }).toThrow();
      expect(function() {
        new Package(['dep1', 'dep2']);
      }).toThrow();
    });

    it('should add dependencies, if provided', function() {
      var package = new Package('packageName', ['dep1', 'dep2']);
      expect(package.dependencies).toEqual(['dep1', 'dep2']);
    });

    it('should create an empty dependencies collection if no dependencies are provided', function() {
      var package = new Package('packageName');
      expect(package.dependencies).toEqual([]);
    });

    it('should complain if dependencies is not an array', function() {
      expect(function() {
        new Package('somePackage', {});
      }).toThrow();
    });
  });

  describe('isPackage', function() {
    it('should return true for instances of Package', function() {
      var realPackage = new Package('realPackage', ['dep1']);
      expect(Package.isPackage(realPackage)).toEqual(true);
    });
    it('should return true for package-like objects', function() {
      var duckPackage = {
        name: 'duckPackage',
        module: {},
        dependencies: ['dep1']
      };
      expect(Package.isPackage(duckPackage)).toEqual(true);
    });
    it('should return false for non-package-like objects', function() {
      var nonPackage = {
        name: 'nonPackage',
        //module: {},
        dependencies: ['dep1']
      };
      expect(Package.isPackage(nonPackage)).toEqual(false);
    });
    it('should return false if passed a non-object', function() {
      var nonPackage = 'packageName';
      expect(Package.isPackage(nonPackage)).toEqual(false);
    });
  });

  describe('processor()', function() {

    it('should add processors defined by an object to the processors property', function() {
      var package = new Package('packageName');
      package.processor({ name: 'testProcessor'});
      expect(package.processors[0]).toEqual('testProcessor');
    });

    it('should add processors defined by a factory function to the processors property', function() {
      var package = new Package('packageName');
      package.processor(function testProcessor() {});
      expect(package.processors[0]).toEqual('testProcessor');
    });

    it('should complain if the processorFactory does not have a name', function() {
      var package = new Package('packageName');
      expect(function() {
        package.processor(function() {});
      }).toThrow();

      expect(function() {
        package.processor({ missing: 'name'});
      }).toThrow();
    });

    it('should use the first param as the name if it is a string', function() {
      var package = new Package('packageName');
      package.processor('testProcessor', { $process: function() { } });
      expect(package.processors[0]).toEqual('testProcessor');
    });

    it('should add the processor to the DI module', function() {
      var package = new Package('packageName');
      var testProcessor = function testProcessor() {};
      package.processor(testProcessor);
      expect(package.module.testProcessor).toEqual(['factory', testProcessor]);
    });
  });

  describe('factory()', function() {
    it('should complain if the factory is not a function', function() {
      var package = new Package('packageName');
      expect(function() {
        package.factory({ name: 'bad factory'});
      }).toThrow();
    });

    it('should complain if the factory does not have a name', function() {
      var package = new Package('packageName');
      expect(function() {
        package.factory(function() {});
      }).toThrow();
    });

    it('should use the first param as the name if it is a string', function() {
      var package = new Package('packageName');

      var testServiceFactory = function() {};
      package.factory('testService', testServiceFactory);
      expect(package.module.testService).toEqual(['factory', testServiceFactory]);
    });

    it('should add the service to the DI module', function() {
      var package = new Package('packageName');
      var testService = function testService() {};
      package.factory(testService);
      expect(package.module.testService).toEqual(['factory', testService]);
    });
  });

  describe('type()', function() {
    it('should complain if the constructor is not a function', function() {
      var package = new Package('packageName');
      expect(function() {
        package.type({ name: 'bad type'});
      }).toThrow();
    });

    it('should complain if the constructor does not have a name', function() {
      var package = new Package('packageName');
      expect(function() {
        package.type(function() {});
      }).toThrow();
    });

    it('should use the first param as the name if it is a string', function() {
      var package = new Package('packageName');
      var TestService = function() {};
      package.type('testService', TestService);
      expect(package.module.testService).toEqual(['type', TestService]);
    });

    it('should add the service to the DI module', function() {
      var package = new Package('packageName');

      var TestService = function TestService() {};
      package.type(TestService);
      expect(package.module.TestService).toEqual(['type', TestService]);
    });
  });

  describe('config()', function() {
    it('should add the function to the configFns property', function() {
      var package = new Package('packageName');
      var testFn = function() {};
      package.config(testFn);
      expect(package.configFns[0]).toEqual(testFn);
    });

    it('should complain if configFn is not a function', function() {
      var package = new Package('packageName');
      expect(function() {
        package.config({ some: 'non-function'});
      }).toThrow();
    });
  });

  describe('eventHandlers()', function() {

    it('should add eventHandler name defined by a factory function to the handlers property', function() {
      var package = new Package('packageName');
      package.eventHandler('testEvent', function testHandler() {});
      expect(package.handlers['testEvent']).toEqual(['testHandler']);
    });

    it('should compute a unique name for the handler if it doesn\'t have one', function() {
      var package = new Package('packageName');
      package.eventHandler('testEvent', function() {});
      expect(package.handlers['testEvent'][0]).toEqual('packageName_testEvent_0');
      package.eventHandler('testEvent', function() {});
      expect(package.handlers['testEvent'][1]).toEqual('packageName_testEvent_1');
    });

    it('should complain if the eventType is not a string', function() {
      var package = new Package('packageName');
      expect(function() {
        package.eventHandler(function() {});
      }).toThrow();
    });

    it('should complain if the handler is not a function', function() {
      var package = new Package('packageName');
      expect(function() {
        package.eventHandler('testEvent', { name: 'bad handler'});
      }).toThrow();
    });

    it('should add the eventHandler to the DI module', function() {
      var package = new Package('packageName');
      var testHandler = function testHandler() {};
      package.eventHandler('testEvent', testHandler);
      expect(package.module.testHandler).toEqual(['factory', testHandler]);

      var anonymousHandler = function() {};
      package.eventHandler('testEvent', anonymousHandler);
      expect(package.module.packageName_testEvent_1).toEqual(['factory', anonymousHandler]);
    });
  });

});<|endoftext|>


<commit_before>// ==UserScript==
// @name         Extra Bitcointip Support on Reddit
// @description  Add a tipping button (/u/bitcointip) and shrinks verifications
// @version      1.2.1
// @license      Public Domain
// @include      http://*.reddit.com/*
// ==/UserScript==

/* Changelog:
 * 1.2.1
 *    More efficient validation marking
 *    Fixed status names
 *    Run on all of reddit, including user pages
 * 1.2
 *    Use the new bitcointip API to get status information
 *    https://github.com/NerdfighterSean/bitcointip/blob/master/src/api/
 * 1.1.4
 *    Don't hide verification when it has children
 *    Don't hide verification when it's the URL target
 * 1.1.3
 *    Fix case insensitivity in regex
 * 1.1.2
 *    Display tipping amount
 * 1.1.1
 *    Support the full tipping syntax
 * 1.1
 *    Hide verification posts, inlining the verification
 * 1.0
 *    Add a "tip bitcoins" button.
 *
 * Useful tests:
 *  http://www.reddit.com/r/bitcointip/comments/13iykn/b/c7dj8ia
 *  http://www.reddit.com/r/bitcointip/comments/132nhq/t/c7c7iue
 *  http://www.reddit.com/r/Bitcoin/comments/14i9e7/y/c7dc6w9
 *  http://www.reddit.com/r/Bitcoin/comments/14qysn/
 */

var baseTip = '0.01 BTC';
var tipregex = /((\+(bitcointip|bitcoin|tip|btctip|bittip|btc))|((\+((?!0)(\d{1,4})) internet(s)?)|(\+((?!0)(\d{1,4})) point(s)? to (Gryffindor|Slytherin|Ravenclaw|Hufflepuff))))/i;
var api = {
    gettips: 'http://bitcointip.net/api/gettips.php?callback=?',
    gettipped: 'http://bitcointip.net/api/gettipped.php?callback=?'
};
var icons = {
    verified: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAAt1BMVEX///8AAAAAyAAAuwAAwQcAvAcAvwAAwQYAyAUAxAUAxwQAwgQAvAMAxQYAvwYAxQYAxwU5yT060j460j871T89wUE9wkFGokdGu0hIzExJl09JmE9JxExJxE1K1U9K1k5Ll09LmVNMmVNM2FBNmlRRx1NSzlRTqlVUslZU1ldVq1hVrFdV2FhWrFhX21pZqlphrWJh3WRotGtrqm1stW91sXd2t3h5t3urz6zA2sHA28HG3sf4+PhvgZhQAAAAEXRSTlMAARweJSYoLTM0O0dMU1dYbkVIv+oAAACKSURBVHjaVc7XEoIwEIXhFRED1tBUxBaPFSyxK3n/5zIBb/yv9pudnVky2Ywxm345MHkVXByllPm4W24qrLbzdo1sLPPRepc+XlnSIAuz9DQYPtXnkLhUF/ysrndV3CYLRpbg2VtpxFMwfRfEl8IghEPUhB9t9lEQoke6FnzONfpU5kEIoKOn/z+/pREPWTic38sAAAAASUVORK5CYII=",
    rejected: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAAQlBMVEX///+qAAAAAAC/AADIABSaTU3YMDDcPj7cSEjeUFDiZGTld3fmfHzoiorqkJDqlpbupKTuqqr99fX99/f+/Pz////kWqLlAAAABXRSTlMAAwcIM6KYVMQAAABfSURBVHjaXc7JDsAgCEVRsYpIBzro//9qHyHpond3AgkklIuXPKJcqleIIEB6FwEhQEW0t4rlUtt+22ZTMQ09NqZyiK8BtBCvc9iDWegY526hBVRmdcQ9RgD9f/G+P1+JEwRF2vKhRgAAAABJRU5ErkJggg==",
    tipped: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAANCAMAAACq939wAAAA/FBMVEWqVQCqcQCZZgCLXQCdYgDMjACOXACOXwCacQCfcQCqegCwggChggCnfwCwggCthACtgQC9iACleQC+iwDMlgDJlACmfgDDiwDBjADHlQDJnQDFlQDKmAChfRGjgBOmfhKmghKnghOqhBKthxOviROvjB+vjCGvjCOwiRSwihixixWxjSGziBOzkSmzky+0kSa0kiy3jxW8kxS8mCi8n0i8oEq9lBe9mSvOoBbUrTTUrTjbukXcsTDctDrdtj/exHbexXDfwWXfwmvjrBnksRjksx7lrhnqx17qyWTqymrq377rz3nr2qftuiHtvSv67cD67sj+997/+OX///8rcy1sAAAAHXRSTlMDCQoLDRQkKystMDc5Oj0+QUlKS0tMTVFSV15/i6wTI/gAAACWSURBVHjaHcrnAoFQGADQryI7sjNKN0RKZJVNQ8io938YN+f3ASDp1B9NAhD15UzXNH26KhNQXZyDBxZcNlloKadvFIbR56owUOFV9425ai8PrGwZaITQeisXoKHs7k/MP44ZaHYl54U5El8EdmjNEWbEjesf/Ljd9v0S1ETBtD3PNgUxB8nOQIybOGknAKgMy2FsmoIflIEZdK7PshkAAAAASUVORK5CYII="
};

/* Add the "tip bitcoins" button after "give gold". */
var $ = unsafeWindow.$, reddit = unsafeWindow;
var tip = $('tip bitcoins').attr({
    'class': 'tip-bitcoins login-required',
    'href': '#'
});
if (/^\/r\//.test(document.location.pathname)) {
    $('a.give-gold').parent().after($('

').append(tip));
}

/* Tipping button functionality. */
$('.tip-bitcoins').bind('click', function(event) {
    reddit.reply(event.target);
    var form = reddit.comment_reply_for_elem(event.target);
    var textarea = form.find('textarea');
    if (!textarea.val().match(tipregex)) {
        var insert = '+tip ' + baseTip;
        if (textarea.val().length > 0) {
            insert = '\n\n' + insert;
        } else {
            insert = insert + '\n\n';
        }
        textarea.val(textarea.val() + insert);
    }
    return false;
});

/* Reddit jQuery plugin. */
(function($) {
    /** Get the comment div for each element in the current set. */
    $.fn.comment = function() {
        return this.closest('.comment');
    };

    /** Get the comment ID for the first selected comment. */
    $.fn.commentID = function() {
        var full = this.first().find('input[name="thing_id"]').first().val();
        return full.replace(/^t1_/, '');
    };

    /** Get the comment body for the first comment in the current set. */
    $.fn.commentBody = function() {
        return this.find('.md').first();
    };

    /** Get the children comments for each comment in the current set. */
    $.fn.commentChildren = function() {
        return this.find('.comment');
    };

    /** Get the parent comment for each comment in the current set. */
    $.fn.commentParent = function() {
        return this.parent().comment();
    };

    /** Determine whether the first element is the target comment. */
    $.fn.isTarget = function() {
        return this.first().find('form').first().hasClass('border');
    };

    /** Return true if the first comment in the current set has a tip. */
    $.fn.hasTip = function() {
        return this.commentBody().children().is(function() {
            return tipregex.test($(this).text());
        });
    };
})($);

/* Hide verification replies. Note: t2_7vw3n is /u/bitcointip. */
$('a.id-t2_7vw3n').comment().each(function() {
    var $this = $(this);
    if ($this.commentChildren().length === 0 && !$this.isTarget()) {
        reddit.hidecomment($this.find('.expand').first());
    }
});

/* Find all the tip comments. */
var tips = {};
$('div.comment').each(function() {
    var $this = $(this);
    if ($this.hasTip()) {
        tips[$this.commentID()] = $this.find('.tagline').first();
    }
});

/* Get status info and update the tip's comment body. */
if (Object.keys(tips).length > 0) {
    var display = {
        "pending": icons.verified,
        "completed": icons.verified,
        "reversed": icons.verified,
        "cancelled": icons.rejected
    };
    $.getJSON(api.gettips + '&tips=' + Object.keys(tips), function(response) {
        response.forEach(function (tip) {
            var tagline = tips[tip.fullname.replace(/^t._/, '')];
            var icon = $('').attr({href: tip.tx, target: '_blank'});
            tagline.append(icon.append($('').attr({
                src: display[tip.status],
                style: 'vertical-align: middle; margin-left: 8px;',
                title: '+$' + tip.amountUSD + ' -> ' + tip.receiver
            })));
        });
    });
}
<commit_msg>Mark rejected tips.<commit_after>// ==UserScript==
// @name         Extra Bitcointip Support on Reddit
// @description  Add a tipping button (/u/bitcointip) and shrinks verifications
// @version      1.2.1
// @license      Public Domain
// @include      http://*.reddit.com/*
// ==/UserScript==

/* Changelog:
 * 1.2.1
 *    More efficient validation marking
 *    Fixed status names
 *    Run on all of reddit, including user pages
 * 1.2
 *    Use the new bitcointip API to get status information
 *    https://github.com/NerdfighterSean/bitcointip/blob/master/src/api/
 * 1.1.4
 *    Don't hide verification when it has children
 *    Don't hide verification when it's the URL target
 * 1.1.3
 *    Fix case insensitivity in regex
 * 1.1.2
 *    Display tipping amount
 * 1.1.1
 *    Support the full tipping syntax
 * 1.1
 *    Hide verification posts, inlining the verification
 * 1.0
 *    Add a "tip bitcoins" button.
 *
 * Useful tests:
 *  http://www.reddit.com/r/bitcointip/comments/13iykn/b/c7dj8ia
 *  http://www.reddit.com/r/bitcointip/comments/132nhq/t/c7c7iue
 *  http://www.reddit.com/r/Bitcoin/comments/14i9e7/y/c7dc6w9
 *  http://www.reddit.com/r/Bitcoin/comments/14qysn/
 */

var baseTip = '0.01 BTC';
var tipregex = /((\+(bitcointip|bitcoin|tip|btctip|bittip|btc))|((\+((?!0)(\d{1,4})) internet(s)?)|(\+((?!0)(\d{1,4})) point(s)? to (Gryffindor|Slytherin|Ravenclaw|Hufflepuff))))/i;
var rejectTime = 60 * 60 * 1000; // milliseconds
var api = {
    gettips: 'http://bitcointip.net/api/gettips.php?callback=?',
    gettipped: 'http://bitcointip.net/api/gettipped.php?callback=?'
};
var icons = {
    verified: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAAt1BMVEX///8AAAAAyAAAuwAAwQcAvAcAvwAAwQYAyAUAxAUAxwQAwgQAvAMAxQYAvwYAxQYAxwU5yT060j460j871T89wUE9wkFGokdGu0hIzExJl09JmE9JxExJxE1K1U9K1k5Ll09LmVNMmVNM2FBNmlRRx1NSzlRTqlVUslZU1ldVq1hVrFdV2FhWrFhX21pZqlphrWJh3WRotGtrqm1stW91sXd2t3h5t3urz6zA2sHA28HG3sf4+PhvgZhQAAAAEXRSTlMAARweJSYoLTM0O0dMU1dYbkVIv+oAAACKSURBVHjaVc7XEoIwEIXhFRED1tBUxBaPFSyxK3n/5zIBb/yv9pudnVky2Ywxm345MHkVXByllPm4W24qrLbzdo1sLPPRepc+XlnSIAuz9DQYPtXnkLhUF/ysrndV3CYLRpbg2VtpxFMwfRfEl8IghEPUhB9t9lEQoke6FnzONfpU5kEIoKOn/z+/pREPWTic38sAAAAASUVORK5CYII=",
    rejected: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAANCAMAAABFNRROAAAAQlBMVEX///+qAAAAAAC/AADIABSaTU3YMDDcPj7cSEjeUFDiZGTld3fmfHzoiorqkJDqlpbupKTuqqr99fX99/f+/Pz////kWqLlAAAABXRSTlMAAwcIM6KYVMQAAABfSURBVHjaXc7JDsAgCEVRsYpIBzro//9qHyHpond3AgkklIuXPKJcqleIIEB6FwEhQEW0t4rlUtt+22ZTMQ09NqZyiK8BtBCvc9iDWegY526hBVRmdcQ9RgD9f/G+P1+JEwRF2vKhRgAAAABJRU5ErkJggg==",
    tipped: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAANCAMAAACq939wAAAA/FBMVEWqVQCqcQCZZgCLXQCdYgDMjACOXACOXwCacQCfcQCqegCwggChggCnfwCwggCthACtgQC9iACleQC+iwDMlgDJlACmfgDDiwDBjADHlQDJnQDFlQDKmAChfRGjgBOmfhKmghKnghOqhBKthxOviROvjB+vjCGvjCOwiRSwihixixWxjSGziBOzkSmzky+0kSa0kiy3jxW8kxS8mCi8n0i8oEq9lBe9mSvOoBbUrTTUrTjbukXcsTDctDrdtj/exHbexXDfwWXfwmvjrBnksRjksx7lrhnqx17qyWTqymrq377rz3nr2qftuiHtvSv67cD67sj+997/+OX///8rcy1sAAAAHXRSTlMDCQoLDRQkKystMDc5Oj0+QUlKS0tMTVFSV15/i6wTI/gAAACWSURBVHjaHcrnAoFQGADQryI7sjNKN0RKZJVNQ8io938YN+f3ASDp1B9NAhD15UzXNH26KhNQXZyDBxZcNlloKadvFIbR56owUOFV9425ai8PrGwZaITQeisXoKHs7k/MP44ZaHYl54U5El8EdmjNEWbEjesf/Ljd9v0S1ETBtD3PNgUxB8nOQIybOGknAKgMy2FsmoIflIEZdK7PshkAAAAASUVORK5CYII="
};

/* Add the "tip bitcoins" button after "give gold". */
var $ = unsafeWindow.$, reddit = unsafeWindow;
var tip = $('tip bitcoins').attr({
    'class': 'tip-bitcoins login-required',
    'href': '#'
});
if (/^\/r\//.test(document.location.pathname)) {
    $('a.give-gold').parent().after($('

').append(tip));
}

/* Tipping button functionality. */
$('.tip-bitcoins').bind('click', function(event) {
    reddit.reply(event.target);
    var form = reddit.comment_reply_for_elem(event.target);
    var textarea = form.find('textarea');
    if (!textarea.val().match(tipregex)) {
        var insert = '+tip ' + baseTip;
        if (textarea.val().length > 0) {
            insert = '\n\n' + insert;
        } else {
            insert = insert + '\n\n';
        }
        textarea.val(textarea.val() + insert);
    }
    return false;
});

/* Reddit jQuery plugin. */
(function($) {
    /** Get the comment div for each element in the current set. */
    $.fn.comment = function() {
        return this.closest('.comment');
    };

    /** Get the comment ID for the first selected comment. */
    $.fn.commentID = function() {
        var full = this.first().find('input[name="thing_id"]').first().val();
        return full.replace(/^t1_/, '');
    };

    /** Get the comment's post time for the first selected comment. */
    $.fn.commentDate = function() {
        return new Date(this.find('.tagline time').first().attr('datetime'));
    };

    /** Get the comment body for the first comment in the current set. */
    $.fn.commentBody = function() {
        return this.find('.md').first();
    };

    /** Get the children comments for each comment in the current set. */
    $.fn.commentChildren = function() {
        return this.find('.comment');
    };

    /** Get the parent comment for each comment in the current set. */
    $.fn.commentParent = function() {
        return this.parent().comment();
    };

    /** Determine whether the first element is the target comment. */
    $.fn.isTarget = function() {
        return this.first().find('form').first().hasClass('border');
    };

    /** Return true if the first comment in the current set has a tip. */
    $.fn.hasTip = function() {
        return this.commentBody().children().is(function() {
            return tipregex.test($(this).text());
        });
    };
})($);

/* Hide verification replies. Note: t2_7vw3n is /u/bitcointip. */
$('a.id-t2_7vw3n').comment().each(function() {
    var $this = $(this);
    if ($this.commentChildren().length === 0 && !$this.isTarget()) {
        reddit.hidecomment($this.find('.expand').first());
    }
});

/* Find all the tip comments. */
var tips = {};
$('div.comment').each(function() {
    var $this = $(this);
    if ($this.hasTip()) {
        tips[$this.commentID()] = $this;
    }
});

/* Get status info and update the tip's comment body. */
if (Object.keys(tips).length > 0) {
    var iconStyle = 'vertical-align: middle; margin-left: 8px;';
    var display = {
        "pending": icons.verified,
        "completed": icons.verified,
        "reversed": icons.verified,
        "cancelled": icons.rejected
    };
    $.getJSON(api.gettips + '&tips=' + Object.keys(tips), function(response) {
        response.forEach(function (tip) {
            var id = tip.fullname.replace(/^t._/, '');
            var tagline = tips[id].find('.tagline').first();
            var icon = $('').attr({href: tip.tx, target: '_blank'});
            tagline.append(icon.append($('').attr({
                src: display[tip.status],
                style: iconStyle,
                title: '+$' + tip.amountUSD + ' -> ' + tip.receiver
            })));
            delete tips[id];
        });

        /* Deal with unanswered tips. */
        for (var id in tips) {
            if (Date.now() - tips[id].commentDate() > rejectTime) {
                var tagline = tips[id].find('.tagline').first();
                tagline.append($('').attr({
                    src: icons.rejected,
                    style: iconStyle,
                    title: 'This tip is invalid.'
                }));
            }
        }
    });
}
<|endoftext|>