create-octane-xplat 0.13.0 → 0.15.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 +21 -1
- package/index.mjs +109 -39
- package/library-template/.oxlintrc.json +24 -0
- package/library-template/AGENTS.md +16 -0
- package/{LICENSE → library-template/LICENSE} +6 -6
- package/library-template/README.md +64 -0
- package/library-template/gitignore +5 -0
- package/library-template/package.json +89 -0
- package/library-template/pnpm-workspace.yaml +17 -0
- package/library-template/src/Badge.tsx +21 -0
- package/library-template/src/Badge.web.tsx +21 -0
- package/library-template/src/index.ts +3 -0
- package/library-template/src/index.web.ts +3 -0
- package/library-template/src/normalizeBadgeLabel.ts +12 -0
- package/library-template/src/props.ts +14 -0
- package/library-template/tests/normalizeBadgeLabel.test.ts +6 -0
- package/library-template/tests/packed-consumer.mjs +155 -0
- package/library-template/tsconfig.envs.json +14 -0
- package/library-template/tsconfig.json +27 -0
- package/library-template/tsconfig.types.json +11 -0
- package/library-template/tsrx-typegen.json +16 -0
- package/package.json +5 -3
- package/scaffold.mjs +105 -14
- package/template/.agents/skills/xplat/SKILL.md +1 -1
- package/template/.agents/skills/xplat/references/navigation.md +9 -5
- package/template/.oxlintrc.json +0 -1
- package/template/package.json +19 -18
- package/template/pnpm-workspace.yaml +34 -7
- package/template/src/app.spec.tsrx +80 -2
- package/template/src/failure-reporting.spec.ts +11 -0
- package/template/src/native-runtime.spec.ts +18 -0
- package/template/tsconfig.json +31 -7
- package/template/vite.config.ts +2 -2
- package/template/tsconfig.native.json +0 -18
package/README.md
CHANGED
|
@@ -16,6 +16,26 @@ With Node.js and pnpm 11.24.0 installed:
|
|
|
16
16
|
pnpm create octane-xplat my-app
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
## Start a library package
|
|
20
|
+
|
|
21
|
+
To start a reusable package instead of an app, select the library template:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
pnpm create octane-xplat my-controls --template library
|
|
25
|
+
cd my-controls
|
|
26
|
+
pnpm typecheck
|
|
27
|
+
pnpm typegen
|
|
28
|
+
pnpm test:packed
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The library starter publishes source for web, iOS, and Android, which the
|
|
32
|
+
consuming app compiles. It includes conditional exports, generated `.d.ts`
|
|
33
|
+
declarations, lint and unit-test
|
|
34
|
+
scripts, and a packed-consumer check. Pass `--name @acme/my-controls` to set a
|
|
35
|
+
scoped npm package name; otherwise the folder name becomes the package name.
|
|
36
|
+
Library creation installs dependencies but does not start an app dev server.
|
|
37
|
+
Desktop targets are not included in this starter.
|
|
38
|
+
|
|
19
39
|
The creator asks which platforms to target — web, iOS, and Android are all
|
|
20
40
|
pre-selected. Pass `--targets` to skip the prompt:
|
|
21
41
|
|
|
@@ -59,7 +79,7 @@ From that directory, these commands run other targets or check the app — the
|
|
|
59
79
|
pnpm dev:ios # needs macOS, Xcode, and NativeScript setup
|
|
60
80
|
pnpm dev:android # needs Android SDK, compatible JDK, and NativeScript setup
|
|
61
81
|
pnpm lint
|
|
62
|
-
pnpm typecheck # every
|
|
82
|
+
pnpm typecheck # tsconfig.json + every declared environment
|
|
63
83
|
pnpm build # web production bundle
|
|
64
84
|
```
|
|
65
85
|
|
package/index.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { existsSync, readdirSync } from 'node:fs'
|
|
3
|
-
import { resolve } from 'node:path'
|
|
3
|
+
import { basename, resolve } from 'node:path'
|
|
4
4
|
import { spawnSync } from 'node:child_process'
|
|
5
5
|
import {
|
|
6
|
+
composeLibrary,
|
|
6
7
|
composeTargets,
|
|
7
8
|
defaultTargets,
|
|
8
9
|
resolveTargets,
|
|
@@ -12,6 +13,41 @@ import {
|
|
|
12
13
|
|
|
13
14
|
const argv = process.argv.slice(2)
|
|
14
15
|
const noInstall = argv.includes('--no-install')
|
|
16
|
+
const templateIndex = argv.findIndex((arg) => arg === '--template' || arg.startsWith('--template='))
|
|
17
|
+
const templateValueIndex =
|
|
18
|
+
templateIndex !== -1 && argv[templateIndex] === '--template' ? templateIndex + 1 : -1
|
|
19
|
+
|
|
20
|
+
const templateArg =
|
|
21
|
+
templateIndex === -1
|
|
22
|
+
? 'app'
|
|
23
|
+
: templateValueIndex !== -1
|
|
24
|
+
? argv[templateValueIndex]
|
|
25
|
+
: argv[templateIndex].slice('--template='.length)
|
|
26
|
+
|
|
27
|
+
const nameIndex = argv.findIndex((arg) => arg === '--name' || arg.startsWith('--name='))
|
|
28
|
+
const nameValueIndex = nameIndex !== -1 && argv[nameIndex] === '--name' ? nameIndex + 1 : -1
|
|
29
|
+
const nameArg =
|
|
30
|
+
nameIndex === -1
|
|
31
|
+
? undefined
|
|
32
|
+
: nameValueIndex !== -1
|
|
33
|
+
? argv[nameValueIndex]
|
|
34
|
+
: argv[nameIndex].slice('--name='.length)
|
|
35
|
+
|
|
36
|
+
if (templateIndex !== -1 && (!templateArg || templateArg.startsWith('-'))) {
|
|
37
|
+
console.error('✗ --template needs a value: app or library')
|
|
38
|
+
process.exit(1)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!['app', 'library'].includes(templateArg)) {
|
|
42
|
+
console.error(`✗ unknown template "${templateArg}" — expected app or library`)
|
|
43
|
+
process.exit(1)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (nameIndex !== -1 && (!nameArg || nameArg.startsWith('-'))) {
|
|
47
|
+
console.error('✗ --name needs an npm package name')
|
|
48
|
+
process.exit(1)
|
|
49
|
+
}
|
|
50
|
+
|
|
15
51
|
const targetsIndex = argv.findIndex((arg) => arg === '--targets' || arg.startsWith('--targets='))
|
|
16
52
|
const targetsValueIndex =
|
|
17
53
|
targetsIndex !== -1 && argv[targetsIndex] === '--targets' ? targetsIndex + 1 : -1
|
|
@@ -28,8 +64,26 @@ if (targetsIndex !== -1 && (targetsArg === undefined || targetsArg.startsWith('-
|
|
|
28
64
|
process.exit(1)
|
|
29
65
|
}
|
|
30
66
|
|
|
31
|
-
const
|
|
32
|
-
|
|
67
|
+
const optionValues = new Set(
|
|
68
|
+
[targetsValueIndex, templateValueIndex, nameValueIndex].filter((index) => index !== -1),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
const positional = argv.filter((arg, i) => !arg.startsWith('-') && !optionValues.has(i))
|
|
72
|
+
const defaultDir = templateArg === 'library' ? 'octane-xplat-library' : 'octane-xplat-app'
|
|
73
|
+
const dir = resolve(positional[0] ?? defaultDir)
|
|
74
|
+
|
|
75
|
+
if (templateArg === 'library' && targetsIndex !== -1) {
|
|
76
|
+
console.error(
|
|
77
|
+
'✗ --targets applies to the app template; libraries start with web and native builds',
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
process.exit(1)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (templateArg === 'app' && nameIndex !== -1) {
|
|
84
|
+
console.error('✗ --name applies only to the library template')
|
|
85
|
+
process.exit(1)
|
|
86
|
+
}
|
|
33
87
|
|
|
34
88
|
if (existsSync(dir) && readdirSync(dir).length > 0) {
|
|
35
89
|
console.error(`✗ ${dir} exists and is not empty`)
|
|
@@ -57,69 +111,85 @@ const parseTargets = (value) => {
|
|
|
57
111
|
return resolveTargets(ids)
|
|
58
112
|
}
|
|
59
113
|
|
|
60
|
-
let ids
|
|
61
|
-
if (
|
|
114
|
+
let ids = []
|
|
115
|
+
if (templateArg === 'library') {
|
|
116
|
+
const packageName = nameArg ?? basename(dir)
|
|
62
117
|
try {
|
|
63
|
-
|
|
118
|
+
composeLibrary(dir, packageName)
|
|
64
119
|
} catch (error) {
|
|
65
120
|
console.error(`✗ ${error.message}`)
|
|
66
121
|
process.exit(1)
|
|
67
122
|
}
|
|
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
123
|
|
|
89
|
-
|
|
124
|
+
console.log(`\n✓ scaffolded ${dir} (library: ${packageName}; web + iOS + Android)`)
|
|
90
125
|
} else {
|
|
91
|
-
|
|
92
|
-
|
|
126
|
+
if (targetsArg !== undefined) {
|
|
127
|
+
try {
|
|
128
|
+
ids = parseTargets(targetsArg)
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error(`✗ ${error.message}`)
|
|
131
|
+
process.exit(1)
|
|
132
|
+
}
|
|
133
|
+
} else if (process.stdout.isTTY) {
|
|
134
|
+
// Lazy: the extracted tarball has no installed deps — only the interactive
|
|
135
|
+
// path needs @clack/prompts (verify-consumer runs the bin dep-free).
|
|
136
|
+
const p = await import('@clack/prompts')
|
|
137
|
+
p.intro('create-octane-xplat')
|
|
138
|
+
const picked = await p.multiselect({
|
|
139
|
+
message: 'Target platforms',
|
|
140
|
+
options: selectableTargets.map((id) => ({
|
|
141
|
+
value: id,
|
|
142
|
+
label: targets[id].label,
|
|
143
|
+
hint: targets[id].note,
|
|
144
|
+
})),
|
|
145
|
+
initialValues: [...defaultTargets],
|
|
146
|
+
required: true,
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
if (p.isCancel(picked)) {
|
|
150
|
+
p.cancel('Cancelled')
|
|
151
|
+
process.exit(0)
|
|
152
|
+
}
|
|
93
153
|
|
|
94
|
-
|
|
154
|
+
ids = picked
|
|
155
|
+
} else {
|
|
156
|
+
ids = defaultTargets
|
|
157
|
+
}
|
|
95
158
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
)
|
|
159
|
+
composeTargets(ids, dir)
|
|
160
|
+
console.log(
|
|
161
|
+
`\n✓ scaffolded ${dir} (${[...new Set(resolveTargets(ids))].filter((id) => !targets[id].hidden).join(', ')})`,
|
|
162
|
+
)
|
|
163
|
+
}
|
|
99
164
|
|
|
100
165
|
if (noInstall) {
|
|
101
166
|
console.log('\nSkipped install (--no-install). To run manually:')
|
|
102
|
-
printNextSteps(dir, ids)
|
|
167
|
+
printNextSteps(dir, ids, templateArg)
|
|
103
168
|
process.exit(0)
|
|
104
169
|
}
|
|
105
170
|
|
|
106
171
|
const install = spawnSync('pnpm', ['install'], { cwd: dir, stdio: 'inherit' })
|
|
107
172
|
if (install.status !== 0) {
|
|
108
173
|
console.log(`\nInstall didn't finish (pnpm missing?). To run manually:`)
|
|
109
|
-
printNextSteps(dir, ids)
|
|
174
|
+
printNextSteps(dir, ids, templateArg)
|
|
110
175
|
process.exit(install.status ?? 1)
|
|
111
176
|
}
|
|
112
177
|
|
|
113
|
-
if (ids.includes('web')) {
|
|
178
|
+
if (templateArg === 'app' && ids.includes('web')) {
|
|
114
179
|
console.log('\n✓ installed — starting dev server (Ctrl+C to stop)\n')
|
|
115
180
|
spawnSync('pnpm', ['dev'], { cwd: dir, stdio: 'inherit' })
|
|
116
181
|
} else {
|
|
117
|
-
console.log('\n✓ installed.
|
|
118
|
-
printNextSteps(dir, ids)
|
|
182
|
+
console.log('\n✓ installed. Next steps:')
|
|
183
|
+
printNextSteps(dir, ids, templateArg)
|
|
119
184
|
}
|
|
120
185
|
|
|
121
|
-
function printNextSteps(dir, ids) {
|
|
186
|
+
function printNextSteps(dir, ids, template) {
|
|
122
187
|
console.log(` cd ${dir}\n pnpm install`)
|
|
188
|
+
if (template === 'library') {
|
|
189
|
+
console.log(' pnpm typecheck\n pnpm lint\n pnpm typegen\n pnpm test\n pnpm test:packed')
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
|
|
123
193
|
if (ids.includes('web')) {
|
|
124
194
|
console.log(' pnpm dev')
|
|
125
195
|
} else {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"jsPlugins": ["@octane-xplat/lint/oxlint-plugin"],
|
|
3
|
+
"rules": {
|
|
4
|
+
"curly": "error",
|
|
5
|
+
"xplat/no-dom-globals": "error",
|
|
6
|
+
"xplat/no-web-only-api": "error",
|
|
7
|
+
"xplat/element-vocabulary": "error",
|
|
8
|
+
"xplat/no-nativescript-import": "error",
|
|
9
|
+
"xplat/no-ns-deep-import": "error",
|
|
10
|
+
"xplat/no-native-dead-style": "error",
|
|
11
|
+
"xplat/no-line-height-unitless": "error",
|
|
12
|
+
"xplat/no-style-string": "error",
|
|
13
|
+
"xplat/no-unknown-class": "warn",
|
|
14
|
+
"xplat/no-ts-imports-tsrx": "error",
|
|
15
|
+
"xplat/signal-dollar-suffix": "error",
|
|
16
|
+
"xplat/require-signals-runtime": "warn",
|
|
17
|
+
"xplat/no-view-onpress": "error",
|
|
18
|
+
"xplat/no-console-debug": "error",
|
|
19
|
+
"xplat/hooks-in-plain-ts": "warn",
|
|
20
|
+
"xplat/native-pragma-first-line": "error",
|
|
21
|
+
"xplat/no-jsx-text-breakers": "error",
|
|
22
|
+
"xplat/no-root-logical-render": "error"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Library package notes
|
|
2
|
+
|
|
3
|
+
- Use pnpm for package management.
|
|
4
|
+
- Keep the public API in `src/index.ts` and `src/index.web.ts`; both barrels
|
|
5
|
+
must export the same names and types.
|
|
6
|
+
- Put shared prop contracts in `src/props.ts` and document public exports.
|
|
7
|
+
- The unsuffixed `.tsx` file is the NativeScript implementation. Add a
|
|
8
|
+
`.web.tsx` sibling when browser behavior or markup needs to differ. Native
|
|
9
|
+
files start with `/** @jsxImportSource @nativescript-community/octane */`.
|
|
10
|
+
- Keep DOM globals out of native files. Use the shared `@octane-xplat/ui`
|
|
11
|
+
elements when behavior can be shared.
|
|
12
|
+
- Declare every imported package. Runtime plugins belong in `dependencies`;
|
|
13
|
+
Octane and renderer packages belong in `peerDependencies` when consumers
|
|
14
|
+
supply the runtime.
|
|
15
|
+
- Before publishing, run `pnpm typecheck`, `pnpm lint`, `pnpm test`,
|
|
16
|
+
`pnpm typegen`, and `pnpm test:packed`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) [year] [copyright holder]
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
9
9
|
copies of the Software, and to permit persons to whom the Software is
|
|
10
10
|
furnished to do so, subject to the following conditions:
|
|
11
11
|
|
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
|
13
|
-
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
14
|
|
|
15
15
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
16
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
17
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
-
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# octane-xplat-library
|
|
2
|
+
|
|
3
|
+
A cross-platform component library for Octane Xplat. The starter includes a
|
|
4
|
+
small `Badge` component and a packed-consumer check so you can build a package
|
|
5
|
+
and check the artifact that will be published.
|
|
6
|
+
|
|
7
|
+
## Start here
|
|
8
|
+
|
|
9
|
+
Install this package in an Octane Xplat app:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pnpm add octane-xplat-library
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Render the example component from your app:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { Badge } from 'octane-xplat-library'
|
|
19
|
+
|
|
20
|
+
export function Welcome() {
|
|
21
|
+
return <Badge label="Ready" />
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Develop and check
|
|
26
|
+
|
|
27
|
+
Run these commands from the package directory:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
pnpm typecheck
|
|
31
|
+
pnpm lint
|
|
32
|
+
pnpm test
|
|
33
|
+
pnpm typegen
|
|
34
|
+
pnpm test:packed
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The package publishes authored source for the app's bundler to compile.
|
|
38
|
+
`pnpm typegen` generates `.d.ts` files so consumers' type checkers use
|
|
39
|
+
declarations. `pnpm test:packed` packs the package, places that artifact into
|
|
40
|
+
small web and native TypeScript consumers, and checks both export conditions.
|
|
41
|
+
|
|
42
|
+
The package sets `nodeLinker: hoisted` in `pnpm-workspace.yaml` because the
|
|
43
|
+
NativeScript bundler expects a flat dependency tree.
|
|
44
|
+
|
|
45
|
+
## Platform files
|
|
46
|
+
|
|
47
|
+
`src/Badge.tsx` is the shared iOS and Android implementation. The
|
|
48
|
+
`src/Badge.web.tsx` sibling supplies the web implementation. Keep their props
|
|
49
|
+
and exports aligned in `src/props.ts` and the two `src/index` barrels. Add
|
|
50
|
+
`.ios.tsx`, `.android.tsx`, or `.mobile.tsx` files only when a behavior needs
|
|
51
|
+
to differ on those targets.
|
|
52
|
+
|
|
53
|
+
The starter supports web, iOS, and Android. It does not include app shells or
|
|
54
|
+
desktop runtime entries. If the package uses a NativeScript plugin, declare it
|
|
55
|
+
as a runtime dependency and add the plugin's required platform files and
|
|
56
|
+
metadata to this package.
|
|
57
|
+
|
|
58
|
+
## Prepare to publish
|
|
59
|
+
|
|
60
|
+
Before publishing, replace the generated package name and repository metadata
|
|
61
|
+
in `package.json`, fill in the year and copyright holder in `LICENSE`, and
|
|
62
|
+
confirm that every runtime import is declared. `pnpm test:packed` checks the
|
|
63
|
+
package tarball's web and native type exports; it does not build or launch a
|
|
64
|
+
consuming app on a device.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "octane-xplat-library",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A cross-platform component library for Octane Xplat",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"android",
|
|
7
|
+
"cross-platform",
|
|
8
|
+
"ios",
|
|
9
|
+
"octane",
|
|
10
|
+
"octane-xplat",
|
|
11
|
+
"ui",
|
|
12
|
+
"web"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/your-org/your-library",
|
|
18
|
+
"directory": "."
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"src",
|
|
22
|
+
"types",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"macos": null,
|
|
29
|
+
"linux": null,
|
|
30
|
+
"windows": null,
|
|
31
|
+
"web": {
|
|
32
|
+
"types": "./types/index.web.d.ts",
|
|
33
|
+
"default": "./src/index.web.ts"
|
|
34
|
+
},
|
|
35
|
+
"native": {
|
|
36
|
+
"types": "./types/index.d.ts",
|
|
37
|
+
"default": "./src/index.ts"
|
|
38
|
+
},
|
|
39
|
+
"types": "./types/index.d.ts",
|
|
40
|
+
"default": "./src/index.ts"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"typegen": "tsrx-typegen --project tsconfig.types.json",
|
|
48
|
+
"typecheck": "xplat typecheck",
|
|
49
|
+
"lint": "xplat-lint",
|
|
50
|
+
"lint:fix": "xplat-lint --fix",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"test:packed": "node tests/packed-consumer.mjs",
|
|
53
|
+
"prepack": "pnpm typegen && tsrx-typegen --pack-check",
|
|
54
|
+
"pack:check": "pnpm test:packed",
|
|
55
|
+
"format": "xplat fmt"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@nativescript-community/octane": "catalog:",
|
|
59
|
+
"@nativescript/core": "catalog:",
|
|
60
|
+
"@nativescript/types": "catalog:",
|
|
61
|
+
"@octane-xplat/cli": "^0.15.0",
|
|
62
|
+
"@octane-xplat/lint": "^0.15.0",
|
|
63
|
+
"@octane-xplat/ui": "^0.15.0",
|
|
64
|
+
"@octanejs/vite-plugin": "catalog:",
|
|
65
|
+
"@tsrx/oxc": "0.20.0",
|
|
66
|
+
"@tsrx/typescript-plugin": "catalog:",
|
|
67
|
+
"octane": "catalog:",
|
|
68
|
+
"oxlint": "^1.74.0",
|
|
69
|
+
"tsrx-typegen": "^0.2.2",
|
|
70
|
+
"typescript": "catalog:",
|
|
71
|
+
"vite": "catalog:",
|
|
72
|
+
"vitest": "catalog:"
|
|
73
|
+
},
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"@nativescript-community/octane": ">=0.2.1 <1",
|
|
76
|
+
"@nativescript/core": ">=9.1.0 <10",
|
|
77
|
+
"@octane-xplat/ui": "^0.15.0",
|
|
78
|
+
"octane": ">=0.6.3 <1"
|
|
79
|
+
},
|
|
80
|
+
"peerDependenciesMeta": {
|
|
81
|
+
"@nativescript-community/octane": {
|
|
82
|
+
"optional": true
|
|
83
|
+
},
|
|
84
|
+
"@nativescript/core": {
|
|
85
|
+
"optional": true
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"packageManager": "pnpm@11.24.0"
|
|
89
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# NativeScript's bundler resolves dependencies from the flat package tree.
|
|
2
|
+
nodeLinker: hoisted
|
|
3
|
+
allowBuilds:
|
|
4
|
+
esbuild: true
|
|
5
|
+
'@parcel/watcher': true
|
|
6
|
+
'@nativescript/core': false
|
|
7
|
+
# Shared version pins — bump these here, every dependency field reads them.
|
|
8
|
+
catalog:
|
|
9
|
+
octane: 0.11.0
|
|
10
|
+
'@nativescript-community/octane': 0.2.4
|
|
11
|
+
'@nativescript/core': 9.1.3
|
|
12
|
+
'@nativescript/types': 9.1.4
|
|
13
|
+
typescript: 5.9.3
|
|
14
|
+
vite: 8.3.0
|
|
15
|
+
'@octanejs/vite-plugin': 0.1.61
|
|
16
|
+
'@tsrx/typescript-plugin': 0.4.11
|
|
17
|
+
vitest: 4.1.11
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** @jsxImportSource @nativescript-community/octane */
|
|
2
|
+
import { Text, View } from '@octane-xplat/ui'
|
|
3
|
+
import { normalizeBadgeLabel } from './normalizeBadgeLabel'
|
|
4
|
+
import type { BadgeProps } from './props'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Render a short label using Octane's NativeScript elements.
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* import { Badge } from 'octane-xplat-library'
|
|
11
|
+
*
|
|
12
|
+
* <Badge label="Ready" />
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export function Badge(props: BadgeProps) {
|
|
16
|
+
return (
|
|
17
|
+
<View className={props.className}>
|
|
18
|
+
<Text>{normalizeBadgeLabel(props.label)}</Text>
|
|
19
|
+
</View>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** @jsxImportSource octane */
|
|
2
|
+
import { Text, View } from '@octane-xplat/ui'
|
|
3
|
+
import { normalizeBadgeLabel } from './normalizeBadgeLabel'
|
|
4
|
+
import type { BadgeProps } from './props'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Render a short label using Octane's web elements.
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* import { Badge } from 'octane-xplat-library'
|
|
11
|
+
*
|
|
12
|
+
* <Badge label="Ready" />
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export function Badge(props: BadgeProps) {
|
|
16
|
+
return (
|
|
17
|
+
<View className={props.className}>
|
|
18
|
+
<Text>{normalizeBadgeLabel(props.label)}</Text>
|
|
19
|
+
</View>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trim surrounding whitespace from a badge label.
|
|
3
|
+
* @example
|
|
4
|
+
* ```ts
|
|
5
|
+
* import { normalizeBadgeLabel } from 'octane-xplat-library'
|
|
6
|
+
*
|
|
7
|
+
* normalizeBadgeLabel(' Ready ')
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export function normalizeBadgeLabel(label: string): string {
|
|
11
|
+
return label.trim()
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Props for the starter badge component. */
|
|
2
|
+
export interface BadgeProps {
|
|
3
|
+
/** Text displayed inside the badge.
|
|
4
|
+
* @example
|
|
5
|
+
* ```tsx
|
|
6
|
+
* import { Badge } from 'octane-xplat-library'
|
|
7
|
+
*
|
|
8
|
+
* <Badge label="Ready" />
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
label: string
|
|
12
|
+
/** Optional class name forwarded to the outer view. */
|
|
13
|
+
className?: string
|
|
14
|
+
}
|