1. NormalModule

class NormalModule extends Module {
  constructor({ type, request, rawRequest, loaders, resource, parser, generator }) {
    // Where do these parameters come from? Check in NormalModuleFactory
    this.type = type
    this.request = request // Loaders.map ()
    this.rawRequest = rawRequest
    this.loaders = loaders // loader
    this.resource = resource // code
    this.parser = parser // the js parser
    this.generator = generator // Template generation
  }
  // Call the build method to start building
  build(options, compilation, resolver, fs, callback) {
    return this.doBuild(options, compilation, resolver, fs, (err) = > {
      // We can use Babel to simulate acorn in webpack
      Parser is a complex process
      const result = this.parser.parse(
        this._ast || this._source.source(),
        {},
        cb
      );
      callback();
    });
  }
  doBuild() {
    This is the time when the loader executes. The code is processed by the loader and parsed by the parse
    // The runLoaders process is also skipped relatively independently
    runLoaders({}, (err, result) = > {
      returncallback(); }); }}Copy the code

2. NormalModuleFactory

class NormalModuleFactory {
  constructor(context, resolverFactory, options) {
    this.resolverFactory = resolverFactory;
    this.hooks.factory.tap("NormalModuleFactory".() = > (result, callback) = > {
      let resolver = this.hooks.resolver.call(null);
      resolver(result, (err, data) = > {
        // We need to analyze the resolver hook to understand the parameters in NormalModule
        let createdModule = new NormalModule(result);
      });
    });
    // The absolute path of this step to find files and loader uses the enhanced Node require to find files rule
    this.hooks.resolver.tap("NormalModuleFactory".() = > (data, callback) = > {
      The // argument returns the absolute path of the loader and file module and some other build information
      // Get the parser of the loader
      const loaderResolver = this.getResolver("loader");
      // Files and modules
      const normalResolver = this.getResolver("normal", data.resolveOptions);
      // Inline loader resolution
      // Loader sort Post inline normal Pre is executed in runLoaders in that order
      callback(null, {
        // Return data to NormalModule
        context,
        request: loaders.map(),
        dependencies: data.dependencies,
        userRequest,
        rawRequest: request,
        loaders,
        resoutce,
        parser: this.getParser("javascript/auto"),
        generator: this.getGenerator("javascript/auto")}); }); }// The create method creates the module
  create() {
    // Execute the factory part of the logic analyzed earlier
    const factory = this.hooks.factory.call(null);
    factory(result, (err, module) = > {
      callback(null.module);
    });
  }
  ResolverFactory is in compiler
  getResolver(type, resolveOptions) {
    return this.resolverFactory.get(type, resolveOptions || {});
  }
  getParser() {
    return this.createParser(type, parserOptions);
  }
  getGenerator(type, generatorOptions) {
    return this.createGenerator(type, generatorOptions);
  }
  
  createParser(type, parserOptions) {
    // Create different parsers for different modules
    const parser = this.hooks.createParser.for(type).call(parserOptions);
    this.hooks.parser.for(type).call(parser, parserOptions);
    return parser;
  }
  
  createGenerator(type, generatorOptions) {
    const generator = this.hooks.createGenerator
      .for(type)
      .call(generatorOptions);
    this.hooks.generator.for(type).call(generator, generatorOptions);
    returngenerator; }}Copy the code

3.ResolverFactory

/ / in the compiler
this.resolverFactory = new ResolverFactory();

class ResolverFactory extends Tapable {
   // The get method gets different loader processes
  get(type, resolveOptions) {
    const newResolver = this._create(type, resolveOptions);
    return newResolver;
  }
  _create(type, resolveOptions) {
    // type Corresponds to the tap type of webpackOptionsApply, normal Loader
    resolveOptions = this.hooks.resolveOptions.for(type).call(resolveOptions);
    // Create the resolver enhanced-resolve enhanced path resolution package
    // Todo does not analyze here
    const resolver = Factory.createResolver(resolveOptions);
    returnresolver; }}Copy the code

4.WebpackOptionsApply

This is where EntryOptionPlugin() was mounted and many others

// javascript parsers and template-generated apis are mounted in JavascriptModulesPlugin
new JavascriptModulesPlugin().apply(compiler); / / js module
// new JsonModulesPlugin().apply(compiler); / / json module

// moduleIds
// chunkIds

// Provide the default argument for factory.createresolver to trigger the hook in ResolverFactory
// compiler.resolverFactory.hooks.resolveOptions.for("normal").tap();
// compiler.resolverFactory.hooks.resolveOptions.for("loader").tap();

Copy the code

5. JavascriptModulesPlugin

class JavascriptModulesPlugin {
  apply(compiler) {
    compiler.hooks.compilation.tap(
      "JavascriptModulesPlugin".(compilation, { normalModuleFactory }) = > {
        normalModuleFactory.hooks.createParser
          .for("javascript/auto")
          .tap("JavascriptModulesPlugin".(options) = > {
            // Acorn is used by Parser in webpack
            return new Parser(options, "auto");
          });

        normalModuleFactory.hooks.createGenerator
          .for("javascript/auto")
          .tap("JavascriptModulesPlugin".() = > {
            return newJavascriptGenerator(); }); }); }}Copy the code

6.Compilation

class Compilation extends Tapable {
  _addModuleChain(context, dependency, onModule, callback) {
    moduleFactory.create({}, (err, module) = > {
      // Handle module dependencies if there are dependencies
      const afterBuild = () = > {
        How do we find module dependencies
        // Get ast syntax tree traversal to find dependency collection
        this.processModuleDependencies(module.(err) = > callback(null.module));
      };
      // The building module calls the build method of the module as module.build(
      this.buildModule(module.false.null.null.(err) = > {
        afterBuild();
      });
    });
  }
  // The thing about Webpack that is so disgusting is that it kills people with all the callbacks
  buildModule(module, optional, origin, dependencies, thisCallback) {
    module.build({}, (err) = > {
      return thisCallback();
    });
  }
  // The module builds with dependencies. Where do these dependencies come from? Analyze require and import after generating ast
  processModuleDependencies(module, callback) {
    module.dependencies.forEach((item, callback) = > {
      // Get the corresponding factory
      const factory = item.factory;
      // Factories create modules and build recursively
      factory.crete({}, (err, dependentModule) = > {
        this.buildModule(); }); }); }}Copy the code

7. To summarize

When we execute module.build => runLoaders will first process loader => and then use the parser to generate ast for analysis. We will mainly analyze some parameters in module We used the enhanced-resolve path resolution package to find the absolute path to the resource. 2. Where does js Parse come from? JavascriptModulesPlugin will tap the corresponding hook and execute the webpack function will apply() 3. In the ModuleFactory resolver we will return the parameters to create the Module (preferably to find the path and build some parameters). Next we will analyze parse's process to generate an AST to find dependencies to recursively handle // briefly describe the module packaging process. Find the absolute path of module and loader in normalFacroty resolver flow 2. The read content is processed by loader to obtain the runLoaders process in JS Module 3. Parse JS to obtain ast syntax tree 4. Analyze ast dependencies (require, import) analyze module dependencies 5. Recursive compilationCopy the code