How to create a Webpack 5 Loader with Preons

How to create a Webpack 5 Loader with Preons

·

11 min read

Why a Webpack Loader

Webpack helps make frontend development more streamlined and recently, I wanted to transform YAML into CSS as part of the build process using the Preons library.

By the end of this article:

  • you will create a Webpack loader that converts a YAML configuration file into CSS
  • you will manually test the loader against a basic HTML page
  • you will know how to create your own Webpack loader in future

Background

Preons (and disclaimer, a project of mine) is a functional CSS utility library. You configure the utility classes in YAML.

Webpack is a popular module bundler, and one helpful thing it does is transform Sass into CSS with a few useful community loaders.

Pre-requisites

To follow this tutorial, you need:

Step 1: Creating a new project

You will create a basic empty project and initialise npm to begin installing local node packages.

Execute the following command to create a new project folder:

mkdir custom-webpack-loader && cd custom-webpack-loader

Initialise Node's package manager:

yarn init

You will be asked a series of questions:

yarn init v1.22.4
question name (custom-webpack-loader): 
question version (1.0.0): 
question description: 
question entry point (index.js): 
question repository url: 
question author: 
question license (MIT): 
question private: 
success Saved package.json

By the end, you will have a basic package.json without any dependencies. But you'll add them in a few step.

Step 2 - Configuring Webpack

In this step, you will create a webpack configuration file and tweak it to load a YAML file. It will allow you to test the loader as if you were using it in a project.

Install Webpack and Webpack's CLI to give you access to the Webpack builder:

yarn add webpack webpack-cli

Next, you'll create the webpack.config.js:

touch webpack.config.js

In webpack.config.js, add:

const path = require("path");

module.exports = {
  //...
  mode: "development",
  entry: path.resolve(__dirname, "fixtures") + "/preons.js",
  module: {
    rules: [
      {
        test: /\.ya?ml$/,
        use: [
          "style-loader",
          "css-loader",
          {
            loader: path.resolve(__dirname, "lib") + "/loader.js"
          },
          "yaml-loader",
        ],
      },
    ],
  },
};
  • The mode property tells Webpack to make optimisations based on environments. In production, however, you would change the mode to production. However, for this use case, the default will be development.
  • Notice how the .module.rules[0].test catches .yaml and .yml files based on regex rules.
  • The entry file points to a fixtures directory as that directory has files for you to test.
  • There are four loaders, 3 of which are existing npm packages. Webpack will apply rules from bottom to top, so first, the yaml-loader will take YAML and convert it into JSON. Your loader will transform Javascript into CSS; afterwards, the css-loader will apply its transformation and finally, the style-loader takes css and puts it into style blocks that are inserted into your page through Webpack's script.

Install other dependency loaders using yarn:

yarn add style-loader css-loader yaml-loader

Now you have a basic Webpack config for testing the loader you're going to build.

Step 3 - Importing YAML from our input js file

When initially learning about Webpack, one of the strange conventions is not to deal with non-javascript files through the configuration. Instead, you import them as if they are javascript modules themselves. You'll do the same here.

First. Create the fixtures directory:

mkdir fixtures

Next, you need a Preons config file. To save you from learning the syntax and configuring one yourself, install the Preons utility globally.

npm install preons -g

Generate the Preons YAML config:

preons config > fixtures/preons.yaml

You will see a YAML file containing CSS class rules such as theme-colors, scales and percentages. The file contents should start like this:

preons:
  baseline: 1.6rem
  rules:
    theme-colors:
      black: "#000000"
      white: "#ffffff"
      transparent: transparent
      hotpinkxl: "#f188bc"

Create fixtures/preons.js.

touch fixtures/preons.js

Add a require expression to it, so you import the YAML through it.

require("./preons.yaml");

Now you have configured the webpack loader to take the fixtures/preons.js file and run any YAML imports through the lib/loader.js, and it will spit that back out inside a dist folder. But before that, you'll need the loader.

Step 4 - Creating the loader

Now you can build the loader.js. The minimum is one exported default function that returns a module.exports = <> string.

First, create the loader.js file:

mkdir -p lib && touch lib/loader.js

Add the basic method to test the loader is working:

module.exports = function (source) {
  console.log(source)
  return `module.exports = {}`;
};

At this point, you can run webpack, and you will see the YAML source load in:

npx webpack

The output will be a lot of JSON, because of the console.log(source). You'll also find a dist folder in your project with a main.js file.

With the source containing JSON, transforming using the Preons library is only a few more steps. Install Preons locally into the project to access its lib functions to do the transformation of the preons.yaml into CSS:

yarn add preons

Change the lib/loader.js to parse the YAML JSON into CSS.

const preons = require("preons/src/lib/stylesheet");

module.exports = function (source) {
  let set = JSON.parse(source);
  let css = preons({ set });
  return `module.exports = ${css}`;
};
  • let set = JSON.parse(source) converts JSON into a javascript object required by the Preons stylesheet function.
  • let css = preons({ set }); is a Preons function that transforms the YAML into CSS.
  • returnmodule.exports = ${css}; returns the css as a module to be used by the rest of the Webpack pipeline ruleset.

Now test your webpack pipeline by running:

npx webpack

If you look inside dist/main.js, you will find the entirety of Preons CSS generated. To check the generation works, do a grep for the css Property bg-black:

cat dist/main.js | grep "bg-black"

Step 5 - Testing the webpack loader with HTML

Now you are generating CSS from the Preons YAML configuration file. You will want to test you can develop with it.

Create a raw HTML index.html file in the root of the project:

touch index.html

Then add a simple starter HTML5 file:

<!DOCTYPE html>
<html class="bg-black">
    <head>
        <link href="https://unpkg.com/browse/preons@0.4.5/dist/reset.css" rel="stylesheet" />
        <script src="dist/main.js" ></script>
    </head>
    <body class="bg-red">
        <h1>Hi</h1>
    </body>
</html>
  • The index.html references Preons reset package as well as your dist/main.js file.
  • The markup has Preons utility classes such as bg-black and bg-red. Open the index.html, and it will look similar to this:

image.png

Finally, you want to test that we can make changes to our YAML and it produces CSS instantly.

Start the Webpack dev server to watch for changes using:

npx webpack --watch

Go into the preons.yaml and look for the theme-color -> black. Change it from black: "#000000" to black: "yellow". The file should have this:

#....
  rules:
    theme-colors:
      black: "yellow"

Refresh the page, and the bg-black rule is now yellow.

image.png

Ok. You will want to keep bg-black as it was, but it's just to prove that the generation runs through Webpack. You can now add more colours, change colours or remove them. Of course, you can add any utilities you want using the configuration file. The documentation on how to use the configuration file is still under development, but I'll remember to add it here once it's done.

Conclusion

You built a Webpack loader to ingest YAML and create CSS to be used as part of a development and production build. You used pre-existing modules (e.g. yaml-loader) to keep your module loader concise.

If you want to continue using Preons, look out for the Preons webpack loader package, so you won't have to maintain it yourself.

Do you have any ideas for your own for a webpack loader?

Also, I hope you enjoyed this article. I'd love feedback on how to improve as a writer and as a developer, so feel free to leave a comment or DM me on Twitter.

Did you find this article valuable?

Support Gemma Black by becoming a sponsor. Any amount is appreciated!