How Does Webpack Fit Into AEM?

August 2, 2017
How Does Webpack Fit Into AEM?
AEM’s out-of-the-box tools can’t keep up with the rapidly advancing frontend world. Open source projects such as React and Webpack appear and get widely adopted within just a few months or years while AEM doesn’t jump on every trend. This is fine because adding new tools can add complexity to a project, so we have to figure out how far we want to go: What tools do we want to use on top of AEM?

 

For example, I’m a fan of the React ecosystem but it can overwhelm developers, especially in the context of AEM where JavaScript rarely reaches a level of complexity that justifies using React, Redux & friends. So one goal of our integration of Webpack into AEM is to adopt the good parts utilized by modern tools while we keep the setup straightforward enough for developers, no matter how experienced they are.

To get you started with Webpack in AEM, we created an open source project that provides you with step-by-step instructions on how to integrate Webpack into AEM. It also includes a fully working Webpack configuration with some essential plugins: Check out AEM Webpack Example on Github.

 

 

The rest of this article addresses common questions regarding the structure of AEM projects, and we highlight some concepts that become possible thanks to the use of Webpack in AEM.

 

Where to put the Webpack configuration in AEM?

Generally speaking, the job of Webpack is to take a bunch of files, process them somehow, and output the result. In an AEM project, we configure Webpack outside of AEM’s jcr_root folder and output the resulting files (bundles) into a folder we call “webpack.bundles” within the jcr_root folder.

 

 

Those Webpack bundles can be used in different ways. Here are common scenarios:

  1. You can reference them in one or more AEM clientlibs and load those clientlibs on a page. This approach conforms with the “AEM-way” of doing things and is easy to get started with.
  2. You can directly load the bundled files on your page referencing “/etc/designs/webpack.bundles/components.bundle.js”, for example, meaning you don’t use clientlibs for your bundles.
  3. You can refine your Webpack configuration and utilize Code Splitting, a feature to split code into various bundles which can then be loaded on demand or in parallel.

 

How to run Webpack using Maven?

We’ve seen AEM projects where tools such as Webpack and Grunt are run in addition to Maven only, meaning developers run Webpack to build (mostly) front end code, then run Maven to build the rest of the project. That’s inconvenient and time-consuming, especially for deployment to multiple AEM servers and continuous integration. Just imagine your back end developer pulling the latest code and not seeing the latest CSS changes because he ran Maven but forgot about Webpack.

For that reason, you can run Webpack using Maven. Our go-to solution is Eirik’s frontend-maven-plugin. You can see an example of its integration in our pom.xml file.

If Webpack is part of the Maven build process, it can be run in two ways:

  1. Run Webpack directly from the terminal (and thus independently from Maven). This empowers developers to only run Webpack-specific tasks, for example a task that watches for changes to JavaScript files and then automatically triggers a build. Maven-only tasks are ignored.
  2. Run Webpack as part of Maven.

Note: If you want your back end developers or DevOps to be able to run Maven without Webpack, you can set up a separate Maven build profile that doesn’t run Webpack.

 

Where to put JavaScript files?

There are different ways to organize JavaScript files that should be processed by Webpack. To take full advantage of Webpack and modern JavaScript features, it’s best practice to organize your JavaScript in modules (examples). Assuming your JavaScript files follow the module syntax, we differentiate between two types of modules:

  1. AEM component-specific JavaScript. The purpose of a component-specific module is to enhance a specific AEM component, for example by making its HTML interactive in some way. The idea is to write one module per component that does not affect any other component. You can say the JavaScript is scoped to the HTML of one component. This prevents multiple components from conflicting with each other. [For a component to be scoped perfectly, more aspects have to be taken into account. For example, the CSS must be scoped to your component as well, and you also have to make sure that `this` is bound correctly when writing callbacks and asynchronous JavaScript.]
  2. Generic JavaScript modules. Generic JavaScript is not tied to a specific AEM component. Generic modules or libraries of modules get imported into component-specific JavaScript modules when needed. Utility libraries such as Underscore and Lodash are part of this category but you should also write your own generic modules for functionalities that should be available to multiple components.

Here are the three most common ways for organizing JavaScript in your AEM tree structure. We favor a combination of (1) and (3):

  1. Place every JavaScript file into one big folder or tree of folders. No matter what your JavaScript does and which components it affects, the file is dumped into one folder with all other files. This approach makes sense for generic JavaScript modules because it’s convenient to access them from one place. However, for component-specific modules, this approach gets messy in the long-term. Imagine you remove a component from AEM. How do you make sure that the associated JavaScript file gets removed as well? Also, imagine you want to copy a component from one project to another. How do you make it as easy as possible that the associated JavaScript file or files get copied with the component?
  2. Place a JavaScript file right into the folder of an individual AEM component. Let’s say you have an input component folder that has a file “input.html”, then you’d add a file “input.js” next to it. That works fine for small and simple projects but gets messy once your components get more complex and it would make sense to split up your “input.js” file into several files. It can also cause confusion with server-side JavaScript files that might be placed in the same folder as well.
  3. Place a JavaScript file into a “webpack.module” folder within an individual component. This approach is our favorite for component-specific JavaScript because it is very explicit and solves the concerns mentioned above.
 

 

How to write component-specific JavaScript in AEM?

Preprocessing JavaScript using Webpack enables us to use JavaScript features that aren’t supported by AEM or web browsers yet. The image below shows what component-specific JavaScript can look like:

 

 

Here are some of our key concepts when writing component-specific JavaScript:

  • We organize component-specific JavaScript in classes.
  • Whenever code can be reused in at least two components, we abstract it into a generic module that then can be imported into each component.
  • A generic AEM module handles the initialization of each JavaScript class when the page loads. It iterates over all registered components and creates a new instance for each class. (See screenshot below.)
  • By extending the super component “AEM.Component” that is part of our AEM module, we inherit methods and properties that should be available across all components. In this case, our super component provides “this.element” and “this.props” from the HTML of each component. While “this.element” simply represents the component’s HTML, “this.props” is a shortcut to access all data attributes of the component.
 

 

How to add Bootstrap?

Our recommendation for including libraries such as Bootstrap is to include them with AEM’s ClientLibs and only run them through AEM’s preprocessors, not through Webpack. By keeping these libraries out of Webpack, you keep the build lean, you don’t increase Webpack’s processing time, and you avoid conflicts between the vendor library and your project-specific setup, such as ESLint rules. We added an example for Bootstrap to the project wiki.

Assuming you’re interested in using Webpack in AEM, you should check out AEM Webpack Example on Github. We regularly use it to kickstart new projects, and the above mentioned insights will help you to write better modular code in Adobe Experience Manager.

And by the way, you can also partner with us for your upcoming projects.

 

The content of this post was originally presented by Kevin Weber at the Adobe Experience Manager Meetup in San Francisco. Here are the slides: