expo-router
Version:
Expo Router is a file-based router for React Native and web applications.
126 lines • 6.29 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBuildTimeServerManifestAsync = exports.getManifest = exports.getStaticContent = void 0;
/**
* Copyright © 2023 650 Industries.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
require("@expo/metro-runtime");
const native_1 = require("@react-navigation/native");
const Font = __importStar(require("expo-font/build/server"));
const react_1 = __importDefault(require("react"));
const server_node_1 = __importDefault(require("react-dom/server.node"));
const react_native_web_1 = require("react-native-web");
const getRootComponent_1 = require("./getRootComponent");
const _ctx_1 = require("../../_ctx");
const ExpoRoot_1 = require("../ExpoRoot");
const getReactNavigationConfig_1 = require("../getReactNavigationConfig");
const getRoutes_1 = require("../getRoutes");
const head_1 = require("../head");
const loadStaticParamsAsync_1 = require("../loadStaticParamsAsync");
const debug = require('debug')('expo:router:renderStaticContent');
react_native_web_1.AppRegistry.registerComponent('App', () => ExpoRoot_1.ExpoRoot);
/** Get the linking manifest from a Node.js process. */
async function getManifest(options = {}) {
const routeTree = (0, getRoutes_1.getRoutes)(_ctx_1.ctx, {
preserveApiRoutes: true,
platform: 'web',
...options,
});
if (!routeTree) {
throw new Error('No routes found');
}
// Evaluate all static params
await (0, loadStaticParamsAsync_1.loadStaticParamsAsync)(routeTree);
return (0, getReactNavigationConfig_1.getReactNavigationConfig)(routeTree, false);
}
exports.getManifest = getManifest;
function resetReactNavigationContexts() {
// https://github.com/expo/router/discussions/588
// https://github.com/react-navigation/react-navigation/blob/9fe34b445fcb86e5666f61e144007d7540f014fa/packages/elements/src/getNamedContext.tsx#LL3C1-L4C1
// React Navigation is storing providers in a global, this is fine for the first static render
// but subsequent static renders of Stack or Tabs will cause React to throw a warning. To prevent this warning, we'll reset the globals before rendering.
const contexts = '__react_navigation__elements_contexts';
global[contexts] = new Map();
}
async function getStaticContent(location) {
const headContext = {};
const ref = react_1.default.createRef();
const {
// NOTE: The `element` that's returned adds two extra Views and
// the seemingly unused `RootTagContext.Provider`.
element, getStyleElement, } = react_native_web_1.AppRegistry.getApplication('App', {
initialProps: {
location,
context: _ctx_1.ctx,
wrapper: ({ children }) => (<Root>
<div id="root">{children}</div>
</Root>),
},
});
const Root = (0, getRootComponent_1.getRootComponent)();
// Clear any existing static resources from the global scope to attempt to prevent leaking between pages.
// This could break if pages are rendered in parallel or if fonts are loaded outside of the React tree
Font.resetServerContext();
// This MUST be run before `ReactDOMServer.renderToString` to prevent
// "Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported."
resetReactNavigationContexts();
const html = await server_node_1.default.renderToString(<head_1.Head.Provider context={headContext}>
<native_1.ServerContainer ref={ref}>{element}</native_1.ServerContainer>
</head_1.Head.Provider>);
// Eval the CSS after the HTML is rendered so that the CSS is in the same order
const css = server_node_1.default.renderToStaticMarkup(getStyleElement());
let output = mixHeadComponentsWithStaticResults(headContext.helmet, html);
output = output.replace('</head>', `${css}</head>`);
const fonts = Font.getServerResources();
debug(`Pushing static fonts: (count: ${fonts.length})`, fonts);
// debug('Push static fonts:', fonts)
// Inject static fonts loaded with expo-font
output = output.replace('</head>', `${fonts.join('')}</head>`);
return '<!DOCTYPE html>' + output;
}
exports.getStaticContent = getStaticContent;
function mixHeadComponentsWithStaticResults(helmet, html) {
// Head components
for (const key of ['title', 'priority', 'meta', 'link', 'script', 'style'].reverse()) {
const result = helmet?.[key]?.toString();
if (result) {
html = html.replace('<head>', `<head>${result}`);
}
}
// attributes
html = html.replace('<html ', `<html ${helmet?.htmlAttributes.toString()} `);
html = html.replace('<body ', `<body ${helmet?.bodyAttributes.toString()} `);
return html;
}
var getServerManifest_1 = require("./getServerManifest");
Object.defineProperty(exports, "getBuildTimeServerManifestAsync", { enumerable: true, get: function () { return getServerManifest_1.getBuildTimeServerManifestAsync; } });
//# sourceMappingURL=renderStaticContent.js.map
;