/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ "use strict"; const util = require("util"); const webpackOptionsSchemaCheck = require("../schemas/WebpackOptions.check.js"); const webpackOptionsSchema = require("../schemas/WebpackOptions.json"); const Compiler = require("./Compiler"); const MultiCompiler = require("./MultiCompiler"); const WebpackOptionsApply = require("./WebpackOptionsApply"); const { applyWebpackOptionsDefaults, applyWebpackOptionsBaseDefaults } = require("./config/defaults"); const { getNormalizedWebpackOptions } = require("./config/normalization"); const NodeEnvironmentPlugin = require("./node/NodeEnvironmentPlugin"); const memoize = require("./util/memoize"); /** @typedef {import("../declarations/WebpackOptions").WebpackOptions} WebpackOptions */ /** @typedef {import("../declarations/WebpackOptions").WebpackPluginFunction} WebpackPluginFunction */ /** @typedef {import("./Compiler").WatchOptions} WatchOptions */ /** @typedef {import("./MultiCompiler").MultiCompilerOptions} MultiCompilerOptions */ /** @typedef {import("./MultiStats")} MultiStats */ /** @typedef {import("./Stats")} Stats */ const getValidateSchema = memoize(() => require("./validateSchema")); /** * @template T * @callback Callback * @param {(Error | null)=} err * @param {T=} stats * @returns {void} */ /** * @param {ReadonlyArray} childOptions options array * @param {MultiCompilerOptions} options options * @returns {MultiCompiler} a multi-compiler */ const createMultiCompiler = (childOptions, options) => { const compilers = childOptions.map(options => createCompiler(options)); const compiler = new MultiCompiler(compilers, options); for (const childCompiler of compilers) { if (childCompiler.options.dependencies) { compiler.setDependencies( childCompiler, childCompiler.options.dependencies ); } } return compiler; }; /** * @param {WebpackOptions} rawOptions options object * @returns {Compiler} a compiler */ const createCompiler = rawOptions => { const options = getNormalizedWebpackOptions(rawOptions); applyWebpackOptionsBaseDefaults(options); const compiler = new Compiler( /** @type {string} */ (options.context), options ); new NodeEnvironmentPlugin({ infrastructureLogging: options.infrastructureLogging }).apply(compiler); if (Array.isArray(options.plugins)) { for (const plugin of options.plugins) { if (typeof plugin === "function") { /** @type {WebpackPluginFunction} */ (plugin).call(compiler, compiler); } else if (plugin) { plugin.apply(compiler); } } } applyWebpackOptionsDefaults(options); compiler.hooks.environment.call(); compiler.hooks.afterEnvironment.call(); new WebpackOptionsApply().process(options, compiler); compiler.hooks.initialize.call(); return compiler; }; /** * @callback WebpackFunctionSingle * @param {WebpackOptions} options options object * @param {Callback=} callback callback * @returns {Compiler} the compiler object */ /** * @callback WebpackFunctionMulti * @param {ReadonlyArray & MultiCompilerOptions} options options objects * @param {Callback=} callback callback * @returns {MultiCompiler} the multi compiler object */ /** * @template T * @param {Array | T} options options * @returns {Array} array of options */ const asArray = options => Array.isArray(options) ? Array.from(options) : [options]; const webpack = /** @type {WebpackFunctionSingle & WebpackFunctionMulti} */ ( /** * @param {WebpackOptions | (ReadonlyArray & MultiCompilerOptions)} options options * @param {Callback & Callback=} callback callback * @returns {Compiler | MultiCompiler} Compiler or MultiCompiler */ (options, callback) => { const create = () => { if (!asArray(options).every(webpackOptionsSchemaCheck)) { getValidateSchema()(webpackOptionsSchema, options); util.deprecate( () => {}, "webpack bug: Pre-compiled schema reports error while real schema is happy. This has performance drawbacks.", "DEP_WEBPACK_PRE_COMPILED_SCHEMA_INVALID" )(); } /** @type {MultiCompiler|Compiler} */ let compiler; /** @type {boolean | undefined} */ let watch = false; /** @type {WatchOptions|WatchOptions[]} */ let watchOptions; if (Array.isArray(options)) { /** @type {MultiCompiler} */ compiler = createMultiCompiler( options, /** @type {MultiCompilerOptions} */ (options) ); watch = options.some(options => options.watch); watchOptions = options.map(options => options.watchOptions || {}); } else { const webpackOptions = /** @type {WebpackOptions} */ (options); /** @type {Compiler} */ compiler = createCompiler(webpackOptions); watch = webpackOptions.watch; watchOptions = webpackOptions.watchOptions || {}; } return { compiler, watch, watchOptions }; }; if (callback) { try { const { compiler, watch, watchOptions } = create(); if (watch) { compiler.watch(watchOptions, callback); } else { compiler.run((err, stats) => { compiler.close(err2 => { callback(err || err2, stats); }); }); } return compiler; } catch (err) { process.nextTick(() => callback(/** @type {Error} */ (err))); return null; } } else { const { compiler, watch } = create(); if (watch) { util.deprecate( () => {}, "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.", "DEP_WEBPACK_WATCH_WITHOUT_CALLBACK" )(); } return compiler; } } ); module.exports = webpack;