Skip to content
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

Ability to import UMD modules #18

Closed
kirill-konshin opened this issue Jun 14, 2019 · 8 comments
Closed

Ability to import UMD modules #18

kirill-konshin opened this issue Jun 14, 2019 · 8 comments

Comments

@kirill-konshin
Copy link

Currently UMD modules (like react or react-dom) cannot be imported directly.

I was exploring the current state and found out that, for example, React is published only as UMD, they have a ticket to add ES6 modules to the published versions. But it's a long awaited feature and there's no conclusion on how to export.

Maybe it is possible to address this feature in this library then? It would be a nice step forward.

Some other useful links:

@Hypercubed
Copy link
Contributor

I can't answer for @guybedford but my guess it is out of scope for this shim. What you can do is bundle the UMD into an ESM. There are a few tools you can use, maybe rollup.

@kirill-konshin
Copy link
Author

kirill-konshin commented Jun 15, 2019

That was my first guess too. But I thought maybe if you guys went ahead and added support for import maps, then UMD would be a nice addition too.

The thing is I’m not in control how NPM packages are published, and above mentioned React case is totally out of my influence, moreover it’s a long going discussion there.

I was experimenting with React and for now I created a super dirty solution https://github.com/kirill-konshin/pure-react-with-dynamic-imports/blob/master/public/sw.js#L19 - use a Service Worker to patch the react sources on the go. The problem with their UMD dist is that global (this) is not defined when UMD is imported, so the entire file is useless.

@chase-moskal
Copy link

chase-moskal commented Jun 16, 2019

i would not really expect es-module-shims to support legacy module types like umd or otherwise

ideally i think our goal should be to drag legacy packages, kicking and screaming, into the future of esm ;)

@kirill-konshin, there might be three interesting branches on the tree of solutions here

  1. fork and publish a new react-esm

    somebody could fork react, and use some tool to convert the UMD modules into ESM modules, and then publish that under a new package name like react-esm — and then launch a pull request for the react team to merge or discard at their leisure — maybe they would even stick a link to a react-esm fork in a wiki somewhere

    this takes some effort, but it might benefit the community, and it might be nice to be able to load react esm directly from unpkg and the like

  2. use a converter like rollup-plugin-commonjs in your build

    as @Hypercubed mentioned, there should be a bundler plugin which can do this automatically in your build step

    according to this stackoverflow answer, it looks like rollup-plugin-commonjs should work with umd modules

    but then of course, you're stuck with a build step, and worse yet — you're hording the es module goodness all to yourself, you selfish module dragon you! ;)

  3. consider making es-module-shims-plugin-legacy

    this would be cool, because it could be loaded optionally, and basically work like rollup-plugin-commonjs does, but for es-module-shims and we get to skip the build step 😎

    this might take a lot of work, though...

considering limited time and resources, i think you should consider grabbing rollup and going that way — unless you want to donate time to the community and/or use react with es-module-shims, in which case you might opt to do the forking — otherwise we can pray for luck that guy bedford will take the third option because maybe he's already done similar work in systemjs, and is a wizard from what i can tell

food for thought.. what do you think? cheers! 👋

@guybedford
Copy link
Owner

@chase-moskal a very nice overview of the space :)

Yes the goal of this project is exactly to be entirely polyfill-like, ideally such that you can develop in modern browsers, and ship this as a shim for older browsers. We're still a ways off that sort of mode, but any work in these directions is always welcome. UMD does fall outside of those goals, although if there is an easy way to support it as an addon (see how SystemJS does extras), or if you want to fork this project and explore the space, please feel free.

For the (2) case, this is exactly what jspm 2.0 does in converting CJS to ESM on install from npm, and ideas to improve the experience there are very welcome.

Yes handling the conversion layer at the package layer would be ideal, but while packages still need to support legacy environments, building direct ESM ecosystems remains the other approach here, sort of like what Pika is doing. How we convert packages to be directly ESM will be a tough process at first, but will be a lot easier once it is the default to support modules in Node.js 12 and enough users have moved over to that.

@kirill-konshin
Copy link
Author

I do agree that conversion may not be the focus of this package.

But I strongly disagree that custom package or custom install converter is a good idea. The whole point is to eliminate as many build steps as possible, so like I showed in my example, I already spent some time and found a viable solution (Service Worker). It just has to become more generic than straightforward replace. And it could be done as a plug-in, yep. Can you give me some information how to make a plug-in for the shim?

@guybedford
Copy link
Owner

@kirill-konshin perhaps we could add a transform hook on the global importShim object. See how the SystemJS translate hook works in https://github.com/systemjs/systemjs/blob/master/src/extras/transform.js. I'd be happy to accept a PR for that, but we have to keep the overall footprint of the project down so I will only accept minimal PRs. Otherwise feel free to fork as I say.

@kirill-konshin
Copy link
Author

kirill-konshin commented Jun 17, 2019

Here's the other way, also via Service Worker, quite elegant. The idea is to inject UMD as-is into the page and then export default the global var (this is the code of Service Worker):

// this is needed to activate the worker immediately without reload
//@see https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle
self.addEventListener('activate', event => clients.claim());

const globalMap = {
    'react': 'React',
    'react-dom': 'ReactDOM'
};

const getGlobalByUrl = (url) => Object.keys(globalMap).reduce((res, key) => {
    if (res) return res;
    if (matchUrl(url, key)) return globalMap[key];
    return res;
}, null);

const matchUrl = (url, key) => url.includes(`/${key}/`);

self.addEventListener('fetch', (event) => {

  const {request: {url}} = event;

  console.log('Req', url);

  if (Object.keys(globalMap).some(key => matchUrl(url, key))) {
         event.respondWith(
            fetch(url)
                .then(response => response.text())
                .then(body => new Response(removeSpaces(`
                        const head = document.getElementsByTagName('head')[0];
                        const script = document.createElement('script');
                        script.setAttribute('type', 'text/javascript');
                        script.appendChild(document.createTextNode(${JSON.stringify(body)}));
                        head.appendChild(script);
                        export default window.${getGlobalByUrl(url)};
                    `), {
                        headers: new Headers({
                            'Content-Type': 'application/javascript'
                        })
                    })
                )
        )
  }
});

Here's what I've done here:

  1. Created the export map, which associates package id with global var name
  2. Created a script tag in head with contents of UMD-packaged script
  3. Exported the mapped global var as default export of module

The code above makes sure that it's executed as a regular script, so we won't have issues with sudden strict mode when the original package was not designed for it.

@guybedford
Copy link
Owner

I think wrapper approaches like the above make a lot of sense, and would be preferable to trying to include it in this project since they would also in theory work with native ES modules.

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

No branches or pull requests

4 participants