Which Loaders are needed to compile less files?

{
    test: /.css$/,
    use: ['style-loader'.'css-loader'.'less-loader']}Copy the code

In order of execution, loaders execute later loaders first and then earlier loaders.

That is, the execution sequence for less files is less-loader, CSS-loader, and style-loader.

Ii. Implementation principle of Loader

A loader is a node module that exports a function. This function is called when a resource should be transformed by this loader. The given function will have access to the Loader API using the this context provided to it.

Loader is a module in a node that exports a function. This loader is called when a file resource needs to be converted. This function accesses the Loader API by providing it with this context.

This is a loader that does nothing.

module.exports = function(source) {

    return source;

}
Copy the code

The loader function takes one argument, which is the contents of the file. In the form of a string, to us developers.

The source above. Is a string containing the contents of a file.

Loader function helper package (Loader-utils)

Loader configuration contains not only the loader name, but also some options configuration options. The main purpose of this package is to provide additional functions, such as obtaining the configured options.

NPM download to local NPM install -- Save Loader-utilsCopy the code

Use get options

const loaderUtils = require('loader-utils');

 

nodule.exports = function(source) {

    const options = loaderUtils.getOptions(this);

    console.log(options);

    return source;

}
Copy the code

3, Loader function check package (schema-utils)

We need to verify that the options that we’ve configured are what the Loader expects, and the schema-utils package does that.

NPM download to local NPM install --save schema-utilsCopy the code

Verify the obtained options.

const loaderUtils = require('loader-utils');
const schemaUtils = require('schema-utils');
// Options must pass a property of testProps whose value is of type number.

const schema = {

    type: 'object'.properties: {

        testProps: {

            type: 'number',}}};module.exports =  function (source) {
 
    const options = loaderUtils.getOptions(this);
 
    console.log(options)


// Check function: parameter 1, check rule. The second parameter is the options parameter.

    schemaUtils.validate(schema, options, {

        name: 'test-loader'.baseDataPath: 'options'});console.log(source)

    return `/** my name is liwudi **/\n${source}`;

}
Copy the code

Loader parse the CSS compilation part

1. What are the functions of each Loader

  • Css-loader: Handles @import and URL () just as JS parses import/require().

  • Style-loader: Inserts CSS into the DOM.

  • Less-loader: compiles less as the CSS loader.

2,

Less – loader resolution:

import less from 'less';
const { css, imports } = less.render(source, lessOptions);
Copy the code

CSS is what you end up with. Less is officially compiled as a CSS string. Less loaders call less library methods, translate less syntax, and output something like this:

// Less-loader implementation (simplified)

 const less = require('less');

  module.exports = function(content) {

     const callback = this.async(); // Translation is time-consuming and asynchronous

     const options = this.getOptions(); // Obtain the options of less-loader in the configuration file

     less.render(

          content,

          createOptions(options), // less Translation configuration

         (err, output) = > {

             callback(err, output.css); // Pass the generated CSS code to the next loader

          }// The third argument is a callback. If the callback is not passed, the render method returns a promise

     );

   };
Copy the code

Processing of compiled results

function processResult(loaderContext, resultPromise) {

  const { callback } = loaderContext;

  resultPromise

    .then(({ css, map, imports }) = > {

      imports.forEach(loaderContext.addDependency, loaderContext);

      return {

        // Removing the sourceMappingURL comment.

        // See removeSourceMappingUrl.js for the reasoning behind this.

        css: removeSourceMappingUrl(css),

        map: typeof map === 'string' ? JSON.parse(map) : map,

      };

    }, (lessError) = > {

      throw formatLessError(lessError);

    })

    .then(({ css, map }) = > {

      callback(null, css, map);

    }, callback);

}
Copy the code

The compiled results include CSS, map, and imports. CSS is the content compiled by less, map is sourceMap information, and imports are all dependent file paths during compilation. After getting the build results, first call addDependency to add all imports files to the dependencies. This method works when the dependency files change while in Watch mode. The next is removeSourceMappingUrl(CSS). This method removes the sourceMappingURL= from the result. The reason is that the less loader has no way of knowing where the final sourceMap will be. Callback (NULL, CSS, map) is called to pass the result to the next loader, and the less-Loader is done

