create-octane-xplat 0.15.0 → 0.16.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.
Files changed (34) hide show
  1. package/README.md +57 -0
  2. package/compatibility.json +199 -0
  3. package/compatibility.ts +109 -0
  4. package/dist/compatibility.js +66 -0
  5. package/dist/index.js +132 -0
  6. package/dist/scaffold.js +439 -0
  7. package/{index.mjs → index.ts} +3 -4
  8. package/library-template/AGENTS.md +3 -1
  9. package/library-template/README.md +20 -0
  10. package/library-template/package.json +22 -19
  11. package/library-template/pnpm-workspace.yaml +150 -11
  12. package/library-template/src/Badge.tsx +1 -1
  13. package/library-template/src/Badge.web.tsx +1 -1
  14. package/library-template/tests/packed-consumer.mjs +2 -1
  15. package/library-template/tsconfig.envs.json +3 -0
  16. package/library-template/tsconfig.json +1 -1
  17. package/library-template/tsrx-typegen.json +1 -1
  18. package/package.json +23 -6
  19. package/{scaffold.mjs → scaffold.ts} +51 -40
  20. package/template/.agents/skills/xplat/SKILL.md +10 -2
  21. package/template/.agents/skills/xplat/references/components.md +2 -2
  22. package/template/.agents/skills/xplat/references/platform.md +2 -2
  23. package/template/AGENTS.md +47 -0
  24. package/template/App_Resources/Android/app.gradle +1 -1
  25. package/template/package.json +33 -31
  26. package/template/pnpm-workspace.yaml +64 -29
  27. package/template/src/app.web.test.tsx +20 -0
  28. package/template/src/main.web.tsrx +1 -0
  29. package/template/src/test-setup.web.ts +17 -0
  30. package/template/tsconfig.json +1 -1
  31. package/template/vite.config.ts +2 -2
  32. package/template/vitest.web.config.mts +13 -0
  33. package/library-template/.oxlintrc.json +0 -24
  34. package/template/.oxlintrc.json +0 -24
@@ -8,45 +8,47 @@
8
8
  "dev": "vite",
9
9
  "dev:ios": "ns run ios",
10
10
  "dev:android": "ns run android",
11
- "test:ios": "ns test ios",
12
- "test:android": "ns test android",
13
11
  "build": "vite build",
14
12
  "build:ios": "ns build ios",
15
13
  "build:android": "ns build android",
16
- "lint": "xplat-lint",
17
- "lint:fix": "xplat-lint --fix",
14
+ "test": "vitest run --config vitest.web.config.mts",
15
+ "test:web": "vitest run --config vitest.web.config.mts",
16
+ "test:ios": "ns test ios",
17
+ "test:android": "ns test android",
18
+ "lint": "xplat lint",
19
+ "lint:fix": "xplat lint --fix",
18
20
  "typecheck": "xplat typecheck",
19
21
  "format": "xplat fmt"
20
22
  },
21
23
  "dependencies": {
22
- "@nativescript-community/gesturehandler": "2.0.45",
23
- "@nativescript-community/octane": "catalog:",
24
- "@nativescript-community/ui-drawer": "0.1.32",
25
- "@nativescript/core": "catalog:",
26
- "@octane-xplat/effects": "^0.15.0",
27
- "@octane-xplat/image": "^0.15.0",
28
- "@octane-xplat/platform": "^0.15.0",
29
- "@octane-xplat/ui": "^0.15.0",
30
- "@valor/nativescript-websockets": "2.0.3",
31
- "octane": "catalog:"
24
+ "@nativescript-community/octane": "catalog:xplat",
25
+ "@nativescript/core": "catalog:xplat",
26
+ "@octane-xplat/jsx": "catalog:xplat",
27
+ "@octane-xplat/platform": "catalog:xplat",
28
+ "@octane-xplat/ui": "catalog:xplat",
29
+ "@triniwiz/nativescript-masonkit": "catalog:xplat",
30
+ "@valor/nativescript-websockets": "catalog:xplat",
31
+ "octane": "catalog:xplat"
32
32
  },
