A few module ideas
Caridy Patino
caridy at gmail.com
Sun Aug 20 14:04:05 UTC 2017
export values are dynamic today! you can do:
let lib = null;
export default lib;
loadScript("foo").then(o => lib = o);
but what cannot be dynamic, is the set of export names, which shapes the module.
/caridy
> On Aug 20, 2017, at 8:57 AM, kai zhu <kaizhu256 at gmail.com> wrote:
>
> for the forseeable future, the only practical solution i see to these
> frontend performance issues is to avoid using es modules, and revert
> to the simple, old-fashioned method of assigning libraries to the
> window namespace.
>
>> On 8/20/17, James Browning <thejamesernator at gmail.com> wrote:
>> These are just some ideas I've had for improving es modules based on
>> my experiences with them. The syntax and stuff with them isn't too
>> important, the main point is the problem I'm trying to solve with each
>> of them, feedback and criticism is welcome.
>>
>> # Dynamic modules
>>
>> One of the biggest issues I've had with ES modules is not being able
>> to load classic scripts as part of the dependency graph, one of the
>> solutions I've used but am not particularly happy with is having an
>> async loader function e.g.:
>>
>> ```js
>> function loadScript(url) {
>> return new Promise(resolve => {
>> const scriptElem = document.create('script')
>> scriptElem.src = url
>> scriptElem.onload = resolve
>> })
>> }
>>
>> // some other file
>>
>> async function computeSpline() {
>> await loadScript('./mathjs.js')
>> // use math here
>> }
>> ```
>>
>> And while this approach works somewhat it's a bit of a pain for a
>> couple reasons:
>>
>> - If something gains a `script` dependency it necessarily breaks all
>> consumers by becoming asynchronous even if the original operations
>> were synchronous
>> - It's not generic for other types of resources e.g. I can't load an
>> image without creating another loader function or so on
>>
>> ---
>>
>> My proposed solution, dynamic (but static) export:
>>
>> ```js
>> // math.mjs
>> import loadScript from ".../loadScript.js"
>>
>> loadScript('./math.js').then(_ => {
>> const math = window.math
>> delete window.math
>> export({
>> math as default
>> })
>> })
>> ```
>>
>> This solution is also generic so it can be used for loading any type
>> of resource:
>>
>> ```
>> // highPerformanceMath.mjs
>> fetch('.../math.wasm').then(response => response.arrayBuffer())
>> .then(buffer => WebAssembly.instantiate(buffer, {}))
>> .then(({ instance }) => {
>> export({
>> instance.exports as default
>> })
>> // or potentially named exports
>> export({
>> instance.exports.fastFourierTransform as fastFourierTransform
>> ...
>> })
>> ```
>>
>> Now this solution would be nice because it's generic and allows for
>> loading any (even asynchronous) object as part of the module graph and
>> doesn't cause explosions where because one part becomes asynchronous
>> everything becomes asynchronous.
>>
>> However there is a deficiency in that it can be quite verbose for
>> similar tasks e.g. loading WebAssembly modules which is why I thought
>> of idea 2:
>>
>> # Module Arguments
>>
>> Effectively module arguments would allow passing data to a module
>> (statically) during loading e.g.:
>>
>> ```js
>> // some-file.js
>> import dict from "./dictionary.js" where { lang = "en-US" }
>>
>> // dictionary.js
>> fetch(`./dictionaries/${ import.arguments.lang }.txt`)
>> .then(response => response.text())
>> .then(text => export({
>> JSON.parse(text) as default
>> })
>> ```
>>
>> This solves the previous problem of very similar dynamic modules for
>> similar types by allowing details like that to be passed in as
>> arguments e.g.:
>>
>>
>> ```js
>> import math from "./loadScript.mjs" where {
>> script = './math.js',
>> globalName = 'math'
>> }
>> ```
>>
>> # Lazy Export-From
>>
>> One of the nice things about named exports is you can minimize the
>> amount of mostly similar `import` declarations e.g.:
>>
>> ```js
>> import map from "./lodash/map.js"
>> import filter from "./lodash/filter.js"
>> import flatMap from "./lodash/flatMap.js"
>> ...
>>
>> // can become
>> import { map, filter, flatMap, ... } from "./lodash/lodash.js"
>> ```
>>
>> However it has a major downside of massively increasing the amount of
>> fetch/parse/execute time for all those additional things exported by
>> the combined module.
>>
>> My idea is to allow modules to declare that parts need to not be
>> fetched parsed or executed if they're not actually imported e.g.:
>>
>> ```js
>> // my-operators-library.js
>> static export { map } from "./map.js"
>> static export { filter } from "./filter.js"
>> static export { reduce } from "./reduce.js"
>> ```
>>
>> Effectively all my idea adds is the `static export` (syntax not
>> important) form that effectively says these names should only be
>> resolved if they're actually imported and can be safely ignored if
>> they're not used. This way you get both the benefits of collection
>> modules (easier to `import` and reduces duplication) and the benefits
>> of individual `import`s (lesser loading sizes).
>>
>> # Summary
>>
>> Basically the ideas suggested here are to solve these particular
>> problems I've had with ES modules:
>>
>> - Unable to load classic scripts (and other types of resources
>> statically e.g. conditional modules) as part of the module graph
>> - Unable to specify more specific behavior for a module to prevent
>> duplication
>> - Either have to have lots of almost duplicate import declarations or
>> have to load unnecessary files
>>
>> The solutions I proposed aimed to keep the constraint that module
>> exports should remain statically parsable which is why `export({ ...
>> })` shares the syntactic form.
>>
>> I refrained from specifying the semantics of the specific operations
>> as there's details that'd need to be sorted out for all of them if
>> there is any interest whatsoever in implementing them.
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
More information about the es-discuss
mailing list