webpack.js 6.7 KB

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