2

Preface

He said we got the factory, and other critical data and rely build modlule by addModuleTree through the above factorizeQueue control come factory.create . This time began the reslove process.
This article mainly analyzes the internal beforeResolve of factorize , resolve , afterResolve NormalModuleFactory inside 0617551da42a2f.

Configuration file

The configuration files surrounding this article are as follows:

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /\.(js?|tsx?|ts?)$/,
        use: [
            {
                loader: 'babel-loader',
            },
        ],
    },
    ]
  },
  resolve: {
    extensions: ['.js', '.ts'],
    alias: {
      demo: path.resolve(__dirname, 'src/second'),
    },
  },
};

factory.create

The entrance starts at factory.create , where factory is the addModuleTree obtained NormalModuleFactory
image.png

NormalModuleFactory first triggered its internal beforeResolve , and then executed the hook function factorize
factorize within the hook and there are calls resolve .

It looks rather convoluted here, just explain it briefly:
The calling sequence of the hooks is like this.
beforeResolve -> factorize -> resolve

  • beforeResolve I didn’t find the place I registered before. It seems that I didn’t do anything, or I didn’t find it.
  • factorize has been ExternalModuleFactoryPlugin plugin before, and the information external
  • resolve hook is registered inside NormalModuleFactory and is used to parse this module and generate the corresponding loader and dependency information. The focus here is on resolve

resolve

getLoaderResolver

In the first step, the resolve hook first calls this.getResolver("loader") to return to the loaderResolver, which can be understood as a method to resolve the loader.
Simply divided into the following steps:

  • Calls to the ResolverFactory in the get method
  • Determine whether there is a corresponding type of cache
  • Created resolveOptions ,
  • Call require("enhanced-resolve").ResolverFactory create a resolver , and then return to NormalModuleFactory to continue executing the code.
const loaderResolver = this.getResolver("loader");

loaderResolver exposes a resolver method for parsing loader .

normalResolver

Then go down, skip some judgment, walked directly defaultResolve this method here will be based on webpack profile resolve option to generate a normalResolver . Similarly, this normalResolver is also require("enhanced-resolve").ResolverFactory , which also exposes a resolve method.

const normalResolver = this.getResolver(
    "normal",
    dependencyType
        ? cachedSetProperty(
            resolveOptions || EMPTY_RESOLVE_OPTIONS,
            "dependencyType",
            dependencyType
      )
    : resolveOptions
);

Next, the normalResolver and some context information will be passed to the resolveResource method, which will eventually call node_modules/enhanced-resolve/lib/Resolver.js of doResolve .

this.resolveResource(
    contextInfo,
    context,
    unresolvedResource,
    normalResolver,
    resolveContext,
    (err, resolvedResource, resolvedResourceResolveData) => {
        if (err) return continueCallback(err);
        if (resolvedResource !== false) {
            resourceData = {
                resource: resolvedResource,
                data: resolvedResourceResolveData,
                ...cacheParseResource(resolvedResource)
            };
        }
        continueCallback();
    }
);

Then according to doResolve returned resolvedResource and resolvedResourceResolveData assembled together into resourceData . We will use this in the subsequent parsing of the loader.

resourceData data structure
image.png

Parsing loader

Continue execution in the callback of resolvedResource

const result = this.ruleSet.exec({
    resource: resourceDataForRules.path,
    realResource: resourceData.path,
    resourceQuery: resourceDataForRules.query,
    resourceFragment: resourceDataForRules.fragment,
    scheme,
    assertions,
    mimetype: matchResourceData
    ? ""
    : resourceData.data.mimetype || "",
    dependency: dependencyType,
    descriptionData: matchResourceData
    ? undefined
    : resourceData.data.descriptionFileData,
    issuer: contextInfo.issuer,
    compiler: contextInfo.compiler,
    issuerLayer: contextInfo.issuerLayer || ""
});

Here we will get the required loader rules in the configuration file. In result is
image.png

This will be followed by result traversing generate useLoadersPost , useLoaders , useLoadersPre .
Then call resolveRequestArray get postLoaders, normalLoaders, preLoaders .

this.resolveRequestArray(
  contextInfo,
  this.context,
  useLoaders,
  loaderResolver,
  resolveContext,
  (err, result) => {
    normalLoaders = result;
    continueCallback(err);
  }
);

The current example does not have postLoaders and preLoaders , here only normalLoaders . resolveRequestArray internally calls loaderResolver.resolve parse useLoaders , and the final result is to result with the corresponding real file address.

{
  ident:undefined
  loader:'/Users/csy/Code/webpack5/node_modules/babel-loader/lib/index.js'
  options:undefined
}

Generate callback data

Finally, the data that has been generated under the processing of continueCallback , the first is the merging loader postLoaders, normalLoaders, preLoaders these 0617551da430fa. Then assign click data.createData , this data comes from the data passed in by the hook entry.

Object.assign(data.createData, {
    layer:
        layer === undefined ? contextInfo.issuerLayer || null : layer,
    request: stringifyLoadersAndResource(
        allLoaders,
        resourceData.resource
    ),
    userRequest,
    rawRequest: request,
    loaders: allLoaders,
    resource: resourceData.resource,
    context:
        resourceData.context || getContext(resourceData.resource),
    matchResource: matchResourceData
        ? matchResourceData.resource
        : undefined,
    resourceResolveData: resourceData.data,
    settings,
    type,
    parser: this.getParser(type, settings.parser),
    parserOptions: settings.parser,
    generator: this.getGenerator(type, settings.generator),
    generatorOptions: settings.generator,
    resolveOptions
});

Here I will focus on getParser and getGenerator . These two methods return the parser of the corresponding file and the method of constructing the template. According to the current example, JavascriptParser and JavascriptGenerator .

Then this createData will be used for createModule .
In the complete execution NormalModuleFactory of afterResolve after hook

const createData = resolveData.createData;
this.hooks.createModule.callAsync(//something)

reslove is over, and the next step is about to start, create module !

summary

  • The module resolve process is used to obtain information such as the absolute path of each loader and module.
  • In the resolver hook, first obtain loaderResolver through enhanced-resolve and provide the resolve method
  • In the defaultResolve method, get normalResolver and provide the resolve method.
  • Parse unresolvedResource to get the absolute path of the file and other information
  • Get loader according to rules
  • Use loaderResolver get the absolute path of the loader and other information
  • Merging loader, splicing data,
  • Call NormalModuleFactory of afterResolve hook, ending resolve process.

csywweb
232 声望8 粉丝

最担心的事不是写出ipcode,而是从不开始