xref: /expo/packages/@expo/server/build/index.js (revision e9783df2)
1"use strict";
2var __importDefault = (this && this.__importDefault) || function (mod) {
3    return (mod && mod.__esModule) ? mod : { "default": mod };
4};
5Object.defineProperty(exports, "__esModule", { value: true });
6exports.ExpoRequest = exports.ExpoResponse = exports.createRequestHandler = exports.getRoutesManifest = void 0;
7require("@expo/server/install");
8const fs_1 = __importDefault(require("fs"));
9const path_1 = __importDefault(require("path"));
10const url_1 = require("url");
11const environment_1 = require("./environment");
12Object.defineProperty(exports, "ExpoRequest", { enumerable: true, get: function () { return environment_1.ExpoRequest; } });
13Object.defineProperty(exports, "ExpoResponse", { enumerable: true, get: function () { return environment_1.ExpoResponse; } });
14const debug = require('debug')('expo:server');
15function getProcessedManifest(path) {
16    // TODO: JSON Schema for validation
17    const routesManifest = JSON.parse(fs_1.default.readFileSync(path, 'utf-8'));
18    const parsed = {
19        ...routesManifest,
20        notFoundRoutes: routesManifest.notFoundRoutes.map((value) => {
21            return {
22                ...value,
23                namedRegex: new RegExp(value.namedRegex),
24            };
25        }),
26        apiRoutes: routesManifest.apiRoutes.map((value) => {
27            return {
28                ...value,
29                namedRegex: new RegExp(value.namedRegex),
30            };
31        }),
32        htmlRoutes: routesManifest.htmlRoutes.map((value) => {
33            return {
34                ...value,
35                namedRegex: new RegExp(value.namedRegex),
36            };
37        }),
38    };
39    return parsed;
40}
41function getRoutesManifest(distFolder) {
42    return getProcessedManifest(path_1.default.join(distFolder, '_expo/routes.json'));
43}
44exports.getRoutesManifest = getRoutesManifest;
45// TODO: Reuse this for dev as well
46function createRequestHandler(distFolder, { getRoutesManifest: getInternalRoutesManifest, getHtml = async (request, route) => {
47    // serve a static file
48    const filePath = path_1.default.join(distFolder, route.page + '.html');
49    if (!fs_1.default.existsSync(filePath)) {
50        return null;
51    }
52    return fs_1.default.readFileSync(filePath, 'utf-8');
53}, getApiRoute = async (route) => {
54    const filePath = path_1.default.join(distFolder, '_expo/functions', route.page + '.js');
55    debug(`Handling API route: ${route.page}: ${filePath}`);
56    // TODO: What's the standard behavior for malformed projects?
57    if (!fs_1.default.existsSync(filePath)) {
58        return null;
59    }
60    return require(filePath);
61}, logApiRouteExecutionError = (error) => {
62    console.error(error);
63}, } = {}) {
64    let routesManifest;
65    function updateRequestWithConfig(request, config) {
66        const params = {};
67        const url = request.url;
68        const expoUrl = new environment_1.ExpoURL(url);
69        const match = config.namedRegex.exec(expoUrl.pathname);
70        if (match?.groups) {
71            for (const [key, value] of Object.entries(match.groups)) {
72                const namedKey = config.routeKeys[key];
73                expoUrl.searchParams.set(namedKey, value);
74                params[namedKey] = value;
75            }
76        }
77        request[environment_1.NON_STANDARD_SYMBOL] = {
78            url: expoUrl,
79        };
80        return params;
81    }
82    return async function handler(request) {
83        if (getInternalRoutesManifest) {
84            const manifest = await getInternalRoutesManifest(distFolder);
85            if (manifest) {
86                routesManifest = manifest;
87            }
88            else {
89                // Development error when Expo Router is not setup.
90                return new environment_1.ExpoResponse('No routes manifest found', {
91                    status: 404,
92                    headers: {
93                        'Content-Type': 'text/plain',
94                    },
95                });
96            }
97        }
98        else if (!routesManifest) {
99            routesManifest = getRoutesManifest(distFolder);
100        }
101        const url = new url_1.URL(request.url, 'http://expo.dev');
102        const sanitizedPathname = url.pathname;
103        debug('Request', sanitizedPathname);
104        if (request.method === 'GET' || request.method === 'HEAD') {
105            // First test static routes
106            for (const route of routesManifest.htmlRoutes) {
107                if (!route.namedRegex.test(sanitizedPathname)) {
108                    continue;
109                }
110                // // Mutate to add the expoUrl object.
111                updateRequestWithConfig(request, route);
112                // serve a static file
113                const contents = await getHtml(request, route);
114                // TODO: What's the standard behavior for malformed projects?
115                if (!contents) {
116                    return new environment_1.ExpoResponse('Not found', {
117                        status: 404,
118                        headers: {
119                            'Content-Type': 'text/plain',
120                        },
121                    });
122                }
123                else if (contents instanceof environment_1.ExpoResponse) {
124                    return contents;
125                }
126                return new environment_1.ExpoResponse(contents, {
127                    status: 200,
128                    headers: {
129                        'Content-Type': 'text/html',
130                    },
131                });
132            }
133        }
134        // Next, test API routes
135        for (const route of routesManifest.apiRoutes) {
136            if (!route.namedRegex.test(sanitizedPathname)) {
137                continue;
138            }
139            const func = await getApiRoute(route);
140            if (func instanceof environment_1.ExpoResponse) {
141                return func;
142            }
143            const routeHandler = func[request.method];
144            if (!routeHandler) {
145                return new environment_1.ExpoResponse('Method not allowed', {
146                    status: 405,
147                    headers: {
148                        'Content-Type': 'text/plain',
149                    },
150                });
151            }
152            // Mutate to add the expoUrl object.
153            const params = updateRequestWithConfig(request, route);
154            try {
155                // TODO: Handle undefined
156                return (await routeHandler(request, params));
157            }
158            catch (error) {
159                if (error instanceof Error) {
160                    logApiRouteExecutionError(error);
161                }
162                return new environment_1.ExpoResponse('Internal server error', {
163                    status: 500,
164                    headers: {
165                        'Content-Type': 'text/plain',
166                    },
167                });
168            }
169        }
170        // Finally, test 404 routes
171        for (const route of routesManifest.notFoundRoutes) {
172            if (!route.namedRegex.test(sanitizedPathname)) {
173                continue;
174            }
175            // // Mutate to add the expoUrl object.
176            updateRequestWithConfig(request, route);
177            // serve a static file
178            const contents = await getHtml(request, route);
179            // TODO: What's the standard behavior for malformed projects?
180            if (!contents) {
181                return new environment_1.ExpoResponse('Not found', {
182                    status: 404,
183                    headers: {
184                        'Content-Type': 'text/plain',
185                    },
186                });
187            }
188            else if (contents instanceof environment_1.ExpoResponse) {
189                return contents;
190            }
191            return new environment_1.ExpoResponse(contents, {
192                status: 404,
193                headers: {
194                    'Content-Type': 'text/html',
195                },
196            });
197        }
198        // 404
199        const response = new environment_1.ExpoResponse('Not found', {
200            status: 404,
201            headers: {
202                'Content-Type': 'text/plain',
203            },
204        });
205        return response;
206    };
207}
208exports.createRequestHandler = createRequestHandler;
209//# sourceMappingURL=index.js.map