create-octane-xplat 0.9.0 → 0.11.0
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 +18 -3
- package/index.mjs +106 -16
- package/package.json +8 -1
- package/scaffold.mjs +532 -0
- package/template/.agents/skills/xplat/SKILL.md +107 -38
- package/template/.agents/skills/xplat/references/components.md +172 -96
- package/template/.agents/skills/xplat/references/navigation.md +81 -31
- package/template/.agents/skills/xplat/references/platform.md +74 -39
- package/template/.agents/skills/xplat/references/styling.md +80 -27
- package/template/package.json +6 -5
- package/template/pnpm-workspace.yaml +36 -7
- package/template/src/App.tsrx +5 -5
- package/template/src/style.css +24 -6
- package/template/vite.config.native.mts +3 -2
package/README.md
CHANGED
|
@@ -16,8 +16,21 @@ With Node.js and pnpm 11.24.0 installed:
|
|
|
16
16
|
pnpm create octane-xplat my-app
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
The creator asks which platforms to target — web, iOS, and Android are all
|
|
20
|
+
pre-selected. Pass `--targets` to skip the prompt:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
pnpm create octane-xplat my-app --targets web # web only
|
|
24
|
+
pnpm create octane-xplat my-app --targets web,android # web + Android
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Platforms skipped here can be enabled later with `pnpm xplat add <platform>`
|
|
28
|
+
inside the app. (Non-interactive runs without `--targets` scaffold all
|
|
29
|
+
three.)
|
|
30
|
+
|
|
19
31
|
The creator copies the template, installs dependencies with pnpm, and starts
|
|
20
|
-
the web dev server. Open the printed URL and
|
|
32
|
+
the web dev server when the web target is included. Open the printed URL and
|
|
33
|
+
open `my-app` in your agent.
|
|
21
34
|
Ask it to read `AGENTS.md` and `.agents/skills/xplat/SKILL.md`, then build a
|
|
22
35
|
packing checklist with add, pack, and remove actions using in-memory state.
|
|
23
36
|
Check the result before asking for the next feature: add “Passport” and
|
|
@@ -38,13 +51,15 @@ cd my-app
|
|
|
38
51
|
pnpm dev # web
|
|
39
52
|
```
|
|
40
53
|
|
|
41
|
-
From that directory, these commands run other targets or check the app
|
|
54
|
+
From that directory, these commands run other targets or check the app — the
|
|
55
|
+
`dev:*`/`build:*` entries exist only for platforms chosen at create time
|
|
56
|
+
(`pnpm xplat add` adds the rest later):
|
|
42
57
|
|
|
43
58
|
```sh
|
|
44
59
|
pnpm dev:ios # needs macOS, Xcode, and NativeScript setup
|
|
45
60
|
pnpm dev:android # needs Android SDK, compatible JDK, and NativeScript setup
|
|
46
61
|
pnpm lint
|
|
47
|
-
pnpm typecheck #
|
|
62
|
+
pnpm typecheck # every enabled tsconfig
|
|
48
63
|
pnpm build # web production bundle
|
|
49
64
|
```
|
|
50
65
|
|
package/index.mjs
CHANGED
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, readdirSync
|
|
3
|
-
import {
|
|
4
|
-
import { fileURLToPath } from 'node:url'
|
|
2
|
+
import { existsSync, readdirSync } from 'node:fs'
|
|
3
|
+
import { resolve } from 'node:path'
|
|
5
4
|
import { spawnSync } from 'node:child_process'
|
|
5
|
+
import {
|
|
6
|
+
composeTargets,
|
|
7
|
+
defaultTargets,
|
|
8
|
+
resolveTargets,
|
|
9
|
+
selectableTargets,
|
|
10
|
+
targets,
|
|
11
|
+
} from './scaffold.mjs'
|
|
6
12
|
|
|
7
|
-
const
|
|
8
|
-
const noInstall =
|
|
13
|
+
const argv = process.argv.slice(2)
|
|
14
|
+
const noInstall = argv.includes('--no-install')
|
|
15
|
+
const targetsIndex = argv.findIndex((arg) => arg === '--targets' || arg.startsWith('--targets='))
|
|
16
|
+
const targetsValueIndex =
|
|
17
|
+
targetsIndex !== -1 && argv[targetsIndex] === '--targets' ? targetsIndex + 1 : -1
|
|
18
|
+
|
|
19
|
+
const targetsArg =
|
|
20
|
+
targetsIndex === -1
|
|
21
|
+
? undefined
|
|
22
|
+
: targetsValueIndex !== -1
|
|
23
|
+
? argv[targetsValueIndex]
|
|
24
|
+
: argv[targetsIndex].slice('--targets='.length)
|
|
25
|
+
|
|
26
|
+
if (targetsIndex !== -1 && (targetsArg === undefined || targetsArg.startsWith('-'))) {
|
|
27
|
+
console.error(`✗ --targets needs a comma list: ${selectableTargets.join(', ')}`)
|
|
28
|
+
process.exit(1)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const positional = argv.filter((arg, i) => !arg.startsWith('-') && i !== targetsValueIndex)
|
|
9
32
|
const dir = resolve(positional[0] ?? 'octane-xplat-app')
|
|
10
33
|
|
|
11
34
|
if (existsSync(dir) && readdirSync(dir).length > 0) {
|
|
@@ -13,27 +36,94 @@ if (existsSync(dir) && readdirSync(dir).length > 0) {
|
|
|
13
36
|
process.exit(1)
|
|
14
37
|
}
|
|
15
38
|
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
39
|
+
const parseTargets = (value) => {
|
|
40
|
+
const ids = value
|
|
41
|
+
.split(',')
|
|
42
|
+
.map((s) => s.trim())
|
|
43
|
+
.filter(Boolean)
|
|
44
|
+
|
|
45
|
+
if (ids.length === 0) {
|
|
46
|
+
throw new Error(`--targets needs a comma list: ${selectableTargets.join(', ')}`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const id of ids) {
|
|
50
|
+
if (!selectableTargets.includes(id)) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`unknown target "${id}" — expected a comma list of: ${selectableTargets.join(', ')}`,
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return resolveTargets(ids)
|
|
58
|
+
}
|
|
22
59
|
|
|
23
|
-
|
|
60
|
+
let ids
|
|
61
|
+
if (targetsArg !== undefined) {
|
|
62
|
+
try {
|
|
63
|
+
ids = parseTargets(targetsArg)
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error(`✗ ${error.message}`)
|
|
66
|
+
process.exit(1)
|
|
67
|
+
}
|
|
68
|
+
} else if (process.stdout.isTTY) {
|
|
69
|
+
// Lazy: the extracted tarball has no installed deps — only the interactive
|
|
70
|
+
// path needs @clack/prompts (verify-consumer runs the bin dep-free).
|
|
71
|
+
const p = await import('@clack/prompts')
|
|
72
|
+
p.intro('create-octane-xplat')
|
|
73
|
+
const picked = await p.multiselect({
|
|
74
|
+
message: 'Target platforms',
|
|
75
|
+
options: selectableTargets.map((id) => ({
|
|
76
|
+
value: id,
|
|
77
|
+
label: targets[id].label,
|
|
78
|
+
hint: targets[id].note,
|
|
79
|
+
})),
|
|
80
|
+
initialValues: [...defaultTargets],
|
|
81
|
+
required: true,
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
if (p.isCancel(picked)) {
|
|
85
|
+
p.cancel('Cancelled')
|
|
86
|
+
process.exit(0)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
ids = picked
|
|
90
|
+
} else {
|
|
91
|
+
ids = defaultTargets
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
composeTargets(ids, dir)
|
|
95
|
+
|
|
96
|
+
console.log(
|
|
97
|
+
`\n✓ scaffolded ${dir} (${[...new Set(resolveTargets(ids))].filter((id) => !targets[id].hidden).join(', ')})`,
|
|
98
|
+
)
|
|
24
99
|
|
|
25
100
|
if (noInstall) {
|
|
26
101
|
console.log('\nSkipped install (--no-install). To run manually:')
|
|
27
|
-
|
|
102
|
+
printNextSteps(dir, ids)
|
|
28
103
|
process.exit(0)
|
|
29
104
|
}
|
|
30
105
|
|
|
31
106
|
const install = spawnSync('pnpm', ['install'], { cwd: dir, stdio: 'inherit' })
|
|
32
107
|
if (install.status !== 0) {
|
|
33
108
|
console.log(`\nInstall didn't finish (pnpm missing?). To run manually:`)
|
|
34
|
-
|
|
109
|
+
printNextSteps(dir, ids)
|
|
35
110
|
process.exit(install.status ?? 1)
|
|
36
111
|
}
|
|
37
112
|
|
|
38
|
-
|
|
39
|
-
|
|
113
|
+
if (ids.includes('web')) {
|
|
114
|
+
console.log('\n✓ installed — starting dev server (Ctrl+C to stop)\n')
|
|
115
|
+
spawnSync('pnpm', ['dev'], { cwd: dir, stdio: 'inherit' })
|
|
116
|
+
} else {
|
|
117
|
+
console.log('\n✓ installed. To run:')
|
|
118
|
+
printNextSteps(dir, ids)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function printNextSteps(dir, ids) {
|
|
122
|
+
console.log(` cd ${dir}\n pnpm install`)
|
|
123
|
+
if (ids.includes('web')) {
|
|
124
|
+
console.log(' pnpm dev')
|
|
125
|
+
} else {
|
|
126
|
+
// No web entry — `xplat dev` discovers whichever native targets exist.
|
|
127
|
+
console.log(' pnpm xplat dev')
|
|
128
|
+
}
|
|
129
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-octane-xplat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Scaffold a minimal @octane-xplat/ui app — one Octane codebase for web + iOS + Android",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"create",
|
|
@@ -24,10 +24,17 @@
|
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"index.mjs",
|
|
27
|
+
"scaffold.mjs",
|
|
27
28
|
"template"
|
|
28
29
|
],
|
|
29
30
|
"type": "module",
|
|
31
|
+
"exports": {
|
|
32
|
+
"./scaffold": "./scaffold.mjs"
|
|
33
|
+
},
|
|
30
34
|
"publishConfig": {
|
|
31
35
|
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@clack/prompts": "1.8.1"
|
|
32
39
|
}
|
|
33
40
|
}
|