reset-framework-cli 1.2.0 → 1.2.2
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/LICENSE +20 -20
- package/README.md +19 -19
- package/package.json +8 -6
- package/src/commands/build.js +91 -73
- package/src/commands/dev.js +143 -122
- package/src/commands/doctor.js +61 -54
- package/src/commands/init.js +946 -866
- package/src/commands/package.js +68 -68
- package/src/index.js +195 -195
- package/src/lib/backend.js +123 -0
- package/src/lib/context.js +66 -66
- package/src/lib/framework.js +57 -57
- package/src/lib/logger.js +11 -11
- package/src/lib/output.js +251 -236
- package/src/lib/process.js +303 -303
- package/src/lib/project.js +531 -494
- package/src/lib/toolchain.js +62 -62
- package/src/lib/ui.js +244 -244
- package/templates/basic/README.md +15 -15
- package/templates/basic/frontend/README.md +73 -73
- package/templates/basic/frontend/eslint.config.js +23 -23
- package/templates/basic/frontend/index.html +13 -13
- package/templates/basic/frontend/package.json +31 -31
- package/templates/basic/frontend/public/icons.svg +24 -24
- package/templates/basic/frontend/src/App.css +216 -216
- package/templates/basic/frontend/src/App.tsx +77 -77
- package/templates/basic/frontend/src/assets/vite.svg +1 -1
- package/templates/basic/frontend/src/index.css +111 -111
- package/templates/basic/frontend/src/lib/reset.ts +1 -1
- package/templates/basic/frontend/src/main.tsx +10 -10
- package/templates/basic/frontend/tsconfig.app.json +28 -28
- package/templates/basic/frontend/tsconfig.json +7 -7
- package/templates/basic/frontend/tsconfig.node.json +26 -26
- package/templates/basic/frontend/vite.config.ts +16 -16
- package/templates/basic/reset.config.json +58 -58
package/src/lib/toolchain.js
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
import { existsSync } from "node:fs"
|
|
2
|
-
import { homedir } from "node:os"
|
|
3
|
-
import path from "node:path"
|
|
4
|
-
|
|
5
|
-
import { runCommand } from "./process.js"
|
|
6
|
-
|
|
7
|
-
function toToolchainPath(root) {
|
|
8
|
-
return path.join(root, "scripts", "buildsystems", "vcpkg.cmake")
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function getVcpkgToolchainCandidates() {
|
|
12
|
-
const candidates = []
|
|
13
|
-
|
|
14
|
-
if (typeof process.env.CMAKE_TOOLCHAIN_FILE === "string" && process.env.CMAKE_TOOLCHAIN_FILE.trim() !== "") {
|
|
15
|
-
candidates.push(process.env.CMAKE_TOOLCHAIN_FILE)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (typeof process.env.VCPKG_ROOT === "string" && process.env.VCPKG_ROOT.trim() !== "") {
|
|
19
|
-
candidates.push(toToolchainPath(process.env.VCPKG_ROOT))
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
candidates.push(toToolchainPath(path.join(homedir(), ".vcpkg")))
|
|
23
|
-
candidates.push(toToolchainPath(path.join(homedir(), ".reset-framework-cli", "vcpkg")))
|
|
24
|
-
|
|
25
|
-
return [...new Set(candidates)]
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function findVcpkgToolchainFile() {
|
|
29
|
-
return getVcpkgToolchainCandidates().find((candidate) => existsSync(candidate)) ?? null
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function ensureVcpkgToolchain(options = {}) {
|
|
33
|
-
const { dryRun = false } = options
|
|
34
|
-
const existing = findVcpkgToolchainFile()
|
|
35
|
-
|
|
36
|
-
if (existing) {
|
|
37
|
-
return existing
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const vcpkgRoot = path.join(homedir(), ".reset-framework-cli", "vcpkg")
|
|
41
|
-
const toolchainFile = toToolchainPath(vcpkgRoot)
|
|
42
|
-
|
|
43
|
-
if (!existsSync(vcpkgRoot)) {
|
|
44
|
-
await runCommand("git", ["clone", "https://github.com/microsoft/vcpkg", vcpkgRoot], {
|
|
45
|
-
dryRun
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (process.platform === "win32") {
|
|
50
|
-
await runCommand("cmd", ["/c", "bootstrap-vcpkg.bat", "-disableMetrics"], {
|
|
51
|
-
cwd: vcpkgRoot,
|
|
52
|
-
dryRun
|
|
53
|
-
})
|
|
54
|
-
} else {
|
|
55
|
-
await runCommand("bash", ["./bootstrap-vcpkg.sh", "-disableMetrics"], {
|
|
56
|
-
cwd: vcpkgRoot,
|
|
57
|
-
dryRun
|
|
58
|
-
})
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return toolchainFile
|
|
62
|
-
}
|
|
1
|
+
import { existsSync } from "node:fs"
|
|
2
|
+
import { homedir } from "node:os"
|
|
3
|
+
import path from "node:path"
|
|
4
|
+
|
|
5
|
+
import { runCommand } from "./process.js"
|
|
6
|
+
|
|
7
|
+
function toToolchainPath(root) {
|
|
8
|
+
return path.join(root, "scripts", "buildsystems", "vcpkg.cmake")
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getVcpkgToolchainCandidates() {
|
|
12
|
+
const candidates = []
|
|
13
|
+
|
|
14
|
+
if (typeof process.env.CMAKE_TOOLCHAIN_FILE === "string" && process.env.CMAKE_TOOLCHAIN_FILE.trim() !== "") {
|
|
15
|
+
candidates.push(process.env.CMAKE_TOOLCHAIN_FILE)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (typeof process.env.VCPKG_ROOT === "string" && process.env.VCPKG_ROOT.trim() !== "") {
|
|
19
|
+
candidates.push(toToolchainPath(process.env.VCPKG_ROOT))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
candidates.push(toToolchainPath(path.join(homedir(), ".vcpkg")))
|
|
23
|
+
candidates.push(toToolchainPath(path.join(homedir(), ".reset-framework-cli", "vcpkg")))
|
|
24
|
+
|
|
25
|
+
return [...new Set(candidates)]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function findVcpkgToolchainFile() {
|
|
29
|
+
return getVcpkgToolchainCandidates().find((candidate) => existsSync(candidate)) ?? null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function ensureVcpkgToolchain(options = {}) {
|
|
33
|
+
const { dryRun = false } = options
|
|
34
|
+
const existing = findVcpkgToolchainFile()
|
|
35
|
+
|
|
36
|
+
if (existing) {
|
|
37
|
+
return existing
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const vcpkgRoot = path.join(homedir(), ".reset-framework-cli", "vcpkg")
|
|
41
|
+
const toolchainFile = toToolchainPath(vcpkgRoot)
|
|
42
|
+
|
|
43
|
+
if (!existsSync(vcpkgRoot)) {
|
|
44
|
+
await runCommand("git", ["clone", "https://github.com/microsoft/vcpkg", vcpkgRoot], {
|
|
45
|
+
dryRun
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (process.platform === "win32") {
|
|
50
|
+
await runCommand("cmd", ["/c", "bootstrap-vcpkg.bat", "-disableMetrics"], {
|
|
51
|
+
cwd: vcpkgRoot,
|
|
52
|
+
dryRun
|
|
53
|
+
})
|
|
54
|
+
} else {
|
|
55
|
+
await runCommand("bash", ["./bootstrap-vcpkg.sh", "-disableMetrics"], {
|
|
56
|
+
cwd: vcpkgRoot,
|
|
57
|
+
dryRun
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return toolchainFile
|
|
62
|
+
}
|
package/src/lib/ui.js
CHANGED
|
@@ -1,244 +1,244 @@
|
|
|
1
|
-
import readline from "node:readline/promises"
|
|
2
|
-
import { stdin as input, stdout as output } from "node:process"
|
|
3
|
-
|
|
4
|
-
const ansi = {
|
|
5
|
-
reset: "\u001b[0m",
|
|
6
|
-
bold: "\u001b[1m",
|
|
7
|
-
dim: "\u001b[2m",
|
|
8
|
-
cyan: "\u001b[36m",
|
|
9
|
-
gray: "\u001b[90m",
|
|
10
|
-
green: "\u001b[32m",
|
|
11
|
-
yellow: "\u001b[33m",
|
|
12
|
-
red: "\u001b[31m"
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function style(text, code) {
|
|
16
|
-
if (!output.isTTY) {
|
|
17
|
-
return text
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return `${code}${text}${ansi.reset}`
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function isInteractiveSession() {
|
|
24
|
-
return Boolean(input.isTTY && output.isTTY)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function bold(text) {
|
|
28
|
-
return style(text, ansi.bold)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function dim(text) {
|
|
32
|
-
return style(text, ansi.dim)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function accent(text) {
|
|
36
|
-
return style(text, ansi.cyan)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function success(text) {
|
|
40
|
-
return style(text, ansi.green)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function warning(text) {
|
|
44
|
-
return style(text, ansi.yellow)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function danger(text) {
|
|
48
|
-
return style(text, ansi.red)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function printBanner(title, subtitle) {
|
|
52
|
-
console.log(bold(title))
|
|
53
|
-
if (subtitle) {
|
|
54
|
-
console.log(dim(subtitle))
|
|
55
|
-
}
|
|
56
|
-
console.log("")
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function printSection(title) {
|
|
60
|
-
console.log(accent(title))
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function printMutedLine(text = "") {
|
|
64
|
-
console.log(dim(text))
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function printKeyValueTable(entries) {
|
|
68
|
-
if (entries.length === 0) {
|
|
69
|
-
return
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const width = entries.reduce((max, [key]) => Math.max(max, key.length), 0)
|
|
73
|
-
|
|
74
|
-
for (const [key, value] of entries) {
|
|
75
|
-
console.log(` ${dim(key.padEnd(width))} ${value}`)
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function formatStatus(status, width) {
|
|
80
|
-
const normalized = String(status).toLowerCase()
|
|
81
|
-
const padded = normalized.toUpperCase().padEnd(width)
|
|
82
|
-
|
|
83
|
-
if (normalized === "ok" || normalized === "done" || normalized === "ready") {
|
|
84
|
-
return success(padded)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (normalized === "warn" || normalized === "skip") {
|
|
88
|
-
return warning(padded)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (normalized === "missing" || normalized === "error") {
|
|
92
|
-
return danger(padded)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return accent(padded)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function printStatusTable(entries) {
|
|
99
|
-
if (entries.length === 0) {
|
|
100
|
-
return
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const statusWidth = entries.reduce((max, [status]) => Math.max(max, String(status).length), 0)
|
|
104
|
-
const labelWidth = entries.reduce((max, [, label]) => Math.max(max, label.length), 0)
|
|
105
|
-
|
|
106
|
-
for (const [status, label, detail = ""] of entries) {
|
|
107
|
-
const suffix = detail ? ` ${detail}` : ""
|
|
108
|
-
console.log(` ${formatStatus(status, statusWidth)} ${label.padEnd(labelWidth)}${suffix}`)
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function printCommandList(commands) {
|
|
113
|
-
if (commands.length === 0) {
|
|
114
|
-
return
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const width = commands.reduce((max, command) => Math.max(max, command.name.length), 0)
|
|
118
|
-
|
|
119
|
-
for (const command of commands) {
|
|
120
|
-
console.log(` ${bold(command.name.padEnd(width))} ${command.description}`)
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function createProgress(total, label = "Progress") {
|
|
125
|
-
let current = 0
|
|
126
|
-
let lastWidth = 0
|
|
127
|
-
|
|
128
|
-
function render(message) {
|
|
129
|
-
const width = 24
|
|
130
|
-
const ratio = total === 0 ? 1 : current / total
|
|
131
|
-
const filled = Math.round(ratio * width)
|
|
132
|
-
const percentage = `${Math.round(ratio * 100)}`.padStart(3, " ")
|
|
133
|
-
const bar = `${"#".repeat(filled)}${"-".repeat(width - filled)}`
|
|
134
|
-
const line = `${dim(label)} [${bar}] ${percentage}% ${message}`
|
|
135
|
-
|
|
136
|
-
if (output.isTTY) {
|
|
137
|
-
const padded = line.padEnd(lastWidth, " ")
|
|
138
|
-
output.write(`\r${padded}`)
|
|
139
|
-
lastWidth = Math.max(lastWidth, line.length)
|
|
140
|
-
if (current >= total) {
|
|
141
|
-
output.write("\n")
|
|
142
|
-
}
|
|
143
|
-
return
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
console.log(line)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
tick(message) {
|
|
151
|
-
current += 1
|
|
152
|
-
render(message)
|
|
153
|
-
},
|
|
154
|
-
complete(message) {
|
|
155
|
-
current = total
|
|
156
|
-
render(message)
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export async function withPromptSession(callback) {
|
|
162
|
-
const rl = readline.createInterface({ input, output })
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
return await callback(rl)
|
|
166
|
-
} finally {
|
|
167
|
-
rl.close()
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export async function promptText(rl, options) {
|
|
172
|
-
const { label, defaultValue = "", validate } = options
|
|
173
|
-
|
|
174
|
-
while (true) {
|
|
175
|
-
const suffix = defaultValue ? ` ${dim(`[${defaultValue}]`)}` : ""
|
|
176
|
-
const answer = (await rl.question(`${label}${suffix}: `)).trim()
|
|
177
|
-
const value = answer === "" ? defaultValue : answer
|
|
178
|
-
|
|
179
|
-
if (!validate) {
|
|
180
|
-
return value
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const result = validate(value)
|
|
184
|
-
if (result === true) {
|
|
185
|
-
return value
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
console.log(dim(typeof result === "string" ? result : "Invalid value"))
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
export async function promptConfirm(rl, options) {
|
|
193
|
-
const { label, defaultValue = true } = options
|
|
194
|
-
const hint = defaultValue ? "[Y/n]" : "[y/N]"
|
|
195
|
-
|
|
196
|
-
while (true) {
|
|
197
|
-
const answer = (await rl.question(`${label} ${dim(hint)}: `)).trim().toLowerCase()
|
|
198
|
-
|
|
199
|
-
if (answer === "") {
|
|
200
|
-
return defaultValue
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (answer === "y" || answer === "yes") {
|
|
204
|
-
return true
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (answer === "n" || answer === "no") {
|
|
208
|
-
return false
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
export async function promptSelect(rl, options) {
|
|
214
|
-
const { label, choices, defaultValue } = options
|
|
215
|
-
|
|
216
|
-
printSection(label)
|
|
217
|
-
for (let index = 0; index < choices.length; index += 1) {
|
|
218
|
-
const choice = choices[index]
|
|
219
|
-
const marker = `${index + 1}.`
|
|
220
|
-
const recommended = choice.value === defaultValue ? ` ${dim("(recommended)")}` : ""
|
|
221
|
-
|
|
222
|
-
console.log(` ${marker} ${choice.label}${recommended}`)
|
|
223
|
-
if (choice.description) {
|
|
224
|
-
console.log(` ${dim(choice.description)}`)
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const fallbackIndex = Math.max(
|
|
229
|
-
0,
|
|
230
|
-
choices.findIndex((choice) => choice.value === defaultValue)
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
while (true) {
|
|
234
|
-
const answer = (await rl.question(`Choose ${dim(`[${fallbackIndex + 1}]`)}: `)).trim()
|
|
235
|
-
const selectedIndex = answer === "" ? fallbackIndex : Number.parseInt(answer, 10) - 1
|
|
236
|
-
|
|
237
|
-
if (choices[selectedIndex]) {
|
|
238
|
-
console.log("")
|
|
239
|
-
return choices[selectedIndex].value
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
console.log(dim("Select one of the listed options."))
|
|
243
|
-
}
|
|
244
|
-
}
|
|
1
|
+
import readline from "node:readline/promises"
|
|
2
|
+
import { stdin as input, stdout as output } from "node:process"
|
|
3
|
+
|
|
4
|
+
const ansi = {
|
|
5
|
+
reset: "\u001b[0m",
|
|
6
|
+
bold: "\u001b[1m",
|
|
7
|
+
dim: "\u001b[2m",
|
|
8
|
+
cyan: "\u001b[36m",
|
|
9
|
+
gray: "\u001b[90m",
|
|
10
|
+
green: "\u001b[32m",
|
|
11
|
+
yellow: "\u001b[33m",
|
|
12
|
+
red: "\u001b[31m"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function style(text, code) {
|
|
16
|
+
if (!output.isTTY) {
|
|
17
|
+
return text
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return `${code}${text}${ansi.reset}`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function isInteractiveSession() {
|
|
24
|
+
return Boolean(input.isTTY && output.isTTY)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function bold(text) {
|
|
28
|
+
return style(text, ansi.bold)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function dim(text) {
|
|
32
|
+
return style(text, ansi.dim)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function accent(text) {
|
|
36
|
+
return style(text, ansi.cyan)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function success(text) {
|
|
40
|
+
return style(text, ansi.green)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function warning(text) {
|
|
44
|
+
return style(text, ansi.yellow)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function danger(text) {
|
|
48
|
+
return style(text, ansi.red)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function printBanner(title, subtitle) {
|
|
52
|
+
console.log(bold(title))
|
|
53
|
+
if (subtitle) {
|
|
54
|
+
console.log(dim(subtitle))
|
|
55
|
+
}
|
|
56
|
+
console.log("")
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function printSection(title) {
|
|
60
|
+
console.log(accent(title))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function printMutedLine(text = "") {
|
|
64
|
+
console.log(dim(text))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function printKeyValueTable(entries) {
|
|
68
|
+
if (entries.length === 0) {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const width = entries.reduce((max, [key]) => Math.max(max, key.length), 0)
|
|
73
|
+
|
|
74
|
+
for (const [key, value] of entries) {
|
|
75
|
+
console.log(` ${dim(key.padEnd(width))} ${value}`)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function formatStatus(status, width) {
|
|
80
|
+
const normalized = String(status).toLowerCase()
|
|
81
|
+
const padded = normalized.toUpperCase().padEnd(width)
|
|
82
|
+
|
|
83
|
+
if (normalized === "ok" || normalized === "done" || normalized === "ready") {
|
|
84
|
+
return success(padded)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (normalized === "warn" || normalized === "skip") {
|
|
88
|
+
return warning(padded)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (normalized === "missing" || normalized === "error") {
|
|
92
|
+
return danger(padded)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return accent(padded)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function printStatusTable(entries) {
|
|
99
|
+
if (entries.length === 0) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const statusWidth = entries.reduce((max, [status]) => Math.max(max, String(status).length), 0)
|
|
104
|
+
const labelWidth = entries.reduce((max, [, label]) => Math.max(max, label.length), 0)
|
|
105
|
+
|
|
106
|
+
for (const [status, label, detail = ""] of entries) {
|
|
107
|
+
const suffix = detail ? ` ${detail}` : ""
|
|
108
|
+
console.log(` ${formatStatus(status, statusWidth)} ${label.padEnd(labelWidth)}${suffix}`)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function printCommandList(commands) {
|
|
113
|
+
if (commands.length === 0) {
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const width = commands.reduce((max, command) => Math.max(max, command.name.length), 0)
|
|
118
|
+
|
|
119
|
+
for (const command of commands) {
|
|
120
|
+
console.log(` ${bold(command.name.padEnd(width))} ${command.description}`)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function createProgress(total, label = "Progress") {
|
|
125
|
+
let current = 0
|
|
126
|
+
let lastWidth = 0
|
|
127
|
+
|
|
128
|
+
function render(message) {
|
|
129
|
+
const width = 24
|
|
130
|
+
const ratio = total === 0 ? 1 : current / total
|
|
131
|
+
const filled = Math.round(ratio * width)
|
|
132
|
+
const percentage = `${Math.round(ratio * 100)}`.padStart(3, " ")
|
|
133
|
+
const bar = `${"#".repeat(filled)}${"-".repeat(width - filled)}`
|
|
134
|
+
const line = `${dim(label)} [${bar}] ${percentage}% ${message}`
|
|
135
|
+
|
|
136
|
+
if (output.isTTY) {
|
|
137
|
+
const padded = line.padEnd(lastWidth, " ")
|
|
138
|
+
output.write(`\r${padded}`)
|
|
139
|
+
lastWidth = Math.max(lastWidth, line.length)
|
|
140
|
+
if (current >= total) {
|
|
141
|
+
output.write("\n")
|
|
142
|
+
}
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.log(line)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
tick(message) {
|
|
151
|
+
current += 1
|
|
152
|
+
render(message)
|
|
153
|
+
},
|
|
154
|
+
complete(message) {
|
|
155
|
+
current = total
|
|
156
|
+
render(message)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export async function withPromptSession(callback) {
|
|
162
|
+
const rl = readline.createInterface({ input, output })
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
return await callback(rl)
|
|
166
|
+
} finally {
|
|
167
|
+
rl.close()
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export async function promptText(rl, options) {
|
|
172
|
+
const { label, defaultValue = "", validate } = options
|
|
173
|
+
|
|
174
|
+
while (true) {
|
|
175
|
+
const suffix = defaultValue ? ` ${dim(`[${defaultValue}]`)}` : ""
|
|
176
|
+
const answer = (await rl.question(`${label}${suffix}: `)).trim()
|
|
177
|
+
const value = answer === "" ? defaultValue : answer
|
|
178
|
+
|
|
179
|
+
if (!validate) {
|
|
180
|
+
return value
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const result = validate(value)
|
|
184
|
+
if (result === true) {
|
|
185
|
+
return value
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
console.log(dim(typeof result === "string" ? result : "Invalid value"))
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export async function promptConfirm(rl, options) {
|
|
193
|
+
const { label, defaultValue = true } = options
|
|
194
|
+
const hint = defaultValue ? "[Y/n]" : "[y/N]"
|
|
195
|
+
|
|
196
|
+
while (true) {
|
|
197
|
+
const answer = (await rl.question(`${label} ${dim(hint)}: `)).trim().toLowerCase()
|
|
198
|
+
|
|
199
|
+
if (answer === "") {
|
|
200
|
+
return defaultValue
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (answer === "y" || answer === "yes") {
|
|
204
|
+
return true
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (answer === "n" || answer === "no") {
|
|
208
|
+
return false
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export async function promptSelect(rl, options) {
|
|
214
|
+
const { label, choices, defaultValue } = options
|
|
215
|
+
|
|
216
|
+
printSection(label)
|
|
217
|
+
for (let index = 0; index < choices.length; index += 1) {
|
|
218
|
+
const choice = choices[index]
|
|
219
|
+
const marker = `${index + 1}.`
|
|
220
|
+
const recommended = choice.value === defaultValue ? ` ${dim("(recommended)")}` : ""
|
|
221
|
+
|
|
222
|
+
console.log(` ${marker} ${choice.label}${recommended}`)
|
|
223
|
+
if (choice.description) {
|
|
224
|
+
console.log(` ${dim(choice.description)}`)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const fallbackIndex = Math.max(
|
|
229
|
+
0,
|
|
230
|
+
choices.findIndex((choice) => choice.value === defaultValue)
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
while (true) {
|
|
234
|
+
const answer = (await rl.question(`Choose ${dim(`[${fallbackIndex + 1}]`)}: `)).trim()
|
|
235
|
+
const selectedIndex = answer === "" ? fallbackIndex : Number.parseInt(answer, 10) - 1
|
|
236
|
+
|
|
237
|
+
if (choices[selectedIndex]) {
|
|
238
|
+
console.log("")
|
|
239
|
+
return choices[selectedIndex].value
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
console.log(dim("Select one of the listed options."))
|
|
243
|
+
}
|
|
244
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
# Reset App
|
|
2
|
-
|
|
3
|
-
Generated with `reset-framework-cli`.
|
|
4
|
-
|
|
5
|
-
## Commands
|
|
6
|
-
|
|
7
|
-
- `reset-framework-cli dev`
|
|
8
|
-
- `reset-framework-cli build`
|
|
9
|
-
- `reset-framework-cli package`
|
|
10
|
-
|
|
11
|
-
## Project files
|
|
12
|
-
|
|
13
|
-
- `reset.config.json`: desktop app metadata and runtime config
|
|
14
|
-
- `frontend/` or project root: web frontend source, depending on the selected layout
|
|
15
|
-
- `.reset/`: generated build output and native runtime cache
|
|
1
|
+
# Reset App
|
|
2
|
+
|
|
3
|
+
Generated with `reset-framework-cli`.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
- `reset-framework-cli dev`
|
|
8
|
+
- `reset-framework-cli build`
|
|
9
|
+
- `reset-framework-cli package`
|
|
10
|
+
|
|
11
|
+
## Project files
|
|
12
|
+
|
|
13
|
+
- `reset.config.json`: desktop app metadata and runtime config
|
|
14
|
+
- `frontend/` or project root: web frontend source, depending on the selected layout
|
|
15
|
+
- `.reset/`: generated build output and native runtime cache
|