webpack.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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. * Defines the callback callback.
  35. * @template T
  36. * @template [R=void]
  37. * @callback Callback
  38. * @param {Error | null} err
  39. * @param {T=} result
  40. * @returns {R}
  41. */
  42. /** @typedef {Callback<void>} ErrorCallback */
  43. /**
  44. * Creates a multi compiler.
  45. * @param {ReadonlyArray<WebpackOptions>} childOptions options array
  46. * @param {MultiCompilerOptions} options options
  47. * @returns {MultiCompiler} a multi-compiler
  48. */
  49. const createMultiCompiler = (childOptions, options) => {
  50. const compilers = childOptions.map((options, index) =>
  51. createCompiler(options, index)
  52. );
  53. const compiler = new MultiCompiler(compilers, options);
  54. for (const childCompiler of compilers) {
  55. if (childCompiler.options.dependencies) {
  56. compiler.setDependencies(
  57. childCompiler,
  58. childCompiler.options.dependencies
  59. );
  60. }
  61. }
  62. return compiler;
  63. };
  64. /**
  65. * Creates a compiler.
  66. * @param {WebpackOptions} rawOptions options object
  67. * @param {number=} compilerIndex index of compiler
  68. * @returns {Compiler} a compiler
  69. */
  70. const createCompiler = (rawOptions, compilerIndex) => {
  71. let options = getNormalizedWebpackOptions(rawOptions);
  72. applyWebpackOptionsBaseDefaults(options);
  73. /** @type {WebpackOptionsInterception=} */
  74. let interception;
  75. ({ options, interception } = applyWebpackOptionsInterception(options));
  76. const compiler = new Compiler(
  77. /** @type {string} */ (options.context),
  78. options
  79. );
  80. new NodeEnvironmentPlugin({
  81. infrastructureLogging: options.infrastructureLogging
  82. }).apply(compiler);
  83. if (Array.isArray(options.plugins)) {
  84. for (const plugin of options.plugins) {
  85. if (typeof plugin === "function") {
  86. /** @type {WebpackPluginFunction} */
  87. (plugin).call(compiler, compiler);
  88. } else if (plugin) {
  89. plugin.apply(compiler);
  90. }
  91. }
  92. }
  93. const resolvedDefaultOptions = applyWebpackOptionsDefaults(
  94. options,
  95. compilerIndex
  96. );
  97. if (resolvedDefaultOptions.platform) {
  98. compiler.platform = resolvedDefaultOptions.platform;
  99. }
  100. if (options.validate) {
  101. compiler.hooks.validate.call();
  102. }
  103. compiler.hooks.environment.call();
  104. compiler.hooks.afterEnvironment.call();
  105. new WebpackOptionsApply().process(
  106. /** @type {WebpackOptionsNormalizedWithDefaults} */
  107. (options),
  108. compiler,
  109. interception
  110. );
  111. compiler.hooks.initialize.call();
  112. return compiler;
  113. };
  114. /**
  115. * Returns array of options.
  116. * @template T
  117. * @param {T[] | T} options options
  118. * @returns {T[]} array of options
  119. */
  120. const asArray = (options) =>
  121. Array.isArray(options) ? [...options] : [options];
  122. /**
  123. * Checks whether it needs validate.
  124. * @param {WebpackOptions | null | undefined} options options
  125. * @returns {boolean} true when need to validate, otherwise false
  126. */
  127. const needValidate = (options) => {
  128. if (
  129. options &&
  130. (options.validate === false ||
  131. (options.experiments &&
  132. options.experiments.futureDefaults === true &&
  133. (options.mode === "production" || !options.mode)))
  134. ) {
  135. return false;
  136. }
  137. return true;
  138. };
  139. /**
  140. * Returns the compiler object.
  141. * @overload
  142. * @param {WebpackOptions} options options object
  143. * @param {Callback<Stats>} callback callback
  144. * @returns {Compiler | null} the compiler object
  145. */
  146. /**
  147. * Returns the compiler object.
  148. * @overload
  149. * @param {WebpackOptions} options options object
  150. * @returns {Compiler} the compiler object
  151. */
  152. /**
  153. * Returns the multi compiler object.
  154. * @overload
  155. * @param {MultiWebpackOptions} options options objects
  156. * @param {Callback<MultiStats>} callback callback
  157. * @returns {MultiCompiler | null} the multi compiler object
  158. */
  159. /**
  160. * Returns the multi compiler object.
  161. * @overload
  162. * @param {MultiWebpackOptions} options options objects
  163. * @returns {MultiCompiler} the multi compiler object
  164. */
  165. /**
  166. * Returns compiler or MultiCompiler.
  167. * @param {WebpackOptions | MultiWebpackOptions} options options
  168. * @param {Callback<Stats> & Callback<MultiStats>=} callback callback
  169. * @returns {Compiler | MultiCompiler | null} Compiler or MultiCompiler
  170. */
  171. const webpack = (options, callback) => {
  172. const create = () => {
  173. const isMultiCompiler = Array.isArray(options);
  174. if (
  175. !asArray(/** @type {WebpackOptions} */ (options)).every((options) =>
  176. needValidate(options) ? webpackOptionsSchemaCheck(options) : true
  177. )
  178. ) {
  179. getValidateSchema()(
  180. webpackOptionsSchema,
  181. isMultiCompiler
  182. ? options.map((options) => (needValidate(options) ? options : {}))
  183. : needValidate(options)
  184. ? options
  185. : {}
  186. );
  187. util.deprecate(
  188. () => {},
  189. "webpack bug: Pre-compiled schema reports error while real schema is happy. This has performance drawbacks.",
  190. "DEP_WEBPACK_PRE_COMPILED_SCHEMA_INVALID"
  191. )();
  192. }
  193. /** @type {MultiCompiler | Compiler} */
  194. let compiler;
  195. /** @type {boolean | undefined} */
  196. let watch = false;
  197. /** @type {WatchOptions | WatchOptions[]} */
  198. let watchOptions;
  199. if (isMultiCompiler) {
  200. /** @type {MultiCompiler} */
  201. compiler = createMultiCompiler(
  202. options,
  203. /** @type {MultiCompilerOptions} */
  204. (options)
  205. );
  206. watch = options.some((options) => options.watch);
  207. watchOptions = options.map((options) => options.watchOptions || {});
  208. } else {
  209. const webpackOptions = /** @type {WebpackOptions} */ (options);
  210. /** @type {Compiler} */
  211. compiler = createCompiler(webpackOptions);
  212. watch = webpackOptions.watch;
  213. watchOptions = webpackOptions.watchOptions || {};
  214. }
  215. return { compiler, watch, watchOptions };
  216. };
  217. if (callback) {
  218. try {
  219. const { compiler, watch, watchOptions } = create();
  220. if (watch) {
  221. compiler.watch(watchOptions, callback);
  222. } else {
  223. compiler.run((err, stats) => {
  224. compiler.close((err2) => {
  225. callback(
  226. err || err2,
  227. /** @type {options extends WebpackOptions ? Stats : MultiStats} */
  228. (stats)
  229. );
  230. });
  231. });
  232. }
  233. return compiler;
  234. } catch (err) {
  235. process.nextTick(() => callback(/** @type {Error} */ (err)));
  236. return null;
  237. }
  238. } else {
  239. const { compiler, watch } = create();
  240. if (watch) {
  241. util.deprecate(
  242. () => {},
  243. "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.",
  244. "DEP_WEBPACK_WATCH_WITHOUT_CALLBACK"
  245. )();
  246. }
  247. return compiler;
  248. }
  249. };
  250. module.exports = webpack;