Skip to content

feat: NativeScript support - #11

Open
NathanWalker wants to merge 8 commits into
aleqsio:mainfrom
nstudio:feat/nativescript
Open

NathanWalker wants to merge 8 commits into
aleqsio:mainfrom
nstudio:feat/nativescript

Conversation

@NathanWalker

Copy link
Copy Markdown

NativeScript support: Core, Octane, React, Vue, Svelte, Solid and Angular apps

screenmap now maps NativeScript apps, whichever framework drives the views, and runs them through the same capture, pack, diff and viewer pipeline as an Expo app. Five commits, each self-contained:

  1. Read NativeScript apps — a nativescript route provider with one flavour per framework.
    • Core: <Page> XML files and Frame.navigate({ moduleName }) / showModal().
    • Octane, React, Vue, Svelte, Solid: no route table, so a screen is a component the framework mounts as a root, pushes, presents as a modal, hosts in a <frame> tab, or registers in a solid-navigation <Route> table. One shared engine, one row of spellings per flavour.
    • No flavour has a linking config: the app registers a URL scheme in App_Resources and maps URLs onto navigation in its own code. Every route starts navigation-only and routes.links in .screenmap/config.json names the ones a deep link opens. Scheme, app id and name are read from nativescript.config.ts, Info.plist (xcconfig variables resolved) and AndroidManifest.xml.
    • Angular: follows provideNativeScriptRouter(routes) into every loadChildren module; reads named outlets, redirectTo aliases, enum-valued paths, barrel re-exports; a route with children is a layout (tabs when its children sit in several named outlets); edges from navigate([...]), navigateByUrl() and nsRouterLink, with relativeTo honoured.
  2. Run a NativeScript build — the JS ships inside the .app/.apk, so the session installs and launches it and that is the whole boot. Builds are found under platforms/. An app with no URL scheme has its root captured by launching. A ns debug build under @nativescript/vite (a stub that imports core from the dev server) is refused with the dev-server origin and the ns build command, instead of silently capturing the simulator home screen.
  3. Viewer fixflowResolution built an open_url matcher for every node and split a null urlPath; any react-navigation map with a screen outside its linking config would have crashed the same way.

Validated against real apps

App Framework Static parse Runtime (iPhone 17 Pro simulator)
ns-octane Octane, Vite root + settings sheet (modal), drawer hint root captured by launch (no URL scheme)
ns-apple-music/ns-angular Angular, Vite 6 routes, listen-now as the landing / root captured by launch
ns-apple-music/ns-vue Vue, Vite 9 routes: Home + 5 tab screens + album + 2 modals root captured by launch
ns-apple-music/ns-solid Solid, Vite 8 routes, <Route> table, router shell read as a layout app halts in a plain ns build (app-side, see below)
HeyKiddo Talk Angular, webpack 54 routes, 9 layouts, 61 edges, 1 unresolved (a commented-out route) 20 of 20 deep-linked screens captured, logged in

Detection scores 0.95 on all five with nothing else above 0.10. Nine fixtures pin the graphs (node fixtures/run-tests.mjs).

Each image below is the map viewer showing the packed .scrmap from one run (screenmap-ci shot), deterministic lane only, no agent: captured screens are real screenshots, the rest are navigation-only and wait for flows or the agent.

Octane — the chat root and the settings sheet it presents as a second Octane root.

ns-octane

Angular — five named-outlet tabs read as Tabs, the album screen reached from four of them.

ns-angular

Vue — the same app in Vue: tab screens hosted in <Frame>s under Home, $navigateTo and $showModal edges.

ns-vue

Solidsolid-navigation's <Route> table and navigate('Album') by name. The black root is the app itself: a plain ns build ios of it halts at launch with Solid's REACTIVITY_HALTED thrown from solid-navigation, so it only runs under its HMR session.

ns-solid

HeyKiddo Talk (Angular) — 54 screens, 20 reached by deep link through the app's own com.heykiddo.talk:// handler.

heykiddo

