diff --git a/.changeset/single-owned-auth-root-navigation.md b/.changeset/single-owned-auth-root-navigation.md new file mode 100644 index 0000000..822907c --- /dev/null +++ b/.changeset/single-owned-auth-root-navigation.md @@ -0,0 +1,5 @@ +--- +'@ankhorage/studio': patch +--- + +Keep generated auth root entries navigation-neutral so the root layout alone canonicalizes authenticated `/` launches without synthetic Back history or `initial=false` workarounds. diff --git a/src/host/layout/authRootBootstrap.test.ts b/src/host/layout/authRootBootstrap.test.ts index 84ee73a..1175324 100644 --- a/src/host/layout/authRootBootstrap.test.ts +++ b/src/host/layout/authRootBootstrap.test.ts @@ -82,20 +82,28 @@ describe('generated auth root bootstrap', () => { expect(rootLayout).toContain(""); }); - test('anchors a non-root post-sign-in route as the initial navigator history entry', () => { + test('keeps non-root / matchable while the root layout alone canonicalizes navigation', () => { const files = generateAuthFiles('products'); - const rootRedirect = files.find((file) => file.path === 'src/app/index.tsx')?.content ?? ''; + const paths = files.map((file) => file.path); + const rootEntry = files.find((file) => file.path === 'src/app/index.tsx')?.content ?? ''; const rootLayout = files.find((file) => file.path === 'src/app/_layout.tsx')?.content ?? ''; const appLayout = files.find((file) => file.path === 'src/app/(app)/_layout.tsx')?.content ?? ''; const tabsLayout = files.find((file) => file.path === 'src/app/(app)/(tabs)/_layout.tsx')?.content ?? ''; - expect(rootRedirect).toContain(""); - expect(rootRedirect).not.toContain(""); + expect(paths).toContain('src/app/index.tsx'); + expect(rootEntry).toContain("canonicalizes / to '/products'"); + expect(rootEntry).toContain('return null;'); + expect(rootEntry).not.toContain('expo-router'); + expect(rootEntry).not.toContain('Redirect'); + expect(rootEntry).not.toContain('withAnchor'); + expect(rootEntry).not.toContain('initial='); + expect(rootLayout).toContain("authenticated && currentPath === '/' && postSignInPath !== '/'"); + expect(rootLayout).toContain('router.replace(AUTH_POST_SIGN_IN_ROUTE_TARGET);'); expect(rootLayout).toContain("initialRouteName: '(app)'"); expect(appLayout).toContain("initialRouteName: '(tabs)'"); expect(tabsLayout).toContain("initialRouteName: 'products'"); - expect(files.map((file) => file.path)).toContain('src/app/(app)/(tabs)/products.tsx'); + expect(paths).toContain('src/app/(app)/(tabs)/products.tsx'); }); }); diff --git a/src/host/layout/templates/redirect.ts b/src/host/layout/templates/redirect.ts index a3b90e5..ebbd5c7 100644 --- a/src/host/layout/templates/redirect.ts +++ b/src/host/layout/templates/redirect.ts @@ -1,10 +1,12 @@ import { escapeStringLiteral } from '../utils/escapeStringLiteral'; export function getIndexRedirectRouteTsx(href: string): string { - return `import { Redirect } from 'expo-router'; - + return `/** + * The root layout owns auth bootstrap and canonicalizes / to '${escapeStringLiteral(href)}'. + * This route only keeps / matchable without creating synthetic navigation history. + */ export default function IndexRoute() { - return ; + return null; } `; }