The Wayback Machine - https://web.archive.org/web/20210628154827/https://github.com/rollup/rollup/issues/3490
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

Support code-splitting for UMD builds #3490

Open
johannes-z opened this issue Apr 9, 2020 · 8 comments
Open

Support code-splitting for UMD builds #3490

johannes-z opened this issue Apr 9, 2020 · 8 comments

Comments

@johannes-z
Copy link
Contributor

@johannes-z johannes-z commented Apr 9, 2020

Feature Use Case

UMD builds should be able to code-split. Webpack can create code-splitting bundles even in UMD.

Webpack example:

Input

main.js

import('./other')

other.js

console.log('test')

Compiled

main.js

// webpack runtime
__webpack_require__.e(/* import() */ 1).then(__webpack_require__.t.bind(null, "aca4", 7));

other.js

((typeof self !== 'undefined' ? self : this)["webpackJsonpcore"] = (typeof self !== 'undefined' ? self : this)["webpackJsonpcore"] || []).push([[1],{

/***/ "aca4":
/***/ (function(module, exports) {

console.log('test');

/***/ })

}]);

Feature Proposal

rollup should support UMD and code-splitting. The only problem I see here is IIFE bundles. CommonJS and AMD are already supported.

I think webpack can code-split UMD bundles because of their runtime (please correct me if I'm wrong).

In order for rollup to support UMD code-splitting, the split IIFE files have to be loaded and executed somehow (maybe a runtime-helper?). Also I guess all chunks need a UMD factory also.

@seivan
Copy link

@seivan seivan commented Jun 3, 2020

The reason Webpack can do it, is because they have their own runtime for loading stuff.

Rollup supports systemjs but doesn't include its "loader", but you can do that yourself by adding SystemJS and necessary polyfills.

import 'unfetch'
import s from "systemjs/dist/s"

Build that as IIFE and add its output as a script tag.

I am actually happy that Rollup doesn't have it's own runtime for that, because it has an overhead not to mention more than 88% of world wide browsers actually support native ES Modules, even more if you disregard dynamic import.

Something else to keep in mind Webpack overhead increases per modules, it could have been changed by now but last I checked it was not negligible.

You're better off with SystemJS.

@johannes-z
Copy link
Contributor Author

@johannes-z johannes-z commented Jun 3, 2020

I think the runtime to support this could be rather small. code-splitting UMD would just require another UMD wrapper for the split file. I've written a small plugin for my use-case, that does exactly this.

Sometimes you cannot use SystemJS, as you are locked in by the ecosystem to use something else. Also the "modern browser" argument doesn't hold for production, as in a business environment IE and the old Edge still (sadly) dominate.

If rollup would support code-splitting in these scenarios, you won't really need webpack anymore...

@seivan
Copy link

@seivan seivan commented Jun 3, 2020

I think the runtime to support this could be rather small.

You need to load them somehow, which means its going to need polyfills for e.g Promise and/or Fetch

Also the "modern browser" argument doesn't hold for production, as in a business environment IE and the old Edge still (sadly) dominate.

Hence the suggestion of SystemJS.

Sometimes you cannot use SystemJS, as you are locked in by the ecosystem to use something else.

Right that's a good point, but the way I understand it, and I could be wrong, the only way UMD actually works out of the box is if it defaults to IIFE, otherwise you're going to need a runtime loader. You're still better of with SystemJS for that as Rollup can output its native format, but then again it's a 2kb polyfill that you could skip if IIFE supported code splits.

Another alternative is AMD but I suspect it has a larger footprint and requires a bigger loader.

I tackled this issue earlier, so I thought I'd offer an alternative.

@johannes-z
Copy link
Contributor Author

@johannes-z johannes-z commented Jun 17, 2020

Rollup could at least support code-splitting on a named module basis. I wrote a plugin that replaces a dynamic import call with an UMD-factory for named modules:

/**
 * Replaces named, dynamic imports with an UMD factory. Absolute and relative imports are ignored.
 *
 * Requires an environment that supports `Promise`.
 */
export default function pluginDynamicImports (options = {}) {
  return {
    name: 'dynamic-imports',
    transform (code, filename) {
      // TODO: warn/error if globalName is not specified in UMD environment.
      // TODO: global is hardcoded to window, which is wrong.
      const transformedCode = code.replace(/import\(['"`](?![./])(.*?)['"`]\)/gi, (match, request) => {
        const globalName = options.globals[request]
        return `new Promise(function (resolve, reject) {
          (function (global) {
            typeof exports === 'object' && typeof module !== 'undefined' ? resolve(require("${request}")) :
            typeof define === 'function' && define.amd ? require(["${request}"], resolve, reject) :
            (global = global || self, resolve(global["${globalName}"]));
          }(window));
        })`
      })

      return transformedCode
    },
  }
}

It assumes that the imported module has an alias set in the requirejs.config, and that it exists on the window for an IIFE import.

This works for my use cases, but surely can be improved. It allows me to use code like this without any issues:

async function doStuff () {
  if (...) return
  const get = await import('lodash.get')
  get(...)
  // do more stuff
}

doStuff is for example a very rarely used function, and splitting lodash.get would improve initial load by a lot.

@seivan
Copy link

@seivan seivan commented Jun 17, 2020

@johannes-z Out of curiosity, do you know if there are any benefits with UMD over SystemJS?

The only thing I can tell is that SystemJS is 3x smaller than RequireJS and builtin into Rollup, I can't see any benefit here other the fact that you seem forced to use UMD? I'm actually wondering if we should also switch.

It allows me to use code like this without any issues:...

Were you under the impression that lazy loading wouldn't work unless you used UMD?

@johannes-z
Copy link
Contributor Author

@johannes-z johannes-z commented Jun 17, 2020

I'm not 100% sure, but in production I only had problems using SystemJS. I've worked with RequireJS for years and never had an issue, or an issue that I couldn't solve. Interestingly, Microsoft used SystemJS for their new SharePoint Modern UI, but after roughly 2 years they changed to RequireJS. RequireJS and AMD seems to be much more flexible. If you are in control of the app and environment you're using/programming, I guess there is not much difference feature wise...

Were you under the impression that lazy loading wouldn't work unless you used UMD?

Yes I'm aware of that: I'd actually only use AMD, but I don't want to lose the option to just import my scripts using a plain script-tag...

So my point is: Support lazy loading where it is supported (AMD), but not where it doesn't have to be (CommonJS, IIFE). Rollup breaks the build though, which isn't a necessity as this can be circumvented with the plugin I just posted. Rollup could do the same, but warn the user instead of erroring.

@itssumitrai
Copy link

@itssumitrai itssumitrai commented Aug 11, 2020

There is a similar issue #2072 which was closed a while back. I would also like this to be supported by Rollup out of the box, maybe this could be some config of some sort (like turn on rollup loader if needed).

@ChristianMurphy
Copy link

@ChristianMurphy ChristianMurphy commented Sep 12, 2020

A workaround that may work for some babel+rollup users in the meantime, a babel plugin that can translate some dynamic imports to static imports to allow rollup to generate a UMD build.
https://www.npmjs.com/package/babel-plugin-transform-dynamic-imports-to-static-imports

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