void 0.7.12 → 0.7.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT_PROMPT.md +1 -1
- package/README.md +2 -2
- package/dist/cli/cli.mjs +2 -2
- package/dist/{init-iIiQWzyB.mjs → init-Br5DTaG7.mjs} +82 -8
- package/dist/pages/islands-plugin.mjs +8 -9
- package/dist/runtime/migration-handler.mjs +68 -1
- package/getting-started-prompt.txt +2 -2
- package/package.json +2 -2
- package/skills/void/SKILL.md +1 -1
- package/skills/void/docs/guide/pages-routing/overview.md +1 -1
- package/skills/void/docs/guide/quickstart.md +2 -2
- package/skills/void/docs/node_modules/void/AGENT_PROMPT.md +1 -1
- package/skills/void/docs/node_modules/void/README.md +2 -2
- package/skills/void/docs/node_modules/void/skills/void/SKILL.md +1 -1
- package/skills/void/docs/reference/cli.md +3 -1
package/AGENT_PROMPT.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## Void
|
|
2
2
|
|
|
3
|
-
This project uses [Void](https://void.cloud) — a fullstack Vite plugin + deployment platform for Cloudflare. `voidPlugin()` in `vite.config.ts` gives you file-based API routing on Hono (`routes/`), Inertia-inspired server-rendered pages with co-located loaders/actions (`pages/` + `@void/vue` or `@void/react`), auto-provisioned D1/KV/R2 bindings, first-class Drizzle ORM integration (schema in `db/schema.ts` -> `void/db` Drizzle instance -> typed routes -> typed fetch client), built-in auth, queues, cron jobs, edge caching (ISR), and one-command deploys via `npx void deploy`. For first-time setup, prefer `npx void init`; in an empty directory, install `void` first and let the interactive flow scaffold the starter, add the matching framework adapter, configure project files, handle auth, and link or create the deploy project before the first deploy. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. Use `void` and `@void/*` package names in source code and package manifests.
|
|
3
|
+
This project uses [Void](https://void.cloud) — a fullstack Vite plugin + deployment platform for Cloudflare. `voidPlugin()` in `vite.config.ts` gives you file-based API routing on Hono (`routes/`), Inertia-inspired server-rendered pages with co-located loaders/actions (`pages/` + `@void/vue` or `@void/react`), auto-provisioned D1/KV/R2 bindings, first-class Drizzle ORM integration (schema in `db/schema.ts` -> `void/db` Drizzle instance -> typed routes -> typed fetch client), built-in auth, queues, cron jobs, edge caching (ISR), and one-command deploys via `npx void deploy`. For first-time setup, prefer `npx void init`; in an empty directory, install `void` first and let the interactive flow scaffold the starter with Vite+ by default, add the matching framework adapter, configure project files, handle auth, and link or create the deploy project before the first deploy. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. Use `void` and `@void/*` package names in source code and package manifests.
|
|
4
4
|
|
|
5
5
|
Database: define Drizzle tables in `db/schema.ts`, import `db` from `void/db` and tables from `@schema`. Use `void db push` for prototyping, `void db generate` for production migrations. `drizzle-orm` and `drizzle-kit` ship with void (no extra install). Migrations live in `db/migrations/`.
|
|
6
6
|
|
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ Install the Void CLI:
|
|
|
21
21
|
npm install -D void
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
If you run `void init` in an empty directory, the scaffold flow adds the matching Pages adapter and Vite
|
|
24
|
+
If you run `void init` in an empty directory, the scaffold flow adds the matching Pages adapter and Vite+ by default.
|
|
25
25
|
|
|
26
26
|
Then run `npx void init`. In an empty directory, the full interactive flow can scaffold a starter, configure local project files, log you in, and link or create your Void project so the next step is just `npx void deploy`. In an existing app, it configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`.
|
|
27
27
|
|
|
@@ -82,7 +82,7 @@ There is Vinext-specific source in the repo, but it is intentionally on hold and
|
|
|
82
82
|
|
|
83
83
|
## Documentation
|
|
84
84
|
|
|
85
|
-
Full docs live at [void.cloud/
|
|
85
|
+
Full docs live at [void.cloud/guide](https://void.cloud/guide).
|
|
86
86
|
|
|
87
87
|
## License
|
|
88
88
|
|
package/dist/cli/cli.mjs
CHANGED
|
@@ -1539,7 +1539,7 @@ else if (command === "prepare") {
|
|
|
1539
1539
|
});
|
|
1540
1540
|
} else if (command === "init") {
|
|
1541
1541
|
const root = process.cwd();
|
|
1542
|
-
import("../init-
|
|
1542
|
+
import("../init-Br5DTaG7.mjs").then(({ runInitCommand, parseInitArgs }) => {
|
|
1543
1543
|
let initArgs;
|
|
1544
1544
|
try {
|
|
1545
1545
|
initArgs = parseInitArgs(args.slice(1));
|
|
@@ -1628,7 +1628,7 @@ else if (command === "-v" || command === "--version") {
|
|
|
1628
1628
|
const root = process.cwd();
|
|
1629
1629
|
switch (cmd) {
|
|
1630
1630
|
case "init": {
|
|
1631
|
-
const { runInitCommand, parseInitArgs } = await import("../init-
|
|
1631
|
+
const { runInitCommand, parseInitArgs } = await import("../init-Br5DTaG7.mjs");
|
|
1632
1632
|
await runInitCommand(root, parseInitArgs([]));
|
|
1633
1633
|
return;
|
|
1634
1634
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as join, i as isAbsolute, n as dirname, o as relative, s as resolve, t as basename } from "./pathe.M-eThtNZ-D-kmWkCS.mjs";
|
|
2
2
|
import { n as cliTitle } from "./output-D3g5otMC.mjs";
|
|
3
3
|
import { c as getToken, n as PlatformClient, u as isStagingMode } from "./client-C_DYiXeu.mjs";
|
|
4
|
-
import { _ as me, c as R, g as ge, i as Ee, u as Se, v as ue, x as q, y as ye } from "./dist-D2L3_KTK.mjs";
|
|
4
|
+
import { _ as me, c as R, g as ge, i as Ee, l as Re, u as Se, v as ue, x as q, y as ye } from "./dist-D2L3_KTK.mjs";
|
|
5
5
|
import { a as writeProjectConfig, r as readProjectConfig } from "./project-DhB4A8_g.mjs";
|
|
6
6
|
import { c as getDatabaseDialect, f as readConfig } from "./config-CeRQg3Lz.mjs";
|
|
7
7
|
import { r as detectFramework } from "./plugin-inference-BexV6LL3.mjs";
|
|
@@ -208,6 +208,16 @@ const SCAFFOLD_BASE_DEV_DEPENDENCIES = {
|
|
|
208
208
|
vite: "^8.0.10",
|
|
209
209
|
typescript: "^5.9.3"
|
|
210
210
|
};
|
|
211
|
+
const SCAFFOLD_VITE_PLUS_DEV_DEPENDENCIES = { "vite-plus": "^0.1.21" };
|
|
212
|
+
const SCAFFOLD_TOOLCHAIN_OPTIONS = [{
|
|
213
|
+
value: "vite-plus",
|
|
214
|
+
label: "Vite+",
|
|
215
|
+
hint: "Uses the Vite+ toolchain and vp commands."
|
|
216
|
+
}, {
|
|
217
|
+
value: "vite",
|
|
218
|
+
label: "Vite",
|
|
219
|
+
hint: "Uses plain Vite commands without Vite+."
|
|
220
|
+
}];
|
|
211
221
|
const REACT_SCAFFOLD_COMPILER_DEV_DEPENDENCIES = {
|
|
212
222
|
"@rolldown/plugin-babel": "^0.2.3",
|
|
213
223
|
"@vitejs/plugin-react": "^6.0.1",
|
|
@@ -294,7 +304,7 @@ function getScaffoldTsconfigCompilerOptions(framework) {
|
|
|
294
304
|
return framework ? { ...SCAFFOLD_FRAMEWORKS[framework].tsconfigCompilerOptions } : {};
|
|
295
305
|
}
|
|
296
306
|
function createPagesStarterTemplate(options) {
|
|
297
|
-
const { framework, starterMode, voidPackageSpec, adapterPackageSpec } = options;
|
|
307
|
+
const { framework, starterMode, toolchain = "vite", voidPackageSpec, adapterPackageSpec } = options;
|
|
298
308
|
const config = SCAFFOLD_FRAMEWORKS[framework];
|
|
299
309
|
const files = {
|
|
300
310
|
"vite.config.ts": getViteConfig(framework, config),
|
|
@@ -308,9 +318,9 @@ function createPagesStarterTemplate(options) {
|
|
|
308
318
|
}
|
|
309
319
|
return {
|
|
310
320
|
scripts: {
|
|
311
|
-
dev: "vite",
|
|
312
|
-
build: "vite build",
|
|
313
|
-
preview: "vite preview"
|
|
321
|
+
dev: toolchain === "vite-plus" ? "vp dev" : "vite",
|
|
322
|
+
build: toolchain === "vite-plus" ? "vp build" : "vite build",
|
|
323
|
+
preview: toolchain === "vite-plus" ? "vp preview" : "vite preview"
|
|
314
324
|
},
|
|
315
325
|
dependencies: {
|
|
316
326
|
...SCAFFOLD_BASE_DEPENDENCIES,
|
|
@@ -319,6 +329,7 @@ function createPagesStarterTemplate(options) {
|
|
|
319
329
|
devDependencies: {
|
|
320
330
|
void: voidPackageSpec,
|
|
321
331
|
...SCAFFOLD_BASE_DEV_DEPENDENCIES,
|
|
332
|
+
...toolchain === "vite-plus" ? SCAFFOLD_VITE_PLUS_DEV_DEPENDENCIES : {},
|
|
322
333
|
[config.adapterPackage]: adapterPackageSpec,
|
|
323
334
|
...config.devDependencies
|
|
324
335
|
},
|
|
@@ -823,6 +834,53 @@ function isScaffoldableEmptyProject(root) {
|
|
|
823
834
|
if (Object.keys(scripts).some((name) => name !== "test")) return false;
|
|
824
835
|
return true;
|
|
825
836
|
}
|
|
837
|
+
function shouldPromptForInitLocation(root) {
|
|
838
|
+
if (isScaffoldableEmptyProject(root)) return false;
|
|
839
|
+
if (existsSync(join(root, "package.json"))) return false;
|
|
840
|
+
if (existsSync(join(root, VOID_CONFIG_FILE))) return false;
|
|
841
|
+
return findViteConfig(root) === void 0;
|
|
842
|
+
}
|
|
843
|
+
function validateSubfolderName(root, value) {
|
|
844
|
+
const name = value?.trim() ?? "";
|
|
845
|
+
if (!name) return "Enter a folder name.";
|
|
846
|
+
if (name === "." || name === ".." || name.includes("/") || name.includes("\\")) return "Enter a single folder name, not a path.";
|
|
847
|
+
if (existsSync(join(root, name))) return "That folder already exists.";
|
|
848
|
+
}
|
|
849
|
+
async function resolveInitRoot(root) {
|
|
850
|
+
if (!shouldPromptForInitLocation(root)) return root;
|
|
851
|
+
const choice = await Ee({
|
|
852
|
+
message: "This folder is not empty. Where should Void initialize?",
|
|
853
|
+
initialValue: "subfolder",
|
|
854
|
+
options: [{
|
|
855
|
+
value: "subfolder",
|
|
856
|
+
label: "Create a new subfolder",
|
|
857
|
+
hint: "Scaffold a new app inside this directory."
|
|
858
|
+
}, {
|
|
859
|
+
value: "current",
|
|
860
|
+
label: "Continue here",
|
|
861
|
+
hint: "Add package.json, vite.config.ts, and Void files to this directory."
|
|
862
|
+
}]
|
|
863
|
+
});
|
|
864
|
+
if (q(choice)) {
|
|
865
|
+
me("Setup cancelled.");
|
|
866
|
+
process.exit(0);
|
|
867
|
+
}
|
|
868
|
+
if (choice === "current") return root;
|
|
869
|
+
const folder = await Re({
|
|
870
|
+
message: "New project folder name:",
|
|
871
|
+
placeholder: "my-void-app",
|
|
872
|
+
initialValue: "my-void-app",
|
|
873
|
+
validate: (value) => validateSubfolderName(root, value)
|
|
874
|
+
});
|
|
875
|
+
if (q(folder)) {
|
|
876
|
+
me("Setup cancelled.");
|
|
877
|
+
process.exit(0);
|
|
878
|
+
}
|
|
879
|
+
const nextRoot = join(root, folder.trim());
|
|
880
|
+
mkdirSync(nextRoot, { recursive: true });
|
|
881
|
+
R.info(`Scaffolding in ${nextRoot}`);
|
|
882
|
+
return nextRoot;
|
|
883
|
+
}
|
|
826
884
|
function parsePackageManagerField(pkg) {
|
|
827
885
|
if (!pkg || typeof pkg !== "object") return;
|
|
828
886
|
const raw = pkg.packageManager;
|
|
@@ -1566,6 +1624,18 @@ async function promptStarterMode() {
|
|
|
1566
1624
|
}
|
|
1567
1625
|
return choice;
|
|
1568
1626
|
}
|
|
1627
|
+
async function promptScaffoldToolchain() {
|
|
1628
|
+
const choice = await Ee({
|
|
1629
|
+
message: "Choose a Pages scaffold toolchain:",
|
|
1630
|
+
initialValue: "vite-plus",
|
|
1631
|
+
options: [...SCAFFOLD_TOOLCHAIN_OPTIONS]
|
|
1632
|
+
});
|
|
1633
|
+
if (q(choice)) {
|
|
1634
|
+
me("Setup cancelled.");
|
|
1635
|
+
process.exit(0);
|
|
1636
|
+
}
|
|
1637
|
+
return choice;
|
|
1638
|
+
}
|
|
1569
1639
|
async function promptScaffoldFramework() {
|
|
1570
1640
|
const choice = await Ee({
|
|
1571
1641
|
message: "Choose a Pages framework to scaffold:",
|
|
@@ -1850,11 +1920,12 @@ function generateStarterMigration(root, starterMode) {
|
|
|
1850
1920
|
}
|
|
1851
1921
|
R.success("Generated initial Drizzle migration");
|
|
1852
1922
|
}
|
|
1853
|
-
function scaffoldPagesApp(root, packageManager, framework, starterMode) {
|
|
1923
|
+
function scaffoldPagesApp(root, packageManager, framework, starterMode, toolchain) {
|
|
1854
1924
|
const packageJsonPath = join(root, "package.json");
|
|
1855
1925
|
const template = createPagesStarterTemplate({
|
|
1856
1926
|
framework,
|
|
1857
1927
|
starterMode,
|
|
1928
|
+
toolchain,
|
|
1858
1929
|
voidPackageSpec: resolveScaffoldDependencySpec(root, "void", getOwnVersion()),
|
|
1859
1930
|
adapterPackageSpec: resolveScaffoldDependencySpec(root, SCAFFOLD_FRAMEWORKS[framework].adapterPackage, getOwnVersion())
|
|
1860
1931
|
});
|
|
@@ -1893,7 +1964,7 @@ function scaffoldPagesApp(root, packageManager, framework, starterMode) {
|
|
|
1893
1964
|
for (const [name, version] of Object.entries(template.dependencies)) ensureDependency("dependencies", name, version);
|
|
1894
1965
|
for (const [name, version] of Object.entries(template.devDependencies)) ensureDependency("devDependencies", name, version);
|
|
1895
1966
|
if (isPnpmProject(root, pkg)) ensurePnpmAllowBuilds(root, pkg);
|
|
1896
|
-
if (packageManager === "pnpm" && (!pkg.packageManager || typeof pkg.packageManager !== "string")) pkg.packageManager = "pnpm@11.0.9";
|
|
1967
|
+
if (packageManager === "pnpm" && resolveWorkspaceDeferral(root, pkg) === void 0 && (!pkg.packageManager || typeof pkg.packageManager !== "string")) pkg.packageManager = "pnpm@11.0.9";
|
|
1897
1968
|
writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
1898
1969
|
R.success("Updated package.json with starter scripts and dependencies");
|
|
1899
1970
|
for (const [filePath, content] of Object.entries(template.files)) writeFileIfMissing(root, filePath, content);
|
|
@@ -2547,6 +2618,7 @@ async function runInitCommand(root, initArgs) {
|
|
|
2547
2618
|
console.log();
|
|
2548
2619
|
ge(cliTitle("init"));
|
|
2549
2620
|
const targeted = Boolean(initArgs && (initArgs.tsconfig || initArgs.github || initArgs.agents));
|
|
2621
|
+
if (!targeted) root = await resolveInitRoot(root);
|
|
2550
2622
|
const framework = detectFramework(root);
|
|
2551
2623
|
const preScaffoldState = detectProjectState(root);
|
|
2552
2624
|
const detectedPagesFramework = detectPagesFramework(root);
|
|
@@ -2558,12 +2630,14 @@ async function runInitCommand(root, initArgs) {
|
|
|
2558
2630
|
if (framework) R.info(`Detected ${framework.name} framework`);
|
|
2559
2631
|
if (!targeted && !framework && isScaffoldableEmptyProject(root)) {
|
|
2560
2632
|
R.info("Empty project detected. Scaffolding a Pages starter...");
|
|
2633
|
+
const scaffoldToolchain = await promptScaffoldToolchain();
|
|
2561
2634
|
if (detectedPagesFramework) {
|
|
2562
2635
|
scaffoldedPagesFramework = detectedPagesFramework;
|
|
2563
2636
|
R.info(`Detected ${SCAFFOLD_FRAMEWORKS[detectedPagesFramework].label} adapter in package.json`);
|
|
2564
2637
|
} else scaffoldedPagesFramework = await promptScaffoldFramework();
|
|
2565
2638
|
const starterMode = await promptStarterMode();
|
|
2566
|
-
const
|
|
2639
|
+
const scaffoldPackageManager = scaffoldToolchain === "vite-plus" ? "pnpm" : preScaffoldState.packageManager;
|
|
2640
|
+
const scaffoldWroteDeps = scaffoldPagesApp(root, scaffoldPackageManager, scaffoldedPagesFramework, starterMode, scaffoldToolchain);
|
|
2567
2641
|
const deferral = resolveWorkspaceDeferral(root, readPackageJson(root));
|
|
2568
2642
|
const voidResolvable = isVoidResolvableFrom(root);
|
|
2569
2643
|
if (deferral && (!voidResolvable || scaffoldWroteDeps)) {
|
|
@@ -5,6 +5,11 @@ import { normalizePath } from "vite";
|
|
|
5
5
|
//#region src/pages/islands-plugin.ts
|
|
6
6
|
const ISLANDS_RENDERER_ID = "virtual:void-islands-renderer";
|
|
7
7
|
const ISLANDS_CLIENT_ENTRY_ID = "virtual:void-islands-client";
|
|
8
|
+
const ISLAND_IMPORT_RE = /import\s+([$_A-Za-z][$_\w]*)\s+from\s+("[^"]+"|'[^']+')\s+with\s*\{\s*island\s*:\s*("[^"]+"|'[^']+')\s*,?\s*\}/g;
|
|
9
|
+
function hasIslandImports(code) {
|
|
10
|
+
ISLAND_IMPORT_RE.lastIndex = 0;
|
|
11
|
+
return ISLAND_IMPORT_RE.test(code);
|
|
12
|
+
}
|
|
8
13
|
/** Resolve a relative import specifier to an absolute path by checking extensions. */
|
|
9
14
|
function resolveSpecifier(specifier, importerDir, extensions) {
|
|
10
15
|
const base = normalizePath(resolve(importerDir, specifier));
|
|
@@ -18,7 +23,6 @@ function resolveSpecifier(specifier, importerDir, extensions) {
|
|
|
18
23
|
* or any .md file has a `<script>` block.
|
|
19
24
|
*/
|
|
20
25
|
function needsIslandClientBundle(dir, islandFilePattern, checkStyleBlocks) {
|
|
21
|
-
const importAttrRe = /import\s+\w+\s+from\s+("[^"]+"|'[^']+')\s+with\s*\{\s*island\s*:/;
|
|
22
26
|
const cssImportRe = /import\b[^"']*["'][^"']+\.(?:css|scss|sass|less|styl|stylus|pcss|postcss)["']/;
|
|
23
27
|
const mdScriptRe = /^<script\b/m;
|
|
24
28
|
const styleRe = checkStyleBlocks ? /<style\b/ : null;
|
|
@@ -31,7 +35,7 @@ function needsIslandClientBundle(dir, islandFilePattern, checkStyleBlocks) {
|
|
|
31
35
|
const name = entry.name;
|
|
32
36
|
if (islandFilePattern.test(name) || name.startsWith("layout.") || name.startsWith("_")) {
|
|
33
37
|
const content = readFileSync(resolve(entry.parentPath, name), "utf-8");
|
|
34
|
-
if (
|
|
38
|
+
if (hasIslandImports(content)) return true;
|
|
35
39
|
if (styleRe && styleRe.test(content)) return true;
|
|
36
40
|
if (cssImportRe.test(content)) return true;
|
|
37
41
|
}
|
|
@@ -67,13 +71,10 @@ function extractCssImports(filePath) {
|
|
|
67
71
|
function computePagesNeedingClientJS(pageScan, pagesDir, resolvedConfig) {
|
|
68
72
|
const mdPlugin = resolvedConfig?.plugins?.find((plugin) => plugin.name === "void-md");
|
|
69
73
|
const mdScriptIds = new Set(mdPlugin?.api?.getClientScripts?.()?.keys() ?? []);
|
|
70
|
-
const importAttrRe = /import\s+\w+\s+from\s+("[^"]+"|'[^']+')\s+with\s*\{\s*island\s*:\s*("[^"]+"|'[^']+')\s*\}/g;
|
|
71
74
|
const layoutDirsWithIslands = /* @__PURE__ */ new Set();
|
|
72
75
|
for (const layout of pageScan.layouts) {
|
|
73
76
|
if (!layout.island) continue;
|
|
74
|
-
|
|
75
|
-
importAttrRe.lastIndex = 0;
|
|
76
|
-
if (importAttrRe.test(code)) layoutDirsWithIslands.add(layout.directory);
|
|
77
|
+
if (hasIslandImports(readFileSync(resolve(pagesDir, layout.filePath), "utf-8"))) layoutDirsWithIslands.add(layout.directory);
|
|
77
78
|
}
|
|
78
79
|
const result = /* @__PURE__ */ new Set();
|
|
79
80
|
for (const page of pageScan.pages) {
|
|
@@ -82,9 +83,7 @@ function computePagesNeedingClientJS(pageScan, pagesDir, resolvedConfig) {
|
|
|
82
83
|
result.add(page.componentId);
|
|
83
84
|
continue;
|
|
84
85
|
}
|
|
85
|
-
|
|
86
|
-
importAttrRe.lastIndex = 0;
|
|
87
|
-
if (importAttrRe.test(code)) {
|
|
86
|
+
if (hasIslandImports(readFileSync(resolve(pagesDir, page.componentPath), "utf-8"))) {
|
|
88
87
|
result.add(page.componentId);
|
|
89
88
|
continue;
|
|
90
89
|
}
|
|
@@ -63,7 +63,8 @@ async function runD1Migrations(db, migrations) {
|
|
|
63
63
|
const start = Date.now();
|
|
64
64
|
try {
|
|
65
65
|
const statements = splitMigrationSql(m.sql);
|
|
66
|
-
if (statements.length === 1
|
|
66
|
+
if (statements.length === 1) if (isSingleSqlStatement(statements[0])) await db.prepare(statements[0]).run();
|
|
67
|
+
else await db.exec(m.sql);
|
|
67
68
|
else for (const statement of statements) await db.prepare(statement).run();
|
|
68
69
|
await db.prepare("INSERT OR IGNORE INTO _void_migrations (name, applied_at) VALUES (?, ?)").bind(m.name, (/* @__PURE__ */ new Date()).toISOString()).run();
|
|
69
70
|
migrationResults.push({
|
|
@@ -121,5 +122,71 @@ function isStatementBreakpointLine(line) {
|
|
|
121
122
|
function hasInlineStatementBreakpoint(line) {
|
|
122
123
|
return /;\s*-->\s*statement-breakpoint\s*$/.test(line.trim());
|
|
123
124
|
}
|
|
125
|
+
function isSingleSqlStatement(sql) {
|
|
126
|
+
let statements = 0;
|
|
127
|
+
let inStatement = false;
|
|
128
|
+
let index = 0;
|
|
129
|
+
while (index < sql.length) {
|
|
130
|
+
const character = sql[index];
|
|
131
|
+
const next = index + 1 < sql.length ? sql[index + 1] : "";
|
|
132
|
+
if (character === "-" && next === "-") {
|
|
133
|
+
index += 2;
|
|
134
|
+
while (index < sql.length && sql[index] !== "\n") index++;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (character === "/" && next === "*") {
|
|
138
|
+
index += 2;
|
|
139
|
+
while (index < sql.length - 1 && !(sql[index] === "*" && sql[index + 1] === "/")) index++;
|
|
140
|
+
index = Math.min(index + 2, sql.length);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (character === "'") {
|
|
144
|
+
inStatement = true;
|
|
145
|
+
index++;
|
|
146
|
+
while (index < sql.length) {
|
|
147
|
+
if (sql[index] === "'" && sql[index + 1] === "'") {
|
|
148
|
+
index += 2;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (sql[index] === "'") {
|
|
152
|
+
index++;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
index++;
|
|
156
|
+
}
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (character === "\"" || character === "`" || character === "[") {
|
|
160
|
+
inStatement = true;
|
|
161
|
+
const close = character === "[" ? "]" : character;
|
|
162
|
+
index++;
|
|
163
|
+
while (index < sql.length) {
|
|
164
|
+
if (close !== "]" && sql[index] === close && sql[index + 1] === close) {
|
|
165
|
+
index += 2;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (sql[index] === close) {
|
|
169
|
+
index++;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
index++;
|
|
173
|
+
}
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (character === ";") {
|
|
177
|
+
if (inStatement) {
|
|
178
|
+
statements++;
|
|
179
|
+
if (statements > 1) return false;
|
|
180
|
+
inStatement = false;
|
|
181
|
+
}
|
|
182
|
+
index++;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (character !== " " && character !== " " && character !== "\n" && character !== "\r") inStatement = true;
|
|
186
|
+
index++;
|
|
187
|
+
}
|
|
188
|
+
if (inStatement) statements++;
|
|
189
|
+
return statements === 1;
|
|
190
|
+
}
|
|
124
191
|
//#endregion
|
|
125
192
|
export { createMigrationHandler };
|
|
@@ -7,9 +7,9 @@ Then run:
|
|
|
7
7
|
|
|
8
8
|
npx void init
|
|
9
9
|
|
|
10
|
-
In an empty directory, `void init` will add the matching Pages adapter and starter dependencies after you choose a framework. In an existing app, it configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`.
|
|
10
|
+
In an empty directory, `void init` will add the matching Pages adapter and starter dependencies after you choose a scaffold toolchain and framework. Vite+ is the default toolchain. In an existing app, it configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`.
|
|
11
11
|
|
|
12
|
-
During `void init`,
|
|
12
|
+
During `void init`, choose Vite+ unless you specifically want plain Vite scripts, then choose a Pages framework (React, Vue, Svelte, or Solid). Choose D1 for a zero-config, fully managed default or PostgreSQL if you already have Postgres infrastructure or expect heavier writes and more complex queries.
|
|
13
13
|
|
|
14
14
|
At the end of the interactive flow, `void init` can also log you in and link or create your Void project so your first deploy is just:
|
|
15
15
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "void",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.13",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/voidzero-dev/void.git",
|
|
@@ -332,7 +332,7 @@
|
|
|
332
332
|
"valibot": ">=1.0.0-beta.7",
|
|
333
333
|
"vite": "^8.0.0",
|
|
334
334
|
"zod": "^3.25.0 || ^4.0.0",
|
|
335
|
-
"@void/md": "0.7.
|
|
335
|
+
"@void/md": "0.7.13"
|
|
336
336
|
},
|
|
337
337
|
"peerDependenciesMeta": {
|
|
338
338
|
"@void/md": {
|
package/skills/void/SKILL.md
CHANGED
|
@@ -13,7 +13,7 @@ Docs in this skill are bundled from `docs/` during `void` package build and live
|
|
|
13
13
|
|
|
14
14
|
## Command Naming
|
|
15
15
|
|
|
16
|
-
Use `void` in examples and commands in this skill. For first-time setup, prefer `void init` followed by `void deploy`; in an empty directory, install `void` first and let `void init` add the matching Pages adapter and starter dependencies. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. `void init` can also handle auth and project linking interactively.
|
|
16
|
+
Use `void` in examples and commands in this skill. For first-time setup, prefer `void init` followed by `void deploy`; in an empty directory, install `void` first and let `void init` add the matching Pages adapter and starter dependencies with Vite+ as the default scaffold toolchain. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. `void init` can also handle auth and project linking interactively.
|
|
17
17
|
|
|
18
18
|
Use `void` and `@void/*` in code examples and package manifests.
|
|
19
19
|
|
|
@@ -12,7 +12,7 @@ Pages mode is also entirely optional - you can use any client-side router to bui
|
|
|
12
12
|
|
|
13
13
|
## Setup
|
|
14
14
|
|
|
15
|
-
If you start in a scaffoldable empty directory, `void init` can generate this setup for you: it asks whether you want React, Vue, Svelte, or Solid Pages mode, then lets you pick a D1 starter, a PostgreSQL starter, or Static Pages. The database-backed starters write the adapter-aware `vite.config.ts`, `pages/`, and `db/` starter files; Static Pages writes just the basic `pages/` setup so you can grow into server features later.
|
|
15
|
+
If you start in a scaffoldable empty directory, `void init` can generate this setup for you: it asks whether to scaffold with Vite+ (the default) or plain Vite, asks whether you want React, Vue, Svelte, or Solid Pages mode, then lets you pick a D1 starter, a PostgreSQL starter, or Static Pages. The database-backed starters write the adapter-aware `vite.config.ts`, `pages/`, and `db/` starter files; Static Pages writes just the basic `pages/` setup so you can grow into server features later.
|
|
16
16
|
|
|
17
17
|
If you're adding Pages mode manually, install a framework adapter alongside `void`:
|
|
18
18
|
|
|
@@ -32,9 +32,9 @@ yarn add -D void
|
|
|
32
32
|
bun add -D void
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
In an empty directory, `void init` adds the matching Pages adapter and starter dependencies after you choose a framework.
|
|
35
|
+
In an empty directory, `void init` adds the matching Pages adapter and starter dependencies after you choose a scaffold toolchain and framework. Vite+ is the default toolchain and uses `vp` scripts.
|
|
36
36
|
|
|
37
|
-
As part of `void init`, you'll choose a Pages framework (React, Vue, Svelte, or Solid) and a starter type. D1 is the default top option and scaffolds a DB-backed page loader, schema, generated migration, `db/seed.ts`, and API route. PostgreSQL scaffolds the same starter and writes `"database": "pg"` to `void.json`. Static Pages skips the database and server starter files so you can start with static content and add Void features later.
|
|
37
|
+
As part of `void init`, you'll choose Vite+ or plain Vite, then a Pages framework (React, Vue, Svelte, or Solid) and a starter type. D1 is the default top option and scaffolds a DB-backed page loader, schema, generated migration, `db/seed.ts`, and API route. PostgreSQL scaffolds the same starter and writes `"database": "pg"` to `void.json`. Static Pages skips the database and server starter files so you can start with static content and add Void features later.
|
|
38
38
|
|
|
39
39
|
After installation, run the setup flow:
|
|
40
40
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## Void
|
|
2
2
|
|
|
3
|
-
This project uses [Void](https://void.cloud) — a fullstack Vite plugin + deployment platform for Cloudflare. `voidPlugin()` in `vite.config.ts` gives you file-based API routing on Hono (`routes/`), Inertia-inspired server-rendered pages with co-located loaders/actions (`pages/` + `@void/vue` or `@void/react`), auto-provisioned D1/KV/R2 bindings, first-class Drizzle ORM integration (schema in `db/schema.ts` -> `void/db` Drizzle instance -> typed routes -> typed fetch client), built-in auth, queues, cron jobs, edge caching (ISR), and one-command deploys via `npx void deploy`. For first-time setup, prefer `npx void init`; in an empty directory, install `void` first and let the interactive flow scaffold the starter, add the matching framework adapter, configure project files, handle auth, and link or create the deploy project before the first deploy. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. Use `void` and `@void/*` package names in source code and package manifests.
|
|
3
|
+
This project uses [Void](https://void.cloud) — a fullstack Vite plugin + deployment platform for Cloudflare. `voidPlugin()` in `vite.config.ts` gives you file-based API routing on Hono (`routes/`), Inertia-inspired server-rendered pages with co-located loaders/actions (`pages/` + `@void/vue` or `@void/react`), auto-provisioned D1/KV/R2 bindings, first-class Drizzle ORM integration (schema in `db/schema.ts` -> `void/db` Drizzle instance -> typed routes -> typed fetch client), built-in auth, queues, cron jobs, edge caching (ISR), and one-command deploys via `npx void deploy`. For first-time setup, prefer `npx void init`; in an empty directory, install `void` first and let the interactive flow scaffold the starter with Vite+ by default, add the matching framework adapter, configure project files, handle auth, and link or create the deploy project before the first deploy. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. Use `void` and `@void/*` package names in source code and package manifests.
|
|
4
4
|
|
|
5
5
|
Database: define Drizzle tables in `db/schema.ts`, import `db` from `void/db` and tables from `@schema`. Use `void db push` for prototyping, `void db generate` for production migrations. `drizzle-orm` and `drizzle-kit` ship with void (no extra install). Migrations live in `db/migrations/`.
|
|
6
6
|
|
|
@@ -21,7 +21,7 @@ Install the Void CLI:
|
|
|
21
21
|
npm install -D void
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
If you run `void init` in an empty directory, the scaffold flow adds the matching Pages adapter and Vite
|
|
24
|
+
If you run `void init` in an empty directory, the scaffold flow adds the matching Pages adapter and Vite+ by default.
|
|
25
25
|
|
|
26
26
|
Then run `npx void init`. In an empty directory, the full interactive flow can scaffold a starter, configure local project files, log you in, and link or create your Void project so the next step is just `npx void deploy`. In an existing app, it configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`.
|
|
27
27
|
|
|
@@ -82,7 +82,7 @@ There is Vinext-specific source in the repo, but it is intentionally on hold and
|
|
|
82
82
|
|
|
83
83
|
## Documentation
|
|
84
84
|
|
|
85
|
-
Full docs live at [void.cloud/
|
|
85
|
+
Full docs live at [void.cloud/guide](https://void.cloud/guide).
|
|
86
86
|
|
|
87
87
|
## License
|
|
88
88
|
|
|
@@ -13,7 +13,7 @@ Docs in this skill are bundled from `docs/` during `void` package build and live
|
|
|
13
13
|
|
|
14
14
|
## Command Naming
|
|
15
15
|
|
|
16
|
-
Use `void` in examples and commands in this skill. For first-time setup, prefer `void init` followed by `void deploy`; in an empty directory, install `void` first and let `void init` add the matching Pages adapter and starter dependencies. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. `void init` can also handle auth and project linking interactively.
|
|
16
|
+
Use `void` in examples and commands in this skill. For first-time setup, prefer `void init` followed by `void deploy`; in an empty directory, install `void` first and let `void init` add the matching Pages adapter and starter dependencies with Vite+ as the default scaffold toolchain. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. `void init` can also handle auth and project linking interactively.
|
|
17
17
|
|
|
18
18
|
Use `void` and `@void/*` in code examples and package manifests.
|
|
19
19
|
|
|
@@ -66,7 +66,9 @@ void init [--tsconfig] [--github] [--agents]
|
|
|
66
66
|
|
|
67
67
|
Setup wizard for Void projects (new or existing).
|
|
68
68
|
|
|
69
|
-
If run in a scaffoldable empty directory, `void init` scaffolds a Pages starter. If a single Pages adapter is already installed, it reuses that framework; otherwise it asks which framework to scaffold (React, Vue, Svelte, or Solid). It then asks which starter you want: D1, PostgreSQL, or Static Pages. The D1 and PostgreSQL starters write a framework-specific `vite.config.ts`, a `pages/` home page plus `.server.ts` loader, `db/schema.ts`, `db/seed.ts`, a generated initial migration under `db/migrations/`, and `routes/api/hello.ts`. The Static Pages starter writes just the framework-specific `vite.config.ts` and a `pages/` home page so you can add server features later.
|
|
69
|
+
If run in a scaffoldable empty directory, `void init` scaffolds a Pages starter. It asks which scaffold toolchain to use, with Vite+ as the default top option and plain Vite as the alternative. If a single Pages adapter is already installed, it reuses that framework; otherwise it asks which framework to scaffold (React, Vue, Svelte, or Solid). It then asks which starter you want: D1, PostgreSQL, or Static Pages. The D1 and PostgreSQL starters write a framework-specific `vite.config.ts`, a `pages/` home page plus `.server.ts` loader, `db/schema.ts`, `db/seed.ts`, a generated initial migration under `db/migrations/`, and `routes/api/hello.ts`. The Static Pages starter writes just the framework-specific `vite.config.ts` and a `pages/` home page so you can add server features later. Vite+ starters add `vite-plus` and use `vp dev`, `vp build`, and `vp preview` scripts.
|
|
70
|
+
|
|
71
|
+
If run in a non-empty folder that does not look like an app yet, such as a parent `Projects/` folder with subdirectories but no `package.json`, `void init` asks whether to create a new subfolder or continue in the current folder. Creating a subfolder is the default selection.
|
|
70
72
|
|
|
71
73
|
If run in an existing project, `void init` configures the project in place: it ensures `void` and `vite` are declared, adds missing `dev` (`vite`) and `build` (`vite build`) scripts without overwriting existing scripts, and creates or patches `vite.config.*` with `voidPlugin()` when the config shape is safe to edit. If the Vite config is too dynamic to patch confidently, it prints the manual snippet instead of rewriting it.
|
|
72
74
|
|