mithril.js
mithril.js copied to clipboard
ES Module support
ES Modules have landed under flags in all browsers, so it would be great if we could get a build of mithril that supports it.
Being able to import { m, request, route, promise } from './mithril.js'
would be great.
It would be possible if we altered the bundle to optionally emit an ES module and export it instead. Do note that it would most likely default-export the m
factory with all its attachments, so you wouldn't see much stripped from Rollup if you were to try to use it.
I think I brought up this issue inside the chat room couple of months ago. I was advised to use syntax like this
import m from "mithril/hyperscript";
import render from "mithril/render";
I would personally just advise in the meantime using Rollup's rollup-plugin-commonjs
, if you're using that. It's low-priority for us currently, but it's something easily fixed. PRs would be welcome, if you would like.
If we're gonna do this it should go all the way. I haven't tried converting everything to ES Modules + rollup
recently, but the last time I tried bundler
beat it by a few bytes. Probably worth trying again as rollup
has continued improving in the meantime (albeit slowly)
Added to the 2.0
milestone, dunno that it'll actually make it in that timeframe but it's good to aspire to something.
@tivac The current bundler still has the advantage that the module
polyfill works in every supported browser, out of the box. We'd have to deprecate the tests/index.html
files and find an alternative... The interest of these files is limited since the DOM API is mocked in the tests, even in browsers, but it allows one to check that the JS at least runs, even if it doesn't exercises the real DOM.
Long term I think we should focus on integration-testing via the public APIs instead of the more tightly-scoped tests we have now. They're sometimes harder to write up-front but we end up with better guarantees of API-correctness. That shift would mean all tests could use the bundle for testing instead of needing to support piecemeal usage.
Investigating ES Modules is solely in the "experimental" phase right now, I added the milestone o try and ensure that I experiment w/ it again before too much longer.
Module | Unminified | Unminified + Gzip | Minified | Minified + Gzip |
---|---|---|---|---|
CommonJS (Bundler) | 45.27KB |
11.35KB |
21.73KB |
8.03KB |
ES Modules (Rollup) | 46.17KB |
11.50KB |
21.85KB |
8.08KB |
bundler
beats rollup
by 0.05KB, or 50 bytes for the "Minified + Gzip" test in a naive port.
A more complete port of mithril's internals to ES Modules would probably yield smaller overall app bundles when factoring in tree-shaking. It'd require a lot of refactoring & removing of the way current APIs work though.
While I appreciate the way the framework has been broken up into discrete chunks, I do wonder if some of the files that enable piecemeal usage could be dropped entirely. This is anecdata at it's finest, but has anyone ever heard of someone actually using anything other than the default m
export? I can't think of an example that I've seen.
Aren't there at least a few people on gitter (names escape me) that are using mainly the m.render
/ hyperscript API in their projects because they want full control of rendering? IIRC that's at least what @foxdonut does with meiois, apart from the routing implementation, and there are probably others. I'm sure there's a spectrum of how usable parts of the codebase is piecemeal, where at least streams, requests (not that I think anyone does this), maybe querystring could be used separately somewhere...
@tivac Just out of curiosity, what's the Rollup bundle look like?
Regarding improving the port:
- I don't think 50 bytes would be a big deal, especially if we can recover it. We've had bug fixes that have added more than that. For comparison, an empty gzip file is 28 bytes and an empty UMD Rollup bundle is 230 bytes raw, 164 gzipped, 154 minified, and 130 bytes min+gzip, so if you're using
--format=umd
, there's your extra 50 bytes, since Rollup generates AMD support. - There's a few people who use
api/
stuff and lower-level hooks (like mithril-node-render IIRC), but if we exposed them somehow*, perhaps in a separate bundle, that could help their use cases while giving us more freedom to change internals and keep the primary bundle smaller.
* Of course, on a "you're on your own" basis, where it may change even across patch versions.
I agree with @orbitbot above. We also use just hyperscript and render from Mithril. Also I think we should avoid including promise by default as most of the environments already have promise in it either natively or through babel-polyfills.
@gyandeeps the Promise polyfill is there to have Mithril work out of the box in IE9-11. IE 11 is still relevant, and not everyone uses Babel or polyfills. Being able to get started with Mithril without other deps is IMO an important feature
Which I dont disagree with. But today, people who use libraries like react and mithril in production tend to use polyfill libraries. That when we have repeated code. Thats why I suggested that their should be a way to ignore it. Most of the libraries today avoid carrying polyfills with them because then you are having duplicates.
I would like to exclude the full polyfill from the bundle, and instead have a small shim layer that can either return a reference to global.Promise
or throw a helpful error like Environment is missing Promises, please see https://mithril.js.org/promises.html
.
It increases friction a tiny bit for IE users, but it'd be part of a major version bump and then means that every other environment would be running less code. That'd improve both our download and parsing time, improving overall startup.
I'd love to use mithril with rollup to only include the code and functions I actually use.
In particular, I often don't use routing, the request code or the polyfill. Consider hybrid apps or other apps that only support a small set of browser engines.
So our new plan is to revert our initial design (#2366) and instead expose the ES module source under mithril/esm
, with each transpiled file duplicated with .js
and .mjs
extensions since tools depend on both. To ensure the files are only valid as modules, I'll append an export {}
to each file so they can never be successfully parsed as scripts, to help encourage people to have their bundlers set up correctly.
Mirroring everything via .js
and .mjs
variants in the same directory structure sounded good in theory, but tooling and runtimes deviate from one another too much in this area for that to work out.
Here's a +1 so you guys are encouraged to treat this issue as more of a priority...
Here's the status: we're waiting for someone to file a PR for something like this. There's two reasons why this is low-priority currently:
- Having a v2 out the door that wasn't breaking everyone was higher priority than having ESM support. So that took most of the time until the last couple weeks, and I'm still working on ensuring a few common community modules are updated for v2 ASAP, most notably
mithril-node-resolve
. - Most bundlers either offer support for CommonJS modules natively or they have a commonly used plugin (or set of plugins, as is the case for Rollup with
rollup-plugin-commonjs
+rollup-plugin-node-resolve
) for it, and we do offer a UMD bundle for global usage if you don't want to bundle.
Once I resolve the first, then I'll look into this.