webpack.js 6.0 KB

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