1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | "use strict";
|
7 |
|
8 | const RuntimeGlobals = require("./RuntimeGlobals");
|
9 | const ConstDependency = require("./dependencies/ConstDependency");
|
10 | const BasicEvaluatedExpression = require("./javascript/BasicEvaluatedExpression");
|
11 | const {
|
12 | approve,
|
13 | evaluateToString,
|
14 | toConstantDependency
|
15 | } = require("./javascript/JavascriptParserHelpers");
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 | class RuntimeValue {
|
26 | constructor(fn, fileDependencies) {
|
27 | this.fn = fn;
|
28 | this.fileDependencies = fileDependencies || [];
|
29 | }
|
30 |
|
31 | exec(parser) {
|
32 | const buildInfo = parser.state.module.buildInfo;
|
33 | if (this.fileDependencies === true) {
|
34 | buildInfo.cacheable = false;
|
35 | } else {
|
36 | for (const fileDependency of this.fileDependencies) {
|
37 | buildInfo.fileDependencies.add(fileDependency);
|
38 | }
|
39 | }
|
40 |
|
41 | return this.fn({ module: parser.state.module });
|
42 | }
|
43 | }
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 | const stringifyObj = (obj, parser, runtimeTemplate, asiSafe) => {
|
53 | let code;
|
54 | let arr = Array.isArray(obj);
|
55 | if (arr) {
|
56 | code = `[${obj
|
57 | .map(code => toCode(code, parser, runtimeTemplate, null))
|
58 | .join(",")}]`;
|
59 | } else {
|
60 | code = `{${Object.keys(obj)
|
61 | .map(key => {
|
62 | const code = obj[key];
|
63 | return (
|
64 | JSON.stringify(key) +
|
65 | ":" +
|
66 | toCode(code, parser, runtimeTemplate, null)
|
67 | );
|
68 | })
|
69 | .join(",")}}`;
|
70 | }
|
71 |
|
72 | switch (asiSafe) {
|
73 | case null:
|
74 | return code;
|
75 | case true:
|
76 | return arr ? code : `(${code})`;
|
77 | case false:
|
78 | return arr ? `;${code}` : `;(${code})`;
|
79 | default:
|
80 | return `Object(${code})`;
|
81 | }
|
82 | };
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 | const toCode = (code, parser, runtimeTemplate, asiSafe) => {
|
93 | if (code === null) {
|
94 | return "null";
|
95 | }
|
96 | if (code === undefined) {
|
97 | return "undefined";
|
98 | }
|
99 | if (Object.is(code, -0)) {
|
100 | return "-0";
|
101 | }
|
102 | if (code instanceof RuntimeValue) {
|
103 | return toCode(code.exec(parser), parser, runtimeTemplate, asiSafe);
|
104 | }
|
105 | if (code instanceof RegExp && code.toString) {
|
106 | return code.toString();
|
107 | }
|
108 | if (typeof code === "function" && code.toString) {
|
109 | return "(" + code.toString() + ")";
|
110 | }
|
111 | if (typeof code === "object") {
|
112 | return stringifyObj(code, parser, runtimeTemplate, asiSafe);
|
113 | }
|
114 | if (typeof code === "bigint") {
|
115 | return runtimeTemplate.supportsBigIntLiteral()
|
116 | ? `${code}n`
|
117 | : `BigInt("${code}")`;
|
118 | }
|
119 | return code + "";
|
120 | };
|
121 |
|
122 | class DefinePlugin {
|
123 | |
124 |
|
125 |
|
126 |
|
127 | constructor(definitions) {
|
128 | this.definitions = definitions;
|
129 | }
|
130 |
|
131 | static runtimeValue(fn, fileDependencies) {
|
132 | return new RuntimeValue(fn, fileDependencies);
|
133 | }
|
134 |
|
135 | |
136 |
|
137 |
|
138 |
|
139 |
|
140 | apply(compiler) {
|
141 | const definitions = this.definitions;
|
142 | compiler.hooks.compilation.tap(
|
143 | "DefinePlugin",
|
144 | (compilation, { normalModuleFactory }) => {
|
145 | compilation.dependencyTemplates.set(
|
146 | ConstDependency,
|
147 | new ConstDependency.Template()
|
148 | );
|
149 | const { runtimeTemplate } = compilation;
|
150 |
|
151 | |
152 |
|
153 |
|
154 |
|
155 |
|
156 | const handler = parser => {
|
157 | |
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 | const walkDefinitions = (definitions, prefix) => {
|
164 | Object.keys(definitions).forEach(key => {
|
165 | const code = definitions[key];
|
166 | if (
|
167 | code &&
|
168 | typeof code === "object" &&
|
169 | !(code instanceof RuntimeValue) &&
|
170 | !(code instanceof RegExp)
|
171 | ) {
|
172 | walkDefinitions(code, prefix + key + ".");
|
173 | applyObjectDefine(prefix + key, code);
|
174 | return;
|
175 | }
|
176 | applyDefineKey(prefix, key);
|
177 | applyDefine(prefix + key, code);
|
178 | });
|
179 | };
|
180 |
|
181 | |
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 | const applyDefineKey = (prefix, key) => {
|
188 | const splittedKey = key.split(".");
|
189 | splittedKey.slice(1).forEach((_, i) => {
|
190 | const fullKey = prefix + splittedKey.slice(0, i + 1).join(".");
|
191 | parser.hooks.canRename.for(fullKey).tap("DefinePlugin", approve);
|
192 | });
|
193 | };
|
194 |
|
195 | |
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 | const applyDefine = (key, code) => {
|
202 | const isTypeof = /^typeof\s+/.test(key);
|
203 | if (isTypeof) key = key.replace(/^typeof\s+/, "");
|
204 | let recurse = false;
|
205 | let recurseTypeof = false;
|
206 | if (!isTypeof) {
|
207 | parser.hooks.canRename.for(key).tap("DefinePlugin", approve);
|
208 | parser.hooks.evaluateIdentifier
|
209 | .for(key)
|
210 | .tap("DefinePlugin", expr => {
|
211 | |
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 | if (recurse) return;
|
220 | recurse = true;
|
221 | const res = parser.evaluate(
|
222 | toCode(code, parser, runtimeTemplate, null)
|
223 | );
|
224 | recurse = false;
|
225 | res.setRange(expr.range);
|
226 | return res;
|
227 | });
|
228 | parser.hooks.expression.for(key).tap("DefinePlugin", expr => {
|
229 | const strCode = toCode(
|
230 | code,
|
231 | parser,
|
232 | runtimeTemplate,
|
233 | !parser.isAsiPosition(expr.range[0])
|
234 | );
|
235 | if (/__webpack_require__\s*(!?\.)/.test(strCode)) {
|
236 | return toConstantDependency(parser, strCode, [
|
237 | RuntimeGlobals.require
|
238 | ])(expr);
|
239 | } else if (/__webpack_require__/.test(strCode)) {
|
240 | return toConstantDependency(parser, strCode, [
|
241 | RuntimeGlobals.requireScope
|
242 | ])(expr);
|
243 | } else {
|
244 | return toConstantDependency(parser, strCode)(expr);
|
245 | }
|
246 | });
|
247 | }
|
248 | parser.hooks.evaluateTypeof.for(key).tap("DefinePlugin", expr => {
|
249 | |
250 |
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 | if (recurseTypeof) return;
|
258 | recurseTypeof = true;
|
259 | const typeofCode = isTypeof
|
260 | ? toCode(code, parser, runtimeTemplate, null)
|
261 | : "typeof (" +
|
262 | toCode(code, parser, runtimeTemplate, null) +
|
263 | ")";
|
264 | const res = parser.evaluate(typeofCode);
|
265 | recurseTypeof = false;
|
266 | res.setRange(expr.range);
|
267 | return res;
|
268 | });
|
269 | parser.hooks.typeof.for(key).tap("DefinePlugin", expr => {
|
270 | const typeofCode = isTypeof
|
271 | ? toCode(code, parser, runtimeTemplate, null)
|
272 | : "typeof (" +
|
273 | toCode(code, parser, runtimeTemplate, null) +
|
274 | ")";
|
275 | const res = parser.evaluate(typeofCode);
|
276 | if (!res.isString()) return;
|
277 | return toConstantDependency(
|
278 | parser,
|
279 | JSON.stringify(res.string)
|
280 | ).bind(parser)(expr);
|
281 | });
|
282 | };
|
283 |
|
284 | |
285 |
|
286 |
|
287 |
|
288 |
|
289 |
|
290 | const applyObjectDefine = (key, obj) => {
|
291 | parser.hooks.canRename.for(key).tap("DefinePlugin", approve);
|
292 | parser.hooks.evaluateIdentifier
|
293 | .for(key)
|
294 | .tap("DefinePlugin", expr =>
|
295 | new BasicEvaluatedExpression()
|
296 | .setTruthy()
|
297 | .setSideEffects(false)
|
298 | .setRange(expr.range)
|
299 | );
|
300 | parser.hooks.evaluateTypeof
|
301 | .for(key)
|
302 | .tap("DefinePlugin", evaluateToString("object"));
|
303 | parser.hooks.expression.for(key).tap("DefinePlugin", expr => {
|
304 | const strCode = stringifyObj(
|
305 | obj,
|
306 | parser,
|
307 | runtimeTemplate,
|
308 | !parser.isAsiPosition(expr.range[0])
|
309 | );
|
310 |
|
311 | if (/__webpack_require__\s*(!?\.)/.test(strCode)) {
|
312 | return toConstantDependency(parser, strCode, [
|
313 | RuntimeGlobals.require
|
314 | ])(expr);
|
315 | } else if (/__webpack_require__/.test(strCode)) {
|
316 | return toConstantDependency(parser, strCode, [
|
317 | RuntimeGlobals.requireScope
|
318 | ])(expr);
|
319 | } else {
|
320 | return toConstantDependency(parser, strCode)(expr);
|
321 | }
|
322 | });
|
323 | parser.hooks.typeof
|
324 | .for(key)
|
325 | .tap(
|
326 | "DefinePlugin",
|
327 | toConstantDependency(parser, JSON.stringify("object"))
|
328 | );
|
329 | };
|
330 |
|
331 | walkDefinitions(definitions, "");
|
332 | };
|
333 |
|
334 | normalModuleFactory.hooks.parser
|
335 | .for("javascript/auto")
|
336 | .tap("DefinePlugin", handler);
|
337 | normalModuleFactory.hooks.parser
|
338 | .for("javascript/dynamic")
|
339 | .tap("DefinePlugin", handler);
|
340 | normalModuleFactory.hooks.parser
|
341 | .for("javascript/esm")
|
342 | .tap("DefinePlugin", handler);
|
343 | }
|
344 | );
|
345 | }
|
346 | }
|
347 | module.exports = DefinePlugin;
|