Skip to main content
An App can use npm packages and a build step. At deploy, Judt installs the dependencies from the package-lock.json file, runs the build script if there is one, and then celld, the Judt runtime, bundles the entry module with esbuild. The dev server prepares the App the same way. Use this page to add a dependency, add TypeScript or JSX, or deploy the output of a frontend bundler.

Add an npm package

In the sandbox, add the package with npm:
Then commit the package.json and package-lock.json files. Deploy installs from the lockfile. We recommend packages that run on web APIs. celld supports only a subset of the Node.js built-in modules, so a package that depends on others fails when it calls them. For more information, see Runtime reference.

Dependency rules

Judt installs dependencies only from the public npm registry, and install scripts never run. A package that needs a postinstall step or a native binary doesn’t work. Deploy checks the dependencies, devDependencies, and optionalDependencies fields of the package.json file, and refuses a source with the unsupported_dependencies code if it has any of the following:
  • A Git, file, URL, or GitHub (user/repo) dependency. A version range, a tag, and an npm: alias are accepted.
  • A workspaces field.
  • A linked package, or a package that the lockfile resolves from a host other than the public npm registry.
  • An .npmrc file.
If the package.json file declares any dependencies, then the App must include a package-lock.json file with lockfileVersion 2 or 3. Otherwise, deploy refuses the source with the lockfile_missing code. To create or update the lockfile, run npm install again.

How deploy bundles your code

At deploy, esbuild bundles the file that main names, its relative imports, and the packages that it imports from node_modules. Write JavaScript ES modules, and import your own modules with relative paths, such as ./src/db.js.

Add a build step

For TypeScript, JSX, or a frontend bundler, add a build script to the package.json file. The script writes JavaScript to the dist/ directory. Then set main to the entry module that the build writes, such as dist/index.js. The following example compiles TypeScript with tsc. The tsconfig.json file, which isn’t shown, compiles src/index.ts to dist/index.js.

How deploy runs the build

At deploy, Judt runs npm ci, and then npm run build. The build runs under the following conditions:
  • The build gets no Secrets.
  • The build has no network access except to the npm registry.
  • The install and the build together must finish within 5 minutes.
  • pre and post scripts, such as prebuild, don’t run.
  • After the build, the entry module that main names must exist.
  • The build must not leave symbolic links, hard links, or special files. Judt removes the node_modules/.bin directories before it checks.
If the build fails, then deploy stops with one of the following codes:

The build on the dev server

When the dev server starts, it runs the build script one time. If the package.json file has a dev script, then the dev server runs it beside the dev server for as long as it runs. Use the dev script for a watcher that rebuilds on change, such as tsc --watch or esbuild --watch. For more information, see Test on the dev server.

Keep build output out of the source

Don’t commit the dist/ or node_modules/ directory. The build creates them at deploy. Deploy refuses a source that contains either of them, with the reserved_path code. For more information, see The App contract.

Deploy bundled output

If your build already bundles the Worker, as Vite does, then set no_bundle to true:
wrangler.json
With no_bundle, celld uses the entry module byte for byte. It doesn’t bundle the module, and deploy ships no other JavaScript file. The build must write the Worker as one module that contains all of its dependencies. For a Vite App, configure the server build to do the following:
  • Resolve the workerd and worker conditions.
  • Bundle all packages, with noExternal: true.
  • Keep cloudflare: and node: imports external.
  • Write one file, with inlineDynamicImports: true.
The following example shows this configuration for Vite 8:
vite.config.ts
Don’t use @cloudflare/vite-plugin or vite dev. They run workerd, and the output of the plugin doesn’t deploy on Judt.
If the build also writes the frontend files, then set assets.directory to the directory that holds them. For more information, see Static assets.