The introduction

This article is based on webpack5 source code interpretation, this article is only for the convenience of the author to learn and memory, if there is anything wrong or guess the wrong place, please point out, do not spray, we are all our own thank you ~

Connect back to the

First of all, based on what we did last time, we continue to draw our graphWe findwebpack-cliThe code in the constructorthis.webpack = require(process.env.WEBPACK_PACKAGE || 'webpack');, know oursthis.webpackActually pull fromnode_modulesIn thewebpackSo let’s focus first onnode_modules/webpack/package.jsonthemainfield

Entry document is on our side to locate node_modules/webpack/lib/index, js, then we open the document file view, file entry statement is mainly to do a lot of parameters, this way we simplified the file, some of our critical path

webpack/lib/index.js

//node_modules/webpack/lib/index.js

const util = require("util");
const memoize = require("./util/memoize");

// It actually does a lazy load in a Currified way, waiting for the function to be called before pulling
const lazyFunction = factory= > {
        // Memoize's internal implementation is relatively simple, encapsulating the Factory with a closure that caches the results of the factory call
	const fac = memoize(factory);
	const f = / * *@type {any} * / (
		(. args) = > {
			return fac()(...args);
		}
	);
	return / * *@type {T} * / (f);
};

/ * * *@template A
 * @template B
 * @param {A} obj input a
 * @param {B} exports input b
 * @returns {A & B} merged* /
 // Recursive calls to initialise the narrative property of the object property are guaranteed to be immutable and cannot be overridden
const mergeExports = (obj, exports) = > {
	const descriptors = Object.getOwnPropertyDescriptors(exports);
	for (const name of Object.keys(descriptors)) {
		const descriptor = descriptors[name];
		if (descriptor.get) {
			const fn = descriptor.get;
			Object.defineProperty(obj, name, {
				configurable: false.enumerable: true.get: memoize(fn)
			});
		} else if (typeof descriptor.value === "object") {
			Object.defineProperty(obj, name, {
				configurable: false.enumerable: true.writable: false.value: mergeExports({}, descriptor.value)
			});
		} else {
			throw new Error(
				"Exposed values must be either a getter or an nested object"); }}return / * *@type {A & B} * / (Object.freeze(obj));
};
// Wrap the webpack pull method
const fn = lazyFunction(() = > require("./webpack"));
// Merge objects and make the merged objects immutable
module.exports = mergeExports(fn, {
    get webpack() {
            return require("./webpack"); },...get cli() {
            return require("./cli"); },...// Many, many declarations are omitted
})
Copy the code

After actually looking at this code and understanding the current entry file, you do three things:

  1. Exposing the entry to a large number of property methods in the WebPack
  2. Cache/lazy load export objects
  3. Modify the narrative property of the exposed property to make it immutable

webpack/lib/webpack.js

While the critical path for node_modules/webpack/lib/webpack js, because we used in webpack – cli with mainly teach building function, The constructor function is obviously taken from the const fn = lazyFunction(() => require(“./webpack”)) above.

// node_modules/webpack/lib/webpack.js

const webpack = ((options, callback) = > {
    const create = () = >{...//
    }
    Node_modules /webpack-cli/lib/webpack-cli.js callback is from node_modules/webpack-cli/lib/webpack-cli.js on line 1847
    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);
                    });
                });
            }
            // Watch returns a compiler regardless of whether it is printed
            return compiler;
        } catch (err) {
            process.nextTick(() = > callback(err));
            return null; }}else {
        const { compiler, watch } = create();
        if (watch) {
            util.deprecate(
                    () = > {},
                    "A 'callback' argument need 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") (); }// The compiler is returned in the same way as in the scenario without call
        returncompiler; }});Copy the code

So all we really know after looking at this little piece of code is that we webpack(…) The main function of compiler is to output a compiler, and then call compiler run(…) according to different scenarios. And watch (…). These two different methods, and mount the callback we passed in (…) Therefore, in the process of compiler production, the most important is nothing more than the omission of create(…). So let’s look at create(…). What does a function do!

