1 |
|
2 |
|
3 |
|
4 |
|
5 | "use strict";
|
6 |
|
7 | const { RawSource, ReplaceSource } = require("webpack-sources");
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | class JavascriptGenerator {
|
15 | generate(module, dependencyTemplates, runtimeTemplate) {
|
16 | const originalSource = module.originalSource();
|
17 | if (!originalSource) {
|
18 | return new RawSource("throw new Error('No source available');");
|
19 | }
|
20 |
|
21 | const source = new ReplaceSource(originalSource);
|
22 |
|
23 | this.sourceBlock(
|
24 | module,
|
25 | module,
|
26 | [],
|
27 | dependencyTemplates,
|
28 | source,
|
29 | runtimeTemplate
|
30 | );
|
31 |
|
32 | return source;
|
33 | }
|
34 |
|
35 | sourceBlock(
|
36 | module,
|
37 | block,
|
38 | availableVars,
|
39 | dependencyTemplates,
|
40 | source,
|
41 | runtimeTemplate
|
42 | ) {
|
43 | for (const dependency of block.dependencies) {
|
44 | this.sourceDependency(
|
45 | dependency,
|
46 | dependencyTemplates,
|
47 | source,
|
48 | runtimeTemplate
|
49 | );
|
50 | }
|
51 |
|
52 | |
53 |
|
54 |
|
55 |
|
56 |
|
57 | const vars = block.variables.reduce((result, value) => {
|
58 | const variable = this.sourceVariables(
|
59 | value,
|
60 | availableVars,
|
61 | dependencyTemplates,
|
62 | runtimeTemplate
|
63 | );
|
64 |
|
65 | if (variable) {
|
66 | result.push(variable);
|
67 | }
|
68 |
|
69 | return result;
|
70 | }, []);
|
71 |
|
72 | |
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 | if (vars.length > 0) {
|
79 | |
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 | const injectionVariableChunks = this.splitVariablesInUniqueNamedChunks(
|
94 | vars
|
95 | );
|
96 |
|
97 |
|
98 | const functionWrapperStarts = injectionVariableChunks.map(
|
99 | variableChunk => {
|
100 | return this.variableInjectionFunctionWrapperStartCode(
|
101 | variableChunk.map(variable => variable.name)
|
102 | );
|
103 | }
|
104 | );
|
105 |
|
106 |
|
107 | const functionWrapperEnds = injectionVariableChunks.map(variableChunk => {
|
108 | return this.variableInjectionFunctionWrapperEndCode(
|
109 | module,
|
110 | variableChunk.map(variable => variable.expression),
|
111 | block
|
112 | );
|
113 | });
|
114 |
|
115 |
|
116 | const varStartCode = functionWrapperStarts.join("");
|
117 |
|
118 |
|
119 | const varEndCode = functionWrapperEnds.reverse().join("");
|
120 |
|
121 |
|
122 | if (varStartCode && varEndCode) {
|
123 | const start = block.range ? block.range[0] : -10;
|
124 | const end = block.range
|
125 | ? block.range[1]
|
126 | : module.originalSource().size() + 1;
|
127 | source.insert(start + 0.5, varStartCode);
|
128 | source.insert(end + 0.5, "\n/* WEBPACK VAR INJECTION */" + varEndCode);
|
129 | }
|
130 | }
|
131 |
|
132 | for (const childBlock of block.blocks) {
|
133 | this.sourceBlock(
|
134 | module,
|
135 | childBlock,
|
136 | availableVars.concat(vars),
|
137 | dependencyTemplates,
|
138 | source,
|
139 | runtimeTemplate
|
140 | );
|
141 | }
|
142 | }
|
143 |
|
144 | sourceDependency(dependency, dependencyTemplates, source, runtimeTemplate) {
|
145 | const template = dependencyTemplates.get(dependency.constructor);
|
146 | if (!template) {
|
147 | throw new Error(
|
148 | "No template for dependency: " + dependency.constructor.name
|
149 | );
|
150 | }
|
151 | template.apply(dependency, source, runtimeTemplate, dependencyTemplates);
|
152 | }
|
153 |
|
154 | sourceVariables(
|
155 | variable,
|
156 | availableVars,
|
157 | dependencyTemplates,
|
158 | runtimeTemplate
|
159 | ) {
|
160 | const name = variable.name;
|
161 | const expr = variable.expressionSource(
|
162 | dependencyTemplates,
|
163 | runtimeTemplate
|
164 | );
|
165 |
|
166 | if (
|
167 | availableVars.some(
|
168 | v => v.name === name && v.expression.source() === expr.source()
|
169 | )
|
170 | ) {
|
171 | return;
|
172 | }
|
173 | return {
|
174 | name: name,
|
175 | expression: expr
|
176 | };
|
177 | }
|
178 |
|
179 | |
180 |
|
181 |
|
182 |
|
183 |
|
184 | variableInjectionFunctionWrapperStartCode(varNames) {
|
185 | const args = varNames.join(", ");
|
186 | return `/* WEBPACK VAR INJECTION */(function(${args}) {`;
|
187 | }
|
188 |
|
189 | contextArgument(module, block) {
|
190 | if (this === block) {
|
191 | return module.exportsArgument;
|
192 | }
|
193 | return "this";
|
194 | }
|
195 |
|
196 | |
197 |
|
198 |
|
199 |
|
200 |
|
201 | variableInjectionFunctionWrapperEndCode(module, varExpressions, block) {
|
202 | const firstParam = this.contextArgument(module, block);
|
203 | const furtherParams = varExpressions.map(e => e.source()).join(", ");
|
204 | return `}.call(${firstParam}, ${furtherParams}))`;
|
205 | }
|
206 |
|
207 | splitVariablesInUniqueNamedChunks(vars) {
|
208 | const startState = [[]];
|
209 | return vars.reduce((chunks, variable) => {
|
210 | const current = chunks[chunks.length - 1];
|
211 |
|
212 |
|
213 | const variableNameAlreadyExists = current.some(
|
214 | v => v.name === variable.name
|
215 | );
|
216 |
|
217 | if (variableNameAlreadyExists) {
|
218 |
|
219 | chunks.push([variable]);
|
220 | } else {
|
221 |
|
222 | current.push(variable);
|
223 | }
|
224 | return chunks;
|
225 | }, startState);
|
226 | }
|
227 | }
|
228 |
|
229 | module.exports = JavascriptGenerator;
|