webpack.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const util = require("util");
  7. const webpackOptionsSchemaCheck = require("../schemas/WebpackOptions.check.js");
  8. const webpackOptionsSchema = require("../schemas/WebpackOptions.json");
  9. const Compiler = require("./Compiler");
  10. const MultiCompiler = require("./MultiCompiler");
  11. const WebpackOptionsApply = require("./WebpackOptionsApply");
  12. const {
  13. applyWebpackOptionsDefaults,
  14. applyWebpackOptionsBaseDefaults
  15. } = require("./config/defaults");
  16. const { getNormalizedWebpackOptions } = require("./config/normalization");
  17. const NodeEnvironmentPlugin = require("./node/NodeEnvironmentPlugin");
  18. const memoize = require("./util/memoize");
  19. /** @typedef {import("../declarations/WebpackOptions").WebpackOptions} WebpackOptions */
  20. /** @typedef {import("../declarations/WebpackOptions").WebpackPluginFunction} WebpackPluginFunction */
  21. /** @typedef {import("./Compiler").WatchOptions} WatchOptions */
  22. /** @typedef {import("./MultiCompiler").MultiCompilerOptions} MultiCompilerOptions */
  23. /** @typedef {import("./MultiStats")} MultiStats */
  24. /** @typedef {import("./Stats")} Stats */
  25. const getValidateSchema = memoize(() => require("./validateSchema"));
  26. /**
  27. * @template T
  28. * @callback Callback
  29. * @param {Error | null} err
  30. * @param {T=} stats
  31. * @returns {void}
  32. */
  33. /**
  34. * @param {ReadonlyArray<WebpackOptions>} childOptions options array
  35. * @param {MultiCompilerOptions} options options
  36. * @returns {MultiCompiler} a multi-compiler
  37. */
  38. const createMultiCompiler = (childOptions, options) => {
  39. const compilers = childOptions.map(options => createCompiler(options));
  40. const compiler = new MultiCompiler(compilers, options);
  41. for (const childCompiler of compilers) {
  42. if (childCompiler.options.dependencies) {
  43. compiler.setDependencies(
  44. childCompiler,
  45. childCompiler.options.dependencies
  46. );
  47. }
  48. }
  49. return compiler;
  50. };
  51. /**
  52. * @param {WebpackOptions} rawOptions options object
  53. * @returns {Compiler} a compiler
  54. */
  55. const createCompiler = rawOptions => {
  56. const options = getNormalizedWebpackOptions(rawOptions);
  57. applyWebpackOptionsBaseDefaults(options);
  58. const compiler = new Compiler(
  59. /** @type {string} */ (options.context),
  60. options
  61. );
  62. new NodeEnvironmentPlugin({
  63. infrastructureLogging: options.infrastructureLogging
  64. }).apply(compiler);
  65. if (Array.isArray(options.plugins)) {
  66. for (const plugin of options.plugins) {
  67. if (typeof plugin === "function") {
  68. /** @type {WebpackPluginFunction} */
  69. (plugin).call(compiler, compiler);
  70. } else if (plugin) {
  71. plugin.apply(compiler);
  72. }
  73. }
  74. }
  75. applyWebpackOptionsDefaults(options);
  76. compiler.hooks.environment.call();
  77. compiler.hooks.afterEnvironment.call();
  78. new WebpackOptionsApply().process(options, compiler);
  79. compiler.hooks.initialize.call();
  80. return compiler;
  81. };
  82. /**
  83. * @callback WebpackFunctionSingle
  84. * @param {WebpackOptions} options options object
  85. * @param {Callback<Stats>=} callback callback
  86. * @returns {Compiler} the compiler object
  87. */
  88. /**
  89. * @callback WebpackFunctionMulti
  90. * @param {ReadonlyArray<WebpackOptions> & MultiCompilerOptions} options options objects
  91. * @param {Callback<MultiStats>=} callback callback
  92. * @returns {MultiCompiler} the multi compiler object
  93. */
  94. /**
  95. * @template T
  96. * @param {Array<T> | T} options options
  97. * @returns {Array<T>} array of options
  98. */
  99. const asArray = options =>
  100. Array.isArray(options) ? Array.from(options) : [options];
  101. const webpack = /** @type {WebpackFunctionSingle & WebpackFunctionMulti} */ (
  102. /**
  103. * @param {WebpackOptions | (ReadonlyArray<WebpackOptions> & MultiCompilerOptions)} options options
  104. * @param {Callback<Stats> & Callback<MultiStats>=} callback callback
  105. * @returns {Compiler | MultiCompiler | null} Compiler or MultiCompiler
  106. */
  107. (options, callback) => {
  108. const create = () => {
  109. if (!asArray(options).every(webpackOptionsSchemaCheck)) {
  110. getValidateSchema()(webpackOptionsSchema, options);
  111. util.deprecate(
  112. () => {},
  113. "webpack bug: Pre-compiled schema reports error while real schema is happy. This has performance drawbacks.",
  114. "DEP_WEBPACK_PRE_COMPILED_SCHEMA_INVALID"
  115. )();
  116. }
  117. /** @type {MultiCompiler|Compiler} */
  118. let compiler;
  119. /** @type {boolean | undefined} */
  120. let watch = false;
  121. /** @type {WatchOptions|WatchOptions[]} */
  122. let watchOptions;
  123. if (Array.isArray(options)) {
  124. /** @type {MultiCompiler} */
  125. compiler = createMultiCompiler(
  126. options,
  127. /** @type {MultiCompilerOptions} */ (options)
  128. );
  129. watch = options.some(options => options.watch);
  130. watchOptions = options.map(options => options.watchOptions || {});
  131. } else {
  132. const webpackOptions = /** @type {WebpackOptions} */ (options);
  133. /** @type {Compiler} */
  134. compiler = createCompiler(webpackOptions);
  135. watch = webpackOptions.watch;
  136. watchOptions = webpackOptions.watchOptions || {};
  137. }
  138. return { compiler, watch, watchOptions };
  139. };
  140. if (callback) {
  141. try {
  142. const { compiler, watch, watchOptions } = create();
  143. if (watch) {
  144. compiler.watch(watchOptions, callback);
  145. } else {
  146. compiler.run((err, stats) => {
  147. compiler.close(err2 => {
  148. callback(
  149. err || err2,
  150. /** @type {options extends WebpackOptions ? Stats : MultiStats} */
  151. (stats)
  152. );
  153. });
  154. });
  155. }
  156. return compiler;
  157. } catch (err) {
  158. process.nextTick(() => callback(/** @type {Error} */ (err)));
  159. return null;
  160. }
  161. } else {
  162. const { compiler, watch } = create();
  163. if (watch) {
  164. util.deprecate(
  165. () => {},
  166. "A 'callback' argument needs to be provided to the 'webpack(options, callback)' function when the 'watch' option is set. There is no way to handle the 'watch' option without a callback.",
  167. "DEP_WEBPACK_WATCH_WITHOUT_CALLBACK"
  168. )();
  169. }
  170. return compiler;
  171. }
  172. }
  173. );
  174. module.exports = webpack;