Skip to content

Add an ESM build #414

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 5, 2021
Merged

Add an ESM build #414

merged 7 commits into from
Jan 5, 2021

Conversation

joshkel
Copy link
Contributor

@joshkel joshkel commented Dec 30, 2020

Based off of chartjs-plugin-annotation's approach.

Specific changes:

  • Switch from Gulp to Rollup for build; using Rollup directly works and seems simpler.
  • Refactor some duplicate configuration in rollup.config.js.
  • Change import style for Chart.js and Chart.js helpers so that it will work with modules.
  • The new import style doesn't allow adding to the imported object, which caused problems for the Chart.Zoom namespace. Since that namespace appears to be undocumented and unused, I removed it.

The ESM version does not automatically register the plugin. This matches chartjs-plugin-annotation and seems to better match Chart.js 3's tree-shaking design.

I'm unclear on whether the UMD build should automatically register the plugin. Not registering seems to better match Chart.js 3, and chartjs-plugin-datalabels recently changed to no longer register, but chartjs-plugin-annotation's UMD build does register.

@benmccann
Copy link
Collaborator

Awesome! Thanks for this!

It looks like chartjs-plugin-datalabels was changed to not register so that the plugin isn't automatically enabled on all charts. I agree you may want to enable a plugin only on some charts. I also agree we should probably make sure this plugin, the data labels plugin, and annotations plugin work the same way. @etimberg @kurkle do you have thoughts on UMD vs ESM behavior here and what the standard should be?

@kurkle
Copy link
Member

kurkle commented Dec 30, 2020

I think we agreed on not registering on any build to be consistent.

@joshkel
Copy link
Contributor Author

joshkel commented Dec 31, 2020

After I asked yesterday about whether plugins should register, I remembered that the v3 migration guide says that Chart.js 3 itself doesn't require registering when it's imported via a <script> tag (which, if I understand correctly, would be when using the UMD build).

So registering in the UMD build seems more consistent?

