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