evanw / esbuild

An extremely fast bundler for the web
https://esbuild.github.io/
MIT License
37.94k stars 1.13k forks source link

[Feature] UMD bundle support #507

Open jyboudreau opened 3 years ago

jyboudreau commented 3 years ago

UMD is used by many libraries to support legacy module systems. Migrating to esbuild is difficult when relying on this format.

There are some workarounds but they have major downsides:

  1. Post-process the esbuild IIFE output to add the UMD wrapper if you don't have any external dependencies: Makes sourcemap difficult to deal with and makes builds slower. (#482) would simplify this workaround and solve the source map issue but still not allow external dependencies.
  2. Use rollup/webpack's UMD support with esbuild transformers (instead of babel): Doesn't benefit from esbuild's ultra fast dependency resolution and bundling.

Willing to give this a go myself if there is interest to include this feature.

shrinktofit commented 3 years ago

System js, too, please:)

prantlf commented 3 years ago

UMD format is similar to the IIFE format. Just the module wrapper and setting the global variable differs. The real "fun" will begin if you want to convert externals to AMD/CJS imports, when you call the factory function in the UMD wrapper. But this is not needed for projects that bundle all their dependencies or use globals to access externals.

I needed just to bundle local modules without external dependencies. It is probably the most often case how to simply publish a library in the most interoperable format.

I introduced --format=umd in #513.

David-Else commented 3 years ago

Do we really need these legacy formats? ECMAScript modules were meant to resign them to the dustbin, and now after a ridiculous delay, we finally even have ECMAScript modules in Node LTS with Node 14 and near universal browser support.

The old module formats can safely die now... or will they haunt us forever?

jyboudreau commented 3 years ago

@David-Else I agree with the general sentiment and would love for ESM to have solved the whole module thing once it was supported in browser and node, unfortunately the world is messy. I think that ESM being supported in Node is a great milestone that marks the start of transition towards libraries supporting ESM. It's unfortunately not the end of CommonJS, IIFE or even AMD and far from it.

If you're building an app, I'm sure you will have an easier time with everything being ESM. I think the problems are more apparent when providing libraries.

ESM in Node is not so straightforward. Many supporting libraries don't support it well yet, see Jest for example.

Here is a good article that touches on some of the problems with ESM in Node: https://redfin.engineering/node-modules-at-war-why-commonjs-and-es-modules-cant-get-along-9617135eeca1

There are also problems with ESM on the browser. Using pure ESM with a huge dependency chain of imports is just not practical for performance reasons.

In the end, UMD is just a way to bundle three formats (CJS, IIFE and AMD) in one and makes it easier to support users. And 2 of those formats are already considered useful enough to be included in esbuild.

Some benefits of UMD for library creators:

There are many other arguments to be made here... but hopefully this is enough for now consider that UMD might be a desirable feature in a bundler/transpiler in order for esbuild to be usable by library creators.

evanw commented 3 years ago

FYI the new banner and footer options from #482 are now implemented and have been released. You should be able to implement this feature using those options.

jyboudreau commented 3 years ago

@evanw That's great! It definitively helps to alleviate the issue for now. For my use-case it's sufficient.

I think that there is still some merit to include a UMD mode, even in limited fashion like #513 in order to reduce friction for people migrating from webpack/rollup.

jacobp100 commented 3 years ago

UMD still has place for webworkers, where ES modules aren't supported in any non-Chrome browser. You gotta use importScripts, and UMD will generate globals that you can use.

danny007in commented 3 years ago

UMD still has place for webworkers, where ES modules aren't supported in any non-Chrome browser. You gotta use importScripts, and UMD will generate globals that you can use.

Ur right Example https://github.com/twbs/bootstrap/blob/e50c11b8c6434b6d68ea5897771e4d35fe12f5c3/build/rollup.config.js#L39

splashsky commented 3 years ago

Are there any updates or ETAs on when this feature will be implemented? It's the one thing I need in order to begin using esbuild in my particular project.

jacobp100 commented 3 years ago

@splashsky You could probably fake it for now - https://github.com/jacobp100/technicalc-core/blob/master/packages/technicalc-prebuilt/build.js#L88-L109

I think this looks to be pretty low priority

BePo65 commented 3 years ago

That's great! It definitively helps to alleviate the issue for now. For my use-case it's sufficient.

@jyboudreau perhaps you can help me with this workaround: what did you use as esbuld paramater format, when adding the umd wrapper by using 'banner' and 'footer'?

niemyjski commented 3 years ago

I too would like this, we have some older projects (angular 1.x) where we'd want to target with a umd bundle so they could consume our modern library.

horvski commented 2 years ago

It seems as though this is a fairly popular request, and one that is already accomplished but still not merged in #513.

Is there anything that is standing in the way of proceeding with this merge? If so, is there anything I can do to assist?

'UMD' format still seems to be fairly common industry practice, and even used in a cryptographically audited library.

The following would be very useful for many of the packages that I use:

1.) Convert from 'esm' format into 'umd' format. 2.) Convert 'umd' into 'esm' format. 3.) If a format is already in 'umd' format, minify the format, while keeping 'umd' format.

Update 08/23/2022: The core problem is that 'global' and 'factory' are being parsed as variables, and not keywords.

For minification, if these keywords are treated as keywords, then the problem is fixed.

Inqnuam commented 1 year ago

Hello, I made a plugin to wrap esbuild cjs into umd format esbuild-plugin-umd-wrapper

jchris commented 1 year ago

I'm just gonna throw my perspective in here as a regular library author who's about to try rollup instead of esbuild, because of things like this. I'm happy to let my downstream users tree shake out polyfill they don't need, but I want the default use to not have to think.

nl-brett-stime commented 7 months ago

FWIW, some of us are less concerned with emitting UMD than we are with making sure that existing UMD libraries are able to be bundled/imported/integrated without issue. I was unable to switch to esbuild because it was choking on the wu library at runtime. That lib comes from the central/public NPM repo as UMD and it was unable to resolve [regeneratorRuntime on] the global scope from within an IIFE bundle generated by esbuild .

My findings seemed to underscore the update/edit at the end of this comment: https://github.com/evanw/esbuild/issues/507#issuecomment-1221091273

restjohn commented 6 months ago

I'd love to see this implemented. My use case is a dynamic microfrontend host Angular app with Angular library extensions that are maintained outside of the host repository by other customers, and thus we cannot include the extensions in our host build process. The host app shares core dependencies, e.g. most of Angular's modules which are many, with the extensions so extensions do not bundle and duplicate those Angular modules.

Back on Angular 9, we used SystemJS to dynamically load the MFE Angular libraries as UMD bundles, which Angular's library builder, ng-packagr generated. However, ng-packagr now only supports FESM output out of the box, so I need a new solution to generate UMD or AMD modules for Angular libraries.

I had been exploring Webpack's module federation, along with an Angular plugin that integrates Webpack MF into the build. That seemed promising, but now of course Angular is moving away from Webpack to esbuild, so I'd rather move in the same direction and take advantage of the modern tools already in the build chain. That would mean esbuild's UMD output would need to support unbundled, external dependency imports in output.

For the moment I believe I'll have to provide a custom Angular builder that uses rollup like ng-packagr to build the Angular library extensions, just with the output format set to UMD rather than FESM, but I'm hoping that will be a stopgap.

dy commented 2 months ago

@evanw can we please consider this PR? UMD for esbuild is such a great pain and time killer atm. There's no simple clean ways to do that.