one
Version:
One is a React framework for web, iOS, and Android apps.
425 lines (422 loc) • 17.5 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all) __defProp(target, name, {
get: all[name],
enumerable: true
});
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: () => from[key],
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var useScreens_exports = {};
__export(useScreens_exports, {
Group: () => Group,
Screen: () => Screen,
createGetIdForRoute: () => createGetIdForRoute,
getQualifiedRouteComponent: () => getQualifiedRouteComponent,
useSortedScreens: () => useSortedScreens
});
module.exports = __toCommonJS(useScreens_exports);
var import_core = require("@react-navigation/core");
var import_react = __toESM(require("react"), 1);
var import_safe_area_context = require("../safe-area-context/index.cjs");
var import_ServerContextScript = require("../server/ServerContextScript.cjs");
var import_skewProtection = require("../skewProtection.cjs");
var import_useMatches = require("../useMatches.cjs");
var import_dynamicImport = require("../utils/dynamicImport.cjs");
var import_getPageExport = require("../utils/getPageExport.cjs");
var import_EmptyRoute = require("../views/EmptyRoute.cjs");
var import_Navigator = require("../views/Navigator.cjs");
var import_RouteErrorView = require("../views/RouteErrorView.cjs");
var import_Try = require("../views/Try.cjs");
var import_ScrollBehavior = require("../views/ScrollBehavior.cjs");
var import_DevHead = require("../vite/DevHead.cjs");
var import_one_server_only = require("../vite/one-server-only.cjs");
var import_getReactNavigationConfig = require("../getReactNavigationConfig.cjs");
var import_filterRootHTML = require("./filterRootHTML.cjs");
var import_Route = require("./Route.cjs");
require("./routeHmr.cjs");
var import_SpaShellContext = require("./SpaShellContext.cjs");
var import_sortRoutes = require("./sortRoutes.cjs");
var import_jsx_runtime = require("react/jsx-runtime");
var import_react2 = require("react");
function hasMetaCharset(children) {
if (process.env.NODE_ENV === "development") {
if (!children) return false;
const checkElement = (child) => {
if (!import_react.default.isValidElement(child)) return false;
if (child.type === "meta") {
const props = child.props;
if ("charSet" in props || "charset" in props) return true;
}
const childProps = child.props;
if (childProps.children) return hasMetaCharset(childProps.children);
return false;
};
if (Array.isArray(children)) return children.some(checkElement);
return checkElement(children);
}
return true;
}
const { Screen, Group } = (0, import_core.createNavigatorFactory)({})();
const cachedInlineCSSElements = typeof document !== "undefined" && typeof document.querySelectorAll === "function" ? (() => {
const elements = [];
document.querySelectorAll("style[id^=\"__one_css_\"], link[rel=\"stylesheet\"][data-one-css]").forEach((el, i) => {
if (el.tagName === "STYLE") elements.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", {
id: el.id,
dangerouslySetInnerHTML: { __html: el.innerHTML }
}, `inline-css-${i}`));
else {
const href = el.getAttribute("href");
elements.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)("link", {
rel: "stylesheet",
href,
"data-one-css": ""
}, href));
}
});
return elements;
})() : [];
const SSR_SAFE_METRICS = {
frame: {
x: 0,
y: 0,
width: 0,
height: 0
},
insets: {
top: 0,
left: 0,
right: 0,
bottom: 0
}
};
function RootLayoutRenderer({ LayoutComponent, layoutProps, forwardedRef }) {
if (process.env.NODE_ENV === "development" && true) {
const [, setHmrKey] = (0, import_react.useState)(0);
(0, import_react.useEffect)(() => {
const handler = () => setHmrKey((k) => k + 1);
window.addEventListener("one-hmr-update", handler);
return () => window.removeEventListener("one-hmr-update", handler);
}, []);
}
const out = LayoutComponent(layoutProps, forwardedRef);
const { children, bodyProps, head, htmlProps } = (0, import_filterRootHTML.filterRootHTML)(out);
const { children: headChildren, ...headProps } = head?.props || {};
const serverContext = (0, import_one_server_only.useServerContext)();
let finalChildren = children;
if (process.env.NODE_ENV === "development") {
if (!hasMetaCharset(headChildren)) console.warn(`[one] Missing <meta charSet="utf-8" /> in your root _layout.tsx <head>. This can cause React hydration issues due to encoding mismatch. Add it as the first element in your <head> tag.`);
}
finalChildren = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("head", {
...headProps,
children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_DevHead.DevHead, {}),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("script", { dangerouslySetInnerHTML: { __html: `globalThis['global'] = globalThis` } }),
serverContext?.cssContents?.length || serverContext?.cssInlineCount ? serverContext?.cssContents ? serverContext.cssContents.map((content, i) => content ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", {
id: `__one_css_${i}`,
dangerouslySetInnerHTML: { __html: content }
}, `inline-css-${i}`) : serverContext.css?.[i] ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("link", {
rel: "stylesheet",
href: serverContext.css[i]
}, serverContext.css[i]) : null) : cachedInlineCSSElements : serverContext?.css?.map((file) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("link", {
rel: "stylesheet",
href: file
}, file)),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ServerContextScript.ServerContextScript, {}),
headChildren
]
}, "head"), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("body", {
suppressHydrationWarning: true,
...bodyProps,
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_safe_area_context.SafeAreaProvider, {
initialMetrics: SSR_SAFE_METRICS,
children: finalChildren
})
}, "body")] });
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("html", {
suppressHydrationWarning: true,
lang: "en-US",
...htmlProps,
children: finalChildren
});
}
function getSortedChildren(children, order, initialRouteName, options) {
if (!order?.length) return children.sort((0, import_sortRoutes.sortRoutesWithInitial)(initialRouteName)).map((route) => ({
route,
props: {}
}));
const entries = [...children];
const ordered = order.map(({ name, redirect, initialParams, listeners, options: options2, getId }) => {
if (!entries.length) {
console.warn(`[Layout children]: Too many screens defined. Route "${name}" is extraneous.`);
return null;
}
const matchIndex = entries.findIndex((child) => child.route === name);
if (matchIndex === -1) {
console.warn(`[Layout children]: No route named "${name}" exists in nested children:`, children.map(({ route }) => route));
return null;
}
const match = entries[matchIndex];
entries.splice(matchIndex, 1);
if (redirect) {
if (typeof redirect === "string") throw new Error(`Redirecting to a specific route is not supported yet.`);
return null;
}
return {
route: match,
props: {
initialParams,
listeners,
options: options2,
getId
}
};
}).filter(Boolean);
const remaining = options?.remaining ?? "all";
if (remaining !== "none") ordered.push(...entries.filter((route) => remaining === "all" || !route.generated).sort((0, import_sortRoutes.sortRoutesWithInitial)(initialRouteName)).map((route) => ({
route,
props: {}
})));
return ordered;
}
function useSortedScreens(order, options) {
const node = (0, import_Route.useRouteNode)();
return import_react.default.useMemo(() => {
return (node?.children?.length ? getSortedChildren(node.children, order, node.initialRouteName, options) : []).filter((value) => {
const routeName = value.route.route;
const normalized = routeName.replace(/\/index$/, "");
return !(options?.protectedScreens?.has(routeName) || options?.protectedScreens?.has(normalized));
}).map((value) => routeToScreen(value.route, value.props));
}, [
node?.children,
node?.initialRouteName,
order,
options?.protectedScreens
]);
}
function fromImport({ ErrorBoundary, SuspenseFallback, ...component }) {
if (process.env.NODE_ENV !== "production") {
const exported = (0, import_getPageExport.getPageExport)(component);
if (exported && typeof exported === "object" && Object.keys(exported).length === 0) return {
default: import_EmptyRoute.EmptyRoute,
ErrorBoundary,
SuspenseFallback
};
}
return {
default: (0, import_getPageExport.getPageExport)(component),
ErrorBoundary,
SuspenseFallback
};
}
function RouteSuspenseFallback({ component: Component, route }) {
const params = (0, import_react.useContext)(import_Route.RouteParamsContext);
return Component ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, {
route,
params: params ?? {}
}) : null;
}
const qualifiedStore = /* @__PURE__ */ new WeakMap();
function getRouteMatchesForProps() {
const serverMatches = (0, import_one_server_only.getServerContext)()?.matches ?? [];
if (process.env.VITE_ENVIRONMENT === "ssr") return serverMatches;
const clientMatches = (0, import_useMatches.getClientMatchesSnapshot)();
return clientMatches.length ? clientMatches : serverMatches;
}
function getQualifiedRouteComponent(value) {
if (value && qualifiedStore.has(value)) return qualifiedStore.get(value);
const ScreenComponent = import_react.default.forwardRef((props, ref) => {
if (process.env.NODE_ENV === "development" && true) {
const [, setHmrKey] = (0, import_react.useState)(0);
(0, import_react.useEffect)(() => {
const handler = () => setHmrKey((k) => k + 1);
window.addEventListener("one-hmr-update", handler);
return () => window.removeEventListener("one-hmr-update", handler);
}, []);
}
if (process.env.NODE_ENV === "development" && false);
const isSpaShell = (0, import_react.useContext)(import_SpaShellContext.SpaShellContext);
const InheritedSuspenseFallback = (0, import_react.useContext)(import_Route.SuspenseFallbackContext);
const matches = getRouteMatchesForProps();
if (isSpaShell && props.segment !== "") {
if (!(value.children?.length && (value.layoutRenderMode === "ssg" || value.layoutRenderMode === "ssr"))) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-one-spa-content": "" });
}
const res = fromImport(value.loadRoute());
const Component = res.default || import_EmptyRoute.EmptyRoute;
const CatchBoundary = res.ErrorBoundary;
const LayoutSuspenseFallback = value.type === "layout" ? res.SuspenseFallback : void 0;
const match = matches.find((match2) => match2.routeId === value.contextKey) || (!value.children?.length ? matches[matches.length - 1] : void 0);
const loaderDataProps = match && "loaderData" in match ? { loaderData: match.loaderData } : {};
if (process.env.NODE_ENV === "development" && process.env.DEBUG === "one") {
console.groupCollapsed(`Render ${props.key} ${props.segment}`);
console.info(`value`, value);
console.info(`Component`, Component);
console.groupEnd();
}
const slotProps = {};
if (value.slots && value.slots.size > 0) for (const [slotName] of value.slots) slotProps[slotName] = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Navigator.NamedSlot, {
name: slotName,
layoutContextKey: value.contextKey
});
let rendered;
if (props.segment === "") rendered = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RootLayoutRenderer, {
LayoutComponent: Component,
layoutProps: {
...props,
...slotProps,
...loaderDataProps
},
forwardedRef: ref
});
else {
const page = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Component, {
...props,
...slotProps,
...loaderDataProps,
ref
});
rendered = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouteErrorBoundary, {
routeName: value.route,
children: CatchBoundary ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Try.Try, {
catch: CatchBoundary,
children: page
}) : page
});
}
if (value.type !== "layout") return rendered;
const providedSuspenseFallback = LayoutSuspenseFallback ?? InheritedSuspenseFallback;
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Route.SuspenseFallbackContext.Provider, {
value: providedSuspenseFallback,
children: LayoutSuspenseFallback ? wrapSuspense(rendered, LayoutSuspenseFallback) : rendered
});
});
const wrapSuspense = (children, SuspenseFallback) => {
const fallback = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouteSuspenseFallback, {
component: SuspenseFallback,
route: value.contextKey
});
if (process.env.ONE_SUSPEND_ROUTES === "1" && globalThis.__ONE_DISABLE_SUSPENSE_ROUTES__ !== true) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, {
fallback,
children
});
return children;
};
const QualifiedRoute = import_react.default.forwardRef(({ route, navigation, ...props }, ref) => {
const InheritedSuspenseFallback = (0, import_react.useContext)(import_Route.SuspenseFallbackContext);
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Route.Route, {
route,
node: value,
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: wrapSuspense(/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScreenComponent, {
...props,
ref,
segment: value.route
}), process.env.VITE_ENVIRONMENT === "client" && value.type !== "layout" && navigation && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ScrollBehavior.ScrollBehaviorRouteCommit, {})] }), InheritedSuspenseFallback) })
});
});
QualifiedRoute.displayName = `Route(${value.route})`;
const Memoized = (0, import_react.memo)(QualifiedRoute);
qualifiedStore.set(value, Memoized);
return Memoized;
}
function createGetIdForRoute(route) {
const include = /* @__PURE__ */ new Map();
if (route.dynamic) for (const segment of route.dynamic) include.set(segment.name, segment);
return ({ params = {} } = {}) => {
const segments = [];
for (const dynamic of include.values()) {
const value = params?.[dynamic.name];
if (Array.isArray(value) && value.length > 0) segments.push(value.join("/"));
else if (value && !Array.isArray(value)) segments.push(value);
else if (dynamic.deep) segments.push(`[...${dynamic.name}]`);
else segments.push(`[${dynamic.name}]`);
}
return segments.join("/") ?? route.contextKey;
};
}
function routeToScreen(route, { options, ...props } = {}) {
const navigationRouteName = (0, import_getReactNavigationConfig.getReactNavigationRouteName)(route);
return /* @__PURE__ */ (0, import_react2.createElement)(Screen, {
getId: createGetIdForRoute(route),
...props,
name: navigationRouteName,
key: navigationRouteName,
options: (args) => {
const staticOptions = route.generated ? route.loadRoute()?.getNavOptions : null;
const staticResult = typeof staticOptions === "function" ? staticOptions(args) : staticOptions;
const dynamicResult = typeof options === "function" ? options?.(args) : options;
const output = {
...staticResult,
...dynamicResult
};
if (route.generated) {
output.tabBarButton = () => null;
output.drawerItemStyle = {
height: 0,
display: "none"
};
}
return output;
},
getComponent: () => {
return getQualifiedRouteComponent(route);
}
});
}
const ROUTE_ERROR_BOUNDARY_INITIAL_STATE = {
hasError: false,
error: null,
errorInfo: null
};
var RouteErrorBoundary = class extends import_react.default.Component {
constructor(props) {
super(props);
this.state = ROUTE_ERROR_BOUNDARY_INITIAL_STATE;
}
static getDerivedStateFromError(error) {
return {
hasError: true,
error
};
}
componentDidCatch(error, errorInfo) {
console.error(`Error occurred while running route "${this.props.routeName}": ${error instanceof Error ? error.message : error}
${error.stack}
Component Stack:
${errorInfo.componentStack}`);
this.setState({ errorInfo });
if (process.env.NODE_ENV === "production" && process.env.ONE_SKEW_PROTECTION !== "false") if ((0, import_dynamicImport.isChunkLoadError)(error)) (0, import_dynamicImport.handleSkewError)();
else (0, import_skewProtection.checkSkewAndReload)();
}
clearError() {
this.setState(ROUTE_ERROR_BOUNDARY_INITIAL_STATE);
}
render() {
if (this.state.hasError) {
const { error, errorInfo } = this.state;
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_RouteErrorView.RouteErrorView, {
routeName: this.props.routeName,
error,
errorInfo,
onRetry: this.clearError.bind(this)
});
}
return this.props.children;
}
};