SystemLibraryPlugin.js (7165B)
1 /* 2 MIT License http://www.opensource.org/licenses/mit-license.php 3 Author Joel Denning @joeldenning 4 */ 5 6 "use strict"; 7 8 const { ConcatSource } = require("webpack-sources"); 9 const { UsageState } = require("../ExportsInfo"); 10 const ExternalModule = require("../ExternalModule"); 11 const Template = require("../Template"); 12 const propertyAccess = require("../util/propertyAccess"); 13 const AbstractLibraryPlugin = require("./AbstractLibraryPlugin"); 14 15 /** @typedef {import("webpack-sources").Source} Source */ 16 /** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */ 17 /** @typedef {import("../../declarations/WebpackOptions").LibraryType} LibraryType */ 18 /** @typedef {import("../Chunk")} Chunk */ 19 /** @typedef {import("../Compilation").ChunkHashContext} ChunkHashContext */ 20 /** @typedef {import("../Compiler")} Compiler */ 21 /** @typedef {import("../javascript/JavascriptModulesPlugin").RenderContext} RenderContext */ 22 /** @typedef {import("../util/Hash")} Hash */ 23 /** @template T @typedef {import("./AbstractLibraryPlugin").LibraryContext<T>} LibraryContext<T> */ 24 25 /** 26 * @typedef {Object} SystemLibraryPluginOptions 27 * @property {LibraryType} type 28 */ 29 30 /** 31 * @typedef {Object} SystemLibraryPluginParsed 32 * @property {string} name 33 */ 34 35 /** 36 * @typedef {SystemLibraryPluginParsed} T 37 * @extends {AbstractLibraryPlugin<SystemLibraryPluginParsed>} 38 */ 39 class SystemLibraryPlugin extends AbstractLibraryPlugin { 40 /** 41 * @param {SystemLibraryPluginOptions} options the plugin options 42 */ 43 constructor(options) { 44 super({ 45 pluginName: "SystemLibraryPlugin", 46 type: options.type 47 }); 48 } 49 50 /** 51 * @param {LibraryOptions} library normalized library option 52 * @returns {T | false} preprocess as needed by overriding 53 */ 54 parseOptions(library) { 55 const { name } = library; 56 if (name && typeof name !== "string") { 57 throw new Error( 58 `System.js library name must be a simple string or unset. ${AbstractLibraryPlugin.COMMON_LIBRARY_NAME_MESSAGE}` 59 ); 60 } 61 return { 62 name: /** @type {string=} */ (name) 63 }; 64 } 65 66 /** 67 * @param {Source} source source 68 * @param {RenderContext} renderContext render context 69 * @param {LibraryContext<T>} libraryContext context 70 * @returns {Source} source with library export 71 */ 72 render(source, { chunkGraph, moduleGraph, chunk }, { options, compilation }) { 73 const modules = chunkGraph 74 .getChunkModules(chunk) 75 .filter(m => m instanceof ExternalModule && m.externalType === "system"); 76 const externals = /** @type {ExternalModule[]} */ (modules); 77 78 // The name this bundle should be registered as with System 79 const name = options.name 80 ? `${JSON.stringify(compilation.getPath(options.name, { chunk }))}, ` 81 : ""; 82 83 // The array of dependencies that are external to webpack and will be provided by System 84 const systemDependencies = JSON.stringify( 85 externals.map(m => 86 typeof m.request === "object" && !Array.isArray(m.request) 87 ? m.request.amd 88 : m.request 89 ) 90 ); 91 92 // The name of the variable provided by System for exporting 93 const dynamicExport = "__WEBPACK_DYNAMIC_EXPORT__"; 94 95 // An array of the internal variable names for the webpack externals 96 const externalWebpackNames = externals.map( 97 m => 98 `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier( 99 `${chunkGraph.getModuleId(m)}` 100 )}__` 101 ); 102 103 // Declaring variables for the internal variable names for the webpack externals 104 const externalVarDeclarations = externalWebpackNames 105 .map(name => `var ${name} = {};`) 106 .join("\n"); 107 108 // Define __esModule flag on all internal variables and helpers 109 const externalVarInitialization = []; 110 111 // The system.register format requires an array of setter functions for externals. 112 const setters = 113 externalWebpackNames.length === 0 114 ? "" 115 : Template.asString([ 116 "setters: [", 117 Template.indent( 118 externals 119 .map((module, i) => { 120 const external = externalWebpackNames[i]; 121 const exportsInfo = moduleGraph.getExportsInfo(module); 122 const otherUnused = 123 exportsInfo.otherExportsInfo.getUsed(chunk.runtime) === 124 UsageState.Unused; 125 const instructions = []; 126 const handledNames = []; 127 for (const exportInfo of exportsInfo.orderedExports) { 128 const used = exportInfo.getUsedName( 129 undefined, 130 chunk.runtime 131 ); 132 if (used) { 133 if (otherUnused || used !== exportInfo.name) { 134 instructions.push( 135 `${external}${propertyAccess([ 136 used 137 ])} = module${propertyAccess([exportInfo.name])};` 138 ); 139 handledNames.push(exportInfo.name); 140 } 141 } else { 142 handledNames.push(exportInfo.name); 143 } 144 } 145 if (!otherUnused) { 146 if ( 147 !Array.isArray(module.request) || 148 module.request.length === 1 149 ) { 150 externalVarInitialization.push( 151 `Object.defineProperty(${external}, "__esModule", { value: true });` 152 ); 153 } 154 if (handledNames.length > 0) { 155 const name = `${external}handledNames`; 156 externalVarInitialization.push( 157 `var ${name} = ${JSON.stringify(handledNames)};` 158 ); 159 instructions.push( 160 Template.asString([ 161 "Object.keys(module).forEach(function(key) {", 162 Template.indent([ 163 `if(${name}.indexOf(key) >= 0)`, 164 Template.indent(`${external}[key] = module[key];`) 165 ]), 166 "});" 167 ]) 168 ); 169 } else { 170 instructions.push( 171 Template.asString([ 172 "Object.keys(module).forEach(function(key) {", 173 Template.indent([`${external}[key] = module[key];`]), 174 "});" 175 ]) 176 ); 177 } 178 } 179 if (instructions.length === 0) return "function() {}"; 180 return Template.asString([ 181 "function(module) {", 182 Template.indent(instructions), 183 "}" 184 ]); 185 }) 186 .join(",\n") 187 ), 188 "]," 189 ]); 190 191 return new ConcatSource( 192 Template.asString([ 193 `System.register(${name}${systemDependencies}, function(${dynamicExport}, __system_context__) {`, 194 Template.indent([ 195 externalVarDeclarations, 196 Template.asString(externalVarInitialization), 197 "return {", 198 Template.indent([ 199 setters, 200 "execute: function() {", 201 Template.indent(`${dynamicExport}(`) 202 ]) 203 ]), 204 "" 205 ]), 206 source, 207 Template.asString([ 208 "", 209 Template.indent([ 210 Template.indent([Template.indent([");"]), "}"]), 211 "};" 212 ]), 213 "})" 214 ]) 215 ); 216 } 217 218 /** 219 * @param {Chunk} chunk the chunk 220 * @param {Hash} hash hash 221 * @param {ChunkHashContext} chunkHashContext chunk hash context 222 * @param {LibraryContext<T>} libraryContext context 223 * @returns {void} 224 */ 225 chunkHash(chunk, hash, chunkHashContext, { options, compilation }) { 226 hash.update("SystemLibraryPlugin"); 227 if (options.name) { 228 hash.update(compilation.getPath(options.name, { chunk })); 229 } 230 } 231 } 232 233 module.exports = SystemLibraryPlugin;