UNPKG

8.39 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra, Zackary Jackson @ScriptedAlchemy, Marais Rossouw @maraisr
4*/
5
6"use strict";
7
8const { OriginalSource, RawSource } = require("webpack-sources");
9const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
10const Module = require("../Module");
11const RuntimeGlobals = require("../RuntimeGlobals");
12const Template = require("../Template");
13const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
14const makeSerializable = require("../util/makeSerializable");
15const ContainerExposedDependency = require("./ContainerExposedDependency");
16
17/** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
18/** @typedef {import("../ChunkGraph")} ChunkGraph */
19/** @typedef {import("../ChunkGroup")} ChunkGroup */
20/** @typedef {import("../Compilation")} Compilation */
21/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
22/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
23/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */
24/** @typedef {import("../Module").NeedBuildContext} NeedBuildContext */
25/** @typedef {import("../RequestShortener")} RequestShortener */
26/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
27/** @typedef {import("../WebpackError")} WebpackError */
28/** @typedef {import("../util/Hash")} Hash */
29/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
30/** @typedef {import("./ContainerEntryDependency")} ContainerEntryDependency */
31
32/**
33 * @typedef {Object} ExposeOptions
34 * @property {string[]} import requests to exposed modules (last one is exported)
35 * @property {string} name custom chunk name for the exposed module
36 */
37
38const SOURCE_TYPES = new Set(["javascript"]);
39
40class ContainerEntryModule extends Module {
41 /**
42 * @param {string} name container entry name
43 * @param {[string, ExposeOptions][]} exposes list of exposed modules
44 * @param {string} shareScope name of the share scope
45 */
46 constructor(name, exposes, shareScope) {
47 super("javascript/dynamic", null);
48 this._name = name;
49 this._exposes = exposes;
50 this._shareScope = shareScope;
51 }
52
53 /**
54 * @returns {Set<string>} types available (do not mutate)
55 */
56 getSourceTypes() {
57 return SOURCE_TYPES;
58 }
59
60 /**
61 * @returns {string} a unique identifier of the module
62 */
63 identifier() {
64 return `container entry (${this._shareScope}) ${JSON.stringify(
65 this._exposes
66 )}`;
67 }
68
69 /**
70 * @param {RequestShortener} requestShortener the request shortener
71 * @returns {string} a user readable identifier of the module
72 */
73 readableIdentifier(requestShortener) {
74 return `container entry`;
75 }
76
77 /**
78 * @param {LibIdentOptions} options options
79 * @returns {string | null} an identifier for library inclusion
80 */
81 libIdent(options) {
82 return `${this.layer ? `(${this.layer})/` : ""}webpack/container/entry/${
83 this._name
84 }`;
85 }
86
87 /**
88 * @param {NeedBuildContext} context context info
89 * @param {function((WebpackError | null)=, boolean=): void} callback callback function, returns true, if the module needs a rebuild
90 * @returns {void}
91 */
92 needBuild(context, callback) {
93 return callback(null, !this.buildMeta);
94 }
95
96 /**
97 * @param {WebpackOptions} options webpack options
98 * @param {Compilation} compilation the compilation
99 * @param {ResolverWithOptions} resolver the resolver
100 * @param {InputFileSystem} fs the file system
101 * @param {function(WebpackError=): void} callback callback function
102 * @returns {void}
103 */
104 build(options, compilation, resolver, fs, callback) {
105 this.buildMeta = {};
106 this.buildInfo = {
107 strict: true,
108 topLevelDeclarations: new Set(["moduleMap", "get", "init"])
109 };
110 this.buildMeta.exportsType = "namespace";
111
112 this.clearDependenciesAndBlocks();
113
114 for (const [name, options] of this._exposes) {
115 const block = new AsyncDependenciesBlock(
116 {
117 name: options.name
118 },
119 { name },
120 options.import[options.import.length - 1]
121 );
122 let idx = 0;
123 for (const request of options.import) {
124 const dep = new ContainerExposedDependency(name, request);
125 dep.loc = {
126 name,
127 index: idx++
128 };
129
130 block.addDependency(dep);
131 }
132 this.addBlock(block);
133 }
134 this.addDependency(new StaticExportsDependency(["get", "init"], false));
135
136 callback();
137 }
138
139 /**
140 * @param {CodeGenerationContext} context context for code generation
141 * @returns {CodeGenerationResult} result
142 */
143 codeGeneration({ moduleGraph, chunkGraph, runtimeTemplate }) {
144 const sources = new Map();
145 const runtimeRequirements = new Set([
146 RuntimeGlobals.definePropertyGetters,
147 RuntimeGlobals.hasOwnProperty,
148 RuntimeGlobals.exports
149 ]);
150 const getters = [];
151
152 for (const block of this.blocks) {
153 const { dependencies } = block;
154
155 const modules = dependencies.map(dependency => {
156 const dep = /** @type {ContainerExposedDependency} */ (dependency);
157 return {
158 name: dep.exposedName,
159 module: moduleGraph.getModule(dep),
160 request: dep.userRequest
161 };
162 });
163
164 let str;
165
166 if (modules.some(m => !m.module)) {
167 str = runtimeTemplate.throwMissingModuleErrorBlock({
168 request: modules.map(m => m.request).join(", ")
169 });
170 } else {
171 str = `return ${runtimeTemplate.blockPromise({
172 block,
173 message: "",
174 chunkGraph,
175 runtimeRequirements
176 })}.then(${runtimeTemplate.returningFunction(
177 runtimeTemplate.returningFunction(
178 `(${modules
179 .map(({ module, request }) =>
180 runtimeTemplate.moduleRaw({
181 module,
182 chunkGraph,
183 request,
184 weak: false,
185 runtimeRequirements
186 })
187 )
188 .join(", ")})`
189 )
190 )});`;
191 }
192
193 getters.push(
194 `${JSON.stringify(modules[0].name)}: ${runtimeTemplate.basicFunction(
195 "",
196 str
197 )}`
198 );
199 }
200
201 const source = Template.asString([
202 `var moduleMap = {`,
203 Template.indent(getters.join(",\n")),
204 "};",
205 `var get = ${runtimeTemplate.basicFunction("module, getScope", [
206 `${RuntimeGlobals.currentRemoteGetScope} = getScope;`,
207 // reusing the getScope variable to avoid creating a new var (and module is also used later)
208 "getScope = (",
209 Template.indent([
210 `${RuntimeGlobals.hasOwnProperty}(moduleMap, module)`,
211 Template.indent([
212 "? moduleMap[module]()",
213 `: Promise.resolve().then(${runtimeTemplate.basicFunction(
214 "",
215 "throw new Error('Module \"' + module + '\" does not exist in container.');"
216 )})`
217 ])
218 ]),
219 ");",
220 `${RuntimeGlobals.currentRemoteGetScope} = undefined;`,
221 "return getScope;"
222 ])};`,
223 `var init = ${runtimeTemplate.basicFunction("shareScope, initScope", [
224 `if (!${RuntimeGlobals.shareScopeMap}) return;`,
225 `var name = ${JSON.stringify(this._shareScope)}`,
226 `var oldScope = ${RuntimeGlobals.shareScopeMap}[name];`,
227 `if(oldScope && oldScope !== shareScope) throw new Error("Container initialization failed as it has already been initialized with a different share scope");`,
228 `${RuntimeGlobals.shareScopeMap}[name] = shareScope;`,
229 `return ${RuntimeGlobals.initializeSharing}(name, initScope);`
230 ])};`,
231 "",
232 "// This exports getters to disallow modifications",
233 `${RuntimeGlobals.definePropertyGetters}(exports, {`,
234 Template.indent([
235 `get: ${runtimeTemplate.returningFunction("get")},`,
236 `init: ${runtimeTemplate.returningFunction("init")}`
237 ]),
238 "});"
239 ]);
240
241 sources.set(
242 "javascript",
243 this.useSourceMap || this.useSimpleSourceMap
244 ? new OriginalSource(source, "webpack/container-entry")
245 : new RawSource(source)
246 );
247
248 return {
249 sources,
250 runtimeRequirements
251 };
252 }
253
254 /**
255 * @param {string=} type the source type for which the size should be estimated
256 * @returns {number} the estimated size of the module (must be non-zero)
257 */
258 size(type) {
259 return 42;
260 }
261
262 serialize(context) {
263 const { write } = context;
264 write(this._name);
265 write(this._exposes);
266 write(this._shareScope);
267 super.serialize(context);
268 }
269
270 static deserialize(context) {
271 const { read } = context;
272 const obj = new ContainerEntryModule(read(), read(), read());
273 obj.deserialize(context);
274 return obj;
275 }
276}
277
278makeSerializable(
279 ContainerEntryModule,
280 "webpack/lib/container/ContainerEntryModule"
281);
282
283module.exports = ContainerEntryModule;