WebpackOptionsDefaulter.js 12 KB

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