UNPKG

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