UNPKG

webpack

Version:

Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.

322 lines (318 loc) 10.3 kB
/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ "use strict"; const Template = require("../Template"); module.exports = class NodeMainTemplatePlugin { constructor(asyncChunkLoading) { this.asyncChunkLoading = asyncChunkLoading; } apply(mainTemplate) { const needChunkOnDemandLoadingCode = chunk => { for (const chunkGroup of chunk.groupsIterable) { if (chunkGroup.getNumberOfChildren() > 0) return true; } return false; }; const asyncChunkLoading = this.asyncChunkLoading; mainTemplate.hooks.localVars.tap( "NodeMainTemplatePlugin", (source, chunk) => { if (needChunkOnDemandLoadingCode(chunk)) { return Template.asString([ source, "", "// object to store loaded chunks", '// "0" means "already loaded"', "var installedChunks = {", Template.indent( chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(",\n") ), "};" ]); } return source; } ); mainTemplate.hooks.requireExtensions.tap( "NodeMainTemplatePlugin", (source, chunk) => { if (needChunkOnDemandLoadingCode(chunk)) { return Template.asString([ source, "", "// uncaught error handler for webpack runtime", `${mainTemplate.requireFn}.oe = function(err) {`, Template.indent([ "process.nextTick(function() {", Template.indent( "throw err; // catch this error by using import().catch()" ), "});" ]), "};" ]); } return source; } ); mainTemplate.hooks.requireEnsure.tap( "NodeMainTemplatePlugin", (source, chunk, hash) => { const chunkFilename = mainTemplate.outputOptions.chunkFilename; const chunkMaps = chunk.getChunkMaps(); const insertMoreModules = [ "var moreModules = chunk.modules, chunkIds = chunk.ids;", "for(var moduleId in moreModules) {", Template.indent( mainTemplate.renderAddModule( hash, chunk, "moduleId", "moreModules[moduleId]" ) ), "}" ]; if (asyncChunkLoading) { return Template.asString([ source, "", "// ReadFile + VM.run chunk loading for javascript", "", "var installedChunkData = installedChunks[chunkId];", 'if(installedChunkData !== 0) { // 0 means "already installed".', Template.indent([ '// array of [resolve, reject, promise] means "currently loading"', "if(installedChunkData) {", Template.indent(["promises.push(installedChunkData[2]);"]), "} else {", Template.indent([ "// load the chunk and return promise to it", "var promise = new Promise(function(resolve, reject) {", Template.indent([ "installedChunkData = installedChunks[chunkId] = [resolve, reject];", "var filename = require('path').join(__dirname, " + mainTemplate.getAssetPath( JSON.stringify(`/${chunkFilename}`), { hash: `" + ${mainTemplate.renderCurrentHashCode( hash )} + "`, hashWithLength: length => `" + ${mainTemplate.renderCurrentHashCode( hash, length )} + "`, chunk: { id: '" + chunkId + "', hash: `" + ${JSON.stringify( chunkMaps.hash )}[chunkId] + "`, hashWithLength: length => { const shortChunkHashMap = {}; for (const chunkId of Object.keys(chunkMaps.hash)) { if (typeof chunkMaps.hash[chunkId] === "string") { shortChunkHashMap[chunkId] = chunkMaps.hash[ chunkId ].substr(0, length); } } return `" + ${JSON.stringify( shortChunkHashMap )}[chunkId] + "`; }, contentHash: { javascript: `" + ${JSON.stringify( chunkMaps.contentHash.javascript )}[chunkId] + "` }, contentHashWithLength: { javascript: length => { const shortContentHashMap = {}; const contentHash = chunkMaps.contentHash.javascript; for (const chunkId of Object.keys(contentHash)) { if (typeof contentHash[chunkId] === "string") { shortContentHashMap[chunkId] = contentHash[ chunkId ].substr(0, length); } } return `" + ${JSON.stringify( shortContentHashMap )}[chunkId] + "`; } }, name: `" + (${JSON.stringify( chunkMaps.name )}[chunkId]||chunkId) + "` }, contentHashType: "javascript" } ) + ");", "require('fs').readFile(filename, 'utf-8', function(err, content) {", Template.indent( [ "if(err) return reject(err);", "var chunk = {};", "require('vm').runInThisContext('(function(exports, require, __dirname, __filename) {' + content + '\\n})', filename)" + "(chunk, require, require('path').dirname(filename), filename);" ] .concat(insertMoreModules) .concat([ "var callbacks = [];", "for(var i = 0; i < chunkIds.length; i++) {", Template.indent([ "if(installedChunks[chunkIds[i]])", Template.indent([ "callbacks = callbacks.concat(installedChunks[chunkIds[i]][0]);" ]), "installedChunks[chunkIds[i]] = 0;" ]), "}", "for(i = 0; i < callbacks.length; i++)", Template.indent("callbacks[i]();") ]) ), "});" ]), "});", "promises.push(installedChunkData[2] = promise);" ]), "}" ]), "}" ]); } else { const request = mainTemplate.getAssetPath( JSON.stringify(`./${chunkFilename}`), { hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`, hashWithLength: length => `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`, chunk: { id: '" + chunkId + "', hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`, hashWithLength: length => { const shortChunkHashMap = {}; for (const chunkId of Object.keys(chunkMaps.hash)) { if (typeof chunkMaps.hash[chunkId] === "string") { shortChunkHashMap[chunkId] = chunkMaps.hash[ chunkId ].substr(0, length); } } return `" + ${JSON.stringify( shortChunkHashMap )}[chunkId] + "`; }, contentHash: { javascript: `" + ${JSON.stringify( chunkMaps.contentHash.javascript )}[chunkId] + "` }, contentHashWithLength: { javascript: length => { const shortContentHashMap = {}; const contentHash = chunkMaps.contentHash.javascript; for (const chunkId of Object.keys(contentHash)) { if (typeof contentHash[chunkId] === "string") { shortContentHashMap[chunkId] = contentHash[ chunkId ].substr(0, length); } } return `" + ${JSON.stringify( shortContentHashMap )}[chunkId] + "`; } }, name: `" + (${JSON.stringify( chunkMaps.name )}[chunkId]||chunkId) + "` }, contentHashType: "javascript" } ); return Template.asString([ source, "", "// require() chunk loading for javascript", "", '// "0" is the signal for "already loaded"', "if(installedChunks[chunkId] !== 0) {", Template.indent( [`var chunk = require(${request});`] .concat(insertMoreModules) .concat([ "for(var i = 0; i < chunkIds.length; i++)", Template.indent("installedChunks[chunkIds[i]] = 0;") ]) ), "}" ]); } } ); mainTemplate.hooks.hotBootstrap.tap( "NodeMainTemplatePlugin", (source, chunk, hash) => { const hotUpdateChunkFilename = mainTemplate.outputOptions.hotUpdateChunkFilename; const hotUpdateMainFilename = mainTemplate.outputOptions.hotUpdateMainFilename; const chunkMaps = chunk.getChunkMaps(); const currentHotUpdateChunkFilename = mainTemplate.getAssetPath( JSON.stringify(hotUpdateChunkFilename), { hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`, hashWithLength: length => `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`, chunk: { id: '" + chunkId + "', hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`, hashWithLength: length => { const shortChunkHashMap = {}; for (const chunkId of Object.keys(chunkMaps.hash)) { if (typeof chunkMaps.hash[chunkId] === "string") { shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substr( 0, length ); } } return `" + ${JSON.stringify(shortChunkHashMap)}[chunkId] + "`; }, name: `" + (${JSON.stringify( chunkMaps.name )}[chunkId]||chunkId) + "` } } ); const currentHotUpdateMainFilename = mainTemplate.getAssetPath( JSON.stringify(hotUpdateMainFilename), { hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`, hashWithLength: length => `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "` } ); return Template.getFunctionContent( asyncChunkLoading ? require("./NodeMainTemplateAsync.runtime") : require("./NodeMainTemplate.runtime") ) .replace(/\$require\$/g, mainTemplate.requireFn) .replace(/\$hotMainFilename\$/g, currentHotUpdateMainFilename) .replace(/\$hotChunkFilename\$/g, currentHotUpdateChunkFilename); } ); mainTemplate.hooks.hash.tap("NodeMainTemplatePlugin", hash => { hash.update("node"); hash.update("4"); }); } };