Skip to content

Commit 8b26dba

Browse files
cpojerdavidgustys
andcommitted
Upgrade the Cloudflare Vite Plugin.
Co-authored-by: David Gustys <me@davidgustys.com>
1 parent 541b508 commit 8b26dba

57 files changed

Lines changed: 1861 additions & 559 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ dist
55
*.tsbuildinfo
66
.DS_Store
77
.wrangler
8+
.cloudflare
89
.void-wrangler.jsonc
910
.env
1011
.claude

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ The SDK's features are optional and composable. Use React, Vue, Svelte, or Solid
1212

1313
## Getting Started
1414

15-
Use Node.js 24.21.0 or later. In an empty directory, run:
15+
Use Node.js 24.22+ or 26.12+. In an empty directory, run:
1616

1717
```sh
1818
npm install -D void

‎docs/guide/platform/installation/prerequisites.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ If your account's dashboard has no **Enable** action, create a disposable Hello
3737

3838
## Install the CLI and Preview
3939

40-
With Node.js 24.21.0 or later, install the CLI:
40+
With Node.js 24.22+ or 26.12+, install the CLI:
4141

4242
```sh
4343
pnpm add --global void

‎docs/reference/api/plugin.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ export default defineConfig({
1616
});
1717
```
1818

19-
| Option | Type | Description |
20-
| ------------------ | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
21-
| `persistTo` | `string` | Directory path for persisting local dev state (D1, KV, R2). Defaults to `.void/` in the project root. |
22-
| `auxiliaryWorkers` | `AuxiliaryWorkerConfig[]` | Additional workers to run inside the same Miniflare instance during dev. Passed through to `@cloudflare/vite-plugin`. Useful for running multiple workers that share bindings (e.g. a separate API worker alongside a dashboard). |
19+
| Option | Type | Description |
20+
| ------------------ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
21+
| `persistTo` | `string` | Directory path for persisting local dev state (D1, KV, R2). Defaults to `.void/` in the project root. |
22+
| `auxiliaryWorkers` | `AuxiliaryWorkerConfig[]` | Additional Workers to run alongside your app, such as a separate API Worker and dashboard that share bindings. Each entry accepts `configPath` (JSON, JSONC, or TOML), `config` (settings or a callback), `viteEnvironment`, and `devOnly`. |
2323

2424
## Project configuration {#project-configuration}
2525

‎docs/reference/cli.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ Use this page as a command reference. If you are setting up a project for the fi
7676
| `void email alias [name]` | Show the shared mail address, or give it a shorter permanent name |
7777
| `void email domain` | Send and receive at your own domain on a Cloudflare zone |
7878
| `void init` | Set up a new or existing project, including coding agents |
79-
| `void migrate` | Convert legacy `void.json` and root Wrangler JSON/JSONC into `void.config.ts` |
79+
| `void migrate` | Update project configuration |
8080

8181
## Binary Invocation {#binary-invocation}
8282

‎docs/reference/cli/setup.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Resources recorded in `void.lock.json` without a matching definition appear sepa
2323

2424
`void migrate` converts legacy `void.json` and root `wrangler.jsonc` or `wrangler.json` files into `void.config.ts`. It also accepts one root `wrangler*.json(c)` file referenced by a supported framework adapter. Void saves backups in `.void/config-migration/`, records resource IDs in `void.lock.json`, and updates supported adapters to use its generated Cloudflare config.
2525

26-
`.void-wrangler.jsonc` is generated for Cloudflare tooling and belongs in `.gitignore`; migration adds the entry. Review and commit `void.config.ts`, `void.lock.json`, and `.gitignore`. `void init` and `void deploy` migrate legacy files automatically. If the project has conflicting or multiple Cloudflare configs, resolve them first. The project's installed `void` package must match the CLI version before Cloudflare deployment.
26+
`void migrate` also adds `.cloudflare` to `.gitignore`, including in projects that already use `void.config.ts`. Configuration migration adds `.void-wrangler.jsonc` to `.gitignore` as well. Review and commit `void.config.ts`, `void.lock.json`, and `.gitignore`. `void init` and `void deploy` migrate legacy files automatically. If the project has conflicting or multiple Cloudflare configs, resolve them first. The project's installed `void` package must match the CLI version before Cloudflare deployment.
2727

2828
## `void init` {#void-init}
2929

‎packages/create-void/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Create a full-stack Vite app with [Void](https://void.cloud), ready for local de
44

55
## Getting Started
66

7-
Use Node.js 24.21.0 or later:
7+
Use Node.js 24.22+ or 26.12+:
88

99
```sh
1010
npm create void@latest my-app

