diff --git a/lib/WebpackOptionsApply.js b/lib/WebpackOptionsApply.js index c2f3cd90b..c16a785f5 100644 --- a/lib/WebpackOptionsApply.js +++ b/lib/WebpackOptionsApply.js @@ -78,10 +78,11 @@ WebpackOptionsApply.prototype.process = function(options, compiler) { ); break; case "node": + case "async-node": var NodeTemplatePlugin = require("./node/NodeTemplatePlugin"); var NodeTargetPlugin = require("./node/NodeTargetPlugin"); compiler.apply( - new NodeTemplatePlugin(options.output), + new NodeTemplatePlugin(options.output, options.target === "async-node"), new FunctionModulePlugin(options.context, options.output), new NodeTargetPlugin() ); diff --git a/lib/node/NodeMainTemplate.js b/lib/node/NodeMainTemplate.js index 0fc0ede0d..2410300e5 100644 --- a/lib/node/NodeMainTemplate.js +++ b/lib/node/NodeMainTemplate.js @@ -5,8 +5,9 @@ var MainTemplate = require("../MainTemplate"); var Template = require("../Template"); -function NodeMainTemplate(outputOptions) { +function NodeMainTemplate(outputOptions, asyncChunkLoading) { MainTemplate.call(this, outputOptions); + this.asyncChunkLoading = asyncChunkLoading; } module.exports = NodeMainTemplate; @@ -35,25 +36,63 @@ NodeMainTemplate.prototype.renderLocalVars = function(hash, chunk) { NodeMainTemplate.prototype.renderRequireEnsure = function(hash, chunk) { var filename = this.outputOptions.filename || "bundle.js"; var chunkFilename = this.outputOptions.chunkFilename || "[id]." + filename; - return [ - "// \"1\" is the signal for \"already loaded\"", - "if(!installedChunks[chunkId]) {", - this.indent([ - "var chunk = require(" + - JSON.stringify("./" + chunkFilename - .replace(Template.REGEXP_HASH, hash) - .replace(Template.REGEXP_NAME, "")) - .replace(Template.REGEXP_ID, "\" + chunkId + \"") + ");", - "var moreModules = chunk.modules, chunkIds = chunk.ids;", - "for(var moduleId in moreModules) {", - this.indent(this.renderAddModule(hash, chunk, "moduleId", "moreModules[moduleId]")), - "}", - "for(var i = 0; i < chunkIds.length; i++)", - this.indent("installedChunks[chunkIds[i]] = 1;"), - ]), - "}", - "callback.call(null, " + this.requireFn + ");", + var insertMoreModules = [ + "var moreModules = chunk.modules, chunkIds = chunk.ids;", + "for(var moduleId in moreModules) {", + this.indent(this.renderAddModule(hash, chunk, "moduleId", "moreModules[moduleId]")), + "}" ]; + if(this.asyncChunkLoading) { + return [ + "if(installedChunks[chunkId] === 1) callback.call(null, " + this.requireFn + ");", + "else if(!installedChunks[chunkId]) {", + this.indent([ + "installedChunks[chunkId] = [callback];", + "var filename = __dirname + " + JSON.stringify("/" + chunkFilename + .replace(Template.REGEXP_HASH, hash) + .replace(Template.REGEXP_NAME, "")) + .replace(Template.REGEXP_ID, "\" + chunkId + \"") + ";", + "require('fs').readFile(filename, 'utf-8', function(err, content) {", + this.indent([ + "if(err) { if(" + this.requireFn + ".onerror) return " + this.requireFn + ".onerror(err); else throw err; }", + "var chunk = {};", + "require('vm').runInThisContext('(function(exports) {' + content + '\\n})', filename)(chunk);", + ].concat(insertMoreModules).concat([ + "var callbacks = [];", + "for(var i = 0; i < chunkIds.length; i++) {", + this.indent([ + "if(Array.isArray(installedChunks[chunkIds[i]]))", + this.indent([ + "callbacks = callbacks.concat(installedChunks[chunkIds[i]]);" + ]), + "installedChunks[chunkIds[i]] = 1;" + ]), + "}", + "for(i = 0; i < callbacks.length; i++)", + this.indent("callbacks[i].call(null, " + this.requireFn + ");") + ])), + "});" + ]), + "} else installedChunks[chunkId].push(callback);", + ]; + } else { + return [ + "// \"1\" is the signal for \"already loaded\"", + "if(!installedChunks[chunkId]) {", + this.indent([ + "var chunk = require(" + + JSON.stringify("./" + chunkFilename + .replace(Template.REGEXP_HASH, hash) + .replace(Template.REGEXP_NAME, "")) + .replace(Template.REGEXP_ID, "\" + chunkId + \"") + ");" + ].concat(insertMoreModules).concat([ + "for(var i = 0; i < chunkIds.length; i++)", + this.indent("installedChunks[chunkIds[i]] = 1;"), + ])), + "}", + "callback.call(null, " + this.requireFn + ");", + ]; + } }; NodeMainTemplate.prototype.renderRequireExtensions = function(hash, chunk) { diff --git a/lib/node/NodeTemplatePlugin.js b/lib/node/NodeTemplatePlugin.js index a5bc9a572..b2d158460 100644 --- a/lib/node/NodeTemplatePlugin.js +++ b/lib/node/NodeTemplatePlugin.js @@ -5,13 +5,14 @@ var NodeMainTemplate = require("./NodeMainTemplate"); var NodeChunkTemplate = require("./NodeChunkTemplate"); -function NodeTemplatePlugin(options) { +function NodeTemplatePlugin(options, asyncChunkLoading) { this.options = options; + this.asyncChunkLoading = asyncChunkLoading; } module.exports = NodeTemplatePlugin; NodeTemplatePlugin.prototype.apply = function(compiler) { var options = this.options; - compiler.mainTemplate = new NodeMainTemplate(options); + compiler.mainTemplate = new NodeMainTemplate(options, this.asyncChunkLoading); compiler.chunkTemplate = new NodeChunkTemplate(options); compiler.plugin("compilation", function(compilation) { compilation.plugin("normal-module-loader", function(loaderContext) {