added contexts, docs and examples

This commit is contained in:
Tobias Koppers 2012-03-11 21:37:18 +01:00
parent a3d4bb4693
commit 14b42ac18b
25 changed files with 670 additions and 45 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
/node_modules
/test/js
/test/browsertest/js
/example/js
/examples/*/js

View File

@ -75,7 +75,7 @@ File 2: 1.web.js
- code of module d and dependencies
```
See [details](modules-webpack/tree/master/example) for exact output.
See [details](modules-webpack/tree/master/examples/code-splitting) for exact output.
## Browser replacements
@ -87,19 +87,52 @@ Modules in `web_modules` replace modules in `node_modules`.
TODO specify replacements in options
## require.context
If the `require`d module is not known while compile time we get into a problem.
A solution is the method `require.context` which takes a directory as parameter
and returns a function which behaves like the `require` function issued from a file
in this directory (but only if used for files in that directory).
### Example
We have a directory full of templates, which are compiled javascript files.
A template should be loaded by template name.
``` javascript
var requireTemplate = require.context("./templates");
function getTemplate(templateName) {
return requireTemplate("./" + templateName);
}
```
In addition to that `webpack` uses the `require.context` function automatically
if you use variables or other non-literal things in the `require` function.
That means the following code behaves like the above:
``` javascript
function getTemplate(templateName) {
return require("./templates/" + templateName);
}
// is compiled like: return require.context("./templates")("./"+templateName)
```
See [details](modules-webpack/tree/master/examples/require.context) for complete example.
*Warning: The complete code in the directory are included. So use it carefully.*
## Limitations
### `require`-function
As dependencies are resolved before running:
* `require` should not be overwritten
* `require` should not be called indirect as `var r = require; r("./a");`
* arguments of `require` should be literals. `"./abc" + "/def"` is allowed to support long lines.
* `require.ensure` has the same limitations as `require`
* `require` should not be called indirect as `var r = require; r("./a");`. Use `require.context`?
* `require.ensure` should not be overwritten or called indirect
* the function passed to `require.ensure` should be inlined in the call.
TODO allow variables passing to `require` like `require("./templates/" + mytemplate)`
(this will cause all modules matching this pattern to be included in addition to a mapping table)
* `require.context` should not be overwritten or called indirect
* the argument to `require.context` should be a literal or addition of multiple literals
### node.js specific modules
@ -173,7 +206,7 @@ add absolute filenames of input files as comments
#### `callback`
`function(err, source / stats)`
`source` if `options.output` is not set
else `stats` as json see [example](/modules-webpack/tree/master/example)
else `stats` as json see [example](/modules-webpack/tree/master/examples/code-splitting)
## medikoo/modules-webmake
@ -183,14 +216,20 @@ So big credit goes to medikoo.
However `webpack` has big differences:
`webpack` replaces module names and paths with numbers. `webmake` don't do that and do resolves requires on client-side.
This design of `webmake` wes intended to support variables as arguments to require calls.
This design of `webmake` was intended to support variables as arguments to require calls.
`webpack` resolves requires in compile time and have no resolve code on client side. This results in smaller bundles.
Variables as argments will be handled different and with more limitations.
Another limitation in `webmake` which are based on the previous one is that modules must be in the current package scope.
In `webpack` this is not a restriction.
The design of `webmake` causes all modules with the same name to overlap. This can be problematic if different submodules rely on specific versions of the same module. The behaivior also differs from the behaivior of node.js, because node.js installs a module for each instance in submodules and `webmake` cause them the merge into a single module which is only installed once. In `webpack` this is not the case. Different versions do not overlap and modules are installed multiple times. But in `webpack` this can (currently) cause duplicate code if a module is used in multiple modules. I want to face this issue (TODO).
The design of `webmake` causes all modules with the same name to overlap.
This can be problematic if different submodules rely on specific versions of the same module.
The behaivior also differs from the behaivior of node.js, because node.js installs a module for each instance in submodules and `webmake` cause them the merge into a single module which is only installed once.
In `webpack` this is not the case.
Different versions do not overlap and modules are installed multiple times.
But in `webpack` this can (currently) cause duplicate code if a module is used in multiple modules.
I want to face this issue (TODO).
`webmake` do (currently) not support Code Splitting.

View File

@ -0,0 +1,138 @@
# example.js
``` javascript
function getTemplate(templateName, callback) {
require.ensure([], function(require) {
callback(require("../require.context/templates/"+templateName));
});
}
getTemplate("a", function(a) {
console.log(a);
});
getTemplate("b", function(b) {
console.log(b);
});
```
# js/output.js
``` javascript
/******/(function(document, undefined) {
/******/ return function(modules) {
/******/ var installedModules = {}, installedChunks = {0:1};
/******/ function require(moduleId) {
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ var module = installedModules[moduleId] = {
/******/ exports: {}
/******/ };
/******/ modules[moduleId](module, module.exports, require);
/******/ return module.exports;
/******/ }
/******/ require.ensure = function(chunkId, callback) {
/******/ if(installedChunks[chunkId] === 1) return callback(require);
/******/ if(installedChunks[chunkId] !== undefined)
/******/ installedChunks[chunkId].push(callback);
/******/ else {
/******/ installedChunks[chunkId] = [callback];
/******/ var head = document.getElementsByTagName('head')[0];
/******/ var script = document.createElement('script');
/******/ script.type = 'text/javascript';
/******/ script.src = modules.c+chunkId+modules.a;
/******/ head.appendChild(script);
/******/ }
/******/ };
/******/ window[modules.b] = function(chunkId, moreModules) {
/******/ for(var moduleId in moreModules)
/******/ modules[moduleId] = moreModules[moduleId];
/******/ var callbacks = installedChunks[chunkId];
/******/ installedChunks[chunkId] = 1;
/******/ for(var i = 0; i < callbacks.length; i++)
/******/ callbacks[i](require);
/******/ };
/******/ return require(0);
/******/ }
/******/})(document)
/******/({a:".output.js",b:"webpackJsonp",c:"",
/******/0: function(module, exports, require) {
function getTemplate(templateName, callback) {
require.ensure(1, function(require) {
callback(require(1)("./"+templateName));
});
}
getTemplate("a", function(a) {
console.log(a);
});
getTemplate("b", function(b) {
console.log(b);
});
/******/},
/******/
/******/})
```
# js/1.output.js
``` javascript
/******/webpackJsonp(1, {
/******/1: function(module, exports, require) {
/***/module.exports = function(name) {
/***/ var map = {"./b.js":3,"./a.js":2,"./c.js":4};
/***/ return require(map[name]||map[name+".web.js"]||map[name+".js"]);
/***/};
/******/},
/******/
/******/2: function(module, exports, require) {
module.exports = function() {
return "This text was generated by template A";
}
/******/},
/******/
/******/3: function(module, exports, require) {
module.exports = function() {
return "This text was generated by template B";
}
/******/},
/******/
/******/4: function(module, exports, require) {
module.exports = function() {
return "This text was generated by template C";
}
/******/},
/******/
/******/})
```
# Info
## Uncompressed
``` javascript
{ chunkCount: 2,
modulesCount: 5,
modulesIncludingDuplicates: 5,
modulesPerChunk: 2.5,
modulesFirstChunk: 1,
fileSizes: { 'output.js': 1855, '1.output.js': 729 } }
```
## Minimized (uglify-js, no zip)
``` javascript
{ chunkCount: 2,
modulesCount: 5,
modulesIncludingDuplicates: 5,
modulesPerChunk: 2.5,
modulesFirstChunk: 1,
fileSizes: { 'output.js': 683, '1.output.js': 404 } }
```

View File

@ -5,7 +5,7 @@ argv.shift();
argv.shift();
var extraArgs = argv.join(" ");
cp.exec("node ../bin/webpack.js "+extraArgs+" example.js js/output.js", function (error, stdout, stderr) {
cp.exec("node ../../bin/webpack.js "+extraArgs+" example.js js/output.js", function (error, stdout, stderr) {
console.log('stdout:\n' + stdout);
console.log('stderr:\n ' + stderr);
if (error !== null) {

View File

@ -0,0 +1,11 @@
function getTemplate(templateName, callback) {
require.ensure([], function(require) {
callback(require("../require.context/templates/"+templateName));
});
}
getTemplate("a", function(a) {
console.log(a);
});
getTemplate("b", function(b) {
console.log(b);
});

View File

@ -114,7 +114,7 @@ webpackJsonp(1,{3:function(a,b,c){},4:function(a,b,c){}})
fileSizes: { 'output.js': 1948, '1.output.js': 200 } }
```
## Compress (uglify-js, no zip)
## Minimized (uglify-js, no zip)
``` javascript
{ chunkCount: 2,

View File

@ -0,0 +1,14 @@
var cp = require('child_process');
var argv = process.argv;
argv.shift();
argv.shift();
var extraArgs = argv.join(" ");
cp.exec("node ../../bin/webpack.js "+extraArgs+" example.js js/output.js", function (error, stdout, stderr) {
console.log('stdout:\n' + stdout);
console.log('stderr:\n ' + stderr);
if (error !== null) {
console.log('error: ' + error);
}
});

View File

@ -0,0 +1,119 @@
# example.js
``` javascript
function getTemplate(templateName) {
return require("./templates/"+templateName);
}
console.log(getTemplate("a"));
console.log(getTemplate("b"));
```
# templates/
* a.js
* b.js
* c.js
All templates are of this pattern:
``` javascript
module.exports = function() {
return "This text was generated by template X";
}
```
# js/output.js
``` javascript
/******/(function(document, undefined) {
/******/ return function(modules) {
/******/ var installedModules = {};
/******/ function require(moduleId) {
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId];
/******/ var module = installedModules[moduleId] = {
/******/ exports: {}
/******/ };
/******/ modules[moduleId](module, module.exports, require);
/******/ return module.exports;
/******/ }
/******/ require.ensure = function(chunkId, callback) {
/******/ callback(require);
/******/ };
/******/ return require(0);
/******/ }
/******/})(document)
/******/({
/******/0: function(module, exports, require) {
function getTemplate(templateName) {
return require(1)("./"+templateName);
}
console.log(getTemplate("a"));
console.log(getTemplate("b"));
/******/},
/******/
/******/1: function(module, exports, require) {
/***/module.exports = function(name) {
/***/ var map = {"./b.js":3,"./c.js":4,"./a.js":2};
/***/ return require(map[name]||map[name+".web.js"]||map[name+".js"]);
/***/};
/******/},
/******/
/******/2: function(module, exports, require) {
module.exports = function() {
return "This text was generated by template A";
}
/******/},
/******/
/******/3: function(module, exports, require) {
module.exports = function() {
return "This text was generated by template B";
}
/******/},
/******/
/******/4: function(module, exports, require) {
module.exports = function() {
return "This text was generated by template C";
}
/******/},
/******/
/******/})
```
# Info
## Uncompressed
``` javascript
{ chunkCount: 1,
modulesCount: 5,
modulesIncludingDuplicates: 5,
modulesPerChunk: 5,
modulesFirstChunk: 5,
fileSizes: { 'output.js': 1529 } }
```
## Minimized (uglify-js, no zip)
``` javascript
{ chunkCount: 1,
modulesCount: 5,
modulesIncludingDuplicates: 5,
modulesPerChunk: 5,
modulesFirstChunk: 5,
fileSizes: { 'output.js': 674 } }
```
# Code Splitting
See [this example combined with code splitting](code-splitted-require.context)

View File

@ -0,0 +1,14 @@
var cp = require('child_process');
var argv = process.argv;
argv.shift();
argv.shift();
var extraArgs = argv.join(" ");
cp.exec("node ../../bin/webpack.js "+extraArgs+" example.js js/output.js", function (error, stdout, stderr) {
console.log('stdout:\n' + stdout);
console.log('stderr:\n ' + stderr);
if (error !== null) {
console.log('error: ' + error);
}
});

View File

@ -0,0 +1,5 @@
function getTemplate(templateName) {
return require("./templates/"+templateName);
}
console.log(getTemplate("a"));
console.log(getTemplate("b"));

View File

@ -0,0 +1,3 @@
module.exports = function() {
return "This text was generated by template A";
}

View File

@ -0,0 +1,3 @@
module.exports = function() {
return "This text was generated by template B";
}

View File

@ -0,0 +1,3 @@
module.exports = function() {
return "This text was generated by template C";
}

View File

@ -45,7 +45,11 @@ module.exports = function buildDeps(context, mainModule, options, callback) {
}
function addModule(depTree, context, module, options, callback) {
resolve(context, module, options.resolve, function(err, filename) {
if(context)
resolve(context, module, options.resolve, resolved);
else
resolved(null, module);
function resolved(err, filename) {
if(err) {
callback(err);
return;
@ -66,24 +70,35 @@ function addModule(depTree, context, module, options, callback) {
var deps = parse(source);
module.requires = deps.requires || [];
module.asyncs = deps.asyncs || [];
module.contexts = deps.contexts || [];
module.source = source;
var requires = {};
var contexts = [];
function add(r) {
requires[r.name] = requires[r.name] || [];
requires[r.name].push(r);
}
function addContext(m) {
return function(c) {
contexts.push({context: c, module: m});
}
}
if(module.requires)
module.requires.forEach(add);
if(module.contexts)
module.contexts.forEach(addContext(module));
if(module.asyncs)
module.asyncs.forEach(function addContext(c) {
module.asyncs.forEach(function addAsync(c) {
if(c.requires)
c.requires.forEach(add);
if(c.asyncs)
c.asyncs.forEach(addContext);
c.asyncs.forEach(addAsync);
if(c.contexts)
c.contexts.forEach(addContext(c));
});
requiresNames = Object.keys(requires);
var count = requiresNames.length;
var count = requiresNames.length + contexts.length + 1;
var errors = [];
if(requiresNames.length)
requiresNames.forEach(function(moduleName) {
@ -95,23 +110,123 @@ function addModule(depTree, context, module, options, callback) {
requireItem.id = moduleId;
});
}
count--;
if(count === 0) {
if(errors.length) {
callback(errors.join("\n"));
} else {
end();
}
}
endOne();
});
});
else end()
function end() {
callback(null, module.id);
if(contexts) {
contexts.forEach(function(contextObj) {
var context = contextObj.context;
var module = contextObj.module;
addContextModule(depTree, path.dirname(filename), context.name, options, function(err, contextModuleId) {
if(err) {
errors.push(err+"\n @ " + filename + " (line " + context.line + ", column " + context.column + ")");
} else {
context.id = contextModuleId;
module.requires.push({id: context.id});
}
endOne();
})
});
}
endOne();
function endOne() {
count--;
if(count === 0) {
if(errors.length) {
callback(errors.join("\n"));
} else {
callback(null, module.id);
}
}
}
});
}
});
}
}
function addContextModule(depTree, context, contextModuleName, options, callback) {
resolve.context(context, contextModuleName, options.resolve, resolved);
function resolved(err, dirname) {
if(err) {
callback(err);
return;
}
if(depTree.modules[dirname]) {
callback(null, depTree.modules[dirname].id);
} else {
var contextModule = depTree.modules[dirname] = {
id: depTree.nextModuleId++,
requireMap: {},
requires: []
};
depTree.modulesById[contextModule.id] = contextModule;
function doDir(dirname, moduleName, done) {
fs.readdir(dirname, function(err, list) {
if(err) {
done(err);
} else {
var count = list.length + 1;
var errors = [];
function endOne(err) {
if(err) {
errors.push(err);
}
count--;
if(count == 0) {
if(errors.length > 0)
done(errors.join("\n"));
else
done();
}
}
list.forEach(function(file) {
var filename = path.join(dirname, file);
fs.stat(filename, function(err, stat) {
if(err) {
errors.push(err);
endOne();
} else {
if(stat.isDirectory()) {
doDir(filename, moduleName + "/" + file, endOne);
} else {
addModule(depTree, null, filename, options, function(err, moduleId) {
if(err) {
endOne(err);
} else {
contextModule.requires.push({id: moduleId});
contextModule.requireMap[moduleName + "/" + file] = moduleId;
endOne();
}
});
}
}
});
});
endOne();
}
});
}
doDir(dirname, ".", function(err) {
if(err) {
callback(err);
return;
}
var extensionsAccess = [];
var extensions = (options.resolve && options.resolve.extensions) || [".web.js", ".js"];
extensions.forEach(function(ext) {
extensionsAccess.push("||map[name+\"");
extensionsAccess.push(ext.replace(/"/g, "\\\""));
extensionsAccess.push("\"]");
});
contextModule.source = "/***/module.exports = function(name) {\n" +
"/***/\tvar map = " + JSON.stringify(contextModule.requireMap) + ";\n" +
"/***/\treturn require(map[name]" + extensionsAccess.join("") + ");\n" +
"/***/};";
callback(null, contextModule.id);
});
}
}
}
function addChunk(depTree, chunkStartpoint, options) {
@ -157,10 +272,10 @@ function addModuleToChunk(depTree, context, chunkId, options) {
function removeParentsModules(depTree, chunk) {
if(!chunk.parents) return;
for(var moduleId in chunk.modules) {
var inParent = false;
var inParent = true;
chunk.parents.forEach(function(parentId) {
if(depTree.chunks[parentId].modules[moduleId])
inParent = true;
if(!depTree.chunks[parentId].modules[moduleId])
inParent = false;
});
if(inParent) {
chunk.modules[moduleId] = "in-parent";

View File

@ -161,17 +161,40 @@ function walkExpression(context, expression) {
break;
case "CallExpression":
if(expression.callee && expression.arguments &&
expression.arguments.length >= 1 &&
expression.arguments.length == 1 &&
expression.callee.type === "Identifier" &&
expression.callee.name === "require") {
var param = parseString(expression.arguments[0]);
context.requires = context.requires || [];
context.requires.push({
name: param,
nameRange: expression.arguments[0].range,
line: expression.loc.start.line,
column: expression.loc.start.column
});
var param = parseCalculatedString(expression.arguments[0]);
if(param.code) {
// make context
var pos = param.value.indexOf("/");
if(pos === -1) {
throw new Error("require a module by variable is not supported");
} else {
var match = /\/[^\/]*$/.exec(param.value);
var dirname = param.value.substring(0, match.index);
var remainder = "." + param.value.substring(match.index);
context.contexts = context.contexts || [];
var newContext = {
name: dirname,
require: true,
replace: [param.range, remainder],
calleeRange: expression.callee.range,
line: expression.loc.start.line,
column: expression.loc.start.column
};
context.contexts.push(newContext);
}
} else {
// normal require
context.requires = context.requires || [];
context.requires.push({
name: param.value,
nameRange: param.range,
line: expression.loc.start.line,
column: expression.loc.start.column
});
}
}
if(expression.callee && expression.arguments &&
expression.arguments.length >= 1 &&
@ -194,6 +217,23 @@ function walkExpression(context, expression) {
context.asyncs.push(newContext);
context = newContext;
}
if(expression.callee && expression.arguments &&
expression.arguments.length == 1 &&
expression.callee.type === "MemberExpression" &&
expression.callee.object.type === "Identifier" &&
expression.callee.object.name === "require" &&
expression.callee.property.type === "Identifier" &&
expression.callee.property.name in {context:1}) {
var param = parseString(expression.arguments[0]);
context.contexts = context.contexts || [];
var newContext = {
name: param,
expressionRange: [expression.callee.range[0], expression.range[1]],
line: expression.loc.start.line,
column: expression.loc.start.column,
};
context.contexts.push(newContext);
}
if(expression.callee)
walkExpression(context, expression.callee);
@ -211,14 +251,37 @@ function walkExpression(context, expression) {
function parseString(expression) {
switch(expression.type) {
case "BinaryExpression":
return parseString(expression.left) + parseString(expression.right);
if(expression.operator == "+")
return parseString(expression.left) + parseString(expression.right);
break;
case "Literal":
if(typeof expression.value === "string")
return expression.value;
return expression.value+"";
}
throw new Error(expression.type + " is not supported as parameter for require");
}
function parseCalculatedString(expression) {
switch(expression.type) {
case "BinaryExpression":
if(expression.operator == "+") {
var left = parseCalculatedString(expression.left);
var right = parseCalculatedString(expression.right);
if(left.code) {
return {range: left.range, value: left.value, code: true};
} else if(right.code) {
return {range: [left.range[0], right.range ? right.range[1] : left.range[1]], value: left.value + right.value, code: true};
} else {
return {range: [left.range[0], right.range[1]], value: left.value + right.value};
}
}
break;
case "Literal":
return {range: expression.range, value: expression.value+""};
break;
}
return {value: "", code: true};
}
function parseStringArray(expression) {
switch(expression.type) {
case "ArrayExpression":
@ -233,7 +296,7 @@ function parseStringArray(expression) {
}
module.exports = function parse(source, options) {
var ast = esprima.parse(source, {range: true, loc: true});
var ast = esprima.parse(source, {range: true, loc: true, raw: true});
if(!ast || typeof ast != "object")
throw new Error("Source couldn't be parsed");
var context = {};

View File

@ -44,6 +44,42 @@ module.exports = function resolve(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);
}
}
function split(a) {
return a.split(/[\/\\]/g);
}
@ -114,6 +150,26 @@ function loadNodeModules(context, identifier, options, callback) {
});
}
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);
return;
}
tryDir(dirs.shift());
return;
}
callback(null, pathname);
});
}
tryDir(dirs.shift());
});
}
function nodeModulesPaths(context, options, callback) {
var parts = context;
var rootNodeModules = parts.indexOf("node_modules");

View File

@ -1,3 +1,7 @@
function stringify(str) {
return '"' + str.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"") + '"';
}
module.exports = function(module) {
var replaces = []; // { from: 123, to: 125, value: "4" }
function genReplaceRequire(requireItem) {
@ -9,9 +13,33 @@ module.exports = function(module) {
});
}
}
function genContextReplaces(contextItem) {
var postfix = "";
if(contextItem.require) {
replaces.push({
from: contextItem.calleeRange[0],
to: contextItem.calleeRange[1],
value: "require(" + ((contextItem.id || "throw new Error('there is not id for this')") + "") + ")"
});
replaces.push({
from: contextItem.replace[0][0],
to: contextItem.replace[0][1],
value: stringify(contextItem.replace[1])
});
} else {
replaces.push({
from: contextItem.expressionRange[0],
to: contextItem.expressionRange[1],
value: "require(" + ((contextItem.id || "throw new Error('there is not id for this')") + "") + ")" + postfix
});
}
}
if(module.requires) {
module.requires.forEach(genReplaceRequire);
}
if(module.contexts) {
module.contexts.forEach(genContextReplaces);
}
if(module.asyncs) {
module.asyncs.forEach(function genReplacesAsync(asyncItem) {
if(asyncItem.requires) {
@ -20,6 +48,9 @@ module.exports = function(module) {
if(asyncItem.asyncs) {
asyncItem.asyncs.forEach(genReplacesAsync);
}
if(asyncItem.contexts) {
asyncItem.contexts.forEach(genContextReplaces);
}
if(asyncItem.namesRange) {
replaces.push({
from: asyncItem.namesRange[0],

View File

@ -16,6 +16,15 @@ window.test(require("./singluar.js").value === 1, "sigular module loaded");
require("./singluar.js").value = 2;
window.test(require("./singluar").value === 2, "exported object is singluar");
window.test(require("subfilemodule") === "subfilemodule", "Modules as single file should load");
window.test(require.context("../templates")("./tmpl") === "test template", "Context should work");
window.test(require.context("../templates")("./subdir/tmpl.js") === "subdir test template", "Context should work with subdirectories");
var template = "tmpl";
window.test(require("../templates/" + template) === "test template", "Automatical context should work");
require.ensure([], function(require) {
var contextRequire = require.context(".");
window.test(contextRequire("./singluar").value === 2, "Context works in chunk");
});
require.ensure([], function(require) {
require("./acircular");

View File

@ -0,0 +1 @@
module.exports = "subdir test template";

View File

@ -0,0 +1 @@
module.exports = "test template";