Skip to content

Workers & Routes & SPAs - assets not resolving #9885

Description

@astanciu

What versions & operating system are you using?

Node 22, wrangler4.22, viteplugin: 1.7.5

Please provide a link to a minimal reproduction

No response

Describe the Bug

I have a worker, using Vite plugin, and it is bound to route: *.domain.com/auth/*. Note the sub-path, that's the key issue here.

In vite, my base is set to : /auth/

wrangler asset config:

  "assets": {
    "not_found_handling": "single-page-application",
    "run_worker_first": ["/auth/api/*"],
    "html_handling": "drop-trailing-slash"
  },

My dist folder after build:

├── auth_svc
│   ├── assets
│   │   ├── bun-sqlite-dialect-BA34DVYO.js
│   │   ├── index-DS8CXNKO.js
│   │   └── server.edge-CMi7F8Zj.js
│   ├── index.js
│   └── wrangler.json
└── client
    ├── assets
    │   ├── index-C6_7OQDu.js
    │   └── etc....
    ├── images
    │   └── favicon.ico
    ├── index.html
    └── manifest.json

Cloudflare will correctly invoke my worker only when the route matches, ie, a url with /auth/*, however, the app is "mounted" at the root /, which creates a conflict that is tricky to solve.

With the config above, we get:
✅ curl test.domain.com/auth/login - OK - returns index.html page
✅ curl test.domain.com/auth/api - OK, worker gets invoked successfully
❓ the index.html file has the following:
- public urls: <link rel="icon" href="/auth/images/favicon.ico" />
- chunks: <script type="module" crossorigin src="/auth/assets/index-C6_7OQDu.js" />
❌ curl https://test.domain.com/auth/images/favicon.ico - returns index.html ( from public folder)
❌ curl https://test.domain.com/auth/assets/index-C6_7OQDu.js - returns index.html page (bundled assets)

The requests fail because the files are actually not under /auth, but under /. the dist/client folder is mounted to /.

We can proove this by

Attempt 1

.. adding this to the vite config:

  build: {
    assetsDir: 'auth/assets'
}

now, dist builds like this:

./dist
├── auth_svc
│   └── auth
│       └── assets
└── client
    ├── auth
    │   └── assets
    └── images

and these now work:
https://test.domain.com/auth/images/favicon.ico
https://test.domain.com/auth/assets/index-C6_7OQDu.js

but we have a different problem; the index.html page has bad URLs:

  • public URLs are OK: <link rel="icon" href="/auth/images/favicon.ico" />
  • chunks are not: <script type="module" crossorigin src="/auth/auth/assets/index-C6_7OQDu.js" />

Attempt 2

Another attempt to fix this with Vite is using an experimental config that gives you full control over the URLs generated:

  build: {
    assetsDir: 'auth/assets'
  },
  experimental: {
    renderBuiltUrl(filename, { hostId, hostType, type }) {
      if (type === 'public') {
        return `/auth/${filename}`;
      }
      return `/${filename}`;
    },
  },

This produces:

  • ✅ file system ( dist/client/auth )
  • ❌ public import URLS:
    <link rel="apple-touch-icon" sizes="180x180" href="/auth/images/apple-touch-icon.png">
  • ✅ chunk import URLS

We could fix this by editing the original index.html and hard coding the /auth prefix there, but not clean.

Attempt 3

Looking deeper into the plugin code, at how it builds the output paths:
https://github.com/cloudflare/workers-sdk/blob/main/packages/vite-plugin-cloudflare/src/utils.ts#L10-L20
We can see that maybe we can set the outDir for the environment:

  environments: {
    client: {
     build: {
        outDir: 'dist/client/auth/'
     }
    }
  },

This gives us:

  • ✅ file system ( dist/client/auth )
  • ✅ public imports (they start with /auth), ex: <link rel="icon" href="/auth/images/favicon.ico" />
  • ✅ chunk imports (start with /auth/assets) ex: <script type="module" crossorigin src="/auth/assets/index-C6_7OQDu.js">
    but..
  • ❌ wrong assets.directory in the generated wrangler config: "directory": "../client/auth".

The wrangler assets.directory should've remained ../client to make everything work.

TLDR

There is no clean way to align the following:

  • Vite base setting
  • Vite build folder structure
  • Wrangler assets.directory

IMO, the correct thing to do would be to have the Vite plugin respect the base config, and append that to the build output, but keep the assets.directory as client

Please provide any relevant error logs

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature:workers-assetsRelating to Cloudflare Workers Assets: https://developers.cloudflare.com/workers/static-assets/package:vite-pluginRelating to the `@cloudflare/vite-plugin` package

    Type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions