One of the most time-consuming and frustrating parts of modern front-end web development is setting up build tooling. As an industry, we've been through a lot of different tools in recent history – some easier to work with than others – and one popular option in 2017 is Webpack. While incredibly powerful, unfortunately at first glance many Webpack configuration files make very little sense, and even with some help from Tanya at nz.js(con), I've personally found myself struggling to produce the output I need in a timely manner.
Enter Laravel Mix. With this tool, your build chain is managed almost completely for you, and you can use all the latest and greatest JS libraries and syntax with practically zero configuration. Bespoke teams at SilverStripe are starting to adopt this quite widely, and are seeing extremely positive results. Let's walk through the basics!
Ignore the 'Laravel' part
Before we start, some clarification. Mix can be used in any project – even one without any PHP at all! Laravel Mix (previously Laravel Elixir) was originally developed to be used in tandem with the Laravel framework and is named as such, but has no dependency on it, and works perfectly in SilverStripe projects or anything else you might need it for.
Getting started
First off, you'll need to add Mix to your project, along with cross-env
to allow building in different environments:
# Yarn > yarn add laravel-mix cross-env --dev # NPM > npm install laravel-mix cross-env --save-dev
Then you'll want to copy the default Mix configuration into your project root:
> cp node_modules/laravel-mix/setup/webpack.mix.js .
Now you can open this new configuration file and start pointing Mix at your source code using the mix
object. There are several methods, but your go-tos will generally be js()
and scss()
. To compile the scripts and styles for a basic SilverStripe theme, your configuration would look something like this (assuming your source files are in the correct place):
let mix = require('laravel-mix') const themepath = 'themes/demo' const distpath = themepath + '/dist/' mix.js(`${themepath}/js/app.js`, distpath) .sass(`${themepath}/scss/app.scss`, distpath) .sass(`${themepath}/scss/editor.scss`, distpath)
Time to compile! The full commands are pretty long and annoying to remember, so we'll add some shortcuts to package.json
that are more memorable:
... "scripts": { "once": "node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", "dev": "node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", "production": "node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" }, ...
Now we can run our first build:
# Yarn > yarn run once # NPM > npm run once
You should see a 'Compiled successfully' message, along with a list of files that have been generated. Congratulations, your app is now ready to go! (You might need to actually finish writing it first, of course.)
During development, you can use yarn run dev
to start Mix in watch mode, so it will continuously rebuild your app as you change files. For a production release, call yarn run production
– this will build with minification and other optimisations.
What it's actually doing
Mix runs Babel with the ES2015 preset across your Javascript, and the PostCSS autoprefixer
plugin across your SASS – meaning you can use the latest and greatest features of both languages, and backwards compatibility with older browsers will generally be maintained. There are exceptions – for example, some recent ES features won't have polyfills applied, such as Object.assign()
. In these cases, you'll need to add additional Babel/PostCSS plugins to your dependency list and either write your own .babelrc
config, similar to the following example:
{ "plugins": [ "transform-object-assign", ], "presets": [ ["env", { "modules": false, "targets": { "browsers": ["> 2%"], "uglify": true } }] ] }
...or add the new PostCSS plugin to your Mix config:
...
mix.config({ postCss: [require('cssnext')] })
Other fancy stuff
Of course, there are a few more features you might find useful.
Building React apps
Thanks to its unique syntax, React applications containing JSX won't build using the normal js()
mix method. You'll want to use react()
instead:
mix.react(`${themepath}/js/react-app.js`, distpath)
Extracting and autoloading libraries
It can be wasteful to have your users redownload the JS libraries you depend on when making minor changes to application code, so you can extract these into a separate file:
mix.extract(['axios'], `${distpath}vendor.js`)
You can also set up global references to libraries – useful for situations where a legacy dependency doesn't use imports (e.g. most jQuery plugins):
mix.autoload({ jquery: ['$', 'window.jQuery'] })
Adding ESLint
Automatically linting code during builds is a great way to ensure teams produce consistently formatted output. ESLint is a good choice for checking your JavaScript, and it's quite simple to add to a Mix-based project. First, install ESLint:
> yarn add eslint eslint-config-airbnb
Next, add an ESLint rule to Webpack via the mix.webpackConfig()
method (notice we're only applying this in development builds):
if (process.env.NODE_ENV === 'development') { mix.webpackConfig({ module: { rules: [ { test: /\\.(js)$/, exclude: /node_modules/, loader: 'eslint-loader', }, ], }, }); }
To make the most of ESLint, you'll need a .eslintrc
file for it. Here's a simple AirBnB-based config to get you started:
{ "extends": "airbnb", "env": { "browser": true, "commonjs": true, "es6": true, "node": true } }
Modifying Mix's Webpack configuration
If you find yourself adding a large amount of custom configuration, you can also copy the base configuration file Mix uses into your root directory and edit that directly:
> cp node_modules/laravel-mix/setup/webpack.config.js .
You'll also need to update the paths in your package.json
methods to use your local version of the file:
... "scripts": { "once": "node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=webpack.config.js", "dev": "node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=webpack.config.js", "production": "node_modules/cross-env/dist/bin/cross-env.js NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=webpack.config.js" }, ...
Putting it all together
SilverStripe developer Ben Manu is putting together an excellent SilverStripe skeleton project that utilises Laravel Mix and provides a great base for starting any size or type of SilverStripe-based application.
Summary
Mix provides a wide enough API that it should cover most uses, but in the case that you find any edge-cases, take a look at the project on GitHub and consider contributing an improvement.
Next time you're getting started on any project that requires front-end asset compilation, be sure to take Mix for a test drive!
Post your comment
Comments
Posted by piccosoft, 25/08/2018 7:42pm (6 years ago)
Mix says "An elegant wrapper around Webpack for the 80% use case" how do you do the other 20%? or is Mix not for the other 20% at all?
Posted by Adam Patterson, 17/11/2017 7:49am (7 years ago)
"once": "node node_modules/cross-env/dist/bin/cross-env.js ..."
Posted by Werner Krauß, 14/06/2017 1:20am (8 years ago)
No one has commented on this page yet.
RSS feed for comments on this page | RSS feed for all comments