WebpackOptionsApply.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const OptionsApply = require("./OptionsApply");
  7. const JavascriptModulesPlugin = require("./JavascriptModulesPlugin");
  8. const JsonModulesPlugin = require("./JsonModulesPlugin");
  9. const WebAssemblyModulesPlugin = require("./wasm/WebAssemblyModulesPlugin");
  10. const LoaderTargetPlugin = require("./LoaderTargetPlugin");
  11. const FunctionModulePlugin = require("./FunctionModulePlugin");
  12. const EvalDevToolModulePlugin = require("./EvalDevToolModulePlugin");
  13. const SourceMapDevToolPlugin = require("./SourceMapDevToolPlugin");
  14. const EvalSourceMapDevToolPlugin = require("./EvalSourceMapDevToolPlugin");
  15. const EntryOptionPlugin = require("./EntryOptionPlugin");
  16. const RecordIdsPlugin = require("./RecordIdsPlugin");
  17. const APIPlugin = require("./APIPlugin");
  18. const ConstPlugin = require("./ConstPlugin");
  19. const CommonJsStuffPlugin = require("./CommonJsStuffPlugin");
  20. const CompatibilityPlugin = require("./CompatibilityPlugin");
  21. const TemplatedPathPlugin = require("./TemplatedPathPlugin");
  22. const WarnCaseSensitiveModulesPlugin = require("./WarnCaseSensitiveModulesPlugin");
  23. const UseStrictPlugin = require("./UseStrictPlugin");
  24. const LoaderPlugin = require("./dependencies/LoaderPlugin");
  25. const CommonJsPlugin = require("./dependencies/CommonJsPlugin");
  26. const HarmonyModulesPlugin = require("./dependencies/HarmonyModulesPlugin");
  27. const SystemPlugin = require("./dependencies/SystemPlugin");
  28. const ImportPlugin = require("./dependencies/ImportPlugin");
  29. const RequireContextPlugin = require("./dependencies/RequireContextPlugin");
  30. const RequireEnsurePlugin = require("./dependencies/RequireEnsurePlugin");
  31. const RequireIncludePlugin = require("./dependencies/RequireIncludePlugin");
  32. const { cachedCleverMerge } = require("./util/cleverMerge");
  33. /** @typedef {import("../declarations/WebpackOptions").WebpackOptions} WebpackOptions */
  34. /** @typedef {import("./Compiler")} Compiler */
  35. class WebpackOptionsApply extends OptionsApply {
  36. constructor() {
  37. super();
  38. }
  39. /**
  40. * @param {WebpackOptions} options options object
  41. * @param {Compiler} compiler compiler object
  42. * @returns {WebpackOptions} options object
  43. */
  44. process(options, compiler) {
  45. let ExternalsPlugin;
  46. compiler.outputPath = options.output.path;
  47. compiler.recordsInputPath = options.recordsInputPath || options.recordsPath;
  48. compiler.recordsOutputPath =
  49. options.recordsOutputPath || options.recordsPath;
  50. compiler.name = options.name;
  51. // TODO webpack 5 refactor this to MultiCompiler.setDependencies() with a WeakMap
  52. // @ts-ignore TODO
  53. compiler.dependencies = options.dependencies;
  54. if (typeof options.target === "string") {
  55. let JsonpTemplatePlugin;
  56. let FetchCompileWasmTemplatePlugin;
  57. let ReadFileCompileWasmTemplatePlugin;
  58. let NodeSourcePlugin;
  59. let NodeTargetPlugin;
  60. let NodeTemplatePlugin;
  61. switch (options.target) {
  62. case "web":
  63. JsonpTemplatePlugin = require("./web/JsonpTemplatePlugin");
  64. FetchCompileWasmTemplatePlugin = require("./web/FetchCompileWasmTemplatePlugin");
  65. NodeSourcePlugin = require("./node/NodeSourcePlugin");
  66. new JsonpTemplatePlugin().apply(compiler);
  67. new FetchCompileWasmTemplatePlugin({
  68. mangleImports: options.optimization.mangleWasmImports
  69. }).apply(compiler);
  70. new FunctionModulePlugin().apply(compiler);
  71. new NodeSourcePlugin(options.node).apply(compiler);
  72. new LoaderTargetPlugin(options.target).apply(compiler);
  73. break;
  74. case "webworker": {
  75. let WebWorkerTemplatePlugin = require("./webworker/WebWorkerTemplatePlugin");
  76. FetchCompileWasmTemplatePlugin = require("./web/FetchCompileWasmTemplatePlugin");
  77. NodeSourcePlugin = require("./node/NodeSourcePlugin");
  78. new WebWorkerTemplatePlugin().apply(compiler);
  79. new FetchCompileWasmTemplatePlugin({
  80. mangleImports: options.optimization.mangleWasmImports
  81. }).apply(compiler);
  82. new FunctionModulePlugin().apply(compiler);
  83. new NodeSourcePlugin(options.node).apply(compiler);
  84. new LoaderTargetPlugin(options.target).apply(compiler);
  85. break;
  86. }
  87. case "node":
  88. case "async-node":
  89. NodeTemplatePlugin = require("./node/NodeTemplatePlugin");
  90. ReadFileCompileWasmTemplatePlugin = require("./node/ReadFileCompileWasmTemplatePlugin");
  91. NodeTargetPlugin = require("./node/NodeTargetPlugin");
  92. new NodeTemplatePlugin({
  93. asyncChunkLoading: options.target === "async-node"
  94. }).apply(compiler);
  95. new ReadFileCompileWasmTemplatePlugin({
  96. mangleImports: options.optimization.mangleWasmImports
  97. }).apply(compiler);
  98. new FunctionModulePlugin().apply(compiler);
  99. new NodeTargetPlugin().apply(compiler);
  100. new LoaderTargetPlugin("node").apply(compiler);
  101. break;
  102. case "node-webkit":
  103. JsonpTemplatePlugin = require("./web/JsonpTemplatePlugin");
  104. NodeTargetPlugin = require("./node/NodeTargetPlugin");
  105. ExternalsPlugin = require("./ExternalsPlugin");
  106. new JsonpTemplatePlugin().apply(compiler);
  107. new FunctionModulePlugin().apply(compiler);
  108. new NodeTargetPlugin().apply(compiler);
  109. new ExternalsPlugin("commonjs", "nw.gui").apply(compiler);
  110. new LoaderTargetPlugin(options.target).apply(compiler);
  111. break;
  112. case "electron-main":
  113. NodeTemplatePlugin = require("./node/NodeTemplatePlugin");
  114. NodeTargetPlugin = require("./node/NodeTargetPlugin");
  115. ExternalsPlugin = require("./ExternalsPlugin");
  116. new NodeTemplatePlugin({
  117. asyncChunkLoading: true
  118. }).apply(compiler);
  119. new FunctionModulePlugin().apply(compiler);
  120. new NodeTargetPlugin().apply(compiler);
  121. new ExternalsPlugin("commonjs", [
  122. "app",
  123. "auto-updater",
  124. "browser-window",
  125. "clipboard",
  126. "content-tracing",
  127. "crash-reporter",
  128. "dialog",
  129. "electron",
  130. "global-shortcut",
  131. "ipc",
  132. "ipc-main",
  133. "menu",
  134. "menu-item",
  135. "native-image",
  136. "original-fs",
  137. "power-monitor",
  138. "power-save-blocker",
  139. "protocol",
  140. "screen",
  141. "session",
  142. "shell",
  143. "tray",
  144. "web-contents"
  145. ]).apply(compiler);
  146. new LoaderTargetPlugin(options.target).apply(compiler);
  147. break;
  148. case "electron-renderer":
  149. case "electron-preload":
  150. FetchCompileWasmTemplatePlugin = require("./web/FetchCompileWasmTemplatePlugin");
  151. NodeTargetPlugin = require("./node/NodeTargetPlugin");
  152. ExternalsPlugin = require("./ExternalsPlugin");
  153. if (options.target === "electron-renderer") {
  154. JsonpTemplatePlugin = require("./web/JsonpTemplatePlugin");
  155. new JsonpTemplatePlugin().apply(compiler);
  156. } else if (options.target === "electron-preload") {
  157. NodeTemplatePlugin = require("./node/NodeTemplatePlugin");
  158. new NodeTemplatePlugin({
  159. asyncChunkLoading: true
  160. }).apply(compiler);
  161. }
  162. new FetchCompileWasmTemplatePlugin({
  163. mangleImports: options.optimization.mangleWasmImports
  164. }).apply(compiler);
  165. new FunctionModulePlugin().apply(compiler);
  166. new NodeTargetPlugin().apply(compiler);
  167. new ExternalsPlugin("commonjs", [
  168. "clipboard",
  169. "crash-reporter",
  170. "desktop-capturer",
  171. "electron",
  172. "ipc",
  173. "ipc-renderer",
  174. "native-image",
  175. "original-fs",
  176. "remote",
  177. "screen",
  178. "shell",
  179. "web-frame"
  180. ]).apply(compiler);
  181. new LoaderTargetPlugin(options.target).apply(compiler);
  182. break;
  183. default:
  184. throw new Error("Unsupported target '" + options.target + "'.");
  185. }
  186. }
  187. // @ts-ignore This is always true, which is good this way
  188. else if (options.target !== false) {
  189. options.target(compiler);
  190. } else {
  191. throw new Error("Unsupported target '" + options.target + "'.");
  192. }
  193. if (options.output.library || options.output.libraryTarget !== "var") {
  194. const LibraryTemplatePlugin = require("./LibraryTemplatePlugin");
  195. new LibraryTemplatePlugin(
  196. options.output.library,
  197. options.output.libraryTarget,
  198. options.output.umdNamedDefine,
  199. options.output.auxiliaryComment || "",
  200. options.output.libraryExport
  201. ).apply(compiler);
  202. }
  203. if (options.externals) {
  204. ExternalsPlugin = require("./ExternalsPlugin");
  205. new ExternalsPlugin(
  206. options.output.libraryTarget,
  207. options.externals
  208. ).apply(compiler);
  209. }
  210. let noSources;
  211. let legacy;
  212. let modern;
  213. let comment;
  214. if (
  215. options.devtool &&
  216. (options.devtool.includes("sourcemap") ||
  217. options.devtool.includes("source-map"))
  218. ) {
  219. const hidden = options.devtool.includes("hidden");
  220. const inline = options.devtool.includes("inline");
  221. const evalWrapped = options.devtool.includes("eval");
  222. const cheap = options.devtool.includes("cheap");
  223. const moduleMaps = options.devtool.includes("module");
  224. noSources = options.devtool.includes("nosources");
  225. legacy = options.devtool.includes("@");
  226. modern = options.devtool.includes("#");
  227. comment =
  228. legacy && modern
  229. ? "\n/*\n//@ source" +
  230. "MappingURL=[url]\n//# source" +
  231. "MappingURL=[url]\n*/"
  232. : legacy
  233. ? "\n/*\n//@ source" + "MappingURL=[url]\n*/"
  234. : modern
  235. ? "\n//# source" + "MappingURL=[url]"
  236. : null;
  237. const Plugin = evalWrapped
  238. ? EvalSourceMapDevToolPlugin
  239. : SourceMapDevToolPlugin;
  240. new Plugin({
  241. filename: inline ? null : options.output.sourceMapFilename,
  242. moduleFilenameTemplate: options.output.devtoolModuleFilenameTemplate,
  243. fallbackModuleFilenameTemplate:
  244. options.output.devtoolFallbackModuleFilenameTemplate,
  245. append: hidden ? false : comment,
  246. module: moduleMaps ? true : cheap ? false : true,
  247. columns: cheap ? false : true,
  248. lineToLine: options.output.devtoolLineToLine,
  249. noSources: noSources,
  250. namespace: options.output.devtoolNamespace
  251. }).apply(compiler);
  252. } else if (options.devtool && options.devtool.includes("eval")) {
  253. legacy = options.devtool.includes("@");
  254. modern = options.devtool.includes("#");
  255. comment =
  256. legacy && modern
  257. ? "\n//@ sourceURL=[url]\n//# sourceURL=[url]"
  258. : legacy
  259. ? "\n//@ sourceURL=[url]"
  260. : modern
  261. ? "\n//# sourceURL=[url]"
  262. : null;
  263. new EvalDevToolModulePlugin({
  264. sourceUrlComment: comment,
  265. moduleFilenameTemplate: options.output.devtoolModuleFilenameTemplate,
  266. namespace: options.output.devtoolNamespace
  267. }).apply(compiler);
  268. }
  269. new JavascriptModulesPlugin().apply(compiler);
  270. new JsonModulesPlugin().apply(compiler);
  271. new WebAssemblyModulesPlugin({
  272. mangleImports: options.optimization.mangleWasmImports
  273. }).apply(compiler);
  274. new EntryOptionPlugin().apply(compiler);
  275. compiler.hooks.entryOption.call(options.context, options.entry);
  276. new CompatibilityPlugin().apply(compiler);
  277. new HarmonyModulesPlugin(options.module).apply(compiler);
  278. if (options.amd !== false) {
  279. const AMDPlugin = require("./dependencies/AMDPlugin");
  280. const RequireJsStuffPlugin = require("./RequireJsStuffPlugin");
  281. new AMDPlugin(options.module, options.amd || {}).apply(compiler);
  282. new RequireJsStuffPlugin().apply(compiler);
  283. }
  284. new CommonJsPlugin(options.module).apply(compiler);
  285. new LoaderPlugin().apply(compiler);
  286. if (options.node !== false) {
  287. const NodeStuffPlugin = require("./NodeStuffPlugin");
  288. new NodeStuffPlugin(options.node).apply(compiler);
  289. }
  290. new CommonJsStuffPlugin().apply(compiler);
  291. new APIPlugin().apply(compiler);
  292. new ConstPlugin().apply(compiler);
  293. new UseStrictPlugin().apply(compiler);
  294. new RequireIncludePlugin().apply(compiler);
  295. new RequireEnsurePlugin().apply(compiler);
  296. new RequireContextPlugin(
  297. options.resolve.modules,
  298. options.resolve.extensions,
  299. options.resolve.mainFiles
  300. ).apply(compiler);
  301. new ImportPlugin(options.module).apply(compiler);
  302. new SystemPlugin(options.module).apply(compiler);
  303. if (typeof options.mode !== "string") {
  304. const WarnNoModeSetPlugin = require("./WarnNoModeSetPlugin");
  305. new WarnNoModeSetPlugin().apply(compiler);
  306. }
  307. const EnsureChunkConditionsPlugin = require("./optimize/EnsureChunkConditionsPlugin");
  308. new EnsureChunkConditionsPlugin().apply(compiler);
  309. if (options.optimization.removeAvailableModules) {
  310. const RemoveParentModulesPlugin = require("./optimize/RemoveParentModulesPlugin");
  311. new RemoveParentModulesPlugin().apply(compiler);
  312. }
  313. if (options.optimization.removeEmptyChunks) {
  314. const RemoveEmptyChunksPlugin = require("./optimize/RemoveEmptyChunksPlugin");
  315. new RemoveEmptyChunksPlugin().apply(compiler);
  316. }
  317. if (options.optimization.mergeDuplicateChunks) {
  318. const MergeDuplicateChunksPlugin = require("./optimize/MergeDuplicateChunksPlugin");
  319. new MergeDuplicateChunksPlugin().apply(compiler);
  320. }
  321. if (options.optimization.flagIncludedChunks) {
  322. const FlagIncludedChunksPlugin = require("./optimize/FlagIncludedChunksPlugin");
  323. new FlagIncludedChunksPlugin().apply(compiler);
  324. }
  325. if (options.optimization.sideEffects) {
  326. const SideEffectsFlagPlugin = require("./optimize/SideEffectsFlagPlugin");
  327. new SideEffectsFlagPlugin().apply(compiler);
  328. }
  329. if (options.optimization.providedExports) {
  330. const FlagDependencyExportsPlugin = require("./FlagDependencyExportsPlugin");
  331. new FlagDependencyExportsPlugin().apply(compiler);
  332. }
  333. if (options.optimization.usedExports) {
  334. const FlagDependencyUsagePlugin = require("./FlagDependencyUsagePlugin");
  335. new FlagDependencyUsagePlugin().apply(compiler);
  336. }
  337. if (options.optimization.concatenateModules) {
  338. const ModuleConcatenationPlugin = require("./optimize/ModuleConcatenationPlugin");
  339. new ModuleConcatenationPlugin().apply(compiler);
  340. }
  341. if (options.optimization.splitChunks) {
  342. const SplitChunksPlugin = require("./optimize/SplitChunksPlugin");
  343. new SplitChunksPlugin(options.optimization.splitChunks).apply(compiler);
  344. }
  345. if (options.optimization.runtimeChunk) {
  346. const RuntimeChunkPlugin = require("./optimize/RuntimeChunkPlugin");
  347. new RuntimeChunkPlugin(options.optimization.runtimeChunk).apply(compiler);
  348. }
  349. if (options.optimization.noEmitOnErrors) {
  350. const NoEmitOnErrorsPlugin = require("./NoEmitOnErrorsPlugin");
  351. new NoEmitOnErrorsPlugin().apply(compiler);
  352. }
  353. if (options.optimization.checkWasmTypes) {
  354. const WasmFinalizeExportsPlugin = require("./wasm/WasmFinalizeExportsPlugin");
  355. new WasmFinalizeExportsPlugin().apply(compiler);
  356. }
  357. let moduleIds = options.optimization.moduleIds;
  358. if (moduleIds === undefined) {
  359. // TODO webpack 5 remove all these options
  360. if (options.optimization.occurrenceOrder) {
  361. moduleIds = "size";
  362. }
  363. if (options.optimization.namedModules) {
  364. moduleIds = "named";
  365. }
  366. if (options.optimization.hashedModuleIds) {
  367. moduleIds = "hashed";
  368. }
  369. if (moduleIds === undefined) {
  370. moduleIds = "natural";
  371. }
  372. }
  373. if (moduleIds) {
  374. const NamedModulesPlugin = require("./NamedModulesPlugin");
  375. const HashedModuleIdsPlugin = require("./HashedModuleIdsPlugin");
  376. const OccurrenceModuleOrderPlugin = require("./optimize/OccurrenceModuleOrderPlugin");
  377. switch (moduleIds) {
  378. case "natural":
  379. // TODO webpack 5: see hint in Compilation.sortModules
  380. break;
  381. case "named":
  382. new NamedModulesPlugin().apply(compiler);
  383. break;
  384. case "hashed":
  385. new HashedModuleIdsPlugin().apply(compiler);
  386. break;
  387. case "size":
  388. new OccurrenceModuleOrderPlugin({
  389. prioritiseInitial: true
  390. }).apply(compiler);
  391. break;
  392. case "total-size":
  393. new OccurrenceModuleOrderPlugin({
  394. prioritiseInitial: false
  395. }).apply(compiler);
  396. break;
  397. default:
  398. throw new Error(
  399. `webpack bug: moduleIds: ${moduleIds} is not implemented`
  400. );
  401. }
  402. }
  403. let chunkIds = options.optimization.chunkIds;
  404. if (chunkIds === undefined) {
  405. // TODO webpack 5 remove all these options
  406. if (options.optimization.occurrenceOrder) {
  407. // This looks weird but it's for backward-compat
  408. // This bug already existed before adding this feature
  409. chunkIds = "total-size";
  410. }
  411. if (options.optimization.namedChunks) {
  412. chunkIds = "named";
  413. }
  414. if (chunkIds === undefined) {
  415. chunkIds = "natural";
  416. }
  417. }
  418. if (chunkIds) {
  419. const NaturalChunkOrderPlugin = require("./optimize/NaturalChunkOrderPlugin");
  420. const NamedChunksPlugin = require("./NamedChunksPlugin");
  421. const OccurrenceChunkOrderPlugin = require("./optimize/OccurrenceChunkOrderPlugin");
  422. switch (chunkIds) {
  423. case "natural":
  424. new NaturalChunkOrderPlugin().apply(compiler);
  425. break;
  426. case "named":
  427. // TODO webapck 5: for backward-compat this need to have OccurrenceChunkOrderPlugin too
  428. // The NamedChunksPlugin doesn't give every chunk a name
  429. // This should be fixed, and the OccurrenceChunkOrderPlugin should be removed here.
  430. new OccurrenceChunkOrderPlugin({
  431. prioritiseInitial: false
  432. }).apply(compiler);
  433. new NamedChunksPlugin().apply(compiler);
  434. break;
  435. case "size":
  436. new OccurrenceChunkOrderPlugin({
  437. prioritiseInitial: true
  438. }).apply(compiler);
  439. break;
  440. case "total-size":
  441. new OccurrenceChunkOrderPlugin({
  442. prioritiseInitial: false
  443. }).apply(compiler);
  444. break;
  445. default:
  446. throw new Error(
  447. `webpack bug: chunkIds: ${chunkIds} is not implemented`
  448. );
  449. }
  450. }
  451. if (options.optimization.nodeEnv) {
  452. const DefinePlugin = require("./DefinePlugin");
  453. new DefinePlugin({
  454. "process.env.NODE_ENV": JSON.stringify(options.optimization.nodeEnv)
  455. }).apply(compiler);
  456. }
  457. if (options.optimization.minimize) {
  458. for (const minimizer of options.optimization.minimizer) {
  459. if (typeof minimizer === "function") {
  460. minimizer.call(compiler, compiler);
  461. } else {
  462. minimizer.apply(compiler);
  463. }
  464. }
  465. }
  466. if (options.performance) {
  467. const SizeLimitsPlugin = require("./performance/SizeLimitsPlugin");
  468. new SizeLimitsPlugin(options.performance).apply(compiler);
  469. }
  470. new TemplatedPathPlugin().apply(compiler);
  471. new RecordIdsPlugin({
  472. portableIds: options.optimization.portableRecords
  473. }).apply(compiler);
  474. new WarnCaseSensitiveModulesPlugin().apply(compiler);
  475. if (options.cache) {
  476. const CachePlugin = require("./CachePlugin");
  477. new CachePlugin(
  478. typeof options.cache === "object" ? options.cache : null
  479. ).apply(compiler);
  480. }
  481. compiler.hooks.afterPlugins.call(compiler);
  482. if (!compiler.inputFileSystem) {
  483. throw new Error("No input filesystem provided");
  484. }
  485. compiler.resolverFactory.hooks.resolveOptions
  486. .for("normal")
  487. .tap("WebpackOptionsApply", resolveOptions => {
  488. return Object.assign(
  489. {
  490. fileSystem: compiler.inputFileSystem
  491. },
  492. cachedCleverMerge(options.resolve, resolveOptions)
  493. );
  494. });
  495. compiler.resolverFactory.hooks.resolveOptions
  496. .for("context")
  497. .tap("WebpackOptionsApply", resolveOptions => {
  498. return Object.assign(
  499. {
  500. fileSystem: compiler.inputFileSystem,
  501. resolveToContext: true
  502. },
  503. cachedCleverMerge(options.resolve, resolveOptions)
  504. );
  505. });
  506. compiler.resolverFactory.hooks.resolveOptions
  507. .for("loader")
  508. .tap("WebpackOptionsApply", resolveOptions => {
  509. return Object.assign(
  510. {
  511. fileSystem: compiler.inputFileSystem
  512. },
  513. cachedCleverMerge(options.resolveLoader, resolveOptions)
  514. );
  515. });
  516. compiler.hooks.afterResolvers.call(compiler);
  517. return options;
  518. }
  519. }
  520. module.exports = WebpackOptionsApply;