What is loader

Loader and plugins are two important components of webpack system. Depending on different combinations of Loader and plugins, we can flexibly customize the packaging and construction process that is highly suitable for our own business.

Loader is an important means for Webpack to accommodate all kinds of resources. It is used to convert the source code of modules, allowing you to preprocess files when importing or loading modules. With Loader, we can convert various types of resources into the resource types accepted by Webpack. Such as javascript.

How to write a YAML-loader

1, YAML

Yaml is mostly used to write configuration files. The structure of YAML is similar to THAT of JSON, but the syntax format is more convenient and concise. Yaml supports annotations, which are case-sensitive and use indentation to indicate hierarchy:

# object
version: 1.24.
# array
author:
  - Mike
  - Hankle
# constants
name: "my project" # define a string
limit: 30 # define a value
es6: true Define a Boolean value
openkey: Null Define a null
# anchor point reference
server:
  base: &base
    port: 8005
  dev:
    ip: 120.168117.21.
    < < : *base
  gamma:
    ip: 120.168117.22.
    < < : *base
Copy the code

Is equal to:

{
  "version": "1."."author": ["Mike"."Hankle"]."name": "my project"."limit": 30."es6": true."openkey": null."server": {
    "base": {
      "port": 8005
    },
    "dev": {
      "ip": "120.168.117.21"."port": 8005
    },
    "gamma": {
      "ip": "120.168.117.22"."port": 8005}}}Copy the code

In webPack-based applications, if you want to be able to reference data from yamL files, you need a YAML-loader to support compilation. In general, you can find loaders available on NPM, but if there is no support for them, or if you want some custom conversions, you will need to write your own WebPack Loader.

2. Principle of Loader

Loader is a Node module that exports as a function to be called when converting resources. This function takes a String/Buffer input and returns a String/Buffer return value. The simplest loader looks like this:

// loaders/yaml-loader.js
module.exports = function(source) {
  return source;
};
Copy the code

Loader supports pipeline transfer. For files of the same type, we can use multiple Loaders to process them. These loaders are executed from bottom to top and from right to left, and the return value of one loader is used as the input parameter of the next loader. This mechanism is intended to avoid reinventing the wheel when we write the Loader and focus only on the core functionality that needs to be implemented. Therefore, we can introduce json-loader when configuring:

// webpack.config.js
const path = require("path");

module.exports = {
  // ...
  module: {
    rules: [{test: /\.yml$/.use: [{loader: "json-loader"
          },
          {
            loader: path.resolve(__dirname, "./loaders/yaml-loader.js"}]}};Copy the code

3, start

Thus, the yamL-loader we need to do only one thing: convert the YAML data into a JSON string. Therefore, we can simply implement a YAML-loader like this:

var yaml = require("js-yaml");

module.exports = function(source) {
  this.cacheable && this.cacheable();
  try {
    var res = yaml.safeLoad(source);
    return JSON.stringify(res, undefined."\t");
  } catch (err) {
    this.emitError(err);
    return null; }};Copy the code

It’s that simple. Js-yaml can convert YAML directly into JavaScript objects. If there is no such module, what should we do? Yes, the core work of loader is actually string processing, which is quite disgusting work, especially in this kind of syntactic conversion scenario, the string processing of source code will become extremely complicated. In this case, we can consider an alternative solution that uses the AST syntax tree to help us more easily manipulate transformations.

4. Use AST for source code conversion

Yaml-ast-parser is a Node module that converts YAML into an AST tree. We hand over string parsing to the AST Parser, and it’s much easier to manipulate the AST tree than strings:

const yaml = require("yaml-ast-parser");

class YamlParser {
  constructor(source) {
    this.data = yaml.load(source);
    this.parse();
  }

  parse() {
    // parse ast into javascript object}}module.exports = function(source) {
  this.cacheable && this.cacheable();
  try {
    const parser = new YamlParser(source);
    return JSON.stringify(parser.data, undefined."\t");
  } catch (err) {
    this.emitError(err);
    return null; }};Copy the code

We can use the AST Parser method to convert JSON directly. If we don’t have it or have it customized, we can also manually implement the parse process by iterating through the tree structure. The key step is to process each type of node in the AST syntax tree separately:

const yaml = require("yaml-ast-parser");
const types = yaml.Kind;

class YamlParser {
  // ...
  parse() {
    this.data = this.traverse(this.data);
  }

  traverse(node) {
    const type = types[node.kind];

    switch (type) {
      / / object
      case "MAP": {
        const ret = {};
        node.mappings.forEach(mapping= > {
          Object.assign(ret, this.traverse(mapping));
        });
        return ret;
      }
      / / key/value pair
      case "MAPPING": {
        let ret = {};
        / / verification
        const keyValid =
          yaml.determineScalarType(node.key) == yaml.ScalarType.string;
        if(! keyValid) {throw Error("Invalid key value");
        }

        if (node.key.value == "< <" && types[node.value.kind] === "ANCHOR_REF") {
          // Reference merge
          ret = this.traverse(node.value);
        } else {
          ret[node.key.value] = this.traverse(node.value);
        }
        return ret;
      }
      / / constant
      case "SCALAR": {
        returnnode.valueObject ! = =undefined ? node.valueObject : node.value;
      }
      / / array
      case "SEQ": {
        const ret = [];
        node.items.forEach(item= > {
          ret.push(this.traverse(item));
        });
        return ret;
      }
      // Anchor reference
      case "ANCHOR_REF": {
        return this.traverse(node.value);
      }
      default:
        throw Error("unvalid node"); }}}// ...
Copy the code

Of course, such implementation is a little rough. Normally, some complete AST parsers generally have traverse methods, which are optimized so that we can call them directly instead of implementing them manually.

In the same way, you can implement a Markdown-loader, or even a more complex vue-loader.

Iii. Some development skills of Loader

1. Single task

Do one thing, do one thing well. The design of loader pipeline is to separate the task into a sub-task, which is processed by multiple Loaders respectively, so as to ensure the reusability of each loader. Therefore, before developing loader, we must first give loader an accurate function positioning, design from a general perspective, and avoid doing redundant things.

2. Stateless

Loader should not save state. On the one hand, it makes the data flow in loader simple and clear, and on the other hand, it ensures that Loader has good testability. Therefore, our Loader should not rely on its previous compilation results every time it runs, nor should it communicate data with other compilation modules in other ways except entry and exit arguments. Of course, this does not mean that loader must be a pure function without any side effects. Loader supports asynchro, so it is possible to have I/O operations in loader.

3. Use caching whenever possible

Loaders may be executed continuously during development, and proper caching can reduce the cost of repeated compilation. The cache is enabled by default during loader execution. In this way, webPack directly bypasses the rebuild process when deciding whether to recompile the Loader instance during compilation, saving the cost caused by unnecessary reconstruction.

You can turn caching off if and only if your loader has other unstable external dependencies (such as I/O interface dependencies) :

this.cacheable && this.cacheable(false);
Copy the code

If you think this article is valuable to you, please like it and follow us on our official website and WecTeam. We have excellent articles every week