Skip to content

Commit

Permalink
feat: Save routeManifest with KnownRoutes type during yarn link-routes
Browse files Browse the repository at this point in the history
  • Loading branch information
codinsonn committed Oct 21, 2023
1 parent 620a871 commit 983d144
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { ComponentProps } from 'react'
import { Text } from 'react-native'
// Schemas
import { TAetherStyleProps } from '../../schemas/ats'
// Types
import type { KnownRoutes } from 'registries/routeManifest'

/* --- Types ----------------------------------------------------------------------------------- */

Expand All @@ -16,17 +18,17 @@ export type AetherLinkBaseType = Partial<ComponentProps<typeof Text>> &
}

export type AetherLinkToType = AetherLinkBaseType & {
to: string
to: KnownRoutes
href?: never
routeName?: never
}
export type AetherLinkHrefType = AetherLinkBaseType & {
href: string
href: KnownRoutes
to?: never
routeName?: never
}
export type AetherLinkRouteType = AetherLinkBaseType & {
routeName: string
routeName: KnownRoutes
to?: never
href?: never
}
Expand Down
36 changes: 33 additions & 3 deletions packages/@aetherspace/scripts/link-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,21 @@ import { excludeDirs, parseWorkspaces } from './helpers/scriptUtils'

const genMsg = `// -i- Automatically generated by 'yarn link-routes', do not modify manually`

const manifestTemplate = `${genMsg}
export const routeManifest = {
{{routeManifestLines}}
} as const
export type KnownRoutes = keyof typeof routeManifest | (string & {})
`

/* --- link-routes ----------------------------------------------------------------------------- */

const linkRoutes = () => {
try {
// Keep track of routes to save manifest
const routeManifest = {}

// Get all route paths in the features & package folders
const packageRoutePaths = glob.sync('../../packages/**/routes/**/*.{ts,tsx}').filter(excludeDirs) // prettier-ignore
const featureRoutePaths = glob.sync('../../features/**/routes/**/*.{ts,tsx}').filter(excludeDirs) // prettier-ignore
Expand All @@ -34,6 +45,7 @@ const linkRoutes = () => {

// Parse & match each route path to a workspace import
const parsePath = (pth, autoDefault = true) => {
let screenComponentName = ''
// Figure out the workspace import
const [packageParts, routeParts] = pth.split('/routes') as [string, string]
const workspaceMatcher = packageParts.replace('../../', '')
Expand All @@ -43,6 +55,13 @@ const linkRoutes = () => {
const expoExports = autoDefault ? ['default'] : ([] as string[])
if ([...indexRoutes, ...paramRoutes, ...apiRoutes].includes(pth)) {
const routeFile = fs.readFileSync(pth, 'utf8')
// Keep track of which Screen component is used?
if (routeFile.includes('screen={'))
screenComponentName = routeFile.split('screen={')[1].split('}')[0]
if (routeFile.includes('ScreenComponent'))
screenComponentName = routeFile.split('ScreenComponent')[1].split('=')[1].split('\n')[0].trim() // prettier-ignore
if (screenComponentName.includes('.'))
screenComponentName = screenComponentName.split('.').pop() as string
// Always check if there's a default export when autoDefault is false
if (!autoDefault && routeFile.includes('default')) nextExports.push('next')
// Next.js Route Segment Config Exports
Expand All @@ -68,7 +87,7 @@ const linkRoutes = () => {
if (routeFile.includes('size')) nextExports.push('size')
}
// Return everything
return { workspacePackageName, routeParts, nextExports, expoExports }
return { workspacePackageName, routeParts, nextExports, expoExports, screenComponentName }
}
// Clear previous generated route files
fs.mkdirSync('../../apps/expo/app/(generated)', { recursive: true }) // create empty folder if it doesn't exist
Expand All @@ -81,8 +100,9 @@ const linkRoutes = () => {

// Reexport fs based index routing in next & expo app dirs
indexRoutes.forEach((pth) => {
const { workspacePackageName, routeParts, nextExports, expoExports } = parsePath(pth)
const { workspacePackageName, routeParts, nextExports, expoExports, screenComponentName } = parsePath(pth) // prettier-ignore
const routeSegments = routeParts.split('index.ts')[0]
if (screenComponentName) routeManifest[routeSegments] = screenComponentName
const importPath = `${workspacePackageName}/routes${routeSegments}index`
const expoExportLine = `${genMsg}\nexport { ${expoExports.join(', ')} } from '${importPath}'\n` // prettier-ignore
const nextExportLine = `'use client'\nexport { ${nextExports.join(', ')} } from '${importPath}'\n` // prettier-ignore
Expand All @@ -96,10 +116,11 @@ const linkRoutes = () => {
})
// Reexport fs based slug routing in next & expo app dirs
paramRoutes.forEach((pth) => {
const { workspacePackageName, routeParts, nextExports, expoExports } = parsePath(pth)
const { workspacePackageName, routeParts, nextExports, expoExports, screenComponentName } = parsePath(pth) // prettier-ignore
const fileName = routeParts.split('/').pop() as string // e.g. "[slug].tsx"
const routeParam = fileName.split('.ts')[0] // e.g. "[slug]"
const routeSegments = routeParts.split(fileName)[0]
if (screenComponentName) routeManifest[routeSegments] = screenComponentName
const importPath = `${workspacePackageName}/routes${routeSegments}${routeParam}`
const expoExportLine = `export { ${expoExports.join(', ')} } from '${importPath}'\n`
const nextExportLine = `'use client'\n${genMsg}\nexport { ${nextExports.join(', ')} } from '${importPath}'\n` // prettier-ignore
Expand Down Expand Up @@ -233,6 +254,15 @@ const linkRoutes = () => {
console.log(` ✅ ${routeSegments} -- API Route from "${pth}"`)
console.log(` └── /apps/next/app/(generated)${routeSegments}route.ts`)
})

// Save route manifest
const routeManifestPath = '../../packages/@registries/routeManifest.ts'
const routeManifestLines = Object.entries(routeManifest).map(([route, compName]) => {
const routePath = route === '/' ? '/' : route.substring(0, route.length - 1) // prettier-ignore
return ` ['${routePath}']: '${compName}',`
}).join('\n') // prettier-ignore
const routeManifestFile = manifestTemplate.replace('{{routeManifestLines}}', routeManifestLines) // prettier-ignore
fs.writeFileSync(routeManifestPath, routeManifestFile)
} catch (err) {
console.log(err)
console.error(err)
Expand Down
12 changes: 12 additions & 0 deletions packages/@registries/routeManifest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// -i- Automatically generated by 'yarn link-routes', do not modify manually
export const routeManifest = {
['/bio']: 'BioScreen',
['/cv']: 'ResumeScreen',
['/']: 'BioScreen',
['/links']: 'BioScreen',
['/resume']: 'ResumeScreen',
['/cv/[slug]']: 'ResumeScreen',
['/resume/[slug]']: 'ResumeScreen',
} as const

export type KnownRoutes = keyof typeof routeManifest | (string & {})

1 comment on commit 983d144

@vercel
Copy link

@vercel vercel bot commented on 983d144 Oct 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.