Front-ends for AEM pre-2017
- How do we write modern ES6 syntax?
- What about linting?
- How do we have good separation between authoring and publish clientlibs?
- How do we support a multi-tennant lifestyle?
- Why are we using tools that have overlap? i.e. NPM and Bower
My front-end proposal scales better than most solutions while supporting ultra-modern front-end standards.
Modern front-ends for AEM in 2017
Let's talk tools, the whys, and then about the hows.
- Dependency management: NPM
- Task Runner: Webpack
- Bundler: Webpack
- Transpiling: Babel
- Linting: ESLint + Airbnb Style Guide
- Syntax: ES6 & LESS
- Templating: TBD
We're using NPM instead of Bower because there's no longer a need for Bower... most FEDs understand package.json these days.
We're using webpack because it's a decent task runner and an excellent bundler... let's not use Grunt / Gulp to then run webpack... one tool. Webpack also doesn't need to be installed globally which makes on-boarding easier.
We use Babel to transpile our ES6 into something browsers can understand. It's widely documented which, again, helps on-boarding.
ES6 is the future (technically the present), let's start using it. LESS is AEM friendly, so if we ever decide to use AEM's native LESS compiler, we're not having to re-write everything.
Templating... templating is a challenging one that I don't think has been solved sufficiently. Handlebars is a natural fit, but it misses the mark in certain contexts. If I'm doing a new AEM project today, I'm using HBS, but I'm yearning for something better.
So, how does all of this work?
The high level overview
- We start with a simple folder for our area of concern... publish, author, theme, bu1, bu2, whatever.
- It uses .content.xml, css.txt, and js.txt to create a proper clientlib.
- It has src and dist folders in it.
- Inside src are our source files to be run through eslint, babel, and webpack.
- Our dist folder consumes the output.
About our clientlib definition
If you've never worked with AEM, you might just think it's OK to drop a script tag on your page and be done. Unfortunately, you will end up hobbling AEM functionality and make more work for your back-end developers. Wrapping your JS (minified or not) inside a proper clientlib is incredibly important and will reduce work in the long run. Certain granite views are built around consuming a namespaced clientlib, and if you wish to view the raw JS files on non-dispatched servers (i.e. 4502), BE winds up needing to write content disposition configs because AEM serves up raw JS as attachments when they're not proper clientlibs.
We add a property to our clientlib definition to allow it to be proxied out to
/etc.clientlibs/. This reduces folder traversal... we're no longer digging into
/apps/ourproject/components. Everything just sits in
Our webpack config
The basics is that we have entries, outputs, rules, and plugins.
The entries exist in the src folder, outputs go in the dist folder, rules and plugins should be fairly readable...
With our three entries (clientlibs) we have a lot of ground covered while being separated into their areas of concern:
- Base Publish - All structure and base-level features consumed by all parties (authors, visitors, etc.)
- Base Author - All structure and base-level features that are only consumed by authors. Everything from TouchUI hacks to Dialog listeners.
- Base Theme - All LESS / JS that can be individualized based on a tenant's requirements (think font families, margin & padding sizes, etc.)
With one maven command, everything is pulled in and setup to start working:
mvn clean install -PautoInstallPackage
Things differ a bit here for BE and FE. BE can continue to use the maven tools they're use to. FE can switch over to start using tools they're use to:
<new terminal tab>
npm run aemlocal
Want to see how it all works? Go check the source code.