(I'm happy to do whichever; I just wanted to confirm before proceeding.)

@simonbrunel
Copy link
Member

The rationale behind this change in the datalabels plugin is detailed in chartjs/chartjs-plugin-datalabels#42.

So registering in the UMD build seems more consistent?

Auto-registering should have nothing to do with the type of package, especially because UMD builds can be used with require and import (not only <script>). So it's quite inconsistent that the same import syntax against different packages doesn't produce the same result.

And when using <script>, users still have to write JavaScript in order to create a chart, so I don't see any benefit of auto-registering plugins in this case. It brings inconsistencies between import methods, makes more complicated the build/release process and needs to be explained in the docs. All of this to prevent <script> users to write Chart.register(plugin).

@kurkle
Copy link
Member

kurkle commented Jan 1, 2021

Its not only about plugins though. If we want to be consistent the registration of controllers, elements and scales should be required in all builds too. and that would be silly in my opinion, because you can not shake anything from an umd build. unless we provide some generic registerAll. And if we do that, maybe plugins should do the same?

@simonbrunel
Copy link
Member

simonbrunel commented Jan 1, 2021

It's not the same because registering a plugin makes it immediately active while registering a controller / element / scale does not. I think I would actually make registering core plugins required for all builds so it's fully consistent. And again, registering a plugin is not about tree-shaking but about making it "active" for all charts. The fact that there is a single Chart.register() method for all the things may make things a bit confusing.

@simonbrunel
Copy link
Member

unless we provide some generic registerAll

I find extremely annoying, not intuitive and not user friendly the way to integrate Chart.js with the ESM build (which is nowadays the most common use case). I understand why "some" wants this granularity of import/register but "most" users should not have to deal with all these imports. For example, a user who wants to create a line chart should not have to think about registering the LineElement, PointElement, LineController, CategoryScale, LinearScale, etc. because these are implicit dependencies coming from defaults.

One solution could be to provide some kind of "presets" so "most" users would just have to:

import { LineChart } from 'chart.js';

Chart.register(LineChart);

new Chart(ctx, {
    type: 'line',
    options,
    data,
});

@benmccann
Copy link
Collaborator

There are some such as pie where registration is probably unnecessarily verbose even for tree-shaking users - you can't have a pie / doughnut / polar area chart without radial scale and arc element, so perhaps we should automatically register those for you in that case.

I kind of like the preset idea. LineController is one of the trickiest because it's pretty common not to use the default scales. E.g. the category scale is often switched out for linear or time/timeseries scale. Line/radar is also the only one with multiple possible elements. Registering both elements would make sense for the preset case. Would we register all compatible scales as well? And also all compatible plugins like the filler plugin?

@benmccann
Copy link
Collaborator

I think the idea of registering and enabling plugins has gotten a bit muddied. I think plugins should always be registered when imported (i.e. available for use), but should not be globally enabled by default in either the UMD or ESM builds. I think it's reasonable to have an API to globally enable, but we should not call it register as it's an overloaded use of the term and we should call it enable or something else.

@simonbrunel
Copy link
Member

I think plugins should always be registered when imported (i.e. available for use)

I disagree on this because it's usually not a good practice to expect side effect on import. Also, we could imagine a plugin that "overrides" another plugin, in which case we don't necessarily want the base plugin to be registered. For example:

import DataLabels from 'chartjs-plugin-datalabels';

// chartjs-plugin-custom-datalabels
export default Object.assign({}, DataLabels , {
   beforeUpdate() {
      // ...
   }
});

I would compare this to Vue.js and their official plugins: they recently chose to remove this side-effect logic in favor of explicit registration. I also think it's a better approach, at least for our plugins. And honestly, I'm not sure to understand what we are trying to simplify?

import ZoomPlugin from 'chartjs-plugin-zoom';

Chart.plugins.register(ZoomPlugin);

vs

import 'chartjs-plugin-zoom';  // side-effect

Chart.plugins.enable('zoom');

The first snippet looks more predictable and robust IMO.

@simonbrunel
Copy link
Member

I think plugins should always be registered when imported (i.e. available for use)

@benmccann after reading again your comment, I'm wondering if I correctly understood your suggestion :) i.e. registering the plugin should be a side effect of importing the plugin or if we should expect the user to explicitly call Chart.register() after the import (i.e. the change I made for the datalabels plugin)?

@benmccann
Copy link
Collaborator

I was suggesting to have a side effect, but to your point that's not really necessary to avoid verbosity. Maybe a better way to solve my concern without side effects is to rename the API. register to me generally just means to make something available in the registry and I didn't like that it's also having another effect of also globally enabling a plugin. Something like registerAndEnable might be more explicit.

@benmccann
Copy link
Collaborator

@joshkel could you rebase this since #416 was merged?

The question about whether to register in the UMD builds seems to be a big one 😄 Perhaps just leave it out for now and we can discuss the best path forward separately without blocking your PR

@jledentu
Copy link
Collaborator

jledentu commented Jan 4, 2021

I agree with @simonbrunel that the first syntax is much more explicit, and it's a good practice to avoid side effects IMO.

@benmccann Maybe a function named use (like in Vue) would be even more explicit (we don't only "register" something but really "use" it):

Chart.plugins.use(ZoomPlugin);

@kurkle
Copy link
Member

kurkle commented Jan 4, 2021

@joshkel could you rebase this since #416 was merged?

The question about whether to register in the UMD builds seems to be a big one 😄 Perhaps just leave it out for now and we can discuss the best path forward separately without blocking your PR

I agree to this, the removal of auto-registration needs changes in test setup, so it should be done separately.

Based off of chartjs-plugin-annotation's approach.

Specific changes:

- Switch from Gulp to Rollup for build; using Rollup directly works and seems simpler.
- Refactor some duplicate configuration in rollup.config.js.
- Change import style for Chart.js and Chart.js helpers so that it will work with modules.
- The new import style doesn't allow adding to the imported object, which caused problems for the `Chart.Zoom` namespace. Since that namespace appears to be undocumented and unused, I removed it.
Use a named export instead of a default export.

Don't automatically register the plugin for ESM builds.  This matches chartjs-plugin-annotation and seems to better match Chart.js 3's tree-shaking design.

I'm unclear on whether the UMD build should automatically register the plugin. Not registering seems to better match Chart.js 3, and chartjs-plugin-datalabels [recently changed][1] to no longer register, but chartjs-plugin-annotation does.

[1]: <chartjs/chartjs-plugin-datalabels@13daa47>
Gulp copied compiled files to the project root. I don't see any reason to do this.
As suggested in code review. This also better matches the coding style in other Chart.js code.
@joshkel
Copy link
Contributor Author

joshkel commented Jan 5, 2021

Thanks for the discussion and feedback. I've rebased and responded to the remaining code review comments.

Switching to an explicit use / register step makes sense to me, but as suggested by @kurkle, I preserved the existing behavior (UMD does auto-register) for now, to keep this PR more focused and to leave the tests unchanged.

* Copy the files from `/dist` to the root directory.
* @todo remove at version 1.0
*/
function copyDistFilesTask() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing this will break gulp package so that the release process doesn't work. you might want to leave this but make it run as the first step of package instead of the last step of build

@@ -9,5 +9,3 @@ coverage/*

nbproject/*
dist/
chartjs-plugin-zoom.js
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should probably leave these and add the esm files as well for packaging. see my other comment below

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm understanding correctly, we don't need these .gitignore lines or the gulpfile copyDistFilesTask.

The previous configuration invoked Rollup to put the files under dist, then it called copyDistFilesTask to copy the files out of dist into the project root (which is why the files are listed in .gitignore here). But package.json references the copies under dist directory, and the gulpfile's packageTask references the copies under dist (via outDir). I can't tell that the copies in the project root are used at all, so I removed them.

If I've missed something, please let me know.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gulp package looks like it looks for files in the root directory:

gulp.src([outDir + '*.js', 'LICENSE.md']),

Alternatively, you might be able to update gulp package to look in dist

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm understanding Gulp correctly, it treats outDir as the glob base for outDir + '*.js', so the files from dist are included at the top level of the resulting zip file. Contrast with the explicit base parameter for the samples glob; those are included in a samples subdirectory within the zip file:

gulp.src('./samples/**/*', {base: '.'})

I tested by running npm run build; npx gulp package in a fresh copy of my branch, and it created a dist/chartjs-plugin-zoom.zip file with contents similar to the releases on GitHub, so I think things are working.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, we might need to update the comments in the packageTask as they threw me off and look like they might be outdated:

// gather "regular" files landing in the package root
// since we moved the dist files one folder up (package root), we need to rewrite
// samples src="../dist/ to src="../ and then copy them in the /samples directory.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I found that confusing too. I just pushed some comments to try and clarify; feedback welcome.

Copy link
Member

@kurkle kurkle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot clearer to me

@benmccann benmccann merged commit 02d561b into chartjs:master Jan 5, 2021
@benmccann
Copy link
Collaborator

Awesome. Thanks for this!!

If you have any interest in contributing further, I'd love to get rid of gulp altogether 😄

@joshkel joshkel deleted the esm-build branch January 5, 2021 19:55
@joshkel
Copy link
Contributor Author

joshkel commented Jan 5, 2021

Sure, I can take a look at removing gulp. Thank you.

@benmccann
Copy link
Collaborator

@joshkel it's gone now 😄 @kurkle was very fast as making a whole bunch of improvements! thanks again for the help

@kurkle
Copy link
Member

kurkle commented Jan 5, 2021

Sure, I can take a look at removing gulp. Thank you.

Oh, sorry! I did not see this 😞

@joshkel
Copy link
Contributor Author

joshkel commented Jan 5, 2021

No problem; I hadn't started yet. Thanks, @kurkle and @benmccann.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants