Web pack source code analysis

Source code analysis of webpack

First of all, let's go to github and make a copy of the source code of clone, which is the 4.30 version of webpack.

git clone https://github.com/webpack/webpack.git

start

  • First of all, the initial step of looking at a source code is to open package.json to find its entry file
    "main": "lib/webpack.js",
    
  • After confirming the webpack.js file, we can start to read the code

webpack.js

  • At first, we can analyze its structure

We can see that webpack.js is mainly used to export some default plugin s and utility functions. We find that exports = module.exports = webpack is the default exported webpack function, which is the function we usually use when executing. Let's take a look at its source code

/**
 * @param {WebpackOptions} options options object
 * @param {function(Error=, Stats=): void=} callback callback
 * @returns {Compiler | MultiCompiler} the compiler object
 */
/**
 * From the parameter list, we can see that webback mainly carries two parameters, namely, the configuration of webback
 * The other is the callback after the execution of the webback. Because it is a node program, the webback refers to the usage of some node functions
 * The first parameter is err information. This is because node is mainly asynchronous and exception cannot be captured normally. So this design
 */
const webpack = (options, callback) => {

  // Verify options according to the set validate. If there is an exception, terminate the program and throw an exception
  const webpackOptionsValidationErrors = validateSchema(
    webpackOptionsSchema,
    options
  )
  if (webpackOptionsValidationErrors.length) {
    throw new WebpackOptionsValidationError(webpackOptionsValidationErrors)
  }

   // Initialization compiler, in fact, the webpack package is a compiler, which converts our code into the packaged target code
   // Two types of parameters are accepted, one is array < Object:options > type, the other is Object:options type
   // If it is not the specified parameter type, parameter exception will be called
  let compiler
  if (Array.isArray(options)) {
    // If the options are array < object: Options >, multiple configurations can be performed in a single compiler
    compiler = new MultiCompiler(options.map(options => webpack(options)))
  } else if (typeof options === 'object') {
    // If options is Object:options, it is the commonly used mode
    // Array < object: Options > is actually to perform this step for each configuration

    //minix the user-defined configuration information with the default information
    options = new WebpackOptionsDefaulter().process(options)

    // Initialize the webpack editor object and configure the configuration information to it according to the configuration information
    compiler = new Compiler(options.context)
    compiler.options = options
    // Register the NodeEnvironmentPlugin node environment plug-in and use it to add some environment information to the compiler
    new NodeEnvironmentPlugin().apply(compiler)

    // Mount the plug-in registered by the user to the compiler
    if (options.plugins && Array.isArray(options.plugins)) {
      for (const plugin of options.plugins) {
        if (typeof plugin === 'function') {
          plugin.call(compiler, compiler)
        } else {
          plugin.apply(compiler)
        }
      }
    }

     // Trigger events registered on environment and afterEnvironment
    compiler.hooks.environment.call()
    compiler.hooks.afterEnvironment.call()

    // Register some built-in plug-ins of webpack
    compiler.options = new WebpackOptionsApply().process(options, compiler)
  } else {
    throw new Error('Invalid argument: options')
  }

  // If a callback function is passed in, check the parameter type first. If the type is not correct, exit the program and throw an exception
  // Then check whether the watch starts in the configuration item. If it is enabled, execute the callback in the way of watch(), otherwise directly execute the callback
  if (callback) {
    if (typeof callback !== 'function') {
      throw new Error('Invalid argument: callback')
    }
    if (
      options.watch === true ||
			(Array.isArray(options) && options.some(o => o.watch))
    ) {
      const watchOptions = Array.isArray(options)
        ? options.map(o => o.watchOptions || {})
        : options.watchOptions || {}
      return compiler.watch(watchOptions, callback)
    }
    compiler.run(callback)
  }

  // Finally return to compiler
  return compiler
}

The flow chart is as follows:

  • Execution process

    1. Think about how we usually use webback

      webpack --config=webpack.build.js
      
    2. This step is actually equivalent to

      const Webpack = require('./node_modules/webpack');
      const config = require('./own-config.js');
      const compiler = Webpack(config);
      compiler.run();
      

      The last step is the execution of the compiler, so this wave of high-level operations can be inherited and executed downward. run() calls the compiler and packages it.

NEXT

Next we need to call the main module according to the steps to an analysis module ~!

WebpackOptionsValidattionError -> validateSchema.js

