simple-squiggle

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

LoaderPlugin.js (7979B)


      1 /*
      2 	MIT License http://www.opensource.org/licenses/mit-license.php
      3 	Author Tobias Koppers @sokra
      4 */
      5 
      6 "use strict";
      7 
      8 const NormalModule = require("../NormalModule");
      9 const LazySet = require("../util/LazySet");
     10 const LoaderDependency = require("./LoaderDependency");
     11 const LoaderImportDependency = require("./LoaderImportDependency");
     12 
     13 /** @typedef {import("../Compilation").DepConstructor} DepConstructor */
     14 /** @typedef {import("../Compiler")} Compiler */
     15 /** @typedef {import("../Module")} Module */
     16 
     17 /**
     18  * @callback LoadModuleCallback
     19  * @param {(Error | null)=} err error object
     20  * @param {string | Buffer=} source source code
     21  * @param {object=} map source map
     22  * @param {Module=} module loaded module if successful
     23  */
     24 
     25 /**
     26  * @callback ImportModuleCallback
     27  * @param {(Error | null)=} err error object
     28  * @param {any=} exports exports of the evaluated module
     29  */
     30 
     31 /**
     32  * @typedef {Object} ImportModuleOptions
     33  * @property {string=} layer the target layer
     34  * @property {string=} publicPath the target public path
     35  * @property {string=} baseUri target base uri
     36  */
     37 
     38 class LoaderPlugin {
     39 	/**
     40 	 * @param {Object} options options
     41 	 */
     42 	constructor(options = {}) {}
     43 
     44 	/**
     45 	 * Apply the plugin
     46 	 * @param {Compiler} compiler the compiler instance
     47 	 * @returns {void}
     48 	 */
     49 	apply(compiler) {
     50 		compiler.hooks.compilation.tap(
     51 			"LoaderPlugin",
     52 			(compilation, { normalModuleFactory }) => {
     53 				compilation.dependencyFactories.set(
     54 					LoaderDependency,
     55 					normalModuleFactory
     56 				);
     57 				compilation.dependencyFactories.set(
     58 					LoaderImportDependency,
     59 					normalModuleFactory
     60 				);
     61 			}
     62 		);
     63 
     64 		compiler.hooks.compilation.tap("LoaderPlugin", compilation => {
     65 			const moduleGraph = compilation.moduleGraph;
     66 			NormalModule.getCompilationHooks(compilation).loader.tap(
     67 				"LoaderPlugin",
     68 				loaderContext => {
     69 					/**
     70 					 * @param {string} request the request string to load the module from
     71 					 * @param {LoadModuleCallback} callback callback returning the loaded module or error
     72 					 * @returns {void}
     73 					 */
     74 					loaderContext.loadModule = (request, callback) => {
     75 						const dep = new LoaderDependency(request);
     76 						dep.loc = {
     77 							name: request
     78 						};
     79 						const factory = compilation.dependencyFactories.get(
     80 							/** @type {DepConstructor} */ (dep.constructor)
     81 						);
     82 						if (factory === undefined) {
     83 							return callback(
     84 								new Error(
     85 									`No module factory available for dependency type: ${dep.constructor.name}`
     86 								)
     87 							);
     88 						}
     89 						compilation.buildQueue.increaseParallelism();
     90 						compilation.handleModuleCreation(
     91 							{
     92 								factory,
     93 								dependencies: [dep],
     94 								originModule: loaderContext._module,
     95 								context: loaderContext.context,
     96 								recursive: false
     97 							},
     98 							err => {
     99 								compilation.buildQueue.decreaseParallelism();
    100 								if (err) {
    101 									return callback(err);
    102 								}
    103 								const referencedModule = moduleGraph.getModule(dep);
    104 								if (!referencedModule) {
    105 									return callback(new Error("Cannot load the module"));
    106 								}
    107 								if (referencedModule.getNumberOfErrors() > 0) {
    108 									return callback(
    109 										new Error("The loaded module contains errors")
    110 									);
    111 								}
    112 								const moduleSource = referencedModule.originalSource();
    113 								if (!moduleSource) {
    114 									return callback(
    115 										new Error(
    116 											"The module created for a LoaderDependency must have an original source"
    117 										)
    118 									);
    119 								}
    120 								let source, map;
    121 								if (moduleSource.sourceAndMap) {
    122 									const sourceAndMap = moduleSource.sourceAndMap();
    123 									map = sourceAndMap.map;
    124 									source = sourceAndMap.source;
    125 								} else {
    126 									map = moduleSource.map();
    127 									source = moduleSource.source();
    128 								}
    129 								const fileDependencies = new LazySet();
    130 								const contextDependencies = new LazySet();
    131 								const missingDependencies = new LazySet();
    132 								const buildDependencies = new LazySet();
    133 								referencedModule.addCacheDependencies(
    134 									fileDependencies,
    135 									contextDependencies,
    136 									missingDependencies,
    137 									buildDependencies
    138 								);
    139 
    140 								for (const d of fileDependencies) {
    141 									loaderContext.addDependency(d);
    142 								}
    143 								for (const d of contextDependencies) {
    144 									loaderContext.addContextDependency(d);
    145 								}
    146 								for (const d of missingDependencies) {
    147 									loaderContext.addMissingDependency(d);
    148 								}
    149 								for (const d of buildDependencies) {
    150 									loaderContext.addBuildDependency(d);
    151 								}
    152 								return callback(null, source, map, referencedModule);
    153 							}
    154 						);
    155 					};
    156 
    157 					/**
    158 					 * @param {string} request the request string to load the module from
    159 					 * @param {ImportModuleOptions=} options options
    160 					 * @param {ImportModuleCallback=} callback callback returning the exports
    161 					 * @returns {void}
    162 					 */
    163 					const importModule = (request, options, callback) => {
    164 						const dep = new LoaderImportDependency(request);
    165 						dep.loc = {
    166 							name: request
    167 						};
    168 						const factory = compilation.dependencyFactories.get(
    169 							/** @type {DepConstructor} */ (dep.constructor)
    170 						);
    171 						if (factory === undefined) {
    172 							return callback(
    173 								new Error(
    174 									`No module factory available for dependency type: ${dep.constructor.name}`
    175 								)
    176 							);
    177 						}
    178 						compilation.buildQueue.increaseParallelism();
    179 						compilation.handleModuleCreation(
    180 							{
    181 								factory,
    182 								dependencies: [dep],
    183 								originModule: loaderContext._module,
    184 								contextInfo: {
    185 									issuerLayer: options.layer
    186 								},
    187 								context: loaderContext.context,
    188 								connectOrigin: false
    189 							},
    190 							err => {
    191 								compilation.buildQueue.decreaseParallelism();
    192 								if (err) {
    193 									return callback(err);
    194 								}
    195 								const referencedModule = moduleGraph.getModule(dep);
    196 								if (!referencedModule) {
    197 									return callback(new Error("Cannot load the module"));
    198 								}
    199 								compilation.executeModule(
    200 									referencedModule,
    201 									{
    202 										entryOptions: {
    203 											baseUri: options.baseUri,
    204 											publicPath: options.publicPath
    205 										}
    206 									},
    207 									(err, result) => {
    208 										if (err) return callback(err);
    209 										for (const d of result.fileDependencies) {
    210 											loaderContext.addDependency(d);
    211 										}
    212 										for (const d of result.contextDependencies) {
    213 											loaderContext.addContextDependency(d);
    214 										}
    215 										for (const d of result.missingDependencies) {
    216 											loaderContext.addMissingDependency(d);
    217 										}
    218 										for (const d of result.buildDependencies) {
    219 											loaderContext.addBuildDependency(d);
    220 										}
    221 										if (result.cacheable === false)
    222 											loaderContext.cacheable(false);
    223 										for (const [name, { source, info }] of result.assets) {
    224 											const { buildInfo } = loaderContext._module;
    225 											if (!buildInfo.assets) {
    226 												buildInfo.assets = Object.create(null);
    227 												buildInfo.assetsInfo = new Map();
    228 											}
    229 											buildInfo.assets[name] = source;
    230 											buildInfo.assetsInfo.set(name, info);
    231 										}
    232 										callback(null, result.exports);
    233 									}
    234 								);
    235 							}
    236 						);
    237 					};
    238 
    239 					/**
    240 					 * @param {string} request the request string to load the module from
    241 					 * @param {ImportModuleOptions} options options
    242 					 * @param {ImportModuleCallback=} callback callback returning the exports
    243 					 * @returns {Promise<any> | void} exports
    244 					 */
    245 					loaderContext.importModule = (request, options, callback) => {
    246 						if (!callback) {
    247 							return new Promise((resolve, reject) => {
    248 								importModule(request, options || {}, (err, result) => {
    249 									if (err) reject(err);
    250 									else resolve(result);
    251 								});
    252 							});
    253 						}
    254 						return importModule(request, options || {}, callback);
    255 					};
    256 				}
    257 			);
    258 		});
    259 	}
    260 }
    261 module.exports = LoaderPlugin;