@geastack/cli 0.1.2 → 0.1.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/README.md CHANGED
@@ -9,16 +9,17 @@ keeping target-specific build logic behind stable backend contracts.
9
9
  ## Commands
10
10
 
11
11
  ```sh
12
- npx @geastack/create-geastack my-panel
12
+ npm install --global @geastack/cli
13
+ gea create my-panel
13
14
  cd my-panel
14
- npx gea doctor
15
- npx gea setup
16
- npx gea dev
17
- npx gea build --target web
18
- npx gea build --target ios
19
- npx gea flash --board amoled --monitor
20
- npx gea monitor --board amoled
21
- npx gea inspect --json
15
+ gea doctor
16
+ gea setup
17
+ gea dev
18
+ gea build --target web
19
+ gea build --target ios
20
+ gea flash --board amoled --monitor
21
+ gea monitor --board amoled
22
+ gea inspect --json
22
23
  ```
23
24
 
24
25
  ## Interactive Menu Map
package/docs/SPEC.md CHANGED
@@ -154,7 +154,6 @@ Core fields:
154
154
  "id": "bouncing-balls-jsx",
155
155
  "name": "Balls JSX",
156
156
  "entry": "index.tsx",
157
- "runtime": "gea",
158
157
  "targets": {
159
158
  "web": true,
160
159
  "esp32": true,
@@ -171,7 +170,7 @@ Validation rules:
171
170
 
172
171
  - `id` is required and should be stable.
173
172
  - `entry` is required and must exist.
174
- - `runtime` defaults to `gea` when omitted.
173
+ - `runtime` defaults to `gea`; only non-Gea native applications should set it.
175
174
  - `targets` must be explicit for generated apps.
176
175
  - target backends may reject apps whose runtime or capabilities they do not
177
176
  support.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geastack/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "Command-line front door for GeaStack apps, targets, and local toolchains.",
6
6
  "publishConfig": {
@@ -38,13 +38,19 @@ export async function runCreateGeastack(argv, io = {}) {
38
38
  const ctx = createContext(parsed, env, cwd)
39
39
  const displayName = option(parsed, 'name', titleFromId(appId))
40
40
  const coreDependency = option(parsed, 'core-dependency') || '^0.1.4'
41
- const cliDependency = option(parsed, 'cli-dependency') || '^0.1.2'
41
+ const cliDependency = option(parsed, 'cli-dependency') || '^0.1.4'
42
42
  const starter = await resolveStarter(ctx, parsed, io)
43
+ const entry = projectRelativePath(option(parsed, 'entry') || starter.starter?.entry || starter.example?.entry || 'index.tsx', 'entry')
44
+ if (option(parsed, 'entry') && starter.kind !== 'empty') {
45
+ fail('--entry can only be used with --starter empty.', ExitCode.usage)
46
+ }
43
47
 
44
48
  fs.mkdirSync(targetDir, { recursive: true })
45
49
  if (starter.kind === 'empty') {
46
- fs.writeFileSync(path.join(targetDir, 'index.tsx'), indexTsx(displayName))
47
- fs.writeFileSync(path.join(targetDir, 'styles.css'), stylesCss())
50
+ const entryPath = path.join(targetDir, entry)
51
+ fs.mkdirSync(path.dirname(entryPath), { recursive: true })
52
+ fs.writeFileSync(entryPath, indexTsx(displayName))
53
+ fs.writeFileSync(path.join(path.dirname(entryPath), 'styles.css'), stylesCss())
48
54
  } else if (starter.kind === 'bundled') {
49
55
  copyStarterFiles(starter.starter.root, targetDir)
50
56
  } else if (starter.kind === 'example') {
@@ -69,12 +75,14 @@ export async function runCreateGeastack(argv, io = {}) {
69
75
  appId,
70
76
  displayName,
71
77
  targets,
78
+ entry,
79
+ bleOta: flag(parsed, 'ble-ota'),
72
80
  coreDependency,
73
81
  cliDependency,
74
82
  sourcePackage,
75
83
  sourceManifest
76
84
  }))
77
- ensureFile(path.join(targetDir, 'index.html'), () => indexHtml(displayName))
85
+ ensureFile(path.join(targetDir, 'index.html'), () => indexHtml(displayName, entry))
78
86
  fs.mkdirSync(path.join(targetDir, '.gea'), { recursive: true })
79
87
  writeJson(path.join(targetDir, '.gea', 'boards.json'), {})
80
88
  ensureJson(path.join(targetDir, 'tsconfig.json'), tsconfigJson)
@@ -203,7 +211,17 @@ function targetManifestFromObject(targets = {}) {
203
211
  }
204
212
  }
205
213
 
206
- function packageJson({ appId, displayName, targets, coreDependency, cliDependency, sourcePackage = {}, sourceManifest = {} }) {
214
+ function packageJson({ appId, displayName, targets, entry, bleOta, coreDependency, cliDependency, sourcePackage = {}, sourceManifest = {} }) {
215
+ const geaManifest = {
216
+ ...sourceManifest,
217
+ id: appId,
218
+ name: displayName,
219
+ entry,
220
+ targets
221
+ }
222
+ if (bleOta) geaManifest.ota = { ...(geaManifest.ota || {}), ble: true }
223
+ if (!geaManifest.runtime || geaManifest.runtime === 'gea') delete geaManifest.runtime
224
+
207
225
  return {
208
226
  name: `gea-${appId}`,
209
227
  version: '0.1.0',
@@ -226,14 +244,7 @@ function packageJson({ appId, displayName, targets, coreDependency, cliDependenc
226
244
  typescript: sourcePackage.devDependencies?.typescript || 'latest',
227
245
  vite: sourcePackage.devDependencies?.vite || 'latest'
228
246
  },
229
- gea: {
230
- ...sourceManifest,
231
- id: appId,
232
- name: displayName,
233
- entry: sourceManifest.entry || 'index.tsx',
234
- runtime: sourceManifest.runtime || 'gea',
235
- targets
236
- },
247
+ gea: geaManifest,
237
248
  license: 'MIT'
238
249
  }
239
250
  }
@@ -297,7 +308,7 @@ h1 {
297
308
  `
298
309
  }
299
310
 
300
- function indexHtml(displayName) {
311
+ function indexHtml(displayName, entry = 'index.tsx') {
301
312
  return `<!doctype html>
302
313
  <html lang="en">
303
314
  <head>
@@ -307,7 +318,7 @@ function indexHtml(displayName) {
307
318
  </head>
308
319
  <body>
309
320
  <div id="app"></div>
310
- <script type="module" src="/index.tsx"></script>
321
+ <script type="module" src="/${entry}"></script>
311
322
  </body>
312
323
  </html>
313
324
  `
@@ -396,6 +407,14 @@ function normalizeStarter(value) {
396
407
  return normalized
397
408
  }
398
409
 
410
+ function projectRelativePath(value, label) {
411
+ const normalized = path.posix.normalize(String(value || '').replaceAll('\\', '/')).replace(/^\.\//, '')
412
+ if (!normalized || normalized === '.' || normalized === '..' || normalized.startsWith('../') || path.posix.isAbsolute(normalized)) {
413
+ fail(`--${label} must be a path inside the project.`, ExitCode.usage)
414
+ }
415
+ return normalized
416
+ }
417
+
399
418
  function readPackageJson(targetDir) {
400
419
  const packagePath = path.join(targetDir, 'package.json')
401
420
  if (!exists(packagePath)) return {}
@@ -420,6 +439,8 @@ Options:
420
439
  --examples-repo <git-url-or-local-path>
421
440
  --examples-ref <git-ref>
422
441
  --targets web,esp32,rp2350,geaos,macos,ios,android
442
+ --entry <relative-path>
443
+ --ble-ota
423
444
  --core-dependency <specifier>
424
445
  --cli-dependency <specifier>
425
446
  --install / --no-install
package/src/gea.mjs CHANGED
@@ -1,6 +1,8 @@
1
1
  import path from 'node:path'
2
+ import fs from 'node:fs'
2
3
 
3
4
  import { flag, option, parseArgs } from './args.mjs'
5
+ import { runCreateGeastack } from './create-geastack.mjs'
4
6
  import { createChildEnv, createContext } from './context.mjs'
5
7
  import { ExitCode, fail } from './errors.mjs'
6
8
  import { exists, readJson } from './fs-utils.mjs'
@@ -18,7 +20,7 @@ import { runExternal } from './run.mjs'
18
20
  import { runSetupWizard } from './setup-wizard.mjs'
19
21
  import { commandVersion, nodeAtLeast } from './toolchain.mjs'
20
22
 
21
- const version = '0.1.1'
23
+ const version = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version
22
24
 
23
25
  export async function runGea(argv, io = {}) {
24
26
  const parsed = parseArgs(argv)
@@ -35,6 +37,11 @@ export async function runGea(argv, io = {}) {
35
37
  stdout(version)
36
38
  return 0
37
39
  }
40
+ if (command === 'create') {
41
+ const createArgs = argv.slice(1)
42
+ if (option(parsed, 'install') === undefined) createArgs.push('--install')
43
+ return runCreateGeastack(createArgs, io)
44
+ }
38
45
  if (!command || command === 'help' || flag(parsed, 'help')) {
39
46
  stdout(usage())
40
47
  return 0
@@ -422,6 +429,7 @@ function requirePath(filePath, label) {
422
429
 
423
430
  function usage() {
424
431
  return `Usage:
432
+ gea create <name> [--starter counter|empty|example] [--targets <list>]
425
433
  gea doctor [--strict] [--json]
426
434
  gea setup --board <alias>
427
435
  gea dev [app] [--target web] [--port 5181]
@@ -20,7 +20,6 @@
20
20
  "name": "Counter",
21
21
  "description": "Minimal JSX counter with one tiny store.",
22
22
  "entry": "index.tsx",
23
- "runtime": "gea",
24
23
  "targets": {
25
24
  "web": true,
26
25
  "esp32": true,
@@ -7,7 +7,6 @@
7
7
  "description": "Minimal JSX counter with one tiny store.",
8
8
  "path": "bundled/counter",
9
9
  "entry": "index.tsx",
10
- "runtime": "gea",
11
10
  "targets": {
12
11
  "web": true,
13
12
  "esp32": true,