Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

solid-nativescript-js

Zero-overhead SolidJS ↔ NativeScript bridge. JSX tags written in their original PascalCase (<StackLayout>, <ActionBar>, <Label>, ...) are matched 1:1 to their @nativescript/core class constructors — no document, no dominative, no undom-ng, and the tag string is never lowercased anywhere on the path.

import { run, Frame, Page, ActionBar, StackLayout, Label, Button, createSignal } from 'solid-nativescript-js'

const Home = () => {
  const [count, setCount] = createSignal(0)
  return (
    <>
      <ActionBar title="App" />
      <StackLayout>
        <Label text="Clean Code" />
        <Button text="Tap me" onTap={() => setCount(c => c + 1)} />
      </StackLayout>
    </>
  )
}

run(() => (
  <Frame>
    <Page><Home /></Page>
  </Frame>
))

Why this package exists

The legacy @nativescript-community/solid-js stack renders through dominative + undom-ng, a full web-DOM emulation layer on top of NativeScript. This package replaces that stack with a lean universal reconciler: SolidJS's own createRenderer (from solid-js/universal) drives diffing and effect-based updates; this package supplies native operations that talk directly to @nativescript/core views.

Architecture

Module Responsibility
src/registry.ts PascalCase tag → core constructor map (+ registerElement for plugin views).
src/renderer.ts createRenderer wiring: createElement/insert/remove/setText on native views.
src/props.ts Attribute binding: splits onTap/on:tap events from props, class, style, ref, android:/ios: prefixes.
src/nodes.ts Per-container-kind child insertion (layouts, content slots, ActionBar, FormattedString, TabView, Frame navigation) + recursive destroyView teardown.
src/listeners.ts Strict event-lifecycle tracking: every .on() the bridge registers is tracked so teardown can always unbind it. Leak audits via activeListenerCount() / getGlobalListenerReport().
src/component.ts+ src/elements.ts Typed components for every built-in view. TypeScript resolves UPPERCASE JSX tags as values, so each view is exported as a component that instantiates the native class via the registry. Generated from the nativescript-dom/types workspace (bun run generate:types).
src/app.ts run() / createAppRoot(): root context, view-tree instantiation, anchoring into Application.run, full disposal.
src/vite.ts solidNativeScript() Vite plugin: universal-mode Solid compiler (moduleName: 'solid-nativescript-js') + solid-js aliasing (client dists only; solid-js/web rerouted to this bridge).

Memory management

NativeScript bridges V8/JSC to Java/Objective-C, so orphaned closures leak across runtimes. The bridge therefore:

  • tracks every event listener it binds (listeners.ts), with optional bind/unbind logging via setListenerLogging(true);
  • removes a node ⇒ recursively destroys it: unbinds all tracked listeners, detaches/destroys children, nulls bridge back-references, empties container slots and calls disposeNativeView() so platform GC can reclaim memory;
  • disposes the Solid root and tears down the whole tree via the disposer returned by render()/createAppRoot() (and disposeCurrentApp()).

Usage

Install

npm i solid-nativescript-js solid-js @nativescript/core
npm i -D @nativescript/vite vite vite-plugin-solid \
  @nativescript-dom/core-types @nativescript-dom/solidjs-types

vite.config.mts

import { defineConfig, mergeConfig } from 'vite'
import { baseConfig } from '@nativescript/vite/base'
import { solidNativeScript } from 'solid-nativescript-js/vite'

export default defineConfig(({ mode }) =>
  mergeConfig(baseConfig({ mode, flavor: 'solid' }), {
    plugins: [solidNativeScript({ mode })],
  })
)

nativescript.config.ts

export default {
  // ...
  bundler: 'vite',
  bundlerConfigPath: 'vite.config.mts',
} as NativeScriptConfig

Typings

Set the JSX type source to the official nativescript-dom typings and import your elements from this package (TypeScript resolves uppercase JSX tags as component values):

// tsconfig.json
{
  "compilerOptions": {
    "jsx": "preserve",
    "jsxImportSource": "@nativescript-dom/solidjs-types",
    "types": ["@nativescript-dom/core-types", "@nativescript-dom/solidjs-types"]
  }
}

Events

  • onTap={fn}view.on('tap', fn) (the char after on is lowercased: onTextChangetextChange).
  • on:tap={fn} binds the raw event name verbatim.
  • Reassignment unbinds the previous handler; node removal unbinds everything.

Plugin views

import { registerElement } from 'solid-nativescript-js'
import { RadSideDrawer } from '@nativescript-community/ui-drawer'
registerElement('RadSideDrawer', RadSideDrawer, 'content')

Development

bun install        # deps
bun test           # unit/integration suite (bun, mocked @nativescript/core, real solid-js)
bun run typecheck  # strict TS
bun run build      # emit dist/ (ESM + d.ts)
bun run generate:types  # regenerate src/elements.ts from the nativescript-dom/types workspace

Tests mock @nativescript/core but run the real solid-js universal renderer, so reconciliation, moves, keyed lists and disposal are covered end-to-end.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages