UNPKG

12 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5"use strict";
6
7const path = require("path");
8
9const OptionsDefaulter = require("./OptionsDefaulter");
10const Template = require("./Template");
11
12const isProductionLikeMode = options => {
13 return options.mode === "production" || !options.mode;
14};
15
16const isWebLikeTarget = options => {
17 return options.target === "web" || options.target === "webworker";
18};
19
20const getDevtoolNamespace = library => {
21 // if options.output.library is a string
22 if (Array.isArray(library)) {
23 return library.join(".");
24 } else if (typeof library === "object") {
25 return getDevtoolNamespace(library.root);
26 }
27 return library || "";
28};
29
30class WebpackOptionsDefaulter extends OptionsDefaulter {
31 constructor() {
32 super();
33
34 this.set("entry", "./src");
35
36 this.set("devtool", "make", options =>
37 options.mode === "development" ? "eval" : false
38 );
39 this.set("cache", "make", options => options.mode === "development");
40
41 this.set("context", process.cwd());
42 this.set("target", "web");
43
44 this.set("module", "call", value => Object.assign({}, value));
45 this.set("module.unknownContextRequest", ".");
46 this.set("module.unknownContextRegExp", false);
47 this.set("module.unknownContextRecursive", true);
48 this.set("module.unknownContextCritical", true);
49 this.set("module.exprContextRequest", ".");
50 this.set("module.exprContextRegExp", false);
51 this.set("module.exprContextRecursive", true);
52 this.set("module.exprContextCritical", true);
53 this.set("module.wrappedContextRegExp", /.*/);
54 this.set("module.wrappedContextRecursive", true);
55 this.set("module.wrappedContextCritical", false);
56 this.set("module.strictExportPresence", false);
57 this.set("module.strictThisContextOnImports", false);
58 this.set("module.unsafeCache", "make", options => !!options.cache);
59 this.set("module.rules", []);
60 this.set("module.defaultRules", "make", options => [
61 {
62 type: "javascript/auto",
63 resolve: {}
64 },
65 {
66 test: /\.mjs$/i,
67 type: "javascript/esm",
68 resolve: {
69 mainFields:
70 options.target === "web" ||
71 options.target === "webworker" ||
72 options.target === "electron-renderer"
73 ? ["browser", "main"]
74 : ["main"]
75 }
76 },
77 {
78 test: /\.json$/i,
79 type: "json"
80 },
81 {
82 test: /\.wasm$/i,
83 type: "webassembly/experimental"
84 }
85 ]);
86
87 this.set("output", "call", (value, options) => {
88 if (typeof value === "string") {
89 return {
90 filename: value
91 };
92 } else if (typeof value !== "object") {
93 return {};
94 } else {
95 return Object.assign({}, value);
96 }
97 });
98
99 this.set("output.filename", "[name].js");
100 this.set("output.chunkFilename", "make", options => {
101 const filename = options.output.filename;
102 if (typeof filename !== "function") {
103 const hasName = filename.includes("[name]");
104 const hasId = filename.includes("[id]");
105 const hasChunkHash = filename.includes("[chunkhash]");
106 // Anything changing depending on chunk is fine
107 if (hasChunkHash || hasName || hasId) return filename;
108 // Elsewise prefix "[id]." in front of the basename to make it changing
109 return filename.replace(/(^|\/)([^/]*(?:\?|$))/, "$1[id].$2");
110 }
111 return "[id].js";
112 });
113 this.set("output.webassemblyModuleFilename", "[modulehash].module.wasm");
114 this.set("output.library", "");
115 this.set("output.hotUpdateFunction", "make", options => {
116 return Template.toIdentifier(
117 "webpackHotUpdate" + Template.toIdentifier(options.output.library)
118 );
119 });
120 this.set("output.jsonpFunction", "make", options => {
121 return Template.toIdentifier(
122 "webpackJsonp" + Template.toIdentifier(options.output.library)
123 );
124 });
125 this.set("output.chunkCallbackName", "make", options => {
126 return Template.toIdentifier(
127 "webpackChunk" + Template.toIdentifier(options.output.library)
128 );
129 });
130 this.set("output.globalObject", "make", options => {
131 switch (options.target) {
132 case "web":
133 case "electron-renderer":
134 case "node-webkit":
135 return "window";
136 case "webworker":
137 return "self";
138 case "node":
139 case "async-node":
140 case "electron-main":
141 return "global";
142 default:
143 return "self";
144 }
145 });
146 this.set("output.devtoolNamespace", "make", options => {
147 return getDevtoolNamespace(options.output.library);
148 });
149 this.set("output.libraryTarget", "var");
150 this.set("output.path", path.join(process.cwd(), "dist"));
151 this.set(
152 "output.pathinfo",
153 "make",
154 options => options.mode === "development"
155 );
156 this.set("output.sourceMapFilename", "[file].map[query]");
157 this.set("output.hotUpdateChunkFilename", "[id].[hash].hot-update.js");
158 this.set("output.hotUpdateMainFilename", "[hash].hot-update.json");
159 this.set("output.crossOriginLoading", false);
160 this.set("output.jsonpScriptType", false);
161 this.set("output.chunkLoadTimeout", 120000);
162 this.set("output.hashFunction", "md4");
163 this.set("output.hashDigest", "hex");
164 this.set("output.hashDigestLength", 20);
165 this.set("output.devtoolLineToLine", false);
166 this.set("output.strictModuleExceptionHandling", false);
167
168 this.set("node", "call", value => {
169 if (typeof value === "boolean") {
170 return value;
171 } else {
172 return Object.assign({}, value);
173 }
174 });
175 this.set("node.console", false);
176 this.set("node.process", true);
177 this.set("node.global", true);
178 this.set("node.Buffer", true);
179 this.set("node.setImmediate", true);
180 this.set("node.__filename", "mock");
181 this.set("node.__dirname", "mock");
182
183 this.set("performance", "call", (value, options) => {
184 if (value === false) return false;
185 if (
186 value === undefined &&
187 (!isProductionLikeMode(options) || !isWebLikeTarget(options))
188 )
189 return false;
190 return Object.assign({}, value);
191 });
192 this.set("performance.maxAssetSize", 250000);
193 this.set("performance.maxEntrypointSize", 250000);
194 this.set("performance.hints", "make", options =>
195 isProductionLikeMode(options) ? "warning" : false
196 );
197
198 this.set("optimization", "call", value => Object.assign({}, value));
199 // TODO webpack 5: Disable by default in a modes
200 this.set(
201 "optimization.removeAvailableModules",
202 "make",
203 options => options.mode !== "development"
204 );
205 this.set("optimization.removeEmptyChunks", true);
206 this.set("optimization.mergeDuplicateChunks", true);
207 this.set("optimization.flagIncludedChunks", "make", options =>
208 isProductionLikeMode(options)
209 );
210 // TODO webpack 5 add `moduleIds: "named"` default for development
211 // TODO webpack 5 add `moduleIds: "size"` default for production
212 // TODO webpack 5 remove optimization.occurrenceOrder
213 this.set("optimization.occurrenceOrder", "make", options =>
214 isProductionLikeMode(options)
215 );
216 this.set("optimization.sideEffects", "make", options =>
217 isProductionLikeMode(options)
218 );
219 this.set("optimization.providedExports", true);
220 this.set("optimization.usedExports", "make", options =>
221 isProductionLikeMode(options)
222 );
223 this.set("optimization.concatenateModules", "make", options =>
224 isProductionLikeMode(options)
225 );
226 this.set("optimization.splitChunks", {});
227 this.set("optimization.splitChunks.hidePathInfo", "make", options => {
228 return isProductionLikeMode(options);
229 });
230 this.set("optimization.splitChunks.chunks", "async");
231 this.set("optimization.splitChunks.minSize", "make", options => {
232 return isProductionLikeMode(options) ? 30000 : 10000;
233 });
234 this.set("optimization.splitChunks.minChunks", 1);
235 this.set("optimization.splitChunks.maxAsyncRequests", "make", options => {
236 return isProductionLikeMode(options) ? 5 : Infinity;
237 });
238 this.set("optimization.splitChunks.automaticNameDelimiter", "~");
239 this.set("optimization.splitChunks.automaticNameMaxLength", 109);
240 this.set("optimization.splitChunks.maxInitialRequests", "make", options => {
241 return isProductionLikeMode(options) ? 3 : Infinity;
242 });
243 this.set("optimization.splitChunks.name", true);
244 this.set("optimization.splitChunks.cacheGroups", {});
245 this.set("optimization.splitChunks.cacheGroups.default", {
246 automaticNamePrefix: "",
247 reuseExistingChunk: true,
248 minChunks: 2,
249 priority: -20
250 });
251 this.set("optimization.splitChunks.cacheGroups.vendors", {
252 automaticNamePrefix: "vendors",
253 test: /[\\/]node_modules[\\/]/,
254 priority: -10
255 });
256 this.set("optimization.runtimeChunk", "call", value => {
257 if (value === "single") {
258 return {
259 name: "runtime"
260 };
261 }
262 if (value === true || value === "multiple") {
263 return {
264 name: entrypoint => `runtime~${entrypoint.name}`
265 };
266 }
267 return value;
268 });
269 this.set("optimization.noEmitOnErrors", "make", options =>
270 isProductionLikeMode(options)
271 );
272 this.set("optimization.checkWasmTypes", "make", options =>
273 isProductionLikeMode(options)
274 );
275 this.set("optimization.mangleWasmImports", false);
276 // TODO webpack 5 remove optimization.namedModules
277 this.set(
278 "optimization.namedModules",
279 "make",
280 options => options.mode === "development"
281 );
282 this.set("optimization.hashedModuleIds", false);
283 // TODO webpack 5 add `chunkIds: "named"` default for development
284 // TODO webpack 5 add `chunkIds: "size"` default for production
285 // TODO webpack 5 remove optimization.namedChunks
286 this.set(
287 "optimization.namedChunks",
288 "make",
289 options => options.mode === "development"
290 );
291 this.set(
292 "optimization.portableRecords",
293 "make",
294 options =>
295 !!(
296 options.recordsInputPath ||
297 options.recordsOutputPath ||
298 options.recordsPath
299 )
300 );
301 this.set("optimization.minimize", "make", options =>
302 isProductionLikeMode(options)
303 );
304 this.set("optimization.minimizer", "make", options => [
305 {
306 apply: compiler => {
307 // Lazy load the Terser plugin
308 const TerserPlugin = require("terser-webpack-plugin");
309 const SourceMapDevToolPlugin = require("./SourceMapDevToolPlugin");
310 new TerserPlugin({
311 cache: true,
312 parallel: true,
313 sourceMap:
314 (options.devtool && /source-?map/.test(options.devtool)) ||
315 (options.plugins &&
316 options.plugins.some(p => p instanceof SourceMapDevToolPlugin))
317 }).apply(compiler);
318 }
319 }
320 ]);
321 this.set("optimization.nodeEnv", "make", options => {
322 // TODO: In webpack 5, it should return `false` when mode is `none`
323 return options.mode || "production";
324 });
325
326 this.set("resolve", "call", value => Object.assign({}, value));
327 this.set("resolve.unsafeCache", true);
328 this.set("resolve.modules", ["node_modules"]);
329 this.set("resolve.extensions", [".wasm", ".mjs", ".js", ".json"]);
330 this.set("resolve.mainFiles", ["index"]);
331 this.set("resolve.aliasFields", "make", options => {
332 if (
333 options.target === "web" ||
334 options.target === "webworker" ||
335 options.target === "electron-renderer"
336 ) {
337 return ["browser"];
338 } else {
339 return [];
340 }
341 });
342 this.set("resolve.mainFields", "make", options => {
343 if (
344 options.target === "web" ||
345 options.target === "webworker" ||
346 options.target === "electron-renderer"
347 ) {
348 return ["browser", "module", "main"];
349 } else {
350 return ["module", "main"];
351 }
352 });
353 this.set("resolve.cacheWithContext", "make", options => {
354 return (
355 Array.isArray(options.resolve.plugins) &&
356 options.resolve.plugins.length > 0
357 );
358 });
359
360 this.set("resolveLoader", "call", value => Object.assign({}, value));
361 this.set("resolveLoader.unsafeCache", true);
362 this.set("resolveLoader.mainFields", ["loader", "main"]);
363 this.set("resolveLoader.extensions", [".js", ".json"]);
364 this.set("resolveLoader.mainFiles", ["index"]);
365 this.set("resolveLoader.cacheWithContext", "make", options => {
366 return (
367 Array.isArray(options.resolveLoader.plugins) &&
368 options.resolveLoader.plugins.length > 0
369 );
370 });
371
372 this.set("infrastructureLogging", "call", value =>
373 Object.assign({}, value)
374 );
375 this.set("infrastructureLogging.level", "info");
376 this.set("infrastructureLogging.debug", false);
377 }
378}
379
380module.exports = WebpackOptionsDefaulter;