@symbiote-native/solid 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 A. Prokopenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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 THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # @symbiote-native/solid
2
+
3
+ SolidJS adapter for SymbioteNative. Compiled Solid JSX drives real native iOS/Android views through
4
+ the same `@symbiote-native/engine` every other adapter uses — React's renderer is never in the path.
5
+
6
+ **Status: L4 in progress (parity phase).** The renderer seam, `mount`/`unmount`, the primitives, the
7
+ stateful touch components, the Touchable family, Button / ImageBackground / InputAccessoryView,
8
+ StatusBar and the list family all ship at full parity with the React reference — off
9
+ `@symbiote-native/components`, via `descriptorToSolid` where the component has a shared render fn.
10
+ The engine-owned runtime modules are re-exported from the barrel. Still absent: `createPortal` and
11
+ `createTunnel`, both of which need a same-surface replacement for `solid-js/web`'s DOM-bound
12
+ `Portal`/`Dynamic`. See `symbiote-new-adapter` §7 for what each layer means and why the adapter is
13
+ built in that order.
14
+
15
+ Solid's own control-flow `Switch`/`Match` are NOT re-exported from this package: `Switch` collides
16
+ with RN's Switch component, which every adapter must export under that name. Import the control-flow
17
+ pair from `solid-js` directly.
18
+
19
+ Publishing is gated by `"private": true` in `package.json`, which is what
20
+ `scripts/lib/publishable-packages.mjs` filters on — so `changeset publish` and the pkg.pr.new canary
21
+ both skip this package until that flag comes off. Without it `0.0.0` (a version absent from npm) would
22
+ be published on the next release run, shipping a reduced surface and violating the repo's
23
+ `<adapters_reach_full_feature_parity>` invariant. Remove the flag in the commit that finishes parity.
24
+
25
+ ## Why Solid needs no DOM shim and no Metro transformer
26
+
27
+ Solid ships an official custom-renderer API, `createRenderer` from `solid-js/universal`. That is
28
+ exactly the framework-agnostic seam this project looks for in every framework, so the adapter is
29
+ `RendererOptions`' ten methods mapped onto the engine's mutation API — no shimming of private
30
+ internals (the Svelte adapter's approach, forced by an unmerged upstream PR), no compiler running
31
+ beside Metro (Angular's AOT), and no custom `babelTransformerPath` at all.
32
+
33
+ Targets **Solid 1.9**, deliberately not 2.0 (RC as of 2026-08): 2.0 moved the package to
34
+ `@solidjs/universal`, changed `RendererOptions` (added `createSentinel`/`cleanupNodes`, gave
35
+ `createElement` a staticProps argument) and changed the compiled-output shape (`use()` →
36
+ `applyRef`/`ref`, `effect(fn, init)` → `effect(fn, effectFn)`). Moving to it is a rewrite of
37
+ `src/renderer.ts`, not a range bump.
38
+
39
+ ## Wiring an app
40
+
41
+ `babel.config.js` — the preset ships preconfigured, so the app can't get `moduleName` or `generate`
42
+ wrong:
43
+
44
+ ```js
45
+ module.exports = {
46
+ presets: [
47
+ 'module:@react-native/babel-preset',
48
+ // LAST on purpose: Babel applies presets in reverse order, so this runs FIRST and claims the
49
+ // JSX before the React Native preset's own React-JSX transform can.
50
+ '@symbiote-native/solid/babel-preset',
51
+ ],
52
+ };
53
+ ```
54
+
55
+ `metro.config.js` needs `unstable_conditionNames: ['browser']` — `solid-js`'s export map has a `node`
56
+ branch pointing at its SSR build, and resolving that into a native bundle is the same failure mode
57
+ the Svelte example hit (`lifecycle_function_unavailable` on the first mount).
58
+
59
+ Solid compiles a component to `function App(props)` — a function with an uppercase name, which is
60
+ exactly what react-refresh's `isLikelyComponentType` heuristic looks for. It then tries to patch a
61
+ React Fiber tree that does not exist in this path, and the update is swallowed with no error. Force a
62
+ full reload instead: `unstable_forceFullRefreshPatterns: [/\.tsx$/]`.
63
+
64
+ ## `./renderer` is a compiler target, not a convenience export
65
+
66
+ `babel-preset-solid` with `generate: 'universal'` rewrites JSX into direct calls imported from the
67
+ `moduleName` it was given. So `src/renderer.ts` exports eleven specific names because generated code
68
+ imports them; dropping one breaks bundling with a module-not-found on an import nobody wrote. The
69
+ list (`createElement`, `createTextNode`, `insertNode`, `insert`, `setProp`, `use`, `effect`, `memo`,
70
+ `createComponent`, `spread`, `mergeProps`) was verified by compiling representative JSX, not read off
71
+ the docs.
72
+
73
+ ## Three things the universal runtime does that the seam has to answer correctly
74
+
75
+ Each is a one-line decision in `src/renderer.ts` with a real failure behind it — the comments there
76
+ cite the runtime line that forces them.
77
+
78
+ 1. **`createTextNode('')` is a placeholder, not content.** The runtime parks an empty text node where
79
+ a dynamic expression will go. An empty `RCTRawText` genuinely paints in Fabric, so an empty string
80
+ maps to an engine anchor instead (skipped by the commit walk).
81
+ 2. **`isTextNode` must answer "can I write a string into this", not "did `createTextNode` make it".**
82
+ The runtime asks it about that placeholder anchor; answering `true` sends it to write text into a
83
+ node that never reaches Fabric.
84
+ 3. **Anchors stay visible to `getFirstChild`/`getNextSibling`.** The runtime re-derives positions
85
+ through those lookups, so hiding a node it inserted itself desyncs its bookkeeping from the tree.
86
+ Anchors are invisible to Fabric, not to traversal.
87
+
88
+ ## Typing — the app points `jsxImportSource` here, and that is the whole setup
89
+
90
+ ```jsonc
91
+ // tsconfig.json
92
+ "jsx": "preserve",
93
+ "jsxImportSource": "@symbiote-native/solid"
94
+ ```
95
+
96
+ That is all an app needs. No `/// <reference types>` line, no `solid-env.d.ts`, no per-file pragma:
97
+ TypeScript resolves the entire JSX namespace from this package's `./jsx-runtime` entry, which is
98
+ type-only (the emitted JS is `export {};` and nothing ever imports it — JSX becomes calls on
99
+ `./renderer` through babel-preset-solid's `moduleName`, a separate mechanism).
100
+
101
+ Two things follow that the earlier `declare module 'solid-js'` shim could not give:
102
+
103
+ - **A web tag is a compile error.** `<div>` reports `TS2339: Property 'div' does not exist on type
104
+ 'JSX.IntrinsicElements'` instead of building fine and failing on device. Augmenting solid-js's
105
+ namespace merges into its HTML/SVG list, so `<div>` stayed legal there. The Svelte adapter needed
106
+ a whole preprocessor for the same guarantee.
107
+ - **JSX children are type-checked at all.** solid-js's `JSX.Element` names the DOM's `Node`, which
108
+ does not resolve without a DOM lib — and `skipLibCheck` swallows that error rather than reporting
109
+ it, leaving `Element` as `any` and every JSX child position unchecked. The visible symptom was
110
+ small (a render-prop parameter needed an explicit annotation); the cause was not.
111
+
112
+ The risk recorded when this was deferred — that our `Element` must line up with what solid-js's own
113
+ `<Show>`/`<For>` return — resolves for the same reason: solid's is `any` in the DOM-less programs we
114
+ target, so it is assignable to anything of ours. `src/jsx-runtime.test.tsx` pins the runtime half;
115
+ the type half rides on `examples/solid`'s typecheck. Full rationale, the measurements behind each
116
+ claim, and the one config that would break it: `.claude/rules/solid-jsx-namespace.md`.
117
+
118
+ ## Single root per process
119
+
120
+ Compiled JSX calls module-level functions with no surface argument, so there is nowhere to thread a
121
+ per-surface renderer through — the active surface is module state, set by `mount`. The Svelte adapter
122
+ reached the same single-root conclusion for its own reasons.
123
+
124
+ ## Reference
125
+
126
+ - The canary app: `examples/solid` (its README covers the tarball loop and what is verified).
127
+ - The engine API this targets: the `symbiote-engine-core` skill, `core/engine/src/node.ts`.
128
+ - Building/porting an adapter: the `symbiote-new-adapter` skill.
129
+ - The nodeOps table this mirrors: `adapters/vue/src/renderer/index.ts`.
130
+ - The same seam on an ANSI target: `wolf-tui/packages/solid/src/renderer/node-ops.ts`.
@@ -0,0 +1,41 @@
1
+ // The Babel preset a consuming app puts in its babel.config.js:
2
+ //
3
+ // presets: ['module:@react-native/babel-preset', '@symbiote-native/solid/babel-preset']
4
+ //
5
+ // It is babel-preset-solid with the two options that MUST be right pre-filled, so an app can
6
+ // never get them wrong:
7
+ //
8
+ // generate: 'universal' — emit calls into a custom renderer instead of DOM operations.
9
+ // moduleName — where those calls are imported FROM. Compiled JSX turns into
10
+ // `import { createElement } from '<moduleName>'`, so this string has
11
+ // to name a real, resolvable module exporting the 11 functions
12
+ // createRenderer() returns (src/renderer.ts, published as the
13
+ // ./renderer subpath). A typo here fails at bundle time with a
14
+ // module-not-found on a path nobody wrote by hand.
15
+ //
16
+ // Same reasoning as ./metro-css-parser.cjs: shipping the wiring from the adapter means the app
17
+ // declares no extra devDependency (babel-preset-solid is OUR dependency, and require() resolves
18
+ // relative to this file, not via the app's node_modules).
19
+ //
20
+ // .cjs, not .js: this package is "type": "module", and Babel require()s a preset.
21
+ //
22
+ // Preset ORDER in the app matters and is not arbitrary. Babel applies presets in REVERSE array
23
+ // order, so listing this one LAST runs it FIRST — before @react-native/babel-preset's own
24
+ // React-JSX transform gets a chance to claim the same JSX nodes.
25
+ module.exports = function symbioteSolidBabelPreset(_api, options = {}) {
26
+ return {
27
+ presets: [
28
+ [
29
+ require('babel-preset-solid'),
30
+ {
31
+ ...options,
32
+ // Last, deliberately: an app may pass other babel-preset-solid options through, but
33
+ // not these two — overriding either one silently produces output this adapter cannot
34
+ // satisfy (DOM operations, or imports from a module that exports nothing we provide).
35
+ generate: 'universal',
36
+ moduleName: '@symbiote-native/solid/renderer',
37
+ },
38
+ ],
39
+ ],
40
+ };
41
+ };
@@ -0,0 +1,8 @@
1
+ // Re-exported from THIS file's own location so a consuming app's
2
+ // require('@symbiote-native/solid/metro-css-parser') resolves css-parser as a dependency of THIS
3
+ // package, not the app's node_modules (Node resolves require() relative to the requiring file,
4
+ // not via pnpm hoisting) — the app never needs to declare @symbiote-native/css-parser itself.
5
+ // .cjs, not .js: this package is "type": "module", but Metro's babelTransformerPath loading needs
6
+ // a require()-able file. createCssMetroTransformer() is a factory — Metro needs the actual
7
+ // {transform, getCacheKey} object it returns, not the css-parser package barrel.
8
+ module.exports = require('@symbiote-native/css-parser').createCssMetroTransformer();
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@symbiote-native/solid",
3
+ "version": "0.0.1",
4
+ "description": "SymbioteNative's Solid adapter — a solid-js/universal createRenderer driving real native iOS/Android views through the same engine as every other adapter.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/OneEyed1366/symbiote-native.git",
9
+ "directory": "adapters/solid"
10
+ },
11
+ "homepage": "https://github.com/OneEyed1366/symbiote-native/tree/master/adapters/solid#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/OneEyed1366/symbiote-native/issues"
14
+ },
15
+ "author": "Andrey Prokopenko <psevdoproger@gmail.com>",
16
+ "type": "module",
17
+ "main": "./build/index.js",
18
+ "module": "./build/index.js",
19
+ "types": "./build/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./build/index.d.ts",
23
+ "default": "./build/index.js"
24
+ },
25
+ "./bootstrap": {
26
+ "types": "./build/bootstrap.d.ts",
27
+ "default": "./build/bootstrap.js"
28
+ },
29
+ "./renderer": {
30
+ "types": "./build/renderer.d.ts",
31
+ "default": "./build/renderer.js"
32
+ },
33
+ "./jsx-runtime": {
34
+ "types": "./build/jsx-runtime.d.ts",
35
+ "default": "./build/jsx-runtime.js"
36
+ },
37
+ "./jsx-dev-runtime": {
38
+ "types": "./build/jsx-runtime.d.ts",
39
+ "default": "./build/jsx-runtime.js"
40
+ },
41
+ "./babel-preset": "./babel-preset.cjs",
42
+ "./metro-css-parser": "./metro-css-parser.cjs"
43
+ },
44
+ "files": [
45
+ "build",
46
+ "babel-preset.cjs",
47
+ "metro-css-parser.cjs"
48
+ ],
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
52
+ "dependencies": {
53
+ "babel-preset-solid": "^1.9.12",
54
+ "typescript": "~6.0.0",
55
+ "@symbiote-native/css-parser": "0.3.0",
56
+ "@symbiote-native/components": "0.4.0"
57
+ },
58
+ "peerDependencies": {
59
+ "@symbiote-native/engine": ">=0.1.7",
60
+ "solid-js": ">=1.9.0",
61
+ "react-native": ">=0.86"
62
+ },
63
+ "devDependencies": {
64
+ "solid-js": "^1.9.14",
65
+ "@symbiote-native/engine": "0.2.0",
66
+ "@symbiote-native/test-utils": "0.1.6"
67
+ },
68
+ "scripts": {
69
+ "typecheck": "tsc --build",
70
+ "format": "prettier --write \"src/**/*.{ts,tsx}\""
71
+ }
72
+ }