For reviewers

  • CI never builds, so a NativeScript project passes a prebuilt .app/.apk as app_path; the EAS lane and expo_token do not apply.
  • routes.provider pins the provider when detection is wrong; routes.links is the deep-link overlay. Both are in the config table in the README.
  • No existing fixture snapshot changed; the expo-router and react-navigation graphs are unaffected; purely additive feature

A `nativescript` route provider, one module with a flavour per framework.
Angular follows provideNativeScriptRouter(routes) into every loadChildren
module and reads named outlets, redirectTo aliases, enum-valued paths and
barrel re-exports; a route with children is a layout (tabs when its
children sit in several named outlets), and edges come from navigate([]),
navigateByUrl() and nsRouterLink with relativeTo honoured. Core reads
<Page> XML files and Frame.navigate({ moduleName }). Octane, React, Vue,
Svelte and Solid have no route table, so a screen is a component the
framework mounts, pushes, presents, hosts in a <frame> tab, or registers
in a solid-navigation <Route> table; each flavour is one row of spellings
over a shared engine.

No flavour has a linking config: the app registers a URL scheme in
App_Resources and maps URLs onto navigation in its own code. Every route
therefore starts navigation-only and .screenmap/config.json's routes.links
names the ones a link opens. The scheme, app id and name come from
nativescript.config.ts, Info.plist (xcconfig variables resolved) and
AndroidManifest.xml, and a provider can now replace the graph's
deepLinkTemplates, since Expo Go means nothing here.

Validated against five real apps (a 54-screen Angular app, an Octane chat
app, and Angular, Vue and Solid Apple Music clones); six fixtures pin the
graphs. The test runner keeps a fixture's committed .screenmap/config.json.
…stub

A NativeScript app ships its JavaScript inside the .app or .apk, so the
session installs and launches the build and that is the whole boot: no
Metro, no dev-client nudging, no dev-menu muting. The runtime is detected
from nativescript.config.* and builds are found under platforms/. An app
that registers no URL scheme no longer fails the run: its root is captured
by launching, and only the rest waits for flows or the agent.

Under @nativescript/vite, `ns debug` writes a stub bundle whose
@nativescript/core imports are http://<host>:5173/ns/core URLs; headless it
dies on the first import and every capture is the simulator home screen,
which is what the first Octane run produced. The session now reads
app/bundle.mjs and fails with the dev-server origin and the `ns build`
command instead.
The skill gets the NativeScript boot steps (ns build, not ns debug), the
routes.links overlay, the no-scheme case, and the drawer, native-menu and
ns-modal hints. The README gains a NativeScript section and the config
table the runtime, routes.provider and routes.links keys; the provider
guide documents the flavour table and the frame-hosting and router-shell
rules. TODOS records what the first pass verified and what it did not.
flowResolution built an open_url matcher for every node, and a navigation-only
screen has no urlPath to split, so the Graph component threw on the first
NativeScript bundle and would have on any react-navigation map with a screen
outside its linking config. Such a screen has no URL an open_url step could
land on, so it simply gets no matcher.
Chrome 152 refuses an https page a fetch to http://localhost unless the
user grants local network access, so the hosted viewer never received the
bundle and the shot timed out on its readiness flag. The one headless
browser the renderer launches now has that check switched off.
?shot=captured drops every screen without a good capture before laying the
map out, so an image of a map the deterministic lane reached only part of
shows the screens it has instead of a wall of placeholders; `screenmap-ci
shot --mode captured` asks for it.

findBuiltApp preferred the Debug directory, which on a project holding both
a Debug and a Release build of the same app meant the older one. The build
most recently written wins now, on both platforms — a Solid app whose debug
build halts on a dev-only reactivity assertion renders from `ns build ios
--release`, and that is the build meant.
Twenty-two hand-recorded argent flows replayed with every landmark check
passing across the Octane, Vue, Angular and Solid apps, and Solid renders
from a release build; TODOS moves both out of the unverified list.
The fixture depended on svelte-native, the package's old name; the
maintained one is @nativescript-community/svelte-native. Both name the
Svelte flavour now, since older apps still carry the old dependency.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant