From c8ab562021c4991da4f15307f55007cf79002464 Mon Sep 17 00:00:00 2001 From: Simon Brunel Date: Fri, 11 Jan 2019 22:01:23 +0100 Subject: [PATCH 1/5] 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). --- rollup.config.js | 9 ++++++- rollup.plugins.js | 61 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 rollup.plugins.js diff --git a/rollup.config.js b/rollup.config.js index 9f76d957317..83ae38d67c5 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -3,6 +3,7 @@ const commonjs = require('rollup-plugin-commonjs'); const resolve = require('rollup-plugin-node-resolve'); const terser = require('rollup-plugin-terser').terser; +const optional = require('./rollup.plugins').optional; const pkg = require('./package.json'); const input = 'src/chart.js'; @@ -21,7 +22,10 @@ module.exports = [ input: input, plugins: [ resolve(), - commonjs() + commonjs(), + optional({ + include: ['moment'] + }) ], output: { name: 'Chart', @@ -42,6 +46,9 @@ module.exports = [ plugins: [ resolve(), commonjs(), + optional({ + include: ['moment'] + }), terser({ output: { preamble: banner diff --git a/rollup.plugins.js b/rollup.plugins.js new file mode 100644 index 00000000000..39c75700e5b --- /dev/null +++ b/rollup.plugins.js @@ -0,0 +1,61 @@ +/* eslint-env es6 */ + +const UMD_WRAPPER_RE = /(\(function \(global, factory\) \{)((?:\s.*?)*)(\}\(this,)/; +const CJS_FACTORY_RE = /(module.exports = )(factory\(.*?\))( :)/; +const AMD_FACTORY_RE = /(define\()(.*?, factory)(\) :)/; + +function optional(config = {}) { + return { + name: 'optional', + renderChunk(code, chunk, options) { + if (options.format !== 'umd') { + this.error('only UMD format is currently supported'); + } + + const wrapper = UMD_WRAPPER_RE.exec(code); + const include = config.include; + if (!wrapper) { + this.error('failed to parse the UMD wrapper'); + } + + let content = wrapper[2]; + let factory = (CJS_FACTORY_RE.exec(content) || [])[2]; + let updated = false; + + for (let lib of chunk.imports) { + if (!include || include.indexOf(lib) !== -1) { + const regex = new RegExp(`require\\('${lib}'\\)`); + if (!regex.test(factory)) { + this.error(`failed to parse the CJS require for ${lib}`); + } + + // We need to write inline try / catch with explicit require + // in order to enable statical extraction of dependencies: + // try { return require('moment'); } catch(e) {} + const loader = `function() { try { return require('${lib}'); } catch(e) { } }()`; + factory = factory.replace(regex, loader); + updated = true; + } + } + + if (!updated) { + return; + } + + // Replace the CJS factory by our updated one. + content = content.replace(CJS_FACTORY_RE, `$1${factory}$3`); + + // Replace the AMD factory by our updated one: we need to use the + // following AMD form in order to be able to try/catch require: + // define(['require'], function(require) { ... require(...); ... }) + // https://github.com/amdjs/amdjs-api/wiki/AMD#using-require-and-exports + content = content.replace(AMD_FACTORY_RE, `$1['require'], function(require) { return ${factory}; }$3`); + + return code.replace(UMD_WRAPPER_RE, `$1${content}$3`); + } + }; +} + +module.exports = { + optional +}; From 4b914be233e5a9131a6b25bbc3f852f378b006b2 Mon Sep 17 00:00:00 2001 From: Simon Brunel Date: Sat, 12 Jan 2019 15:00:59 +0100 Subject: [PATCH 2/5] Change package.json CJS entry point --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bf913bf0aee..0062c3e93e8 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "license": "MIT", "jsdelivr": "dist/Chart.min.js", "unpkg": "dist/Chart.min.js", - "main": "src/chart.js", + "main": "dist/Chart.js", "keywords": [ "canvas", "charts", From 891f7c504a5c18de33a0f34ca5c6efc435aabbc8 Mon Sep 17 00:00:00 2001 From: Simon Brunel Date: Mon, 28 Jan 2019 17:20:53 +0100 Subject: [PATCH 3/5] Add docs about excluding moment in Webpack/Rollup --- docs/getting-started/integration.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/getting-started/integration.md b/docs/getting-started/integration.md index 285bf195b92..fa8fca52739 100644 --- a/docs/getting-started/integration.md +++ b/docs/getting-started/integration.md @@ -11,13 +11,33 @@ Chart.js can be integrated with plain JavaScript or with different module loader ``` -## Webpack +## Bundlers (Webpack, Rollup, etc.) ```javascript import Chart from 'chart.js'; var myChart = new Chart(ctx, {...}); ``` +**Note:** Moment.js is installed along Chart.js as dependency. If you don't want to use Momemt.js (either because you use a different date adapter or simply because don't need time functionalities), you will have to configure your bundler to exclude this dependency (e.g. using [`externals` for Webpack](https://webpack.js.org/configuration/externals/) or [`external` for Rollup](https://rollupjs.org/guide/en#peer-dependencies)). + +```javascript +// Webpack +{ + externals: { + moment: 'moment' + } +} +``` + +```javascript +// Rollup +{ + external: { + ['moment'] + } +} +``` + ## Common JS ```javascript From 3fac02d15c1771e8238cf246b4bc2469d852f80e Mon Sep 17 00:00:00 2001 From: Simon Brunel Date: Tue, 29 Jan 2019 11:43:52 +0100 Subject: [PATCH 4/5] Add a note about Moment.js in Require.js --- docs/getting-started/integration.md | 44 ++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/docs/getting-started/integration.md b/docs/getting-started/integration.md index fa8fca52739..210e0d77dc6 100644 --- a/docs/getting-started/integration.md +++ b/docs/getting-started/integration.md @@ -11,6 +11,13 @@ Chart.js can be integrated with plain JavaScript or with different module loader ``` +## Common JS + +```javascript +var Chart = require('chart.js'); +var myChart = new Chart(ctx, {...}); +``` + ## Bundlers (Webpack, Rollup, etc.) ```javascript @@ -38,19 +45,42 @@ var myChart = new Chart(ctx, {...}); } ``` -## Common JS +## Require JS + +**Important:** RequireJS [can **not** load CommonJS module as is](https://requirejs.org/docs/commonjs.html#intro), so be sure to require one of the UMD builds instead (i.e. `dist/Chart.js`, `dist/Chart.min.js`, etc.). ```javascript -var Chart = require('chart.js'); -var myChart = new Chart(ctx, {...}); +require(['path/to/chartjs/dist/Chart.min.js'], function(Chart){ + var myChart = new Chart(ctx, {...}); +}); ``` -## Require JS +**Note:** starting v2.8, Moment.js is now an optional dependency for `Chart.js` and `Chart.min.js`. That means you need to make sure Moment.js is fully loaded **before** requiring Chart.js. You can either use a shim: ```javascript -require(['path/to/chartjs/dist/Chart.js'], function(Chart) { - var myChart = new Chart(ctx, {...}); +require.config({ + shim: { + 'chartjs': { + deps: ['moment'] // enforce moment to be loaded before chartjs + } + }, + paths: { + 'chartjs': 'path/to/chartjs/dist/Chart.min.js', + 'moment': 'path/to/moment' + } +}); + +require(['chartjs'], function(Chart) { + new Chart(ctx, {...}); }); ``` -> **Important:** RequireJS [can **not** load CommonJS module as is](https://requirejs.org/docs/commonjs.html#intro), so be sure to require one of the built UMD files instead (i.e. `dist/Chart.js`, `dist/Chart.min.js`, etc.). +or simply use two nested `require()`: + +```javascript +require(['moment'], function() { + require(['chartjs'], function(Chart) { + new Chart(ctx, {...}); + }); +}); +``` From 91c877e3bd8b8343275128fee2abac34d4e736b7 Mon Sep 17 00:00:00 2001 From: Simon Brunel Date: Tue, 29 Jan 2019 16:49:57 +0100 Subject: [PATCH 5/5] Update require.js note --- docs/getting-started/integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/integration.md b/docs/getting-started/integration.md index 210e0d77dc6..71e7d28ef08 100644 --- a/docs/getting-started/integration.md +++ b/docs/getting-started/integration.md @@ -55,7 +55,7 @@ require(['path/to/chartjs/dist/Chart.min.js'], function(Chart){ }); ``` -**Note:** starting v2.8, Moment.js is now an optional dependency for `Chart.js` and `Chart.min.js`. That means you need to make sure Moment.js is fully loaded **before** requiring Chart.js. You can either use a shim: +**Note:** starting v2.8, Moment.js is an optional dependency for `Chart.js` and `Chart.min.js`. In order to use the time scale with Moment.js, you need to make sure Moment.js is fully loaded **before** requiring Chart.js. You can either use a shim: ```javascript require.config({