better amd support, support for jam package manager

This commit is contained in:
Tobias Koppers 2012-08-07 20:52:48 +02:00
parent 14c441430b
commit bb1752204e
11 changed files with 132 additions and 46 deletions

View File

@ -3,7 +3,7 @@
As developer you want to reuse existing code.
As with node.js and web all files 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.
The goal of `webpack` is to bundle CommonJs (and AMD) modules into javascript files which can be loaded by `<script>`-tags.
Simply concatenating 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.
This happens mostly transparent to the developer.
@ -19,7 +19,7 @@ You as developer can use such files like any other module.
**TL;DR**
* bundle CommonJs and/or AMD modules for browser
* bundle [CommonJs](/webpack/webpack/tree/master/examples/commonjs/) and/or [AMD](/webpack/webpack/tree/master/examples/mixed/) 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 or on mobile connections)
* dependencies managed for you, on compile time (no resolution on runtime needed)
@ -39,7 +39,7 @@ require("bootstrap/less/bootstrap.less");
``` shell
npm install webpack -g
webpack lib/yourCommonJsEntryModule.js output/bundle.js
webpack lib/yourEntryModule.js output/bundle.js
```
## Goals
@ -51,6 +51,7 @@ webpack lib/yourCommonJsEntryModule.js output/bundle.js
* require minimal configuration, but offer a maximum
* load polyfills for node-specific things if used
* offer replacements for node buildin libraries
* support [npm](https://npmjs.org/) and [jam](http://jamjs.org/)
# Examples
@ -143,7 +144,7 @@ For example this works out of the box:
Somethings it happens that browsers require other code than node.js do.
`webpack` allow module developers to specify replacements which are used in the compile process of `webpack`.
Modules in `web_modules` replace modules in `node_modules`.
Modules in `web_modules` and `jam` replace modules in `node_modules`.
`filename.web.js` replaces `filename.js` when required without file extension.
in options: `alias: { "http": "http-browserify" }`
@ -429,6 +430,11 @@ You can also save this options object in a JSON file and use it with the shell c
// ".../node_modules"]
// search paths for modules
modulesDirectorys: ["xyz_modules", "node_modules"],
// default: (defaults are NOT included if you define your own)
// ["web_modules", "jam", "node_modules"];
// directories to be searched for modules
alias: {
"old-module": "new-module"
},

View File

@ -0,0 +1,16 @@
var amdRequire = require("./__webpack_amd_require");
module.exports = function(module, req) {
req = amdRequire(req);
function define(name, requires, fn) {
if(!fn) {
fn = requires;
requires = name;
}
if(!fn) {
return module.exports = name.call(module.exports, req);
}
return module.exports = fn.apply(module.exports, requires);
}
define.amd = true;
return define;
}

View File

@ -0,0 +1,13 @@
module.exports = function(req) {
function amdRequire(requires, fn) {
if(!fn) {
// commonjs
return req(requires);
}
return fn.apply(null, requires);
}
for(var name in req)
amdRequire[name] = req[name];
amdRequire.amd = true;
return amdRequire;
}

View File

@ -239,24 +239,53 @@ function walkExpression(context, expression) {
});
}
// AMD require
if(context.overwrite.indexOf("require") === -1 &&
expression.callee && expression.arguments &&
if(expression.callee && expression.arguments &&
expression.arguments.length == 2 &&
expression.callee.type === "Identifier" &&
expression.callee.name === "require") {
((context.overwrite.indexOf("require") === -1 &&
expression.callee.name === "require") ||
(context.overwrite.indexOf("define") === -1 &&
expression.callee.name === "define"))) {
var params = parseCalculatedStringArray(expression.arguments[0]);
var elements = expression.arguments[0].type == "ArrayExpression" ?
expression.arguments[0].elements : [expression.arguments[0]];
processAmdArray(params, elements);
context.requires = context.requires || [];
context.requires.push({
amdRequireRange: expression.callee.range,
line: expression.callee.loc.start.line,
column: expression.callee.loc.start.column
});
if(expression.callee.name === "define") {
context.requires.push({
name: "__webpack_amd_define",
append: "(module,require)",
line: expression.callee.loc.start.line,
column: expression.callee.loc.start.column,
variable: "define"
});
} else {
context.requires.push({
name: "__webpack_amd_require",
append: "(require)",
line: expression.callee.loc.start.line,
column: expression.callee.loc.start.column,
variable: "require"
});
}
noCallee = true;
}
// AMD define
if(context.overwrite.indexOf("define") === -1 &&
expression.callee && expression.arguments &&
expression.arguments.length == 1 &&
expression.callee.type === "Identifier" &&
expression.callee.name === "define") {
context.requires.push({
name: "__webpack_amd_define",
append: "(module,require)",
line: expression.callee.loc.start.line,
column: expression.callee.loc.start.column,
variable: "define"
});
context.ignoreOverride = true;
noCallee = true;
}
if(context.overwrite.indexOf("define") === -1 &&
expression.callee && expression.arguments &&
expression.arguments.length == 3 &&
@ -268,11 +297,13 @@ function walkExpression(context, expression) {
processAmdArray(params, elements);
context.requires = context.requires || [];
context.requires.push({
amdDefineRange: expression.callee.range,
name: "__webpack_amd_define",
append: "(module,require)",
amdNameRange: expression.arguments[0].range,
label: expression.arguments[0].value+"",
line: expression.callee.loc.start.line,
column: expression.callee.loc.start.column
column: expression.callee.loc.start.column,
variable: "define"
});
noCallee = true;
}
@ -445,8 +476,7 @@ function walkExpression(context, expression) {
break;
if(expression.object.type === "Identifier" &&
expression.object.name === "require" &&
expression.property.type === "Identifier" &&
{valueOf:1, cache:1, modules:1}.hasOwnProperty(expression.property.name))
expression.property.type === "Identifier")
break;
walkExpression(context, expression.object);
if(expression.property.type !== "Identifier")

View File

@ -121,6 +121,8 @@ function setupDefaultOptions(options) {
options.loaderPostfixes = ["-webpack-web-loader", "-webpack-loader", "-web-loader", "-loader", ""];
if(!options.paths)
options.paths = [];
if(!options.modulesDirectorys)
options.modulesDirectorys = ["web_modules", "jam", "node_modules"];
if(!options.alias)
options.alias = {};
if(!options.postprocess)
@ -319,21 +321,23 @@ function loadNodeModules(context, identifier, options, type, callback) {
function nodeModulesPaths(context, options, callback) {
var parts = context;
var rootNodeModules = parts.indexOf("node_modules");
var rootWebModules = parts.indexOf("web_modules");
var root = 0;
if(rootWebModules != -1 && rootNodeModules != -1)
root = Math.min(rootWebModules, rootNodeModules)-1;
else if(rootWebModules != -1 || rootNodeModules != -1)
root = Math.max(rootWebModules, rootNodeModules)-1;
options.modulesDirectorys.forEach(function(dir) {
var index = parts.indexOf(dir)-1;
if(index >= 0 && index < root)
root = index;
});
var dirs = [];
options.paths.forEach(function(path) { dirs.push(path) });
options.paths.forEach(function(path) {
dirs.push(path);
});
for(var i = parts.length; i > root; i--) {
if(parts[i-1] === "node_modules" || parts[i-1] === "web_modules")
if(options.modulesDirectorys.indexOf(parts[i-1]) >= 0)
continue;
var part = parts.slice(0, i);
dirs.push(join(part, ["web_modules"]));
dirs.push(join(part, ["node_modules"]));
options.modulesDirectorys.forEach(function(dir) {
dirs.push(join(part, [dir]));
});
}
var count = dirs.length;
dirs.forEach(function(dir, idx) {

View File

@ -69,19 +69,7 @@ module.exports = function(module, options, toRealId, toRealChuckId) {
}
}
}
if(requireItem.amdRequireRange) {
replaces.push({
from: requireItem.amdRequireRange[0],
to: requireItem.amdRequireRange[1],
value: "(/** AMD require **/ function(modules, fn) { return fn.apply(null, modules) })"
});
} else if(requireItem.amdDefineRange) {
replaces.push({
from: requireItem.amdDefineRange[0],
to: requireItem.amdDefineRange[1],
value: "(/** AMD define **/ function(name, modules, fn) { return module.exports = fn.apply(null, modules) })"
});
console.dir(requireItem);
if(requireItem.amdNameRange) {
replaces.push({
from: requireItem.amdNameRange[0],
to: requireItem.amdNameRange[1],

View File

@ -1,8 +1,8 @@
{
"name": "webpack",
"version": "0.5.1",
"version": "0.5.2",
"author": "Tobias Koppers @sokra",
"description": "Packs CommonJs Modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loading of js, json, jade, coffee, css, ... out of the box and more with custom loaders.",
"description": "Packs CommonJs/AMD Modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loading of js, json, jade, coffee, css, ... out of the box and more with custom loaders.",
"dependencies": {
"esprima": "0.9.x",
"optimist": "0.2.x",
@ -34,7 +34,7 @@
"engines": {
"node": ">=0.1.30"
},
"homepage": "http://github.com/sokra/modules-webpack",
"homepage": "http://github.com/webpack/webpack",
"main": "lib/webpack.js",
"bin": "./bin/webpack.js",
"scripts": {

View File

@ -0,0 +1,4 @@
define("subcontent-jam", [], function() {
window.test(true, "This file should be loaded, because it is the replacement");
return "replaced";
});

View File

@ -9,6 +9,7 @@ require.ensure("subcontent", function(require) {
// Comments work!
exports.ok = true;
window.test(require("subcontent") === "replaced", "node_modules should be replaced with web_modules");
window.test(require("subcontent-jam") === "replaced", "node_modules should be replaced with jam");
window.test(require("subcontent2/file.js") === "orginal", "node_modules should still work when web_modules exists");
});
setTimeout(function() {
@ -159,14 +160,36 @@ window.test(require("./singluar2") !== singlarObj, "require.cache can be deleted
// AMD
var template = "tmpl";
var amdLoaded = false;
var amdLoaded = 0;
require(["./circular", "../templates/" + template, true ? "./circular" : "./circular"], function(circular, testTemplate, circular2) {
window.test(circular === 1, "AMD-style requires should work");
window.test(circular2 === 1, "AMD-style requires should work with conditionals");
window.test(testTemplate === "test template", "AMD-style requires should work with context");
amdLoaded = true;
amdLoaded++;
});
window.test(amdLoaded, "AMD-style require should work (sync)");
define("name", ["./circular"], function(circular) {
window.test(circular === 1, "AMD-style requires should work, in define");
amdLoaded++;
});
define("name", [], function() {
amdLoaded++;
});
define(["./circular"], function(circular) {
window.test(circular === 1, "AMD-style requires should work, in define without name");
amdLoaded++;
});
define(function(require) {
window.test(require("./circular") === 1, "AMD-style requires should work, in define without name and requires");
amdLoaded++;
});
window.test(amdLoaded == 5, "AMD-style require should work (sync)");
// cross module system support
window.test(typeof require === "function", "require should be a function");
window.test(typeof define === "function", "define should be a function");
window.test(require.amd, "require.amd should be true");
window.test(define.amd, "define.amd should be true");
window.test(typeof module === "object", "module should be a object");

View File

@ -0,0 +1,2 @@
window.test(false, "This file should not be loaded, because it is replaced");
module.exports = "error";

View File

@ -1,2 +1,2 @@
window.test(false, "This file should not be loaded, because it is replaced");
modules.exports = "error";
module.exports = "error";