webpack.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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");
  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. applyWebpackOptionsBaseDefaults,
  14. applyWebpackOptionsDefaults
  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("./MultiCompiler").MultiWebpackOptions} MultiWebpackOptions */
  24. /** @typedef {import("./MultiStats")} MultiStats */
  25. /** @typedef {import("./Stats")} Stats */
  26. const getValidateSchema = memoize(() => require("./validateSchema"));
  27. /**
  28. * @template T
  29. * @callback Callback
  30. * @param {Error | null} err
  31. * @param {T=} stats
  32. * @returns {void}
  33. */
  34. /**
  35. * @param {ReadonlyArray<WebpackOptions>} childOptions options array
  36. * @param {MultiCompilerOptions} options options
  37. * @returns {MultiCompiler} a multi-compiler
  38. */
  39. const createMultiCompiler = (childOptions, options) => {
  40. const compilers = childOptions.map((options, index) =>
  41. createCompiler(options, index)
  42. );
  43. const compiler = new MultiCompiler(compilers, options);
  44. for (const childCompiler of compilers) {
  45. if (childCompiler.options.dependencies) {
  46. compiler.setDependencies(
  47. childCompiler,
  48. childCompiler.options.dependencies
  49. );
  50. }
  51. }
  52. return compiler;
  53. };
  54. /**
  55. * @param {WebpackOptions} rawOptions options object
  56. * @param {number=} compilerIndex index of compiler
  57. * @returns {Compiler} a compiler
  58. */
  59. const createCompiler = (rawOptions, compilerIndex) => {
  60. const options = getNormalizedWebpackOptions(rawOptions);
  61. applyWebpackOptionsBaseDefaults(options);
  62. const compiler = new Compiler(
  63. /** @type {string} */ (options.context),
  64. options
  65. );
  66. new NodeEnvironmentPlugin({
  67. infrastructureLogging: options.infrastructureLogging
  68. }).apply(compiler);
  69. if (Array.isArray(options.plugins)) {
  70. for (const plugin of options.plugins) {
  71. if (typeof plugin === "function") {
  72. /** @type {WebpackPluginFunction} */
  73. (plugin).call(compiler, compiler);
  74. } else if (plugin) {
  75. plugin.apply(compiler);
  76. }
  77. }
  78. }
  79. const resolvedDefaultOptions = applyWebpackOptionsDefaults(
  80. options,
  81. compilerIndex
  82. );
  83. if (resolvedDefaultOptions.platform) {
  84. compiler.platform = resolvedDefaultOptions.platform;
  85. }
  86. compiler.hooks.environment.call();
  87. compiler.hooks.afterEnvironment.call();
  88. new WebpackOptionsApply().process(options, compiler);
  89. compiler.hooks.initialize.call();
  90. return compiler;
  91. };
  92. /**
  93. * @callback WebpackFunctionSingle
  94. * @param {WebpackOptions} options options object
  95. * @param {Callback<Stats>=} callback callback
  96. * @returns {Compiler | null} the compiler object
  97. */
  98. /**
  99. * @callback WebpackFunctionMulti
  100. * @param {MultiWebpackOptions} options options objects
  101. * @param {Callback<MultiStats>=} callback callback
  102. * @returns {MultiCompiler | null} the multi compiler object
  103. */
  104. /**
  105. * @template T
  106. * @param {Array<T> | T} options options
  107. * @returns {Array<T>} array of options
  108. */
  109. const asArray = (options) =>
  110. Array.isArray(options) ? [...options] : [options];
  111. /**
  112. * @callback WebpackCallback
  113. * @param {WebpackOptions | MultiWebpackOptions} options options
  114. * @param {Callback<Stats> & Callback<MultiStats>=} callback callback
  115. * @returns {Compiler | MultiCompiler | null} Compiler or MultiCompiler
  116. */
  117. const webpack = /** @type {WebpackFunctionSingle & WebpackFunctionMulti} */ (
  118. /** @type {WebpackCallback} */
  119. (options, callback) => {
  120. const create = () => {
  121. if (!asArray(options).every(webpackOptionsSchemaCheck)) {
  122. getValidateSchema()(webpackOptionsSchema, options);
  123. util.deprecate(
  124. () => {},
  125. "webpack bug: Pre-compiled schema reports error while real schema is happy. This has performance drawbacks.",
  126. "DEP_WEBPACK_PRE_COMPILED_SCHEMA_INVALID"
  127. )();
  128. }
  129. /** @type {MultiCompiler|Compiler} */
  130. let compiler;
  131. /** @type {boolean | undefined} */
  132. let watch = false;
  133. /** @type {WatchOptions|WatchOptions[]} */
  134. let watchOptions;
  135. if (Array.isArray(options)) {
  136. /** @type {MultiCompiler} */
  137. compiler = createMultiCompiler(
  138. options,
  139. /** @type {MultiCompilerOptions} */ (options)
  140. );
  141. watch = options.some((options) => options.watch);
  142. watchOptions = options.map((options) => options.watchOptions || {});
  143. } else {
  144. const webpackOptions = /** @type {WebpackOptions} */ (options);
  145. /** @type {Compiler} */
  146. compiler = createCompiler(webpackOptions);
  147. watch = webpackOptions.watch;
  148. watchOptions = webpackOptions.watchOptions || {};
  149. }
  150. return { compiler, watch, watchOptions };
  151. };
  152. if (callback) {
  153. try {
  154. const { compiler, watch, watchOptions } = create();
  155. if (watch) {
  156. compiler.watch(watchOptions, callback);
  157. } else {
  158. compiler.run((err, stats) => {
  159. compiler.close((err2) => {
  160. callback(
  161. err || err2,
  162. /** @type {options extends WebpackOptions ? Stats : MultiStats} */
  163. (stats)
  164. );
  165. });
  166. });
  167. }
  168. return compiler;
  169. } catch (err) {
  170. process.nextTick(() => callback(/** @type {Error} */ (err)));
  171. return null;
  172. }
  173. } else {
  174. const { compiler, watch } = create();
  175. if (watch) {
  176. util.deprecate(
  177. () => {},
  178. "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.",
  179. "DEP_WEBPACK_WATCH_WITHOUT_CALLBACK"
  180. )();
  181. }
  182. return compiler;
  183. }
  184. }
  185. );
  186. module.exports = webpack;