Skyrocket
your Old Toolchain
Into the Future!
Eindhoven Developers Meetup
new Date(2017, 1, 22, 20, 15, 0);
On the menu today
- Toolchains: The Old versus The New
- What we wanted to solve
- Steps we took
- Where we are now
What is the "old" toolchain?
- Backend-first
- Grunt / Gulp
- LESS / SASS by Grunt / Gulp
- AMD (Require.js) or Concatenation
- jQuery like approach
Getting assets
- Get JavaScript from script tags
- Get Stylesheets from link tags
- Get images from img tags
- Require JavaScript files through injection
What is the "new" toolchain?
- Single Page Applications
- Backend is API
- ES6+
- NPM scripts for tasks
- webpack for bundling entry points
- JavaScript as a starting point
Getting assets
- Get JavaScript from script tags
- Webpack tells to include style
- Webpack loads images
- Require JavaScript files through webpack chunks
Why going forward is hard
- Don't know where to start
- Moving over (forgotten) paradigms
- Backend is not supported yet
- You have to figure out everything
- You have a lot of code to manage
Why starting from scratch is easier
- Clean slate
- Freedom
- No legacy
- You can try out everything
- You can adjust your backend to it
- Project is most likely smaller
How the "old" way stinks
- Gets messy really quick
- AMD (Require.js) has better alternatives
- Dependency management is unclear
- Pipeline can be confusing
- Support is getting worse
What we needed to solve at JouwWeb
New
- Managealbe code base
- ES6+
- ES7 modules + chunks
- Source maps for development
What we needed to solve at JouwWeb
Keep
- Make sure old libs keep working
- Adjust as little as possible
- AMD should work too
- Development "watch" mode
- Production output using webpack
- File size
"Don't pounce on every chance to rewrite the world. Refactor little bits as you go. Make it work, first. Then make it right."
- Eric Elliott
Know problems
- What are entry points?
- Source was copied from a weird folder structure
- Backend injects script tags
- Dynamic requires in our code
- We required 3th party libs using
require('lib/jquery');
- Some dependencies couldn't be bundled
- Files copied from
node_modules
using grunt
Set deliverables
Try to ship as little as possible
Steps we took
- Organize code in structure webpack will understand
- Have all our entry points work with webpack
- Introduce Babel
- Solve
require('lib/jquery');
problem
- Solve dynamic requires
- Let JavaScript handle injected scripts
- Fix broken "old" libraries
- Have a development "watch" environment
Steps we didn't take
- LESS was still part of the "old"
- Fix our folder structure
- Handle copying of assets
- Make it blazing fast
Basics
We created a JavaScript file...
Execute in NPM
// package.json
{
...
"scripts": {
"build": "node ./tasks/webpack"
}
}
// cli
> npm run build
Organising the structure
module/Text/assets/js/plugin/text.js
module/Video/assets/js/plugin/video.js
require('plugin/text.js');
require('plugin/video.js');
Organising the structure
module/*/assets/js/**/*.js
copy to
public/assets/js/**/*.js
Make public/assets/js
a "module root" in webpack
require('filename.js');
const config = {
...
resolve: {
modules: [
'public/assets/js',
'node_modules',
'bower_components',
],
},
};
How we made "watch" work
webpack watches
public/assets/js/**/*.js
We watch for changes in
module/*/assets/js/**/*.js
Copy all to
public/assets/js/**/*.js
webpack reruns
It's not elegant, but effective
Entry points
const config = {
...
entry: {
'js/initialize': './js/app/initialize',
},
output: {
path: 'public/assets/',
filename: '[name].js',
}
}
// 'public/assets/' + 'js/initialize' + '.js'
// lib/bootstrap.js (AMD style)
define(
['bootstrap/dist/js/bootstrap'],
function (bootstrap) => { return bootstrap }
);
// lib/bxslider.js (CommonJS style)
module.exports = require('bxslider-4/dist/jquery.bxslider');
Dynamic module includes
define(['scripts/backend'], function(module) { ... });
function includeModule(name) {
define([name], function(module) { ... });
}
// somewhere else
includeModule('scripts/backend');
function includeModule(name) {
function cb(module) { ... }
switch (name) {
case 'scripts/backend':
require(['scripts/backend'], cb);
break;
case 'scripts/editor':
require(['scripts/editor'], cb);
break;
}
}
Injected scripts
What if the backend generates <script />
tags?
<script src="assets/js/module-a.js" />
<script src="assets/js/module-b.js" />
<script>
window.scripts = ['module-a', 'module-b'];
</script>
// in JavaScript
window.scripts.forEach(includeModule);
Old dependencies
- Fixed them by hand
- Not really important
- tinymce is a totally different story
What we did later
- Fixed our folder structure
- Got LESS (and other assets) to webpack
- Hot Reloading with webpack dev server
- Able to refactor a lot
- SUPER FAST!
Where we want to be
- Single Page Application
- Mostly built on React
- Dynamic route loading
- Drinking beer on a sunny island
Improve your project,
just do it
one little step
at the time.
- Gaya Kessler
Thanks!
For more brain farts check @GayaNinja
(p.s. we're always looking for people, so come chat)