33
33
  "devDependencies": {
34
- "@nativescript-community/vite-octane": "catalog:",
35
- "@nativescript/android": "9.1.1",
36
- "@nativescript/ios": "9.1.0",
37
- "@nativescript/types": "catalog:",
38
- "@nativescript/unit-test-runner": "catalog:",
39
- "@nativescript/vite": "8.0.17",
40
- "@octane-xplat/cli": "^0.15.0",
41
- "@octane-xplat/lint": "^0.15.0",
42
- "@octanejs/vite-plugin": "catalog:",
43
- "@tsrx/oxc": "0.20.0",
44
- "@tsrx/typescript-plugin": "catalog:",
45
- "nativescript": "9.1.1",
46
- "oxlint": "^1.74.0",
47
- "typescript": "catalog:",
48
- "vite": "catalog:",
49
- "vitest": "catalog:"
34
+ "@nativescript-community/vite-octane": "catalog:xplat",
35
+ "@nativescript/android": "catalog:xplat",
36
+ "@nativescript/ios": "catalog:xplat",
37
+ "@nativescript/types": "catalog:xplat",
38
+ "@nativescript/unit-test-runner": "catalog:xplat",
39
+ "@nativescript/vite": "catalog:xplat",
40
+ "@octane-xplat/cli": "catalog:xplat",
41
+ "@octanejs/vite-plugin": "catalog:xplat",
42
+ "@tsrx/oxc": "catalog:xplat",
43
+ "@tsrx/typescript-plugin": "catalog:xplat",
44
+ "jsdom": "catalog:xplat",
45
+ "nativescript": "catalog:xplat",
46
+ "typescript": "catalog:xplat",
47
+ "vite": "catalog:xplat",
48
+ "vitest": "catalog:xplat"
50
49
  },
51
- "packageManager": "pnpm@11.24.0"
50
+ "packageManager": "pnpm@11.24.0",
51
+ "engines": {
52
+ "node": ">=22.18.0"
53
+ }
52
54
  }
@@ -14,19 +14,6 @@ allowBuilds:
14
14
  '@parcel/watcher': true
15
15
  nativescript: true
16
16
  '@nativescript/core': false
17
- # Shared version pins — bump these here, every dependency field reads them.
18
- catalog:
19
- octane: 0.11.0
20
- '@nativescript-community/octane': 0.2.4
21
- '@nativescript-community/vite-octane': 0.2.1
22
- '@nativescript/core': 9.1.3
23
- '@nativescript/types': 9.1.4
24
- '@nativescript/unit-test-runner': 5.0.0
25
- typescript: 5.9.3
26
- vite: 8.3.0
27
- '@octanejs/vite-plugin': 0.1.61
28
- '@tsrx/typescript-plugin': 0.4.11
29
- vitest: 4.1.11
30
17
  # The patch set covers optional leaf deps (tiptap, linkifyjs, …) the starter
31
18
  # doesn't install — tolerate entries that apply to nothing.
32
19
  allowUnusedPatches: true
@@ -74,30 +61,37 @@ patchedDependencies:
74
61
  # turbo.json as a workspace root — Turborepo's per-package task configs marked apps/* as
75
62
  # roots, masking the real pnpm root so served `/node_modules/` tails never reached the
76
63
  # `.pnpm` store scan and non-hoisted transitive deps like source-map-js 404'd dev boot.
77
- # The /ns/m import rewriter now resolves `#`-prefixed package `imports` specifiers through
78
- # the importer's own package.json — raw-read node_modules files (e.g. octane's
64
+ # Linked dependency fallbacks also discover each linked package's enclosing workspace and
65
+ # reuse the cached, bounded workspace node_modules scan so transitive workspace siblings
66
+ # (auth to secure-storage to a private native plugin) resolve without the consumer
67
+ # supplying that workspace root. Transform candidates now keep root-relative URLs only
68
+ # when they identify the discovered file; nested linked-dependency and pnpm-store matches
69
+ # inside the app root use their actual filesystem paths, preserving query suffixes. The
70
+ # /ns/m import rewriter now resolves `#`-prefixed package `imports` specifiers through the
71
+ # importer's own package.json — raw-read node_modules files (e.g. octane's
79
72
  # dist/signals/graph.js importing `#octane/dist/signal-actions/graph`) otherwise reach the
80
73
  # device verbatim and the HTTP loader throws `Cannot find module`, deadlocking dev boot
81
- # before any boot recording can bundle the dep.
74
+ # before any boot recording can bundle the dep. NativeScript#11551: preserve user virtual
75
+ # stylesheet imports while retaining browser-helper filtering, route virtual CSS through
76
+ # the native inline compiler/registration path before the generic virtual-module shortcut,
77
+ # and relay Vite virtual stylesheet updates as tagged native CSS updates through the same
78
+ # adapted-text endpoint. Empty sheets clear previous rules; CSS compilation failures
79
+ # return HTTP 500 instead of raw CSS.
82
80
  "@nativescript/vite@8.0.17": node_modules/.pnpm-config/@octane-xplat/patches/patches/@nativescript__vite@8.0.17.patch
