ES6 allows you to import code from other Javascript files using the following syntax:
1 | import foo from './foo'; |
Try creating a file app/foo.js
with the contents:
1 | // app/foo.js |
and set the contents of app/app.js
to:
1 | // app/app.js |
Now run yarn serve
, refresh the browser, and:
1 | app.js:5 Uncaught ReferenceError: require is not defined |
Uh oh, what's going on? Well, babel will transpile ES6 to ES5, however import
statements are converted to
node compatible require()
AMD calls. The browser doesn't have a mechanism for resolving require()
calls by default
so we need a way of handling this.
- Browserify via
broccoli-watchify
- Requirejs via
broccoli-requirejs
- Babel via
babel-plugin-transform-es2015-modules-systemjs
- Rollup via
broccoli-rollup
Browserify, Requirejs and Babel all require additional code to resolve dependencies, however Rollup does something different.
So we're going to focus on Rollup here because aside from module loading that natively works in the browser, it has some fairly cool other features we will touch on.
Here's what the website says:
Rollup is a next-generation JavaScript module bundler. Author your app or library using ES2015 modules, then efficiently bundle them up into a single file for use in browsers and Node.js
So, first off, install rollup (and remove babel dependencies, provided by rollup-plugin-babel):
1 | yarn add --dev rollup@^0.66.2 broccoli-rollup@^2.1.1 rollup-plugin-babel@^4.0.3 |
Note: we're removing broccoli-babel-transpiler
@babel/preset-env
and @babel/plugin-external-helpers
as these are
provided by rollup-plugin-babel
.
And set your Brocfile.js
file to:
1 | // Brocfile.js |
Here are the changes:
- Removed the JS funnel, this is no longer needed
- Now we pass
appRoot
toRollup
, pass it an input filter withinputFiles
to include all JS files - Rollup is configured with an
entry
file, this is the first file that isrequired
. - Define a destination for the resulting rolled up build, and enable sourceMaps.
- Add the
rollup-plugin-babel
to facilitate babel transformations, this is because the Rollup and Babel broccoli plugins do not play nicely together when it comes to sourcemaps, however Rollup has plugin support for Babel.
Now build & serve
and notice that the require()
error has gone, and it console logs out foo
, all is
good in the world!
But wait, there's more...
Whereas Browserify and Requirejs will wrap each module in a specialised function, rollup is more intelligent
and hoists modules up to first class citizens, producing the most efficient output.
Checkout dist/assets/app.js
, you should see:
1 | // dist/assets/app.js |
As you can see, even though app.js
imports foo.js
, the compiled output doesn't contain any dynamic import functions,
like you'll see with require.js or system.js code, nor does it need an external loader.
The code is actually inlined. With the format
parameter iife
, the output gets wrapped in immediately invoked
function expressions so that it will run correctly in the browser. There are other format options available, checkout the
Rollup docs for details.
But wait, there's more...
Tree shaking
Tree what? Yeah, this an actual term that refers to removing dead/unused imported code.
Open app/foo.js
Notice we're also exporting a constant:
1 | export const fooNamed = 'fooNamed'; |
Open app/app.js
Notice we're importing this constant:
1 | import { fooNamed } from "./foo"; |
Now open dist/assets/app.js
, notice how the fooNamed
variable is nowhere to be seen? What is this witchcraft?
This is part of the magic of Rollup, it knows, through static analysis, what code is not being used and dynamically removes it. Cool huh?
Completed Branch: 06-es6-modules
Next: 07-node-modules