UNPKG

16.1 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const util = require("util");
9
10/** @typedef {import("../../declarations/WebpackOptions").EntryStatic} EntryStatic */
11/** @typedef {import("../../declarations/WebpackOptions").EntryStaticNormalized} EntryStaticNormalized */
12/** @typedef {import("../../declarations/WebpackOptions").LibraryName} LibraryName */
13/** @typedef {import("../../declarations/WebpackOptions").LibraryOptions} LibraryOptions */
14/** @typedef {import("../../declarations/WebpackOptions").OptimizationRuntimeChunk} OptimizationRuntimeChunk */
15/** @typedef {import("../../declarations/WebpackOptions").OptimizationRuntimeChunkNormalized} OptimizationRuntimeChunkNormalized */
16/** @typedef {import("../../declarations/WebpackOptions").OutputNormalized} OutputNormalized */
17/** @typedef {import("../../declarations/WebpackOptions").WebpackOptions} WebpackOptions */
18/** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptionsNormalized */
19
20const handledDeprecatedNoEmitOnErrors = util.deprecate(
21 (noEmitOnErrors, emitOnErrors) => {
22 if (emitOnErrors !== undefined && !noEmitOnErrors === !emitOnErrors) {
23 throw new Error(
24 "Conflicting use of 'optimization.noEmitOnErrors' and 'optimization.emitOnErrors'. Remove deprecated 'optimization.noEmitOnErrors' from config."
25 );
26 }
27 return !noEmitOnErrors;
28 },
29 "optimization.noEmitOnErrors is deprecated in favor of optimization.emitOnErrors",
30 "DEP_WEBPACK_CONFIGURATION_OPTIMIZATION_NO_EMIT_ON_ERRORS"
31);
32
33/**
34 * @template T
35 * @template R
36 * @param {T|undefined} value value or not
37 * @param {function(T): R} fn nested handler
38 * @returns {R} result value
39 */
40const nestedConfig = (value, fn) =>
41 value === undefined ? fn(/** @type {T} */ ({})) : fn(value);
42
43/**
44 * @template T
45 * @param {T|undefined} value value or not
46 * @returns {T} result value
47 */
48const cloneObject = value => {
49 return /** @type {T} */ ({ ...value });
50};
51
52/**
53 * @template T
54 * @template R
55 * @param {T|undefined} value value or not
56 * @param {function(T): R} fn nested handler
57 * @returns {R|undefined} result value
58 */
59const optionalNestedConfig = (value, fn) =>
60 value === undefined ? undefined : fn(value);
61
62/**
63 * @template T
64 * @template R
65 * @param {T[]|undefined} value array or not
66 * @param {function(T[]): R[]} fn nested handler
67 * @returns {R[]|undefined} cloned value
68 */
69const nestedArray = (value, fn) => (Array.isArray(value) ? fn(value) : fn([]));
70
71/**
72 * @template T
73 * @template R
74 * @param {T[]|undefined} value array or not
75 * @param {function(T[]): R[]} fn nested handler
76 * @returns {R[]|undefined} cloned value
77 */
78const optionalNestedArray = (value, fn) =>
79 Array.isArray(value) ? fn(value) : undefined;
80
81/**
82 * @template T
83 * @template R
84 * @param {Record<string, T>|undefined} value value or not
85 * @param {function(T): R} fn nested handler
86 * @param {Record<string, function(T): R>=} customKeys custom nested handler for some keys
87 * @returns {Record<string, R>} result value
88 */
89const keyedNestedConfig = (value, fn, customKeys) => {
90 const result =
91 value === undefined
92 ? {}
93 : Object.keys(value).reduce(
94 (obj, key) => (
95 (obj[key] = (
96 customKeys && key in customKeys ? customKeys[key] : fn
97 )(value[key])),
98 obj
99 ),
100 /** @type {Record<string, R>} */ ({})
101 );
102 if (customKeys) {
103 for (const key of Object.keys(customKeys)) {
104 if (!(key in result)) {
105 result[key] = customKeys[key](/** @type {T} */ ({}));
106 }
107 }
108 }
109 return result;
110};
111
112/**
113 * @param {WebpackOptions} config input config
114 * @returns {WebpackOptionsNormalized} normalized options
115 */
116const getNormalizedWebpackOptions = config => {
117 return {
118 amd: config.amd,
119 bail: config.bail,
120 cache: optionalNestedConfig(config.cache, cache => {
121 if (cache === false) return false;
122 if (cache === true) {
123 return {
124 type: "memory",
125 maxGenerations: undefined
126 };
127 }
128 switch (cache.type) {
129 case "filesystem":
130 return {
131 type: "filesystem",
132 allowCollectingMemory: cache.allowCollectingMemory,
133 maxMemoryGenerations: cache.maxMemoryGenerations,
134 maxAge: cache.maxAge,
135 profile: cache.profile,
136 buildDependencies: cloneObject(cache.buildDependencies),
137 cacheDirectory: cache.cacheDirectory,
138 cacheLocation: cache.cacheLocation,
139 hashAlgorithm: cache.hashAlgorithm,
140 compression: cache.compression,
141 idleTimeout: cache.idleTimeout,
142 idleTimeoutForInitialStore: cache.idleTimeoutForInitialStore,
143 idleTimeoutAfterLargeChanges: cache.idleTimeoutAfterLargeChanges,
144 name: cache.name,
145 store: cache.store,
146 version: cache.version
147 };
148 case undefined:
149 case "memory":
150 return {
151 type: "memory",
152 maxGenerations: cache.maxGenerations
153 };
154 default:
155 // @ts-expect-error Property 'type' does not exist on type 'never'. ts(2339)
156 throw new Error(`Not implemented cache.type ${cache.type}`);
157 }
158 }),
159 context: config.context,
160 dependencies: config.dependencies,
161 devServer: optionalNestedConfig(config.devServer, devServer => ({
162 ...devServer
163 })),
164 devtool: config.devtool,
165 entry:
166 config.entry === undefined
167 ? { main: {} }
168 : typeof config.entry === "function"
169 ? (
170 fn => () =>
171 Promise.resolve().then(fn).then(getNormalizedEntryStatic)
172 )(config.entry)
173 : getNormalizedEntryStatic(config.entry),
174 experiments: nestedConfig(config.experiments, experiments => ({
175 ...experiments,
176 buildHttp: optionalNestedConfig(experiments.buildHttp, options =>
177 Array.isArray(options) ? { allowedUris: options } : options
178 ),
179 lazyCompilation: optionalNestedConfig(
180 experiments.lazyCompilation,
181 options =>
182 options === true ? {} : options === false ? undefined : options
183 )
184 })),
185 externals: config.externals,
186 externalsPresets: cloneObject(config.externalsPresets),
187 externalsType: config.externalsType,
188 ignoreWarnings: config.ignoreWarnings
189 ? config.ignoreWarnings.map(ignore => {
190 if (typeof ignore === "function") return ignore;
191 const i = ignore instanceof RegExp ? { message: ignore } : ignore;
192 return (warning, { requestShortener }) => {
193 if (!i.message && !i.module && !i.file) return false;
194 if (i.message && !i.message.test(warning.message)) {
195 return false;
196 }
197 if (
198 i.module &&
199 (!warning.module ||
200 !i.module.test(
201 warning.module.readableIdentifier(requestShortener)
202 ))
203 ) {
204 return false;
205 }
206 if (i.file && (!warning.file || !i.file.test(warning.file))) {
207 return false;
208 }
209 return true;
210 };
211 })
212 : undefined,
213 infrastructureLogging: cloneObject(config.infrastructureLogging),
214 loader: cloneObject(config.loader),
215 mode: config.mode,
216 module: nestedConfig(config.module, module => ({
217 noParse: module.noParse,
218 unsafeCache: module.unsafeCache,
219 parser: keyedNestedConfig(module.parser, cloneObject, {
220 javascript: parserOptions => ({
221 unknownContextRequest: module.unknownContextRequest,
222 unknownContextRegExp: module.unknownContextRegExp,
223 unknownContextRecursive: module.unknownContextRecursive,
224 unknownContextCritical: module.unknownContextCritical,
225 exprContextRequest: module.exprContextRequest,
226 exprContextRegExp: module.exprContextRegExp,
227 exprContextRecursive: module.exprContextRecursive,
228 exprContextCritical: module.exprContextCritical,
229 wrappedContextRegExp: module.wrappedContextRegExp,
230 wrappedContextRecursive: module.wrappedContextRecursive,
231 wrappedContextCritical: module.wrappedContextCritical,
232 strictExportPresence: module.strictExportPresence,
233 strictThisContextOnImports: module.strictThisContextOnImports,
234 ...parserOptions
235 })
236 }),
237 generator: cloneObject(module.generator),
238 defaultRules: optionalNestedArray(module.defaultRules, r => [...r]),
239 rules: nestedArray(module.rules, r => [...r])
240 })),
241 name: config.name,
242 node: nestedConfig(
243 config.node,
244 node =>
245 node && {
246 ...node
247 }
248 ),
249 optimization: nestedConfig(config.optimization, optimization => {
250 return {
251 ...optimization,
252 runtimeChunk: getNormalizedOptimizationRuntimeChunk(
253 optimization.runtimeChunk
254 ),
255 splitChunks: nestedConfig(
256 optimization.splitChunks,
257 splitChunks =>
258 splitChunks && {
259 ...splitChunks,
260 defaultSizeTypes: splitChunks.defaultSizeTypes
261 ? [...splitChunks.defaultSizeTypes]
262 : ["..."],
263 cacheGroups: cloneObject(splitChunks.cacheGroups)
264 }
265 ),
266 emitOnErrors:
267 optimization.noEmitOnErrors !== undefined
268 ? handledDeprecatedNoEmitOnErrors(
269 optimization.noEmitOnErrors,
270 optimization.emitOnErrors
271 )
272 : optimization.emitOnErrors
273 };
274 }),
275 output: nestedConfig(config.output, output => {
276 const { library } = output;
277 const libraryAsName = /** @type {LibraryName} */ (library);
278 const libraryBase =
279 typeof library === "object" &&
280 library &&
281 !Array.isArray(library) &&
282 "type" in library
283 ? library
284 : libraryAsName || output.libraryTarget
285 ? /** @type {LibraryOptions} */ ({
286 name: libraryAsName
287 })
288 : undefined;
289 /** @type {OutputNormalized} */
290 const result = {
291 assetModuleFilename: output.assetModuleFilename,
292 charset: output.charset,
293 chunkFilename: output.chunkFilename,
294 chunkFormat: output.chunkFormat,
295 chunkLoading: output.chunkLoading,
296 chunkLoadingGlobal: output.chunkLoadingGlobal,
297 chunkLoadTimeout: output.chunkLoadTimeout,
298 clean: output.clean,
299 compareBeforeEmit: output.compareBeforeEmit,
300 crossOriginLoading: output.crossOriginLoading,
301 devtoolFallbackModuleFilenameTemplate:
302 output.devtoolFallbackModuleFilenameTemplate,
303 devtoolModuleFilenameTemplate: output.devtoolModuleFilenameTemplate,
304 devtoolNamespace: output.devtoolNamespace,
305 environment: cloneObject(output.environment),
306 enabledChunkLoadingTypes: output.enabledChunkLoadingTypes
307 ? [...output.enabledChunkLoadingTypes]
308 : ["..."],
309 enabledLibraryTypes: output.enabledLibraryTypes
310 ? [...output.enabledLibraryTypes]
311 : ["..."],
312 enabledWasmLoadingTypes: output.enabledWasmLoadingTypes
313 ? [...output.enabledWasmLoadingTypes]
314 : ["..."],
315 filename: output.filename,
316 globalObject: output.globalObject,
317 hashDigest: output.hashDigest,
318 hashDigestLength: output.hashDigestLength,
319 hashFunction: output.hashFunction,
320 hashSalt: output.hashSalt,
321 hotUpdateChunkFilename: output.hotUpdateChunkFilename,
322 hotUpdateGlobal: output.hotUpdateGlobal,
323 hotUpdateMainFilename: output.hotUpdateMainFilename,
324 iife: output.iife,
325 importFunctionName: output.importFunctionName,
326 importMetaName: output.importMetaName,
327 scriptType: output.scriptType,
328 library: libraryBase && {
329 type:
330 output.libraryTarget !== undefined
331 ? output.libraryTarget
332 : libraryBase.type,
333 auxiliaryComment:
334 output.auxiliaryComment !== undefined
335 ? output.auxiliaryComment
336 : libraryBase.auxiliaryComment,
337 export:
338 output.libraryExport !== undefined
339 ? output.libraryExport
340 : libraryBase.export,
341 name: libraryBase.name,
342 umdNamedDefine:
343 output.umdNamedDefine !== undefined
344 ? output.umdNamedDefine
345 : libraryBase.umdNamedDefine
346 },
347 module: output.module,
348 path: output.path,
349 pathinfo: output.pathinfo,
350 publicPath: output.publicPath,
351 sourceMapFilename: output.sourceMapFilename,
352 sourcePrefix: output.sourcePrefix,
353 strictModuleExceptionHandling: output.strictModuleExceptionHandling,
354 trustedTypes: optionalNestedConfig(
355 output.trustedTypes,
356 trustedTypes => {
357 if (trustedTypes === true) return {};
358 if (typeof trustedTypes === "string")
359 return { policyName: trustedTypes };
360 return { ...trustedTypes };
361 }
362 ),
363 uniqueName: output.uniqueName,
364 wasmLoading: output.wasmLoading,
365 webassemblyModuleFilename: output.webassemblyModuleFilename,
366 workerChunkLoading: output.workerChunkLoading,
367 workerWasmLoading: output.workerWasmLoading
368 };
369 return result;
370 }),
371 parallelism: config.parallelism,
372 performance: optionalNestedConfig(config.performance, performance => {
373 if (performance === false) return false;
374 return {
375 ...performance
376 };
377 }),
378 plugins: nestedArray(config.plugins, p => [...p]),
379 profile: config.profile,
380 recordsInputPath:
381 config.recordsInputPath !== undefined
382 ? config.recordsInputPath
383 : config.recordsPath,
384 recordsOutputPath:
385 config.recordsOutputPath !== undefined
386 ? config.recordsOutputPath
387 : config.recordsPath,
388 resolve: nestedConfig(config.resolve, resolve => ({
389 ...resolve,
390 byDependency: keyedNestedConfig(resolve.byDependency, cloneObject)
391 })),
392 resolveLoader: cloneObject(config.resolveLoader),
393 snapshot: nestedConfig(config.snapshot, snapshot => ({
394 resolveBuildDependencies: optionalNestedConfig(
395 snapshot.resolveBuildDependencies,
396 resolveBuildDependencies => ({
397 timestamp: resolveBuildDependencies.timestamp,
398 hash: resolveBuildDependencies.hash
399 })
400 ),
401 buildDependencies: optionalNestedConfig(
402 snapshot.buildDependencies,
403 buildDependencies => ({
404 timestamp: buildDependencies.timestamp,
405 hash: buildDependencies.hash
406 })
407 ),
408 resolve: optionalNestedConfig(snapshot.resolve, resolve => ({
409 timestamp: resolve.timestamp,
410 hash: resolve.hash
411 })),
412 module: optionalNestedConfig(snapshot.module, module => ({
413 timestamp: module.timestamp,
414 hash: module.hash
415 })),
416 immutablePaths: optionalNestedArray(snapshot.immutablePaths, p => [...p]),
417 managedPaths: optionalNestedArray(snapshot.managedPaths, p => [...p])
418 })),
419 stats: nestedConfig(config.stats, stats => {
420 if (stats === false) {
421 return {
422 preset: "none"
423 };
424 }
425 if (stats === true) {
426 return {
427 preset: "normal"
428 };
429 }
430 if (typeof stats === "string") {
431 return {
432 preset: stats
433 };
434 }
435 return {
436 ...stats
437 };
438 }),
439 target: config.target,
440 watch: config.watch,
441 watchOptions: cloneObject(config.watchOptions)
442 };
443};
444
445/**
446 * @param {EntryStatic} entry static entry options
447 * @returns {EntryStaticNormalized} normalized static entry options
448 */
449const getNormalizedEntryStatic = entry => {
450 if (typeof entry === "string") {
451 return {
452 main: {
453 import: [entry]
454 }
455 };
456 }
457 if (Array.isArray(entry)) {
458 return {
459 main: {
460 import: entry
461 }
462 };
463 }
464 /** @type {EntryStaticNormalized} */
465 const result = {};
466 for (const key of Object.keys(entry)) {
467 const value = entry[key];
468 if (typeof value === "string") {
469 result[key] = {
470 import: [value]
471 };
472 } else if (Array.isArray(value)) {
473 result[key] = {
474 import: value
475 };
476 } else {
477 result[key] = {
478 import:
479 value.import &&
480 (Array.isArray(value.import) ? value.import : [value.import]),
481 filename: value.filename,
482 layer: value.layer,
483 runtime: value.runtime,
484 publicPath: value.publicPath,
485 chunkLoading: value.chunkLoading,
486 wasmLoading: value.wasmLoading,
487 dependOn:
488 value.dependOn &&
489 (Array.isArray(value.dependOn) ? value.dependOn : [value.dependOn]),
490 library: value.library
491 };
492 }
493 }
494 return result;
495};
496
497/**
498 * @param {OptimizationRuntimeChunk=} runtimeChunk runtimeChunk option
499 * @returns {OptimizationRuntimeChunkNormalized=} normalized runtimeChunk option
500 */
501const getNormalizedOptimizationRuntimeChunk = runtimeChunk => {
502 if (runtimeChunk === undefined) return undefined;
503 if (runtimeChunk === false) return false;
504 if (runtimeChunk === "single") {
505 return {
506 name: () => "runtime"
507 };
508 }
509 if (runtimeChunk === true || runtimeChunk === "multiple") {
510 return {
511 name: entrypoint => `runtime~${entrypoint.name}`
512 };
513 }
514 const { name } = runtimeChunk;
515 return {
516 name: typeof name === "function" ? name : () => name
517 };
518};
519
520exports.getNormalizedWebpackOptions = getNormalizedWebpackOptions;