simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

ReadFileChunkLoadingRuntimeModule.js (9798B)


      1 /*
      2 	MIT License http://www.opensource.org/licenses/mit-license.php
      3 */
      4 
      5 "use strict";
      6 
      7 const RuntimeGlobals = require("../RuntimeGlobals");
      8 const RuntimeModule = require("../RuntimeModule");
      9 const Template = require("../Template");
     10 const {
     11 	chunkHasJs,
     12 	getChunkFilenameTemplate
     13 } = require("../javascript/JavascriptModulesPlugin");
     14 const { getInitialChunkIds } = require("../javascript/StartupHelpers");
     15 const compileBooleanMatcher = require("../util/compileBooleanMatcher");
     16 const { getUndoPath } = require("../util/identifier");
     17 
     18 /** @typedef {import("../Chunk")} Chunk */
     19 
     20 class ReadFileChunkLoadingRuntimeModule extends RuntimeModule {
     21 	constructor(runtimeRequirements) {
     22 		super("readFile chunk loading", RuntimeModule.STAGE_ATTACH);
     23 		this.runtimeRequirements = runtimeRequirements;
     24 	}
     25 
     26 	/**
     27 	 * @private
     28 	 * @param {Chunk} chunk chunk
     29 	 * @param {string} rootOutputDir root output directory
     30 	 * @returns {string} generated code
     31 	 */
     32 	_generateBaseUri(chunk, rootOutputDir) {
     33 		const options = chunk.getEntryOptions();
     34 		if (options && options.baseUri) {
     35 			return `${RuntimeGlobals.baseURI} = ${JSON.stringify(options.baseUri)};`;
     36 		}
     37 
     38 		return `${RuntimeGlobals.baseURI} = require("url").pathToFileURL(${
     39 			rootOutputDir
     40 				? `__dirname + ${JSON.stringify("/" + rootOutputDir)}`
     41 				: "__filename"
     42 		});`;
     43 	}
     44 
     45 	/**
     46 	 * @returns {string} runtime code
     47 	 */
     48 	generate() {
     49 		const { chunkGraph, chunk } = this;
     50 		const { runtimeTemplate } = this.compilation;
     51 		const fn = RuntimeGlobals.ensureChunkHandlers;
     52 		const withBaseURI = this.runtimeRequirements.has(RuntimeGlobals.baseURI);
     53 		const withExternalInstallChunk = this.runtimeRequirements.has(
     54 			RuntimeGlobals.externalInstallChunk
     55 		);
     56 		const withOnChunkLoad = this.runtimeRequirements.has(
     57 			RuntimeGlobals.onChunksLoaded
     58 		);
     59 		const withLoading = this.runtimeRequirements.has(
     60 			RuntimeGlobals.ensureChunkHandlers
     61 		);
     62 		const withHmr = this.runtimeRequirements.has(
     63 			RuntimeGlobals.hmrDownloadUpdateHandlers
     64 		);
     65 		const withHmrManifest = this.runtimeRequirements.has(
     66 			RuntimeGlobals.hmrDownloadManifest
     67 		);
     68 		const conditionMap = chunkGraph.getChunkConditionMap(chunk, chunkHasJs);
     69 		const hasJsMatcher = compileBooleanMatcher(conditionMap);
     70 		const initialChunkIds = getInitialChunkIds(chunk, chunkGraph, chunkHasJs);
     71 
     72 		const outputName = this.compilation.getPath(
     73 			getChunkFilenameTemplate(chunk, this.compilation.outputOptions),
     74 			{
     75 				chunk,
     76 				contentHashType: "javascript"
     77 			}
     78 		);
     79 		const rootOutputDir = getUndoPath(
     80 			outputName,
     81 			this.compilation.outputOptions.path,
     82 			false
     83 		);
     84 
     85 		const stateExpression = withHmr
     86 			? `${RuntimeGlobals.hmrRuntimeStatePrefix}_readFileVm`
     87 			: undefined;
     88 
     89 		return Template.asString([
     90 			withBaseURI
     91 				? this._generateBaseUri(chunk, rootOutputDir)
     92 				: "// no baseURI",
     93 			"",
     94 			"// object to store loaded chunks",
     95 			'// "0" means "already loaded", Promise means loading',
     96 			`var installedChunks = ${
     97 				stateExpression ? `${stateExpression} = ${stateExpression} || ` : ""
     98 			}{`,
     99 			Template.indent(
    100 				Array.from(initialChunkIds, id => `${JSON.stringify(id)}: 0`).join(
    101 					",\n"
    102 				)
    103 			),
    104 			"};",
    105 			"",
    106 			withOnChunkLoad
    107 				? `${
    108 						RuntimeGlobals.onChunksLoaded
    109 				  }.readFileVm = ${runtimeTemplate.returningFunction(
    110 						"installedChunks[chunkId] === 0",
    111 						"chunkId"
    112 				  )};`
    113 				: "// no on chunks loaded",
    114 			"",
    115 			withLoading || withExternalInstallChunk
    116 				? `var installChunk = ${runtimeTemplate.basicFunction("chunk", [
    117 						"var moreModules = chunk.modules, chunkIds = chunk.ids, runtime = chunk.runtime;",
    118 						"for(var moduleId in moreModules) {",
    119 						Template.indent([
    120 							`if(${RuntimeGlobals.hasOwnProperty}(moreModules, moduleId)) {`,
    121 							Template.indent([
    122 								`${RuntimeGlobals.moduleFactories}[moduleId] = moreModules[moduleId];`
    123 							]),
    124 							"}"
    125 						]),
    126 						"}",
    127 						`if(runtime) runtime(__webpack_require__);`,
    128 						"for(var i = 0; i < chunkIds.length; i++) {",
    129 						Template.indent([
    130 							"if(installedChunks[chunkIds[i]]) {",
    131 							Template.indent(["installedChunks[chunkIds[i]][0]();"]),
    132 							"}",
    133 							"installedChunks[chunkIds[i]] = 0;"
    134 						]),
    135 						"}",
    136 						withOnChunkLoad ? `${RuntimeGlobals.onChunksLoaded}();` : ""
    137 				  ])};`
    138 				: "// no chunk install function needed",
    139 			"",
    140 			withLoading
    141 				? Template.asString([
    142 						"// ReadFile + VM.run chunk loading for javascript",
    143 						`${fn}.readFileVm = function(chunkId, promises) {`,
    144 						hasJsMatcher !== false
    145 							? Template.indent([
    146 									"",
    147 									"var installedChunkData = installedChunks[chunkId];",
    148 									'if(installedChunkData !== 0) { // 0 means "already installed".',
    149 									Template.indent([
    150 										'// array of [resolve, reject, promise] means "currently loading"',
    151 										"if(installedChunkData) {",
    152 										Template.indent(["promises.push(installedChunkData[2]);"]),
    153 										"} else {",
    154 										Template.indent([
    155 											hasJsMatcher === true
    156 												? "if(true) { // all chunks have JS"
    157 												: `if(${hasJsMatcher("chunkId")}) {`,
    158 											Template.indent([
    159 												"// load the chunk and return promise to it",
    160 												"var promise = new Promise(function(resolve, reject) {",
    161 												Template.indent([
    162 													"installedChunkData = installedChunks[chunkId] = [resolve, reject];",
    163 													`var filename = require('path').join(__dirname, ${JSON.stringify(
    164 														rootOutputDir
    165 													)} + ${
    166 														RuntimeGlobals.getChunkScriptFilename
    167 													}(chunkId));`,
    168 													"require('fs').readFile(filename, 'utf-8', function(err, content) {",
    169 													Template.indent([
    170 														"if(err) return reject(err);",
    171 														"var chunk = {};",
    172 														"require('vm').runInThisContext('(function(exports, require, __dirname, __filename) {' + content + '\\n})', filename)" +
    173 															"(chunk, require, require('path').dirname(filename), filename);",
    174 														"installChunk(chunk);"
    175 													]),
    176 													"});"
    177 												]),
    178 												"});",
    179 												"promises.push(installedChunkData[2] = promise);"
    180 											]),
    181 											"} else installedChunks[chunkId] = 0;"
    182 										]),
    183 										"}"
    184 									]),
    185 									"}"
    186 							  ])
    187 							: Template.indent(["installedChunks[chunkId] = 0;"]),
    188 						"};"
    189 				  ])
    190 				: "// no chunk loading",
    191 			"",
    192 			withExternalInstallChunk
    193 				? Template.asString([
    194 						"module.exports = __webpack_require__;",
    195 						`${RuntimeGlobals.externalInstallChunk} = installChunk;`
    196 				  ])
    197 				: "// no external install chunk",
    198 			"",
    199 			withHmr
    200 				? Template.asString([
    201 						"function loadUpdateChunk(chunkId, updatedModulesList) {",
    202 						Template.indent([
    203 							"return new Promise(function(resolve, reject) {",
    204 							Template.indent([
    205 								`var filename = require('path').join(__dirname, ${JSON.stringify(
    206 									rootOutputDir
    207 								)} + ${RuntimeGlobals.getChunkUpdateScriptFilename}(chunkId));`,
    208 								"require('fs').readFile(filename, 'utf-8', function(err, content) {",
    209 								Template.indent([
    210 									"if(err) return reject(err);",
    211 									"var update = {};",
    212 									"require('vm').runInThisContext('(function(exports, require, __dirname, __filename) {' + content + '\\n})', filename)" +
    213 										"(update, require, require('path').dirname(filename), filename);",
    214 									"var updatedModules = update.modules;",
    215 									"var runtime = update.runtime;",
    216 									"for(var moduleId in updatedModules) {",
    217 									Template.indent([
    218 										`if(${RuntimeGlobals.hasOwnProperty}(updatedModules, moduleId)) {`,
    219 										Template.indent([
    220 											`currentUpdate[moduleId] = updatedModules[moduleId];`,
    221 											"if(updatedModulesList) updatedModulesList.push(moduleId);"
    222 										]),
    223 										"}"
    224 									]),
    225 									"}",
    226 									"if(runtime) currentUpdateRuntime.push(runtime);",
    227 									"resolve();"
    228 								]),
    229 								"});"
    230 							]),
    231 							"});"
    232 						]),
    233 						"}",
    234 						"",
    235 						Template.getFunctionContent(
    236 							require("../hmr/JavascriptHotModuleReplacement.runtime.js")
    237 						)
    238 							.replace(/\$key\$/g, "readFileVm")
    239 							.replace(/\$installedChunks\$/g, "installedChunks")
    240 							.replace(/\$loadUpdateChunk\$/g, "loadUpdateChunk")
    241 							.replace(/\$moduleCache\$/g, RuntimeGlobals.moduleCache)
    242 							.replace(/\$moduleFactories\$/g, RuntimeGlobals.moduleFactories)
    243 							.replace(
    244 								/\$ensureChunkHandlers\$/g,
    245 								RuntimeGlobals.ensureChunkHandlers
    246 							)
    247 							.replace(/\$hasOwnProperty\$/g, RuntimeGlobals.hasOwnProperty)
    248 							.replace(/\$hmrModuleData\$/g, RuntimeGlobals.hmrModuleData)
    249 							.replace(
    250 								/\$hmrDownloadUpdateHandlers\$/g,
    251 								RuntimeGlobals.hmrDownloadUpdateHandlers
    252 							)
    253 							.replace(
    254 								/\$hmrInvalidateModuleHandlers\$/g,
    255 								RuntimeGlobals.hmrInvalidateModuleHandlers
    256 							)
    257 				  ])
    258 				: "// no HMR",
    259 			"",
    260 			withHmrManifest
    261 				? Template.asString([
    262 						`${RuntimeGlobals.hmrDownloadManifest} = function() {`,
    263 						Template.indent([
    264 							"return new Promise(function(resolve, reject) {",
    265 							Template.indent([
    266 								`var filename = require('path').join(__dirname, ${JSON.stringify(
    267 									rootOutputDir
    268 								)} + ${RuntimeGlobals.getUpdateManifestFilename}());`,
    269 								"require('fs').readFile(filename, 'utf-8', function(err, content) {",
    270 								Template.indent([
    271 									"if(err) {",
    272 									Template.indent([
    273 										'if(err.code === "ENOENT") return resolve();',
    274 										"return reject(err);"
    275 									]),
    276 									"}",
    277 									"try { resolve(JSON.parse(content)); }",
    278 									"catch(e) { reject(e); }"
    279 								]),
    280 								"});"
    281 							]),
    282 							"});"
    283 						]),
    284 						"}"
    285 				  ])
    286 				: "// no HMR manifest"
    287 		]);
    288 	}
    289 }
    290 
    291 module.exports = ReadFileChunkLoadingRuntimeModule;