‎packages/create-void/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@
3535
"vite-plus": "catalog:"
3636
},
3737
"engines": {
38-
"node": ">=24.21.0"
38+
"node": "^24.22.0 || >=26.12.0"
3939
}
4040
}

‎packages/deploy-cloudflare/src/diagnostics.ts‎

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,26 @@ import type { CloudflareCommandResult } from './execute.ts';
33

44
const MAX_DIAGNOSTIC_LENGTH = 4096;
55
const MAX_DIAGNOSTIC_LINES = 24;
6+
const ERROR_BOX =
7+
/(?:^|\n)[ \t]*┌[ \t]*(?:APIError|Error)\b[^\n]*\n(?:[ \t]*│[^\n]*\n)*[ \t]*└[^\n]*/i;
8+
// cf draws upload progress, binding tables, config diffs and upload conflicts as log
9+
// lines: a guide symbol, or its fallback for terminals without Unicode, then two spaces.
10+
// These lines can show binding values. cf printed them on stdout before 1.0.0-beta.21,
11+
// and on stderr since.
12+
const LOG_LINE = /^[├│◆◇●▲■└|*o•!x—](?: {2}|$)/;
13+
const UPLOAD_CONFLICT =
14+
/^[ \t]*(?:\S[ \t]+)?(Aborting the upload operation because of conflicts\..*)$/m;
15+
const SCRIPT_API_ORIGIN =
16+
/^[ \t]*(?:\S[ \t]+)?(You are about to upload a Worker that was last updated via the script API\.)/m;
617

7-
/** Keep the uploader's error, excluding stdout binding tables and redacting common credential formats. */
18+
function withoutLogLines(output: string): string {
19+
return output
20+
.split('\n')
21+
.filter((line) => !LOG_LINE.test(line))
22+
.join('\n');
23+
}
24+
25+
/** Keep the uploader's error, excluding cf's log lines and redacting common credential formats. */
826
export function cloudflareCommandDiagnostic(result: CloudflareCommandResult): string | undefined {
927
const stderr = stripVTControlCharacters(result.stderr).replace(/\r\n?/g, '\n');
1028
const stdout = stripVTControlCharacters(result.stdout).replace(/\r\n?/g, '\n');
@@ -17,29 +35,28 @@ export function cloudflareCommandDiagnostic(result: CloudflareCommandResult): st
1735
}
1836
}
1937
if (message === stderr && !/(?:^|\n)[ \t]*(?:[✘×X][ \t]+)?\[ERROR\]/i.test(stderr)) {
20-
// cf renders API failures as a box on stdout while config warnings go to
21-
// stderr. Only copy the error box; stdout may also contain binding values.
22-
const box =
23-
/(?:^|\n)[ \t]*┌[ \t]*(?:APIError|Error)\b[^\n]*\n(?:[ \t]*│[^\n]*\n)*[ \t]*└[^\n]*/i.exec(
24-
stdout,
25-
);
38+
// Copy only an error box on stdout, the upload conflict without the config diff
39+
// that explains it, or stderr without cf's log lines.
40+
const box = ERROR_BOX.exec(stdout);
41+
const conflict = UPLOAD_CONFLICT.exec(`${stdout}\n${stderr}`);
2642
if (box) {
2743
message = box[0].trim();
44+
} else if (conflict) {
45+
message = [SCRIPT_API_ORIGIN.exec(`${stdout}\n${stderr}`)?.[1], conflict[1]]
46+
.filter(Boolean)
47+
.join('\n');
2848
} else {
29-
const lines = stdout.split('\n');
30-
const abort = lines.find((line) =>
31-
line.startsWith('■ Aborting the upload operation because of conflicts.'),
32-
);
33-
if (abort) {
34-
const reason = 'You are about to upload a Worker that was last updated via the script API.';
35-
message = [lines.includes(`▲ ${reason}`) ? reason : '', abort.slice('■ '.length)]
36-
.filter(Boolean)
37-
.join('\n');
38-
}
49+
// Keep an error box on stderr whole: its bars look like log lines.
50+
const stderrBox = ERROR_BOX.exec(stderr);
51+
message = stderrBox
52+
? withoutLogLines(stderr.slice(0, stderrBox.index)) +
53+
stderrBox[0] +
54+
withoutLogLines(stderr.slice(stderrBox.index + stderrBox[0].length))
55+
: withoutLogLines(stderr);
3956
}
4057
}
41-
// Upload progress and bindings are printed on stdout. Never echo that whole stream
42-
// when no error block was found, even when the process failed without stderr.
58+
// Never echo stdout, or cf's log lines on stderr, when no error block was found,
59+
// even when the process failed without stderr.
4360
message = stripVTControlCharacters(message)
4461
.split(/\n\s*(?:🪵|Logs (?:were )?written|If you think this is a bug)/)[0]!
4562
.trim();

