reset-framework-cli 1.2.1 → 1.2.4
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 +25 -25
- package/package.json +8 -6
- package/src/commands/build.js +144 -126
- package/src/commands/dev.js +195 -174
- package/src/commands/doctor.js +140 -133
- 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 +283 -268
- package/src/lib/process.js +303 -303
- package/src/lib/project.js +897 -833
- 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/commands/package.js
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
import { existsSync } from "node:fs"
|
|
2
|
-
|
|
3
|
-
import { createMacOSZipArchive, createWindowsZipArchive } from "../lib/output.js"
|
|
4
|
-
import {
|
|
5
|
-
assertAppProject,
|
|
6
|
-
loadResetConfig,
|
|
7
|
-
resolveAppOutputPaths,
|
|
8
|
-
resolveAppPaths
|
|
9
|
-
} from "../lib/project.js"
|
|
10
|
-
import {
|
|
11
|
-
createProgress,
|
|
12
|
-
printBanner,
|
|
13
|
-
printKeyValueTable,
|
|
14
|
-
printSection,
|
|
15
|
-
printStatusTable
|
|
16
|
-
} from "../lib/ui.js"
|
|
17
|
-
|
|
18
|
-
export const description = "Package the built desktop app into a distributable archive"
|
|
19
|
-
|
|
20
|
-
export async function run(context) {
|
|
21
|
-
const dryRun = Boolean(context.flags["dry-run"])
|
|
22
|
-
const isWindows = process.platform === "win32"
|
|
23
|
-
const archiveBuilder = isWindows ? createWindowsZipArchive : createMacOSZipArchive
|
|
24
|
-
const archiveDescription = isWindows
|
|
25
|
-
? "Create a Windows zip package from the built app folder"
|
|
26
|
-
: "Create a macOS zip package from the built app bundle"
|
|
27
|
-
const appPaths = resolveAppPaths(context.projectRoot)
|
|
28
|
-
assertAppProject(appPaths)
|
|
29
|
-
|
|
30
|
-
const config = loadResetConfig(appPaths)
|
|
31
|
-
assertAppProject(appPaths, config)
|
|
32
|
-
const outputPaths = resolveAppOutputPaths(appPaths, config)
|
|
33
|
-
|
|
34
|
-
printBanner("reset-framework-cli package", description)
|
|
35
|
-
printSection("Project")
|
|
36
|
-
printKeyValueTable([
|
|
37
|
-
["Desktop app", outputPaths.appBundlePath],
|
|
38
|
-
["Archive", outputPaths.zipPath]
|
|
39
|
-
])
|
|
40
|
-
console.log("")
|
|
41
|
-
|
|
42
|
-
printSection("Plan")
|
|
43
|
-
printStatusTable([
|
|
44
|
-
["ready", "Archive", archiveDescription]
|
|
45
|
-
])
|
|
46
|
-
|
|
47
|
-
if (!existsSync(outputPaths.appBundlePath) && !dryRun) {
|
|
48
|
-
throw new Error(
|
|
49
|
-
`Missing built app bundle at ${outputPaths.appBundlePath}. Run 'reset-framework-cli build' first.`
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (dryRun) {
|
|
54
|
-
console.log("")
|
|
55
|
-
printSection("Dry run")
|
|
56
|
-
printStatusTable([["plan", "Package", "No archive will be written"]])
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
console.log("")
|
|
61
|
-
const progress = createProgress(1, "Package")
|
|
62
|
-
await archiveBuilder(outputPaths, { dryRun })
|
|
63
|
-
progress.tick("Archive created")
|
|
64
|
-
|
|
65
|
-
console.log("")
|
|
66
|
-
printSection("Result")
|
|
67
|
-
printStatusTable([["done", "Package", outputPaths.zipPath]])
|
|
68
|
-
}
|
|
1
|
+
import { existsSync } from "node:fs"
|
|
2
|
+
|
|
3
|
+
import { createMacOSZipArchive, createWindowsZipArchive } from "../lib/output.js"
|
|
4
|
+
import {
|
|
5
|
+
assertAppProject,
|
|
6
|
+
loadResetConfig,
|
|
7
|
+
resolveAppOutputPaths,
|
|
8
|
+
resolveAppPaths
|
|
9
|
+
} from "../lib/project.js"
|
|
10
|
+
import {
|
|
11
|
+
createProgress,
|
|
12
|
+
printBanner,
|
|
13
|
+
printKeyValueTable,
|
|
14
|
+
printSection,
|
|
15
|
+
printStatusTable
|
|
16
|
+
} from "../lib/ui.js"
|
|
17
|
+
|
|
18
|
+
export const description = "Package the built desktop app into a distributable archive"
|
|
19
|
+
|
|
20
|
+
export async function run(context) {
|
|
21
|
+
const dryRun = Boolean(context.flags["dry-run"])
|
|
22
|
+
const isWindows = process.platform === "win32"
|
|
23
|
+
const archiveBuilder = isWindows ? createWindowsZipArchive : createMacOSZipArchive
|
|
24
|
+
const archiveDescription = isWindows
|
|
25
|
+
? "Create a Windows zip package from the built app folder"
|
|
26
|
+
: "Create a macOS zip package from the built app bundle"
|
|
27
|
+
const appPaths = resolveAppPaths(context.projectRoot)
|
|
28
|
+
assertAppProject(appPaths)
|
|
29
|
+
|
|
30
|
+
const config = loadResetConfig(appPaths)
|
|
31
|
+
assertAppProject(appPaths, config)
|
|
32
|
+
const outputPaths = resolveAppOutputPaths(appPaths, config)
|
|
33
|
+
|
|
34
|
+
printBanner("reset-framework-cli package", description)
|
|
35
|
+
printSection("Project")
|
|
36
|
+
printKeyValueTable([
|
|
37
|
+
["Desktop app", outputPaths.appBundlePath],
|
|
38
|
+
["Archive", outputPaths.zipPath]
|
|
39
|
+
])
|
|
40
|
+
console.log("")
|
|
41
|
+
|
|
42
|
+
printSection("Plan")
|
|
43
|
+
printStatusTable([
|
|
44
|
+
["ready", "Archive", archiveDescription]
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
if (!existsSync(outputPaths.appBundlePath) && !dryRun) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Missing built app bundle at ${outputPaths.appBundlePath}. Run 'reset-framework-cli build' first.`
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (dryRun) {
|
|
54
|
+
console.log("")
|
|
55
|
+
printSection("Dry run")
|
|
56
|
+
printStatusTable([["plan", "Package", "No archive will be written"]])
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log("")
|
|
61
|
+
const progress = createProgress(1, "Package")
|
|
62
|
+
await archiveBuilder(outputPaths, { dryRun })
|
|
63
|
+
progress.tick("Archive created")
|
|
64
|
+
|
|
65
|
+
console.log("")
|
|
66
|
+
printSection("Result")
|
|
67
|
+
printStatusTable([["done", "Package", outputPaths.zipPath]])
|
|
68
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { run as runBuild, description as buildDescription } from "./commands/build.js"
|
|
4
|
-
import { run as runCreateApp, description as createAppDescription } from "./commands/init.js"
|
|
5
|
-
import { run as runDev, description as devDescription } from "./commands/dev.js"
|
|
6
|
-
import { run as runDoctor, description as doctorDescription } from "./commands/doctor.js"
|
|
7
|
-
import { run as runInit, description as initDescription } from "./commands/init.js"
|
|
8
|
-
import { run as runPackage, description as packageDescription } from "./commands/package.js"
|
|
9
|
-
import { createCommandContext } from "./lib/context.js"
|
|
10
|
-
import { logger } from "./lib/logger.js"
|
|
11
|
-
import {
|
|
12
|
-
printBanner,
|
|
13
|
-
printCommandList,
|
|
14
|
-
printKeyValueTable,
|
|
15
|
-
printSection
|
|
16
|
-
} from "./lib/ui.js"
|
|
17
|
-
|
|
18
|
-
const commands = {
|
|
2
|
+
|
|
3
|
+
import { run as runBuild, description as buildDescription } from "./commands/build.js"
|
|
4
|
+
import { run as runCreateApp, description as createAppDescription } from "./commands/init.js"
|
|
5
|
+
import { run as runDev, description as devDescription } from "./commands/dev.js"
|
|
6
|
+
import { run as runDoctor, description as doctorDescription } from "./commands/doctor.js"
|
|
7
|
+
import { run as runInit, description as initDescription } from "./commands/init.js"
|
|
8
|
+
import { run as runPackage, description as packageDescription } from "./commands/package.js"
|
|
9
|
+
import { createCommandContext } from "./lib/context.js"
|
|
10
|
+
import { logger } from "./lib/logger.js"
|
|
11
|
+
import {
|
|
12
|
+
printBanner,
|
|
13
|
+
printCommandList,
|
|
14
|
+
printKeyValueTable,
|
|
15
|
+
printSection
|
|
16
|
+
} from "./lib/ui.js"
|
|
17
|
+
|
|
18
|
+
const commands = {
|
|
19
19
|
build: {
|
|
20
20
|
description: buildDescription,
|
|
21
21
|
usage: "reset-framework-cli build [--skip-frontend] [--skip-runtime] [--skip-stage] [--runtime-source] [--dry-run]",
|
|
@@ -26,37 +26,37 @@ const commands = {
|
|
|
26
26
|
["--runtime-source", "Build the runtime from @reset-framework/native instead of using a bundled runtime package"],
|
|
27
27
|
["--dry-run", "Print the build plan without running commands"]
|
|
28
28
|
],
|
|
29
|
-
examples: [
|
|
30
|
-
"reset-framework-cli build",
|
|
31
|
-
"reset-framework-cli build --dry-run"
|
|
32
|
-
],
|
|
33
|
-
run: runBuild
|
|
34
|
-
},
|
|
35
|
-
"create-app": {
|
|
36
|
-
description: createAppDescription,
|
|
37
|
-
usage: "reset-framework-cli create-app <directory> [options]",
|
|
38
|
-
options: [
|
|
39
|
-
["--template <name>", "Choose a starter template"],
|
|
40
|
-
["--tailwind", "Use the Tailwind CSS starter"],
|
|
41
|
-
["--no-tailwind", "Keep the plain CSS starter"],
|
|
42
|
-
["--frontend-dir <path>", "Use a custom relative frontend directory"],
|
|
43
|
-
["--flat", "Place the frontend directly in the project root"],
|
|
44
|
-
["--product-name <name>", "Override the generated product name"],
|
|
45
|
-
["--app-id <id>", "Override the generated app identifier"],
|
|
46
|
-
["--package-manager <name>", "Choose npm, pnpm, yarn, or bun for dependency install"],
|
|
47
|
-
["--no-install", "Skip installing frontend dependencies after scaffolding"],
|
|
48
|
-
["--force", "Allow scaffolding into an existing directory"],
|
|
49
|
-
["--yes", "Skip the interactive questionnaire and use defaults"],
|
|
50
|
-
["--no-prompt", "Disable prompts even in an interactive terminal"],
|
|
51
|
-
["--dry-run", "Preview the scaffold plan without writing files"]
|
|
52
|
-
],
|
|
53
|
-
examples: [
|
|
54
|
-
"reset-framework-cli create-app my-app",
|
|
55
|
-
"reset-framework-cli create-app my-app --tailwind",
|
|
56
|
-
"reset-framework-cli create-app my-app --flat --yes"
|
|
57
|
-
],
|
|
58
|
-
run: runCreateApp
|
|
59
|
-
},
|
|
29
|
+
examples: [
|
|
30
|
+
"reset-framework-cli build",
|
|
31
|
+
"reset-framework-cli build --dry-run"
|
|
32
|
+
],
|
|
33
|
+
run: runBuild
|
|
34
|
+
},
|
|
35
|
+
"create-app": {
|
|
36
|
+
description: createAppDescription,
|
|
37
|
+
usage: "reset-framework-cli create-app <directory> [options]",
|
|
38
|
+
options: [
|
|
39
|
+
["--template <name>", "Choose a starter template"],
|
|
40
|
+
["--tailwind", "Use the Tailwind CSS starter"],
|
|
41
|
+
["--no-tailwind", "Keep the plain CSS starter"],
|
|
42
|
+
["--frontend-dir <path>", "Use a custom relative frontend directory"],
|
|
43
|
+
["--flat", "Place the frontend directly in the project root"],
|
|
44
|
+
["--product-name <name>", "Override the generated product name"],
|
|
45
|
+
["--app-id <id>", "Override the generated app identifier"],
|
|
46
|
+
["--package-manager <name>", "Choose npm, pnpm, yarn, or bun for dependency install"],
|
|
47
|
+
["--no-install", "Skip installing frontend dependencies after scaffolding"],
|
|
48
|
+
["--force", "Allow scaffolding into an existing directory"],
|
|
49
|
+
["--yes", "Skip the interactive questionnaire and use defaults"],
|
|
50
|
+
["--no-prompt", "Disable prompts even in an interactive terminal"],
|
|
51
|
+
["--dry-run", "Preview the scaffold plan without writing files"]
|
|
52
|
+
],
|
|
53
|
+
examples: [
|
|
54
|
+
"reset-framework-cli create-app my-app",
|
|
55
|
+
"reset-framework-cli create-app my-app --tailwind",
|
|
56
|
+
"reset-framework-cli create-app my-app --flat --yes"
|
|
57
|
+
],
|
|
58
|
+
run: runCreateApp
|
|
59
|
+
},
|
|
60
60
|
dev: {
|
|
61
61
|
description: devDescription,
|
|
62
62
|
usage: "reset-framework-cli dev [--skip-frontend] [--skip-runtime] [--no-open] [--runtime-source] [--dry-run]",
|
|
@@ -67,150 +67,150 @@ const commands = {
|
|
|
67
67
|
["--runtime-source", "Build the runtime from @reset-framework/native instead of using a bundled runtime package"],
|
|
68
68
|
["--dry-run", "Print the dev plan without starting processes"]
|
|
69
69
|
],
|
|
70
|
-
examples: [
|
|
71
|
-
"reset-framework-cli dev",
|
|
72
|
-
"reset-framework-cli dev --skip-runtime"
|
|
73
|
-
],
|
|
74
|
-
run: runDev
|
|
75
|
-
},
|
|
76
|
-
doctor: {
|
|
77
|
-
description: doctorDescription,
|
|
78
|
-
usage: "reset-framework-cli doctor",
|
|
79
|
-
options: [],
|
|
80
|
-
examples: ["reset-framework-cli doctor"],
|
|
81
|
-
run: runDoctor
|
|
82
|
-
},
|
|
83
|
-
init: {
|
|
84
|
-
description: `${initDescription} (alias for create-app)`,
|
|
85
|
-
usage: "reset-framework-cli init <directory> [options]",
|
|
86
|
-
options: [
|
|
87
|
-
["--template <name>", "Choose a starter template"],
|
|
88
|
-
["--tailwind", "Use the Tailwind CSS starter"],
|
|
89
|
-
["--no-tailwind", "Keep the plain CSS starter"],
|
|
90
|
-
["--frontend-dir <path>", "Use a custom relative frontend directory"],
|
|
91
|
-
["--flat", "Place the frontend directly in the project root"],
|
|
92
|
-
["--product-name <name>", "Override the generated product name"],
|
|
93
|
-
["--app-id <id>", "Override the generated app identifier"],
|
|
94
|
-
["--package-manager <name>", "Choose npm, pnpm, yarn, or bun for dependency install"],
|
|
95
|
-
["--no-install", "Skip installing frontend dependencies after scaffolding"],
|
|
96
|
-
["--force", "Allow scaffolding into an existing directory"],
|
|
97
|
-
["--yes", "Skip the interactive questionnaire and use defaults"],
|
|
98
|
-
["--no-prompt", "Disable prompts even in an interactive terminal"],
|
|
99
|
-
["--dry-run", "Preview the scaffold plan without writing files"]
|
|
100
|
-
],
|
|
101
|
-
examples: ["reset-framework-cli init my-app"],
|
|
102
|
-
run: runInit
|
|
103
|
-
},
|
|
104
|
-
package: {
|
|
105
|
-
description: packageDescription,
|
|
106
|
-
usage: "reset-framework-cli package [--dry-run]",
|
|
107
|
-
options: [
|
|
108
|
-
["--dry-run", "Preview the archive step without writing the package file"]
|
|
109
|
-
],
|
|
110
|
-
examples: [
|
|
111
|
-
"reset-framework-cli package"
|
|
112
|
-
],
|
|
113
|
-
run: runPackage
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const coreWorkflow = [
|
|
118
|
-
"reset-framework-cli create-app my-app",
|
|
119
|
-
"cd my-app",
|
|
120
|
-
"reset-framework-cli dev",
|
|
121
|
-
"reset-framework-cli build",
|
|
122
|
-
"reset-framework-cli package"
|
|
123
|
-
]
|
|
124
|
-
|
|
125
|
-
function getVisibleCommands() {
|
|
126
|
-
return ["create-app", "dev", "build", "package", "doctor"].map((name) => ({
|
|
127
|
-
name,
|
|
128
|
-
description: commands[name].description
|
|
129
|
-
}))
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function printGeneralHelp() {
|
|
133
|
-
printBanner("reset-framework-cli", "Developer-facing orchestration for Reset Framework desktop apps.")
|
|
134
|
-
|
|
135
|
-
printSection("Usage")
|
|
136
|
-
console.log(" reset-framework-cli <command> [options]")
|
|
137
|
-
console.log("")
|
|
138
|
-
|
|
139
|
-
printSection("Core workflow")
|
|
140
|
-
for (const step of coreWorkflow) {
|
|
141
|
-
console.log(` ${step}`)
|
|
142
|
-
}
|
|
143
|
-
console.log("")
|
|
144
|
-
|
|
145
|
-
printSection("Commands")
|
|
146
|
-
printCommandList(getVisibleCommands())
|
|
147
|
-
console.log("")
|
|
148
|
-
console.log(" reset-framework-cli help <command> Show command-specific help")
|
|
149
|
-
console.log("")
|
|
150
|
-
printSection("Global shortcuts")
|
|
151
|
-
printKeyValueTable([
|
|
152
|
-
["-h, --help", "Show help for the current command"],
|
|
153
|
-
["-y, --yes", "Accept defaults in create-app without prompts"]
|
|
154
|
-
])
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function printCommandHelp(name) {
|
|
158
|
-
const command = commands[name]
|
|
159
|
-
|
|
160
|
-
if (!command) {
|
|
161
|
-
logger.error(`Unknown command: ${name}`)
|
|
162
|
-
printGeneralHelp()
|
|
163
|
-
process.exit(1)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
printBanner(`reset-framework-cli ${name}`, command.description)
|
|
167
|
-
|
|
168
|
-
printSection("Usage")
|
|
169
|
-
console.log(` ${command.usage}`)
|
|
170
|
-
|
|
171
|
-
if (command.options.length > 0) {
|
|
172
|
-
console.log("")
|
|
173
|
-
printSection("Options")
|
|
174
|
-
printKeyValueTable(command.options)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (command.examples.length > 0) {
|
|
178
|
-
console.log("")
|
|
179
|
-
printSection("Examples")
|
|
180
|
-
for (const example of command.examples) {
|
|
181
|
-
console.log(` ${example}`)
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const context = createCommandContext(process.argv.slice(2))
|
|
187
|
-
|
|
188
|
-
if (context.command === "help") {
|
|
189
|
-
if (context.args[0]) {
|
|
190
|
-
printCommandHelp(context.args[0])
|
|
191
|
-
} else {
|
|
192
|
-
printGeneralHelp()
|
|
193
|
-
}
|
|
194
|
-
process.exit(0)
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (context.flags.help) {
|
|
198
|
-
printCommandHelp(context.command)
|
|
199
|
-
process.exit(0)
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const command = commands[context.command]
|
|
203
|
-
|
|
204
|
-
if (!command) {
|
|
205
|
-
logger.error(`Unknown command: ${context.command}`)
|
|
206
|
-
printGeneralHelp()
|
|
207
|
-
process.exit(1)
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
try {
|
|
211
|
-
await command.run(context)
|
|
212
|
-
} catch (error) {
|
|
213
|
-
const message = error instanceof Error ? error.message : String(error)
|
|
214
|
-
logger.error(message)
|
|
215
|
-
process.exit(1)
|
|
216
|
-
}
|
|
70
|
+
examples: [
|
|
71
|
+
"reset-framework-cli dev",
|
|
72
|
+
"reset-framework-cli dev --skip-runtime"
|
|
73
|
+
],
|
|
74
|
+
run: runDev
|
|
75
|
+
},
|
|
76
|
+
doctor: {
|
|
77
|
+
description: doctorDescription,
|
|
78
|
+
usage: "reset-framework-cli doctor",
|
|
79
|
+
options: [],
|
|
80
|
+
examples: ["reset-framework-cli doctor"],
|
|
81
|
+
run: runDoctor
|
|
82
|
+
},
|
|
83
|
+
init: {
|
|
84
|
+
description: `${initDescription} (alias for create-app)`,
|
|
85
|
+
usage: "reset-framework-cli init <directory> [options]",
|
|
86
|
+
options: [
|
|
87
|
+
["--template <name>", "Choose a starter template"],
|
|
88
|
+
["--tailwind", "Use the Tailwind CSS starter"],
|
|
89
|
+
["--no-tailwind", "Keep the plain CSS starter"],
|
|
90
|
+
["--frontend-dir <path>", "Use a custom relative frontend directory"],
|
|
91
|
+
["--flat", "Place the frontend directly in the project root"],
|
|
92
|
+
["--product-name <name>", "Override the generated product name"],
|
|
93
|
+
["--app-id <id>", "Override the generated app identifier"],
|
|
94
|
+
["--package-manager <name>", "Choose npm, pnpm, yarn, or bun for dependency install"],
|
|
95
|
+
["--no-install", "Skip installing frontend dependencies after scaffolding"],
|
|
96
|
+
["--force", "Allow scaffolding into an existing directory"],
|
|
97
|
+
["--yes", "Skip the interactive questionnaire and use defaults"],
|
|
98
|
+
["--no-prompt", "Disable prompts even in an interactive terminal"],
|
|
99
|
+
["--dry-run", "Preview the scaffold plan without writing files"]
|
|
100
|
+
],
|
|
101
|
+
examples: ["reset-framework-cli init my-app"],
|
|
102
|
+
run: runInit
|
|
103
|
+
},
|
|
104
|
+
package: {
|
|
105
|
+
description: packageDescription,
|
|
106
|
+
usage: "reset-framework-cli package [--dry-run]",
|
|
107
|
+
options: [
|
|
108
|
+
["--dry-run", "Preview the archive step without writing the package file"]
|
|
109
|
+
],
|
|
110
|
+
examples: [
|
|
111
|
+
"reset-framework-cli package"
|
|
112
|
+
],
|
|
113
|
+
run: runPackage
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const coreWorkflow = [
|
|
118
|
+
"reset-framework-cli create-app my-app",
|
|
119
|
+
"cd my-app",
|
|
120
|
+
"reset-framework-cli dev",
|
|
121
|
+
"reset-framework-cli build",
|
|
122
|
+
"reset-framework-cli package"
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
function getVisibleCommands() {
|
|
126
|
+
return ["create-app", "dev", "build", "package", "doctor"].map((name) => ({
|
|
127
|
+
name,
|
|
128
|
+
description: commands[name].description
|
|
129
|
+
}))
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function printGeneralHelp() {
|
|
133
|
+
printBanner("reset-framework-cli", "Developer-facing orchestration for Reset Framework desktop apps.")
|
|
134
|
+
|
|
135
|
+
printSection("Usage")
|
|
136
|
+
console.log(" reset-framework-cli <command> [options]")
|
|
137
|
+
console.log("")
|
|
138
|
+
|
|
139
|
+
printSection("Core workflow")
|
|
140
|
+
for (const step of coreWorkflow) {
|
|
141
|
+
console.log(` ${step}`)
|
|
142
|
+
}
|
|
143
|
+
console.log("")
|
|
144
|
+
|
|
145
|
+
printSection("Commands")
|
|
146
|
+
printCommandList(getVisibleCommands())
|
|
147
|
+
console.log("")
|
|
148
|
+
console.log(" reset-framework-cli help <command> Show command-specific help")
|
|
149
|
+
console.log("")
|
|
150
|
+
printSection("Global shortcuts")
|
|
151
|
+
printKeyValueTable([
|
|
152
|
+
["-h, --help", "Show help for the current command"],
|
|
153
|
+
["-y, --yes", "Accept defaults in create-app without prompts"]
|
|
154
|
+
])
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function printCommandHelp(name) {
|
|
158
|
+
const command = commands[name]
|
|
159
|
+
|
|
160
|
+
if (!command) {
|
|
161
|
+
logger.error(`Unknown command: ${name}`)
|
|
162
|
+
printGeneralHelp()
|
|
163
|
+
process.exit(1)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
printBanner(`reset-framework-cli ${name}`, command.description)
|
|
167
|
+
|
|
168
|
+
printSection("Usage")
|
|
169
|
+
console.log(` ${command.usage}`)
|
|
170
|
+
|
|
171
|
+
if (command.options.length > 0) {
|
|
172
|
+
console.log("")
|
|
173
|
+
printSection("Options")
|
|
174
|
+
printKeyValueTable(command.options)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (command.examples.length > 0) {
|
|
178
|
+
console.log("")
|
|
179
|
+
printSection("Examples")
|
|
180
|
+
for (const example of command.examples) {
|
|
181
|
+
console.log(` ${example}`)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const context = createCommandContext(process.argv.slice(2))
|
|
187
|
+
|
|
188
|
+
if (context.command === "help") {
|
|
189
|
+
if (context.args[0]) {
|
|
190
|
+
printCommandHelp(context.args[0])
|
|
191
|
+
} else {
|
|
192
|
+
printGeneralHelp()
|
|
193
|
+
}
|
|
194
|
+
process.exit(0)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (context.flags.help) {
|
|
198
|
+
printCommandHelp(context.command)
|
|
199
|
+
process.exit(0)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const command = commands[context.command]
|
|
203
|
+
|
|
204
|
+
if (!command) {
|
|
205
|
+
logger.error(`Unknown command: ${context.command}`)
|
|
206
|
+
printGeneralHelp()
|
|
207
|
+
process.exit(1)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
await command.run(context)
|
|
212
|
+
} catch (error) {
|
|
213
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
214
|
+
logger.error(message)
|
|
215
|
+
process.exit(1)
|
|
216
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import path from "node:path"
|
|
2
|
+
import { chmod, copyFile, mkdir, realpath } from "node:fs/promises"
|
|
3
|
+
import { existsSync } from "node:fs"
|
|
4
|
+
|
|
5
|
+
import { build as buildWithEsbuild } from "esbuild"
|
|
6
|
+
|
|
7
|
+
import { logger } from "./logger.js"
|
|
8
|
+
import { resolveOptionalBackendEntry } from "./project.js"
|
|
9
|
+
|
|
10
|
+
function isRuntimeExecutableName(name) {
|
|
11
|
+
const normalized = name.toLowerCase()
|
|
12
|
+
return normalized === "node" || normalized === "node.exe" || normalized === "bun" || normalized === "bun.exe"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function uniqueNonEmpty(items) {
|
|
16
|
+
return [...new Set(items.filter(Boolean))]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function resolvePathExecutable(commandName) {
|
|
20
|
+
const pathValue = process.env.PATH ?? ""
|
|
21
|
+
const searchDirectories = pathValue.split(path.delimiter).filter(Boolean)
|
|
22
|
+
const extensions =
|
|
23
|
+
process.platform === "win32"
|
|
24
|
+
? uniqueNonEmpty(["", ...(process.env.PATHEXT?.split(";") ?? [".EXE", ".CMD", ".BAT", ".COM"])])
|
|
25
|
+
: [""]
|
|
26
|
+
|
|
27
|
+
for (const directory of searchDirectories) {
|
|
28
|
+
for (const extension of extensions) {
|
|
29
|
+
const candidate = path.join(directory, `${commandName}${extension}`)
|
|
30
|
+
if (existsSync(candidate)) {
|
|
31
|
+
return candidate
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function resolveSidecarRuntimeBinary() {
|
|
40
|
+
const candidates = uniqueNonEmpty([
|
|
41
|
+
process.execPath,
|
|
42
|
+
resolvePathExecutable("node"),
|
|
43
|
+
resolvePathExecutable("bun")
|
|
44
|
+
])
|
|
45
|
+
|
|
46
|
+
for (const candidate of candidates) {
|
|
47
|
+
if (!candidate || !existsSync(candidate)) {
|
|
48
|
+
continue
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (isRuntimeExecutableName(path.basename(candidate))) {
|
|
52
|
+
return candidate
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function resolveBackendBuildDir(appPaths, mode) {
|
|
60
|
+
return path.join(appPaths.appRoot, ".reset", "backend", mode)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function prepareAppBackend(frameworkPaths, appPaths, mode) {
|
|
64
|
+
const entryPath = resolveOptionalBackendEntry(appPaths)
|
|
65
|
+
if (!entryPath) {
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const runtimeExecutablePath = resolveSidecarRuntimeBinary()
|
|
70
|
+
if (!runtimeExecutablePath) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
"Could not resolve a JavaScript runtime for the Reset backend sidecar. Install Node.js or Bun."
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const outputDir = resolveBackendBuildDir(appPaths, mode)
|
|
77
|
+
const bundleEntryPath = path.join(outputDir, "app.mjs")
|
|
78
|
+
const runnerPath = path.join(frameworkPaths.backendPackage.packageRoot, "src", "runner.js")
|
|
79
|
+
const supportModulePath = path.join(frameworkPaths.backendPackage.packageRoot, "src", "index.js")
|
|
80
|
+
|
|
81
|
+
await mkdir(outputDir, { recursive: true })
|
|
82
|
+
await buildWithEsbuild({
|
|
83
|
+
absWorkingDir: appPaths.appRoot,
|
|
84
|
+
entryPoints: [entryPath],
|
|
85
|
+
outfile: bundleEntryPath,
|
|
86
|
+
bundle: true,
|
|
87
|
+
platform: "node",
|
|
88
|
+
format: "esm",
|
|
89
|
+
target: "node20",
|
|
90
|
+
sourcemap: mode === "dev" ? "inline" : false,
|
|
91
|
+
logLevel: "silent"
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
entryPath,
|
|
96
|
+
bundleEntryPath,
|
|
97
|
+
runnerPath,
|
|
98
|
+
supportModulePath,
|
|
99
|
+
runtimeExecutablePath,
|
|
100
|
+
runtimeName: path.basename(runtimeExecutablePath),
|
|
101
|
+
workingDirectory: appPaths.appRoot
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export async function stageAppBackend(backendArtifact, outputPaths) {
|
|
106
|
+
if (!backendArtifact) {
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
logger.info(`Staging backend sidecar into ${outputPaths.bundledBackendDir}`)
|
|
111
|
+
|
|
112
|
+
const resolvedRuntimePath = await realpath(backendArtifact.runtimeExecutablePath)
|
|
113
|
+
|
|
114
|
+
await mkdir(outputPaths.bundledBackendDir, { recursive: true })
|
|
115
|
+
await copyFile(backendArtifact.bundleEntryPath, outputPaths.bundledBackendEntryPath)
|
|
116
|
+
await copyFile(backendArtifact.runnerPath, outputPaths.bundledBackendRunnerPath)
|
|
117
|
+
await copyFile(backendArtifact.supportModulePath, outputPaths.bundledBackendSupportModulePath)
|
|
118
|
+
await copyFile(resolvedRuntimePath, outputPaths.bundledBackendRuntimePath)
|
|
119
|
+
|
|
120
|
+
if (process.platform !== "win32") {
|
|
121
|
+
await chmod(outputPaths.bundledBackendRuntimePath, 0o755)
|
|
122
|
+
}
|
|
123
|
+
}
|