提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
6.4 KiB

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