Skip to content

Commit

Permalink
Make moment optional from our UMD builds (chartjs#5978)
Browse files Browse the repository at this point in the history
  • Loading branch information
simonbrunel committed Jan 29, 2019
1 parent f31445f commit 9e82e52
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 8 deletions.
62 changes: 56 additions & 6 deletions docs/getting-started/integration.md
Expand Up @@ -11,26 +11,76 @@ Chart.js can be integrated with plain JavaScript or with different module loader
</script>
```

## Webpack
## Common JS

```javascript
import Chart from 'chart.js';
var Chart = require('chart.js');
var myChart = new Chart(ctx, {...});
```

## Common JS
## Bundlers (Webpack, Rollup, etc.)

```javascript
var Chart = require('chart.js');
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.js'], function(Chart) {
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, {...});
});
});
```
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -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",
Expand Down
9 changes: 8 additions & 1 deletion rollup.config.js
Expand Up @@ -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';
Expand All @@ -21,7 +22,10 @@ module.exports = [
input: input,
plugins: [
resolve(),
commonjs()
commonjs(),
optional({
include: ['moment']
})
],
output: {
name: 'Chart',
Expand All @@ -42,6 +46,9 @@ module.exports = [
plugins: [
resolve(),
commonjs(),
optional({
include: ['moment']
}),
terser({
output: {
preamble: banner
Expand Down
61 changes: 61 additions & 0 deletions 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
};

0 comments on commit 9e82e52

Please sign in to comment.