@@ -7,10 +7,21 @@ import { excludeDirs, parseWorkspaces } from './helpers/scriptUtils'
77
88const genMsg = `// -i- Automatically generated by 'yarn link-routes', do not modify manually`
99
10+ const manifestTemplate = `${ genMsg }
11+ export const routeManifest = {
12+ {{routeManifestLines}}
13+ } as const
14+
15+ export type KnownRoutes = keyof typeof routeManifest | (string & {})
16+ `
17+
1018/* --- link-routes ----------------------------------------------------------------------------- */
1119
1220const linkRoutes = ( ) => {
1321 try {
22+ // Keep track of routes to save manifest
23+ const routeManifest = { }
24+
1425 // Get all route paths in the features & package folders
1526 const packageRoutePaths = glob . sync ( '../../packages/**/routes/**/*.{ts,tsx}' ) . filter ( excludeDirs ) // prettier-ignore
1627 const featureRoutePaths = glob . sync ( '../../features/**/routes/**/*.{ts,tsx}' ) . filter ( excludeDirs ) // prettier-ignore
@@ -34,6 +45,7 @@ const linkRoutes = () => {
3445
3546 // Parse & match each route path to a workspace import
3647 const parsePath = ( pth , autoDefault = true ) => {
48+ let screenComponentName = ''
3749 // Figure out the workspace import
3850 const [ packageParts , routeParts ] = pth . split ( '/routes' ) as [ string , string ]
3951 const workspaceMatcher = packageParts . replace ( '../../' , '' )
@@ -43,6 +55,13 @@ const linkRoutes = () => {
4355 const expoExports = autoDefault ? [ 'default' ] : ( [ ] as string [ ] )
4456 if ( [ ...indexRoutes , ...paramRoutes , ...apiRoutes ] . includes ( pth ) ) {
4557 const routeFile = fs . readFileSync ( pth , 'utf8' )
58+ // Keep track of which Screen component is used?
59+ if ( routeFile . includes ( 'screen={' ) )
60+ screenComponentName = routeFile . split ( 'screen={' ) [ 1 ] . split ( '}' ) [ 0 ]
61+ if ( routeFile . includes ( 'ScreenComponent' ) )
62+ screenComponentName = routeFile . split ( 'ScreenComponent' ) [ 1 ] . split ( '=' ) [ 1 ] . split ( '\n' ) [ 0 ] . trim ( ) // prettier-ignore
63+ if ( screenComponentName . includes ( '.' ) )
64+ screenComponentName = screenComponentName . split ( '.' ) . pop ( ) as string
4665 // Always check if there's a default export when autoDefault is false
4766 if ( ! autoDefault && routeFile . includes ( 'default' ) ) nextExports . push ( 'next' )
4867 // Next.js Route Segment Config Exports
@@ -68,7 +87,7 @@ const linkRoutes = () => {
6887 if ( routeFile . includes ( 'size' ) ) nextExports . push ( 'size' )
6988 }
7089 // Return everything
71- return { workspacePackageName, routeParts, nextExports, expoExports }
90+ return { workspacePackageName, routeParts, nextExports, expoExports, screenComponentName }
7291 }
7392 // Clear previous generated route files
7493 fs . mkdirSync ( '../../apps/expo/app/(generated)' , { recursive : true } ) // create empty folder if it doesn't exist
@@ -81,8 +100,9 @@ const linkRoutes = () => {
81100
82101 // Reexport fs based index routing in next & expo app dirs
83102 indexRoutes . forEach ( ( pth ) => {
84- const { workspacePackageName, routeParts, nextExports, expoExports } = parsePath ( pth )
103+ const { workspacePackageName, routeParts, nextExports, expoExports, screenComponentName } = parsePath ( pth ) // prettier-ignore
85104 const routeSegments = routeParts . split ( 'index.ts' ) [ 0 ]
105+ if ( screenComponentName ) routeManifest [ routeSegments ] = screenComponentName
86106 const importPath = `${ workspacePackageName } /routes${ routeSegments } index`
87107 const expoExportLine = `${ genMsg } \nexport { ${ expoExports . join ( ', ' ) } } from '${ importPath } '\n` // prettier-ignore
88108 const nextExportLine = `'use client'\nexport { ${ nextExports . join ( ', ' ) } } from '${ importPath } '\n` // prettier-ignore
@@ -96,10 +116,11 @@ const linkRoutes = () => {
96116 } )
97117 // Reexport fs based slug routing in next & expo app dirs
98118 paramRoutes . forEach ( ( pth ) => {
99- const { workspacePackageName, routeParts, nextExports, expoExports } = parsePath ( pth )
119+ const { workspacePackageName, routeParts, nextExports, expoExports, screenComponentName } = parsePath ( pth ) // prettier-ignore
100120 const fileName = routeParts . split ( '/' ) . pop ( ) as string // e.g. "[slug].tsx"
101121 const routeParam = fileName . split ( '.ts' ) [ 0 ] // e.g. "[slug]"
102122 const routeSegments = routeParts . split ( fileName ) [ 0 ]
123+ if ( screenComponentName ) routeManifest [ routeSegments ] = screenComponentName
103124 const importPath = `${ workspacePackageName } /routes${ routeSegments } ${ routeParam } `
104125 const expoExportLine = `export { ${ expoExports . join ( ', ' ) } } from '${ importPath } '\n`
105126 const nextExportLine = `'use client'\n${ genMsg } \nexport { ${ nextExports . join ( ', ' ) } } from '${ importPath } '\n` // prettier-ignore
@@ -233,6 +254,15 @@ const linkRoutes = () => {
233254 console . log ( ` ✅ ${ routeSegments } -- API Route from "${ pth } "` )
234255 console . log ( ` └── /apps/next/app/(generated)${ routeSegments } route.ts` )
235256 } )
257+
258+ // Save route manifest
259+ const routeManifestPath = '../../packages/@registries/routeManifest.ts'
260+ const routeManifestLines = Object . entries ( routeManifest ) . map ( ( [ route , compName ] ) => {
261+ const routePath = route === '/' ? '/' : route . substring ( 0 , route . length - 1 ) // prettier-ignore
262+ return ` ['${ routePath } ']: '${ compName } ',`
263+ } ) . join ( '\n' ) // prettier-ignore
264+ const routeManifestFile = manifestTemplate . replace ( '{{routeManifestLines}}' , routeManifestLines ) // prettier-ignore
265+ fs . writeFileSync ( routeManifestPath , routeManifestFile )
236266 } catch ( err ) {
237267 console . log ( err )
238268 console . error ( err )
0 commit comments