diff --git a/docs/getting-started/integration.md b/docs/getting-started/integration.md index 285bf195b..71e7d28ef 100644 --- a/docs/getting-started/integration.md +++ b/docs/getting-started/integration.md @@ -11,13 +11,6 @@ Chart.js can be integrated with plain JavaScript or with different module loader ``` -## Webpack - -```javascript -import Chart from 'chart.js'; -var myChart = new Chart(ctx, {...}); -``` - ## Common JS ```javascript @@ -25,12 +18,69 @@ var Chart = require('chart.js'); var myChart = new Chart(ctx, {...}); ``` -## Require JS +## Bundlers (Webpack, Rollup, etc.) ```javascript -require(['path/to/chartjs/dist/Chart.js'], function(Chart) { +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'] + } +} +``` + +## 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 +require(['path/to/chartjs/dist/Chart.min.js'], function(Chart){ var myChart = 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.). +**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({ + 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, {...}); +}); +``` + +or simply use two nested `require()`: + +```javascript +require(['moment'], function() { + require(['chartjs'], function(Chart) { + new Chart(ctx, {...}); + }); +}); +``` diff --git a/package.json b/package.json index bf913bf0a..0062c3e93 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", diff --git a/rollup.config.js b/rollup.config.js index 9f76d9573..83ae38d67 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 000000000..39c75700e --- /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 +};