Integrate ES2015, Rollup in legacy ‘AMD’ project
Background
I’ve been working on an AMD-driven project for a while, now there is a requirement to build a relatively independent subsystem. I thought this might be a good timing to start write javascript in ES2015 style and try out some new tools.
But the reality is, it’s not building a new project from scratch where I can choose a whole new architecture or structure, it is vital that the new subsystem should be conformed with existing project structure, especially all these setups built on top of AMD modules.
So my plan is simply use compile ES2015 modules use Babel and Gulp(since gulp is already used in the project).
Integrate with ES6 in Gulp
The setup here is pretty straightforward, I just added a new task in gulp to convert ES2015 to AMD modules.
This is how I import modules in the new ES2015 modules:
Two things worth noting here, the first is that common libraries are already defined in requirejs config(such as d3.js), and the reason why I need to import myModule
from a ‘parent’ directory is that I would like the dependencies in the complied AMD file is consistent.
The initial goal is basically completed since I didn’t introduce many new things, now I can import modules in ES6 style, and this setup doesn’t require any other change in AMD config files.
However, this ES6-to-ES5 compiling task generate more files in the project folder, which sort of slows down the release process.
So to take one step further, I’d like to bundle all the ES6 modules I created for this new subsystem into one file, so that I can compile just this one bundle file into one AMD file instead of compiling all ES2015 module files.
So basically the project structure should look like this:
Introduce Rollup
There are several module bundler tools available, such as Browserify and Webpack, although browserify is more suitable for commonjs. Webpack is awesome, but I think that I won’t be needing many features of webpack this time, all I need is a ES6 module bundler to bundle es2015 modules into an AMD file. Rollup seems to be a good fit.
There is nothing complicated to use rollup in a ‘hello world’ tutorial as described here. However, it is relatively new, Below are the problems I encountered.
Resolve File Path
Initially I set up the rollup.config.js
with minimal configuration, and created a entry.js
and moduleA.js
in the es6
directory as mentioned above:
The weird thing is, after running the rollup -c
command, it logged Treating moduleA.js as external dependency
. That’s because currently Rollup currently considers all non-relative paths to be references to external modules, so you’ll have to write relative paths to import modules, bummer. Luckily, as mentioned in the issue thread, this problem can be solved by using this plugin - rollup-plugin-includepaths, let’s update rollup config file:
Now rollup should bundle moduleA into the bundle.js without treating it as external dependency.
Bundle Third Party Libraries
I switched to i18next for localization instead of the requirejs loader in the new subsystem, not because it is more compatible with different module setup, but also it has more useful API for localization. But it turned out to be the most frustrating process while integrating with Rollup.
Approaches Didn’t Work
At first, I thought it should be fine to download the compressed i18next.min.js
, and treat them like a regular module file due to its UMD format. That didn’t work, because rollup is a ES6 module bundler, the prerequisite of bundling files is that those files have to be in ES6 module format. Considering I might need more third party libraries, and not every library is written in ES6, I need to find a way to make them compatible with Rollup.
There is a plugin listed in Rollup wiki - rollup-plugin-commonjs, it is supposed to convert CommonJS to rollup compatible format, so I update rollup config:
Running rollup will only get an error message like this: Module [directory]/vendors/i18nextXHRBackend.min.js does not export default (imported by [directory]/es6/localization.js)
. No matter how I tried to tweak it just wouldn’t work.
So I figured it might be the wrong way to use standalone minified js(eventually), I then installed the libraries via npm, but in the parent folder of es6
:
And error message is either can't find module
or does not export default
. So I post it as an issue on Github, luckily rollup authors responded soon. I went to see the source code of i18next, and found that a) the source code is already in ES6, so there shouldn’t be a compatible issue, b) there is no require === function
test in the minified js, so it is not the same issue.
At that time, I was thinking perhaps the way I was using npm package was incorrect since the rollup author suggested install via npm. So I installed npm packages in the same directory of entry.js:
And updated rollup config (as the response said, used node resolve to handle requiring libraries):
Finally, this setup is working! Here is the sample entry.js
:
and localizaiton.js
:
And the localization function works, yay!
Include Existing Libraries
One more thing I need to test is to use existing libraries which are already defined in require.js config. For example, I added this line in localization.js:
Initially I thought rollup will treat d3 as an external dependency just like when I import moduleA.js, but it didn’t work, the error message is can't find module d3 in directory/es6
, I suppose it is because ‘rollup-plugin-includepaths’ kind of ‘override’ the default rollup behavior? Anyway, since rollup said it can’t find the module, I had to specify where to find the module:
This time rollup didn’t report any errors.
Issue with i18next
One last issue is that even though there is no error message from rollup, when I loaded the bundle file in browser, there was an error message from i18next-xhr-backend saying ‘utils.defaults is not a function’, so I digged into the source file of i18next-xhr-backend, everything seems to be fine, but it failed to import utils
module here:
So I tried to change the require to:
Then it works, honestly, I don’t know if this is rollup problem, node-resolve problem, or commonjs plugin problem, don’t think it’s problem of the package though. The author of i18next is working on to improve the library to avoid this kind of error.
Watch File Change
For now, rollup doesn’t have any watch options yet, but many developers shared their alternative solutions in this thread. I prefer the watch approach, So I installed ‘watch’ in the subsystem folder (I tried to install it in the es6 folder and update config, but it didn’t work, I think it is fine to just use the existing folder structure anyway):
In the outer directory, package.json:
This way, any changes in es6 folder will trigger rollup to re-bundle files.
Additionally, Gulp can also watches the bundle file change for other tasks.
Add Classic jQuery Plugins as ES2015 module
See details here.