@ossido-labs/ossido-router 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"RouteMatch.js","names":[],"sources":["../../../src/components/RouteMatch.tsx"],"sourcesContent":["import type { JSX } from 'react';\nimport { memo, Suspense, useMemo } from 'react';\n\nimport { DefaultLoading, DefaultError, DevErrorReporter } from '@ossido-labs/ossido-ui';\n\nimport type { Mode } from '../types';\nimport type { Route } from '../route';\nimport { buildResourceKey, readLayoutData } from '../data/resourceCache';\n\nimport { useRouterContext } from './RouterContext';\nimport { CriticalCss } from './CriticalCss';\nimport { RouteDataLoader } from './RouteDataLoader';\nimport { OssidoErrorBoundary } from './OssidoErrorBoundary';\n\ninterface RouteMatchProps {\n route: Route;\n mode?: Mode;\n}\n\n/**\n * Renders the matched route: its parent layouts wrap an error boundary +\n * `<Suspense>` boundary around the data-reading leaf. The boundary is keyed by\n * the data-resource key so a navigation (including same-route param changes)\n * remounts it and shows the loading fallback, while the layouts persist.\n */\nexport const RouteMatch = ({ route, mode }: RouteMatchProps): JSX.Element => {\n const { location, navigationId, retry } = useRouterContext();\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const routes = useMemo(() => loadParentComponents(route), [route.id]);\n\n const resourceKey = buildResourceKey(navigationId, location);\n\n const LoadingComponent = route.options.loadingComponent ?? DefaultLoading;\n // In dev the boundary reports the error to the shared store (rendered by the\n // floating DevErrorOverlayHost); production falls back to a detail-free page\n // so source/stack are never leaked to end users.\n const ErrorComponent =\n route.options.errorComponent ??\n (mode === 'Dev' ? DevErrorReporter : DefaultError);\n\n return (\n <TraverseRootComponents routes={routes} mode={mode}>\n {/* The leaf route's critical CSS must stay OUTSIDE the <Suspense> below:\n React 19 ties a boundary's reveal to any `precedence` stylesheet\n rendered inside it, which would push the streamed initial content into\n an out-of-order late chunk (empty shell paints first — a flash on\n every cold load). Rendered here it still hoists into the shell's\n <head> as render-blocking, but the boundary streams inline. */}\n <CriticalCss routeFilePath={route.filePath} mode={mode} />\n {/* The <Suspense> is not keyed by the resource key, so navigation\n re-renders (rather than remounts) the subtree. With the destination's\n data + code prefetched by `updateLocation`, that lets the new page\n appear in place with no fallback flash. The error boundary resets via\n `resetKey` instead of a key change. */}\n <Suspense fallback={<LoadingComponent />}>\n <OssidoErrorBoundary\n resetKey={resourceKey}\n fallback={ErrorComponent}\n onReset={retry}\n >\n <RouteDataLoader\n route={route}\n resourceKey={resourceKey}\n location={location}\n />\n </OssidoErrorBoundary>\n </Suspense>\n </TraverseRootComponents>\n );\n};\n\ninterface TraverseRootComponentsProps {\n routes: Array<Route>;\n children?: React.ReactNode;\n index?: number;\n mode?: Mode;\n}\n\n/**\n * Renders the layout (`layout`) components that wrap the selected route.\n * Layouts receive only `children` now — they live OUTSIDE the keyed Suspense\n * boundary, so they persist across navigation while only the page area shows\n * the loading fallback. Memoized so navigation does not re-render layouts.\n */\nconst TraverseRootComponents = memo(\n ({\n routes,\n index = 0,\n mode,\n children,\n }: TraverseRootComponentsProps): React.JSX.Element => {\n if (routes.length > index) {\n const route = routes[index] as Route;\n const Parent = route.component;\n\n // Fallback to the route id if the filePath is not defined\n // as is the case for the root route\n const routeFilePath = route.filePath || route.id;\n\n // A `layout.rs` handler's data is seeded (by SSR or the page's data fetch)\n // and read synchronously here, then spread as the layout's props alongside\n // `children`. Layouts without a data handler receive only `children`.\n const layoutProps =\n route.options.hasHandler && route.options.dataKey\n ? readLayoutData(route.options.dataKey)\n : undefined;\n\n return (\n <Parent {...layoutProps}>\n <CriticalCss routeFilePath={routeFilePath} mode={mode} />\n <TraverseRootComponents routes={routes} index={index + 1} mode={mode}>\n {children}\n </TraverseRootComponents>\n </Parent>\n );\n }\n\n return <>{children}</>;\n },\n);\nTraverseRootComponents.displayName = 'TraverseRootComponents';\n\nconst loadParentComponents = (\n route: Route,\n loader: Array<Route> = [],\n): Array<Route> => {\n const parentComponent = route.options.getParentRoute?.() as Route;\n\n loader.push(parentComponent);\n\n if (!parentComponent.isRoot) {\n return loadParentComponents(parentComponent, loader);\n }\n\n return loader.reverse();\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAyBA,MAAa,cAAc,EAAE,OAAO,WAAyC;CAC3E,MAAM,EAAE,UAAU,cAAc,UAAU,iBAAiB;CAG3D,MAAM,SAAS,cAAc,qBAAqB,KAAK,GAAG,CAAC,MAAM,EAAE,CAAC;CAEpE,MAAM,cAAc,iBAAiB,cAAc,QAAQ;CAE3D,MAAM,mBAAmB,MAAM,QAAQ,oBAAoB;CAI3D,MAAM,iBACJ,MAAM,QAAQ,mBACb,SAAS,QAAQ,mBAAmB;CAEvC,OACE,qBAAC,wBAAD;EAAgC;EAAc;YAA9C,CAOE,oBAAC,aAAD;GAAa,eAAe,MAAM;GAAgB;EAAO,IAMzD,oBAAC,UAAD;GAAU,UAAU,oBAAC,kBAAD,CAAmB;aACrC,oBAAC,qBAAD;IACE,UAAU;IACV,UAAU;IACV,SAAS;cAET,oBAAC,iBAAD;KACS;KACM;KACH;IACX;GACkB;EACb,EACY;;AAE5B;;;;;;;AAeA,MAAM,yBAAyB,MAC5B,EACC,QACA,QAAQ,GACR,MACA,eACoD;CACpD,IAAI,OAAO,SAAS,OAAO;EACzB,MAAM,QAAQ,OAAO;EACrB,MAAM,SAAS,MAAM;EAIrB,MAAM,gBAAgB,MAAM,YAAY,MAAM;EAK9C,MAAM,cACJ,MAAM,QAAQ,cAAc,MAAM,QAAQ,UACtC,eAAe,MAAM,QAAQ,OAAO,IACpC;EAEN,OACE,qBAAC,QAAD;GAAQ,GAAI;aAAZ,CACE,oBAAC,aAAD;IAA4B;IAAqB;GAAO,IACxD,oBAAC,wBAAD;IAAgC;IAAQ,OAAO,QAAQ;IAAS;IAC7D;GACqB,EAClB;;CAEZ;CAEA,OAAO,gCAAG,SAAW;AACvB,CACF;AACA,uBAAuB,cAAc;AAErC,MAAM,wBACJ,OACA,SAAuB,CAAC,MACP;CACjB,MAAM,kBAAkB,MAAM,QAAQ,iBAAiB;CAEvD,OAAO,KAAK,eAAe;CAE3B,IAAI,CAAC,gBAAgB,QACnB,OAAO,qBAAqB,iBAAiB,MAAM;CAGrD,OAAO,OAAO,QAAQ;AACxB"}
1
+ {"version":3,"file":"RouteMatch.js","names":[],"sources":["../../../src/components/RouteMatch.tsx"],"sourcesContent":["import type { JSX } from 'react';\nimport { memo, Suspense, useMemo } from 'react';\n\nimport {\n DefaultLoading,\n DefaultError,\n DevErrorReporter,\n} from '@ossido-labs/ossido-ui';\n\nimport type { Mode } from '../types';\nimport type { Route } from '../route';\nimport { buildResourceKey, readLayoutData } from '../data/resourceCache';\n\nimport { useRouterContext } from './RouterContext';\nimport { CriticalCss } from './CriticalCss';\nimport { RouteDataLoader } from './RouteDataLoader';\nimport { OssidoErrorBoundary } from './OssidoErrorBoundary';\n\ninterface RouteMatchProps {\n route: Route;\n mode?: Mode;\n}\n\n/**\n * Renders the matched route: its parent layouts wrap an error boundary +\n * `<Suspense>` boundary around the data-reading leaf. The boundary is keyed by\n * the data-resource key so a navigation (including same-route param changes)\n * remounts it and shows the loading fallback, while the layouts persist.\n */\nexport const RouteMatch = ({ route, mode }: RouteMatchProps): JSX.Element => {\n const { location, navigationId, retry } = useRouterContext();\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const routes = useMemo(() => loadParentComponents(route), [route.id]);\n\n const resourceKey = buildResourceKey(navigationId, location);\n\n const LoadingComponent = route.options.loadingComponent ?? DefaultLoading;\n // In dev the boundary reports the error to the shared store (rendered by the\n // floating DevErrorOverlayHost); production falls back to a detail-free page\n // so source/stack are never leaked to end users.\n const ErrorComponent =\n route.options.errorComponent ??\n (mode === 'Dev' ? DevErrorReporter : DefaultError);\n\n return (\n <TraverseRootComponents routes={routes} mode={mode}>\n {/* The leaf route's critical CSS must stay OUTSIDE the <Suspense> below:\n React 19 ties a boundary's reveal to any `precedence` stylesheet\n rendered inside it, which would push the streamed initial content into\n an out-of-order late chunk (empty shell paints first — a flash on\n every cold load). Rendered here it still hoists into the shell's\n <head> as render-blocking, but the boundary streams inline. */}\n <CriticalCss routeFilePath={route.filePath} mode={mode} />\n {/* The <Suspense> is not keyed by the resource key, so navigation\n re-renders (rather than remounts) the subtree. With the destination's\n data + code prefetched by `updateLocation`, that lets the new page\n appear in place with no fallback flash. The error boundary resets via\n `resetKey` instead of a key change. */}\n <Suspense fallback={<LoadingComponent />}>\n <OssidoErrorBoundary\n resetKey={resourceKey}\n fallback={ErrorComponent}\n onReset={retry}\n >\n <RouteDataLoader\n route={route}\n resourceKey={resourceKey}\n location={location}\n />\n </OssidoErrorBoundary>\n </Suspense>\n </TraverseRootComponents>\n );\n};\n\ninterface TraverseRootComponentsProps {\n routes: Array<Route>;\n children?: React.ReactNode;\n index?: number;\n mode?: Mode;\n}\n\n/**\n * Renders the layout (`layout`) components that wrap the selected route.\n * Layouts receive only `children` now — they live OUTSIDE the keyed Suspense\n * boundary, so they persist across navigation while only the page area shows\n * the loading fallback. Memoized so navigation does not re-render layouts.\n */\nconst TraverseRootComponents = memo(\n ({\n routes,\n index = 0,\n mode,\n children,\n }: TraverseRootComponentsProps): React.JSX.Element => {\n if (routes.length > index) {\n const route = routes[index] as Route;\n const Parent = route.component;\n\n // Fallback to the route id if the filePath is not defined\n // as is the case for the root route\n const routeFilePath = route.filePath || route.id;\n\n // A `layout.rs` handler's data is seeded (by SSR or the page's data fetch)\n // and read synchronously here, then spread as the layout's props alongside\n // `children`. Layouts without a data handler receive only `children`.\n const layoutProps =\n route.options.hasHandler && route.options.dataKey\n ? readLayoutData(route.options.dataKey)\n : undefined;\n\n return (\n <Parent {...layoutProps}>\n <CriticalCss routeFilePath={routeFilePath} mode={mode} />\n <TraverseRootComponents routes={routes} index={index + 1} mode={mode}>\n {children}\n </TraverseRootComponents>\n </Parent>\n );\n }\n\n return <>{children}</>;\n },\n);\nTraverseRootComponents.displayName = 'TraverseRootComponents';\n\nconst loadParentComponents = (\n route: Route,\n loader: Array<Route> = [],\n): Array<Route> => {\n const parentComponent = route.options.getParentRoute?.() as Route;\n\n loader.push(parentComponent);\n\n if (!parentComponent.isRoot) {\n return loadParentComponents(parentComponent, loader);\n }\n\n return loader.reverse();\n};\n"],"mappings":";;;;;;;;;;;;;;;;AA6BA,MAAa,cAAc,EAAE,OAAO,WAAyC;CAC3E,MAAM,EAAE,UAAU,cAAc,UAAU,iBAAiB;CAG3D,MAAM,SAAS,cAAc,qBAAqB,KAAK,GAAG,CAAC,MAAM,EAAE,CAAC;CAEpE,MAAM,cAAc,iBAAiB,cAAc,QAAQ;CAE3D,MAAM,mBAAmB,MAAM,QAAQ,oBAAoB;CAI3D,MAAM,iBACJ,MAAM,QAAQ,mBACb,SAAS,QAAQ,mBAAmB;CAEvC,OACE,qBAAC,wBAAD;EAAgC;EAAc;YAA9C,CAOE,oBAAC,aAAD;GAAa,eAAe,MAAM;GAAgB;EAAO,IAMzD,oBAAC,UAAD;GAAU,UAAU,oBAAC,kBAAD,CAAmB;aACrC,oBAAC,qBAAD;IACE,UAAU;IACV,UAAU;IACV,SAAS;cAET,oBAAC,iBAAD;KACS;KACM;KACH;IACX;GACkB;EACb,EACY;;AAE5B;;;;;;;AAeA,MAAM,yBAAyB,MAC5B,EACC,QACA,QAAQ,GACR,MACA,eACoD;CACpD,IAAI,OAAO,SAAS,OAAO;EACzB,MAAM,QAAQ,OAAO;EACrB,MAAM,SAAS,MAAM;EAIrB,MAAM,gBAAgB,MAAM,YAAY,MAAM;EAK9C,MAAM,cACJ,MAAM,QAAQ,cAAc,MAAM,QAAQ,UACtC,eAAe,MAAM,QAAQ,OAAO,IACpC;EAEN,OACE,qBAAC,QAAD;GAAQ,GAAI;aAAZ,CACE,oBAAC,aAAD;IAA4B;IAAqB;GAAO,IACxD,oBAAC,wBAAD;IAAgC;IAAQ,OAAO,QAAQ;IAAS;IAC7D;GACqB,EAClB;;CAEZ;CAEA,OAAO,gCAAG,SAAW;AACvB,CACF;AACA,uBAAuB,cAAc;AAErC,MAAM,wBACJ,OACA,SAAuB,CAAC,MACP;CACjB,MAAM,kBAAkB,MAAM,QAAQ,iBAAiB;CAEvD,OAAO,KAAK,eAAe;CAE3B,IAAI,CAAC,gBAAgB,QACnB,OAAO,qBAAqB,iBAAiB,MAAM;CAGrD,OAAO,OAAO,QAAQ;AACxB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossido-labs/ossido-router",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "dependencies": {
48
48
  "react-intersection-observer": "^9.13.0",
49
- "@ossido-labs/ossido-ui": "0.1.0"
49
+ "@ossido-labs/ossido-ui": "0.1.2"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@testing-library/react": "16.3.0",