‎packages/deploy-cloudflare/test/unit/diagnostics.test.ts‎

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,92 @@ describe('Cloudflare command diagnostics', () => {
5353
).toBe('┌ APIError\n│ [10000] Worker upload failed.\n└');
5454
});
5555

56+
it('keeps an error box and its advice on stderr without the binding table before it', () => {
57+
expect(
58+
cloudflareCommandDiagnostic({
59+
status: 1,
60+
stdout: '',
61+
// cf 1.0.0-beta.21 and later print upload progress and bindings on stderr.
62+
stderr: [
63+
'├ Upload',
64+
'│ Your Worker has access to the following bindings:',
65+
'│ Binding Resource',
66+
'│ env.PRIVATE_VALUE ("do-not-echo") Environment Variable',
67+
'| env.ASCII_VALUE ("do-not-echo") Environment Variable',
68+
'│',
69+
'',
70+
'┌ APIError',
71+
'│ [10000] Authentication error [code: 10000]',
72+
'│',
73+
'│ 403 Forbidden',
74+
'└',
75+
'',
76+
'→ Check your API token permissions',
77+
].join('\n'),
78+
}),
79+
).toBe(
80+
'┌ APIError\n│ [10000] Authentication error [code: 10000]\n│\n│ 403 Forbidden\n└\n\n→ Check your API token permissions',
81+
);
82+
expect(
83+
cloudflareCommandDiagnostic({
84+
status: 1,
85+
stdout: '',
86+
stderr: '├ Upload\n│ env.PRIVATE_VALUE ("do-not-echo")\nUpload stopped.',
87+
}),
88+
).toBe('Upload stopped.');
89+
expect(
90+
cloudflareCommandDiagnostic({
91+
status: 1,
92+
stdout: '',
93+
stderr: '├ Upload\n│ env.PRIVATE_VALUE ("do-not-echo")\n◆ Version upload complete',
94+
}),
95+
).toBeUndefined();
96+
});
97+
98+
it('reports upload conflicts from either stream without the config diff', () => {
99+
const abort =
100+
'Aborting the upload operation because of conflicts. To override and upload anyway, remove the `--strict` flag';
101+
const reason = 'You are about to upload a Worker that was last updated via the script API.';
102+
const scriptApi = [
103+
'├ Upload',
104+
`▲ ${reason}`,
105+
'│ Edits that have been made via the script API will be overridden by your local code and config.',
106+
`■ ${abort}`,
107+
'◆ Version upload complete',
108+
].join('\n');
109+
// cf 1.0.0-beta.21 and later print upload messages on stderr, earlier releases on stdout.
110+
for (const [stdout, stderr] of [
111+
['', scriptApi],
112+
[scriptApi, ''],
113+
// Without a Unicode terminal, cf prints fallback symbols.
114+
['', scriptApi.replace('▲', '!').replace('■', 'x').replace('│', '|').replace('◆', '*')],
115+
]) {
116+
expect(cloudflareCommandDiagnostic({ status: 1, stdout: stdout!, stderr: stderr! })).toBe(
117+
`${reason}\n${abort}`,
118+
);
119+
}
120+
expect(
121+
cloudflareCommandDiagnostic({
122+
status: 1,
123+
stdout: '',
124+
stderr: [
125+
'├ Upload',
126+
'▲ The local configuration being used (generated from your local configuration file) differs from the remote configuration of your Worker set via the Cloudflare Dashboard:',
127+
'│ {',
128+
'│ vars: {',
129+
'│ - REMOTE_VALUE: "do-not-echo"',
130+
`│ + LOCAL_VALUE: "${abort} do-not-echo"`,
131+
'│ }',
132+
'│ }',
133+
'│',
134+
'│ Uploading the Worker will override the remote configuration with your local one.',
135+
`■ ${abort}`,
136+
'◆ Version upload complete',
137+
].join('\n'),
138+
}),
139+
).toBe(abort);
140+
});
141+
56142
it('retains plain stderr and process-spawn failures', () => {
57143
expect(
58144
cloudflareCommandDiagnostic({ status: 1, stdout: '', stderr: 'Unknown argument: strict' }),

0 commit comments

Comments
 (0)