create-cascade 0.1.4 → 0.1.5
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 +8 -7
- package/index.js +152 -49
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ bun create cascade
|
|
|
13
13
|
- Create in a new folder or directly in the current folder
|
|
14
14
|
- Interactive framework selection (`core`, `react`, `solid`)
|
|
15
15
|
- Multiple built-in starter code presets per framework
|
|
16
|
-
-
|
|
16
|
+
- Scaffold only by default (no install, no auto-run)
|
|
17
17
|
|
|
18
18
|
## Frameworks
|
|
19
19
|
|
|
@@ -48,8 +48,8 @@ Options:
|
|
|
48
48
|
-f, --framework <name> Framework: core, react, solid
|
|
49
49
|
-s, --starter <name> Starter preset for selected framework
|
|
50
50
|
--here Use current directory
|
|
51
|
-
--
|
|
52
|
-
--
|
|
51
|
+
--install Run bun install after scaffolding
|
|
52
|
+
--start Run bun install, then bun run dev
|
|
53
53
|
-h, --help Show help
|
|
54
54
|
```
|
|
55
55
|
|
|
@@ -65,14 +65,15 @@ bun create cascade --here
|
|
|
65
65
|
# Create React app with counter starter
|
|
66
66
|
bun create cascade my-app -f react -s counter
|
|
67
67
|
|
|
68
|
-
# Scaffold
|
|
69
|
-
bun create cascade my-app --
|
|
68
|
+
# Scaffold then install
|
|
69
|
+
bun create cascade my-app --install
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
## Behavior
|
|
73
73
|
|
|
74
|
-
-
|
|
75
|
-
-
|
|
74
|
+
- By default, the CLI only scaffolds files.
|
|
75
|
+
- With `--install`, it runs `bun install` in the target directory.
|
|
76
|
+
- With `--start`, it runs `bun install` then `bun run dev`.
|
|
76
77
|
|
|
77
78
|
## Package
|
|
78
79
|
|
package/index.js
CHANGED
|
@@ -5,6 +5,12 @@ import { basename, resolve } from "node:path"
|
|
|
5
5
|
import process from "node:process"
|
|
6
6
|
import { spawnSync } from "node:child_process"
|
|
7
7
|
import { createInterface } from "node:readline/promises"
|
|
8
|
+
import { emitKeypressEvents } from "node:readline"
|
|
9
|
+
|
|
10
|
+
const ANSI_RESET = "\x1b[0m"
|
|
11
|
+
const ANSI_BOLD = "\x1b[1m"
|
|
12
|
+
const ANSI_DIM = "\x1b[2m"
|
|
13
|
+
const ANSI_CYAN = "\x1b[36m"
|
|
8
14
|
|
|
9
15
|
const FRAMEWORKS = [
|
|
10
16
|
{ id: "core", label: "Core", description: "Vanilla Cascade API with renderables" },
|
|
@@ -33,8 +39,8 @@ const STARTERS = {
|
|
|
33
39
|
function parseArgs(argv) {
|
|
34
40
|
const args = argv.slice(2)
|
|
35
41
|
const options = {
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
install: false,
|
|
43
|
+
start: false,
|
|
38
44
|
here: false,
|
|
39
45
|
framework: undefined,
|
|
40
46
|
starter: undefined,
|
|
@@ -45,12 +51,21 @@ function parseArgs(argv) {
|
|
|
45
51
|
for (let i = 0; i < args.length; i += 1) {
|
|
46
52
|
const arg = args[i]
|
|
47
53
|
|
|
54
|
+
if (arg === "--install") {
|
|
55
|
+
options.install = true
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
if (arg === "--start") {
|
|
59
|
+
options.start = true
|
|
60
|
+
options.install = true
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
48
63
|
if (arg === "--no-install") {
|
|
49
|
-
options.
|
|
64
|
+
options.install = false
|
|
50
65
|
continue
|
|
51
66
|
}
|
|
52
67
|
if (arg === "--no-start") {
|
|
53
|
-
options.
|
|
68
|
+
options.start = false
|
|
54
69
|
continue
|
|
55
70
|
}
|
|
56
71
|
if (arg === "--here") {
|
|
@@ -84,8 +99,8 @@ function printHelp() {
|
|
|
84
99
|
console.log(" -f, --framework <name> Framework: core, react, solid")
|
|
85
100
|
console.log(" -s, --starter <name> Starter preset for selected framework")
|
|
86
101
|
console.log(" --here Use current directory")
|
|
87
|
-
console.log(" --
|
|
88
|
-
console.log(" --
|
|
102
|
+
console.log(" --install Run bun install after scaffolding")
|
|
103
|
+
console.log(" --start Run bun install, then bun run dev")
|
|
89
104
|
console.log(" -h, --help Show help")
|
|
90
105
|
console.log("")
|
|
91
106
|
console.log("Examples:")
|
|
@@ -118,47 +133,83 @@ function promptLine(rl, label) {
|
|
|
118
133
|
}
|
|
119
134
|
|
|
120
135
|
async function selectOption(rl, label, options) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
for (let i = 0; i < options.length; i += 1) {
|
|
124
|
-
const option = options[i]
|
|
125
|
-
console.log(` ${i + 1}. ${option.label} (${option.id}) - ${option.description}`)
|
|
136
|
+
if (!process.stdin.isTTY) {
|
|
137
|
+
return options[0].id
|
|
126
138
|
}
|
|
127
139
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
140
|
+
const stdin = process.stdin
|
|
141
|
+
const stdout = process.stdout
|
|
142
|
+
let selectedIndex = 0
|
|
143
|
+
const totalLines = options.length + 2
|
|
144
|
+
let renderedOnce = false
|
|
145
|
+
|
|
146
|
+
const render = () => {
|
|
147
|
+
if (renderedOnce) {
|
|
148
|
+
stdout.write(`\x1b[${totalLines}F`)
|
|
149
|
+
} else {
|
|
150
|
+
stdout.write("\n")
|
|
133
151
|
}
|
|
134
|
-
console.log("Invalid choice. Try again.")
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
152
|
|
|
138
|
-
|
|
139
|
-
if (options.here) {
|
|
140
|
-
return "."
|
|
141
|
-
}
|
|
153
|
+
stdout.write(`${ANSI_BOLD}${label}${ANSI_RESET}\n`)
|
|
142
154
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
155
|
+
for (let i = 0; i < options.length; i += 1) {
|
|
156
|
+
const option = options[i]
|
|
157
|
+
const isSelected = i === selectedIndex
|
|
158
|
+
const prefix = isSelected ? `${ANSI_BOLD}${ANSI_CYAN}>${ANSI_RESET}` : " "
|
|
159
|
+
const styleStart = isSelected ? `${ANSI_BOLD}${ANSI_CYAN}` : ""
|
|
160
|
+
const styleEnd = isSelected ? ANSI_RESET : ""
|
|
161
|
+
stdout.write(`${prefix} ${styleStart}${option.label}${styleEnd} ${ANSI_DIM}(${option.id}) - ${option.description}${ANSI_RESET}\n`)
|
|
162
|
+
}
|
|
146
163
|
|
|
147
|
-
|
|
148
|
-
|
|
164
|
+
stdout.write(`${ANSI_DIM}Use Up/Down arrows and Enter${ANSI_RESET}\n`)
|
|
165
|
+
renderedOnce = true
|
|
149
166
|
}
|
|
150
167
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
168
|
+
return new Promise((resolve, reject) => {
|
|
169
|
+
const cleanup = () => {
|
|
170
|
+
stdin.off("keypress", onKeyPress)
|
|
171
|
+
if (stdin.isTTY) {
|
|
172
|
+
stdin.setRawMode(false)
|
|
173
|
+
}
|
|
174
|
+
stdout.write("\n")
|
|
175
|
+
}
|
|
155
176
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
177
|
+
const onKeyPress = (_, key) => {
|
|
178
|
+
if (!key) {
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (key.ctrl && key.name === "c") {
|
|
183
|
+
cleanup()
|
|
184
|
+
reject(new Error("Operation cancelled"))
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (key.name === "up") {
|
|
189
|
+
selectedIndex = selectedIndex === 0 ? options.length - 1 : selectedIndex - 1
|
|
190
|
+
render()
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (key.name === "down") {
|
|
195
|
+
selectedIndex = selectedIndex === options.length - 1 ? 0 : selectedIndex + 1
|
|
196
|
+
render()
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (key.name === "return") {
|
|
201
|
+
const selected = options[selectedIndex]
|
|
202
|
+
cleanup()
|
|
203
|
+
resolve(selected.id)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
159
206
|
|
|
160
|
-
|
|
161
|
-
|
|
207
|
+
emitKeypressEvents(stdin)
|
|
208
|
+
stdin.setRawMode(true)
|
|
209
|
+
stdin.resume()
|
|
210
|
+
stdin.on("keypress", onKeyPress)
|
|
211
|
+
render()
|
|
212
|
+
})
|
|
162
213
|
}
|
|
163
214
|
|
|
164
215
|
async function resolveFramework(rl, frameworkArg) {
|
|
@@ -196,6 +247,46 @@ async function resolveStarter(rl, framework, starterArg) {
|
|
|
196
247
|
return selectOption(rl, `Choose a starter for ${framework}:`, choices)
|
|
197
248
|
}
|
|
198
249
|
|
|
250
|
+
async function resolveLocation(rl, options, positionals) {
|
|
251
|
+
if (options.here) {
|
|
252
|
+
return "here"
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (positionals[0]) {
|
|
256
|
+
return "new"
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!process.stdin.isTTY) {
|
|
260
|
+
return "new"
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return selectOption(rl, "Where should the project be created?", [
|
|
264
|
+
{ id: "new", label: "New folder", description: "Create and use a new project directory" },
|
|
265
|
+
{ id: "here", label: "Current folder", description: "Use the current directory directly" },
|
|
266
|
+
])
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function resolveProjectName(rl, positionals, defaultName = "cascade-app") {
|
|
270
|
+
if (positionals[0]) {
|
|
271
|
+
return positionals[0]
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (!process.stdin.isTTY) {
|
|
275
|
+
return defaultName
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const input = (await promptLine(rl, `Project name (default: ${defaultName}): `)).trim()
|
|
279
|
+
return input || defaultName
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function shouldRunInteractiveWizard(options, positionals) {
|
|
283
|
+
if (!process.stdin.isTTY) {
|
|
284
|
+
return false
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return !positionals[0] && !options.here && !options.framework && !options.starter
|
|
288
|
+
}
|
|
289
|
+
|
|
199
290
|
function getPackageJson(projectName, framework) {
|
|
200
291
|
const dependencies = {
|
|
201
292
|
"@cascadetui/core": "latest",
|
|
@@ -502,13 +593,27 @@ async function main() {
|
|
|
502
593
|
})
|
|
503
594
|
|
|
504
595
|
try {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
596
|
+
let framework
|
|
597
|
+
let projectName
|
|
598
|
+
let location
|
|
599
|
+
let starter
|
|
600
|
+
|
|
601
|
+
if (shouldRunInteractiveWizard(options, positionals)) {
|
|
602
|
+
framework = await resolveFramework(rl, undefined)
|
|
603
|
+
projectName = await resolveProjectName(rl, [], "cascade-app")
|
|
604
|
+
location = await resolveLocation(rl, { ...options, here: false }, [])
|
|
605
|
+
starter = await resolveStarter(rl, framework, undefined)
|
|
606
|
+
} else {
|
|
607
|
+
framework = await resolveFramework(rl, options.framework)
|
|
608
|
+
location = await resolveLocation(rl, options, positionals)
|
|
609
|
+
const defaultName = location === "here" ? basename(process.cwd()) : "cascade-app"
|
|
610
|
+
projectName = await resolveProjectName(rl, positionals, defaultName)
|
|
611
|
+
starter = await resolveStarter(rl, framework, options.starter)
|
|
612
|
+
}
|
|
508
613
|
|
|
509
|
-
const usingCurrentDirectory =
|
|
510
|
-
const targetDir = usingCurrentDirectory ? process.cwd() : resolve(process.cwd(),
|
|
511
|
-
const packageNameSeed =
|
|
614
|
+
const usingCurrentDirectory = location === "here"
|
|
615
|
+
const targetDir = usingCurrentDirectory ? process.cwd() : resolve(process.cwd(), projectName)
|
|
616
|
+
const packageNameSeed = projectName
|
|
512
617
|
|
|
513
618
|
writeProject(targetDir, packageNameSeed, framework, starter)
|
|
514
619
|
|
|
@@ -517,13 +622,13 @@ async function main() {
|
|
|
517
622
|
console.log(`Framework: ${framework}`)
|
|
518
623
|
console.log(`Starter: ${starter}`)
|
|
519
624
|
|
|
520
|
-
if (
|
|
625
|
+
if (options.install) {
|
|
521
626
|
console.log("")
|
|
522
627
|
console.log("Installing dependencies with bun...")
|
|
523
628
|
runCommand("bun", ["install"], targetDir)
|
|
524
629
|
}
|
|
525
630
|
|
|
526
|
-
if (
|
|
631
|
+
if (options.start) {
|
|
527
632
|
console.log("")
|
|
528
633
|
console.log("Starting the project...")
|
|
529
634
|
runCommand("bun", ["run", "dev"], targetDir)
|
|
@@ -533,11 +638,9 @@ async function main() {
|
|
|
533
638
|
console.log("")
|
|
534
639
|
console.log("Next steps:")
|
|
535
640
|
if (!usingCurrentDirectory) {
|
|
536
|
-
console.log(` cd ${
|
|
537
|
-
}
|
|
538
|
-
if (options.noInstall) {
|
|
539
|
-
console.log(" bun install")
|
|
641
|
+
console.log(` cd ${projectName}`)
|
|
540
642
|
}
|
|
643
|
+
console.log(" bun install")
|
|
541
644
|
console.log(" bun run dev")
|
|
542
645
|
} finally {
|
|
543
646
|
rl.close()
|