Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/tidy-hoops-win.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"@cloudflare/vite-plugin": minor
---

Enhanced build support for Workers with assets.

Assets that are imported in the entry Worker are now automatically moved to the client build output. This enables importing assets in your Worker and accessing them via the [assets binding](https://developers.cloudflare.com/workers/static-assets/binding/#binding). See [Static Asset Handling](https://vite.dev/guide/assets) to find out about all the ways you can import assets in Vite.

Additionally, a broader range of build scenarios are now supported. These are:

- Assets only build with client entry/entries
- Assets only build with no client entry/entries that includes `public` directory assets
- Worker(s) + assets build with client entry/entries
- Worker(s) + assets build with no client entry/entries that includes imported and/or `public` directory assets
- Worker(s) build with no assets
Original file line number Diff line number Diff line change
@@ -1,34 +1,6 @@
import { expect, test } from "vitest";
import {
getResponse,
getTextResponse,
isBuild,
page,
viteTestUrl,
} from "../../__test-utils__";

test("fetches public directory asset", async () => {
const response = await getResponse("/public-directory-asset");
const contentType = await response.headerValue("content-type");
const additionalHeader = await response.headerValue("additional-header");
expect(contentType).toBe("image/svg+xml");
expect(additionalHeader).toBe("public-directory-asset");
});

// TODO: enable build test when assets are copied to client output directory
test.skipIf(isBuild)("fetches imported asset", async () => {
const response = await getResponse("/imported-asset");
const contentType = await response.headerValue("content-type");
const additionalHeader = await response.headerValue("additional-header");
expect(contentType).toBe("image/svg+xml");
expect(additionalHeader).toBe("imported-asset");
});

// TODO: enable build test when assets are copied to client output directory
test.skipIf(isBuild)("fetches imported asset with url suffix", async () => {
const text = await getTextResponse("/imported-asset-url-suffix");
expect(text).toBe(`The text content is "Text content"`);
});
import { page, viteTestUrl } from "../../__test-utils__";
import "./base-tests";

test("fetches transformed HTML asset", async () => {
await page.goto(`${viteTestUrl}/transformed-html-asset`);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { expect, test } from "vitest";
import { getResponse, getTextResponse } from "../../__test-utils__";

test("fetches public directory asset", async () => {
const response = await getResponse("/public-directory-asset");
const contentType = await response.headerValue("content-type");
const additionalHeader = await response.headerValue("additional-header");
expect(contentType).toBe("image/svg+xml");
expect(additionalHeader).toBe("public-directory-asset");
});

test("fetches imported asset", async () => {
const response = await getResponse("/imported-asset");
const contentType = await response.headerValue("content-type");
const additionalHeader = await response.headerValue("additional-header");
expect(contentType).toBe("image/svg+xml");
expect(additionalHeader).toBe("imported-asset");
});

test("fetches imported asset with url suffix", async () => {
const text = await getTextResponse("/imported-asset-url-suffix");
expect(text).toBe(`The text content is "Text content"`);
});

test("fetches inline asset", async () => {
const response = await getResponse("/inline-asset");
const contentType = await response.headerValue("content-type");
const additionalHeader = await response.headerValue("additional-header");
expect(contentType).toBe("image/svg+xml");
expect(additionalHeader).toBe("inline-asset");
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as fs from "node:fs";
import * as path from "node:path";
import { expect, test, vi } from "vitest";
import { isBuild, testDir } from "../../../__test-utils__";
import "../base-tests";

test.runIf(isBuild)("deletes fallback client entry file", async () => {
const fallbackEntryPath = path.join(
testDir,
"dist",
"client",
"__cloudflare_fallback_entry__"
);

await vi.waitFor(() => {
expect(fs.existsSync(fallbackEntryPath)).toBe(false);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as fs from "node:fs";
import * as path from "node:path";
import { expect, test, vi } from "vitest";
import { getResponse, isBuild, testDir } from "../../../__test-utils__";

test("fetches public directory asset", async () => {
const response = await getResponse("/public-image.svg");
const contentType = await response.headerValue("content-type");
expect(contentType).toBe("image/svg+xml");
});

test.runIf(isBuild)("deletes fallback client entry file", async () => {
const fallbackEntryPath = path.join(
testDir,
"dist",
"__cloudflare_fallback_entry__"
);

await vi.waitFor(() => {
expect(fs.existsSync(fallbackEntryPath)).toBe(false);
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<!doctype html>
<head>
<title>HTML page</title>
</head>
<body>
<h1>Original content</h1>
</body>
<html lang="en">
<head>
<title>HTML page</title>
</head>
<body>
<h1>Original content</h1>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
"private": true,
"type": "module",
"scripts": {
"build": "vite build --app",
"build": "vite build",
"build:no-client-entry": "vite build -c vite.config.no-client-entry.ts",
"build:public-dir-only": "vite build -c vite.config.public-dir-only.ts",
"check:types": "tsc --build",
"dev": "vite dev",
"dev:no-client-entry": "vite dev -c vite.config.no-client-entry.ts",
"dev:public-dir-only": "vite dev -c vite.config.public-dir-only.ts",
"preview": "vite preview"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
>Imported asset with URL query param</a
>
</li>
<li><a href="/inline-asset">Inline asset</a></li>
<li><a href="/transformed-html-asset">Transformed HTML asset</a></li>
</ul>
</body>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import html from "./index.html?raw";
import importedImage from "./imported-image.svg";
import importedText from "./imported-text.txt?url";
import inlineImage from "./inline-image.svg?inline";

interface Env {
ASSETS: Fetcher;
Expand Down Expand Up @@ -42,6 +43,13 @@ export default {

return new Response(`The text content is "${textContent}"`);
}
case "/inline-asset": {
const response = await env.ASSETS.fetch(new URL(inlineImage, origin));
const modifiedResponse = new Response(response.body, response);
modifiedResponse.headers.append("additional-header", "inline-asset");

return modifiedResponse;
}
case "/transformed-html-asset": {
const response = await env.ASSETS.fetch(new URL("/html-page", origin));

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"extends": ["@cloudflare/workers-tsconfig/base.json"],
"include": ["vite.config.ts", "__tests__"]
"include": [
"vite.config.ts",
"vite.config.no-client-entry.ts",
"vite.config.public-dir-only.ts",
"__tests__"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { cloudflare } from "@cloudflare/vite-plugin";
import { defineConfig } from "vite";

export default defineConfig({
environments: {
worker: {
build: {
assetsInlineLimit: 0,
},
},
},
plugins: [cloudflare({ inspectorPort: false, persistState: false })],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { cloudflare } from "@cloudflare/vite-plugin";
import { defineConfig } from "vite";

export default defineConfig({
environments: {
worker: {
build: {
assetsInlineLimit: 0,
},
},
},
plugins: [
cloudflare({
configPath: "./wrangler.public-dir-only.jsonc",
inspectorPort: false,
persistState: false,
}),
],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "public-only",
"compatibility_date": "2024-12-30",
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { expect, test } from "vitest";
import { getTextResponse, isBuild, serverLogs } from "../../__test-utils__";
import {
getTextResponse,
isBuild,
page,
serverLogs,
} from "../../__test-utils__";

test("returns the correct response", async () => {
expect(await getTextResponse()).toEqual("Hello World!");
test("returns the index.html page", async () => {
const content = await page.textContent("h1");
expect(content).toBe("HTML page");
});

test("returns the Worker response", async () => {
const response = await getTextResponse("/another-path");
expect(response).toBe("Worker response");
});

test.runIf(isBuild)("runs a custom buildApp function", async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!doctype html>
<html lang="en">
<head>
<title>HTML page</title>
</head>
<body>
<h1>HTML page</h1>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export default {
async fetch() {
return new Response("Hello World!");
return new Response("Worker response");
},
} satisfies ExportedHandler;
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import assert from "node:assert";
import { cloudflare } from "@cloudflare/vite-plugin";
import { defineConfig } from "vite";

export default defineConfig({
builder: {
async buildApp(builder) {
const workerEnvironment = builder.environments.worker;
const clientEnvironment = builder.environments.client;

if (workerEnvironment) {
builder.config.logger.info("__before-build__");
await builder.build(workerEnvironment);
builder.config.logger.info("__after-build__");
}
assert(workerEnvironment, `No "worker" environment`);
assert(clientEnvironment, `No "client" environment`);

builder.config.logger.info("__before-build__");
await builder.build(workerEnvironment);
builder.config.logger.info("__after-build__");

await builder.build(clientEnvironment);

// The output `wrangler.json` will always include an `assets` field so will fail to run if there is no client build.
// To build correctly without assets, a custom `buildApp` would need to remove this field.
},
},
plugins: [cloudflare({ inspectorPort: false, persistState: false })],
Expand Down
Loading
Loading