Abstract

The integration of modern JavaScript toolchains such as bunJS and esbuild into Adobe Experience Manager (AEM) poses intriguing possibilities for optimizing frontend build processes. This article delves into a comparative analysis, assessing the feasibility, performance, and compatibility of these tools as alternatives to the traditional npm and Webpack setup.

1. Introduction

In the realm of web development, Adobe Experience Manager (AEM) stands as a pivotal platform for delivering compelling digital experiences. Essential to this delivery is the efficiency and reliability of frontend build processes. Traditionally dominated by npm and Webpack, these processes face new potential shifts with the emergence of bunJS and esbuild. This article aims to explore these modern JavaScript toolchains within the context of AEM’s frontend development, seeking to uncover performance improvements and possible simplifications in build processes.

2. Background

AEM’s latest archetypes come equipped with an Out-Of-The-Box (OOTB) ui.frontend module, facilitating the creation of client libraries through a preconfigured build process. This process, while comprehensive, involves several steps including bundling, static resource copying, and client library configuration, primarily leveraging Webpack and its associated plugins. To get a better understanding of the default process, it is highly recommended to hava a look at Adobes sketches related to this topic:

High Level Clientlib-Organization
Source: Adobe
UI-Frontend Architecture
Source: Adobe

If you are unfamiliar with the AEM ui.frontend build process, it is highly recommended to read through Adobe’s documentation

With advancements in JavaScript tooling, bunJS and esbuild as notable contenders, promising enhanced performance and simplified configurations.

Bundler speed
Source: https://bun.sh/blog/bun-bundler

3. Methodology

The evaluation begins with an in-depth analysis of the necessary Webpack plugins in AEM’s standard build process, identifying potential equivalents within the bunJS ecosystem. However, A critical aspect of modern frontend development is the use of SASS for styling. The bundler included in bun, at the time of this study, does not natively support SASS, posing a significant limitation for its adoption in projects where SASS is extensively used. While it is possible to develop custom plugins to bridge this gap, and some community members have indeed taken this initiative, there lacks an Out-Of-The-Box (OOTB) solution comparable to what is available with esbuild - A widely recognized and easy to use sass-plugin called esbuild-sass-plugin. And as this project aimed to maintain migration steps as concise and straightforward as possible, avoiding the complexities of custom plugin development was a good reason to use the esbuild bundler, which is just barely slower than the bun bundler, instead. Following this, a step-by-step integration strategy is outlined, detailing the conversion of AEM’s build pipeline to incorporate bunJS for package management and esbuild for module bundling. The integration of bunJS and esbuild into AEM’s frontend development process was conducted through a systematic approach, leveraging the latest tools and practices. This step-by-step guide outlines the specific actions taken, using a new AEM archetype as a reference for this documentation.

Step 1: Installing bun

The initial step involves the installation of bun, a prerequisite for utilizing its features within our project. Detailed instructions are available on the official bun documentation page. This step ensures that the environment is prepared for bun’s integration.

Step 2: Configuring the Maven Frontend Plugin

A significant aspect of the frontend build process revolves around the use of the maven frontend-plugin, which traditionally facilitates the installation and execution of Node & Npm packages within the Maven build process. The eirslett/frontend-maven-plugin, as of version 1.15.0, offers native support for bun. Upgrading to this version and configuring our Project Object Model (POM) files to use bun instead of npm is a critical step.

In the project’s pom.xml, the plugin can be reconfigured as following:

...
 
<plugin>
    <groupId>com.github.eirslett</groupId>
    <artifactId>frontend-maven-plugin</artifactId>
    <version>1.15.0</version>
    <configuration>
        <installDirectory>target</installDirectory>
    </configuration>
    <executions>
        <execution>
            <id>install bun runtime</id>
            <goals>
                <goal>install-bun</goal>
            </goals>
            <configuration>
                <bunVersion>v1.0.10</bunVersion>
            </configuration>
        </execution>
        <execution>
            <id>bun install</id>
            <goals>
                <goal>bun</goal>
            </goals>
            <configuration>
                <arguments>install</arguments>
            </configuration>
        </execution>
    </executions>
</plugin>
 
...

This involves the removal of npm residues by deleting local node, node_modules folders and the package-lock.json in our AEM project’s ui.frontend module, followed by executing the bun install command, which generates a bun.lockb file and reinstates the node_modules folder.

Step 3: Installing Additional Packages

To fully equip the project for the transition, several additional packages are necessary:

bun install bun-types
bun install esbuild
bun install esbuild-sass-plugin
bun install fs-extra

