better loader support

This commit is contained in:
Tobias Koppers 2012-04-03 16:26:08 +02:00
parent 41f36de302
commit 97ed773cb1
16 changed files with 354 additions and 213 deletions

5
.npmignore Normal file
View File

@ -0,0 +1,5 @@
/node_modules
/test/js
/test/browsertest/js
/examples
README.md

View File

@ -2,6 +2,7 @@
As developer you want to reuse existing code.
As with node.js and web all file are already in the same language, but it is extra work to use your code with the node.js module system and the browser.
The goal of `webpack` is to bundle CommonJs modules into javascript files which can be loaded by `<script>`-tags.
Simply concating all required files has a disadvantage: many code to download (and execute) on page load.
Therefore `webpack` uses the `require.ensure` function ([CommonJs/Modules/Async/A](http://wiki.commonjs.org/wiki/Modules/Async/A)) to split your code automatically into multiple bundles which are loaded on demand.
@ -12,16 +13,16 @@ The result is a smaller inital code download which results in faster page load.
* bundle CommonJs modules for browser
* reuse server-side code (node.js) on client-side
* create multiple files which are loaded on demand (faster page load in big webapps)
* dependencies managed for you, on compile time
* create multiple files which are loaded on demand (faster page load in big webapps or on mobile connections)
* dependencies managed for you, on compile time (no resolution on runtime needed)
## Goals
* minimize code size
* make node.js and browser development similar
* minimize code size (mobile connection)
* minimize code size on inital download
* download code only on demand
* hide development details, like module names and folder structure
* require minimal configuration
* require minimal configuration, but offer a maximum
* load polyfills for node-specific things if used
* offer replacements for node buildin libaries
@ -83,6 +84,10 @@ File 2: 1.web.js
- code of module d and dependencies
```
Initially only `web.js` is included (and loaded) by your application.
`1.web.js` is loaded when the call to `require.ensure` happens.
After the second bundle (`1.web.js`) is loaded, the callback function will be invoked.
See [details](modules-webpack/tree/master/examples/code-splitting) for exact output.
## Reusing node.js code
@ -143,7 +148,7 @@ function getTemplate(templateName) {
// which compiles to: return require(123)("./"+templateName)
```
See [details](modules-webpack/tree/master/examples/require.context) for complete example.
See [details](/sokra/modules-webpack/tree/master/examples/require.context) for complete example.
When try to store the `require` function in another variable or try to pass it as parameter,
`webpack` convert it to a `require.context(".")` to be compatible.
@ -168,7 +173,7 @@ The `raw` loader plugin is looked up at modules `raw-webpack-web-loader`, `raw-w
and the following files are looked up: `index.webpack-web-loader.js`, `index.webpack-loader.js`, `index.web-loader.js`, `index.loader.js`, `index`, `index.js`.
Note that the `web-` versions are omitted if loaders are used in node.js.
See [example](modules-webpack/tree/master/examples/loader).
See [example](/sokra/modules-webpack/tree/master/examples/loader).
The following loaders are included as optional dependencies:
@ -218,7 +223,7 @@ try `--min` to minimize with `uglify-js`.
### `require`-function
* `require` should not be overwritten
* `require` should not be overwritten, except from polyfill
* `require.ensure` should not be overwritten or called indirect
* `require.context` should not be overwritten or called indirect
* the argument to `require.context` should be a literal or addition of multiple literals
@ -226,21 +231,22 @@ try `--min` to minimize with `uglify-js`.
The following cases could result in **too much code** in result file if used wrong:
* indirect call of `require`: `var r = require; r("./file");`
* indirect call of `require`: `var r = require; r("./file");`. It includes the whole directory.
* `require.context`. It includes the whole directory.
* expressions in require arguments: `require(variable)` (but `webpack` is smart enough for this `require(condition ? "a" : "b")`)
* the function passed to `require.ensure` is not inlined in the call.
* expressions in require arguments: `require(variable)`. It includes the whole directory. (except from `?:`-operator `require(condition ? "a" : "b")`)
* the function passed to `require.ensure` is not inlined in the call. Only requires in inlined function move into the second bundle.
### node.js specific modules
As node.js specific modules like `fs` will not work in browser they are not included and cause an error.
As node.js specific modules like `fs` will not work in browser they are not included (by default) and cause an exception.
You should replace them by own modules if you want to use them.
For some modules are replacements included in `webpack`.
Some credit goes to the browserify contributors, as I took some replacements from them.
For some simple modules are replacements included in `webpack`.
Expensive replacements are not needed by everyone, so that are not included by default.
You need to specify `--alias [module]=[replacement]` to use them.
A warning saying that some module is missing is emitted in the case you use it.
A warning saying that some module is missing is emitted in the case you use it without providing a replacement.
Some credit goes to the browserify contributors, you can use replacements provided by them.
Included simple replacements:
@ -289,32 +295,76 @@ webpack(absoluteModulePath, [options], callback)
#### `options`
you can save this options object in a JSON file and use it with the shell command.
You can also save this options object in a JSON file and use it with the shell command.
`outputJsonpFunction`
JSONP function used to load chunks
`scriptSrcPrefix`
Path from where chunks are loaded
`outputDirectory`
write files to this directory (absolute path)
`output`
write first chunk to this file
`outputPostfix`
write chunks to files named chunkId plus outputPostfix
`libary`
exports of input file are stored in this variable
`minimize`
minimize outputs with uglify-js
`includeFilenames`
add absolute filenames of input files as comments
`resolve.alias` (object)
replace a module. ex. `{"old-module": "new-module"}`
`resolve.paths` (array)
search paths
`resolve.extensions` (object)
possible extensions for files
default: `["", ".webpack.js", ".web.js", ".js"]`
`resolve.loaders` (array)
extension to loader mappings. ex. `[{test: /\.extension$/, loader: "myloader"}]`
loads files that matches the RegExp to the loader if no other loader set
`resolve.loaderExtensions` (array)
possible extensions for loaders
default: `[".webpack-web-loader.js", ".webpack-loader.js", ".web-loader.js", ".loader.js", "", ".js"]`
`resolve.loaderPostfixes` (array)
possible postfixes for loaders
default: `["-webpack-web-loader", "-webpack-loader", "-web-loader", "-loader", ""]`
`parse.overwrites` (object)
free module variables which are replaced with a module. ex. `{ "$": "jquery" }`
#### `callback`
`function(err, source / stats)`
@ -439,13 +489,13 @@ else `stats` as json see [example](modules-webpack/tree/master/examples/code-spl
<code>require("http");</code>
</td>
<td>
some
some optional
</td>
<td>
no
</td>
<td>
many
many by default
</td>
</tr>
@ -559,7 +609,7 @@ else `stats` as json see [example](modules-webpack/tree/master/examples/code-spl
require JSON
</td>
<td>
yes **NEW**
yes <em>NEW</em>
</td>
<td>
no
@ -589,7 +639,7 @@ else `stats` as json see [example](modules-webpack/tree/master/examples/code-spl
loaders
</td>
<td>
yes **NEW**
yes <em>NEW</em>
</td>
<td>
no
@ -604,7 +654,7 @@ else `stats` as json see [example](modules-webpack/tree/master/examples/code-spl
compile coffee script
</td>
<td>
yes **NEW**
yes <em>NEW</em>
</td>
<td>
no

View File

@ -39,10 +39,14 @@ var argv = require("optimist")
.boolean("json")
.describe("json", "Output Stats as JSON")
.default("json", false)
.string("alias")
.describe("alias", "Set a alias name for a module. ex. http=http-browserify")
.boolean("debug")
.describe("debug", "Prints debug info to output files")
.default("debug", false)
.demand(1)
.argv;
@ -70,6 +74,10 @@ if(argv.min) {
options.minimize = true;
}
if(argv.debug) {
options.debug = true;
}
if(argv.filenames) {
options.includeFilenames = true;
}
@ -161,6 +169,11 @@ if(argv.single) {
console.log("\033[1m\033[33mWARNING: " + warning + "\033[39m\033[22m");
});
}
if(stats.errors) {
stats.errors.forEach(function(error) {
console.log("\033[1m\033[31mERROR: " + error + "\033[39m\033[22m");
});
}
}
});
}

View File

@ -22,6 +22,7 @@ module.exports = function buildDeps(context, mainModule, options, callback) {
var depTree = {
warnings: [],
errors: [],
modules: {},
modulesById: {},
chunks: {},
@ -57,11 +58,71 @@ module.exports = function buildDeps(context, mainModule, options, callback) {
}
}
function addModule(depTree, context, module, options, reason, callback) {
if(context)
resolve(context, module, options.resolve, resolved);
else
resolved(null, module);
function execLoaders(request, loaders, filenames, contents, options, callback) {
if(loaders.length === 0)
callback(null, contents[0]);
else {
var loaderFunctions = [];
try {
loaders.forEach(function(name) {
var loader = require(name);
loaderFunctions.push(loader);
});
} catch(e) {
callback(e);
return;
}
function nextLoader() {
var args = Array.prototype.slice.apply(arguments);
var err = args.shift();
if(err) {
callback(err);
return;
}
if(loaderFunctions.length > 0) {
try {
var async = false;
var context = {
request: request,
filenames: filenames,
exec: function(code, filename) {
var Module = require("module");
var m = new Module("exec in " + request, module);
m._compile(code, filename);
return m.exports;
},
async: function() {
async = true;
return nextLoader;
},
callback: function() {
async = true;
nextLoader.apply(null, arguments);
},
web: true,
debug: options.debug,
values: undefined,
options: options
};
var retVal = loaderFunctions.pop().apply(context, args);
if(!async)
nextLoader(retVal === undefined ? new Error("loader did not return a value") : null, retVal);
} catch(e) {
callback(err);
return;
}
} else {
callback(null, args[0]);
}
}
contents.unshift(null);
nextLoader.apply(null, contents);
}
}
function addModule(depTree, context, modu, options, reason, callback) {
resolve(context || path.dirname(modu), modu, options.resolve, resolved);
function resolved(err, filename) {
if(err) {
callback(err);
@ -71,12 +132,12 @@ function addModule(depTree, context, module, options, reason, callback) {
depTree.modules[filename].reasons.push(reason);
callback(null, depTree.modules[filename].id);
} else {
var module = depTree.modules[filename] = {
var modu = depTree.modules[filename] = {
id: depTree.nextModuleId++,
filename: filename,
reasons: [reason]
};
depTree.modulesById[module.id] = module;
depTree.modulesById[modu.id] = modu;
var filenameWithLoaders = filename;
var loaders = filename.split(/!/g);
filename = loaders.pop();
@ -85,41 +146,12 @@ function addModule(depTree, context, module, options, reason, callback) {
callback(err);
return;
}
if(loaders.length === 0)
processJs(content);
else {
var loaderFunctions = [];
try {
loaders.forEach(function(name) {
var loader = require(name);
loaderFunctions.push(loader);
});
} catch(e) {
callback(e);
execLoaders(filenameWithLoaders, loaders, [filename], [content], options, processJs);
function processJs(err, source) {
if(err) {
callback(err);
return;
}
function nextLoader(err, content) {
if(err) {
callback(err);
return;
}
if(loaderFunctions.length > 0) {
try {
loaderFunctions.pop()([content], {
request: filenameWithLoaders,
filename: filename
}, nextLoader);
} catch(e) {
callback(err);
return;
}
} else {
processJs(content);
}
}
nextLoader(null, content);
}
function processJs(source) {
var deps;
try {
deps = parse(source, options.parse);
@ -127,10 +159,10 @@ function addModule(depTree, context, module, options, reason, callback) {
callback("File \"" + filenameWithLoaders + "\" parsing failed: " + e);
return;
}
module.requires = deps.requires || [];
module.asyncs = deps.asyncs || [];
module.contexts = deps.contexts || [];
module.source = source;
modu.requires = deps.requires || [];
modu.asyncs = deps.asyncs || [];
modu.contexts = deps.contexts || [];
modu.source = source;
var requires = {}, directRequire = {};
var contexts = [], directContexts = {};
@ -143,20 +175,20 @@ function addModule(depTree, context, module, options, reason, callback) {
contexts.push({context: c, module: m});
}
}
if(module.requires) {
module.requires.forEach(add);
module.requires.forEach(function(r) {
if(modu.requires) {
modu.requires.forEach(add);
modu.requires.forEach(function(r) {
directRequire[r.name] = true;
});
}
if(module.contexts) {
module.contexts.forEach(addContext(module));
module.contexts.forEach(function(c) {
if(modu.contexts) {
modu.contexts.forEach(addContext(modu));
modu.contexts.forEach(function(c) {
directContexts[c.name] = true;
});
}
if(module.asyncs)
module.asyncs.forEach(function addAsync(c) {
if(modu.asyncs)
modu.asyncs.forEach(function addAsync(c) {
if(c.requires)
c.requires.forEach(add);
if(c.asyncs)
@ -176,7 +208,7 @@ function addModule(depTree, context, module, options, reason, callback) {
};
addModule(depTree, path.dirname(filename), moduleName, options, reason, function(err, moduleId) {
if(err) {
depTree.warnings.push("Cannot find module '" + moduleName + "'\n " + err +
depTree.errors.push("Cannot find module '" + moduleName + "'\n " + err +
"\n @ " + filename + " (line " + requires[moduleName][0].line + ", column " + requires[moduleName][0].column + ")");
} else {
requires[moduleName].forEach(function(requireItem) {
@ -196,7 +228,8 @@ function addModule(depTree, context, module, options, reason, callback) {
};
addContextModule(depTree, path.dirname(filename), context.name, options, reason, function(err, contextModuleId) {
if(err) {
errors.push(err+"\n @ " + filename + " (line " + context.line + ", column " + context.column + ")");
depTree.errors.push("Cannot find context '"+context.name+"'\n " + err +
"\n @ " + filename + " (line " + context.line + ", column " + context.column + ")");
} else {
context.id = contextModuleId;
module.requires.push({id: context.id});
@ -216,7 +249,7 @@ function addModule(depTree, context, module, options, reason, callback) {
if(errors.length) {
callback(errors.join("\n"));
} else {
callback(null, module.id);
callback(null, modu.id);
}
}
}
@ -246,6 +279,10 @@ function addContextModule(depTree, context, contextModuleName, options, reason,
reasons: [reason]
};
depTree.modulesById[contextModule.id] = contextModule;
var contextModuleNameWithLoaders = dirname;
var loaders = dirname.split(/!/g);
dirname = loaders.pop();
var prependLoaders = loaders.length === 0 ? "" : loaders.join("!") + "!";
var extensions = (options.resolve && options.resolve.extensions) || [".web.js", ".js"];
function doDir(dirname, moduleName, done) {
fs.readdir(dirname, function(err, list) {
@ -279,12 +316,18 @@ function addContextModule(depTree, context, contextModuleName, options, reason,
else
doDir(filename, moduleName + "/" + file, endOne);
} else {
var hasExt = false;
extensions.forEach(function(ext) {
if(file.substr(file.length - ext.length) === ext)
hasExt = true;
});
if(!hasExt) {
var match = false;
if(loaders.length === 0)
extensions.forEach(function(ext) {
if(file.substr(file.length - ext.length) === ext)
match = true;
if(options.resolve && options.resolve.loaders)
options.resolve.loaders.forEach(function(loader) {
if(loader.test.test(filename))
match = true;
});
});
if(!match && loaders.length === 0) {
endOne();
return;
}
@ -292,7 +335,7 @@ function addContextModule(depTree, context, contextModuleName, options, reason,
type: "context",
filename: reason.filename
};
addModule(depTree, null, filename, options, reason, function(err, moduleId) {
addModule(depTree, dirname, prependLoaders + filename, options, reason, function(err, moduleId) {
if(err) {
depTree.warnings.push("A file in context was excluded because of error: " + err);
endOne();

View File

@ -28,26 +28,33 @@ function resolve(context, identifier, options, type, callback) {
}
if(identArray[0] === "." || identArray[0] === ".." || identArray[0] === "" || identArray[0].match(/^[A-Z]:$/i)) {
var pathname = identArray[0][0] === "." ? join(contextArray, identArray) : path.join.apply(path, identArray);
loadAsFile(pathname, options, type, function(err, absoluteFilename) {
if(err) {
loadAsDirectory(pathname, options, type, finalResult);
return;
}
callback(null, absoluteFilename);
});
if(type === "context") {
fs.stat(pathname, function(err, stat) {
if(err) {
finalResult(err);
return;
}
if(!stat.isDirectory()) {
finalResult("Context \"" + identifier + "\" in not a directory");
return;
}
callback(null, pathname);
});
} else {
loadAsFile(pathname, options, type, function(err, absoluteFilename) {
if(err) {
loadAsDirectory(pathname, options, type, finalResult);
return;
}
callback(null, absoluteFilename);
});
}
} else {
loadNodeModules(contextArray, identArray, options, type, finalResult);
}
}
/**
* context: absolute filename of current file
* identifier: module to find
* options:
* paths: array of lookup paths
* callback: function(err, absoluteFilename)
*/
module.exports = function(context, identifier, options, callback) {
function doResolve(context, identifier, options, type, callback) {
if(!callback) {
callback = options;
options = {};
@ -93,7 +100,7 @@ module.exports = function(context, identifier, options, callback) {
}
}
identifiers.forEach(function(ident, index) {
resolve(context, ident, options, index === identifiers.length - 1 ? "normal" : "loader", function(err, filename) {
resolve(context, ident, options, index === identifiers.length - 1 ? type : "loader", function(err, filename) {
if(err) {
errors.push(err);
} else {
@ -108,42 +115,22 @@ module.exports = function(context, identifier, options, callback) {
});
}
module.exports.context = function(context, identifier, options, callback) {
if(!callback) {
callback = options;
options = {};
}
if(!options)
options = {};
if(!options.paths)
options.paths = [];
function finalResult(err, absoluteFilename) {
if(err) {
callback("Context \"" + identifier + "\" not found in context \"" + context + "\"");
return;
}
callback(null, absoluteFilename);
}
var identArray = identifier.split("/");
var contextArray = split(context);
if(identArray[0] === "." || identArray[0] === ".." || identArray[0] === "") {
var pathname = join(contextArray, identArray);
fs.stat(pathname, function(err, stat) {
if(err) {
finalResult(err);
return;
}
if(!stat.isDirectory()) {
finalResult("Context \"" + identifier + "\" in not a directory");
return;
}
callback(null, pathname);
});
} else {
loadNodeModulesAsContext(contextArray, identArray, options, finalResult);
}
/**
* context: absolute filename of current file
* identifier: module to find
* options:
* paths: array of lookup paths
* callback: function(err, absoluteFilename)
*/
module.exports = function(context, identifier, options, callback) {
return doResolve(context, identifier, options, "normal", callback);
}
module.exports.context = function(context, identifier, options, callback) {
return doResolve(context, identifier, options, "context", callback);
}
function split(a) {
return a.split(/[\/\\]/g);
}
@ -214,43 +201,37 @@ function loadNodeModules(context, identifier, options, type, callback) {
});
function tryDir(dir) {
var pathname = join(split(dir), identifier);
loadAsFile(pathname, options, type, function(err, absoluteFilename) {
if(err) {
loadAsDirectory(pathname, options, type, function(err, absoluteFilename) {
if(err) {
if(dirs.length === 0) {
callback("no module in any path of paths");
return;
}
tryDir(dirs.shift());
if(type === "context") {
fs.stat(pathname, function(err, stat) {
if(err || !stat.isDirectory()) {
if(dirs.length === 0) {
callback("no directory in any path of paths");
return;
}
callback(null, absoluteFilename);
});
return;
}
callback(null, absoluteFilename);
});
}
tryDir(dirs.shift());
});
}
function loadNodeModulesAsContext(context, identifier, options, callback) {
nodeModulesPaths(context, options, function(err, dirs) {
function tryDir(dir) {
var pathname = join(split(dir), identifier);
fs.stat(pathname, function(err, stat) {
if(err || !stat.isDirectory()) {
if(dirs.length === 0) {
callback(true);
tryDir(dirs.shift());
return;
}
tryDir(dirs.shift());
return;
}
callback(null, pathname);
});
callback(null, pathname);
});
} else {
loadAsFile(pathname, options, type, function(err, absoluteFilename) {
if(err) {
loadAsDirectory(pathname, options, type, function(err, absoluteFilename) {
if(err) {
if(dirs.length === 0) {
callback("no module in any path of paths");
return;
}
tryDir(dirs.shift());
return;
}
callback(null, absoluteFilename);
});
return;
}
callback(null, absoluteFilename);
});
}
}
tryDir(dirs.shift());
});

View File

@ -80,7 +80,7 @@ module.exports = function(context, moduleName, options, callback) {
options.resolve.paths.push(path.join(path.dirname(__dirname), "buildin", "node_modules"));
options.resolve.paths.push(path.join(path.dirname(__dirname), "node_modules"));
options.resolve.alias = options.resolve.alias || {};
options.resolve.loaders = options.loaders || [];
options.resolve.loaders = options.resolve.loaders || [];
options.resolve.loaders.push({test: /\.coffee$/, loader: "coffee"});
options.resolve.loaders.push({test: /\.json$/, loader: "json"});
options.resolve.loaders.push({test: /\.jade$/, loader: "jade"});
@ -121,11 +121,11 @@ module.exports = function(context, moduleName, options, callback) {
if(Object.keys(depTree.chunks).length > 1) {
buffer.push(templateAsync);
buffer.push("/******/({a:");
buffer.push(stringify(options.outputPostfix));
buffer.push(JSON.stringify(options.outputPostfix));
buffer.push(",b:");
buffer.push(stringify(options.outputJsonpFunction));
buffer.push(JSON.stringify(options.outputJsonpFunction));
buffer.push(",c:");
buffer.push(stringify(options.scriptSrcPrefix));
buffer.push(JSON.stringify(options.scriptSrcPrefix));
buffer.push(",\n");
} else {
buffer.push(templateSingle);
@ -183,6 +183,7 @@ module.exports = function(context, moduleName, options, callback) {
buffer.modulesFirstChunk = sum;
buffer.fileSizes = fileSizeMap;
buffer.warnings = depTree.warnings;
buffer.errors = depTree.errors;
buffer.fileModules = fileModulesMap;
callback(null, buffer);
} else {
@ -218,8 +219,4 @@ function uglify(input, filename) {
return input;
}
return source;
}
function stringify(str) {
return '"' + str.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"") + '"';
}

View File

@ -2,10 +2,6 @@
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
function stringify(str) {
return '"' + str.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"") + '"';
}
module.exports = function(module, options) {
if(!module.source) {
if(module.requireMap) {
@ -54,19 +50,19 @@ module.exports = function(module, options) {
replaces.push({
from: contextItem.calleeRange[0],
to: contextItem.calleeRange[1],
value: "require(" + prefix + ((contextItem.id || "throw new Error('there is not id for this')") + "") + ")"
value: "require(" + prefix + ((contextItem.id || JSON.stringify("context: " + contextItem.name || "context failed")) + "") + ")"
});
if(contextItem.replace)
replaces.push({
from: contextItem.replace[0][0],
to: contextItem.replace[0][1],
value: stringify(contextItem.replace[1])
value: JSON.stringify(contextItem.replace[1])
});
} else {
replaces.push({
from: contextItem.expressionRange[0],
to: contextItem.expressionRange[1],
value: "require(" + prefix + ((contextItem.id || "throw new Error('there is not id for this')") + "") + ")" + postfix
value: "require(" + prefix + ((contextItem.id || JSON.stringify("context: " + contextItem.name || "context failed")) + "") + ")" + postfix
});
}
}
@ -109,5 +105,28 @@ module.exports = function(module, options) {
remSource.substr(repl.to+1)
);
});
if(options.debug) {
if(options.minimize) {
result = [uglify(result.join(""), module.filename)];
}
result.push("\n\n// WEBPACK FOOTER //\n"+
"// module.id = " + module.id + "\n" +
"//@ sourceURL=webpack-module://" + encodeURI(module.filename).replace(/%5C|%2F/g, "/"));
return "eval(" + JSON.stringify(result.join("")) + ")";
}
return result.join("");
}
function uglify(input, filename) {
var uglify = require("uglify-js");
try {
source = uglify.parser.parse(input);
source = uglify.uglify.ast_mangle(source);
source = uglify.uglify.ast_squeeze(source);
source = uglify.uglify.gen_code(source);
} catch(e) {
throw new Error(filename + " @ Line " + e.line + ", Col " + e.col + ", " + e.message);
return input;
}
return source;
}

View File

@ -1,7 +1,7 @@
// Polyfill for node.js
// - adds require.ensure
// - adds require.context
// call it like this:
// call it like this:
// require = require("webpack/require-polyfill")(require.valueOf());
// This is only required when you want to use the special require.xxx methods
// in server-side code which should be so only in rar cases.
@ -60,13 +60,40 @@ module.exports = function(req) {
if(cacheEntry)
return cacheEntry;
var content = [require("fs").readFileSync(resource, "utf-8")];
var values;
function exec(code, filename) {
var Module = require("module");
var m = new Module("exec in " + cacheLine, module);
m._compile(code, filename);
return m.exports;
}
resolved.forEach(function(loader) {
content = oldReq(loader)(content, {
var set = false, err = null;
var context = {
request: cacheLine,
filename: resource
});
filenames: [resource],
exec: exec,
async: function() { return false; },
callback: function() {
set = true;
content = Array.prototype.slice.apply(arguments);
err = content.shift();
values = context.values;
},
inputValues: values,
values: undefined
};
var retVal = oldReq(loader).apply(context, content);
if(set) {
if(err) throw err;
} else {
content = [retVal];
values = context.values;
}
});
return content;
if(values !== undefined)
return values[0];
return exec(content[0], cacheLine);
} else
return oldReq(name);
};

View File

@ -121,3 +121,9 @@ window.test(require("json!../../../package.json").name === "webpack", "Buildin '
window.test(require("../../../package.json").name === "webpack", "Buildin 'json' loader, by ext");
window.test(require("coffee!../resources/script.coffee") === "coffee test", "Buildin 'coffee' loader");
window.test(require("../resources/script.coffee") === "coffee test", "Buildin 'coffee' loader, by ext");
// Loader & Context
var abc = "abc", scr = "script.coffee";
window.test(require("../resources/" + scr) === "coffee test", "context should process extensions");
window.test(require("raw!../resources/" + abc + ".txt") === "abc", "raw loader with context");

View File

@ -1,3 +1,3 @@
module.exports = function(contents, options, callback) {
callback(null, contents[0].split("").reverse().join(""));
module.exports = function(content) {
return content.split("").reverse().join("");
}

View File

@ -1,7 +1,4 @@
module.exports = function(contents, options, callback) {
module.exports = function(content) {
var content = contents[0];
callback(null, "module.exports=" + stringify(content));
callback(null, "module.exports=" + JSON.stringify(content));
}
function stringify(str) {
return '"' + str.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"") + '"';
}

View File

@ -1,7 +1,4 @@
module.exports = function(contents, options, callback) {
var content = contents[0];
callback(null, "module.exports=" + stringify(content+"loader"));
module.exports = function(content) {
var callback = this.async();
callback(null, "module.exports=" + JSON.stringify(content+"loader"));
}
function stringify(str) {
return '"' + str.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"") + '"';
}

View File

@ -1,7 +1,3 @@
module.exports = function(contents, options, callback) {
var content = contents[0];
callback(null, "module.exports=" + stringify(content+"web"));
module.exports = function(content) {
this.callback(null, "module.exports=" + JSON.stringify(content+"web"));
}
function stringify(str) {
return '"' + str.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"") + '"';
}

View File

@ -1,7 +1,3 @@
module.exports = function(contents, options, callback) {
var content = contents[0];
callback(null, "module.exports=" + stringify(content+"webpack"));
module.exports = function(content) {
return "module.exports=" + JSON.stringify(content+"webpack");
}
function stringify(str) {
return '"' + str.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"") + '"';
}

View File

@ -3,7 +3,7 @@
<title>webpack tests</title>
<script>
window.test = function(ok, message) {
document.write("<p>" + (ok?"OK: ":"FAILED: ")+message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</p>");
document.write("<p " + (ok?">OK: ":"style='color: red; font-weight: bold;'>FAILED: ")+message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") + "</p>");
};
window.onerror = function(error) {
window.test(false, "Shouldn't throw error: " + error);
@ -17,6 +17,8 @@
window.test = function(ok, message) {
var p = document.createElement("p");
p.appendChild(document.createTextNode((ok?"OK: ":"FAILED: ")+message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")));
if(!ok)
p.style = "color: red; font-weight: bold;";
document.body.appendChild(p);
};
window.writing = false;

View File

@ -19,6 +19,17 @@ function testResolve(context, moduleName, result) {
}
}
}
function testResolveContext(context, moduleName, result) {
return {
topic: function() {
resolve.context(context, moduleName, {}, this.callback);
},
"correct filename": function(filename) {
assert.equal(filename, result);
}
}
}
vows.describe("resolve").addBatch({
"resolve simple 1": testResolve(fixtures, "./main1.js", path.join(fixtures, "main1.js")),
@ -31,6 +42,7 @@ vows.describe("resolve").addBatch({
"resolve complex 2": testResolve(path.join(fixtures, "node_modules", "complexm", "web_modules", "m1"),
"m2/b.js", path.join(fixtures, "node_modules", "m2", "b.js")),
"resolve loader 1": testResolve(fixtures, "m1/a!./main1.js", path.join(fixtures, "node_modules", "m1", "a.js") + "!" + path.join(fixtures, "main1.js")),
"resolve loader context 1": testResolveContext(fixtures, "m1/a!./", path.join(fixtures, "node_modules", "m1", "a.js") + "!" + fixtures),
}).export(module);