create-volt 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -6
- package/index.js +104 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,12 +27,31 @@ Edit `public/app.js` and save — the page hot-reloads itself.
|
|
|
27
27
|
|
|
28
28
|
## Options
|
|
29
29
|
|
|
30
|
-
| Flag
|
|
31
|
-
|
|
|
32
|
-
| `--
|
|
33
|
-
| `--
|
|
34
|
-
|
|
|
35
|
-
|
|
|
30
|
+
| Flag | Effect |
|
|
31
|
+
| ----------------- | ------------------------------------------------------- |
|
|
32
|
+
| `--port <number>` | Dev port for the app (default: derived from today's date)|
|
|
33
|
+
| `--skip-install` | Don't run the install step (scaffold files only) |
|
|
34
|
+
| `--no-git` | Don't initialize a git repository |
|
|
35
|
+
| `--dry-run` | Show what would be created without writing anything |
|
|
36
|
+
| `--force` | Scaffold into an existing non-empty directory |
|
|
37
|
+
| `-h`, `--help` | Show help |
|
|
38
|
+
| `-v`, `--version` | Print the create-volt version |
|
|
39
|
+
|
|
40
|
+
By default the new project is initialized as a git repository with one initial
|
|
41
|
+
commit (skip with `--no-git`).
|
|
42
|
+
|
|
43
|
+
### Dev port
|
|
44
|
+
|
|
45
|
+
Each app's dev port is baked into its `server.js`. By default it's derived from
|
|
46
|
+
**today's date** — two-digit year + month + two-digit day (e.g. `2026-06-28` →
|
|
47
|
+
`26628`) — so apps created on different days never collide. Scaffolding more than
|
|
48
|
+
one app on the same day? Give them distinct ports with `--port`:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm create volt@latest api-app -- --port 26630
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The runtime `PORT` env var still overrides it at launch.
|
|
36
55
|
|
|
37
56
|
The installer auto-detects whichever package manager invoked it
|
|
38
57
|
(npm / pnpm / yarn / bun) and uses it for `install`.
|
package/index.js
CHANGED
|
@@ -36,7 +36,10 @@ ${bold("Usage")}
|
|
|
36
36
|
npx create-volt <project-directory> [options]
|
|
37
37
|
|
|
38
38
|
${bold("Options")}
|
|
39
|
+
--port <number> Dev port for the app (default: derived from today's date)
|
|
39
40
|
--skip-install Don't run the package manager install step
|
|
41
|
+
--no-git Don't initialize a git repository
|
|
42
|
+
--dry-run Show what would be created without writing anything
|
|
40
43
|
--force Scaffold into an existing non-empty directory
|
|
41
44
|
-h, --help Show this help
|
|
42
45
|
-v, --version Show the create-volt version
|
|
@@ -46,10 +49,18 @@ ${bold("Example")}
|
|
|
46
49
|
cd my-app && npm run dev
|
|
47
50
|
`;
|
|
48
51
|
|
|
49
|
-
// --- arg parsing ---
|
|
52
|
+
// --- arg parsing (supports `--port <n>` and `--port=<n>`) ---
|
|
50
53
|
const argv = process.argv.slice(2);
|
|
51
|
-
const flags = new Set(
|
|
52
|
-
const positionals =
|
|
54
|
+
const flags = new Set();
|
|
55
|
+
const positionals = [];
|
|
56
|
+
let portArg = null;
|
|
57
|
+
for (let i = 0; i < argv.length; i++) {
|
|
58
|
+
const a = argv[i];
|
|
59
|
+
if (a === "--port") portArg = argv[++i];
|
|
60
|
+
else if (a.startsWith("--port=")) portArg = a.slice("--port=".length);
|
|
61
|
+
else if (a.startsWith("-")) flags.add(a);
|
|
62
|
+
else positionals.push(a);
|
|
63
|
+
}
|
|
53
64
|
|
|
54
65
|
if (flags.has("-h") || flags.has("--help")) {
|
|
55
66
|
console.log(HELP);
|
|
@@ -62,6 +73,30 @@ if (flags.has("-v") || flags.has("--version")) {
|
|
|
62
73
|
|
|
63
74
|
const skipInstall = flags.has("--skip-install");
|
|
64
75
|
const force = flags.has("--force");
|
|
76
|
+
const dryRun = flags.has("--dry-run");
|
|
77
|
+
const noGit = flags.has("--no-git");
|
|
78
|
+
|
|
79
|
+
// Resolve the dev port: --port wins, else derive it from today's date as
|
|
80
|
+
// two-digit-year + month (no leading zero) + two-digit-day (house convention),
|
|
81
|
+
// so apps scaffolded on different days don't collide on the same port.
|
|
82
|
+
function datePort(d) {
|
|
83
|
+
const yy = String(d.getFullYear()).slice(-2);
|
|
84
|
+
const mm = String(d.getMonth() + 1);
|
|
85
|
+
const dd = String(d.getDate()).padStart(2, "0");
|
|
86
|
+
return Number(`${yy}${mm}${dd}`);
|
|
87
|
+
}
|
|
88
|
+
let port;
|
|
89
|
+
if (portArg != null) {
|
|
90
|
+
port = Number(portArg);
|
|
91
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
92
|
+
die(`Invalid --port "${portArg}" — use a whole number between 1 and 65535.`);
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
port = datePort(new Date());
|
|
96
|
+
if (port > 65535) {
|
|
97
|
+
die(`The date-derived port (${port}) is above 65535 — pass --port <1-65535>.`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
65
100
|
|
|
66
101
|
const rawName = positionals[0];
|
|
67
102
|
if (!rawName) die(`Please specify a project directory:\n ${cyan("npm create volt@latest")} ${green("<project-directory>")}`);
|
|
@@ -79,16 +114,43 @@ const targetDir = path.resolve(process.cwd(), projectName);
|
|
|
79
114
|
const appName = path.basename(targetDir);
|
|
80
115
|
const templateDir = path.join(__dirname, "template");
|
|
81
116
|
|
|
117
|
+
// List every file in the template, relative to its root (for dry-run preview).
|
|
118
|
+
function listTemplateFiles(dir, base = dir) {
|
|
119
|
+
const out = [];
|
|
120
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
121
|
+
const full = path.join(dir, entry.name);
|
|
122
|
+
if (entry.isDirectory()) out.push(...listTemplateFiles(full, base));
|
|
123
|
+
else out.push(path.relative(base, full));
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
|
|
82
128
|
// --- target directory checks ---
|
|
83
129
|
if (fs.existsSync(targetDir)) {
|
|
84
130
|
const existing = fs.readdirSync(targetDir).filter((f) => f !== ".git");
|
|
85
131
|
if (existing.length && !force) {
|
|
86
132
|
die(`Directory ${green(projectName)} already exists and is not empty.\n Pass ${cyan("--force")} to scaffold into it anyway.`);
|
|
87
133
|
}
|
|
88
|
-
} else {
|
|
89
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
90
134
|
}
|
|
91
135
|
|
|
136
|
+
// --- dry run: print the plan and exit without writing anything ---
|
|
137
|
+
if (dryRun) {
|
|
138
|
+
console.log(`\n${bold("⚡ Dry run")} — would create a Volt app in ${cyan(targetDir)}\n`);
|
|
139
|
+
console.log("Would write:");
|
|
140
|
+
for (const f of listTemplateFiles(templateDir).sort()) {
|
|
141
|
+
// the shipped "gitignore" is renamed to ".gitignore" on scaffold
|
|
142
|
+
console.log(" " + dim(f === "gitignore" ? ".gitignore" : f));
|
|
143
|
+
}
|
|
144
|
+
console.log("");
|
|
145
|
+
console.log(dim(`Would ${skipInstall ? "skip dependency install" : "install dependencies"}.`));
|
|
146
|
+
console.log(dim(`Would ${noGit ? "skip git init" : "initialize a git repository with an initial commit"}.`));
|
|
147
|
+
console.log(dim(`Would set the dev port to ${port}.`));
|
|
148
|
+
console.log(`\n${green("✔")} Dry run complete — nothing was written.\n`);
|
|
149
|
+
process.exit(0);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
|
|
153
|
+
|
|
92
154
|
console.log(`\n${bold("⚡ Creating a new Volt app in")} ${cyan(targetDir)}\n`);
|
|
93
155
|
|
|
94
156
|
// --- copy the template tree (recursive, cross-platform) ---
|
|
@@ -107,6 +169,17 @@ const appPkg = JSON.parse(fs.readFileSync(appPkgPath, "utf8"));
|
|
|
107
169
|
appPkg.name = appName;
|
|
108
170
|
fs.writeFileSync(appPkgPath, JSON.stringify(appPkg, null, 2) + "\n");
|
|
109
171
|
|
|
172
|
+
// --- stamp the chosen dev port into server.js + README ---
|
|
173
|
+
const serverPath = path.join(targetDir, "server.js");
|
|
174
|
+
fs.writeFileSync(
|
|
175
|
+
serverPath,
|
|
176
|
+
fs.readFileSync(serverPath, "utf8").replace(/(Number\(process\.env\.PORT\)\s*\|\|\s*)\d+/, `$1${port}`),
|
|
177
|
+
);
|
|
178
|
+
const appReadme = path.join(targetDir, "README.md");
|
|
179
|
+
if (fs.existsSync(appReadme)) {
|
|
180
|
+
fs.writeFileSync(appReadme, fs.readFileSync(appReadme, "utf8").replace(/localhost:\d+/g, `localhost:${port}`));
|
|
181
|
+
}
|
|
182
|
+
|
|
110
183
|
const created = [
|
|
111
184
|
"public/volt.js — the Volt library (no build step)",
|
|
112
185
|
"public/app.js — your app (Counter + Todos demo)",
|
|
@@ -143,6 +216,31 @@ if (!skipInstall) {
|
|
|
143
216
|
}
|
|
144
217
|
}
|
|
145
218
|
|
|
219
|
+
// --- initialize a git repository (unless skipped or already inside one) ---
|
|
220
|
+
if (!noGit) {
|
|
221
|
+
const git = (gitArgs) =>
|
|
222
|
+
spawnSync("git", gitArgs, {
|
|
223
|
+
cwd: targetDir,
|
|
224
|
+
stdio: "ignore",
|
|
225
|
+
shell: process.platform === "win32",
|
|
226
|
+
});
|
|
227
|
+
const gitAvailable = git(["--version"]).status === 0;
|
|
228
|
+
const insideRepo = gitAvailable && git(["rev-parse", "--is-inside-work-tree"]).status === 0;
|
|
229
|
+
if (gitAvailable && !insideRepo) {
|
|
230
|
+
const initOk = git(["init", "-b", "main"]).status === 0 || git(["init"]).status === 0;
|
|
231
|
+
if (initOk) {
|
|
232
|
+
git(["add", "-A"]);
|
|
233
|
+
const committed = git(["commit", "-m", "Initial commit from create-volt"]).status === 0;
|
|
234
|
+
console.log(
|
|
235
|
+
green("✔") +
|
|
236
|
+
(committed
|
|
237
|
+
? " Initialized a git repository (1 commit)\n"
|
|
238
|
+
: " Initialized a git repository (commit skipped — set git user.name/email)\n"),
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
146
244
|
// --- next steps ---
|
|
147
245
|
const runCmd = pm === "npm" ? "npm run dev" : `${pm} dev`;
|
|
148
246
|
const installCmd = pm === "yarn" ? "yarn" : `${pm} install`;
|
|
@@ -150,4 +248,4 @@ console.log(`\n${green("✔")} ${bold("Done!")} Next steps:\n`);
|
|
|
150
248
|
console.log(` ${cyan("cd")} ${projectName}`);
|
|
151
249
|
if (!installed) console.log(` ${cyan(installCmd)}`);
|
|
152
250
|
console.log(` ${cyan(runCmd)}`);
|
|
153
|
-
console.log(`\nThen open ${cyan("http://localhost:
|
|
251
|
+
console.log(`\nThen open ${cyan("http://localhost:" + port)} and edit ${bold("public/app.js")}.\n`);
|