From 9fbc8cfe012b5632aa7a8c9c54be202563fa6745 Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Tue, 15 Sep 2026 17:09:02 -0700 Subject: [PATCH 1/8] Read NativeScript apps: Angular routes, Core pages, and component roots 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 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 tab, or registers in a solid-navigation 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. --- .../.screenmap/config.json | 10 + .../Android/src/main/AndroidManifest.xml | 18 + .../App_Resources/iOS/Info.plist | 24 + .../App_Resources/iOS/build.xcconfig | 4 + .../ns-angular-demo-app/expected-graph.json | 216 ++++ .../nativescript.config.ts | 8 + fixtures/ns-angular-demo-app/package.json | 16 + .../src/app/app.component.ts | 10 + .../ns-angular-demo-app/src/app/app.routes.ts | 37 + .../src/app/common/auth.guard.ts | 8 + .../src/app/common/screens.enum.ts | 6 + .../src/app/compose/compose.component.ts | 16 + .../src/app/details/details.component.html | 6 + .../src/app/details/details.component.ts | 30 + .../src/app/details/index.ts | 1 + .../src/app/home/feed/feed-item.component.ts | 22 + .../src/app/home/feed/feed.component.html | 8 + .../src/app/home/feed/feed.component.ts | 18 + .../src/app/home/home.component.html | 12 + .../src/app/home/home.component.ts | 10 + .../src/app/home/home.routes.ts | 27 + .../home/search/filters-sheet.component.ts | 10 + .../src/app/home/search/search.component.html | 5 + .../src/app/home/search/search.component.ts | 23 + .../src/app/login/login.component.html | 5 + .../src/app/login/login.component.ts | 16 + .../src/app/not-found/not-found.component.ts | 10 + .../src/app/settings/settings.component.html | 5 + .../src/app/settings/settings.component.ts | 24 + .../src/app/share/share-dialog.component.ts | 10 + fixtures/ns-angular-demo-app/src/main.ts | 10 + fixtures/ns-angular-demo-app/tsconfig.json | 12 + .../App_Resources/iOS/Info.plist | 17 + fixtures/ns-core-demo-app/app/app.ts | 3 + fixtures/ns-core-demo-app/app/main-page.ts | 17 + fixtures/ns-core-demo-app/app/main-page.xml | 7 + .../ns-core-demo-app/app/main-view-model.ts | 5 + .../app/pages/details/details-page.ts | 5 + .../app/pages/details/details-page.xml | 7 + .../app/pages/filters/filters-modal.ts | 7 + .../app/pages/filters/filters-modal.xml | 6 + .../app/pages/settings/settings-page.ts | 5 + .../app/pages/settings/settings-page.xml | 7 + fixtures/ns-core-demo-app/expected-graph.json | 107 ++ .../ns-core-demo-app/nativescript.config.ts | 7 + fixtures/ns-core-demo-app/package.json | 12 + .../App_Resources/iOS/Info.plist | 8 + .../ns-octane-demo-app/expected-graph.json | 75 ++ .../ns-octane-demo-app/nativescript.config.ts | 9 + fixtures/ns-octane-demo-app/package.json | 16 + fixtures/ns-octane-demo-app/src/app.tsx | 27 + .../src/components/chat-screen.tsx | 26 + .../src/components/drawer.tsx | 16 + .../src/components/settings.tsx | 26 + fixtures/ns-octane-demo-app/src/elements.ts | 4 + fixtures/ns-octane-demo-app/src/index.ts | 13 + .../App_Resources/iOS/Info.plist | 8 + .../ns-react-demo-app/expected-graph.json | 46 + .../ns-react-demo-app/nativescript.config.ts | 7 + fixtures/ns-react-demo-app/package.json | 14 + fixtures/ns-react-demo-app/src/app.ts | 5 + .../src/components/AppContainer.tsx | 12 + .../src/components/HomeScreen.tsx | 17 + .../App_Resources/iOS/Info.plist | 8 + .../ns-solid-demo-app/expected-graph.json | 132 +++ .../ns-solid-demo-app/nativescript.config.ts | 9 + fixtures/ns-solid-demo-app/package.json | 16 + fixtures/ns-solid-demo-app/src/app.tsx | 14 + .../ns-solid-demo-app/src/components/home.tsx | 15 + .../src/components/screens/album-detail.tsx | 12 + .../src/components/screens/browse.tsx | 14 + .../src/components/screens/listen-now.tsx | 16 + .../src/components/screens/now-playing.tsx | 23 + fixtures/ns-solid-demo-app/src/index.ts | 14 + .../src/utils/album-navigation.ts | 7 + .../App_Resources/iOS/Info.plist | 8 + fixtures/ns-svelte-demo-app/app/App.svelte | 20 + fixtures/ns-svelte-demo-app/app/app.ts | 4 + .../app/pages/Details.svelte | 17 + .../app/pages/Filters.svelte | 10 + .../ns-svelte-demo-app/expected-graph.json | 90 ++ .../ns-svelte-demo-app/nativescript.config.ts | 7 + fixtures/ns-svelte-demo-app/package.json | 14 + .../App_Resources/iOS/Info.plist | 17 + fixtures/ns-vue-demo-app/app/app.ts | 4 + .../app/components/Details.vue | 16 + .../app/components/Filters.vue | 12 + .../ns-vue-demo-app/app/components/Home.vue | 31 + .../app/components/Settings.vue | 8 + fixtures/ns-vue-demo-app/expected-graph.json | 109 ++ .../ns-vue-demo-app/nativescript.config.ts | 7 + fixtures/ns-vue-demo-app/package.json | 13 + fixtures/run-tests.mjs | 11 +- .../screenmap/scripts/routes/lib/graph.mjs | 4 +- .../screenmap/scripts/routes/lib/project.mjs | 77 +- .../scripts/routes/providers/nativescript.mjs | 1025 +++++++++++++++++ .../screenmap/scripts/routes/registry.mjs | 3 +- 97 files changed, 2960 insertions(+), 5 deletions(-) create mode 100644 fixtures/ns-angular-demo-app/.screenmap/config.json create mode 100644 fixtures/ns-angular-demo-app/App_Resources/Android/src/main/AndroidManifest.xml create mode 100644 fixtures/ns-angular-demo-app/App_Resources/iOS/Info.plist create mode 100644 fixtures/ns-angular-demo-app/App_Resources/iOS/build.xcconfig create mode 100644 fixtures/ns-angular-demo-app/expected-graph.json create mode 100644 fixtures/ns-angular-demo-app/nativescript.config.ts create mode 100644 fixtures/ns-angular-demo-app/package.json create mode 100644 fixtures/ns-angular-demo-app/src/app/app.component.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/app.routes.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/common/auth.guard.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/common/screens.enum.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/compose/compose.component.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/details/details.component.html create mode 100644 fixtures/ns-angular-demo-app/src/app/details/details.component.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/details/index.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/home/feed/feed-item.component.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/home/feed/feed.component.html create mode 100644 fixtures/ns-angular-demo-app/src/app/home/feed/feed.component.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/home/home.component.html create mode 100644 fixtures/ns-angular-demo-app/src/app/home/home.component.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/home/home.routes.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/home/search/filters-sheet.component.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/home/search/search.component.html create mode 100644 fixtures/ns-angular-demo-app/src/app/home/search/search.component.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/login/login.component.html create mode 100644 fixtures/ns-angular-demo-app/src/app/login/login.component.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/not-found/not-found.component.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/settings/settings.component.html create mode 100644 fixtures/ns-angular-demo-app/src/app/settings/settings.component.ts create mode 100644 fixtures/ns-angular-demo-app/src/app/share/share-dialog.component.ts create mode 100644 fixtures/ns-angular-demo-app/src/main.ts create mode 100644 fixtures/ns-angular-demo-app/tsconfig.json create mode 100644 fixtures/ns-core-demo-app/App_Resources/iOS/Info.plist create mode 100644 fixtures/ns-core-demo-app/app/app.ts create mode 100644 fixtures/ns-core-demo-app/app/main-page.ts create mode 100644 fixtures/ns-core-demo-app/app/main-page.xml create mode 100644 fixtures/ns-core-demo-app/app/main-view-model.ts create mode 100644 fixtures/ns-core-demo-app/app/pages/details/details-page.ts create mode 100644 fixtures/ns-core-demo-app/app/pages/details/details-page.xml create mode 100644 fixtures/ns-core-demo-app/app/pages/filters/filters-modal.ts create mode 100644 fixtures/ns-core-demo-app/app/pages/filters/filters-modal.xml create mode 100644 fixtures/ns-core-demo-app/app/pages/settings/settings-page.ts create mode 100644 fixtures/ns-core-demo-app/app/pages/settings/settings-page.xml create mode 100644 fixtures/ns-core-demo-app/expected-graph.json create mode 100644 fixtures/ns-core-demo-app/nativescript.config.ts create mode 100644 fixtures/ns-core-demo-app/package.json create mode 100644 fixtures/ns-octane-demo-app/App_Resources/iOS/Info.plist create mode 100644 fixtures/ns-octane-demo-app/expected-graph.json create mode 100644 fixtures/ns-octane-demo-app/nativescript.config.ts create mode 100644 fixtures/ns-octane-demo-app/package.json create mode 100644 fixtures/ns-octane-demo-app/src/app.tsx create mode 100644 fixtures/ns-octane-demo-app/src/components/chat-screen.tsx create mode 100644 fixtures/ns-octane-demo-app/src/components/drawer.tsx create mode 100644 fixtures/ns-octane-demo-app/src/components/settings.tsx create mode 100644 fixtures/ns-octane-demo-app/src/elements.ts create mode 100644 fixtures/ns-octane-demo-app/src/index.ts create mode 100644 fixtures/ns-react-demo-app/App_Resources/iOS/Info.plist create mode 100644 fixtures/ns-react-demo-app/expected-graph.json create mode 100644 fixtures/ns-react-demo-app/nativescript.config.ts create mode 100644 fixtures/ns-react-demo-app/package.json create mode 100644 fixtures/ns-react-demo-app/src/app.ts create mode 100644 fixtures/ns-react-demo-app/src/components/AppContainer.tsx create mode 100644 fixtures/ns-react-demo-app/src/components/HomeScreen.tsx create mode 100644 fixtures/ns-solid-demo-app/App_Resources/iOS/Info.plist create mode 100644 fixtures/ns-solid-demo-app/expected-graph.json create mode 100644 fixtures/ns-solid-demo-app/nativescript.config.ts create mode 100644 fixtures/ns-solid-demo-app/package.json create mode 100644 fixtures/ns-solid-demo-app/src/app.tsx create mode 100644 fixtures/ns-solid-demo-app/src/components/home.tsx create mode 100644 fixtures/ns-solid-demo-app/src/components/screens/album-detail.tsx create mode 100644 fixtures/ns-solid-demo-app/src/components/screens/browse.tsx create mode 100644 fixtures/ns-solid-demo-app/src/components/screens/listen-now.tsx create mode 100644 fixtures/ns-solid-demo-app/src/components/screens/now-playing.tsx create mode 100644 fixtures/ns-solid-demo-app/src/index.ts create mode 100644 fixtures/ns-solid-demo-app/src/utils/album-navigation.ts create mode 100644 fixtures/ns-svelte-demo-app/App_Resources/iOS/Info.plist create mode 100644 fixtures/ns-svelte-demo-app/app/App.svelte create mode 100644 fixtures/ns-svelte-demo-app/app/app.ts create mode 100644 fixtures/ns-svelte-demo-app/app/pages/Details.svelte create mode 100644 fixtures/ns-svelte-demo-app/app/pages/Filters.svelte create mode 100644 fixtures/ns-svelte-demo-app/expected-graph.json create mode 100644 fixtures/ns-svelte-demo-app/nativescript.config.ts create mode 100644 fixtures/ns-svelte-demo-app/package.json create mode 100644 fixtures/ns-vue-demo-app/App_Resources/iOS/Info.plist create mode 100644 fixtures/ns-vue-demo-app/app/app.ts create mode 100644 fixtures/ns-vue-demo-app/app/components/Details.vue create mode 100644 fixtures/ns-vue-demo-app/app/components/Filters.vue create mode 100644 fixtures/ns-vue-demo-app/app/components/Home.vue create mode 100644 fixtures/ns-vue-demo-app/app/components/Settings.vue create mode 100644 fixtures/ns-vue-demo-app/expected-graph.json create mode 100644 fixtures/ns-vue-demo-app/nativescript.config.ts create mode 100644 fixtures/ns-vue-demo-app/package.json create mode 100644 plugins/screenmap/skills/screenmap/scripts/routes/providers/nativescript.mjs diff --git a/fixtures/ns-angular-demo-app/.screenmap/config.json b/fixtures/ns-angular-demo-app/.screenmap/config.json new file mode 100644 index 0000000..931d421 --- /dev/null +++ b/fixtures/ns-angular-demo-app/.screenmap/config.json @@ -0,0 +1,10 @@ +{ + "routes": { + "links": { + "home/feed": "feed", + "details/:id": "item/:id", + "settings": true, + "login": false + } + } +} diff --git a/fixtures/ns-angular-demo-app/App_Resources/Android/src/main/AndroidManifest.xml b/fixtures/ns-angular-demo-app/App_Resources/Android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..5139727 --- /dev/null +++ b/fixtures/ns-angular-demo-app/App_Resources/Android/src/main/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/fixtures/ns-angular-demo-app/App_Resources/iOS/Info.plist b/fixtures/ns-angular-demo-app/App_Resources/iOS/Info.plist new file mode 100644 index 0000000..31b6545 --- /dev/null +++ b/fixtures/ns-angular-demo-app/App_Resources/iOS/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDisplayName + ${BUNDLE_DISPLAY_NAME} + CFBundleName + ${PRODUCT_NAME} + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + com.googleusercontent.apps.123456-abcdef + ${BUNDLE_IDENTIFIER} + + + + UILaunchStoryboardName + LaunchScreen + + diff --git a/fixtures/ns-angular-demo-app/App_Resources/iOS/build.xcconfig b/fixtures/ns-angular-demo-app/App_Resources/iOS/build.xcconfig new file mode 100644 index 0000000..caee98c --- /dev/null +++ b/fixtures/ns-angular-demo-app/App_Resources/iOS/build.xcconfig @@ -0,0 +1,4 @@ +// Values the Info.plist refers to by name. +BUNDLE_DISPLAY_NAME = NS Demo; +BUNDLE_IDENTIFIER = org.screenmap.nsdemo; +IPHONEOS_DEPLOYMENT_TARGET = 17.0; diff --git a/fixtures/ns-angular-demo-app/expected-graph.json b/fixtures/ns-angular-demo-app/expected-graph.json new file mode 100644 index 0000000..b531885 --- /dev/null +++ b/fixtures/ns-angular-demo-app/expected-graph.json @@ -0,0 +1,216 @@ +{ + "appName": "NS Demo", + "scheme": "org.screenmap.nsdemo", + "deepLinkTemplates": { + "devBuild": "org.screenmap.nsdemo://" + }, + "mode": "nativescript", + "flavor": "angular", + "appPath": "src", + "routesFile": "src/app/app.routes.ts", + "screensResolved": 7, + "layouts": [ + { + "file": "src/app/app.routes.ts", + "dir": "", + "navigator": "Stack" + }, + { + "file": "src/app/home/home.component.ts", + "dir": "home", + "navigator": "Tabs" + } + ], + "routes": [ + { + "id": "login", + "file": "src/app/login/login.component.ts", + "urlPath": null, + "title": "/login", + "reach": "navigation-only", + "slug": "login", + "params": [], + "navigator": "Stack", + "layoutDir": "", + "presentation": null, + "stateHints": [] + }, + { + "id": "home/feed", + "file": "src/app/home/feed/feed.component.ts", + "urlPath": "/feed", + "title": "/home/(feedTab:feed)", + "reach": "deep-link", + "slug": "home_feed", + "params": [], + "navigator": "Tabs", + "layoutDir": "home", + "presentation": null, + "stateHints": [] + }, + { + "id": "home/search", + "file": "src/app/home/search/search.component.ts", + "urlPath": null, + "title": "/home/(searchTab:search)", + "reach": "navigation-only", + "slug": "home_search", + "params": [], + "navigator": "Tabs", + "layoutDir": "home", + "presentation": null, + "stateHints": [ + { + "type": "bottom-sheet", + "lib": "material-bottomsheet", + "snapPoints": null, + "component": "FiltersSheetComponent" + } + ] + }, + { + "id": "details/:id", + "file": "src/app/details/details.component.ts", + "urlPath": "/item/:id", + "title": "/details/:id", + "reach": "deep-link", + "slug": "details_id", + "params": [ + "id" + ], + "navigator": "Stack", + "layoutDir": "", + "presentation": null, + "stateHints": [ + { + "type": "ns-modal", + "component": "ShareDialogComponent" + } + ] + }, + { + "id": "settings", + "file": "src/app/settings/settings.component.ts", + "urlPath": "/settings", + "title": "/settings", + "reach": "deep-link", + "slug": "settings", + "params": [], + "navigator": "Stack", + "layoutDir": "", + "presentation": null, + "stateHints": [] + }, + { + "id": "compose", + "file": "src/app/compose/compose.component.ts", + "urlPath": null, + "title": "/compose", + "reach": "navigation-only", + "slug": "compose", + "params": [], + "navigator": "Stack", + "layoutDir": "", + "presentation": null, + "stateHints": [] + }, + { + "id": "**", + "file": "src/app/not-found/not-found.component.ts", + "urlPath": null, + "title": "/**", + "reach": "navigation-only", + "slug": "wildcard", + "params": [], + "navigator": "Stack", + "layoutDir": "", + "presentation": null, + "stateHints": [] + } + ], + "edges": [ + { + "from": "login", + "to": "home/feed", + "raw": "['/home', { outlets: { feedTab: ['feed'] } }]", + "target": "/home/feed" + }, + { + "from": "home/feed", + "to": "settings", + "raw": "'/settings'", + "target": "/settings" + }, + { + "from": "home/feed", + "to": "compose", + "raw": "['/compose']", + "target": "/compose" + }, + { + "from": "home/feed", + "to": "details/:id", + "raw": "['/details', item.id]", + "target": "/details/:id" + }, + { + "from": "home/search", + "to": "details/:id", + "raw": "['/details', id]", + "target": "/details/:id" + }, + { + "from": "details/:id", + "to": "details/:id", + "raw": "[`/details/${this.id + 1}`]", + "target": "/details/:id" + }, + { + "from": "details/:id", + "to": "settings", + "raw": "['settings']", + "target": "/settings" + }, + { + "from": "settings", + "to": "settings", + "raw": "['/account']", + "target": "/settings" + }, + { + "from": "settings", + "to": "login", + "raw": "'/login'", + "target": "/login" + }, + { + "from": "settings", + "to": "home/search", + "raw": "['/home', { outlets: { searchTab: ['search'] } }]", + "target": "/home/search" + }, + { + "from": "compose", + "to": "home/feed", + "raw": "['/home']", + "target": "/home/feed" + }, + { + "from": "**", + "to": "home/feed", + "raw": "/home", + "target": "/home/feed" + } + ], + "summary": { + "mode": "nativescript", + "routes": 7, + "layouts": 2, + "edges": 12, + "unresolvedEdges": 0, + "routesWithStateHints": 2, + "routesNeedingParams": 1, + "navigationOnlyRoutes": 4, + "orphanRoutes": 1 + } +} diff --git a/fixtures/ns-angular-demo-app/nativescript.config.ts b/fixtures/ns-angular-demo-app/nativescript.config.ts new file mode 100644 index 0000000..c4f4cc6 --- /dev/null +++ b/fixtures/ns-angular-demo-app/nativescript.config.ts @@ -0,0 +1,8 @@ +import { NativeScriptConfig } from '@nativescript/core'; + +export default { + id: 'org.screenmap.nsdemo', + appPath: 'src', + appResourcesPath: 'App_Resources', + ios: { discardUncaughtJsExceptions: false } +} as NativeScriptConfig; diff --git a/fixtures/ns-angular-demo-app/package.json b/fixtures/ns-angular-demo-app/package.json new file mode 100644 index 0000000..c957689 --- /dev/null +++ b/fixtures/ns-angular-demo-app/package.json @@ -0,0 +1,16 @@ +{ + "name": "ns-angular-demo", + "main": "./src/main.ts", + "private": true, + "dependencies": { + "@angular/core": "~22.0.0", + "@angular/router": "~22.0.0", + "@nativescript/angular": "~22.0.0", + "@nativescript/core": "~9.1.0", + "@nativescript-community/ui-material-bottomsheet": "~7.2.0" + }, + "devDependencies": { + "@nativescript/ios": "~9.1.0", + "typescript": "~5.9.0" + } +} diff --git a/fixtures/ns-angular-demo-app/src/app/app.component.ts b/fixtures/ns-angular-demo-app/src/app/app.component.ts new file mode 100644 index 0000000..3a3ab11 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/app.component.ts @@ -0,0 +1,10 @@ +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; +import { PageRouterOutlet } from '@nativescript/angular'; + +@Component({ + selector: 'ns-app', + template: '', + imports: [PageRouterOutlet], + schemas: [NO_ERRORS_SCHEMA] +}) +export class AppComponent {} diff --git a/fixtures/ns-angular-demo-app/src/app/app.routes.ts b/fixtures/ns-angular-demo-app/src/app/app.routes.ts new file mode 100644 index 0000000..3421660 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/app.routes.ts @@ -0,0 +1,37 @@ +import { inject } from '@angular/core'; +import { Routes } from '@angular/router'; +import { DetailsComponent } from './details'; +import { SettingsComponent } from '~/app/settings/settings.component'; +import { Screens, SETTINGS_PATH } from './common/screens.enum'; +import { AuthGuard } from './common/auth.guard'; + +export const routes: Routes = [ + { + path: 'login', + loadComponent: () => import('./login/login.component').then(m => m.LoginComponent) + }, + { + path: 'home', + loadChildren: () => import('./home/home.routes').then(m => m.routes), + canActivate: [() => inject(AuthGuard).canActivate()] + }, + // Detail screens take the item id in the URL. + { + path: 'details/:id', + component: DetailsComponent + }, + { path: SETTINGS_PATH, component: SettingsComponent }, + { + path: 'account', + redirectTo: 'settings', + pathMatch: 'full' + }, + { + path: Screens.Compose, + loadComponent: () => import('./compose/compose.component').then((m) => m.ComposeComponent) + }, + { + path: '**', + loadComponent: () => import('./not-found/not-found.component').then(m => m.NotFoundComponent) + } +]; diff --git a/fixtures/ns-angular-demo-app/src/app/common/auth.guard.ts b/fixtures/ns-angular-demo-app/src/app/common/auth.guard.ts new file mode 100644 index 0000000..a9e42ca --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/common/auth.guard.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ providedIn: 'root' }) +export class AuthGuard { + canActivate(): boolean { + return true; + } +} diff --git a/fixtures/ns-angular-demo-app/src/app/common/screens.enum.ts b/fixtures/ns-angular-demo-app/src/app/common/screens.enum.ts new file mode 100644 index 0000000..ccc6cc1 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/common/screens.enum.ts @@ -0,0 +1,6 @@ +export enum Screens { + Search = 'search', + Compose = 'compose' +} + +export const SETTINGS_PATH = 'settings'; diff --git a/fixtures/ns-angular-demo-app/src/app/compose/compose.component.ts b/fixtures/ns-angular-demo-app/src/app/compose/compose.component.ts new file mode 100644 index 0000000..4590766 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/compose/compose.component.ts @@ -0,0 +1,16 @@ +import { Component, NO_ERRORS_SCHEMA, inject } from '@angular/core'; +import { NativeScriptCommonModule, RouterExtensions } from '@nativescript/angular'; + +@Component({ + selector: 'ns-compose', + template: '', + imports: [NativeScriptCommonModule], + schemas: [NO_ERRORS_SCHEMA] +}) +export class ComposeComponent { + private router = inject(RouterExtensions); + + post() { + this.router.navigate(['/home'], { clearHistory: true }); + } +} diff --git a/fixtures/ns-angular-demo-app/src/app/details/details.component.html b/fixtures/ns-angular-demo-app/src/app/details/details.component.html new file mode 100644 index 0000000..3fc8c56 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/details/details.component.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/fixtures/ns-angular-demo-app/src/app/details/details.component.ts b/fixtures/ns-angular-demo-app/src/app/details/details.component.ts new file mode 100644 index 0000000..f39da76 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/details/details.component.ts @@ -0,0 +1,30 @@ +import { Component, NO_ERRORS_SCHEMA, inject } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { NativeScriptCommonModule, NativeScriptRouterModule, RouterExtensions } from '@nativescript/angular'; +import { NativeDialogService } from '@nativescript/angular'; +import { ShareDialogComponent } from '../share/share-dialog.component'; + +@Component({ + selector: 'ns-details', + templateUrl: './details.component.html', + imports: [NativeScriptCommonModule, NativeScriptRouterModule], + schemas: [NO_ERRORS_SCHEMA] +}) +export class DetailsComponent { + private router = inject(RouterExtensions); + private route = inject(ActivatedRoute); + private nativeDialog = inject(NativeDialogService); + id = Number(this.route.snapshot.params.id); + + share() { + this.nativeDialog.open(ShareDialogComponent, { data: { id: this.id } }); + } + + next() { + this.router.navigate([`/details/${this.id + 1}`]); + } + + settings() { + this.router.navigate(['settings'], { relativeTo: this.route.parent }); + } +} diff --git a/fixtures/ns-angular-demo-app/src/app/details/index.ts b/fixtures/ns-angular-demo-app/src/app/details/index.ts new file mode 100644 index 0000000..0788dcd --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/details/index.ts @@ -0,0 +1 @@ +export * from './details.component'; diff --git a/fixtures/ns-angular-demo-app/src/app/home/feed/feed-item.component.ts b/fixtures/ns-angular-demo-app/src/app/home/feed/feed-item.component.ts new file mode 100644 index 0000000..3cf0ca5 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/home/feed/feed-item.component.ts @@ -0,0 +1,22 @@ +import { Component, Input, NO_ERRORS_SCHEMA, inject } from '@angular/core'; +import { NativeScriptCommonModule, NativeScriptRouterModule, RouterExtensions } from '@nativescript/angular'; + +@Component({ + selector: 'ns-feed-item', + template: ` + + + + + `, + imports: [NativeScriptCommonModule, NativeScriptRouterModule], + schemas: [NO_ERRORS_SCHEMA] +}) +export class FeedItemComponent { + @Input() item: { id: number; title: string }; + private router = inject(RouterExtensions); + + reply() { + this.router.navigate(['/compose'], { queryParams: { replyTo: this.item.id } }); + } +} diff --git a/fixtures/ns-angular-demo-app/src/app/home/feed/feed.component.html b/fixtures/ns-angular-demo-app/src/app/home/feed/feed.component.html new file mode 100644 index 0000000..624feb6 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/home/feed/feed.component.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/fixtures/ns-angular-demo-app/src/app/home/feed/feed.component.ts b/fixtures/ns-angular-demo-app/src/app/home/feed/feed.component.ts new file mode 100644 index 0000000..d10e2e3 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/home/feed/feed.component.ts @@ -0,0 +1,18 @@ +import { Component, NO_ERRORS_SCHEMA, inject } from '@angular/core'; +import { NativeScriptCommonModule, NativeScriptRouterModule, RouterExtensions } from '@nativescript/angular'; +import { FeedItemComponent } from './feed-item.component'; + +@Component({ + selector: 'ns-feed', + templateUrl: './feed.component.html', + imports: [NativeScriptCommonModule, NativeScriptRouterModule, FeedItemComponent], + schemas: [NO_ERRORS_SCHEMA] +}) +export class FeedComponent { + private router = inject(RouterExtensions); + items = [{ id: 1, title: 'First' }, { id: 2, title: 'Second' }]; + + openSettings() { + this.router.router.navigateByUrl('/settings'); + } +} diff --git a/fixtures/ns-angular-demo-app/src/app/home/home.component.html b/fixtures/ns-angular-demo-app/src/app/home/home.component.html new file mode 100644 index 0000000..13d8986 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/home/home.component.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/fixtures/ns-angular-demo-app/src/app/home/home.component.ts b/fixtures/ns-angular-demo-app/src/app/home/home.component.ts new file mode 100644 index 0000000..11ac64c --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/home/home.component.ts @@ -0,0 +1,10 @@ +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; +import { PageRouterOutlet } from '@nativescript/angular'; + +@Component({ + selector: 'ns-home', + templateUrl: './home.component.html', + imports: [PageRouterOutlet], + schemas: [NO_ERRORS_SCHEMA] +}) +export class HomeComponent {} diff --git a/fixtures/ns-angular-demo-app/src/app/home/home.routes.ts b/fixtures/ns-angular-demo-app/src/app/home/home.routes.ts new file mode 100644 index 0000000..d15785b --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/home/home.routes.ts @@ -0,0 +1,27 @@ +import { Routes } from '@angular/router'; +import { HomeComponent } from './home.component'; +import { Screens } from '~/app/common/screens.enum'; + +export const routes: Routes = [ + { + path: '', + component: HomeComponent, + children: [ + { + path: '', + redirectTo: 'feed', + pathMatch: 'full' + }, + { + path: 'feed', + outlet: 'feedTab', + loadComponent: () => import('./feed/feed.component').then(m => m.FeedComponent) + }, + { + path: Screens.Search, + outlet: 'searchTab', + loadComponent: () => import('./search/search.component').then(m => m.SearchComponent) + } + ] + } +]; diff --git a/fixtures/ns-angular-demo-app/src/app/home/search/filters-sheet.component.ts b/fixtures/ns-angular-demo-app/src/app/home/search/filters-sheet.component.ts new file mode 100644 index 0000000..0150858 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/home/search/filters-sheet.component.ts @@ -0,0 +1,10 @@ +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; +import { NativeScriptCommonModule } from '@nativescript/angular'; + +@Component({ + selector: 'ns-filters-sheet', + template: '', + imports: [NativeScriptCommonModule], + schemas: [NO_ERRORS_SCHEMA] +}) +export class FiltersSheetComponent {} diff --git a/fixtures/ns-angular-demo-app/src/app/home/search/search.component.html b/fixtures/ns-angular-demo-app/src/app/home/search/search.component.html new file mode 100644 index 0000000..bcaeebd --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/home/search/search.component.html @@ -0,0 +1,5 @@ + + + + + diff --git a/fixtures/ns-angular-demo-app/src/app/home/search/search.component.ts b/fixtures/ns-angular-demo-app/src/app/home/search/search.component.ts new file mode 100644 index 0000000..7c8ce38 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/home/search/search.component.ts @@ -0,0 +1,23 @@ +import { Component, NO_ERRORS_SCHEMA, inject } from '@angular/core'; +import { NativeScriptCommonModule, RouterExtensions } from '@nativescript/angular'; +import { BottomSheetService } from '@nativescript-community/ui-material-bottomsheet/angular'; +import { FiltersSheetComponent } from './filters-sheet.component'; + +@Component({ + selector: 'ns-search', + templateUrl: './search.component.html', + imports: [NativeScriptCommonModule], + schemas: [NO_ERRORS_SCHEMA] +}) +export class SearchComponent { + private router = inject(RouterExtensions); + private bottomSheet = inject(BottomSheetService); + + openFilters() { + this.bottomSheet.show(FiltersSheetComponent, { dismissOnBackgroundTap: true }); + } + + openResult(id: number) { + this.router.navigate(['/details', id]); + } +} diff --git a/fixtures/ns-angular-demo-app/src/app/login/login.component.html b/fixtures/ns-angular-demo-app/src/app/login/login.component.html new file mode 100644 index 0000000..b913a32 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/login/login.component.html @@ -0,0 +1,5 @@ + + + + + diff --git a/fixtures/ns-angular-demo-app/src/app/login/login.component.ts b/fixtures/ns-angular-demo-app/src/app/login/login.component.ts new file mode 100644 index 0000000..396d8e0 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/login/login.component.ts @@ -0,0 +1,16 @@ +import { Component, NO_ERRORS_SCHEMA, inject } from '@angular/core'; +import { NativeScriptCommonModule, NativeScriptRouterModule, RouterExtensions } from '@nativescript/angular'; + +@Component({ + selector: 'ns-login', + templateUrl: './login.component.html', + imports: [NativeScriptCommonModule, NativeScriptRouterModule], + schemas: [NO_ERRORS_SCHEMA] +}) +export class LoginComponent { + private router = inject(RouterExtensions); + + signIn() { + this.router.navigate(['/home', { outlets: { feedTab: ['feed'] } }], { clearHistory: true, transition: { name: 'fade' } }); + } +} diff --git a/fixtures/ns-angular-demo-app/src/app/not-found/not-found.component.ts b/fixtures/ns-angular-demo-app/src/app/not-found/not-found.component.ts new file mode 100644 index 0000000..b8c4b60 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/not-found/not-found.component.ts @@ -0,0 +1,10 @@ +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; +import { NativeScriptCommonModule, NativeScriptRouterModule } from '@nativescript/angular'; + +@Component({ + selector: 'ns-not-found', + template: '', + imports: [NativeScriptCommonModule, NativeScriptRouterModule], + schemas: [NO_ERRORS_SCHEMA] +}) +export class NotFoundComponent {} diff --git a/fixtures/ns-angular-demo-app/src/app/settings/settings.component.html b/fixtures/ns-angular-demo-app/src/app/settings/settings.component.html new file mode 100644 index 0000000..86be7d6 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/settings/settings.component.html @@ -0,0 +1,5 @@ + + + + + diff --git a/fixtures/ns-angular-demo-app/src/app/settings/settings.component.ts b/fixtures/ns-angular-demo-app/src/app/settings/settings.component.ts new file mode 100644 index 0000000..a625a39 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/settings/settings.component.ts @@ -0,0 +1,24 @@ +import { Component, NO_ERRORS_SCHEMA, inject } from '@angular/core'; +import { NativeScriptCommonModule, RouterExtensions } from '@nativescript/angular'; + +@Component({ + selector: 'ns-settings', + templateUrl: './settings.component.html', + imports: [NativeScriptCommonModule], + schemas: [NO_ERRORS_SCHEMA] +}) +export class SettingsComponent { + private router = inject(RouterExtensions); + + account() { + this.router.navigate(['/account']); + } + + signOut() { + this.router.router.navigateByUrl('/login'); + } + + search() { + this.router.navigate(['/home', { outlets: { searchTab: ['search'] } }]); + } +} diff --git a/fixtures/ns-angular-demo-app/src/app/share/share-dialog.component.ts b/fixtures/ns-angular-demo-app/src/app/share/share-dialog.component.ts new file mode 100644 index 0000000..ed8c735 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/app/share/share-dialog.component.ts @@ -0,0 +1,10 @@ +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; +import { NativeScriptCommonModule } from '@nativescript/angular'; + +@Component({ + selector: 'ns-share-dialog', + template: '', + imports: [NativeScriptCommonModule], + schemas: [NO_ERRORS_SCHEMA] +}) +export class ShareDialogComponent {} diff --git a/fixtures/ns-angular-demo-app/src/main.ts b/fixtures/ns-angular-demo-app/src/main.ts new file mode 100644 index 0000000..b535346 --- /dev/null +++ b/fixtures/ns-angular-demo-app/src/main.ts @@ -0,0 +1,10 @@ +import { bootstrapApplication, provideNativeScriptRouter, runNativeScriptAngularApp } from '@nativescript/angular'; +import { AppComponent } from './app/app.component'; +import { routes } from './app/app.routes'; + +runNativeScriptAngularApp({ + appModuleBootstrap: () => + bootstrapApplication(AppComponent, { + providers: [provideNativeScriptRouter(routes)] + }) +}); diff --git a/fixtures/ns-angular-demo-app/tsconfig.json b/fixtures/ns-angular-demo-app/tsconfig.json new file mode 100644 index 0000000..6e8f66e --- /dev/null +++ b/fixtures/ns-angular-demo-app/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "esnext", + "target": "es2020", + "moduleResolution": "bundler", + "experimentalDecorators": true, + "paths": { + "~/*": ["./src/*"] + } + }, + "files": ["./src/main.ts"] +} diff --git a/fixtures/ns-core-demo-app/App_Resources/iOS/Info.plist b/fixtures/ns-core-demo-app/App_Resources/iOS/Info.plist new file mode 100644 index 0000000..81cb536 --- /dev/null +++ b/fixtures/ns-core-demo-app/App_Resources/iOS/Info.plist @@ -0,0 +1,17 @@ + + + + + CFBundleDisplayName + NS Core Demo + CFBundleURLTypes + + + CFBundleURLSchemes + + nscore + + + + + diff --git a/fixtures/ns-core-demo-app/app/app.ts b/fixtures/ns-core-demo-app/app/app.ts new file mode 100644 index 0000000..865d16b --- /dev/null +++ b/fixtures/ns-core-demo-app/app/app.ts @@ -0,0 +1,3 @@ +import { Application } from '@nativescript/core'; + +Application.run({ moduleName: 'main-page' }); diff --git a/fixtures/ns-core-demo-app/app/main-page.ts b/fixtures/ns-core-demo-app/app/main-page.ts new file mode 100644 index 0000000..83c5356 --- /dev/null +++ b/fixtures/ns-core-demo-app/app/main-page.ts @@ -0,0 +1,17 @@ +import { EventData, Page } from '@nativescript/core'; +import { HelloWorldModel } from './main-view-model'; + +export function onNavigatingTo(args: EventData) { + const page = args.object; + page.bindingContext = new HelloWorldModel(); +} + +export function onOpenDetails(args: EventData) { + const page = (args.object).page as Page; + page.frame.navigate({ moduleName: 'pages/details/details-page', context: { id: 42 } }); +} + +export function onOpenFilters(args: EventData) { + const page = (args.object).page as Page; + page.showModal('pages/filters/filters-modal', { context: {}, closeCallback: () => undefined, fullscreen: false }); +} diff --git a/fixtures/ns-core-demo-app/app/main-page.xml b/fixtures/ns-core-demo-app/app/main-page.xml new file mode 100644 index 0000000..7cf7266 --- /dev/null +++ b/fixtures/ns-core-demo-app/app/main-page.xml @@ -0,0 +1,7 @@ + + + +