1*46f023faSEvan Bacon/** 2*46f023faSEvan Bacon * Copyright © 2022 650 Industries. 3*46f023faSEvan Bacon * 4*46f023faSEvan Bacon * This source code is licensed under the MIT license found in the 5*46f023faSEvan Bacon * LICENSE file in the root directory of this source tree. 6*46f023faSEvan Bacon */ 7*46f023faSEvan Baconimport resolveFrom from 'resolve-from'; 8*46f023faSEvan Bacon 9*46f023faSEvan Baconimport { getRoutePaths } from './router'; 10*46f023faSEvan Bacon 11*46f023faSEvan Baconexport type ExpoRouterServerManifestV1Route<TRegex = string> = { 12*46f023faSEvan Bacon page: string; 13*46f023faSEvan Bacon routeKeys: Record<string, string>; 14*46f023faSEvan Bacon namedRegex: TRegex; 15*46f023faSEvan Bacon generated?: boolean; 16*46f023faSEvan Bacon}; 17*46f023faSEvan Bacon 18*46f023faSEvan Baconexport type ExpoRouterServerManifestV1<TRegex = string> = { 19*46f023faSEvan Bacon apiRoutes: ExpoRouterServerManifestV1Route<TRegex>[]; 20*46f023faSEvan Bacon htmlRoutes: ExpoRouterServerManifestV1Route<TRegex>[]; 21*46f023faSEvan Bacon notFoundRoutes: ExpoRouterServerManifestV1Route<TRegex>[]; 22*46f023faSEvan Bacon}; 23*46f023faSEvan Bacon 24*46f023faSEvan Baconfunction getExpoRouteManifestBuilderAsync(projectRoot: string) { 25*46f023faSEvan Bacon return require(resolveFrom(projectRoot, 'expo-router/build/routes-manifest')) 26*46f023faSEvan Bacon .createRoutesManifest as typeof import('expo-router/build/routes-manifest').createRoutesManifest; 27*46f023faSEvan Bacon} 28*46f023faSEvan Bacon 29*46f023faSEvan Bacon// TODO: Simplify this now that we use Node.js directly, no need for the Metro bundler caching layer. 30*46f023faSEvan Baconexport async function fetchManifest<TRegex = string>( 31*46f023faSEvan Bacon projectRoot: string, 32*46f023faSEvan Bacon options: { asJson?: boolean; appDir: string } 33*46f023faSEvan Bacon): Promise<ExpoRouterServerManifestV1<TRegex> | null> { 34*46f023faSEvan Bacon const getManifest = getExpoRouteManifestBuilderAsync(projectRoot); 35*46f023faSEvan Bacon const paths = getRoutePaths(options.appDir); 36*46f023faSEvan Bacon // Get the serialized manifest 37*46f023faSEvan Bacon const jsonManifest = getManifest(paths); 38*46f023faSEvan Bacon 39*46f023faSEvan Bacon if (!jsonManifest) { 40*46f023faSEvan Bacon return null; 41*46f023faSEvan Bacon } 42*46f023faSEvan Bacon 43*46f023faSEvan Bacon if (!jsonManifest.htmlRoutes || !jsonManifest.apiRoutes) { 44*46f023faSEvan Bacon throw new Error('Routes manifest is malformed: ' + JSON.stringify(jsonManifest, null, 2)); 45*46f023faSEvan Bacon } 46*46f023faSEvan Bacon 47*46f023faSEvan Bacon if (!options.asJson) { 48*46f023faSEvan Bacon // @ts-expect-error 49*46f023faSEvan Bacon return inflateManifest(jsonManifest); 50*46f023faSEvan Bacon } 51*46f023faSEvan Bacon // @ts-expect-error 52*46f023faSEvan Bacon return jsonManifest; 53*46f023faSEvan Bacon} 54*46f023faSEvan Bacon 55*46f023faSEvan Bacon// Convert the serialized manifest to a usable format 56*46f023faSEvan Baconexport function inflateManifest( 57*46f023faSEvan Bacon json: ExpoRouterServerManifestV1<string> 58*46f023faSEvan Bacon): ExpoRouterServerManifestV1<RegExp> { 59*46f023faSEvan Bacon return { 60*46f023faSEvan Bacon ...json, 61*46f023faSEvan Bacon htmlRoutes: json.htmlRoutes?.map((value) => { 62*46f023faSEvan Bacon return { 63*46f023faSEvan Bacon ...value, 64*46f023faSEvan Bacon namedRegex: new RegExp(value.namedRegex), 65*46f023faSEvan Bacon }; 66*46f023faSEvan Bacon }), 67*46f023faSEvan Bacon apiRoutes: json.apiRoutes?.map((value) => { 68*46f023faSEvan Bacon return { 69*46f023faSEvan Bacon ...value, 70*46f023faSEvan Bacon namedRegex: new RegExp(value.namedRegex), 71*46f023faSEvan Bacon }; 72*46f023faSEvan Bacon }), 73*46f023faSEvan Bacon notFoundRoutes: json.notFoundRoutes?.map((value) => { 74*46f023faSEvan Bacon return { 75*46f023faSEvan Bacon ...value, 76*46f023faSEvan Bacon namedRegex: new RegExp(value.namedRegex), 77*46f023faSEvan Bacon }; 78*46f023faSEvan Bacon }), 79*46f023faSEvan Bacon }; 80*46f023faSEvan Bacon} 81