CSS – loader resolution:

The csS-Loader is used to parse @import and URL statements in CSS files, process css-modules, and return the result as a JS module if we have a.ss, B.SS, c.SS:

// a.css

@import './b.css'; / / import biggest ss

.a {
  font-size: 16px;
}

// b.css
@import './c.css'; / / import Arthur c. ss
.b {
  color: red;
}

// c.css
.c {
  font-weight: bolder;
}
Copy the code

The compiled output for a.ss by csS-Loader:

/ / CSS - loader output

exports = module.exports = require(".. /.. /.. /node_modules/css-loader/lib/css-base.js") (false);

// imports

// File needs to rely on the JS module, this is empty...// module

exports.push([ // Module export content

  module.id,

  ".src-components-Home-index__c--3riXS {\n  font-weight: bolder;\n}\n.src-components-Home-index__b--I-yI3 {\n  color: red;\n}\n.src-components-Home-index__a--3EFPE {\n  font-size: 16px;\n}\n".""

]);

// exports

exports.locals = { // Class name mapping for css-modules

  "c": "src-components-Home-index__c--3riXS"."b": "src-components-Home-index__b--I-yI3"."a": "src-components-Home-index__a--3EFPE"

};
Copy the code

It can be understood that csS-Loader concatenates the style contents of A.SS, B.SS, and C. SS in the form of strings and uses them as the exported content of JS module.

// CSS -loader source code

// https://github.com/webpack-contrib/css-loader/blob/master/src/index.js

import postcss from 'postcss';

module.exports = async function (content, map, meta) {

  const options = this.getOptions(); // Get the configuration

  const plugins = []; // PostCSS plug-in required to translate the source code

  shouldUseModulesPlugins(options, this) && plugins.push(modulesPlugins); / / handle CSS - modules

  shouldUseImportPlugin(options, this) && plugins.push(importPlugin); // Handle the @import statement

  shouldUseURLPlugin(options, this) && plugins.push(urlPlugin); // Process the URL () statement

  shouldUseIcssPlugin(options, this) && plugins.push(icssPlugin); // Processing icSS related logic

  if (meta && meta.ast) { // Reuse the CSS AST generated by the previous loader (e.g. Postcss-loader)
    content = meta.ast.root;
  }
  const result = await postcss(plugins).process(content); // Translate the source code using PostCSS

  const importCode = getImportCode(); // Dependent statements to import

  const moduleCode = getModuleCode(result); // Module export content

  const exportCode = getExportCode(); // Other information that needs to be exported, such as the class name mapping of CSS-modules, etc
  
  const callback = this.async(); // Return asynchronously

  callback(null.`${importCode}${moduleCode}${exportCode}`);

};
Copy the code

Style – loader resolution:

The complete CSS style code is translated by csS-Loader, which inserts the result into the DOM tree as a style tag.

// style-loader

import loaderUtils from 'loader-utils';

module.exports = function (content) {

  // do nothing

};

module.exports.pitch = function (remainingRequest) {

  /* * obtain the js module export returned by CSS - Loader with require statement * use '!! * Use the remainingRequest parameter to get the rest of the Loader chain. In this case, it is csS-Loader and less-Loader * that use the stringifyRequest method of loaderUtils to convert the absolute path in the request statement into a relative path */

  const requestPath = loaderUtils.stringifyRequest(this.'!!!!! ' + remainingRequest);

  // In this example, the requestPath is:

  / / '!!!!! . /node_modules/css-loader/index.js! . /node_modules/less-loader/dist/cjs.js! src/styles/index.less'

  return `

    const content = require(${requestPath})

    const style = document.createElement('style');

    style.innerHTML = content;

    document.head.appendChild(style);

  `;

};
Copy the code

About PostCSS: github.com/postcss/pos…