Reference material
Core third party library ajv
Core third-party library AJV keywords
json schemas validation

Check input

/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Gajus Kuizinas @gajus
*/
'use strict'

// Initializing ajv objects and configuring information
const Ajv = require('ajv')
const ajv = new Ajv({
  errorDataPath: 'configuration',
  allErrors: true,
  verbose: true
})
// Add instance verification logic for ajv verification. Otherwise, the value of reference type can only be verified as Object
require('ajv-keywords')(ajv, ['instanceof'])
// The verification rules of absolute path are added for the verification of ajv. The relevant codes will be described later
require('../schemas/ajv.absolutePath')(ajv)

// Verify the passed json rule sequence and the verified property object
// It is also verified according to the type of options passed in
// If the options passed in are of type array < Object >, then the traversal call validates the function
// If the options passed in are of type Object, the verification function is used directly
// After the verification result, the error collection will be integrated and an error information array will be returned
const validateSchema = (schema, options) => {
  if (Array.isArray(options)) {
    const errors = options.map(options => validateObject(schema, options))
    errors.forEach((list, idx) => {
      const applyPrefix = err => {
        err.dataPath = `[${idx}]${err.dataPath}`
        if (err.children) {
          err.children.forEach(applyPrefix)
        }
      }
      list.forEach(applyPrefix)
    })
    return errors.reduce((arr, items) => {
      return arr.concat(items)
    }, [])
  } else {
    return validateObject(schema, options)
  }
}

// Generate the corresponding validate according to the schema for verification, and finally return an error array. If there is no error, return an empty array
const validateObject = (schema, options) => {
  const validate = ajv.compile(schema)
  const valid = validate(options)
  return valid ? [] : filterErrors(validate.errors)
}

// Filtering error information to prevent some content from being repeatedly reported and misused is actually an error message de duplication
const filterErrors = errors => {
  let newErrors = []
  for (const err of errors) {
    const dataPath = err.dataPath
    let children = []
    newErrors = newErrors.filter(oldError => {
      if (oldError.dataPath.includes(dataPath)) {
        if (oldError.children) {
          children = children.concat(oldError.children.slice(0))
        }
        oldError.children = undefined
        children.push(oldError)
        return false
      }
      return true
    })
    if (children.length) {
      err.children = children
    }
    newErrors.push(err)
  }

  return newErrors
}

module.exports = validateSchema

User defined AJV verifier (highlight, don't test) - > ajv.sbsolutepath.js

"use strict";

// Assembly error information, return an object containing detailed error information
const errorMessage = (schema, data, message) => ({
	keyword: "absolutePath",
	params: { absolutePath: data },
	message: message,
	parentSchema: schema
});

// Generate an error message based on the situation (whether absolute path is required or not)
const getErrorFor = (shouldBeAbsolute, data, schema) => {
	const message = shouldBeAbsolute
		? `The provided value ${JSON.stringify(data)} is not an absolute path!`
		: `A relative path is expected. However, the provided value ${JSON.stringify(
				data
		  )} is an absolute path!`;

	return errorMessage(schema, data, message);
};

// It is to add a keyword absolute path for ajv, so that it will verify the definition of keyword other than type
module.exports = ajv =>
	ajv.addKeyword("absolutePath", {
		errors: true,
		type: "string",
		// This is actually the function executed during verification, the passed in value and verification rules
		// The main function is to judge whether the incoming value is an absolute path according to the rule, and then return error results according to the rule. If it is correct, return an empty array
		compile(expected, schema) {
			function callback(data) {
				let passes = true;
				const isExclamationMarkPresent = data.includes("!");
				const isCorrectAbsoluteOrRelativePath =
					expected === /^(?:[A-Za-z]:\\|\/)/.test(data);

				if (isExclamationMarkPresent) {
					callback.errors = [
						errorMessage(
							schema,
							data,
							`The provided value ${JSON.stringify(
								data
							)} contains exclamation mark (!) which is not allowed because it's reserved for loader syntax.`
						)
					];
					passes = false;
				}

				if (!isCorrectAbsoluteOrRelativePath) {
					callback.errors = [getErrorFor(expected, data, schema)];
					passes = false;
				}

				return passes;
			}
			callback.errors = [];

			return callback;
		}
	});

Tags: Webpack JSON github git

Posted on Sat, 09 Nov 2019 10:06:10 -0500 by pauper_i