+14
ROADMAP.md
+14
ROADMAP.md
···
255
255
256
256
## Parking Lot
257
257
258
+
### IIFE Build Support
259
+
260
+
Provide an IIFE (Immediately Invoked Function Expression) build target for VoltX.js to support direct `<script>` tag usage without module systems.
261
+
262
+
**Deliverables:**
263
+
264
+
- IIFE build output (voltx.iife.js) alongside ESM build
265
+
- Global `Volt` namespace for browser environments
266
+
- CDN-friendly distribution (unpkg, jsdelivr)
267
+
- Update build pipeline to generate IIFE bundle
268
+
- Document usage: `<script src="voltx.iife.min.js"></script>`
269
+
- Ensure plugins work with IIFE build
270
+
- Add IIFE examples to documentation
271
+
258
272
### Evaluator & Binder Hardening
259
273
260
274
All expression evaluation now flows through a cached `new Function` compiler guarded by a hardened scope proxy, with the binder slimmed into a directive registry so plugins self-register while tests verify the sandboxed error surfaces.
+83
cli/README.md
+83
cli/README.md
···
1
+
# create-voltx
2
+
3
+
CLI for creating and managing VoltX.js applications.
4
+
5
+
## Usage
6
+
7
+
### Create a New Project
8
+
9
+
```bash
10
+
# Using pnpm (recommended)
11
+
pnpm create voltx my-app
12
+
13
+
# Using npm
14
+
npm create voltx@latest my-app
15
+
16
+
# Using npx
17
+
npx create-voltx my-app
18
+
```
19
+
20
+
### Commands
21
+
22
+
#### `init [project-name]`
23
+
24
+
Create a new VoltX.js project with interactive template selection.
25
+
26
+
```bash
27
+
voltx init my-app
28
+
```
29
+
30
+
**Templates:**
31
+
32
+
- **Minimal** - Basic VoltX.js app with counter
33
+
- **With Router** - Multi-page app with routing
34
+
- **With Plugins** - All plugins demo
35
+
- **Styles Only** - Just HTML + CSS, no framework
36
+
37
+
#### `dev`
38
+
39
+
Start Vite development server.
40
+
41
+
```bash
42
+
voltx dev [--port 3000] [--open]
43
+
```
44
+
45
+
#### `build`
46
+
47
+
Build project for production.
48
+
49
+
```bash
50
+
voltx build [--out dist]
51
+
```
52
+
53
+
#### `download`
54
+
55
+
Download VoltX.js assets from CDN.
56
+
57
+
```bash
58
+
voltx download [--version latest] [--output .]
59
+
```
60
+
61
+
## Documentation
62
+
63
+
See the [CLI Guide](https://stormlightlabs.github.io/volt/cli) for complete documentation.
64
+
65
+
## Development
66
+
67
+
```bash
68
+
# Install dependencies
69
+
pnpm install
70
+
71
+
# Build CLI
72
+
pnpm build
73
+
74
+
# Run tests
75
+
pnpm test
76
+
77
+
# Type check
78
+
pnpm typecheck
79
+
```
80
+
81
+
## License
82
+
83
+
MIT
+25
cli/package.json
+25
cli/package.json
···
1
+
{
2
+
"name": "create-voltx",
3
+
"version": "0.1.0",
4
+
"description": "CLI for creating and managing VoltX.js applications",
5
+
"type": "module",
6
+
"author": "Owais Jamil",
7
+
"license": "MIT",
8
+
"repository": { "type": "git", "url": "https://github.com/stormlightlabs/volt.git", "directory": "cli" },
9
+
"bin": { "create-voltx": "./dist/index.js", "voltx": "./dist/index.js" },
10
+
"files": ["dist", "templates"],
11
+
"main": "./dist/index.js",
12
+
"module": "./dist/index.js",
13
+
"types": "./dist/index.d.ts",
14
+
"exports": { ".": "./dist/index.js", "./package.json": "./package.json" },
15
+
"scripts": {
16
+
"build": "tsdown",
17
+
"dev": "tsdown --watch",
18
+
"test": "vitest",
19
+
"test:run": "vitest run",
20
+
"typecheck": "tsc --noEmit"
21
+
},
22
+
"devDependencies": { "tsdown": "^0.15.6", "@vitest/coverage-v8": "^3.2.4" },
23
+
"dependencies": { "chalk": "^5.6.2", "commander": "^14.0.1", "@inquirer/prompts": "^8.0.1" },
24
+
"keywords": ["voltx", "reactive", "framework", "cli", "scaffold", "create"]
25
+
}
+39
cli/src/commands/build.ts
+39
cli/src/commands/build.ts
···
1
+
import { echo } from "$utils/echo.js";
2
+
import { spawn } from "node:child_process";
3
+
4
+
/**
5
+
* Builds the VoltX.js project for production using Vite.
6
+
*/
7
+
export async function buildCommand(options: { outDir?: string } = {}): Promise<void> {
8
+
const outDir = options.outDir || "dist";
9
+
10
+
echo.title("\n⚡ Building VoltX.js project for production...\n");
11
+
12
+
try {
13
+
const { existsSync } = await import("node:fs");
14
+
if (!existsSync("index.html")) {
15
+
echo.warn("Warning: No index.html found in current directory");
16
+
echo.info("Are you in a VoltX.js project?\n");
17
+
}
18
+
19
+
const viteArgs = ["vite", "build", "--outDir", outDir];
20
+
const viteProcess = spawn("npx", viteArgs, { stdio: "inherit", shell: true });
21
+
22
+
viteProcess.on("error", (error) => {
23
+
echo.err("Failed to build project:", error);
24
+
process.exit(1);
25
+
});
26
+
27
+
viteProcess.on("exit", (code) => {
28
+
if (code === 0) {
29
+
echo.success(`\n✓ Build completed successfully!\n`);
30
+
echo.info(`Output directory: ${outDir}\n`);
31
+
} else if (code !== null) {
32
+
process.exit(code);
33
+
}
34
+
});
35
+
} catch (error) {
36
+
echo.err("Error building project:", error);
37
+
process.exit(1);
38
+
}
39
+
}
+47
cli/src/commands/dev.ts
+47
cli/src/commands/dev.ts
···
1
+
import { echo } from "$utils/echo.js";
2
+
import { spawn } from "node:child_process";
3
+
4
+
/**
5
+
* Starts a Vite development server for the current project.
6
+
*/
7
+
export async function devCommand(options: { port?: number; open?: boolean } = {}): Promise<void> {
8
+
const port = options.port || 3000;
9
+
const shouldOpen = options.open || false;
10
+
11
+
echo.title("\n⚡ Starting VoltX.js development server...\n");
12
+
13
+
try {
14
+
const { existsSync } = await import("node:fs");
15
+
if (!existsSync("index.html")) {
16
+
echo.warn("Warning: No index.html found in current directory");
17
+
echo.info("Are you in a VoltX.js project?\n");
18
+
}
19
+
20
+
const viteArgs = ["vite", "--port", port.toString(), "--host"];
21
+
22
+
if (shouldOpen) {
23
+
viteArgs.push("--open");
24
+
}
25
+
26
+
const viteProcess = spawn("npx", viteArgs, { stdio: "inherit", shell: true });
27
+
28
+
viteProcess.on("error", (error) => {
29
+
echo.err("Failed to start dev server:", error);
30
+
process.exit(1);
31
+
});
32
+
33
+
viteProcess.on("exit", (code) => {
34
+
if (code !== 0 && code !== null) {
35
+
process.exit(code);
36
+
}
37
+
});
38
+
39
+
process.on("SIGINT", () => {
40
+
viteProcess.kill("SIGINT");
41
+
process.exit(0);
42
+
});
43
+
} catch (error) {
44
+
echo.err("Error starting dev server:", error);
45
+
process.exit(1);
46
+
}
47
+
}
+40
cli/src/commands/download.ts
+40
cli/src/commands/download.ts
···
1
+
import { downloadFile, getCDNUrls } from "$utils/download.js";
2
+
import { echo } from "$utils/echo.js";
3
+
import path from "node:path";
4
+
5
+
/**
6
+
* Downloads VoltX.js assets (JS and/or CSS) from the CDN.
7
+
*/
8
+
export async function downloadCommand(
9
+
options: { version?: string; js?: boolean; css?: boolean; output?: string } = {},
10
+
): Promise<void> {
11
+
const version = options.version || "latest";
12
+
const downloadJS = options.js !== false;
13
+
const downloadCSS = options.css !== false;
14
+
const outputDir = options.output || ".";
15
+
16
+
echo.title("\n⚡ Downloading VoltX.js assets...\n");
17
+
18
+
try {
19
+
const urls = getCDNUrls(version);
20
+
21
+
if (downloadJS) {
22
+
const jsPath = path.join(outputDir, "voltx.min.js");
23
+
echo.info(`Downloading voltx.min.js (${version})...`);
24
+
await downloadFile(urls.js, jsPath);
25
+
echo.ok(`✓ Downloaded: ${jsPath}`);
26
+
}
27
+
28
+
if (downloadCSS) {
29
+
const cssPath = path.join(outputDir, "voltx.min.css");
30
+
echo.info(`Downloading voltx.min.css (${version})...`);
31
+
await downloadFile(urls.css, cssPath);
32
+
echo.ok(`✓ Downloaded: ${cssPath}`);
33
+
}
34
+
35
+
echo.success("\n✓ Download completed successfully!\n");
36
+
} catch (error) {
37
+
echo.err("Failed to download assets:", error);
38
+
process.exit(1);
39
+
}
40
+
}
+156
cli/src/commands/init.ts
+156
cli/src/commands/init.ts
···
1
+
import {
2
+
generateMinimalCSS,
3
+
generateMinimalHTML,
4
+
generateMinimalPackageJSON,
5
+
generateMinimalREADME,
6
+
} from "$templates/minimal.js";
7
+
import {
8
+
generateStylesCSS,
9
+
generateStylesHTML,
10
+
generateStylesPackageJSON,
11
+
generateStylesREADME,
12
+
} from "$templates/styles.js";
13
+
import {
14
+
generatePluginsCSS,
15
+
generatePluginsHTML,
16
+
generatePluginsPackageJSON,
17
+
generatePluginsREADME,
18
+
} from "$templates/with-plugins.js";
19
+
import {
20
+
generateRouterCSS,
21
+
generateRouterHTML,
22
+
generateRouterPackageJSON,
23
+
generateRouterREADME,
24
+
} from "$templates/with-router.js";
25
+
import { downloadFile, getCDNUrls } from "$utils/download.js";
26
+
import { echo } from "$utils/echo.js";
27
+
import { createFile, isEmptyOrMissing } from "$utils/files.js";
28
+
import { input, select } from "@inquirer/prompts";
29
+
import path from "node:path";
30
+
31
+
type Template = "minimal" | "with-router" | "with-plugins" | "styles";
32
+
33
+
/**
34
+
* Download VoltX.js assets to the project directory.
35
+
*/
36
+
async function downloadAssets(projectDir: string, template: Template): Promise<void> {
37
+
const urls = getCDNUrls();
38
+
39
+
echo.info("Downloading VoltX.js assets...");
40
+
41
+
const cssPath = path.join(projectDir, "voltx.min.css");
42
+
await downloadFile(urls.css, cssPath);
43
+
echo.ok(` Downloaded: voltx.min.css`);
44
+
45
+
if (template !== "styles") {
46
+
const jsPath = path.join(projectDir, "voltx.min.js");
47
+
await downloadFile(urls.js, jsPath);
48
+
echo.ok(` Downloaded: voltx.min.js`);
49
+
}
50
+
}
51
+
52
+
/**
53
+
* Generate project files based on the selected template.
54
+
*/
55
+
async function generateProjectFiles(projectDir: string, projectName: string, template: Template): Promise<void> {
56
+
echo.info("Generating project files...");
57
+
58
+
let htmlContent: string;
59
+
let cssContent: string;
60
+
let packageJsonContent: string;
61
+
let readmeContent: string;
62
+
63
+
switch (template) {
64
+
case "minimal":
65
+
htmlContent = generateMinimalHTML(projectName);
66
+
cssContent = generateMinimalCSS();
67
+
packageJsonContent = generateMinimalPackageJSON(projectName);
68
+
readmeContent = generateMinimalREADME(projectName);
69
+
break;
70
+
71
+
case "styles":
72
+
htmlContent = generateStylesHTML(projectName);
73
+
cssContent = generateStylesCSS();
74
+
packageJsonContent = generateStylesPackageJSON(projectName);
75
+
readmeContent = generateStylesREADME(projectName);
76
+
break;
77
+
78
+
case "with-router":
79
+
htmlContent = generateRouterHTML(projectName);
80
+
cssContent = generateRouterCSS();
81
+
packageJsonContent = generateRouterPackageJSON(projectName);
82
+
readmeContent = generateRouterREADME(projectName);
83
+
break;
84
+
85
+
case "with-plugins":
86
+
htmlContent = generatePluginsHTML(projectName);
87
+
cssContent = generatePluginsCSS();
88
+
packageJsonContent = generatePluginsPackageJSON(projectName);
89
+
readmeContent = generatePluginsREADME(projectName);
90
+
break;
91
+
}
92
+
93
+
await createFile(path.join(projectDir, "index.html"), htmlContent);
94
+
echo.ok(` Created: index.html`);
95
+
96
+
await createFile(path.join(projectDir, "styles.css"), cssContent);
97
+
echo.ok(` Created: styles.css`);
98
+
99
+
await createFile(path.join(projectDir, "package.json"), packageJsonContent);
100
+
echo.ok(` Created: package.json`);
101
+
102
+
await createFile(path.join(projectDir, "README.md"), readmeContent);
103
+
echo.ok(` Created: README.md`);
104
+
}
105
+
106
+
/**
107
+
* Init command implementation.
108
+
*
109
+
* Creates a new VoltX.js project with the selected template.
110
+
*/
111
+
export async function initCommand(projectName?: string): Promise<void> {
112
+
echo.title("\n⚡ Create VoltX.js App\n");
113
+
114
+
if (!projectName) {
115
+
projectName = await input({ message: "Project name:", default: "my-voltx-app" });
116
+
117
+
if (!projectName) {
118
+
echo.err("Project name is required");
119
+
process.exit(1);
120
+
}
121
+
}
122
+
123
+
const projectDir = path.resolve(process.cwd(), projectName);
124
+
125
+
if (!(await isEmptyOrMissing(projectDir))) {
126
+
echo.err(`Directory ${projectName} already exists and is not empty`);
127
+
process.exit(1);
128
+
}
129
+
130
+
const template = await select<Template>({
131
+
message: "Select a template:",
132
+
choices: [
133
+
{ name: "Minimal", value: "minimal", description: "Basic VoltX.js app with counter" },
134
+
{ name: "With Router", value: "with-router", description: "Multi-page app with routing" },
135
+
{ name: "With Plugins", value: "with-plugins", description: "All plugins demo" },
136
+
{ name: "Styles Only", value: "styles", description: "Just HTML + CSS, no framework" },
137
+
],
138
+
});
139
+
140
+
try {
141
+
echo.text("");
142
+
await generateProjectFiles(projectDir, projectName, template);
143
+
144
+
echo.text("");
145
+
await downloadAssets(projectDir, template);
146
+
147
+
echo.success(`\n✓ Project created successfully!\n`);
148
+
echo.info(`Next steps:\n`);
149
+
echo.text(` cd ${projectName}`);
150
+
echo.text(` pnpm install`);
151
+
echo.text(` pnpm dev\n`);
152
+
} catch (error) {
153
+
echo.err("Failed to create project:", error);
154
+
process.exit(1);
155
+
}
156
+
}
+82
cli/src/index.ts
+82
cli/src/index.ts
···
1
+
#!/usr/bin/env node
2
+
/* eslint-disable unicorn/no-process-exit */
3
+
import { buildCommand } from "$commands/build.js";
4
+
import { devCommand } from "$commands/dev.js";
5
+
import { downloadCommand } from "$commands/download.js";
6
+
import { initCommand } from "$commands/init.js";
7
+
import { echo } from "$utils/echo.js";
8
+
import { Command } from "commander";
9
+
10
+
const program = new Command();
11
+
const isCreateMode = process.argv[1]?.includes("create-voltx");
12
+
13
+
program.name(isCreateMode ? "create-voltx" : "voltx").description("CLI for creating and managing VoltX.js applications")
14
+
.version("0.1.0");
15
+
16
+
program.command("init [project-name]").description("Create a new VoltX.js project").action(
17
+
async (projectName: string | undefined) => {
18
+
try {
19
+
await initCommand(projectName);
20
+
} catch (error) {
21
+
echo.err("Error creating project:", error);
22
+
process.exit(1);
23
+
}
24
+
},
25
+
);
26
+
27
+
program.command("dev").description("Start development server").option(
28
+
"-p, --port <port>",
29
+
"Port to run the dev server on",
30
+
"3000",
31
+
).option("-o, --open", "Open browser automatically", false).action(
32
+
async (options: { port?: string; open?: boolean }) => {
33
+
try {
34
+
const port = options.port ? Number.parseInt(options.port, 10) : 3000;
35
+
await devCommand({ port, open: options.open });
36
+
} catch (error) {
37
+
echo.err("Error starting dev server:", error);
38
+
process.exit(1);
39
+
}
40
+
},
41
+
);
42
+
43
+
program.command("build").description("Build project for production").option("--out <dir>", "Output directory", "dist")
44
+
.action(async (options: { out?: string }) => {
45
+
try {
46
+
await buildCommand({ outDir: options.out });
47
+
} catch (error) {
48
+
echo.err("Error building project:", error);
49
+
process.exit(1);
50
+
}
51
+
});
52
+
53
+
program.command("download").description("Download VoltX.js assets (JS and CSS)").option(
54
+
"--version <version>",
55
+
"VoltX.js version to download",
56
+
"latest",
57
+
).option("--no-js", "Skip downloading JavaScript file").option("--no-css", "Skip downloading CSS file").option(
58
+
"-o, --output <dir>",
59
+
"Output directory",
60
+
".",
61
+
).action(async (options: { version?: string; js?: boolean; css?: boolean; output?: string }) => {
62
+
try {
63
+
await downloadCommand(options);
64
+
} catch (error) {
65
+
echo.err("Error downloading assets:", error);
66
+
process.exit(1);
67
+
}
68
+
});
69
+
70
+
if (isCreateMode && process.argv.length === 2) {
71
+
initCommand().catch((error) => {
72
+
echo.err("Error:", error);
73
+
process.exit(1);
74
+
});
75
+
} else if (isCreateMode && process.argv.length === 3 && !process.argv[2]?.startsWith("-")) {
76
+
initCommand(process.argv[2]).catch((error) => {
77
+
echo.err("Error:", error);
78
+
process.exit(1);
79
+
});
80
+
} else {
81
+
program.parse();
82
+
}
+149
cli/src/templates/minimal.ts
+149
cli/src/templates/minimal.ts
···
1
+
/**
2
+
* Generate a minimal VoltX.js project with declarative mode.
3
+
*/
4
+
export function generateMinimalHTML(projectName: string): string {
5
+
return `<!DOCTYPE html>
6
+
<html lang="en">
7
+
<head>
8
+
<meta charset="UTF-8">
9
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
10
+
<title>${projectName}</title>
11
+
<link rel="stylesheet" href="voltx.min.css">
12
+
<link rel="stylesheet" href="styles.css">
13
+
</head>
14
+
<body>
15
+
<div data-volt data-volt-state='{"count": 0, "message": "Hello VoltX!"}'>
16
+
<div class="container">
17
+
<h1 data-volt-text="message"></h1>
18
+
19
+
<div class="counter">
20
+
<button data-volt-on-click="count.set(count.get() - 1)">-</button>
21
+
<span data-volt-text="count"></span>
22
+
<button data-volt-on-click="count.set(count.get() + 1)">+</button>
23
+
</div>
24
+
25
+
<p class="hint">Edit this file to start building your app!</p>
26
+
</div>
27
+
</div>
28
+
29
+
<script type="module">
30
+
import { charge, registerPlugin, persistPlugin } from './voltx.min.js';
31
+
32
+
registerPlugin('persist', persistPlugin);
33
+
charge();
34
+
</script>
35
+
</body>
36
+
</html>
37
+
`;
38
+
}
39
+
40
+
export function generateMinimalCSS(): string {
41
+
return `/* Custom styles for your VoltX.js app */
42
+
43
+
body {
44
+
margin: 0;
45
+
font-family: system-ui, -apple-system, sans-serif;
46
+
background: #f5f5f5;
47
+
color: #333;
48
+
}
49
+
50
+
.container {
51
+
max-width: 600px;
52
+
margin: 4rem auto;
53
+
padding: 2rem;
54
+
background: white;
55
+
border-radius: 8px;
56
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
57
+
text-align: center;
58
+
}
59
+
60
+
h1 {
61
+
margin: 0 0 2rem;
62
+
color: #2563eb;
63
+
}
64
+
65
+
.counter {
66
+
display: flex;
67
+
gap: 1rem;
68
+
justify-content: center;
69
+
align-items: center;
70
+
margin: 2rem 0;
71
+
}
72
+
73
+
.counter button {
74
+
width: 48px;
75
+
height: 48px;
76
+
font-size: 1.5rem;
77
+
border: 2px solid #2563eb;
78
+
background: white;
79
+
color: #2563eb;
80
+
border-radius: 8px;
81
+
cursor: pointer;
82
+
transition: all 0.2s;
83
+
}
84
+
85
+
.counter button:hover {
86
+
background: #2563eb;
87
+
color: white;
88
+
}
89
+
90
+
.counter span {
91
+
font-size: 2rem;
92
+
font-weight: bold;
93
+
min-width: 60px;
94
+
}
95
+
96
+
.hint {
97
+
margin-top: 2rem;
98
+
color: #666;
99
+
font-size: 0.9rem;
100
+
}
101
+
`;
102
+
}
103
+
104
+
export function generateMinimalPackageJSON(projectName: string): string {
105
+
return JSON.stringify(
106
+
{
107
+
name: projectName,
108
+
version: "0.1.0",
109
+
type: "module",
110
+
scripts: { dev: "voltx dev", build: "voltx build" },
111
+
devDependencies: { "create-voltx": "^0.1.0" },
112
+
},
113
+
null,
114
+
2,
115
+
);
116
+
}
117
+
118
+
export function generateMinimalREADME(projectName: string): string {
119
+
return `# ${projectName}
120
+
121
+
A minimal VoltX.js application.
122
+
123
+
## Getting Started
124
+
125
+
1. Install dependencies:
126
+
\`\`\`bash
127
+
pnpm install
128
+
\`\`\`
129
+
130
+
2. Start the development server:
131
+
\`\`\`bash
132
+
pnpm dev
133
+
\`\`\`
134
+
135
+
3. Open your browser to the URL shown in the terminal.
136
+
137
+
## Project Structure
138
+
139
+
- \`index.html\` - Main HTML file with declarative VoltX.js markup
140
+
- \`styles.css\` - Custom styles
141
+
- \`voltx.min.js\` - VoltX.js framework (ES module)
142
+
- \`voltx.min.css\` - VoltX.js base styles
143
+
144
+
## Learn More
145
+
146
+
- [VoltX.js Documentation](https://stormlightlabs.github.io/volt)
147
+
- [API Reference](https://stormlightlabs.github.io/volt/api)
148
+
`;
149
+
}
+194
cli/src/templates/styles.ts
+194
cli/src/templates/styles.ts
···
1
+
/**
2
+
* Generate a styles-only project with VoltX.js CSS but no JavaScript framework.
3
+
*/
4
+
export function generateStylesHTML(projectName: string): string {
5
+
return `<!DOCTYPE html>
6
+
<html lang="en">
7
+
<head>
8
+
<meta charset="UTF-8">
9
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
10
+
<title>${projectName}</title>
11
+
<link rel="stylesheet" href="voltx.min.css">
12
+
<link rel="stylesheet" href="styles.css">
13
+
</head>
14
+
<body>
15
+
<div class="container">
16
+
<h1>Welcome to ${projectName}</h1>
17
+
18
+
<div class="card">
19
+
<h2>VoltX.js CSS</h2>
20
+
<p>This project uses VoltX.js CSS utility classes without the reactive framework.</p>
21
+
<p>Style your HTML with utility classes and semantic CSS variables.</p>
22
+
</div>
23
+
24
+
<div class="button-group">
25
+
<button class="btn btn-primary">Primary</button>
26
+
<button class="btn btn-secondary">Secondary</button>
27
+
<button class="btn btn-danger">Danger</button>
28
+
</div>
29
+
30
+
<div class="grid">
31
+
<div class="card">
32
+
<h3>Card 1</h3>
33
+
<p>Build with semantic CSS.</p>
34
+
</div>
35
+
<div class="card">
36
+
<h3>Card 2</h3>
37
+
<p>No JavaScript required.</p>
38
+
</div>
39
+
<div class="card">
40
+
<h3>Card 3</h3>
41
+
<p>Just HTML and CSS.</p>
42
+
</div>
43
+
</div>
44
+
</div>
45
+
</body>
46
+
</html>
47
+
`;
48
+
}
49
+
50
+
export function generateStylesCSS(): string {
51
+
return `/* Custom styles for your project */
52
+
53
+
body {
54
+
margin: 0;
55
+
font-family: system-ui, -apple-system, sans-serif;
56
+
background: #f5f5f5;
57
+
color: #333;
58
+
}
59
+
60
+
.container {
61
+
max-width: 900px;
62
+
margin: 0 auto;
63
+
padding: 2rem;
64
+
}
65
+
66
+
h1 {
67
+
text-align: center;
68
+
margin-bottom: 3rem;
69
+
color: #2563eb;
70
+
}
71
+
72
+
.card {
73
+
background: white;
74
+
padding: 1.5rem;
75
+
border-radius: 8px;
76
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
77
+
margin-bottom: 2rem;
78
+
}
79
+
80
+
.card h2,
81
+
.card h3 {
82
+
margin-top: 0;
83
+
color: #1e40af;
84
+
}
85
+
86
+
.button-group {
87
+
display: flex;
88
+
gap: 1rem;
89
+
justify-content: center;
90
+
margin: 2rem 0;
91
+
}
92
+
93
+
.btn {
94
+
padding: 0.75rem 1.5rem;
95
+
border: none;
96
+
border-radius: 6px;
97
+
font-size: 1rem;
98
+
font-weight: 500;
99
+
cursor: pointer;
100
+
transition: all 0.2s;
101
+
}
102
+
103
+
.btn-primary {
104
+
background: #2563eb;
105
+
color: white;
106
+
}
107
+
108
+
.btn-primary:hover {
109
+
background: #1e40af;
110
+
}
111
+
112
+
.btn-secondary {
113
+
background: #64748b;
114
+
color: white;
115
+
}
116
+
117
+
.btn-secondary:hover {
118
+
background: #475569;
119
+
}
120
+
121
+
.btn-danger {
122
+
background: #dc2626;
123
+
color: white;
124
+
}
125
+
126
+
.btn-danger:hover {
127
+
background: #b91c1c;
128
+
}
129
+
130
+
.grid {
131
+
display: grid;
132
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
133
+
gap: 1.5rem;
134
+
margin-top: 2rem;
135
+
}
136
+
137
+
.grid .card {
138
+
margin-bottom: 0;
139
+
}
140
+
`;
141
+
}
142
+
143
+
export function generateStylesPackageJSON(projectName: string): string {
144
+
return JSON.stringify(
145
+
{
146
+
name: projectName,
147
+
version: "0.1.0",
148
+
scripts: { dev: "voltx dev", build: "voltx build" },
149
+
devDependencies: { "create-voltx": "^0.1.0" },
150
+
},
151
+
null,
152
+
2,
153
+
);
154
+
}
155
+
156
+
export function generateStylesREADME(projectName: string): string {
157
+
return `# ${projectName}
158
+
159
+
A styles-only project using VoltX.js CSS utilities.
160
+
161
+
## Getting Started
162
+
163
+
1. Install dependencies:
164
+
\`\`\`bash
165
+
pnpm install
166
+
\`\`\`
167
+
168
+
2. Start the development server:
169
+
\`\`\`bash
170
+
pnpm dev
171
+
\`\`\`
172
+
173
+
3. Open your browser to the URL shown in the terminal.
174
+
175
+
## What's Included
176
+
177
+
This project uses VoltX.js CSS for styling without the reactive framework:
178
+
179
+
- Utility classes for common patterns
180
+
- CSS custom properties for theming
181
+
- Semantic HTML with clean CSS
182
+
183
+
## Adding VoltX.js Reactivity
184
+
185
+
To add reactivity to this project later:
186
+
187
+
\`\`\`bash
188
+
# Add data-volt attributes to your HTML
189
+
# Import and initialize VoltX.js in a script tag
190
+
\`\`\`
191
+
192
+
See the [VoltX.js Documentation](https://stormlightlabs.github.io/volt) for more information.
193
+
`;
194
+
}
+278
cli/src/templates/with-plugins.ts
+278
cli/src/templates/with-plugins.ts
···
1
+
/**
2
+
* Generate a VoltX.js project with all plugins pre-registered.
3
+
*/
4
+
export function generatePluginsHTML(projectName: string): string {
5
+
return `<!DOCTYPE html>
6
+
<html lang="en">
7
+
<head>
8
+
<meta charset="UTF-8">
9
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
10
+
<title>${projectName}</title>
11
+
<link rel="stylesheet" href="voltx.min.css">
12
+
<link rel="stylesheet" href="styles.css">
13
+
</head>
14
+
<body>
15
+
<div data-volt data-volt-state='{"count": 0, "message": "Hello VoltX!"}'>
16
+
<div class="container">
17
+
<h1 data-volt-text="message"></h1>
18
+
19
+
<!-- Persist plugin demo -->
20
+
<div class="card">
21
+
<h2>Persist Plugin</h2>
22
+
<p>This counter persists to localStorage:</p>
23
+
<div class="counter">
24
+
<button data-volt-on-click="count.set(count.get() - 1)">-</button>
25
+
<span data-volt-text="count" data-volt-persist="count"></span>
26
+
<button data-volt-on-click="count.set(count.get() + 1)">+</button>
27
+
</div>
28
+
<p class="hint">Refresh the page - your count is saved!</p>
29
+
</div>
30
+
31
+
<!-- Scroll plugin demo -->
32
+
<div class="card">
33
+
<h2>Scroll Plugin</h2>
34
+
<p>Smooth scroll to sections:</p>
35
+
<button data-volt-scroll-to="#section-1">Scroll to Section 1</button>
36
+
<button data-volt-scroll-to="#section-2">Scroll to Section 2</button>
37
+
</div>
38
+
39
+
<!-- URL plugin demo -->
40
+
<div class="card">
41
+
<h2>URL Plugin</h2>
42
+
<p>State synced with URL params:</p>
43
+
<input type="text" data-volt-model="message" data-volt-url-param="msg">
44
+
<p class="hint">Your message is in the URL!</p>
45
+
</div>
46
+
47
+
<!-- Surge plugin demo -->
48
+
<div class="card">
49
+
<h2>Surge Plugin (Animations)</h2>
50
+
<button data-volt-on-click="$scope.toggle('showBox', !$scope.get('showBox'))">
51
+
Toggle Box
52
+
</button>
53
+
<div
54
+
data-volt-if="$scope.get('showBox')"
55
+
data-volt-surge="fade"
56
+
class="animated-box"
57
+
>
58
+
I fade in and out!
59
+
</div>
60
+
</div>
61
+
62
+
<div id="section-1" class="section">
63
+
<h3>Section 1</h3>
64
+
<p>This is a scrollable section.</p>
65
+
</div>
66
+
67
+
<div id="section-2" class="section">
68
+
<h3>Section 2</h3>
69
+
<p>Scroll here with smooth animations!</p>
70
+
</div>
71
+
</div>
72
+
</div>
73
+
74
+
<script type="module">
75
+
import {
76
+
charge,
77
+
registerPlugin,
78
+
persistPlugin,
79
+
scrollPlugin,
80
+
urlPlugin,
81
+
surgePlugin,
82
+
navigatePlugin
83
+
} from './voltx.min.js';
84
+
85
+
// Register all plugins
86
+
registerPlugin('persist', persistPlugin);
87
+
registerPlugin('scroll', scrollPlugin);
88
+
registerPlugin('url', urlPlugin);
89
+
registerPlugin('surge', surgePlugin);
90
+
registerPlugin('navigate', navigatePlugin);
91
+
92
+
charge();
93
+
</script>
94
+
</body>
95
+
</html>
96
+
`;
97
+
}
98
+
99
+
export function generatePluginsCSS(): string {
100
+
return `/* Custom styles for your VoltX.js app */
101
+
102
+
body {
103
+
margin: 0;
104
+
font-family: system-ui, -apple-system, sans-serif;
105
+
background: #f5f5f5;
106
+
color: #333;
107
+
}
108
+
109
+
.container {
110
+
max-width: 900px;
111
+
margin: 2rem auto;
112
+
padding: 2rem;
113
+
}
114
+
115
+
h1 {
116
+
text-align: center;
117
+
margin-bottom: 3rem;
118
+
color: #2563eb;
119
+
}
120
+
121
+
.card {
122
+
background: white;
123
+
padding: 2rem;
124
+
border-radius: 8px;
125
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
126
+
margin-bottom: 2rem;
127
+
}
128
+
129
+
.card h2 {
130
+
margin-top: 0;
131
+
color: #1e40af;
132
+
}
133
+
134
+
.counter {
135
+
display: flex;
136
+
gap: 1rem;
137
+
justify-content: center;
138
+
align-items: center;
139
+
margin: 1.5rem 0;
140
+
}
141
+
142
+
.counter button {
143
+
width: 48px;
144
+
height: 48px;
145
+
font-size: 1.5rem;
146
+
border: 2px solid #2563eb;
147
+
background: white;
148
+
color: #2563eb;
149
+
border-radius: 8px;
150
+
cursor: pointer;
151
+
transition: all 0.2s;
152
+
}
153
+
154
+
.counter button:hover {
155
+
background: #2563eb;
156
+
color: white;
157
+
}
158
+
159
+
.counter span {
160
+
font-size: 2rem;
161
+
font-weight: bold;
162
+
min-width: 60px;
163
+
text-align: center;
164
+
}
165
+
166
+
button:not(.counter button) {
167
+
padding: 0.75rem 1.5rem;
168
+
background: #2563eb;
169
+
color: white;
170
+
border: none;
171
+
border-radius: 6px;
172
+
font-size: 1rem;
173
+
font-weight: 500;
174
+
cursor: pointer;
175
+
transition: all 0.2s;
176
+
margin-right: 0.5rem;
177
+
}
178
+
179
+
button:not(.counter button):hover {
180
+
background: #1e40af;
181
+
}
182
+
183
+
input[type="text"] {
184
+
width: 100%;
185
+
padding: 0.75rem;
186
+
border: 1px solid #ddd;
187
+
border-radius: 4px;
188
+
font-size: 1rem;
189
+
margin: 1rem 0;
190
+
}
191
+
192
+
input[type="text"]:focus {
193
+
outline: none;
194
+
border-color: #2563eb;
195
+
}
196
+
197
+
.hint {
198
+
color: #666;
199
+
font-size: 0.9rem;
200
+
margin-top: 1rem;
201
+
}
202
+
203
+
.animated-box {
204
+
margin-top: 1.5rem;
205
+
padding: 2rem;
206
+
background: #dbeafe;
207
+
border: 2px solid #2563eb;
208
+
border-radius: 8px;
209
+
text-align: center;
210
+
font-weight: 500;
211
+
color: #1e40af;
212
+
}
213
+
214
+
.section {
215
+
margin-top: 3rem;
216
+
padding: 3rem;
217
+
background: white;
218
+
border-radius: 8px;
219
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
220
+
min-height: 300px;
221
+
}
222
+
223
+
.section h3 {
224
+
margin-top: 0;
225
+
color: #2563eb;
226
+
}
227
+
`;
228
+
}
229
+
230
+
export function generatePluginsPackageJSON(projectName: string): string {
231
+
return JSON.stringify(
232
+
{
233
+
name: projectName,
234
+
version: "0.1.0",
235
+
type: "module",
236
+
scripts: { dev: "voltx dev", build: "voltx build" },
237
+
devDependencies: { "create-voltx": "^0.1.0" },
238
+
},
239
+
null,
240
+
2,
241
+
);
242
+
}
243
+
244
+
export function generatePluginsREADME(projectName: string): string {
245
+
return `# ${projectName}
246
+
247
+
A VoltX.js application with all plugins pre-registered.
248
+
249
+
## Getting Started
250
+
251
+
1. Install dependencies:
252
+
\`\`\`bash
253
+
pnpm install
254
+
\`\`\`
255
+
256
+
2. Start the development server:
257
+
\`\`\`bash
258
+
pnpm dev
259
+
\`\`\`
260
+
261
+
3. Open your browser to the URL shown in the terminal.
262
+
263
+
## Included Plugins
264
+
265
+
This project includes all VoltX.js plugins:
266
+
267
+
- **Persist Plugin**: Save state to localStorage/sessionStorage
268
+
- **Scroll Plugin**: Smooth scrolling and scroll restoration
269
+
- **URL Plugin**: Sync state with URL parameters
270
+
- **Surge Plugin**: Declarative animations (fade, slide, scale)
271
+
- **Navigate Plugin**: Client-side routing
272
+
273
+
## Learn More
274
+
275
+
- [VoltX.js Documentation](https://stormlightlabs.github.io/volt)
276
+
- [Plugin Reference](https://stormlightlabs.github.io/volt/plugins)
277
+
`;
278
+
}
+235
cli/src/templates/with-router.ts
+235
cli/src/templates/with-router.ts
···
1
+
/**
2
+
* Generate a VoltX.js project with router plugin.
3
+
*/
4
+
export function generateRouterHTML(projectName: string): string {
5
+
return `<!DOCTYPE html>
6
+
<html lang="en">
7
+
<head>
8
+
<meta charset="UTF-8">
9
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
10
+
<title>${projectName}</title>
11
+
<link rel="stylesheet" href="voltx.min.css">
12
+
<link rel="stylesheet" href="styles.css">
13
+
</head>
14
+
<body>
15
+
<div data-volt>
16
+
<nav class="nav">
17
+
<a href="/" data-volt-navigate>Home</a>
18
+
<a href="/about" data-volt-navigate>About</a>
19
+
<a href="/contact" data-volt-navigate>Contact</a>
20
+
</nav>
21
+
22
+
<main class="container">
23
+
<!-- Home page -->
24
+
<div data-volt-url="/" data-volt-url-exact>
25
+
<h1>Home</h1>
26
+
<p>Welcome to ${projectName}!</p>
27
+
<p>This is a VoltX.js app with client-side routing.</p>
28
+
</div>
29
+
30
+
<!-- About page -->
31
+
<div data-volt-url="/about">
32
+
<h1>About</h1>
33
+
<p>This project demonstrates VoltX.js routing capabilities.</p>
34
+
<ul>
35
+
<li>Client-side navigation</li>
36
+
<li>Clean URLs with History API</li>
37
+
<li>Declarative route matching</li>
38
+
</ul>
39
+
</div>
40
+
41
+
<!-- Contact page -->
42
+
<div data-volt-url="/contact">
43
+
<h1>Contact</h1>
44
+
<p>Get in touch with us!</p>
45
+
<form>
46
+
<label>
47
+
Name:
48
+
<input type="text" placeholder="Your name">
49
+
</label>
50
+
<label>
51
+
Email:
52
+
<input type="email" placeholder="your@email.com">
53
+
</label>
54
+
<label>
55
+
Message:
56
+
<textarea rows="4" placeholder="Your message"></textarea>
57
+
</label>
58
+
<button type="submit">Send</button>
59
+
</form>
60
+
</div>
61
+
62
+
<!-- 404 page -->
63
+
<div data-volt-url-fallback>
64
+
<h1>404 - Page Not Found</h1>
65
+
<p>The page you're looking for doesn't exist.</p>
66
+
<a href="/" data-volt-navigate>Go back home</a>
67
+
</div>
68
+
</main>
69
+
</div>
70
+
71
+
<script type="module">
72
+
import { charge, registerPlugin, navigatePlugin, urlPlugin } from './voltx.min.js';
73
+
74
+
registerPlugin('navigate', navigatePlugin);
75
+
registerPlugin('url', urlPlugin);
76
+
charge();
77
+
</script>
78
+
</body>
79
+
</html>
80
+
`;
81
+
}
82
+
83
+
export function generateRouterCSS(): string {
84
+
return `/* Custom styles for your VoltX.js app */
85
+
86
+
body {
87
+
margin: 0;
88
+
font-family: system-ui, -apple-system, sans-serif;
89
+
background: #f5f5f5;
90
+
color: #333;
91
+
}
92
+
93
+
.nav {
94
+
background: #2563eb;
95
+
padding: 1rem 2rem;
96
+
display: flex;
97
+
gap: 2rem;
98
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
99
+
}
100
+
101
+
.nav a {
102
+
color: white;
103
+
text-decoration: none;
104
+
font-weight: 500;
105
+
padding: 0.5rem 1rem;
106
+
border-radius: 4px;
107
+
transition: background 0.2s;
108
+
}
109
+
110
+
.nav a:hover {
111
+
background: rgba(255, 255, 255, 0.1);
112
+
}
113
+
114
+
.nav a[aria-current="page"] {
115
+
background: rgba(255, 255, 255, 0.2);
116
+
}
117
+
118
+
.container {
119
+
max-width: 800px;
120
+
margin: 2rem auto;
121
+
padding: 2rem;
122
+
background: white;
123
+
border-radius: 8px;
124
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
125
+
}
126
+
127
+
h1 {
128
+
margin-top: 0;
129
+
color: #2563eb;
130
+
}
131
+
132
+
form {
133
+
display: flex;
134
+
flex-direction: column;
135
+
gap: 1rem;
136
+
margin-top: 2rem;
137
+
}
138
+
139
+
label {
140
+
display: flex;
141
+
flex-direction: column;
142
+
gap: 0.5rem;
143
+
font-weight: 500;
144
+
}
145
+
146
+
input,
147
+
textarea {
148
+
padding: 0.75rem;
149
+
border: 1px solid #ddd;
150
+
border-radius: 4px;
151
+
font-size: 1rem;
152
+
font-family: inherit;
153
+
}
154
+
155
+
input:focus,
156
+
textarea:focus {
157
+
outline: none;
158
+
border-color: #2563eb;
159
+
}
160
+
161
+
button[type="submit"] {
162
+
padding: 0.75rem 1.5rem;
163
+
background: #2563eb;
164
+
color: white;
165
+
border: none;
166
+
border-radius: 4px;
167
+
font-size: 1rem;
168
+
font-weight: 500;
169
+
cursor: pointer;
170
+
transition: background 0.2s;
171
+
align-self: flex-start;
172
+
}
173
+
174
+
button[type="submit"]:hover {
175
+
background: #1e40af;
176
+
}
177
+
`;
178
+
}
179
+
180
+
export function generateRouterPackageJSON(projectName: string): string {
181
+
return JSON.stringify(
182
+
{
183
+
name: projectName,
184
+
version: "0.1.0",
185
+
type: "module",
186
+
scripts: { dev: "voltx dev", build: "voltx build" },
187
+
devDependencies: { "create-voltx": "^0.1.0" },
188
+
},
189
+
null,
190
+
2,
191
+
);
192
+
}
193
+
194
+
export function generateRouterREADME(projectName: string): string {
195
+
return `# ${projectName}
196
+
197
+
A VoltX.js application with client-side routing.
198
+
199
+
## Getting Started
200
+
201
+
1. Install dependencies:
202
+
\`\`\`bash
203
+
pnpm install
204
+
\`\`\`
205
+
206
+
2. Start the development server:
207
+
\`\`\`bash
208
+
pnpm dev
209
+
\`\`\`
210
+
211
+
3. Open your browser to the URL shown in the terminal.
212
+
213
+
## Features
214
+
215
+
This project demonstrates:
216
+
217
+
- Client-side routing with the History API
218
+
- Declarative route matching with \`data-volt-url\`
219
+
- Navigation with \`data-volt-navigate\`
220
+
- 404 fallback pages
221
+
- Clean URLs without hash routing
222
+
223
+
## Project Structure
224
+
225
+
- \`index.html\` - Main HTML file with routes
226
+
- \`styles.css\` - Custom styles
227
+
- \`voltx.min.js\` - VoltX.js framework with router
228
+
- \`voltx.min.css\` - VoltX.js base styles
229
+
230
+
## Learn More
231
+
232
+
- [VoltX.js Documentation](https://stormlightlabs.github.io/volt)
233
+
- [Routing Guide](https://stormlightlabs.github.io/volt/guides/routing)
234
+
`;
235
+
}
+158
cli/src/tests/commands.test.ts
+158
cli/src/tests/commands.test.ts
···
1
+
import { downloadCommand } from "$commands/download.js";
2
+
import { initCommand } from "$commands/init.js";
3
+
import * as downloadUtils from "$utils/download.js";
4
+
import * as filesUtils from "$utils/files.js";
5
+
import { mkdtemp, rm } from "node:fs/promises";
6
+
import { tmpdir } from "node:os";
7
+
import path from "node:path";
8
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
9
+
10
+
// Mock inquirer prompts
11
+
vi.mock("@inquirer/prompts", () => ({ input: vi.fn(), select: vi.fn() }));
12
+
13
+
describe("CLI commands", () => {
14
+
let tempDir: string;
15
+
16
+
beforeEach(async () => {
17
+
tempDir = await mkdtemp(path.join(tmpdir(), "voltx-cmd-test-"));
18
+
vi.spyOn(downloadUtils, "downloadFile").mockResolvedValue();
19
+
vi.spyOn(filesUtils, "createFile").mockResolvedValue();
20
+
});
21
+
22
+
afterEach(async () => {
23
+
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
24
+
vi.clearAllMocks();
25
+
vi.restoreAllMocks();
26
+
});
27
+
28
+
describe("downloadCommand", () => {
29
+
it("should download JS and CSS by default", async () => {
30
+
await downloadCommand({ output: tempDir });
31
+
32
+
const downloadFileSpy = vi.mocked(downloadUtils.downloadFile);
33
+
expect(downloadFileSpy).toHaveBeenCalledTimes(2);
34
+
expect(downloadFileSpy).toHaveBeenCalledWith(
35
+
"https://cdn.jsdelivr.net/npm/voltx.js@latest/dist/voltx.min.js",
36
+
expect.stringContaining("voltx.min.js"),
37
+
);
38
+
expect(downloadFileSpy).toHaveBeenCalledWith(
39
+
"https://cdn.jsdelivr.net/npm/voltx.js@latest/dist/voltx.min.css",
40
+
expect.stringContaining("voltx.min.css"),
41
+
);
42
+
});
43
+
44
+
it("should download only JS when css is disabled", async () => {
45
+
await downloadCommand({ output: tempDir, css: false });
46
+
47
+
const downloadFileSpy = vi.mocked(downloadUtils.downloadFile);
48
+
expect(downloadFileSpy).toHaveBeenCalledTimes(1);
49
+
expect(downloadFileSpy).toHaveBeenCalledWith(
50
+
"https://cdn.jsdelivr.net/npm/voltx.js@latest/dist/voltx.min.js",
51
+
expect.stringContaining("voltx.min.js"),
52
+
);
53
+
});
54
+
55
+
it("should download only CSS when js is disabled", async () => {
56
+
await downloadCommand({ output: tempDir, js: false });
57
+
58
+
const downloadFileSpy = vi.mocked(downloadUtils.downloadFile);
59
+
expect(downloadFileSpy).toHaveBeenCalledTimes(1);
60
+
expect(downloadFileSpy).toHaveBeenCalledWith(
61
+
"https://cdn.jsdelivr.net/npm/voltx.js@latest/dist/voltx.min.css",
62
+
expect.stringContaining("voltx.min.css"),
63
+
);
64
+
});
65
+
66
+
it("should download specific version when specified", async () => {
67
+
await downloadCommand({ output: tempDir, version: "0.5.0" });
68
+
69
+
const downloadFileSpy = vi.mocked(downloadUtils.downloadFile);
70
+
expect(downloadFileSpy).toHaveBeenCalledWith(
71
+
"https://cdn.jsdelivr.net/npm/voltx.js@0.5.0/dist/voltx.min.js",
72
+
expect.stringContaining("voltx.min.js"),
73
+
);
74
+
expect(downloadFileSpy).toHaveBeenCalledWith(
75
+
"https://cdn.jsdelivr.net/npm/voltx.js@0.5.0/dist/voltx.min.css",
76
+
expect.stringContaining("voltx.min.css"),
77
+
);
78
+
});
79
+
80
+
it("should handle download errors and exit with code 1", async () => {
81
+
const downloadFileSpy = vi.spyOn(downloadUtils, "downloadFile").mockRejectedValue(new Error("Network error"));
82
+
const exitSpy = vi.spyOn(process, "exit").mockImplementation((() => {}) as any);
83
+
84
+
await downloadCommand({ output: tempDir });
85
+
86
+
expect(downloadFileSpy).toHaveBeenCalled();
87
+
expect(exitSpy).toHaveBeenCalledWith(1);
88
+
89
+
exitSpy.mockRestore();
90
+
});
91
+
});
92
+
93
+
describe("initCommand", () => {
94
+
it("should check for existing non-empty directory", async () => {
95
+
const { input, select } = await import("@inquirer/prompts");
96
+
vi.mocked(input).mockResolvedValue("test-project");
97
+
vi.mocked(select).mockResolvedValue("minimal" as any);
98
+
99
+
const isEmptyOrMissingSpy = vi.spyOn(filesUtils, "isEmptyOrMissing").mockResolvedValue(false);
100
+
const exitSpy = vi.spyOn(process, "exit").mockImplementation((() => {}) as any);
101
+
102
+
await initCommand();
103
+
104
+
expect(isEmptyOrMissingSpy).toHaveBeenCalled();
105
+
expect(exitSpy).toHaveBeenCalledWith(1);
106
+
107
+
exitSpy.mockRestore();
108
+
});
109
+
110
+
it("should create minimal template", async () => {
111
+
const { select } = await import("@inquirer/prompts");
112
+
vi.mocked(select).mockResolvedValue("minimal" as any);
113
+
114
+
vi.spyOn(filesUtils, "isEmptyOrMissing").mockResolvedValue(true);
115
+
116
+
await initCommand("minimal-app");
117
+
118
+
expect(vi.mocked(filesUtils.createFile)).toHaveBeenCalled();
119
+
expect(vi.mocked(downloadUtils.downloadFile)).toHaveBeenCalled();
120
+
});
121
+
122
+
it("should create styles template without JS", async () => {
123
+
const { select } = await import("@inquirer/prompts");
124
+
vi.mocked(select).mockResolvedValue("styles" as any);
125
+
126
+
vi.spyOn(filesUtils, "isEmptyOrMissing").mockResolvedValue(true);
127
+
128
+
await initCommand("styles-app");
129
+
130
+
const downloadSpy = vi.mocked(downloadUtils.downloadFile);
131
+
const calls = downloadSpy.mock.calls;
132
+
expect(calls.some((call) => call[0].includes("voltx.min.css"))).toBe(true);
133
+
expect(calls.some((call) => call[0].includes("voltx.min.js"))).toBe(false);
134
+
});
135
+
136
+
it("should create with-router template", async () => {
137
+
const { select } = await import("@inquirer/prompts");
138
+
vi.mocked(select).mockResolvedValue("with-router" as any);
139
+
140
+
vi.spyOn(filesUtils, "isEmptyOrMissing").mockResolvedValue(true);
141
+
142
+
await initCommand("router-app");
143
+
144
+
expect(vi.mocked(filesUtils.createFile)).toHaveBeenCalled();
145
+
});
146
+
147
+
it("should create with-plugins template", async () => {
148
+
const { select } = await import("@inquirer/prompts");
149
+
vi.mocked(select).mockResolvedValue("with-plugins" as any);
150
+
151
+
vi.spyOn(filesUtils, "isEmptyOrMissing").mockResolvedValue(true);
152
+
153
+
await initCommand("plugins-app");
154
+
155
+
expect(vi.mocked(filesUtils.createFile)).toHaveBeenCalled();
156
+
});
157
+
});
158
+
});
+27
cli/src/tests/download.test.ts
+27
cli/src/tests/download.test.ts
···
1
+
import { getCDNUrls } from "$utils/download.js";
2
+
import { describe, expect, it } from "vitest";
3
+
4
+
describe("download utilities", () => {
5
+
describe("getCDNUrls", () => {
6
+
it("should return latest URLs when no version specified", () => {
7
+
const urls = getCDNUrls();
8
+
9
+
expect(urls.js).toBe("https://cdn.jsdelivr.net/npm/voltx.js@latest/dist/voltx.min.js");
10
+
expect(urls.css).toBe("https://cdn.jsdelivr.net/npm/voltx.js@latest/dist/voltx.min.css");
11
+
});
12
+
13
+
it("should return latest URLs when 'latest' is specified", () => {
14
+
const urls = getCDNUrls("latest");
15
+
16
+
expect(urls.js).toBe("https://cdn.jsdelivr.net/npm/voltx.js@latest/dist/voltx.min.js");
17
+
expect(urls.css).toBe("https://cdn.jsdelivr.net/npm/voltx.js@latest/dist/voltx.min.css");
18
+
});
19
+
20
+
it("should return versioned URLs when version is specified", () => {
21
+
const urls = getCDNUrls("1.0.0");
22
+
23
+
expect(urls.js).toBe("https://cdn.jsdelivr.net/npm/voltx.js@1.0.0/dist/voltx.min.js");
24
+
expect(urls.css).toBe("https://cdn.jsdelivr.net/npm/voltx.js@1.0.0/dist/voltx.min.css");
25
+
});
26
+
});
27
+
});
+87
cli/src/tests/echo.test.ts
+87
cli/src/tests/echo.test.ts
···
1
+
import { echo } from "$utils/echo.js";
2
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+
4
+
describe("echo utility", () => {
5
+
let consoleLogSpy: ReturnType<typeof vi.spyOn>;
6
+
let consoleErrorSpy: ReturnType<typeof vi.spyOn>;
7
+
let consoleWarnSpy: ReturnType<typeof vi.spyOn>;
8
+
9
+
beforeEach(() => {
10
+
consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => {});
11
+
consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
12
+
consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
13
+
});
14
+
15
+
afterEach(() => {
16
+
consoleLogSpy.mockRestore();
17
+
consoleErrorSpy.mockRestore();
18
+
consoleWarnSpy.mockRestore();
19
+
});
20
+
21
+
describe("err", () => {
22
+
it("should log to stderr", () => {
23
+
echo.err("Error message");
24
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.any(String));
25
+
});
26
+
27
+
it("should accept additional parameters", () => {
28
+
echo.err("Error:", "details", 123);
29
+
expect(consoleErrorSpy).toHaveBeenCalledWith(expect.any(String), "details", 123);
30
+
});
31
+
});
32
+
33
+
describe("danger", () => {
34
+
it("should log to stdout", () => {
35
+
echo.danger("Danger message");
36
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.any(String));
37
+
});
38
+
});
39
+
40
+
describe("ok", () => {
41
+
it("should log success message", () => {
42
+
echo.ok("Success message");
43
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.any(String));
44
+
});
45
+
});
46
+
47
+
describe("success", () => {
48
+
it("should log bold success message", () => {
49
+
echo.success("Success!");
50
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.any(String));
51
+
});
52
+
});
53
+
54
+
describe("info", () => {
55
+
it("should log info message", () => {
56
+
echo.info("Info message");
57
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.any(String));
58
+
});
59
+
});
60
+
61
+
describe("label", () => {
62
+
it("should log label message", () => {
63
+
echo.label("Label");
64
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.any(String));
65
+
});
66
+
});
67
+
68
+
describe("title", () => {
69
+
it("should log bold title", () => {
70
+
echo.title("Title");
71
+
expect(consoleLogSpy).toHaveBeenCalledWith(expect.any(String));
72
+
});
73
+
});
74
+
75
+
describe("warn", () => {
76
+
it("should log warning", () => {
77
+
echo.warn("Warning message");
78
+
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.any(String));
79
+
});
80
+
});
81
+
82
+
describe("text", () => {
83
+
it("should be a reference to console.log", () => {
84
+
expect(typeof echo.text).toBe("function");
85
+
});
86
+
});
87
+
});
+64
cli/src/tests/files.test.ts
+64
cli/src/tests/files.test.ts
···
1
+
import { createFile, isEmptyOrMissing } from "$utils/files.js";
2
+
import { mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
3
+
import { tmpdir } from "node:os";
4
+
import path from "node:path";
5
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
+
7
+
describe("files utilities", () => {
8
+
let tempDir: string;
9
+
10
+
beforeEach(async () => {
11
+
tempDir = await mkdtemp(path.join(tmpdir(), "voltx-test-"));
12
+
});
13
+
14
+
afterEach(async () => {
15
+
await rm(tempDir, { recursive: true, force: true });
16
+
});
17
+
18
+
describe("createFile", () => {
19
+
it("should create a file with content", async () => {
20
+
const filePath = path.join(tempDir, "test.txt");
21
+
const content = "Hello VoltX!";
22
+
23
+
await createFile(filePath, content);
24
+
25
+
const fileContent = await readFile(filePath, "utf8");
26
+
expect(fileContent).toBe(content);
27
+
});
28
+
29
+
it("should create parent directories if they don't exist", async () => {
30
+
const filePath = path.join(tempDir, "nested", "deep", "test.txt");
31
+
const content = "Nested file";
32
+
33
+
await createFile(filePath, content);
34
+
35
+
const fileContent = await readFile(filePath, "utf8");
36
+
expect(fileContent).toBe(content);
37
+
});
38
+
});
39
+
40
+
describe("isEmptyOrMissing", () => {
41
+
it("should return true for non-existent directory", async () => {
42
+
const nonExistentDir = path.join(tempDir, "does-not-exist");
43
+
const result = await isEmptyOrMissing(nonExistentDir);
44
+
expect(result).toBe(true);
45
+
});
46
+
47
+
it("should return true for empty directory", async () => {
48
+
const emptyDir = path.join(tempDir, "empty");
49
+
await mkdir(emptyDir);
50
+
51
+
const result = await isEmptyOrMissing(emptyDir);
52
+
expect(result).toBe(true);
53
+
});
54
+
55
+
it("should return false for directory with files", async () => {
56
+
const dirWithFiles = path.join(tempDir, "with-files");
57
+
await mkdir(dirWithFiles);
58
+
await createFile(path.join(dirWithFiles, "test.txt"), "content");
59
+
60
+
const result = await isEmptyOrMissing(dirWithFiles);
61
+
expect(result).toBe(false);
62
+
});
63
+
});
64
+
});
+146
cli/src/tests/templates.test.ts
+146
cli/src/tests/templates.test.ts
···
1
+
import { describe, it, expect } from "vitest";
2
+
import {
3
+
generateMinimalHTML,
4
+
generateMinimalCSS,
5
+
generateMinimalPackageJSON,
6
+
generateMinimalREADME,
7
+
} from "$templates/minimal.js";
8
+
import {
9
+
generateStylesHTML,
10
+
generateStylesCSS,
11
+
generateStylesPackageJSON,
12
+
generateStylesREADME,
13
+
} from "$templates/styles.js";
14
+
import {
15
+
generateRouterHTML,
16
+
generateRouterCSS,
17
+
generateRouterPackageJSON,
18
+
generateRouterREADME,
19
+
} from "$templates/with-router.js";
20
+
import {
21
+
generatePluginsHTML,
22
+
generatePluginsCSS,
23
+
generatePluginsPackageJSON,
24
+
generatePluginsREADME,
25
+
} from "$templates/with-plugins.js";
26
+
27
+
describe("template generators", () => {
28
+
const projectName = "test-project";
29
+
30
+
describe("minimal template", () => {
31
+
it("should generate HTML with project name", () => {
32
+
const html = generateMinimalHTML(projectName);
33
+
expect(html).toContain(projectName);
34
+
expect(html).toContain("data-volt");
35
+
expect(html).toContain("voltx.min.js");
36
+
});
37
+
38
+
it("should generate CSS", () => {
39
+
const css = generateMinimalCSS();
40
+
expect(css).toContain(".container");
41
+
expect(css).toContain(".counter");
42
+
});
43
+
44
+
it("should generate valid package.json", () => {
45
+
const packageJson = generateMinimalPackageJSON(projectName);
46
+
const parsed = JSON.parse(packageJson);
47
+
expect(parsed.name).toBe(projectName);
48
+
expect(parsed.scripts.dev).toBe("voltx dev");
49
+
expect(parsed.scripts.build).toBe("voltx build");
50
+
});
51
+
52
+
it("should generate README with project name", () => {
53
+
const readme = generateMinimalREADME(projectName);
54
+
expect(readme).toContain(projectName);
55
+
expect(readme).toContain("pnpm dev");
56
+
});
57
+
});
58
+
59
+
describe("styles template", () => {
60
+
it("should generate HTML without VoltX.js framework", () => {
61
+
const html = generateStylesHTML(projectName);
62
+
expect(html).toContain(projectName);
63
+
expect(html).toContain("voltx.min.css");
64
+
expect(html).not.toContain("voltx.min.js");
65
+
expect(html).not.toContain("data-volt");
66
+
});
67
+
68
+
it("should generate CSS", () => {
69
+
const css = generateStylesCSS();
70
+
expect(css).toContain(".container");
71
+
expect(css).toContain(".card");
72
+
});
73
+
74
+
it("should generate valid package.json", () => {
75
+
const packageJson = generateStylesPackageJSON(projectName);
76
+
const parsed = JSON.parse(packageJson);
77
+
expect(parsed.name).toBe(projectName);
78
+
});
79
+
80
+
it("should generate README explaining styles-only approach", () => {
81
+
const readme = generateStylesREADME(projectName);
82
+
expect(readme).toContain(projectName);
83
+
expect(readme).toContain("styles-only");
84
+
});
85
+
});
86
+
87
+
describe("router template", () => {
88
+
it("should generate HTML with routing", () => {
89
+
const html = generateRouterHTML(projectName);
90
+
expect(html).toContain(projectName);
91
+
expect(html).toContain("data-volt-navigate");
92
+
expect(html).toContain("data-volt-url");
93
+
expect(html).toContain("navigatePlugin");
94
+
});
95
+
96
+
it("should generate CSS with navigation styles", () => {
97
+
const css = generateRouterCSS();
98
+
expect(css).toContain(".nav");
99
+
});
100
+
101
+
it("should generate valid package.json", () => {
102
+
const packageJson = generateRouterPackageJSON(projectName);
103
+
const parsed = JSON.parse(packageJson);
104
+
expect(parsed.name).toBe(projectName);
105
+
});
106
+
107
+
it("should generate README explaining routing", () => {
108
+
const readme = generateRouterREADME(projectName);
109
+
expect(readme).toContain(projectName);
110
+
expect(readme).toContain("routing");
111
+
});
112
+
});
113
+
114
+
describe("plugins template", () => {
115
+
it("should generate HTML with all plugins", () => {
116
+
const html = generatePluginsHTML(projectName);
117
+
expect(html).toContain(projectName);
118
+
expect(html).toContain("persistPlugin");
119
+
expect(html).toContain("scrollPlugin");
120
+
expect(html).toContain("urlPlugin");
121
+
expect(html).toContain("surgePlugin");
122
+
expect(html).toContain("navigatePlugin");
123
+
});
124
+
125
+
it("should generate CSS for plugin demos", () => {
126
+
const css = generatePluginsCSS();
127
+
expect(css).toContain(".card");
128
+
expect(css).toContain(".counter");
129
+
expect(css).toContain(".animated-box");
130
+
});
131
+
132
+
it("should generate valid package.json", () => {
133
+
const packageJson = generatePluginsPackageJSON(projectName);
134
+
const parsed = JSON.parse(packageJson);
135
+
expect(parsed.name).toBe(projectName);
136
+
});
137
+
138
+
it("should generate README listing all plugins", () => {
139
+
const readme = generatePluginsREADME(projectName);
140
+
expect(readme).toContain(projectName);
141
+
expect(readme).toContain("Persist Plugin");
142
+
expect(readme).toContain("Scroll Plugin");
143
+
expect(readme).toContain("URL Plugin");
144
+
});
145
+
});
146
+
});
+56
cli/src/utils/download.ts
+56
cli/src/utils/download.ts
···
1
+
import { mkdir, writeFile } from "node:fs/promises";
2
+
import https from "node:https";
3
+
import path from "node:path";
4
+
5
+
/**
6
+
* Download a file from a URL and save it to the specified path.
7
+
*/
8
+
export async function downloadFile(url: string, outputPath: string): Promise<void> {
9
+
const dir = path.dirname(outputPath);
10
+
await mkdir(dir, { recursive: true });
11
+
12
+
return new Promise((resolve, reject) => {
13
+
https.get(url, (response) => {
14
+
if (response.statusCode === 302 || response.statusCode === 301) {
15
+
if (response.headers.location) {
16
+
downloadFile(response.headers.location, outputPath).then(resolve).catch(reject);
17
+
return;
18
+
}
19
+
}
20
+
21
+
if (response.statusCode !== 200) {
22
+
reject(new Error(`Failed to download: ${response.statusCode} ${response.statusMessage}`));
23
+
return;
24
+
}
25
+
26
+
const chunks: Buffer[] = [];
27
+
28
+
response.on("data", (chunk) => {
29
+
chunks.push(chunk);
30
+
});
31
+
32
+
response.on("end", async () => {
33
+
try {
34
+
const buffer = Buffer.concat(chunks);
35
+
await writeFile(outputPath, buffer);
36
+
resolve();
37
+
} catch (error) {
38
+
reject(error);
39
+
}
40
+
});
41
+
42
+
response.on("error", reject);
43
+
}).on("error", reject);
44
+
});
45
+
}
46
+
47
+
/**
48
+
* Get the CDN URLs for VoltX.js assets.
49
+
*/
50
+
export function getCDNUrls(version: string = "latest"): { js: string; css: string } {
51
+
const baseUrl = version === "latest"
52
+
? "https://cdn.jsdelivr.net/npm/voltx.js@latest/dist"
53
+
: `https://cdn.jsdelivr.net/npm/voltx.js@${version}/dist`;
54
+
55
+
return { js: `${baseUrl}/voltx.min.js`, css: `${baseUrl}/voltx.min.css` };
56
+
}
+40
cli/src/utils/echo.ts
+40
cli/src/utils/echo.ts
···
1
+
import chalk from "chalk";
2
+
3
+
type Echo = Record<
4
+
"info" | "success" | "ok" | "warn" | "text" | "err" | "danger" | "label" | "title",
5
+
(message?: any, ...optionalParams: any[]) => void
6
+
>;
7
+
8
+
export const echo: Echo = {
9
+
/**
10
+
* Red text to stderr
11
+
*/
12
+
err(message, ...optionalParams) {
13
+
console.error(chalk.red(message), ...optionalParams);
14
+
},
15
+
/**
16
+
* Red text for recoverable errors (to stdout)
17
+
*/
18
+
danger(message, ...optionalParams) {
19
+
console.log(chalk.red(message), ...optionalParams);
20
+
},
21
+
ok(message, ...optionalParams) {
22
+
console.log(chalk.green(message), ...optionalParams);
23
+
},
24
+
success(message, ...optionalParams) {
25
+
console.log(chalk.green.bold(message), ...optionalParams);
26
+
},
27
+
info(message, ...optionalParams) {
28
+
console.log(chalk.cyan(message), ...optionalParams);
29
+
},
30
+
label(message, ...optionalParams) {
31
+
console.log(chalk.blue(message), ...optionalParams);
32
+
},
33
+
title(message, ...optionalParams) {
34
+
console.log(chalk.blue.bold(message), ...optionalParams);
35
+
},
36
+
warn(message, ...optionalParams) {
37
+
console.warn(chalk.yellow(message), ...optionalParams);
38
+
},
39
+
text: console.log,
40
+
};
+28
cli/src/utils/files.ts
+28
cli/src/utils/files.ts
···
1
+
import { mkdir, writeFile } from "node:fs/promises";
2
+
import path from "node:path";
3
+
4
+
/**
5
+
* Create a file with the given content at the specified path.
6
+
*
7
+
* Creates parent directories if they don't exist.
8
+
*/
9
+
export async function createFile(filePath: string, content: string): Promise<void> {
10
+
const dir = path.dirname(filePath);
11
+
await mkdir(dir, { recursive: true });
12
+
await writeFile(filePath, content, "utf8");
13
+
}
14
+
15
+
/**
16
+
* Check if a directory is empty or doesn't exist.
17
+
*/
18
+
export async function isEmptyOrMissing(dirPath: string): Promise<boolean> {
19
+
const { existsSync } = await import("node:fs");
20
+
const { readdir } = await import("node:fs/promises");
21
+
22
+
if (!existsSync(dirPath)) {
23
+
return true;
24
+
}
25
+
26
+
const files = await readdir(dirPath);
27
+
return files.length === 0;
28
+
}
+25
cli/tsconfig.json
+25
cli/tsconfig.json
···
1
+
{
2
+
"compilerOptions": {
3
+
"target": "ES2022",
4
+
"module": "ESNext",
5
+
"lib": ["ES2022"],
6
+
"moduleResolution": "bundler",
7
+
"resolveJsonModule": true,
8
+
"allowJs": true,
9
+
"strict": true,
10
+
"esModuleInterop": true,
11
+
"skipLibCheck": true,
12
+
"forceConsistentCasingInFileNames": true,
13
+
"declaration": true,
14
+
"declarationMap": true,
15
+
"outDir": "./dist",
16
+
"rootDir": "./src",
17
+
"paths": {
18
+
"$commands/*": ["./src/commands/*"],
19
+
"$templates/*": ["./src/templates/*"],
20
+
"$utils/*": ["./src/utils/*"]
21
+
}
22
+
},
23
+
"include": ["src/**/*"],
24
+
"exclude": ["node_modules", "dist", "tests"]
25
+
}
+12
cli/tsdown.config.ts
+12
cli/tsdown.config.ts
+24
cli/vitest.config.ts
+24
cli/vitest.config.ts
···
1
+
import path from "node:path";
2
+
import { defineConfig } from "vitest/config";
3
+
4
+
export default defineConfig({
5
+
test: {
6
+
globals: true,
7
+
environment: "node",
8
+
coverage: {
9
+
provider: "v8",
10
+
reporter: ["text", "html", "lcov"],
11
+
include: ["src/**/*.ts"],
12
+
exclude: ["src/tests/**", "src/**/*.test.ts", "src/index.ts", "src/commands/dev.ts", "src/commands/build.ts"],
13
+
all: true,
14
+
thresholds: { lines: 95, functions: 95, branches: 80, statements: 95 },
15
+
},
16
+
},
17
+
resolve: {
18
+
alias: {
19
+
"$commands": path.resolve(__dirname, "./src/commands"),
20
+
"$templates": path.resolve(__dirname, "./src/templates"),
21
+
"$utils": path.resolve(__dirname, "./src/utils"),
22
+
},
23
+
},
24
+
});
+3
-3
docs/.vitepress/config.ts
+3
-3
docs/.vitepress/config.ts
···
30
30
{
31
31
text: "Getting Started",
32
32
items:
33
-
([{ text: "Overview", link: "/overview" }, {
34
-
text: "Installation",
35
-
link: "/installation",
33
+
([{ text: "Overview", link: "/overview" }, { text: "Installation", link: "/installation" }, {
34
+
text: "CLI",
35
+
link: "/cli",
36
36
}] as DefaultTheme.SidebarItem[]).concat(...u.scanDir("usage", "/usage")),
37
37
},
38
38
{
+337
docs/cli.md
+337
docs/cli.md
···
1
+
# CLI
2
+
3
+
The VoltX.js CLI provides tools for creating and managing VoltX.js applications. Use it to scaffold new projects, run development servers, build for production, and download framework assets.
4
+
5
+
## Installation
6
+
7
+
The CLI is available as `create-voltx` on npm:
8
+
9
+
```bash
10
+
# Use with pnpm (recommended)
11
+
pnpm create voltx my-app
12
+
13
+
# Use with npm
14
+
npm create voltx@latest my-app
15
+
16
+
# Use with npx
17
+
npx create-voltx my-app
18
+
```
19
+
20
+
For ongoing development commands, install the CLI in your project:
21
+
22
+
```bash
23
+
pnpm add -D create-voltx
24
+
```
25
+
26
+
## Commands
27
+
28
+
### `init`
29
+
30
+
Create a new VoltX.js project with an interactive template selector.
31
+
32
+
```bash
33
+
# Interactive mode - prompts for project name and template
34
+
pnpm create voltx
35
+
36
+
# Specify project name
37
+
pnpm create voltx my-app
38
+
39
+
# Using the voltx command
40
+
voltx init my-app
41
+
```
42
+
43
+
**Templates:**
44
+
45
+
- **Minimal** - Basic VoltX.js app with a counter demo
46
+
- **With Router** - Multi-page app with client-side routing
47
+
- **With Plugins** - Demonstration of all VoltX.js plugins
48
+
- **Styles Only** - HTML + CSS using VoltX.js styles without the framework
49
+
50
+
The init command:
51
+
52
+
- Creates project directory
53
+
- Generates HTML, CSS, package.json, and README
54
+
- Downloads latest VoltX.js assets from CDN
55
+
- Sets up development scripts
56
+
57
+
**Generated Structure:**
58
+
59
+
```sh
60
+
my-app/
61
+
├── index.html # Main HTML file
62
+
├── styles.css # Custom styles
63
+
├── package.json # Project configuration
64
+
├── README.md # Getting started guide
65
+
├── voltx.min.js # VoltX.js framework
66
+
└── voltx.min.css # VoltX.js base styles
67
+
```
68
+
69
+
### `dev`
70
+
71
+
Start a Vite development server for your VoltX.js project.
72
+
73
+
```bash
74
+
voltx dev
75
+
```
76
+
77
+
**Options:**
78
+
79
+
- `-p, --port <port>` - Port to run the dev server on (default: 3000)
80
+
- `-o, --open` - Open browser automatically
81
+
82
+
**Examples:**
83
+
84
+
```bash
85
+
# Start dev server on default port (3000)
86
+
voltx dev
87
+
88
+
# Use custom port
89
+
voltx dev --port 8080
90
+
91
+
# Open browser automatically
92
+
voltx dev --open
93
+
```
94
+
95
+
The dev server provides:
96
+
97
+
- Hot module replacement (HMR)
98
+
- Fast refresh for development
99
+
- Automatic browser reload
100
+
- HTTPS support (via Vite)
101
+
102
+
### `build`
103
+
104
+
Build your VoltX.js project for production.
105
+
106
+
```bash
107
+
voltx build
108
+
```
109
+
110
+
**Options:**
111
+
112
+
- `--out <dir>` - Output directory (default: dist)
113
+
114
+
**Examples:**
115
+
116
+
```bash
117
+
# Build to default dist/ directory
118
+
voltx build
119
+
120
+
# Build to custom directory
121
+
voltx build --out public
122
+
```
123
+
124
+
The build command:
125
+
126
+
- Minifies HTML, CSS, and JavaScript
127
+
- Optimizes assets for production
128
+
- Generates source maps
129
+
- Creates optimized bundle
130
+
131
+
### `download`
132
+
133
+
Download VoltX.js assets (JS and CSS) from the CDN.
134
+
135
+
```bash
136
+
voltx download
137
+
```
138
+
139
+
**Options:**
140
+
141
+
- `--version <version>` - VoltX.js version to download (default: latest)
142
+
- `--no-js` - Skip downloading JavaScript file
143
+
- `--no-css` - Skip downloading CSS file
144
+
- `-o, --output <dir>` - Output directory (default: current directory)
145
+
146
+
**Examples:**
147
+
148
+
```bash
149
+
# Download latest JS and CSS
150
+
voltx download
151
+
152
+
# Download specific version
153
+
voltx download --version 0.5.0
154
+
155
+
# Download only CSS
156
+
voltx download --no-js
157
+
158
+
# Download to custom directory
159
+
voltx download --output assets
160
+
```
161
+
162
+
This command downloads minified assets from the jsDelivr CDN.
163
+
164
+
## Workflows
165
+
166
+
### Creating a New Project
167
+
168
+
```bash
169
+
# Create new project
170
+
pnpm create voltx my-app
171
+
172
+
# Navigate to project
173
+
cd my-app
174
+
175
+
# Install dependencies
176
+
pnpm install
177
+
178
+
# Start dev server
179
+
pnpm dev
180
+
```
181
+
182
+
### Development
183
+
184
+
```bash
185
+
# Start dev server (watches for changes)
186
+
pnpm dev
187
+
188
+
# In another terminal, run tests if available
189
+
pnpm test
190
+
```
191
+
192
+
### Production Build
193
+
194
+
```bash
195
+
# Build for production
196
+
pnpm build
197
+
198
+
# Preview production build locally
199
+
npx vite preview --outDir dist
200
+
```
201
+
202
+
### Updating VoltX.js Assets
203
+
204
+
```bash
205
+
# Download latest version
206
+
voltx download
207
+
208
+
# Or download specific version
209
+
voltx download --version 0.5.1
210
+
```
211
+
212
+
## Project Scripts
213
+
214
+
All generated projects include these npm scripts:
215
+
216
+
```json
217
+
{
218
+
"scripts": {
219
+
"dev": "voltx dev",
220
+
"build": "voltx build"
221
+
}
222
+
}
223
+
```
224
+
225
+
Run them with your package manager:
226
+
227
+
```bash
228
+
pnpm dev
229
+
pnpm build
230
+
```
231
+
232
+
## Templates
233
+
234
+
### Minimal
235
+
236
+
A basic VoltX.js application demonstrating:
237
+
238
+
- Declarative state with `data-volt-state`
239
+
- Reactive bindings
240
+
- Event handlers
241
+
- Counter example
242
+
243
+
Best for: Learning VoltX.js basics, simple interactive pages.
244
+
245
+
### With Router
246
+
247
+
A multi-page application featuring:
248
+
249
+
- Client-side routing with History API
250
+
- Multiple routes (home, about, contact, 404)
251
+
- Navigation with `data-volt-navigate`
252
+
- Route matching with `data-volt-url`
253
+
254
+
Best for: Multi-page applications, documentation sites, dashboards.
255
+
256
+
### With Plugins
257
+
258
+
A comprehensive demo showcasing:
259
+
260
+
- Persist plugin (localStorage sync)
261
+
- Scroll plugin (smooth scrolling)
262
+
- URL plugin (URL parameter sync)
263
+
- Surge plugin (animations)
264
+
- Navigate plugin (routing)
265
+
266
+
Best for: Learning all VoltX.js features, reference implementation.
267
+
268
+
### Styles Only
269
+
270
+
HTML and CSS using VoltX.js styles without the reactive framework:
271
+
272
+
- VoltX.js CSS utilities
273
+
- Semantic HTML
274
+
- No JavaScript required
275
+
276
+
Best for: Static sites, progressively enhanced pages, CSS-only projects.
277
+
278
+
## Configuration
279
+
280
+
### Vite Configuration
281
+
282
+
The CLI uses Vite as the dev server and build tool. To customize Vite, create a `vite.config.js` in your project root:
283
+
284
+
```js
285
+
import { defineConfig } from 'vite';
286
+
287
+
export default defineConfig({
288
+
// Custom Vite configuration
289
+
server: {
290
+
port: 3000,
291
+
},
292
+
build: {
293
+
outDir: 'dist',
294
+
},
295
+
});
296
+
```
297
+
298
+
See the [Vite documentation](https://vitejs.dev/config/) for all available options.
299
+
300
+
## Troubleshooting
301
+
302
+
### Dev Server Won't Start
303
+
304
+
Ensure you're in a VoltX.js project directory with an `index.html` file:
305
+
306
+
```bash
307
+
ls index.html
308
+
```
309
+
310
+
If `index.html` is missing, you may not be in a VoltX.js project.
311
+
312
+
### Download Fails
313
+
314
+
Check your internet connection and try again. The CLI downloads assets from:
315
+
316
+
```text
317
+
https://cdn.jsdelivr.net/npm/voltx.js@{version}/dist/
318
+
```
319
+
320
+
If jsDelivr is blocked, manually download from the [npm package](https://www.npmjs.com/package/voltx.js).
321
+
322
+
### Build Fails
323
+
324
+
Ensure all dependencies are installed:
325
+
326
+
```bash
327
+
pnpm install
328
+
```
329
+
330
+
Check for syntax errors in your HTML, CSS, or JavaScript files.
331
+
332
+
## Next Steps
333
+
334
+
- Read the [Installation Guide](./installation) for framework setup
335
+
- Explore [Usage Patterns](./usage/state) for state management
336
+
- Check the [API Reference](./api) for detailed documentation
337
+
- Learn about [Plugins](./plugins) for extended functionality
+13
docs/installation.md
+13
docs/installation.md
···
26
26
</script>
27
27
```
28
28
29
+
## CLI (Recommended)
30
+
31
+
The fastest way to create a new VoltX.js project is with the CLI:
32
+
33
+
```bash
34
+
pnpm create voltx my-app
35
+
cd my-app
36
+
pnpm install
37
+
pnpm dev
38
+
```
39
+
40
+
This scaffolds a complete project with development server, build tools, and framework assets. See the [CLI Guide](./cli) for all available commands and templates.
41
+
29
42
## Package Manager
30
43
31
44
For applications using node based tools, install VoltX.js via npm or JSR:
+2
package.json
+2
package.json
···
14
14
"docs:dev": "pnpm --filter @voltx/docs dev",
15
15
"docs:build": "pnpm --filter @voltx/docs build",
16
16
"docs:preview": "pnpm --filter @voltx/docs preview",
17
+
"cli:dev": "pnpm --filter create-voltx dev",
18
+
"cli:build": "pnpm --filter create-voltx build",
17
19
"typecheck": "pnpm -r typecheck"
18
20
},
19
21
"devDependencies": {
+399
-71
pnpm-lock.yaml
+399
-71
pnpm-lock.yaml
···
39
39
specifier: ^3.2.4
40
40
version: 3.2.4(@types/node@24.8.1)(esbuild@0.25.11)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(terser@5.44.0)(yaml@2.8.1)
41
41
42
+
cli:
43
+
dependencies:
44
+
'@inquirer/prompts':
45
+
specifier: ^8.0.1
46
+
version: 8.0.1(@types/node@24.8.1)
47
+
chalk:
48
+
specifier: ^5.6.2
49
+
version: 5.6.2
50
+
commander:
51
+
specifier: ^14.0.1
52
+
version: 14.0.1
53
+
devDependencies:
54
+
'@vitest/coverage-v8':
55
+
specifier: ^3.2.4
56
+
version: 3.2.4(vitest@3.2.4(@types/node@24.8.1)(esbuild@0.25.11)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(terser@5.44.0)(yaml@2.8.1))
57
+
tsdown:
58
+
specifier: ^0.15.6
59
+
version: 0.15.8(typescript@5.9.3)
60
+
42
61
dev:
43
62
dependencies:
44
63
chalk:
···
592
611
'@iconify/types@2.0.0':
593
612
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
594
613
614
+
'@inquirer/ansi@2.0.1':
615
+
resolution: {integrity: sha512-QAZUk6BBncv/XmSEZTscd8qazzjV3E0leUMrEPjxCd51QBgCKmprUGLex5DTsNtURm7LMzv+CLcd6S86xvBfYg==}
616
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
617
+
618
+
'@inquirer/checkbox@5.0.1':
619
+
resolution: {integrity: sha512-5VPFBK8jKdsjMK3DTFOlbR0+Kkd4q0AWB7VhWQn6ppv44dr3b7PU8wSJQTC5oA0f/aGW7v/ZozQJAY9zx6PKig==}
620
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
621
+
peerDependencies:
622
+
'@types/node': '>=18'
623
+
peerDependenciesMeta:
624
+
'@types/node':
625
+
optional: true
626
+
627
+
'@inquirer/confirm@6.0.1':
628
+
resolution: {integrity: sha512-wD+pM7IxLn1TdcQN12Q6wcFe5VpyCuh/I2sSmqO5KjWH2R4v+GkUToHb+PsDGobOe1MtAlXMwGNkZUPc2+L6NA==}
629
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
630
+
peerDependencies:
631
+
'@types/node': '>=18'
632
+
peerDependenciesMeta:
633
+
'@types/node':
634
+
optional: true
635
+
636
+
'@inquirer/core@11.0.1':
637
+
resolution: {integrity: sha512-Tpf49h50e4KYffVUCXzkx4gWMafUi3aDQDwfVAAGBNnVcXiwJIj4m2bKlZ7Kgyf6wjt1eyXH1wDGXcAokm4Ssw==}
638
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
639
+
peerDependencies:
640
+
'@types/node': '>=18'
641
+
peerDependenciesMeta:
642
+
'@types/node':
643
+
optional: true
644
+
645
+
'@inquirer/editor@5.0.1':
646
+
resolution: {integrity: sha512-zDKobHI7Ry++4noiV9Z5VfYgSVpPZoMApviIuGwLOMciQaP+dGzCO+1fcwI441riklRiZg4yURWyEoX0Zy2zZw==}
647
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
648
+
peerDependencies:
649
+
'@types/node': '>=18'
650
+
peerDependenciesMeta:
651
+
'@types/node':
652
+
optional: true
653
+
654
+
'@inquirer/expand@5.0.1':
655
+
resolution: {integrity: sha512-TBrTpAB6uZNnGQHtSEkbvJZIQ3dXZOrwqQSO9uUbwct3G2LitwBCE5YZj98MbQ5nzihzs5pRjY1K9RRLH4WgoA==}
656
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
657
+
peerDependencies:
658
+
'@types/node': '>=18'
659
+
peerDependenciesMeta:
660
+
'@types/node':
661
+
optional: true
662
+
663
+
'@inquirer/external-editor@2.0.1':
664
+
resolution: {integrity: sha512-BPYWJXCAK9w6R+pb2s3WyxUz9ts9SP/LDOUwA9fu7LeuyYgojz83i0DSRwezu736BgMwz14G63Xwj70hSzHohQ==}
665
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
666
+
peerDependencies:
667
+
'@types/node': '>=18'
668
+
peerDependenciesMeta:
669
+
'@types/node':
670
+
optional: true
671
+
672
+
'@inquirer/figures@2.0.1':
673
+
resolution: {integrity: sha512-KtMxyjLCuDFqAWHmCY9qMtsZ09HnjMsm8H3OvpSIpfhHdfw3/AiGWHNrfRwbyvHPtOJpumm8wGn5fkhtvkWRsg==}
674
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
675
+
676
+
'@inquirer/input@5.0.1':
677
+
resolution: {integrity: sha512-cEhEUohCpE2BCuLKtFFZGp4Ief05SEcqeAOq9NxzN5ThOQP8Rl5N/Nt9VEDORK1bRb2Sk/zoOyQYfysPQwyQtA==}
678
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
679
+
peerDependencies:
680
+
'@types/node': '>=18'
681
+
peerDependenciesMeta:
682
+
'@types/node':
683
+
optional: true
684
+
685
+
'@inquirer/number@4.0.1':
686
+
resolution: {integrity: sha512-4//zgBGHe8Q/FfCoUXZUrUHyK/q5dyqiwsePz3oSSPSmw1Ijo35ZkjaftnxroygcUlLYfXqm+0q08lnB5hd49A==}
687
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
688
+
peerDependencies:
689
+
'@types/node': '>=18'
690
+
peerDependenciesMeta:
691
+
'@types/node':
692
+
optional: true
693
+
694
+
'@inquirer/password@5.0.1':
695
+
resolution: {integrity: sha512-UJudHpd7Ia30Q+x+ctYqI9Nh6SyEkaBscpa7J6Ts38oc1CNSws0I1hJEdxbQBlxQd65z5GEJPM4EtNf6tzfWaQ==}
696
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
697
+
peerDependencies:
698
+
'@types/node': '>=18'
699
+
peerDependenciesMeta:
700
+
'@types/node':
701
+
optional: true
702
+
703
+
'@inquirer/prompts@8.0.1':
704
+
resolution: {integrity: sha512-MURRu/cyvLm9vchDDaVZ9u4p+ADnY0Mz3LQr0KTgihrrvuKZlqcWwlBC4lkOMvd0KKX4Wz7Ww9+uA7qEpQaqjg==}
705
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
706
+
peerDependencies:
707
+
'@types/node': '>=18'
708
+
peerDependenciesMeta:
709
+
'@types/node':
710
+
optional: true
711
+
712
+
'@inquirer/rawlist@5.0.1':
713
+
resolution: {integrity: sha512-vVfVHKUgH6rZmMlyd0jOuGZo0Fw1jfcOqZF96lMwlgavx7g0x7MICe316bV01EEoI+c68vMdbkTTawuw3O+Fgw==}
714
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
715
+
peerDependencies:
716
+
'@types/node': '>=18'
717
+
peerDependenciesMeta:
718
+
'@types/node':
719
+
optional: true
720
+
721
+
'@inquirer/search@4.0.1':
722
+
resolution: {integrity: sha512-XwiaK5xBvr31STX6Ji8iS3HCRysBXfL/jUbTzufdWTS6LTGtvDQA50oVETt1BJgjKyQBp9vt0VU6AmU/AnOaGA==}
723
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
724
+
peerDependencies:
725
+
'@types/node': '>=18'
726
+
peerDependenciesMeta:
727
+
'@types/node':
728
+
optional: true
729
+
730
+
'@inquirer/select@5.0.1':
731
+
resolution: {integrity: sha512-gPByrgYoezGyKMq5KjV7Tuy1JU2ArIy6/sI8sprw0OpXope3VGQwP5FK1KD4eFFqEhKu470Dwe6/AyDPmGRA0Q==}
732
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
733
+
peerDependencies:
734
+
'@types/node': '>=18'
735
+
peerDependenciesMeta:
736
+
'@types/node':
737
+
optional: true
738
+
739
+
'@inquirer/type@4.0.1':
740
+
resolution: {integrity: sha512-odO8YwoQAw/eVu/PSPsDDVPmqO77r/Mq7zcoF5VduVqIu2wSRWUgmYb5K9WH1no0SjLnOe8MDKtDL++z6mfo2g==}
741
+
engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'}
742
+
peerDependencies:
743
+
'@types/node': '>=18'
744
+
peerDependenciesMeta:
745
+
'@types/node':
746
+
optional: true
747
+
595
748
'@isaacs/cliui@8.0.2':
596
749
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
597
750
engines: {node: '>=12'}
···
638
791
'@oxc-project/types@0.93.0':
639
792
resolution: {integrity: sha512-yNtwmWZIBtJsMr5TEfoZFDxIWV6OdScOpza/f5YxbqUMJk+j6QX3Cf3jgZShGEFYWQJ5j9mJ6jM0tZHu2J9Yrg==}
640
793
641
-
'@oxc-project/types@0.97.0':
642
-
resolution: {integrity: sha512-lxmZK4xFrdvU0yZiDwgVQTCvh2gHWBJCBk5ALsrtsBWhs0uDIi+FTOnXRQeQfs304imdvTdaakT/lqwQ8hkOXQ==}
794
+
'@oxc-project/types@0.98.0':
795
+
resolution: {integrity: sha512-Vzmd6FsqVuz5HQVcRC/hrx7Ujo3WEVeQP7C2UNP5uy1hUY4SQvMB+93jxkI1KRHz9a/6cni3glPOtvteN+zpsw==}
643
796
644
797
'@pkgjs/parseargs@0.11.0':
645
798
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
···
654
807
cpu: [arm64]
655
808
os: [android]
656
809
657
-
'@rolldown/binding-android-arm64@1.0.0-beta.50':
658
-
resolution: {integrity: sha512-XlEkrOIHLyGT3avOgzfTFSjG+f+dZMw+/qd+Y3HLN86wlndrB/gSimrJCk4gOhr1XtRtEKfszpadI3Md4Z4/Ag==}
810
+
'@rolldown/binding-android-arm64@1.0.0-beta.51':
811
+
resolution: {integrity: sha512-Ctn8FUXKWWQI9pWC61P1yumS9WjQtelNS9riHwV7oCkknPGaAry4o7eFx2KgoLMnI2BgFJYpW7Im8/zX3BuONg==}
659
812
engines: {node: ^20.19.0 || >=22.12.0}
660
813
cpu: [arm64]
661
814
os: [android]
···
666
819
cpu: [arm64]
667
820
os: [darwin]
668
821
669
-
'@rolldown/binding-darwin-arm64@1.0.0-beta.50':
670
-
resolution: {integrity: sha512-+JRqKJhoFlt5r9q+DecAGPLZ5PxeLva+wCMtAuoFMWPoZzgcYrr599KQ+Ix0jwll4B4HGP43avu9My8KtSOR+w==}
822
+
'@rolldown/binding-darwin-arm64@1.0.0-beta.51':
823
+
resolution: {integrity: sha512-EL1aRW2Oq15ShUEkBPsDtLMO8GTqfb/ktM/dFaVzXKQiEE96Ss6nexMgfgQrg8dGnNpndFyffVDb5IdSibsu1g==}
671
824
engines: {node: ^20.19.0 || >=22.12.0}
672
825
cpu: [arm64]
673
826
os: [darwin]
···
678
831
cpu: [x64]
679
832
os: [darwin]
680
833
681
-
'@rolldown/binding-darwin-x64@1.0.0-beta.50':
682
-
resolution: {integrity: sha512-fFXDjXnuX7/gQZQm/1FoivVtRcyAzdjSik7Eo+9iwPQ9EgtA5/nB2+jmbzaKtMGG3q+BnZbdKHCtOacmNrkIDA==}
834
+
'@rolldown/binding-darwin-x64@1.0.0-beta.51':
835
+
resolution: {integrity: sha512-uGtYKlFen9pMIPvkHPWZVDtmYhMQi5g5Ddsndg1gf3atScKYKYgs5aDP4DhHeTwGXQglhfBG7lEaOIZ4UAIWww==}
683
836
engines: {node: ^20.19.0 || >=22.12.0}
684
837
cpu: [x64]
685
838
os: [darwin]
···
690
843
cpu: [x64]
691
844
os: [freebsd]
692
845
693
-
'@rolldown/binding-freebsd-x64@1.0.0-beta.50':
694
-
resolution: {integrity: sha512-F1b6vARy49tjmT/hbloplzgJS7GIvwWZqt+tAHEstCh0JIh9sa8FAMVqEmYxDviqKBaAI8iVvUREm/Kh/PD26Q==}
846
+
'@rolldown/binding-freebsd-x64@1.0.0-beta.51':
847
+
resolution: {integrity: sha512-JRoVTQtHYbZj1P07JLiuTuXjiBtIa7ag7/qgKA6CIIXnAcdl4LrOf7nfDuHPJcuRKaP5dzecMgY99itvWfmUFQ==}
695
848
engines: {node: ^20.19.0 || >=22.12.0}
696
849
cpu: [x64]
697
850
os: [freebsd]
···
702
855
cpu: [arm]
703
856
os: [linux]
704
857
705
-
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.50':
706
-
resolution: {integrity: sha512-U6cR76N8T8M6lHj7EZrQ3xunLPxSvYYxA8vJsBKZiFZkT8YV4kjgCO3KwMJL0NOjQCPGKyiXO07U+KmJzdPGRw==}
858
+
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.51':
859
+
resolution: {integrity: sha512-BKATVnpPZ0TYBW9XfDwyd4kPGgvf964HiotIwUgpMrFOFYWqpZ+9ONNzMV4UFAYC7Hb5C2qgYQk/qj2OnAd4RQ==}
707
860
engines: {node: ^20.19.0 || >=22.12.0}
708
861
cpu: [arm]
709
862
os: [linux]
···
714
867
cpu: [arm64]
715
868
os: [linux]
716
869
717
-
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.50':
718
-
resolution: {integrity: sha512-ONgyjofCrrE3bnh5GZb8EINSFyR/hmwTzZ7oVuyUB170lboza1VMCnb8jgE6MsyyRgHYmN8Lb59i3NKGrxrYjw==}
870
+
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.51':
871
+
resolution: {integrity: sha512-xLd7da5jkfbVsBCm1buIRdWtuXY8+hU3+6ESXY/Tk5X5DPHaifrUblhYDgmA34dQt6WyNC2kfXGgrduPEvDI6Q==}
719
872
engines: {node: ^20.19.0 || >=22.12.0}
720
873
cpu: [arm64]
721
874
os: [linux]
···
726
879
cpu: [arm64]
727
880
os: [linux]
728
881
729
-
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.50':
730
-
resolution: {integrity: sha512-L0zRdH2oDPkmB+wvuTl+dJbXCsx62SkqcEqdM+79LOcB+PxbAxxjzHU14BuZIQdXcAVDzfpMfaHWzZuwhhBTcw==}
882
+
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.51':
883
+
resolution: {integrity: sha512-EQFXTgHxxTzv3t5EmjUP/DfxzFYx9sMndfLsYaAY4DWF6KsK1fXGYsiupif6qPTViPC9eVmRm78q0pZU/kuIPg==}
731
884
engines: {node: ^20.19.0 || >=22.12.0}
732
885
cpu: [arm64]
733
886
os: [linux]
···
738
891
cpu: [x64]
739
892
os: [linux]
740
893
741
-
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.50':
742
-
resolution: {integrity: sha512-gyoI8o/TGpQd3OzkJnh1M2kxy1Bisg8qJ5Gci0sXm9yLFzEXIFdtc4EAzepxGvrT2ri99ar5rdsmNG0zP0SbIg==}
894
+
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.51':
895
+
resolution: {integrity: sha512-p5P6Xpa68w3yFaAdSzIZJbj+AfuDnMDqNSeglBXM7UlJT14Q4zwK+rV+8Mhp9MiUb4XFISZtbI/seBprhkQbiQ==}
743
896
engines: {node: ^20.19.0 || >=22.12.0}
744
897
cpu: [x64]
745
898
os: [linux]
···
750
903
cpu: [x64]
751
904
os: [linux]
752
905
753
-
'@rolldown/binding-linux-x64-musl@1.0.0-beta.50':
754
-
resolution: {integrity: sha512-zti8A7M+xFDpKlghpcCAzyOi+e5nfUl3QhU023ce5NCgUxRG5zGP2GR9LTydQ1rnIPwZUVBWd4o7NjZDaQxaXA==}
906
+
'@rolldown/binding-linux-x64-musl@1.0.0-beta.51':
907
+
resolution: {integrity: sha512-sNVVyLa8HB8wkFipdfz1s6i0YWinwpbMWk5hO5S+XAYH2UH67YzUT13gs6wZTKg2x/3gtgXzYnHyF5wMIqoDAw==}
755
908
engines: {node: ^20.19.0 || >=22.12.0}
756
909
cpu: [x64]
757
910
os: [linux]
···
762
915
cpu: [arm64]
763
916
os: [openharmony]
764
917
765
-
'@rolldown/binding-openharmony-arm64@1.0.0-beta.50':
766
-
resolution: {integrity: sha512-eZUssog7qljrrRU9Mi0eqYEPm3Ch0UwB+qlWPMKSUXHNqhm3TvDZarJQdTevGEfu3EHAXJvBIe0YFYr0TPVaMA==}
918
+
'@rolldown/binding-openharmony-arm64@1.0.0-beta.51':
919
+
resolution: {integrity: sha512-e/JMTz9Q8+T3g/deEi8DK44sFWZWGKr9AOCW5e8C8SCVWzAXqYXAG7FXBWBNzWEZK0Rcwo9TQHTQ9Q0gXgdCaA==}
767
920
engines: {node: ^20.19.0 || >=22.12.0}
768
921
cpu: [arm64]
769
922
os: [openharmony]
···
773
926
engines: {node: '>=14.0.0'}
774
927
cpu: [wasm32]
775
928
776
-
'@rolldown/binding-wasm32-wasi@1.0.0-beta.50':
777
-
resolution: {integrity: sha512-nmCN0nIdeUnmgeDXiQ+2HU6FT162o+rxnF7WMkBm4M5Ds8qTU7Dzv2Wrf22bo4ftnlrb2hKK6FSwAJSAe2FWLg==}
929
+
'@rolldown/binding-wasm32-wasi@1.0.0-beta.51':
930
+
resolution: {integrity: sha512-We3LWqSu6J9s5Y0MK+N7fUiiu37aBGPG3Pc347EoaROuAwkCS2u9xJ5dpIyLW4B49CIbS3KaPmn4kTgPb3EyPw==}
778
931
engines: {node: '>=14.0.0'}
779
932
cpu: [wasm32]
780
933
···
784
937
cpu: [arm64]
785
938
os: [win32]
786
939
787
-
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.50':
788
-
resolution: {integrity: sha512-7kcNLi7Ua59JTTLvbe1dYb028QEPaJPJQHqkmSZ5q3tJueUeb6yjRtx8mw4uIqgWZcnQHAR3PrLN4XRJxvgIkA==}
940
+
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.51':
941
+
resolution: {integrity: sha512-fj56buHRuMM+r/cb6ZYfNjNvO/0xeFybI6cTkTROJatdP4fvmQ1NS8D/Lm10FCSDEOkqIz8hK3TGpbAThbPHsA==}
789
942
engines: {node: ^20.19.0 || >=22.12.0}
790
943
cpu: [arm64]
791
944
os: [win32]
···
796
949
cpu: [ia32]
797
950
os: [win32]
798
951
799
-
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.50':
800
-
resolution: {integrity: sha512-lL70VTNvSCdSZkDPPVMwWn/M2yQiYvSoXw9hTLgdIWdUfC3g72UaruezusR6ceRuwHCY1Ayu2LtKqXkBO5LIwg==}
952
+
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.51':
953
+
resolution: {integrity: sha512-fkqEqaeEx8AySXiDm54b/RdINb3C0VovzJA3osMhZsbn6FoD73H0AOIiaVAtGr6x63hefruVKTX8irAm4Jkt2w==}
801
954
engines: {node: ^20.19.0 || >=22.12.0}
802
955
cpu: [ia32]
803
956
os: [win32]
···
808
961
cpu: [x64]
809
962
os: [win32]
810
963
811
-
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.50':
812
-
resolution: {integrity: sha512-4qU4x5DXWB4JPjyTne/wBNPqkbQU8J45bl21geERBKtEittleonioACBL1R0PsBu0Aq21SwMK5a9zdBkWSlQtQ==}
964
+
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.51':
965
+
resolution: {integrity: sha512-CWuLG/HMtrVcjKGa0C4GnuxONrku89g0+CsH8nT0SNhOtREXuzwgjIXNJImpE/A/DMf9JF+1Xkrq/YRr+F/rCg==}
813
966
engines: {node: ^20.19.0 || >=22.12.0}
814
967
cpu: [x64]
815
968
os: [win32]
···
817
970
'@rolldown/pluginutils@1.0.0-beta.41':
818
971
resolution: {integrity: sha512-ycMEPrS3StOIeb87BT3/+bu+blEtyvwQ4zmo2IcJQy0Rd1DAAhKksA0iUZ3MYSpJtjlPhg0Eo6mvVS6ggPhRbw==}
819
972
820
-
'@rolldown/pluginutils@1.0.0-beta.50':
821
-
resolution: {integrity: sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==}
973
+
'@rolldown/pluginutils@1.0.0-beta.51':
974
+
resolution: {integrity: sha512-51/8cNXMrqWqX3o8DZidhwz1uYq0BhHDDSfVygAND1Skx5s1TDw3APSSxCMcFFedwgqGcx34gRouwY+m404BBQ==}
822
975
823
976
'@shikijs/core@2.5.0':
824
977
resolution: {integrity: sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==}
···
1255
1408
character-entities-legacy@3.0.0:
1256
1409
resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
1257
1410
1411
+
chardet@2.1.1:
1412
+
resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==}
1413
+
1258
1414
check-error@2.1.1:
1259
1415
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
1260
1416
engines: {node: '>= 16'}
···
1278
1434
resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
1279
1435
engines: {node: '>=4'}
1280
1436
1437
+
cli-width@4.1.0:
1438
+
resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==}
1439
+
engines: {node: '>= 12'}
1440
+
1281
1441
cliui@8.0.1:
1282
1442
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
1283
1443
engines: {node: '>=12'}
···
1478
1638
emoji-regex-xs@1.0.0:
1479
1639
resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==}
1480
1640
1641
+
emoji-regex@10.6.0:
1642
+
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
1643
+
1481
1644
emoji-regex@8.0.0:
1482
1645
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
1483
1646
···
1648
1811
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
1649
1812
engines: {node: 6.* || 8.* || >= 10.*}
1650
1813
1814
+
get-east-asian-width@1.4.0:
1815
+
resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==}
1816
+
engines: {node: '>=18'}
1817
+
1651
1818
get-tsconfig@4.12.0:
1652
1819
resolution: {integrity: sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==}
1653
1820
···
1718
1885
1719
1886
iconv-lite@0.6.3:
1720
1887
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
1888
+
engines: {node: '>=0.10.0'}
1889
+
1890
+
iconv-lite@0.7.0:
1891
+
resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==}
1721
1892
engines: {node: '>=0.10.0'}
1722
1893
1723
1894
ignore@5.3.2:
···
2026
2197
ms@2.1.3:
2027
2198
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
2028
2199
2200
+
mute-stream@3.0.0:
2201
+
resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==}
2202
+
engines: {node: ^20.17.0 || >=22.9.0}
2203
+
2029
2204
nanoid@3.3.11:
2030
2205
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
2031
2206
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
···
2501
2676
engines: {node: ^20.19.0 || >=22.12.0}
2502
2677
hasBin: true
2503
2678
2504
-
rolldown@1.0.0-beta.50:
2505
-
resolution: {integrity: sha512-JFULvCNl/anKn99eKjOSEubi0lLmNqQDAjyEMME2T4CwezUDL0i6t1O9xZsu2OMehPnV2caNefWpGF+8TnzB6A==}
2679
+
rolldown@1.0.0-beta.51:
2680
+
resolution: {integrity: sha512-ZRLgPlS91l4JztLYEZnmMcd3Umcla1hkXJgiEiR4HloRJBBoeaX8qogTu5Jfu36rRMVLndzqYv0h+M5gJAkUfg==}
2506
2681
engines: {node: ^20.19.0 || >=22.12.0}
2507
2682
hasBin: true
2508
2683
···
2583
2758
string-width@5.1.2:
2584
2759
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
2585
2760
engines: {node: '>=12'}
2761
+
2762
+
string-width@7.2.0:
2763
+
resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
2764
+
engines: {node: '>=18'}
2586
2765
2587
2766
stringify-entities@4.0.4:
2588
2767
resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
···
2887
3066
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
2888
3067
engines: {node: '>=12'}
2889
3068
3069
+
wrap-ansi@9.0.2:
3070
+
resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
3071
+
engines: {node: '>=18'}
3072
+
2890
3073
ws@8.18.3:
2891
3074
resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
2892
3075
engines: {node: '>=10.0.0'}
···
3351
3534
3352
3535
'@iconify/types@2.0.0': {}
3353
3536
3537
+
'@inquirer/ansi@2.0.1': {}
3538
+
3539
+
'@inquirer/checkbox@5.0.1(@types/node@24.8.1)':
3540
+
dependencies:
3541
+
'@inquirer/ansi': 2.0.1
3542
+
'@inquirer/core': 11.0.1(@types/node@24.8.1)
3543
+
'@inquirer/figures': 2.0.1
3544
+
'@inquirer/type': 4.0.1(@types/node@24.8.1)
3545
+
optionalDependencies:
3546
+
'@types/node': 24.8.1
3547
+
3548
+
'@inquirer/confirm@6.0.1(@types/node@24.8.1)':
3549
+
dependencies:
3550
+
'@inquirer/core': 11.0.1(@types/node@24.8.1)
3551
+
'@inquirer/type': 4.0.1(@types/node@24.8.1)
3552
+
optionalDependencies:
3553
+
'@types/node': 24.8.1
3554
+
3555
+
'@inquirer/core@11.0.1(@types/node@24.8.1)':
3556
+
dependencies:
3557
+
'@inquirer/ansi': 2.0.1
3558
+
'@inquirer/figures': 2.0.1
3559
+
'@inquirer/type': 4.0.1(@types/node@24.8.1)
3560
+
cli-width: 4.1.0
3561
+
mute-stream: 3.0.0
3562
+
signal-exit: 4.1.0
3563
+
wrap-ansi: 9.0.2
3564
+
optionalDependencies:
3565
+
'@types/node': 24.8.1
3566
+
3567
+
'@inquirer/editor@5.0.1(@types/node@24.8.1)':
3568
+
dependencies:
3569
+
'@inquirer/core': 11.0.1(@types/node@24.8.1)
3570
+
'@inquirer/external-editor': 2.0.1(@types/node@24.8.1)
3571
+
'@inquirer/type': 4.0.1(@types/node@24.8.1)
3572
+
optionalDependencies:
3573
+
'@types/node': 24.8.1
3574
+
3575
+
'@inquirer/expand@5.0.1(@types/node@24.8.1)':
3576
+
dependencies:
3577
+
'@inquirer/core': 11.0.1(@types/node@24.8.1)
3578
+
'@inquirer/type': 4.0.1(@types/node@24.8.1)
3579
+
optionalDependencies:
3580
+
'@types/node': 24.8.1
3581
+
3582
+
'@inquirer/external-editor@2.0.1(@types/node@24.8.1)':
3583
+
dependencies:
3584
+
chardet: 2.1.1
3585
+
iconv-lite: 0.7.0
3586
+
optionalDependencies:
3587
+
'@types/node': 24.8.1
3588
+
3589
+
'@inquirer/figures@2.0.1': {}
3590
+
3591
+
'@inquirer/input@5.0.1(@types/node@24.8.1)':
3592
+
dependencies:
3593
+
'@inquirer/core': 11.0.1(@types/node@24.8.1)
3594
+
'@inquirer/type': 4.0.1(@types/node@24.8.1)
3595
+
optionalDependencies:
3596
+
'@types/node': 24.8.1
3597
+
3598
+
'@inquirer/number@4.0.1(@types/node@24.8.1)':
3599
+
dependencies:
3600
+
'@inquirer/core': 11.0.1(@types/node@24.8.1)
3601
+
'@inquirer/type': 4.0.1(@types/node@24.8.1)
3602
+
optionalDependencies:
3603
+
'@types/node': 24.8.1
3604
+
3605
+
'@inquirer/password@5.0.1(@types/node@24.8.1)':
3606
+
dependencies:
3607
+
'@inquirer/ansi': 2.0.1
3608
+
'@inquirer/core': 11.0.1(@types/node@24.8.1)
3609
+
'@inquirer/type': 4.0.1(@types/node@24.8.1)
3610
+
optionalDependencies:
3611
+
'@types/node': 24.8.1
3612
+
3613
+
'@inquirer/prompts@8.0.1(@types/node@24.8.1)':
3614
+
dependencies:
3615
+
'@inquirer/checkbox': 5.0.1(@types/node@24.8.1)
3616
+
'@inquirer/confirm': 6.0.1(@types/node@24.8.1)
3617
+
'@inquirer/editor': 5.0.1(@types/node@24.8.1)
3618
+
'@inquirer/expand': 5.0.1(@types/node@24.8.1)
3619
+
'@inquirer/input': 5.0.1(@types/node@24.8.1)
3620
+
'@inquirer/number': 4.0.1(@types/node@24.8.1)
3621
+
'@inquirer/password': 5.0.1(@types/node@24.8.1)
3622
+
'@inquirer/rawlist': 5.0.1(@types/node@24.8.1)
3623
+
'@inquirer/search': 4.0.1(@types/node@24.8.1)
3624
+
'@inquirer/select': 5.0.1(@types/node@24.8.1)
3625
+
optionalDependencies:
3626
+
'@types/node': 24.8.1
3627
+
3628
+
'@inquirer/rawlist@5.0.1(@types/node@24.8.1)':
3629
+
dependencies:
3630
+
'@inquirer/core': 11.0.1(@types/node@24.8.1)
3631
+
'@inquirer/type': 4.0.1(@types/node@24.8.1)
3632
+
optionalDependencies:
3633
+
'@types/node': 24.8.1
3634
+
3635
+
'@inquirer/search@4.0.1(@types/node@24.8.1)':
3636
+
dependencies:
3637
+
'@inquirer/core': 11.0.1(@types/node@24.8.1)
3638
+
'@inquirer/figures': 2.0.1
3639
+
'@inquirer/type': 4.0.1(@types/node@24.8.1)
3640
+
optionalDependencies:
3641
+
'@types/node': 24.8.1
3642
+
3643
+
'@inquirer/select@5.0.1(@types/node@24.8.1)':
3644
+
dependencies:
3645
+
'@inquirer/ansi': 2.0.1
3646
+
'@inquirer/core': 11.0.1(@types/node@24.8.1)
3647
+
'@inquirer/figures': 2.0.1
3648
+
'@inquirer/type': 4.0.1(@types/node@24.8.1)
3649
+
optionalDependencies:
3650
+
'@types/node': 24.8.1
3651
+
3652
+
'@inquirer/type@4.0.1(@types/node@24.8.1)':
3653
+
optionalDependencies:
3654
+
'@types/node': 24.8.1
3655
+
3354
3656
'@isaacs/cliui@8.0.2':
3355
3657
dependencies:
3356
3658
string-width: 5.1.2
···
3404
3706
3405
3707
'@oxc-project/types@0.93.0': {}
3406
3708
3407
-
'@oxc-project/types@0.97.0': {}
3709
+
'@oxc-project/types@0.98.0': {}
3408
3710
3409
3711
'@pkgjs/parseargs@0.11.0':
3410
3712
optional: true
···
3416
3718
'@rolldown/binding-android-arm64@1.0.0-beta.41':
3417
3719
optional: true
3418
3720
3419
-
'@rolldown/binding-android-arm64@1.0.0-beta.50':
3721
+
'@rolldown/binding-android-arm64@1.0.0-beta.51':
3420
3722
optional: true
3421
3723
3422
3724
'@rolldown/binding-darwin-arm64@1.0.0-beta.41':
3423
3725
optional: true
3424
3726
3425
-
'@rolldown/binding-darwin-arm64@1.0.0-beta.50':
3727
+
'@rolldown/binding-darwin-arm64@1.0.0-beta.51':
3426
3728
optional: true
3427
3729
3428
3730
'@rolldown/binding-darwin-x64@1.0.0-beta.41':
3429
3731
optional: true
3430
3732
3431
-
'@rolldown/binding-darwin-x64@1.0.0-beta.50':
3733
+
'@rolldown/binding-darwin-x64@1.0.0-beta.51':
3432
3734
optional: true
3433
3735
3434
3736
'@rolldown/binding-freebsd-x64@1.0.0-beta.41':
3435
3737
optional: true
3436
3738
3437
-
'@rolldown/binding-freebsd-x64@1.0.0-beta.50':
3739
+
'@rolldown/binding-freebsd-x64@1.0.0-beta.51':
3438
3740
optional: true
3439
3741
3440
3742
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.41':
3441
3743
optional: true
3442
3744
3443
-
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.50':
3745
+
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.51':
3444
3746
optional: true
3445
3747
3446
3748
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.41':
3447
3749
optional: true
3448
3750
3449
-
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.50':
3751
+
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.51':
3450
3752
optional: true
3451
3753
3452
3754
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.41':
3453
3755
optional: true
3454
3756
3455
-
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.50':
3757
+
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.51':
3456
3758
optional: true
3457
3759
3458
3760
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.41':
3459
3761
optional: true
3460
3762
3461
-
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.50':
3763
+
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.51':
3462
3764
optional: true
3463
3765
3464
3766
'@rolldown/binding-linux-x64-musl@1.0.0-beta.41':
3465
3767
optional: true
3466
3768
3467
-
'@rolldown/binding-linux-x64-musl@1.0.0-beta.50':
3769
+
'@rolldown/binding-linux-x64-musl@1.0.0-beta.51':
3468
3770
optional: true
3469
3771
3470
3772
'@rolldown/binding-openharmony-arm64@1.0.0-beta.41':
3471
3773
optional: true
3472
3774
3473
-
'@rolldown/binding-openharmony-arm64@1.0.0-beta.50':
3775
+
'@rolldown/binding-openharmony-arm64@1.0.0-beta.51':
3474
3776
optional: true
3475
3777
3476
3778
'@rolldown/binding-wasm32-wasi@1.0.0-beta.41':
···
3478
3780
'@napi-rs/wasm-runtime': 1.0.7
3479
3781
optional: true
3480
3782
3481
-
'@rolldown/binding-wasm32-wasi@1.0.0-beta.50':
3783
+
'@rolldown/binding-wasm32-wasi@1.0.0-beta.51':
3482
3784
dependencies:
3483
3785
'@napi-rs/wasm-runtime': 1.0.7
3484
3786
optional: true
···
3486
3788
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.41':
3487
3789
optional: true
3488
3790
3489
-
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.50':
3791
+
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.51':
3490
3792
optional: true
3491
3793
3492
3794
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.41':
3493
3795
optional: true
3494
3796
3495
-
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.50':
3797
+
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.51':
3496
3798
optional: true
3497
3799
3498
3800
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.41':
3499
3801
optional: true
3500
3802
3501
-
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.50':
3803
+
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.51':
3502
3804
optional: true
3503
3805
3504
3806
'@rolldown/pluginutils@1.0.0-beta.41': {}
3505
3807
3506
-
'@rolldown/pluginutils@1.0.0-beta.50': {}
3808
+
'@rolldown/pluginutils@1.0.0-beta.51': {}
3507
3809
3508
3810
'@shikijs/core@2.5.0':
3509
3811
dependencies:
···
4050
4352
4051
4353
character-entities-legacy@3.0.0: {}
4052
4354
4355
+
chardet@2.1.1: {}
4356
+
4053
4357
check-error@2.1.1: {}
4054
4358
4055
4359
chokidar@3.6.0:
···
4078
4382
dependencies:
4079
4383
escape-string-regexp: 1.0.5
4080
4384
4385
+
cli-width@4.1.0: {}
4386
+
4081
4387
cliui@8.0.1:
4082
4388
dependencies:
4083
4389
string-width: 4.2.3
···
4280
4586
electron-to-chromium@1.5.237: {}
4281
4587
4282
4588
emoji-regex-xs@1.0.0: {}
4589
+
4590
+
emoji-regex@10.6.0: {}
4283
4591
4284
4592
emoji-regex@8.0.0: {}
4285
4593
···
4494
4802
4495
4803
get-caller-file@2.0.5: {}
4496
4804
4805
+
get-east-asian-width@1.4.0: {}
4806
+
4497
4807
get-tsconfig@4.12.0:
4498
4808
dependencies:
4499
4809
resolve-pkg-maps: 1.0.0
···
4584
4894
dependencies:
4585
4895
safer-buffer: 2.1.2
4586
4896
4897
+
iconv-lite@0.7.0:
4898
+
dependencies:
4899
+
safer-buffer: 2.1.2
4900
+
4587
4901
ignore@5.3.2: {}
4588
4902
4589
4903
ignore@7.0.5: {}
···
4861
5175
mitt@3.0.1: {}
4862
5176
4863
5177
ms@2.1.3: {}
5178
+
5179
+
mute-stream@3.0.0: {}
4864
5180
4865
5181
nanoid@3.3.11: {}
4866
5182
···
5235
5551
5236
5552
rfdc@1.4.1: {}
5237
5553
5238
-
rolldown-plugin-dts@0.16.12(rolldown@1.0.0-beta.50)(typescript@5.9.3):
5554
+
rolldown-plugin-dts@0.16.12(rolldown@1.0.0-beta.51)(typescript@5.9.3):
5239
5555
dependencies:
5240
5556
'@babel/generator': 7.28.3
5241
5557
'@babel/parser': 7.28.4
···
5246
5562
dts-resolver: 2.1.2
5247
5563
get-tsconfig: 4.12.0
5248
5564
magic-string: 0.30.19
5249
-
rolldown: 1.0.0-beta.50
5565
+
rolldown: 1.0.0-beta.51
5250
5566
optionalDependencies:
5251
5567
typescript: 5.9.3
5252
5568
transitivePeerDependencies:
···
5291
5607
'@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.41
5292
5608
'@rolldown/binding-win32-x64-msvc': 1.0.0-beta.41
5293
5609
5294
-
rolldown@1.0.0-beta.50:
5610
+
rolldown@1.0.0-beta.51:
5295
5611
dependencies:
5296
-
'@oxc-project/types': 0.97.0
5297
-
'@rolldown/pluginutils': 1.0.0-beta.50
5612
+
'@oxc-project/types': 0.98.0
5613
+
'@rolldown/pluginutils': 1.0.0-beta.51
5298
5614
optionalDependencies:
5299
-
'@rolldown/binding-android-arm64': 1.0.0-beta.50
5300
-
'@rolldown/binding-darwin-arm64': 1.0.0-beta.50
5301
-
'@rolldown/binding-darwin-x64': 1.0.0-beta.50
5302
-
'@rolldown/binding-freebsd-x64': 1.0.0-beta.50
5303
-
'@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.50
5304
-
'@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.50
5305
-
'@rolldown/binding-linux-arm64-musl': 1.0.0-beta.50
5306
-
'@rolldown/binding-linux-x64-gnu': 1.0.0-beta.50
5307
-
'@rolldown/binding-linux-x64-musl': 1.0.0-beta.50
5308
-
'@rolldown/binding-openharmony-arm64': 1.0.0-beta.50
5309
-
'@rolldown/binding-wasm32-wasi': 1.0.0-beta.50
5310
-
'@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.50
5311
-
'@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.50
5312
-
'@rolldown/binding-win32-x64-msvc': 1.0.0-beta.50
5615
+
'@rolldown/binding-android-arm64': 1.0.0-beta.51
5616
+
'@rolldown/binding-darwin-arm64': 1.0.0-beta.51
5617
+
'@rolldown/binding-darwin-x64': 1.0.0-beta.51
5618
+
'@rolldown/binding-freebsd-x64': 1.0.0-beta.51
5619
+
'@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.51
5620
+
'@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.51
5621
+
'@rolldown/binding-linux-arm64-musl': 1.0.0-beta.51
5622
+
'@rolldown/binding-linux-x64-gnu': 1.0.0-beta.51
5623
+
'@rolldown/binding-linux-x64-musl': 1.0.0-beta.51
5624
+
'@rolldown/binding-openharmony-arm64': 1.0.0-beta.51
5625
+
'@rolldown/binding-wasm32-wasi': 1.0.0-beta.51
5626
+
'@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.51
5627
+
'@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.51
5628
+
'@rolldown/binding-win32-x64-msvc': 1.0.0-beta.51
5313
5629
5314
5630
rrweb-cssom@0.8.0: {}
5315
5631
···
5379
5695
dependencies:
5380
5696
eastasianwidth: 0.2.0
5381
5697
emoji-regex: 9.2.2
5698
+
strip-ansi: 7.1.2
5699
+
5700
+
string-width@7.2.0:
5701
+
dependencies:
5702
+
emoji-regex: 10.6.0
5703
+
get-east-asian-width: 1.4.0
5382
5704
strip-ansi: 7.1.2
5383
5705
5384
5706
stringify-entities@4.0.4:
···
5503
5825
diff: 8.0.2
5504
5826
empathic: 2.0.0
5505
5827
hookable: 5.5.3
5506
-
rolldown: 1.0.0-beta.50
5507
-
rolldown-plugin-dts: 0.16.12(rolldown@1.0.0-beta.50)(typescript@5.9.3)
5828
+
rolldown: 1.0.0-beta.51
5829
+
rolldown-plugin-dts: 0.16.12(rolldown@1.0.0-beta.51)(typescript@5.9.3)
5508
5830
semver: 7.7.3
5509
5831
tinyexec: 1.0.1
5510
5832
tinyglobby: 0.2.15
···
5758
6080
dependencies:
5759
6081
ansi-styles: 6.2.3
5760
6082
string-width: 5.1.2
6083
+
strip-ansi: 7.1.2
6084
+
6085
+
wrap-ansi@9.0.2:
6086
+
dependencies:
6087
+
ansi-styles: 6.2.3
6088
+
string-width: 7.2.0
5761
6089
strip-ansi: 7.1.2
5762
6090
5763
6091
ws@8.18.3: {}