83
- # Driver treats a prop dropped from an update's complete prop bag — or set to
84
- # `undefined`/`null` — as a removal and restores the documented default:
85
- # Property/CssProperty setters get `unsetValue`, plain fields restore the captured
86
- # pre-prop value, and applied `style` keys diff per key. A never-applied `undefined` still
87
- # skips the write since NativeScript native setters coerce it (e.g. editable →
88
- # ios.userInteractionEnabled = NO). Aligns intrinsic ref types with Octane callback
89
- # cleanup and nested arrays with optional entries. Advertises the implemented visibility
90
- # capability so committed suspense bodies can be retained while pending. Preserves
91
- # authored Grid row/col/span metadata independently of NativeScript defaults and computed
92
- # auto-placement, notifying Grid when placement props change.
93
- "@nativescript-community/octane@0.2.4": node_modules/.pnpm-config/@octane-xplat/patches/patches/@nativescript-community__octane@0.2.4.patch
94
81
  # Retains the workspace's NativeScript driver fixes when upgrading to 0.2.5 for portal
95
82
  # support: a prop dropped from an update's complete prop bag — or set to
96
83
  # `undefined`/`null` — is a removal that restores the documented default
97
84
  # (Property/CssProperty setters get `unsetValue`, plain fields the captured pre-prop
98
85
  # value, `style` keys diff per key); a never-applied `undefined` still skips the write.
99
86
  # Also advertises the implemented visibility capability, carries Octane callback ref
100
- # types, and preserves authored Grid row/col/span metadata.
87
+ # types, and preserves authored Grid row/col/span metadata. Hosts custom-layout containers
88
+ # (e.g. MasonKit ViewBase) that own child lists via
89
+ # insertChild/removeChild/getChildrenCount without subclassing LayoutBase. Additionally,
90
+ # the masonry cutover: masonview resolves to MasonKit View and scrollview to MasonKit
91
+ # Scroll as the only layout containers, with zero layout props — every former prop name
92
+ # warns-and-drops and all CSS semantics arrive through the style bag; style keys on a
93
+ # mason child route to its leaf (stashed pre-attach, replayed at attach) via
94
+ # Style.fromView.
101
95
  "@nativescript-community/octane@0.2.5": node_modules/.pnpm-config/@octane-xplat/patches/patches/@nativescript-community__octane@0.2.5.patch
102
96
  # octaneServerStrategy's SCRIPT_FILE_RE only covers [jt]sx? — .tsrx edits never emit the
103
97
  # deferred ns:hmr-delta broadcast, so component HMR silently never reaches the device.
@@ -138,5 +132,46 @@ patchedDependencies:
138
132
  # SDWebImageContextOriginalStoreCacheType so a memory prefetch does not spill original
139
133
  # bytes to disk.
140
134
  "@nativescript-community/ui-image@5.0.17": node_modules/.pnpm-config/@octane-xplat/patches/patches/@nativescript-community__ui-image@5.0.17.patch
135
+ # @nativescript/doctor's Android runtime check reads the app's raw devDependencies
136
+ # specifier and npm dist-tag resolves anything that isn't a semver range — a pnpm
137
+ # `catalog:` pin throws 'The determined Android runtime version ... is not valid' and
138
+ # kills `ns build android` before toolchain checks. Resolve catalog specifiers through the
139
+ # installed package's manifest, reusing the existing tgz-branch mechanism.
140
+ "@nativescript/doctor@2.0.18": node_modules/.pnpm-config/@octane-xplat/patches/patches/@nativescript__doctor@2.0.18.patch
141
+ # list/index.ios.js assigns `ObjCProtocols = [MasonListDelegate]` at module scope.
142
+ # MasonListDelegate is declared in Mason-Swift.h, which the app's metadata generation does
143
+ # not surface as a runtime global — importing masonkit on iOS throws `MasonListDelegate is
144
+ # not defined` during bundle eval and kills the app at boot. Guard the binding; actual
145
+ # List delegate conformance when the protocol is absent is a separate gap.
146
+ "@triniwiz/nativescript-masonkit@1.0.0-beta.110": node_modules/.pnpm-config/@octane-xplat/patches/patches/@triniwiz__nativescript-masonkit@1.0.0-beta.110.patch
141
147
  configDependencies:
142
- "@octane-xplat/patches": 0.15.0
148
+ "@octane-xplat/patches": 0.16.0
149
+ catalogs:
150
+ xplat:
151
+ "@nativescript-community/octane": 0.2.5
152
+ "@nativescript-community/vite-octane": 0.2.1
153
+ "@nativescript/android": 9.1.1
154
+ "@nativescript/core": 9.1.3
155
+ "@nativescript/ios": 9.1.0
156
+ "@nativescript/types": 9.1.4
157
+ "@nativescript/unit-test-runner": 5.0.0
158
+ "@nativescript/vite": 8.0.17
159
+ "@octane-xplat/cli": 0.16.0
160
+ "@octane-xplat/jsx": 0.16.0
161
+ "@octane-xplat/platform": 0.16.0
162
+ "@octane-xplat/ui": 0.16.0
163
+ "@octanejs/vite-plugin": 0.1.61
164
+ "@triniwiz/nativescript-masonkit": 1.0.0-beta.110
165
+ "@tsrx/oxc": 0.20.0
166
+ "@tsrx/typescript-plugin": 0.4.11
167
+ "@valor/nativescript-websockets": 2.0.3
168
+ jsdom: 30.1.1
169
+ nativescript: 9.1.1
170
+ octane: 0.11.0
171
+ typescript: 5.9.3
172
+ vite: 8.3.0
173
+ vitest: 4.1.11
174
+ overrides:
175
+ "@nativescript/vite>esbuild": 0.27.7
176
+ "@nativescript-community/vite-octane>esbuild": 0.27.7
177
+ vite>esbuild: 0.27.7
@@ -0,0 +1,20 @@
1
+ /** @jsxImportSource @octane-xplat/jsx/dom */
2
+ import { expect, it, vi } from 'vitest'
3
+ import { createRoot } from 'octane'
4
+ import { App } from './App.tsrx'
5
+
6
+ it('updates the counter when its button is pressed', async () => {
7
+ const container = document.createElement('div')
8
+ document.body.append(container)
9
+ const root = createRoot(container)
10
+ try {
11
+ root.render(<App />)
12
+ const value = () => container.querySelector('[data-testid="counter.value"]')?.textContent
13
+ await vi.waitFor(() => expect(value()).toBe('0'))
14
+ container.querySelector<HTMLElement>('[data-testid="counter.increment"]')!.click()
15
+ await vi.waitFor(() => expect(value()).toBe('1'))
16
+ } finally {
17
+ root.unmount()
18
+ container.remove()
19
+ }
20
+ })
@@ -1,3 +1,4 @@
1
+ /** @jsxImportSource @octane-xplat/jsx/dom */
1
2
  import { createRoot } from 'octane';
2
3
  import { App } from './App.tsrx';
3
4
  import '@octane-xplat/ui/theme/tokens.css';
@@ -0,0 +1,17 @@
1
+ import { vi } from 'vitest'
2
+
3
+ // jsdom has no system color-scheme producer. Keep component tests in light mode.
4
+ globalThis.matchMedia ??= (query: string) => ({
5
+ media: query,
6
+ matches: false,
7
+ onchange: null,
8
+ addListener() {},
9
+ removeListener() {},
10
+ addEventListener() {},
11
+ removeEventListener() {},
12
+ dispatchEvent: () => true,
13
+ })
14
+
15
+ // Node 26 also exposes a host localStorage getter. Use jsdom's window storage.
16
+ declare const jsdom: { window: Window }
17
+ vi.stubGlobal('localStorage', jsdom.window.localStorage)
@@ -5,7 +5,7 @@
5
5
  "module": "ESNext",
6
6
  "moduleResolution": "bundler",
7
7
  "jsx": "preserve",
8
- "jsxImportSource": "octane",
8
+ "jsxImportSource": "@octane-xplat/jsx",
9
9
  "moduleSuffixes": [
10
10
  "",
11
11
  ".web",
@@ -1,13 +1,13 @@
1
1
  import { defineConfig } from 'vite'
2
2
  import { octane } from '@octanejs/vite-plugin'
3
- import { xplatBoundary, xplatDevBridge, xplatNodeEnvDefine } from '@octane-xplat/cli/vite'
3
+ import { xplatCss, xplatBoundary, xplatDevBridge, xplatNodeEnvDefine } from '@octane-xplat/cli/vite'
4
4
 
5
5
  // The web build. For native (iOS/Android), the same source runs through
6
6
  // @nativescript-community/vite-octane with `conditions: ['native']`, the
7
7
  // .ios/.android/.mobile suffix chain, and the unsuffixed native default — see
8
8
  // the octane-xplat repo's apps/mobile/vite.config.mts.
9
9
  export default defineConfig(({ mode }) => ({
10
- plugins: [...octane(), xplatBoundary('web'), xplatDevBridge()],
10
+ plugins: [xplatCss('web'), ...octane(), xplatBoundary('web'), xplatDevBridge()],
11
11
  define: xplatNodeEnvDefine(mode),
12
12
  server: { port: 5200 },
13
13
  resolve: {
@@ -0,0 +1,13 @@
1
+ import { defineConfig, mergeConfig } from 'vitest/config'
2
+ import appConfig from './vite.config.ts'
3
+
4
+ // Reuse the app's compiler, web platform resolution and aliases.
5
+ export default defineConfig(async (env) =>
6
+ mergeConfig(await (typeof appConfig === 'function' ? appConfig(env) : appConfig), {
7
+ test: {
8
+ environment: 'jsdom',
9
+ include: ['src/**/*.web.test.{ts,tsx,tsrx}'],
10
+ setupFiles: ['src/test-setup.web.ts'],
11
+ },
12
+ }),
13
+ )
@@ -1,24 +0,0 @@
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
- }
@@ -1,24 +0,0 @@
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
- }