// node_modules/webpack/lib/webpack.js webpack(...) =>create(...)
const create = () = > {
    /* * node_modules/schema-utils/dist/index.js; /* * node_modules/schema-utils/dist/index.js; The check method seems to be based on JSON Schema rules. The first check is faster and the second check is more comprehensive 
    if(! webpackOptionsSchemaCheck(options)) { getValidateSchema()(webpackOptionsSchema, options); }let compiler;
    let watch = false;
    let watchOptions;
    /* * WebPack creates a compiler * for each configuration. /* * Webpack creates a compiler * for a production process. After all, different products require different assembly lines * so in the follow-up understanding, compiler should be temporarily understood as a "production line" */ 
    if (Array.isArray(options)) {
        compiler = createMultiCompiler(
            options,
            options
        );
        watch = options.some(options= > options.watch);
        watchOptions = options.map(options= > options.watchOptions || {});
    } else {
        /* * So based on our initial configuration we will go to this process * * we know from the code that the critical path must be createCompiler which sounds rather factory-like * * we will go directly to this code in the next section */ 
        const webpackOptions = options;
        compiler = createCompiler(webpackOptions);
        watch = webpackOptions.watch;
        watchOptions = webpackOptions.watchOptions || {};
    }
    return { compiler, watch, watchOptions };
};
Copy the code
// node_modules/webpack/lib/webpack.js createCompiler(...)
const createCompiler = rawOptions= > {
    /* * We normalize rawOptions with webpack.config.js in the outermost layer of the webpack.config.js file. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    const options = getNormalizedWebpackOptions(rawOptions);
    After everybody to DEBUG / * * will find a lot more on the options webpack official configuration items, then here don't bullshit to continue downward * applyWebpackOptionsBaseDefaults (...). Mainly based on the process done in the context of the value binding * and look like logging parameters initialization applyInfrastructureLoggingDefaults (...). * / 
    applyWebpackOptionsBaseDefaults(options);
    /* * Compiler's constructor, Options. The context is just in the applyWebpackOptionsBaseDefaults assignment () = > process. The CWD () * Compiler a lot has been done in the constructor of the main parameters initialization, here will be on the next code * / 
    const compiler = new Compiler(options.context);
    // Assign options (nonsense)
    compiler.options = options;
    / * * incoming infrastructureLogging parameters is here from applyInfrastructureLoggingDefaults (...). The partially initialized log parameter * NodeEnvironmentPlugin does three things: * 1. File reading related methods mount, cache, operation * 2. Initialize file listening (Watchpack) * 3. Mount plug-in hook beforeRun */ 
    new NodeEnvironmentPlugin({
        infrastructureLogging: options.infrastructureLogging
    }).apply(compiler);
    / / mount CLI plugin before implementation method, mainly some console related content node_modules/webpack CLI/lib/plugins/CLIPlugin js interested students can go to have a look
    if (Array.isArray(options.plugins)) {
        for (const plugin of options.plugins) {
            if (typeof plugin === "function") {
                plugin.call(compiler, compiler);
            } else{ plugin.apply(compiler); }}}// Base assignment after flattening format
    applyWebpackOptionsDefaults(options);
    // Call all "environment" hooks
    compiler.hooks.environment.call();
    // Call all "afterEnvironment" hooks
    compiler.hooks.afterEnvironment.call();
    // WebpackOptionsApply contains a large number of operations to initialize plugins according to options, including related hook triggering, and various key node hook initialization can be found here
    new WebpackOptionsApply().process(options, compiler);
    // Call all "initialize" hooks
    compiler.hooks.initialize.call();
    / / return the compiler
    return compiler;
};

Copy the code

rawOptionsData Content ~

Ok, we found that the current method did not trigger compiler to enter a compilation process, and mainly made a full initialization scheme. Then, after debugging, we discover that the critical path is actually compiler.run(…). In the next chapter, we will mainly look at the compiler. Run (…) What exactly happens during the execution of

Next chapter address: not published yet