UNPKG

one

Version:

One is a new React Framework that makes Vite serve both native and web.

469 lines (467 loc) 17.6 kB
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( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. 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_elements = require("@react-navigation/elements"); var import_react = __toESM(require("react"), 1); var import_react_native = require("react-native-web"); var import_ServerContextScript = require("../server/ServerContextScript.cjs"); var import_getPageExport = require("../utils/getPageExport.cjs"); var import_EmptyRoute = require("../views/EmptyRoute.cjs"); var import_Try = require("../views/Try.cjs"); var import_DevHead = require("../vite/DevHead.cjs"); var import_one_server_only = require("../vite/one-server-only.cjs"); var import_filterRootHTML = require("./filterRootHTML.cjs"); var import_Route = require("./Route.cjs"); var import_SpaShellContext = require("./SpaShellContext.cjs"); var import_Navigator = require("../views/Navigator.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" ? (() => { 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; })() : []; 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_elements.SafeAreaProviderCompat, { 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); if (!options?.onlyMatching) ordered.push(...entries.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, ...component }) { if (ErrorBoundary) return { default: import_react.default.forwardRef((props, ref) => { const children = import_react.default.createElement((0, import_getPageExport.getPageExport)(component) || import_EmptyRoute.EmptyRoute, { ...props, ref }); return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_Try.Try, { catch: ErrorBoundary, children }); }) }; 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 }; } return { default: (0, import_getPageExport.getPageExport)(component) }; } const qualifiedStore = /* @__PURE__ */new WeakMap(); 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 ((0, import_react.useContext)(import_SpaShellContext.SpaShellContext) && 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 = value.loadRoute(); const Component = (0, import_getPageExport.getPageExport)(fromImport(res)); 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 }); if (props.segment === "") return /* @__PURE__ */(0, import_jsx_runtime.jsx)(RootLayoutRenderer, { LayoutComponent: Component, layoutProps: { ...props, ...slotProps }, forwardedRef: ref }); return /* @__PURE__ */(0, import_jsx_runtime.jsx)(RouteErrorBoundary, { routeName: value.route, children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(Component, { ...props, ...slotProps, ref }) }); }); const wrapSuspense = children => { if (process.env.ONE_SUSPEND_ROUTES === "1" && globalThis.__ONE_DISABLE_SUSPENSE_ROUTES__ !== true) return /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: null, children }); return children; }; const QualifiedRoute = import_react.default.forwardRef(({ route, navigation, ...props }, ref) => { 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.jsx)(ScreenComponent, { ...props, ref, segment: value.route })) }) }); }); QualifiedRoute.displayName = `Route(${value.route})`; qualifiedStore.set(value, QualifiedRoute); return (0, import_react.memo)(QualifiedRoute); } 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 } = {}) { return /* @__PURE__ */(0, import_react2.createElement)(Screen, { getId: createGetIdForRoute(route), ...props, name: route.route, key: route.route, 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 }; class RouteErrorBoundary 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 }); } 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_react_native.SafeAreaView, { style: { backgroundColor: "#000" }, children: /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_react_native.View, { style: { margin: 16, gap: 16 }, children: [/* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_react_native.Text, { style: { alignSelf: "flex-start", padding: 5, margin: -5, backgroundColor: "red", color: "white", fontSize: 20, fontFamily: "monospace" }, children: ["Error on route \"", this.props.routeName, "\""] }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_react_native.Text, { style: { color: "white", fontSize: 16, fontFamily: "monospace" }, children: error instanceof Error ? error.message : error }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_react_native.TouchableOpacity, { onPress: this.clearError.bind(this), children: /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_react_native.Text, { style: { alignSelf: "flex-start", margin: -6, padding: 6, backgroundColor: "white", color: "black", fontSize: 14, fontFamily: "monospace" }, children: "Retry" }) }), /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_react_native.ScrollView, { contentContainerStyle: { gap: 12 }, children: [error instanceof Error ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(import_react_native.Text, { style: { color: "white", fontSize: 12, fontFamily: "monospace" }, children: error.stack }) : null, errorInfo?.componentStack ? /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_react_native.Text, { style: { color: "white", fontSize: 12, fontFamily: "monospace" }, children: ["Component Stack: ", errorInfo.componentStack] }) : null] })] }) }); } return this.props.children; } }