Skip to content
This repository has been archived by the owner on Jul 23, 2019. It is now read-only.

Add support for ES modules (work in progress) #489

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

nolanlawson
Copy link
Contributor

@nolanlawson nolanlawson commented Apr 30, 2017

This PR isn't fully baked yet. The point is to start a conversation. 😃

Background

I've been looking into shrinking the size of the emojione JS bundle. The context is that I've been analyzing the performance of the Mastodon webapp, and it appears emojione is roughly 1/3 the size of the entire app bundle and may contribute as much as 1 full second to the load time (on mobile devices in particular).

One way to make the emojione codebase "smaller" without much extra work is to support named exports via ES modules. Then consumers can use a module bundler like Webpack or Rollup to include only the parts of the API that they need (tree-shaking).

Improvement

What this PR does is divide the existing codebase into a few small source files (mostly for readability), then build emojione.js as a UMD build and emojione.es.js as an ES module build using Rollup. Then it points to the ES module build from package.json as "module", which both Webpack and Rollup support.

I tested out the size improvements with this PR, and it works! In this PR, emojione.js is 932429 bytes / 566902 gzipped, whereas just doing import { shortnameToUnicode } from 'emojione' results in 610828 bytes (34% smaller) / 380337 gzipped (32% smaller).

FWIW, you can compare this to 565800 bytes for the current production version of emojione.min.js, so note I also managed to make it slightly smaller even for CommonJS/globals users, mostly due to removing repeated ns.foo = foo statements and using inline variables instead.

Next steps

There's only one catch: there are some parts of the API that I couldn't faithfully reproduce in both CommonJS/UMD format and ES module format. That's these ones:

  • greedyMatch
  • imageTitleTag
  • sprites
  • unicodeAlt
  • ascii

For these options, the user is supposed to toggle the boolean and the relevant functions update themselves automagically. I could probably write a Rollup plugin to do this correctly for the CommonJS output (ES modules have a concept of variable binding and so it's not needed there), but another option I considered was to simply add these as input parameters to the functions that need them, which to me is more explicit anyway. But unfortunately that would be a breaking change and so would require a major version bump (per SemVer).

What do you think of these two options, and of this PR in general? Looking forward to helping improve emojione's perf! 😄

@caseyahenson
Copy link
Contributor

@nolanlawson nice work! This is a clever solution to a problem that we've wanted to address for some time. As for defining properties in this scenario I don't immediately see any better options than passing them into the functions when called, which as you mention is a breaking change.

I'll be doing some testing with your proposal over the next couple of weeks with a focus on an alternative for those object properties and hopefully come up with some options.

@nolanlawson
Copy link
Contributor Author

nolanlawson commented May 6, 2017

Are you concerned that we'd be better off keeping the old behavior and not doing a major version bump? If so, I'd be happy to go back and investigate to see if we can maintain the old behavior while still supporting a new ES module API. I suspect just bumping the major version may be easier though.

@nolanlawson
Copy link
Contributor Author

nolanlawson commented May 6, 2017

I just realized this may be a moot point: I wrote a test for greedyMatch, and actually it passes before this PR and after this PR. I think I had misinterpreted how Rollup was creating the CommonJS version of the module; it appears to work exactly the same before and after. This is wrong, ignore...

The issue now seems to be a merge conflict; I'll look into this…

@nolanlawson nolanlawson force-pushed the es6-modules branch 2 times, most recently from 2f40ca7 to 8b6c78e Compare May 6, 2017 16:51
@nolanlawson
Copy link
Contributor Author

OK, I very carefully rebased on top of the latest master (648da99). I note that there seem to be two variables, emojiSize and spriteSize, which do exactly the same thing, but I kept both of them because that's how it appears in master.

This PR also contains the new test for greedyMatch. Unfortunately it appears that all the tests are now failing in master due to the 64->32 change. 😕

@nolanlawson
Copy link
Contributor Author

Addressed the broken tests in #498

@nolanlawson
Copy link
Contributor Author

Hm okay, tried to write an ES module format that could produce both a default export with booleans that could be toggled on and off, and named exports for tree-shaking, but seems Rollup complains when you do this:

Using named and default exports together. Consumers of your bundle will have to use emojione['default'] to access the default export, which may not be what you want. Use exports: 'named' to disable this warning

My vote would be to just go with adding the boolean options to each API and then releasing it as a breaking change.

@nolanlawson
Copy link
Contributor Author

Ping... wondering if there are some improvements I could make to make this PR more palatable:

  1. Figure out a backwards-compatible way to ship both an ES module and a CommonJS module without breaking the old functionality. I think it can be done with Rollup plugins.
  2. Hook the build process into the build pipeline, e.g. as an npm prepublish script so that the built files don't need to be checked into version control.

Do those sound good @caseyahenson?

@caseyahenson
Copy link
Contributor

@nolanlawson the big thing we want to avoid here is a major version bump.

We could use default parameters within the predefined functions so that backwards compatibility is maintained, but the properties would need to be passed into the functions to overwrite defaults so I believe that would still be considered a breaking change.

Can you elaborate a bit on the idea of shipping both ES and CommonJS modules?

@nolanlawson
Copy link
Contributor Author

The idea would be to ship a default object containing booleans you can tweak, and then non-default functions for everything else. Presumably with the right Rollup configuration or a custom plugin it could be made to emit a CommonJS module that does the same thing as the current behavior, while still allowing ESM users to tree-shake.

@RemyLespagnol
Copy link

No more update about this important feature ?
Emojione is a huge package, and take 1/4 of my app...

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

Successfully merging this pull request may close these issues.

3 participants