aem-frontend-build (3 Part Series)
It's time we release the second part of this series. And get to the actual frontend setup!
if you haven't yet, I recommend you start by reviewing the proposed structure, by reading the first part. It is important for everything else to make sense.
As mentioned, the frontend build will be in a completely separated Maven module. Why? Well, to begin with, because then our backend counterparts can skip that part of the compilation (by, for example, implementing a Maven profile). Frontend build times are exponentially higher now, and sometimes not necessary for some backend features.
Another reason, separation of concerns. It's better to encapsulate code that does something completely specialized. Same as we do with functions, etc, right?
So we will extend our current structure by adding a new module called frontend, like this.
That makes the frontend one, a folder. But when it comes to Maven, in order for it to be a module, you need to add a pom.xml file to it. We will get to this pom file later (or rather, the one that's inside of the package 2 folders below), but now, let's focus purely in frontend matters.
For maven, this will be a module. But for npm, this will be a package. So let's go ahead and make it one with
npm init. As usual, we answer a few questions and get our
package.json. Here I am assuming you have node and npm installed either globally or locally. Now we could potentially, start installing dependencies.
So at a minimum, these are some of the node modules you will need as dev dependencies.
to run the whole show. You will need, at minimum
to transpile your ES. And
to work your scss code.
Also, because you care for code quality, you will want to have due linting configuration and tools, such as
- eslint-config-airbnb-base (this is an industry standard, and the one I follow, with some tweaks, here and there)
Additionally, of course, you would want to keep an eye on your styles with
You will need
- postcss-loader and
and ensure cross browser support.
And because you will have to be traversing your structure to gather clientlib entries here and there, you will also need
- fast-glob (or glob, but fast-glob is...well, faster and lighter)
Once you have all that, run and
NOTE: Whether you make each one a dependency or devDependency, will very much depend on if you need them at build time, or run time. Make conscious decisions!
Now you need to focus on the rest of items inside of this package. So my recommendation is that you create a series of folders to store
- other scripts
So that that folder contents looks something like this
It's time to hit the configuration scene. The configurable aspect of webpack is by far its best feature when it comes to large enterprise platforms, like the one we're discussing, it's essencial.
But since you have so many tools, and you probably want to keep concerns sparated so they're more maintainable, I encourage you to create a configs folder inside your package, and import them (or relevant members) to the webpack config, in aggregation.
That way you will have several configurations in that folder, like this:
One of the most important configuration files, is where you will store all the paths to accomplish your tasks (we call it
poject.paths.config.js here. Since you have to go collect .scss and js entry files from different components and modules, you want to establish certain patterns, and then traverse your structure to fetch them.
For you to have an idea, something like this
Once the example repository is ready, you will be able to see exactly what this file looks like but it's basically a series of constants receiving the paths values to be able to retrieve all the commons and components .scss and .js files, in order to process them. Here is a gist of it
Some of those constants will receive values from the environment (hence they are called environment variables), like NODE_ENV, or whatever other information you need to process at build time.
We will learn more about this in a bit.
Another important piece of information are the aliases. Especially for webpack. You won't want to be counting dots back and forth to import your modules or its members, your utilities and common code. This is why you will want to create project aliases, that you can both use to import JS as much as to include paths for .scss
(here I will have to assume, that you are used to working with webpack, and understand what I am talking about already. If you don't, follow this link )
As usual, utilities may be additional helper functions and snippets that are necessary to run your tasks, but are generic enough that can be called from different sources. It is a good practice to keep them in their own files, and under a utility folder.
Now you have done some configuration and got stocked with utilities, you have to do something with all that! Common tasks you may want to perform include linting your code, compiling your .scss, implementing plugins. All those tasks will live in your tasks folder, and you will be able to execute those tasks directly from your package.json file, when executing
npm run build . Of course you may just use webpack mechanics, default or custom to initiate all those processes, but at large scale, it's not unlikely you need to perform additional operations.
As you've seen me mention before, I really like keeping things separated, so whatever tasks may not be strictly necessary on each run, or are very specific, like generating svg sprites, or processing other assets in any way, etc, you may want to put in this folder.
If you're familiar with how webpack works, you know that it basically takes an entry, processes it, and then produces an output. If you're still not familiar, please read about it here
In the case of a multi tenant project, you will have a collection of entry points to process, and you want to make sure to collect them all, according to a certain pattern (since it's basically impossible to hardcode a path in this case!). So how do you do that? Basically you use glob to traverse your tree and get those files that match, according to a pattern or naming convention, and their extension. Something like this:
Again, if you're familiar with npm, you know that every
package.json has a
scripts property, where you can declare execution commands.
One module that is very helpful, and I recommend you install, is
npm-run-all. As explained here the
npm run-script command can only run one script at a time, so you're much better when you're able to simplify this.
Yes! And how do you make sure you get all the environment variables and have the frontend build running, when you (or Jenkins!) execute a maven build? Learn in the next part of this series!