cloudflare / workers-sdk

⛅️ Home to Wrangler, the CLI for Cloudflare Workers®
https://developers.cloudflare.com/workers/
Apache License 2.0
2.64k stars 691 forks source link

🚀 Feature Request: Make bundling and rules 2 different concepts #6944

Open vicb opened 1 week ago

vicb commented 1 week ago

1) We have rules to upload different files:

rules = [
  { type = "CommonJS", globs = ["**/*.js"], fallthrough = true }
]

2) By default wrangler bundles all the files together (using esbuild) that is rules from 1 above are ignore and all the imports are inlined.

As of today 1 and 2 are grouped around a single "Bundling" paradigm.

That is:

I believe 1 and 2 should be different concepts and not mutually exclusive.

For example you could have you framework composed of multiple files and then a directory containing the different pages of your application.

- app/
  - index.js
  - util.js
  - render.js
- pages/
  - homepage.js  
  - about.js

In that case it would be nice to bundle the app into a single file (2) and being able to require the page matching the url (1).

Updating ESBuild could solve some of the use case with wildcard imports.

workerd recently introduced createRequire. You can now write:

import { createRequire } from 'module';
const require = createRequire('/');
const msg = require('./hello.cjs').world();
console.log(`hello ${msg}`);

The code works fine with --no-bundle - as long as the hello.cjs has a matching rule in wrangler.toml. However the code will fail without --no-bundle because it can not resolve hello.cjs. If require is passed a variable, the code will fail to build because of the dynamic require.

That's why I believe we should be able to bundle the app/framework but still have module that you can require.

If both 1 and 2 can be used together, files matched by a rules must be marked as external so that esbuild does not try to inline them.

/cc @IgorMinar

threepointone commented 1 week ago

Can you explain the usecase you're going for?

vicb commented 1 week ago

@threepointone

The code snippet I added in the description above would not work consistently wether bundling is turned on or not:

import { createRequire } from 'module';
const require = createRequire('/');
const msg = require('./hello.cjs').world();
console.log(`hello ${msg}`);

Also if at some point we lazy parse module on worked, it could save CPU time

threepointone commented 1 week ago

right, got it. can you try by adding find_additional_modules = true in your wrangler.toml?

vicb commented 1 week ago

It looks the the (undocumented feature) I was looking for, thanks! I'll give it a try.

vicb commented 1 week ago

find_additional_modules = true seems to work - I am not exactly sure how.

Q1) Is my understanding right? Q2) what does "additional" refers to in "find_additional_modules"? Does it mean in addition to the implicit rules? Q3) Would it make sense that wrangler dev and wrangler dev --no-bundle have the same behavior (that is respect the rules) by default - I think it would Q4) Would it make sense for rules to have a "Typescript" type ? (The rationale here is that we would compile the main, why not do the same for additional modules).

I'm asking all those question to understand and try to update the docs. Thanks

edit: because my interepretation of "bundling" was not the right one. To me it means bundling the main entry point.

/cc @petebacondarwin

petebacondarwin commented 5 days ago

find_additional_modules will cause Wrangler to traverse the directories below the base_dir (which defaults to the directory containing the main entry-point) looking for files ("modules") to include in the Worker upload in "addition" to the entry-point module.

It only includes modules (files) that match rules as specified in your wrangler.toml (plus the default rules).

Rules are evaluated per "type" and if you set fallthrough to be false then it will not try subsequent rules for that type, which gives you some control over turning off the default rules (by providing impossible globs). (I think)

When no_bundle is true, then find_additional_modules is true by default. So I am surprised that you found there was different behaviour.

We don't do any processing at all of the "additional" modules; which is a gap in the tooling, I agree. Not sure if we need another type though because it would also be true that you might want to process JavaScript.


Finally, the Vite integration might also provide an alternative approach to all of this. Watch this space...

petebacondarwin commented 5 days ago

Also, bundling is an over used term in this field. We often need distinguish between the single file that esbuild generates (the "esbuild bundle") and the collection of modules that are uploaded to Miniflare/Cloudflare (the "deployment bundle").

irvinebroque commented 4 days ago

Super supportive of us clarifying in docs, agree this is confusing today and lot of opportunity to get super clear about current behavior. Would love us to do that first before adding new stuff in this area, so that we're building off of a base that there's a shared understanding of.