a reactive (signals based) hypermedia web framework (wip)
stormlightlabs.github.io/volt/
hypermedia
frontend
signals
1import {
2 generateMinimalCSS,
3 generateMinimalHTML,
4 generateMinimalPackageJSON,
5 generateMinimalREADME,
6} from "$templates/minimal.js";
7import {
8 generateStylesCSS,
9 generateStylesHTML,
10 generateStylesPackageJSON,
11 generateStylesREADME,
12} from "$templates/styles.js";
13import {
14 generatePluginsCSS,
15 generatePluginsHTML,
16 generatePluginsPackageJSON,
17 generatePluginsREADME,
18} from "$templates/with-plugins.js";
19import {
20 generateRouterCSS,
21 generateRouterHTML,
22 generateRouterPackageJSON,
23 generateRouterREADME,
24} from "$templates/with-router.js";
25import { downloadFile, getCDNUrls } from "$utils/download.js";
26import { echo } from "$utils/echo.js";
27import { createFile, isEmptyOrMissing } from "$utils/files.js";
28import { input, select } from "@inquirer/prompts";
29import path from "node:path";
30
31type Template = "minimal" | "with-router" | "with-plugins" | "styles";
32
33/**
34 * Download VoltX.js assets to the project directory.
35 */
36async 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 */
55async 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 */
111export 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}