oven-sh / bun

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
https://bun.sh
Other
73.13k stars 2.67k forks source link

Include `import.meta.resolve(…)` arguments in the source graph for `bun build` #2906

Open lgarron opened 1 year ago

lgarron commented 1 year ago

I was super excited to see https://bun.sh/blog/bun-v0.6.0 this morning — I was able to use it on the first try with a codebase containing over 300 TypeScript source files! 😻

What is the problem this feature would solve?

I'd like to advocate for bun build to become a go-to tool for authoring interoperable code that works in browsers as well as runtimes, by preserving import.meta.resolve(…) resolution. This is necessary for writing code that can instantiate web workers and load WASM across environments:

// Web worker
const workerPath = import.meta.resolve("./client/worker.js");
const worker = new Worker(workerPath, { type: "module" });
worker.postMessage("hi");

// WASM
const wasmPath = import.meta.resolve("./code.wasm");
const wasmModule = WebAssembly.compileStreaming(fetch(wasmPath), /* imports */);
console.log(wasmModule.exports.foo());

Although bun doesn't support web workers or WASM yet, import.meta.resolve(…) is implemented already[^1]. I would love to see bun build include import.meta.resolve(…) arguments in the source graph, particularly for relative imports of files for the two web-native languages:

I think it would be great to support this for more resources as well, but these two are table-stakes for writing performant JavaScript apps.

[^1]: It differs from all other environments (https://github.com/parcel-bundler/parcel/issues/8924) but that is not a concern for this issue.

What is the feature you are proposing to solve the problem?

I would love to get bun to implement enough features to pass the tests I have at the following:

git clone https://github.com/lgarron/loadeverything.net && cd loadeverything.net
make test-bun-build

I could use help identifying what is needed, and if there's anything I could do to contribute.

What alternatives have you considered?

No response

Jarred-Sumner commented 1 year ago

yeah this is very reasonable

We should specifically detect new Worker and create bundles for it.

lgarron commented 1 year ago

yeah this is very reasonable

Glad to hear it! 😻

We should specifically detect new Worker and create bundles for it.

I think this is good in general, but I'd like to ask for an important consideration when it comes to detecting new Worker(…): the following code does not work for libraries hosted on a CDN:

// Source code at https://cdn.cubing.net/foo.js

// fails in browsers if the source file origin does not match the top-level page origin.
new Worker(import.meta.resolve("./worker.js"), { type: "module" });

Instead (or in addition), libraries have to implement trampoline fallback:

// Source code at https://cdn.cubing.net/foo.js

const workerURL = import.meta.resolve("./worker.js");
const blob = new Blob([`import ${JSON.stringify(workerURL)}`], { type: "text/javascript" });
new Worker(URL.createObjectURL(blob), { type: "module" });

In a few years we may be able to avoid this using either blank workers or module expressions, but the solution above is the only interoperable fallback that works as of today.[^1]

If possible, I'd like to ask that the semantics of the trampoline version of the code are also preserved by bun build. 🙏

[^1]: Technically node requires additional workarounds (https://github.com/nodejs/node/issues/43583) but for the purposes of this issue the only difference is that Worker would be an imported constructor rather than globalThis.Worker.

lgarron commented 1 year ago

I've started trying to tackle this at https://github.com/oven-sh/bun/compare/main...lgarron:bun:lgarron/import.meta.resolve

There's a lot more code to update, but I might appreciate some pointers if I'm not on the right track.