@geastack/cli 0.1.7 → 0.1.9
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 +9 -5
- package/docs/NPX-COMMANDS.md +5 -4
- package/docs/SPEC.md +3 -2
- package/package.json +1 -1
- package/src/create-geastack.mjs +67 -7
package/README.md
CHANGED
|
@@ -31,8 +31,10 @@ flowchart TD
|
|
|
31
31
|
|
|
32
32
|
starter -->|"Embedded component counter"| counter["Copy the touchscreen counter used in the embedded tutorial"]
|
|
33
33
|
|
|
34
|
-
starter -->|"Blank application"|
|
|
35
|
-
|
|
34
|
+
starter -->|"Blank application"| emptyTarget{"Where should it run?"}
|
|
35
|
+
emptyTarget -->|"Web browser"| emptyWeb["Generate browser entry, HTML, and Vite config"]
|
|
36
|
+
emptyTarget -->|"ESP32 board"| emptyEsp["Generate native entry<br/>Ask whether to enable Bluetooth updates"]
|
|
37
|
+
emptyTarget -->|"Other native target"| emptyNative["Generate native entry for the selected platform"]
|
|
36
38
|
|
|
37
39
|
starter -->|"Example application"| pickExample["Pick from the example gallery<br/>web, ESP32, GeaOS, iOS, macOS, Android"]
|
|
38
40
|
pickExample --> fetchExample["Fetch selected app from GitHub"]
|
|
@@ -41,13 +43,15 @@ flowchart TD
|
|
|
41
43
|
|
|
42
44
|
counter --> projectWiring["Add @geastack/core and @geastack/cli"]
|
|
43
45
|
rewriteExample --> projectWiring["Add @geastack/core and @geastack/cli"]
|
|
44
|
-
|
|
46
|
+
emptyWeb --> projectWiring
|
|
47
|
+
emptyEsp --> projectWiring
|
|
48
|
+
emptyNative --> projectWiring
|
|
45
49
|
projectWiring --> boardConfig["Create .gea/boards.json"]
|
|
46
50
|
boardConfig --> install["Install npm dependencies<br/>(interactive default)"]
|
|
47
51
|
install --> setup["Next: npx gea setup"]
|
|
48
52
|
|
|
49
53
|
auto["Automation flags"] -.-> starter
|
|
50
|
-
auto --> autoEmpty["--starter
|
|
54
|
+
auto --> autoEmpty["--starter blank --yes"]
|
|
51
55
|
auto --> autoExample["--starter example --example watch"]
|
|
52
56
|
```
|
|
53
57
|
|
|
@@ -157,6 +161,6 @@ First implementation is in place:
|
|
|
157
161
|
- npm-resolved embedded board builds for ESP32 and RP2350;
|
|
158
162
|
- `flash`, `monitor`, WiFi OTA, and BLE OTA through `@geastack/targets`;
|
|
159
163
|
- `list` and `inspect` helpers;
|
|
160
|
-
- `create-geastack` with a bundled counter starter,
|
|
164
|
+
- `create-geastack` with a bundled counter starter, a blank application, and a
|
|
161
165
|
GitHub-backed rich example flow for web, embedded, GeaOS, iOS, macOS, and Android apps,
|
|
162
166
|
all with `.gea/boards.json`.
|
package/docs/NPX-COMMANDS.md
CHANGED
|
@@ -53,9 +53,10 @@ What do you want to build?
|
|
|
53
53
|
`Embedded component counter` is the bundled tutorial starter. It uses local
|
|
54
54
|
component state, targets ESP32, and enables BLE updates.
|
|
55
55
|
|
|
56
|
-
`Blank application`
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
`Blank application` asks where the app should run. It creates the smallest
|
|
57
|
+
project for that target and, for ESP32, asks whether to enable wireless
|
|
58
|
+
firmware updates over Bluetooth. Browser-only files such as `index.html` and
|
|
59
|
+
`vite.config.ts` are added only when the web target is selected.
|
|
59
60
|
|
|
60
61
|
`Example application` opens an example picker populated from the hard-coded
|
|
61
62
|
`examples/catalog.json` shipped inside `@geastack/cli`. The catalog contains web,
|
|
@@ -69,7 +70,7 @@ Automation can skip prompts:
|
|
|
69
70
|
|
|
70
71
|
```sh
|
|
71
72
|
npx @geastack/create-geastack my-app --starter counter
|
|
72
|
-
npx @geastack/create-geastack my-app --starter
|
|
73
|
+
npx @geastack/create-geastack my-app --starter blank --yes
|
|
73
74
|
npx @geastack/create-geastack my-app --starter example --example watch
|
|
74
75
|
npx @geastack/create-geastack my-ios-app --starter example --example ios-native-showcase
|
|
75
76
|
npx gea build --target ios --mode simulator
|
package/docs/SPEC.md
CHANGED
|
@@ -120,7 +120,7 @@ or optional dependency is missing.
|
|
|
120
120
|
Scaffolds a new app folder with:
|
|
121
121
|
|
|
122
122
|
- `package.json` containing a `gea` manifest;
|
|
123
|
-
- a bundled counter starter,
|
|
123
|
+
- a bundled counter starter, a blank application, or a selected GitHub example;
|
|
124
124
|
- `index.tsx` or `index.ts`;
|
|
125
125
|
- `tsconfig.json`;
|
|
126
126
|
- `vite.config.ts`;
|
|
@@ -132,7 +132,8 @@ Interactive starter choices must explain the tradeoff in-line:
|
|
|
132
132
|
|
|
133
133
|
- `Embedded component counter`: create a touchscreen counter with local
|
|
134
134
|
component state, an ESP32 target, and BLE updates enabled.
|
|
135
|
-
- `Blank application`:
|
|
135
|
+
- `Blank application`: ask where the app should run, generate the matching
|
|
136
|
+
minimal project, and ask whether to enable Bluetooth updates for ESP32.
|
|
136
137
|
- `Example application`: fetch a selected app from `geastack/examples`, then rewrite
|
|
137
138
|
package name, app id, dependencies, and board config for the new project.
|
|
138
139
|
|
package/package.json
CHANGED
package/src/create-geastack.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { flag, option, optionList, parseArgs } from './args.mjs'
|
|
|
5
5
|
import { createContext } from './context.mjs'
|
|
6
6
|
import { ExitCode, fail } from './errors.mjs'
|
|
7
7
|
import { exists, readJson, writeJson } from './fs-utils.mjs'
|
|
8
|
-
import { canPrompt, choose, createPrompt } from './prompts.mjs'
|
|
8
|
+
import { canPrompt, choose, confirm, createPrompt } from './prompts.mjs'
|
|
9
9
|
import { runExternal } from './run.mjs'
|
|
10
10
|
import {
|
|
11
11
|
copyStarterFiles,
|
|
@@ -67,12 +67,12 @@ export async function runCreateGeastack(argv, io = {}) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
const sourcePackage = readPackageJson(targetDir)
|
|
70
|
-
const sourceManifest = sourcePackage.gea || starter.starter?.manifest || starter.example?.manifest || {}
|
|
70
|
+
const sourceManifest = sourcePackage.gea || starter.manifest || starter.starter?.manifest || starter.example?.manifest || {}
|
|
71
71
|
const requestedTargets = optionList(parsed, 'targets')
|
|
72
72
|
const targets = requestedTargets.length > 0
|
|
73
73
|
? targetManifest(requestedTargets)
|
|
74
74
|
: starter.kind === 'empty'
|
|
75
|
-
? targetManifest(['web'])
|
|
75
|
+
? targetManifest(starter.targets || ['web'])
|
|
76
76
|
: targetManifestFromObject(sourceManifest.targets)
|
|
77
77
|
writeJson(path.join(targetDir, 'package.json'), packageJson({
|
|
78
78
|
appId,
|
|
@@ -106,7 +106,7 @@ export async function runCreateGeastack(argv, io = {}) {
|
|
|
106
106
|
stdout(`Created ${displayName} at ${targetDir}`)
|
|
107
107
|
if (starter.kind === 'bundled') stdout(`Starter: ${starter.starter.name}`)
|
|
108
108
|
else if (starter.kind === 'example') stdout(`Example: fetched ${starter.example.name}`)
|
|
109
|
-
else stdout('Starter:
|
|
109
|
+
else stdout('Starter: blank application')
|
|
110
110
|
if (installDependencies) stdout(`Next: cd ${targetDir} && npx gea setup`)
|
|
111
111
|
else stdout(`Next: cd ${targetDir} && npm install && npx gea setup`)
|
|
112
112
|
return 0
|
|
@@ -121,7 +121,23 @@ async function resolveStarter(ctx, parsed, io) {
|
|
|
121
121
|
try {
|
|
122
122
|
const mode = requested || await chooseStarterMode({ interactive, prompt, bundled, examples })
|
|
123
123
|
|
|
124
|
-
if (mode === 'empty')
|
|
124
|
+
if (mode === 'empty') {
|
|
125
|
+
const requestedTargets = optionList(parsed, 'targets')
|
|
126
|
+
if (!interactive || requestedTargets.length > 0) return { kind: 'empty' }
|
|
127
|
+
|
|
128
|
+
const target = await chooseBlankTarget(prompt)
|
|
129
|
+
const enableBleOta = target === 'esp32'
|
|
130
|
+
? await confirm(prompt, {
|
|
131
|
+
message: 'Enable wireless firmware updates over Bluetooth?',
|
|
132
|
+
defaultValue: true
|
|
133
|
+
})
|
|
134
|
+
: false
|
|
135
|
+
return {
|
|
136
|
+
kind: 'empty',
|
|
137
|
+
targets: [target],
|
|
138
|
+
manifest: enableBleOta ? { ota: { ble: true } } : {}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
125
141
|
if (mode === 'bundled' || mode === 'counter') {
|
|
126
142
|
const requestedBundled = mode === 'counter' ? 'counter' : option(parsed, 'bundled') || option(parsed, 'starter-id') || ''
|
|
127
143
|
const starter = requestedBundled
|
|
@@ -134,10 +150,10 @@ async function resolveStarter(ctx, parsed, io) {
|
|
|
134
150
|
return { kind: 'bundled', starter }
|
|
135
151
|
}
|
|
136
152
|
if (mode !== 'example') {
|
|
137
|
-
fail(`Unknown starter '${mode}'. Expected counter,
|
|
153
|
+
fail(`Unknown starter '${mode}'. Expected counter, blank, or example.`, ExitCode.usage)
|
|
138
154
|
}
|
|
139
155
|
if (examples.length === 0) {
|
|
140
|
-
fail('No GitHub examples are available. Use --starter counter or --starter
|
|
156
|
+
fail('No GitHub examples are available. Use --starter counter or --starter blank.', ExitCode.usage)
|
|
141
157
|
}
|
|
142
158
|
|
|
143
159
|
const requestedExample = option(parsed, 'example') || option(parsed, 'from-example') || ''
|
|
@@ -191,6 +207,50 @@ async function chooseExample({ interactive, prompt, examples }) {
|
|
|
191
207
|
return examples.find((example) => example.id === id) || null
|
|
192
208
|
}
|
|
193
209
|
|
|
210
|
+
async function chooseBlankTarget(prompt) {
|
|
211
|
+
return choose(prompt, {
|
|
212
|
+
message: 'Where should this application run?',
|
|
213
|
+
choices: [
|
|
214
|
+
{
|
|
215
|
+
value: 'web',
|
|
216
|
+
label: 'Web browser',
|
|
217
|
+
description: 'A browser app with a local development server.'
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
value: 'esp32',
|
|
221
|
+
label: 'ESP32 board',
|
|
222
|
+
description: 'Native firmware for a supported ESP32 device.'
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
value: 'rp2350',
|
|
226
|
+
label: 'RP2350 board',
|
|
227
|
+
description: 'Native firmware for a supported Raspberry Pi RP2350 device.'
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
value: 'geaos',
|
|
231
|
+
label: 'GeaOS device',
|
|
232
|
+
description: 'A native application for GeaOS.'
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
value: 'macos',
|
|
236
|
+
label: 'macOS',
|
|
237
|
+
description: 'A native Mac application.'
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
value: 'ios',
|
|
241
|
+
label: 'iPhone or iPad',
|
|
242
|
+
description: 'A native iOS application.'
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
value: 'android',
|
|
246
|
+
label: 'Android',
|
|
247
|
+
description: 'A native Android application.'
|
|
248
|
+
}
|
|
249
|
+
],
|
|
250
|
+
defaultValue: 'web'
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
|
|
194
254
|
function ensureWritableTarget(targetDir, force) {
|
|
195
255
|
if (!exists(targetDir)) return
|
|
196
256
|
const entries = fs.readdirSync(targetDir).filter((entry) => entry !== '.DS_Store')
|