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…