-
Notifications
You must be signed in to change notification settings - Fork 11.9k
Improve bundling page #12980
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
Improve bundling page #12980
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,56 +7,149 @@ meta: | |||||||||
|
|
||||||||||
| # Bundling | ||||||||||
|
|
||||||||||
| By default, Wrangler bundles your Worker code using [`esbuild`](https://esbuild.github.io/). This means that Wrangler has built-in support for importing modules from [npm](https://www.npmjs.com/) defined in your `package.json`. To review the exact code that Wrangler will upload to Cloudflare, run `npx wrangler deploy --dry-run --outdir dist`, which will show your Worker code after Wrangler's bundling. | ||||||||||
| By default, Wrangler bundles your Worker code using [`esbuild`](https://esbuild.github.io/) before uploading your Worker to Cloudflare. To review the exact code that Wrangler will upload to Cloudflare, run `npx wrangler deploy --dry-run --outdir dist`, which will show your Worker code after Wrangler’s bundling—this command will not deploy your Worker. By default, Wrangler will look at your `wrangler.toml` file and read the `main` field. This will be passed to ESBuild as the [entrypoint](https://esbuild.github.io/api/#entry-points) to your build. You can override this by passing a positional `script` argument to `wrangler dev` or `wrangler deploy`, which overrides the `main` value in your `wrangler.toml` file. | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| {{<Aside type="note">}} | ||||||||||
| We recommend using Wrangler's inbuilt bundling, but we understand there are cases where you will need more flexibility. We have built an escape hatch in the form of [Custom Builds](/workers/wrangler/custom-builds/), which lets you run your own build before Wrangler's built-in one. | ||||||||||
| {{</Aside>}} | ||||||||||
| Content on this page is relevant to all Wrangler users. If you'r authoring a library that you'd like to work with Wrangler, make sure to review [Notes for Library Authors](/workers/wrangler/bundling/#notes-for-library-authors). | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| ## Files which will not be bundled | ||||||||||
| ## Bundling TypeScript & JavaScript modules | ||||||||||
|
|
||||||||||
| Bundling your Worker code takes multiple modules and bundles them into one. Sometimes, you might have modules that should not be inlined directly into the bundle. For example, instead of bundling a Wasm file into your JavaScript Worker, you would want to upload the Wasm file as a separate module that can be imported at runtime. Wrangler supports this for the following file types: | ||||||||||
| When authoring a Worker with Wrangler, you're able to use multiple source modules (i.e. multiple TypeScript files in your source directory). You're also able to import from `npm` packages which you've installed. When Wrangler runs ESBuild, ESBuild will bundle all of these JavaScript and TypeScript modules into a single JavaScript file, which is what will be uploaded to the Cloudflare network as your Worker. You can see the exact bundled JavaScript file that's run by the Workers runtime by opening the Quick Editor on your uploaded Worker, which will display the bundled file. | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| > Why does Wrangler bundle? The Workers runtime supports uploading multiple JS modules, but Wrangler bundles your code by default to support TypeScript and to reduce the size of your Worker by leveraging e.g. tree-shaking | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| ## Support for non-JavaScript module types | ||||||||||
|
|
||||||||||
| Often you'll have modules in your app that aren't JavaScript or TypeScript. WASM is most common here, but this could also include text or HTML files that you want to import from your Worker code. Since these modules aren't JS or TS, these can't be bundled into your Worker's JS with ESBuild, and so Wrangler has specific logic for handling them. Wrangler supports this for the following file types: | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| - `.txt` | ||||||||||
| - `.html` | ||||||||||
| - `.bin` | ||||||||||
| - `.wasm` and `.wasm?module` | ||||||||||
|
|
||||||||||
| Refer to [Bundling configuration](/workers/wrangler/configuration/#bundling) to customize these file types. | ||||||||||
| Refer to [Bundling configuration](https://developers.cloudflare.com/workers/wrangler/configuration/#bundling) to customize these file types. | ||||||||||
kodster28 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
|
||||||||||
| For example, with the following import, the variable `data` will be a string containing the contents of `example.html`: | ||||||||||
| For example, if your code contains the following import of an HTML file: | ||||||||||
|
|
||||||||||
| ```js | ||||||||||
| ``` | ||||||||||
| import data from "./example.html"; // Where `example.html` is a file in your local directory | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| This is also the basis of Wasm support with Wrangler. To use a Wasm module in a Worker developed with Wrangler, add the following to your Worker: | ||||||||||
| Wrangler will upload _two_ modules to the Cloudflare platform; one containing your bundled JS code (with the above import statement intact), and one containing the file `example.html`. When your Worker is run by the Workers runtime, the runtime will recognise that the import matches a module that was uploaded alongside your worker, and will load the contents and make it available to your JS code. In this case (since the file is a text file), importing the module will result in a `string` (i.e. the `data` variable will contain the string contents of `example.com`) but that's not always the case. For imports from a `*.bin` file, the Workers runtime will provide the value to your Worker as a `ArrayBuffer`, and for imports from a `.wasm` or `.wasm?module` file the Workers runtime will provide the value as an `ArrayBuffer` representing the binary WASM code. To turn a WASM import into callable code, you need to use the [`WebAssembly.instantiate()` API](https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/instantiate_static). For example: | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| ```js | ||||||||||
| ``` | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this have specific syntax highlighting? |
||||||||||
| import wasm from "./example.wasm"; // Where `example.wasm` is a file in your local directory | ||||||||||
| const instance = await WebAssembly.instantiate(wasm); // Instantiate Wasm modules in global scope, not within the fetch() handler | ||||||||||
|
|
||||||||||
| export default { | ||||||||||
| fetch(request) { | ||||||||||
| const result = instance.exports.exported_func(); | ||||||||||
| }, | ||||||||||
| export default { fetch(request) { const result = instance.exports.exported_func(); }, | ||||||||||
| }; | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| {{<Aside type="warning">}} | ||||||||||
| Cloudflare Workers does not support `WebAssembly.instantiateStreaming()`. | ||||||||||
| {{</Aside>}} | ||||||||||
|
|
||||||||||
| ## Conditional exports | ||||||||||
| ## Non-Bundled JavaScript modules | ||||||||||
|
|
||||||||||
| > Caution, this is an advanced use case of Wrangler. You probably don't need it! | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| In some cases, you'll also want to make sure Wrangler doesn't bundle some of your JavaScript modules, as well as just non-JavaScript modules. This will result in Wrangler uploading multiple _JavaScript_ modules to the Workers runtime (where imports between them will be resolved at runtime), rather than uploading a single bundled JavaScript module containing all your Worker's code. This is primarily to support dynamic imports and code-splitting, which can improve your Worker's startup time by reducing the size of the entrypoint JavaScript module. There are two options—disabling bundling entirely, or disabling it partially: | ||||||||||
kodster28 marked this conversation as resolved.
Show resolved
Hide resolved
kodster28 marked this conversation as resolved.
Show resolved
Hide resolved
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| ### Completely disable bundling | ||||||||||
|
|
||||||||||
| When the `--no-bundle` flag is set, Wrangler has support for uploading additional modules alongside the entrypoint. This will allow modules to be imported at runtime on Cloudflare's Edge. This respects Wrangler's [module rules](https://developers.cloudflare.com/workers/wrangler/configuration/#bundling) configuration, which means that only imports of non-JS modules will trigger an upload by default. For instance, the following code will now work with `--no-bundle` (assuming the `example.wasm` file exists at the correct path): | ||||||||||
kodster28 marked this conversation as resolved.
Show resolved
Hide resolved
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| Wrangler respects the [conditional `exports` field](https://nodejs.org/api/packages.html#conditional-exports) in `package.json`. This allows developers to implement isomorphic libraries that have different implementations depending on the JavaScript runtime they are running in. When bundling, Wrangler will try to load the [`workerd` key](https://runtime-keys.proposal.wintercg.org/#workerd). Refer to the Wrangler repository for [an example isomorphic package](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/isomorphic-random-example). | ||||||||||
| ```js | ||||||||||
| // index.js | ||||||||||
| import wasm from './example.wasm' | ||||||||||
|
|
||||||||||
| export default { | ||||||||||
| async fetch() { | ||||||||||
| await WebAssembly.instantiate(wasm, ...) | ||||||||||
| ... | ||||||||||
| } | ||||||||||
| } | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| For JS modules, it's necessary to specify an additional [module rule](https://developers.cloudflare.com/workers/wrangler/configuration/#bundling) (or rules) in your `wrangler.toml` to configure your modules as ES modules or Common JS modules. For instance, to upload additional JavaScript files as ES modules, add the following module rule to your `wrangler.toml`, which tells Wrangler that all `**/*.js` files are ES modules. | ||||||||||
kodster28 marked this conversation as resolved.
Show resolved
Hide resolved
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| ```toml | ||||||||||
| rules = [ | ||||||||||
| { type = "ESModule", globs = ["**/*.js"]}, | ||||||||||
| ] | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| If you have Common JS modules, you'd configure Wrangler with a CommonJS rule (the following rule tells Wrangler that all `.cjs` files are Common JS modules): | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| ```toml | ||||||||||
| rules = [ | ||||||||||
| { type = "CommonJS", globs = ["**/*.cjs"]}, | ||||||||||
| ] | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| In most projects, adding a single rule will be sufficient. However, for advanced usecases where you're mixing ES modules and Common JS modules, you'll need to use multiple rule definitions. For instance, the following set of rules will match all `.mjs` files as ES modules, all `.cjs` files as Common JS modules, and the `nested/say-hello.js` file as Common JS. | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| ## Disable bundling | ||||||||||
| ```toml | ||||||||||
| rules = [ | ||||||||||
| { type = "CommonJS", globs = ["nested/say-hello.js", "**/*.cjs"]}, | ||||||||||
| { type = "ESModule", globs = ["**/*.mjs"]} | ||||||||||
| ] | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| {{<Aside type="warning">}} | ||||||||||
| Disabling bundling is not recommended in most scenarios. Use this option only when deploying code pre-processed by other tooling. | ||||||||||
| {{</Aside>}} | ||||||||||
| If multiple rules overlap, Wrangler will log a warning about the duplicate rules, and will discard additional rules that matches a module. For example, the following rule configuration classifies `dep.js` as both a Common JS module and an ES module: | ||||||||||
|
|
||||||||||
| If your build tooling already produces build artifacts suitable for direct deployment to Cloudflare, you can opt out of bundling by using the `--no-bundle` command line flag: `npx wrangler deploy --no-bundle`. If you opt out of bundling, Wrangler will not process your code and some features introduced by Wrangler bundling (for example minification, and polyfills injection) will not be available. | ||||||||||
| ```toml | ||||||||||
| rules = [ | ||||||||||
| { type = "CommonJS", globs = ["dep.js"]}, | ||||||||||
| { type = "ESModule", globs = ["dep.js"]} | ||||||||||
| ] | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| Wrangler will treat `dep.js` as a Common JS module, since that was the first rule that matched, and will log the following warning: | ||||||||||
|
|
||||||||||
| ``` | ||||||||||
| ▲ [WARNING] Ignoring duplicate module: dep.js (esm) | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| This also adds a new configuration option to `wrangler.toml`: `base_dir`. Defaulting to the directory of your Worker's main entrypoint, this tells Wrangler where your additional modules are located, and determines the module paths against which your module rule globs are matched. | ||||||||||
|
|
||||||||||
| For instance, given the following directory structure: | ||||||||||
|
|
||||||||||
| ``` | ||||||||||
| - wrangler.toml | ||||||||||
| - src/ | ||||||||||
| - index.html | ||||||||||
| - vendor/ | ||||||||||
| - dependency.js | ||||||||||
| - js/ | ||||||||||
| - index.js | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| If your `wrangler.toml` had `main = "src/js/index.js"`, you would need to set `base_dir = "src"` in order to be able to import `src/vendor/dependency.js` and `src/index.html` from `src/js/index.js`. | ||||||||||
|
|
||||||||||
| ### Partially disabling bundling | ||||||||||
|
|
||||||||||
| Setting `find_additional_modules` to `true` in your configuration file will instruct Wrangler to look for files in your `base_dir` that match your configured `rules`, and deploy them as unbundled, external modules with your Worker. `base_dir` defaults to the directory containing your `main` entrypoint. | ||||||||||
|
|
||||||||||
| Files are bundled into a single Worker entry-point file unless `find_additional_modules` is `true`, and the file matches one of the configured `rules`. | ||||||||||
|
|
||||||||||
| ## Notes for Library Authors | ||||||||||
|
|
||||||||||
| ### Conditional exports | ||||||||||
|
|
||||||||||
| Wrangler respects the [conditional `exports` field](https://nodejs.org/api/packages.html#conditional-exports) | ||||||||||
|
|
||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix the spacing here :) |
||||||||||
| in `package.json`. This allows developers to implement isomorphic libraries that have different implementations depending on the JavaScript runtime they are running in. When bundling, Wrangler will try to load the [`workerd` key](https://runtime-keys.proposal.wintercg.org/#workerd). Refer to the Wrangler repository for [an example isomorphic package](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/isomorphic-random-example). If you're building a library that supports the Workers runtime, we recommend adding the `workerd` `exports` field to indicate the correct configuration of your package for Wrangler to load. | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| ### Feature detection | ||||||||||
|
|
||||||||||
| If you're writing a library that needs to know whether or not it's running in the Workers runtime, make sure you use the user agent for feature detection (i.e. `navigator.userAgent === "Cloudflare-Workers`). For example, you could write this `randomBytes()` function that will work in both Node.js and Workers: | ||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
|
|
||||||||||
| ``` | ||||||||||
| export function randomBytes(length: number) { | ||||||||||
| if (navigator.userAgent !== "Cloudflare-Workers") { | ||||||||||
| return new Uint8Array(require("node:crypto").randomBytes(length)); | ||||||||||
| } else { | ||||||||||
| return crypto.getRandomValues(new Uint8Array(length)); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| Use [Custom Builds](/workers/wrangler/custom-builds/) to customize what Wrangler will bundle and upload to the Cloudflare global network when you use [`wrangler dev`](/workers/wrangler/commands/#dev) and [`wrangler deploy`](/workers/wrangler/commands/#deploy). | ||||||||||
| Using `navigator.userAgent` for feature detection will allow Wrangler to tree-shake the dead code branches from your library, reducing the size of Workers which depend on it. | ||||||||||
Uh oh!
There was an error while loading. Please reload this page.