Skip to content

Commit 3e43f98

Browse files
committed
Migrate from Browserify to rollup
Browserify isn't optimal bundling Chart.js because it adds too many internal wrappers, doesn't handle external/global dependencies and doesn't provide a way to generate ESM builds. Therefore, it seems the right choice to switch to rollup, so move all the build process in `rollup.config.js` and make Gulp to execute `rollup -c`. We also had to switch to Terser instead of UglifyJS because this last one contains a breaking bug. Note that tests now use the exact same rollup config as our builds (the minified one) to ensure that the bundling and minification steps don't break anything. Finally, replace the `gulp watch` task by `gulp build --watch` to be consistent with the other `unittest` and `docs` watching syntax.
1 parent 7c45fda commit 3e43f98

File tree

10 files changed

+208
-124
lines changed

10 files changed

+208
-124
lines changed

docs/developers/contributing.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ This will install the local development dependencies for Chart.js, along with a
3030
The following commands are now available from the repository root:
3131

3232
```bash
33-
> gulp build // build Chart.js in ./dist
33+
> gulp build // build dist files in ./dist
34+
> gulp build --watch // build and watch for changes
3435
> gulp unittest // run tests from ./test/specs
3536
> gulp unittest --watch // run tests and watch for source changes
3637
> gulp unittest --coverage // run tests and generate coverage reports in ./coverage

gulpfile.js

Lines changed: 32 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,34 @@
11
var gulp = require('gulp');
2-
var concat = require('gulp-concat');
32
var eslint = require('gulp-eslint');
43
var file = require('gulp-file');
5-
var insert = require('gulp-insert');
64
var replace = require('gulp-replace');
75
var size = require('gulp-size');
86
var streamify = require('gulp-streamify');
9-
var uglify = require('gulp-uglify');
7+
var terser = require('gulp-terser');
108
var util = require('gulp-util');
119
var zip = require('gulp-zip');
12-
var exec = require('child-process-promise').exec;
10+
var exec = require('child_process').exec;
1311
var karma = require('karma');
14-
var browserify = require('browserify');
15-
var source = require('vinyl-source-stream');
1612
var merge = require('merge-stream');
17-
var collapse = require('bundle-collapser/plugin');
1813
var yargs = require('yargs');
1914
var path = require('path');
20-
var fs = require('fs');
2115
var htmllint = require('gulp-htmllint');
2216
var pkg = require('./package.json');
2317

2418
var argv = yargs
25-
.option('force-output', {default: false})
26-
.option('silent-errors', {default: false})
2719
.option('verbose', {default: false})
2820
.argv;
2921

3022
var srcDir = './src/';
3123
var outDir = './dist/';
3224

33-
var header = "/*!\n" +
34-
" * Chart.js\n" +
35-
" * http://chartjs.org/\n" +
36-
" * Version: {{ version }}\n" +
37-
" *\n" +
38-
" * Copyright " + (new Date().getFullYear()) + " Chart.js Contributors\n" +
39-
" * Released under the MIT license\n" +
40-
" * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md\n" +
41-
" */\n";
42-
4325
if (argv.verbose) {
4426
util.log("Gulp running with options: " + JSON.stringify(argv, null, 2));
4527
}
4628

4729
gulp.task('bower', bowerTask);
4830
gulp.task('build', buildTask);
4931
gulp.task('package', packageTask);
50-
gulp.task('watch', watchTask);
5132
gulp.task('lint-html', lintHtmlTask);
5233
gulp.task('lint-js', lintJsTask);
5334
gulp.task('lint', gulp.parallel('lint-html', 'lint-js'));
@@ -57,7 +38,25 @@ gulp.task('test', gulp.parallel('lint', 'unittest'));
5738
gulp.task('library-size', librarySizeTask);
5839
gulp.task('module-sizes', moduleSizesTask);
5940
gulp.task('size', gulp.parallel('library-size', 'module-sizes'));
60-
gulp.task('default', gulp.parallel('build', 'watch'));
41+
gulp.task('default', gulp.parallel('build'));
42+
43+
function run(bin, args, done) {
44+
return new Promise(function(resolve, reject) {
45+
var exe = '"' + process.execPath + '"';
46+
var src = require.resolve(bin);
47+
var ps = exec([exe, src].concat(args || []).join(' '));
48+
49+
ps.stdout.pipe(process.stdout);
50+
ps.stderr.pipe(process.stderr);
51+
ps.on('close', function(error) {
52+
if (error) {
53+
reject(error);
54+
} else {
55+
resolve();
56+
}
57+
});
58+
});
59+
}
6160

6261
/**
6362
* Generates the bower.json manifest file which will be pushed along release tags.
@@ -70,7 +69,7 @@ function bowerTask() {
7069
homepage: pkg.homepage,
7170
license: pkg.license,
7271
version: pkg.version,
73-
main: outDir + "Chart.js",
72+
main: outDir + 'Chart.js',
7473
ignore: [
7574
'.github',
7675
'.codeclimate.yml',
@@ -86,52 +85,7 @@ function bowerTask() {
8685
}
8786

8887
function buildTask() {
89-
90-
var errorHandler = function (err) {
91-
if(argv.forceOutput) {
92-
var browserError = 'console.error("Gulp: ' + err.toString() + '")';
93-
['Chart', 'Chart.min', 'Chart.bundle', 'Chart.bundle.min'].forEach(function(fileName) {
94-
fs.writeFileSync(outDir+fileName+'.js', browserError);
95-
});
96-
}
97-
if(argv.silentErrors) {
98-
util.log(util.colors.red('[Error]'), err.toString());
99-
this.emit('end');
100-
} else {
101-
throw err;
102-
}
103-
}
104-
105-
var bundled = browserify('./src/chart.js', { standalone: 'Chart' })
106-
.plugin(collapse)
107-
.bundle()
108-
.on('error', errorHandler)
109-
.pipe(source('Chart.bundle.js'))
110-
.pipe(insert.prepend(header))
111-
.pipe(streamify(replace('{{ version }}', pkg.version)))
112-
.pipe(gulp.dest(outDir))
113-
.pipe(streamify(uglify()))
114-
.pipe(insert.prepend(header))
115-
.pipe(streamify(replace('{{ version }}', pkg.version)))
116-
.pipe(streamify(concat('Chart.bundle.min.js')))
117-
.pipe(gulp.dest(outDir));
118-
119-
var nonBundled = browserify('./src/chart.js', { standalone: 'Chart' })
120-
.ignore('moment')
121-
.plugin(collapse)
122-
.bundle()
123-
.on('error', errorHandler)
124-
.pipe(source('Chart.js'))
125-
.pipe(insert.prepend(header))
126-
.pipe(streamify(replace('{{ version }}', pkg.version)))
127-
.pipe(gulp.dest(outDir))
128-
.pipe(streamify(uglify()))
129-
.pipe(insert.prepend(header))
130-
.pipe(streamify(replace('{{ version }}', pkg.version)))
131-
.pipe(streamify(concat('Chart.min.js')))
132-
.pipe(gulp.dest(outDir));
133-
134-
return merge(bundled, nonBundled);
88+
return run('rollup/bin/rollup', ['-c', argv.watch ? '--watch' : '']);
13589
}
13690

13791
function packageTask() {
@@ -180,17 +134,12 @@ function lintHtmlTask() {
180134
}));
181135
}
182136

183-
function docsTask(done) {
184-
var script = require.resolve('gitbook-cli/bin/gitbook.js');
185-
var cmd = '"' + process.execPath + '"';
186-
187-
exec([cmd, script, 'install', './'].join(' ')).then(() => {
188-
return exec([cmd, script, argv.watch ? 'serve' : 'build', './', './dist/docs'].join(' '));
189-
}).then(() => {
190-
done();
191-
}).catch((err) => {
192-
done(new Error(err.stdout || err));
193-
})
137+
function docsTask() {
138+
var bin = 'gitbook-cli/bin/gitbook.js';
139+
var cmd = argv.watch ? 'serve' : 'build';
140+
141+
return run(bin, ['install', './'])
142+
.then(() => run(bin, [cmd, './', './dist/docs']));
194143
}
195144

196145
function unittestTask(done) {
@@ -199,9 +148,8 @@ function unittestTask(done) {
199148
singleRun: !argv.watch,
200149
args: {
201150
coverage: !!argv.coverage,
202-
inputs: argv.inputs
203-
? argv.inputs.split(';')
204-
: ['./test/specs/**/*.js']
151+
inputs: (argv.inputs || 'test/specs/**/*.js').split(';'),
152+
watch: argv.watch
205153
}
206154
},
207155
// https://github.com/karma-runner/gulp-karma/issues/18
@@ -220,13 +168,9 @@ function librarySizeTask() {
220168

221169
function moduleSizesTask() {
222170
return gulp.src(srcDir + '**/*.js')
223-
.pipe(uglify())
171+
.pipe(terser())
224172
.pipe(size({
225173
showFiles: true,
226174
gzip: true
227175
}));
228176
}
229-
230-
function watchTask() {
231-
return gulp.watch('./src/**', gulp.parallel('build'));
232-
}

karma.conf.js

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
1-
/* eslint camelcase: 0 */
1+
/* eslint-env es6 */
2+
3+
const commonjs = require('rollup-plugin-commonjs');
4+
const istanbul = require('rollup-plugin-istanbul');
5+
const resolve = require('rollup-plugin-node-resolve');
6+
const builds = require('./rollup.config');
27

38
module.exports = function(karma) {
4-
var args = karma.args || {};
5-
var config = {
6-
frameworks: ['browserify', 'jasmine'],
9+
const args = karma.args || {};
10+
11+
// Use the same rollup config as our dist files: when debugging (--watch),
12+
// we will prefer the unminified build which is easier to browse and works
13+
// better with source mapping. In other cases, pick the minified build to
14+
// make sure that the minification process (terser) doesn't break anything.
15+
const regex = args.watch ? /Chart\.js$/ : /Chart\.min\.js$/;
16+
const build = builds.filter(v => v.output.file.match(regex))[0];
17+
18+
if (args.watch) {
19+
build.output.sourcemap = 'inline';
20+
}
21+
22+
karma.set({
23+
frameworks: ['jasmine'],
724
reporters: ['progress', 'kjhtml'],
825
browsers: ['chrome', 'firefox'],
26+
logLevel: karma.LOG_WARN,
927

1028
// Explicitly disable hardware acceleration to make image
1129
// diff more stable when ran on Travis and dev machine.
@@ -31,42 +49,60 @@ module.exports = function(karma) {
3149
{pattern: 'test/fixtures/**/*.png', included: false},
3250
'node_modules/moment/min/moment.min.js',
3351
'test/index.js',
34-
'src/**/*.js'
52+
'src/chart.js'
3553
].concat(args.inputs),
3654

3755
preprocessors: {
38-
'test/index.js': ['browserify'],
39-
'src/**/*.js': ['browserify']
56+
'test/index.js': ['rollup'],
57+
'src/chart.js': ['sources']
4058
},
4159

42-
browserify: {
43-
debug: true
60+
rollupPreprocessor: {
61+
plugins: [
62+
resolve(),
63+
commonjs()
64+
],
65+
output: {
66+
name: 'test',
67+
format: 'umd'
68+
}
69+
},
70+
71+
customPreprocessors: {
72+
sources: {
73+
base: 'rollup',
74+
options: build
75+
}
4476
},
4577

4678
// These settings deal with browser disconnects. We had seen test flakiness from Firefox
4779
// [Firefox 56.0.0 (Linux 0.0.0)]: Disconnected (1 times), because no message in 10000 ms.
4880
// https://github.com/jasmine/jasmine/issues/1327#issuecomment-332939551
4981
browserDisconnectTolerance: 3
50-
};
82+
});
5183

5284
// https://swizec.com/blog/how-to-run-javascript-tests-in-chrome-on-travis/swizec/6647
5385
if (process.env.TRAVIS) {
54-
config.customLaunchers.chrome.flags.push('--no-sandbox');
86+
karma.customLaunchers.chrome.flags.push('--no-sandbox');
5587
}
5688

5789
if (args.coverage) {
58-
config.reporters.push('coverage');
59-
config.browserify.transform = ['browserify-istanbul'];
60-
61-
// https://github.com/karma-runner/karma-coverage/blob/master/docs/configuration.md
62-
config.coverageReporter = {
90+
karma.reporters.push('coverage');
91+
karma.coverageReporter = {
6392
dir: 'coverage/',
6493
reporters: [
65-
{type: 'html', subdir: 'report-html'},
66-
{type: 'lcovonly', subdir: '.', file: 'lcov.info'}
94+
{type: 'html', subdir: 'html'},
95+
{type: 'lcovonly', subdir: '.'}
6796
]
6897
};
98+
[
99+
karma.rollupPreprocessor,
100+
karma.customPreprocessors.sources.options
101+
].forEach(v => {
102+
(v.plugins || (v.plugins = [])).unshift(
103+
istanbul({
104+
include: 'src/**/*.js'
105+
}));
106+
});
69107
}
70-
71-
karma.set(config);
72108
};

package.json

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,39 +23,37 @@
2323
"url": "https://github.com/chartjs/Chart.js/issues"
2424
},
2525
"devDependencies": {
26-
"browserify": "^16.2.3",
27-
"browserify-istanbul": "^3.0.1",
28-
"bundle-collapser": "^1.3.0",
29-
"child-process-promise": "^2.2.1",
3026
"coveralls": "^3.0.0",
3127
"eslint": "^5.9.0",
3228
"eslint-config-chartjs": "^0.1.0",
3329
"eslint-plugin-html": "^5.0.0",
3430
"gitbook-cli": "^2.3.2",
3531
"gulp": "^4.0.0",
36-
"gulp-concat": "^2.6.0",
3732
"gulp-eslint": "^5.0.0",
3833
"gulp-file": "^0.4.0",
3934
"gulp-htmllint": "^0.0.16",
40-
"gulp-insert": "^0.5.0",
4135
"gulp-replace": "^1.0.0",
4236
"gulp-size": "^3.0.0",
4337
"gulp-streamify": "^1.0.2",
44-
"gulp-uglify": "^3.0.0",
38+
"gulp-terser": "^1.1.6",
4539
"gulp-util": "^3.0.0",
4640
"gulp-zip": "^4.2.0",
4741
"jasmine": "^3.3.0",
4842
"jasmine-core": "^3.3.0",
4943
"karma": "^3.1.1",
50-
"karma-browserify": "^5.1.1",
5144
"karma-chrome-launcher": "^2.2.0",
5245
"karma-coverage": "^1.1.1",
5346
"karma-firefox-launcher": "^1.0.1",
5447
"karma-jasmine": "^2.0.1",
5548
"karma-jasmine-html-reporter": "^1.4.0",
49+
"karma-rollup-preprocessor": "^6.1.1",
5650
"merge-stream": "^1.0.1",
5751
"pixelmatch": "^4.0.2",
58-
"vinyl-source-stream": "^2.0.0",
52+
"rollup": "^0.67.4",
53+
"rollup-plugin-commonjs": "^9.2.0",
54+
"rollup-plugin-istanbul": "^2.0.1",
55+
"rollup-plugin-node-resolve": "^3.4.0",
56+
"rollup-plugin-terser": "^3.0.0",
5957
"watchify": "^3.9.0",
6058
"yargs": "^12.0.5"
6159
},

0 commit comments

Comments
 (0)