esbuild
esbuild copied to clipboard
Support SystemJS as a format
Any plans for SystemJS as an output format?
I am currently using this for browser support. It seems to be supported by other bundlers, such as Rollup and Webpack. It would be nice to see it in ESBuild.
I don't currently have any plans to support SystemJS. The format seems pretty complicated compared to other browser-based formats, especially the way live bindings need to be implemented. All of esbuild's live exports currently just use getters, which is much simpler for me to implement and maintain and which often results in smaller code size. It's currently already possible to target the browser with esbuild using the iife
and esm
output formats. Is there a reason those formats don't work for you?
The use-case is when there is a library I want to import
without bundling.
For me in particular, I am using this to load data that the server is generating dynamically. For example, I can do:
import data from "/api/endpoint";
doSomethingWith(data);
With SystemJS, it emulates all aspects of ES6, so all I have to do is wrap my /api/endpoint
as a SystemJS module and the example above acts just like importing a blob of JSON. Dynamically! Before my module even runs! Very useful for me.
I can imagine other reasons someone might want to import an external module that gets resolved at run-time.
I don't know how iife
would handle importing an external module like this. Would it work the same way? I've solved it in other bundlers using SystemJS.
My understanding is that you could import Systemjs globally from your html and use const data = System.import('/api/endpoint');
. So maybe there’s no need to have the bundler do it ?
The difference is that System.import
returns a promise. Using a static import
does not return a promise. It returns the data right now. The fetching is done ahead-of-time by the module loader, at the same time that the JavaScript is being fetched.
I could also get my data using const data = fetch('/api/endpoint')
with the same penalty. It returns a promise.
Compare something like this:
import data from "/api/endpoint";
doSomethingWith(data);
doSomethingElseWith(data);
to something like this:
fetch('/api/endpoint').then(res => {
if (!res.ok) {
return res.text().then(txt => {
throw new Error(txt);
});
}
return res.json();
}).then(data => {
doSomethingWith(data);
doSomethingElseWith(data);
});
It kind of gets more convoluted if there are multiple data endpoints involved.
~~Additionally, the fetch
approach suffers from the "waterfall" effect, where the browser doesn't start fetching my data until after the JavaScript has been fetched and parsed and executed.~~
EDIT: Sorry, this is incorrect. The JavaScript still needs to be fetched and parsed before the import can be resolved.
Is there a reason those formats don't work for you?
Our library uses SystemJS as esm fallback. And our library have multi entry point but esbuild only supports splitting for esm.
SystemJS is very useful in MFE (Microfrontend) applications.
System JS is often used for large micro front end frameworks, such as SingleSPA. Is there a way to support this standard in the future?
SystemJS it's kinda ES Modules polyfill, and it will be great to have it in esbuild!
I remember reading a comment that this would not be a supportive feature by this tool, however, many people are using system JS as part of their micro front end solutions (SingleSPA)
I think maybe using rollup to transform the output translated by esbuild to systemjs format is a good way.
I think maybe using rollup to transform the output translated by esbuild to systemjs format is a good way.
Indeed that is simple. ESM in, Systemjs out:
import { rollup } from 'rollup';
import virtual from '@rollup/plugin-virtual';
export const toSystemjs = async (esmCode: string): Promise<string> => {
const inputOptions = {
external: () => true,
input: 'esmCode',
plugins: [
virtual({ esmCode }),
],
treeshake: false,
};
const outputOptions = {
file: 'out.mjs', // not really used
format: 'systemjs',
sourcemap: false,
};
const bundle = await rollup(inputOptions);
const { output } = await bundle.generate(outputOptions);
await bundle.close();
return output[0].code;
}
We are also using SingleSPA and are currently using it with webpack which is very slow. Today I tried to use single spa with esbuild and native import maps but the current support of native import maps don't allow for loading the import maps from an external json file. So esbuild outputting systemjs format would solve for us a lot of developer experience pain points.
I think maybe using rollup to transform the output translated by esbuild to systemjs format is a good way.
Indeed that is simple. ESM in, Systemjs out:
import { rollup } from 'rollup'; import virtual from '@rollup/plugin-virtual'; export const toSystemjs = async (esmCode: string): Promise<string> => { const inputOptions = { external: () => true, input: 'esmCode', plugins: [ virtual({ esmCode }), ], treeshake: false, }; const outputOptions = { file: 'out.mjs', // not really used format: 'systemjs', sourcemap: false, }; const bundle = await rollup(inputOptions); const { output } = await bundle.generate(outputOptions); await bundle.close(); return output[0].code; }
Hi, I've stumbled upon this, how would one execute that for local build outputs?