Skip to content

Commit af13b84

Browse files
committed
Make moment optional from our UMD builds
Create a rollup plugin altering the UMD header to wrap optional dependencies between try/catch, which allows to load moment only when the dependency is installed. Since AMD loaders are asynchronous, `'moment'` needs to be explicitly loaded before 'chart.js' so when 'chart.js' requires moment, it's already loaded and returns synchronously (at least with requirejs).
1 parent 8a3eb85 commit af13b84

File tree

2 files changed

+69
-1
lines changed

2 files changed

+69
-1
lines changed

rollup.config.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const commonjs = require('rollup-plugin-commonjs');
44
const resolve = require('rollup-plugin-node-resolve');
55
const terser = require('rollup-plugin-terser').terser;
6+
const optional = require('./rollup.plugins').optional;
67
const pkg = require('./package.json');
78

89
const input = 'src/chart.js';
@@ -21,7 +22,10 @@ module.exports = [
2122
input: input,
2223
plugins: [
2324
resolve(),
24-
commonjs()
25+
commonjs(),
26+
optional({
27+
include: ['moment']
28+
})
2529
],
2630
output: {
2731
name: 'Chart',
@@ -42,6 +46,9 @@ module.exports = [
4246
plugins: [
4347
resolve(),
4448
commonjs(),
49+
optional({
50+
include: ['moment']
51+
}),
4552
terser({
4653
output: {
4754
preamble: banner

rollup.plugins.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/* eslint-env es6 */
2+
3+
const UMD_WRAPPER_RE = /(\(function \(global, factory\) \{)((?:\s.*?)*)(\}\(this,)/;
4+
const CJS_FACTORY_RE = /(module.exports = )(factory\(.*?\))( :)/;
5+
const AMD_FACTORY_RE = /(define\()(.*?, factory)(\) :)/;
6+
7+
function optional(config = {}) {
8+
return {
9+
name: 'optional',
10+
renderChunk(code, chunk, options) {
11+
if (options.format !== 'umd') {
12+
this.error('only UMD format is currently supported');
13+
}
14+
15+
const wrapper = UMD_WRAPPER_RE.exec(code);
16+
const include = config.include;
17+
if (!wrapper) {
18+
this.error('failed to parse the UMD wrapper');
19+
}
20+
21+
let content = wrapper[2];
22+
let factory = (CJS_FACTORY_RE.exec(content) || [])[2];
23+
let updated = false;
24+
25+
for (let lib of chunk.imports) {
26+
if (!include || include.indexOf(lib) !== -1) {
27+
const regex = new RegExp(`require\\('${lib}'\\)`);
28+
if (!regex.test(factory)) {
29+
this.error(`failed to parse the CJS require for ${lib}`);
30+
}
31+
32+
// We need to write inline try / catch with explicit require
33+
// in order to enable statical extraction of dependencies:
34+
// try { return require('moment'); } catch(e) {}
35+
const loader = `function() { try { return require('${lib}'); } catch(e) { } }()`;
36+
factory = factory.replace(regex, loader);
37+
updated = true;
38+
}
39+
}
40+
41+
if (!updated) {
42+
return;
43+
}
44+
45+
// Replace the CJS factory by our updated one.
46+
content = content.replace(CJS_FACTORY_RE, `$1${factory}$3`);
47+
48+
// Replace the AMD factory by our updated one: we need to use the
49+
// following AMD form in order to be able to try/catch require:
50+
// define(['require'], function(require) { ... require(...); ... })
51+
// https://github.com/amdjs/amdjs-api/wiki/AMD#using-require-and-exports
52+
content = content.replace(AMD_FACTORY_RE, `$1['require'], function(require) { return ${factory}; }$3`);
53+
54+
return code.replace(UMD_WRAPPER_RE, '$1' + content + '$3');
55+
}
56+
};
57+
}
58+
59+
module.exports = {
60+
optional
61+
};

0 commit comments

Comments
 (0)