diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2125666 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..07e6e47 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/node_modules diff --git a/README.md b/README.md new file mode 100644 index 0000000..72a5414 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# webpack-core + +The shared core of [webpack](https://github.com/webpack/webpack) and [enhanced-require](https://github.com/webpack/enhanced-require). + +It mainly encapsulate + +* the loader stuff + +Not useable as standalone module, but this may change in the future. + +# License + +Copyright (c) 2012 - 2013 Tobias Koppers + +MIT (http://www.opensource.org/licenses/mit-license.php) \ No newline at end of file diff --git a/lib/ModuleBuildError.js b/lib/ModuleBuildError.js new file mode 100644 index 0000000..f26d902 --- /dev/null +++ b/lib/ModuleBuildError.js @@ -0,0 +1,15 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +function ModuleBuildError(module, err) { + Error.call(this); + Error.captureStackTrace(this, ModuleBuildError); + this.name = "ModuleBuildError"; + this.message = "Module build failed: " + err.toString(); + this.module = module; + this.error = err; +} +module.exports = ModuleBuildError; + +ModuleBuildError.prototype = Object.create(Error.prototype); diff --git a/lib/ModuleError.js b/lib/ModuleError.js new file mode 100644 index 0000000..96b2ca1 --- /dev/null +++ b/lib/ModuleError.js @@ -0,0 +1,14 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +function ModuleError(err) { + Error.call(this); + Error.captureStackTrace(this, ModuleError); + this.name = "ModuleError"; + this.message = err; + this.error = err; +} +module.exports = ModuleError; + +ModuleError.prototype = Object.create(Error.prototype); diff --git a/lib/ModuleWarning.js b/lib/ModuleWarning.js new file mode 100644 index 0000000..cb454a1 --- /dev/null +++ b/lib/ModuleWarning.js @@ -0,0 +1,14 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +function ModuleWarning(warning) { + Error.call(this); + Error.captureStackTrace(this, ModuleWarning); + this.name = "ModuleWarning"; + this.message = warning; + this.warning = warning; +} +module.exports = ModuleWarning; + +ModuleWarning.prototype = Object.create(Error.prototype); diff --git a/lib/NormalModuleMixin.js b/lib/NormalModuleMixin.js new file mode 100644 index 0000000..23286b5 --- /dev/null +++ b/lib/NormalModuleMixin.js @@ -0,0 +1,213 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +var RawSource = require("./RawSource"); +var path = require("path"); + +var ModuleBuildError = require("./ModuleBuildError"); +var ModuleError = require("./ModuleError"); +var ModuleWarning = require("./ModuleWarning"); + +function NormalModuleMixin(request, preLoaders, postLoaders) { + var splittedRequest = request.split("!"); + this.resource = splittedRequest.pop(); + this.loaders = splittedRequest; + var resourcePath = this.splitQuery(this.resource)[0]; + this.context = resourcePath && path.dirname(resourcePath); + this.request = request; + this.preLoaders = preLoaders; + this.postLoaders = postLoaders; + this.fileDependencies = []; + this.contextDependencies = []; +} +module.exports = NormalModuleMixin; + +NormalModuleMixin.mixin = function(pt) { + for(var name in NormalModuleMixin.prototype) + pt[name] = NormalModuleMixin.prototype[name]; +}; + +NormalModuleMixin.prototype.splitQuery = function splitQuery(req) { + var i = req.indexOf("?"); + if(i < 0) return [req, ""]; + return [req.substr(0, i), req.substr(i)]; +}; + +NormalModuleMixin.prototype.build = function(options, resolver, fs, callback) { + var splitQuery = this.splitQuery.bind(this); + + // Prepare context + var loaders = []; + function addLoaderToList(loader) { + var l = splitQuery(loader); + loaders.push({ + request: loader, + path: l[0], + query: l[1], + module: null + }); + } + this.preLoaders.forEach(addLoaderToList); + this.loaders.forEach(addLoaderToList); + this.postLoaders.forEach(addLoaderToList); + var loaderContext = { + version: 1, + context: this.context, + loaders: loaders, + loaderIndex: 0, + resource: this.resource, + resourcePath: splitQuery(this.resource)[0], + resourceQuery: this.resource ? splitQuery(this.resource)[1] : null, + emitWarning: function(warning) { + this.warnings.push(new ModuleWarning(warning)); + }.bind(this), + emitError: function(error) { + this.errors.push(new ModuleError(error)); + }.bind(this), + resolve: function(context, request, callback) { + resolver.resolve(context, request, callback); + }, + resolveSync: function(context, request, callback) { + return resolver.resolveSync(context, request); + }, + addDependency: function(file) { + this.fileDependencies.push(file); + }.bind(this), + addContextDependency: function(context) { + this.contextDependencies.push(context); + }.bind(this), + clearDependencies: function() { + this.fileDependencies.length = 0; + this.contextDependencies.length = 0; + }.bind(this), + options: options, + debug: options.debug, + }; + this.fillLoaderContext(loaderContext, options); + if(options.loader) for(var key in options.loader) + loaderContext[key] = options.loader[key]; + + + function runSyncOrAsync(fn, context, args, callback) { + var isSync = true; + var isDone = false; + var isError = false; // internal error + context.async = function() { + if(isDone) throw new Error("async(): The callback was already called."); + isSync = false; + return context.callback; + }; + context.callback = function() { + if(isDone) throw new Error("callback(): The callback was already called."); + isDone = true; + isSync = false; + try { + callback.apply(null, arguments); + } catch(e) { + isError = true; + throw e; + } + }; + try { + var result = fn.apply(context, args); + if(isSync) { + isDone = true; + if(result === undefined) + return callback(); + return callback(null, result); + } + } catch(e) { + if(isError) throw e; + if(isDone) { + // loader is already "done", so we cannot use the callback function + // for better debugging we print the error on the console + if(typeof e === "object" && e.stack) console.error(e.stack); + else console.error(e); + return; + } + isDone = true; + callback(e); + } + + } + + // Load and pitch loaders + (function loadPitch() { + var l = loaderContext.loaders[loaderContext.loaderIndex]; + if(!l) { + return onLoadPitchDone.call(this); + } + if(l.module) { + loaderContext.loaderIndex++; + return loadPitch.call(this); + } + if(require.supportQuery) { + l.module = require(l.request); + } else { + l.module = require(l.path); + } + if(typeof l.module !== "function") + return callback(new Error("Loader " + l.request + " didn't returned a function")); + var remaining = []; + for(var i = loaderContext.loaderIndex; i < loaderContext.loaders.length; i++) + remaining.push(loaderContext.loaders[i].request); + remaining.push(loaderContext.resource); + if(typeof l.module.pitch !== "function") return loadPitch.call(this); + runSyncOrAsync(l.module.pitch, loaderContext, [remaining.join("!"), l.data = {}], function(err) { + if(err) return onModuleBuildFailed.call(this, err); + var args = Array.prototype.slice.call(arguments, 1); + if(args.length > 0) { + onModuleBuild.apply(this, args); + } else { + loadPitch.call(this); + } + }.bind(this)); + }.call(this)); + + + function onLoadPitchDone() { + loaderContext.loaderIndex++; + var resourcePath = loaderContext.resourcePath; + if(resourcePath) { + loaderContext.addDependency(resourcePath); + fs.readFile(resourcePath, nextLoader.bind(this)); + } else + nextLoader.call(this, null, null); + } + + function nextLoader(err/*, paramBuffer1, paramBuffer2, ...*/) { + var args = Array.prototype.slice.apply(arguments, 1); + if(err) { + // a loader emitted an error + return onModuleBuildFailed.call(this, err); + } + if(loaderContext.loaderIndex === 0) { + if(Buffer.isBuffer(args[0])) + args[0] = args[0].toString("utf-8"); + return onModuleBuild.apply(this, args); + } + loaderContext.loaderIndex--; + var l = loaderContext.loaders[loaderContext.loaderIndex]; + loaderContext.data = l.data; + if(!l.module.raw && Buffer.isBuffer(args[0])) { + args[0] = args[0].toString("utf-8"); + } else if(l.module.raw && typeof args[0] === "string") { + args[0] = new Buffer(args[0], "utf-8"); + } + runSyncOrAsync(l.module, loaderContext, args, nextLoader); + } + + + function onModuleBuild(source) { + this._source = new RawSource(source); + return callback(); + } + + function onModuleBuildFailed(err) { + this.error = err; + return callback(err); + } +}; + +NormalModuleMixin.prototype.fillLoaderContext = function fillLoaderContext() {}; diff --git a/lib/RawSource.js b/lib/RawSource.js new file mode 100644 index 0000000..b8774c0 --- /dev/null +++ b/lib/RawSource.js @@ -0,0 +1,18 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +var Source = require("./Source"); + +function RawSource(value) { + Source.call(this); + this._value = value; +} +module.exports = RawSource; + +RawSource.prototype = Object.create(Source.prototype); +RawSource.prototype._bake = function() { + return { + source: this._value + }; +}; \ No newline at end of file diff --git a/lib/Source.js b/lib/Source.js new file mode 100644 index 0000000..6f7afc5 --- /dev/null +++ b/lib/Source.js @@ -0,0 +1,37 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +function Source() { + this._result = null; +} +module.exports = Source; + +Source.prototype.source = function() { + if(!this._result) + this._result = this._bake(); + return this._result.source; +}; +Source.prototype.size = function() { + if(!this._result) + this._result = this._bake(); + return this._result.source.length; +}; +Source.prototype.map = function() { + if(!this._result) + this._result = this._bake(); + return this._result.map; +}; +Source.prototype.origins = function() { + if(!this._result) + this._result = this._bake(); + return this._result.origins; +}; +Source.prototype.updateHash = function(hash) { + if(!this._result) + this._result = this._bake(); + hash.update(this._result.source || ""); + // TODO + // hash.update(this._result.map || ""); + // hash.update(this._result.origins || ""); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..9a6ebeb --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "webpack-core", + "version": "0.1.0", + "author": "Tobias Koppers @sokra", + "description": "The core of webpack and enhanced-require.", + "dependencies": { + }, + "licenses": [ + { + "type": "MIT", + "url": "http://www.opensource.org/licenses/mit-license.php" + } + ], + "devDependencies": { + "mocha": "1.3.x", + "should": "1.1.x" + }, + "engines": { + "node": ">=0.6" + }, + "homepage": "http://github.com/webpack/core", + "scripts": { + "test": "mocha --reporter spec" + } +} \ No newline at end of file