a reactive (signals based) hypermedia web framework (wip) stormlightlabs.github.io/volt/
hypermedia frontend signals

feat: create-voltx CLI (#10)

* feat: add cli package for project scaffolding

* feat: add comprehensive CLI documentation and tests

authored by Owais and committed by GitHub 70f80773 91224ef4

+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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 1 + import { defineConfig } from "tsdown"; 2 + 3 + export default defineConfig({ 4 + entry: ["src/index.ts"], 5 + format: ["esm"], 6 + clean: true, 7 + dts: true, 8 + shims: true, 9 + platform: "node", 10 + target: "node18", 11 + tsconfig: "./tsconfig.json", 12 + });
+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
··· 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
··· 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
··· 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
··· 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
··· 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: {}
+1
pnpm-workspace.yaml
··· 2 2 - 'lib' 3 3 - 'dev' 4 4 - 'docs' 5 + - 'cli'