These packages lay the foundation for a robust build process capable of handling AEM’s frontend development requirements.

Step 4: Configuring the Bundler

With the necessary tools installed, the next step involves configuring the bundler through the creation of an esbuild.config.js file. This configuration mirrors the structure and approach of the OOTB Webpack build, adapted for esbuild’s syntax and capabilities. The configuration specifies entry points, output directories, plugins, and other options essential for a successful build.

// build.js
const esbuild = require('esbuild');
import { sassPlugin } from 'esbuild-sass-plugin';
const fs = require('fs-extra');
const path = require('path');
 
//Didn't adapt source directory for this PoC
const SOURCE_ROOT = path.join(__dirname, 'src/main/webpack');
const DIST_PATH = path.join(__dirname, 'dist/clientlib-site');
 
// Start the build process
await esbuild
    .build({
        entryPoints: [path.join(SOURCE_ROOT, 'site/main.ts')],
        bundle: true,
        outdir: DIST_PATH,
        minify: true,
        plugins: [sassPlugin()],
        entryNames: 'site',
    })
    .then(() => {
        console.log('Build completed');
    })
    .catch(() => process.exit(1));
 
//Copy static resources
await fs.copy(path.join(SOURCE_ROOT, 'resources'), DIST_PATH)
    .then(() => console.log('Resources copied successfully.'))
    .catch((err) => console.error('Error copying resources:', err));
 

Step 5: Adjusting the Build Script

Finally, to integrate this new build process into the project’s workflow, a new script is added to the package.json file:

...
 
"aemesbuild": "bun run ./esbuild.config.js && clientlib --verbose"
 
...
 

This script combines the execution of the esbuild bundling process with the verbose generation of AEM client libraries, encapsulating the build process in a single command. Modifications to the POM file are also required to standardize this script as the default build mechanism.

In the ui.frontend’s pom.xml, the plugin can be reconfigured as following:

...
 
<build>
    <sourceDirectory>src/main/content/jcr_root</sourceDirectory>
    <plugins>
        <plugin>
            <groupId>com.github.eirslett</groupId>
            <artifactId>frontend-maven-plugin</artifactId>
            <executions>
                <execution>
                    <id>bun run aemesbuild</id>
                    <phase>generate-resources</phase>
                    <goals>
                        <goal>bun</goal>
                    </goals>
                    <configuration>
                        <arguments>run aemesbuild</arguments>
                    </configuration>
                </execution>
            </executions>
        </plugin>
 
        ...
</build>
 
...
 

NOTE: This is just an example and does not include a differenciation between a development & a production build version and offers no support for a watcher (Altough it is technically possible). This is just a PoC.

4. Findings

Initial investigations reveal a mixed compatibility landscape. While bunJS offers a promising alternative to npm with native support for various JavaScript features, the lack of widespread support for essential tools like SCSS plugins poses challenges. Conversely, esbuild, with its robust plugin support, presents a viable alternative to Webpack. However, the integration of these tools within AEM’s complex environment is not straightforward, requiring custom configurations and adaptations. Performance benchmarks highlight significant improvements in build times and efficiency when utilizing esbuild over traditional Webpack setups.

The described method had been tested within an example project and achieved the following results:

NPM + Webpack

...
-----------------
BUILD SUCCESS
-----------------
Total time: 43.592s
...

Bun + esbuild

...
-----------------
BUILD SUCCESS
-----------------
Total time: 1.908s
...

NOTE: This is not a scientific research nor an evidence for a general improvement, but it gives us a brief understanding where this journey could continue…

5. Discussion

The persistent use of npm and Webpack within the AEM community can be attributed to their extensive documentation, wide-ranging plugin ecosystems, and the familiarity developers hold with these tools. Although bunJS and esbuild offer compelling benefits, such as performance enhancements and simplified configurations, their adoption faces obstacles including plugin compatibility issues and the need for further development to fully support certain AEM’s frontend build requirements.

6. Conclusion

This analysis reveals that while bunJS and esbuild hold potential for optimizing AEM’s frontend development process, significant challenges remain. The performance improvements and simplified build configurations they offer must be weighed against the current limitations in plugin support and compatibility with AEM’s complex project structures. As the tooling landscape evolves, further research and community engagement are essential to unlocking the full potential of these modern JavaScript toolchains within AEM.


Disclaimer

It is important to note that this article aims to present a streamlined overview of the feasibility and outcomes of integrating bunJS and esbuild into AEM’s frontend development process. To achieve this, certain technical details and complexities have been simplified. The goal is to provide a clear and accessible understanding of the potential improvements and challenges without delving into the intricacies of each step or configuration.

References