Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Socket
Sign inDemoInstall

@remix-run/router

Package Overview
Dependencies
Maintainers
2
Versions
217
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@remix-run/router - npm Package Compare versions

Comparing version 0.2.0-pre.4 to 0.2.0-pre.5

80

CHANGELOG.md
# @remix-run/router
## 0.2.0-pre.5
### Patch Changes
- feat: Deferred API Updates (#9070)
- Support array and single promise usages
- `return deferred([ await critical(), lazy() ])`
- `return deferred(lazy())`
- Remove `Deferrable`/`ResolvedDeferrable` in favor of raw `Promise`'s and `Awaited`
- Remove generics from `useDeferredData` until `useLoaderData` generic is decided in 6.5
- feat: Add `createStaticRouter` for `@remix-run/router` SSR usage (#9013)
**Notable changes:**
- `request` is now the driving force inside the router utils, so that we can better handle `Request` instances coming form the server (as opposed to `string` and `Path` instances coming from the client)
- Removed the `signal` param from `loader` and `action` functions in favor of `request.signal`
**Example usage (Document Requests):**
```jsx
// Create a static handler
let { query } = unstable_createStaticHandler(routes);
// Perform a full-document query for the incoming Fetch Request. This will
// execute the appropriate action/loaders and return either the state or a
// Fetch Response in the case of redirects.
let state = await query(fetchRequest);
// If we received a Fetch Response back, let our server runtime handle directly
if (state instanceof Response) {
throw state;
}
// Otherwise, render our application providing the data routes and state
let html = ReactDOMServer.renderToString(
<React.StrictMode>
<DataStaticRouter routes={routes} state={state} />
</React.StrictMode>
);
```
**Example usage (Data Requests):**
```jsx
// Create a static route handler
let { queryRoute } = unstable_createStaticHandler(routes);
// Perform a single-route query for the incoming Fetch Request. This will
// execute the appropriate singular action/loader and return either the raw
// data or a Fetch Response
let data = await queryRoute(fetchRequest);
// If we received a Fetch Response back, return it directly
if (data instanceof Response) {
return data;
}
// Otherwise, construct a Response from the raw data (assuming json here)
return new Response(JSON.stringify(data), {
headers: {
"Content-Type": "application/json; charset=utf-8",
},
});
```
- feat: SSR Updates for React Router (#9058)
_Note: The Data-Router SSR aspects of `@remix-run/router` and `react-router-dom` are being released as **unstable** in this release (`unstable_createStaticHandler` and `unstable_DataStaticRouter`), and we plan to finalize them in a subsequent minor release once the kinks can be worked out with the Remix integration. To that end, they are available for use, but are subject to breaking changes in the next minor release._
- Remove `useRenderDataRouter()` in favor of `<DataRouterProvider>`/`<DataRouter>`
- Support automatic hydration in `<DataStaticRouter>`/`<DataBrowserRouter>`/`<DataHashRouter>`
- Uses `window.__staticRouterHydrationData`
- Can be disabled on the server via `<DataStaticRouter hydrate={false}>`
- Can be disabled (or overridden) in the browser by passing `hydrationData` to `<DataBrowserRouter>`/`<DataHashRouter>`
- `<DataStaticRouter>` now tracks it's own SSR error boundaries on `StaticHandlerContext`
- `StaticHandlerContext` now exposes `statusCode`/`loaderHeaders`/`actionHeaders`
- `foundMissingHydrationData` check removed since Remix routes may have loaders (for modules) that don't return data for `loaderData`
## 0.2.0-pre.4

@@ -4,0 +84,0 @@

14

dist/index.d.ts
import type { BrowserHistoryOptions, HashHistoryOptions, MemoryHistoryOptions } from "./history";
import type { Router, RouterInit } from "./router";
declare function createMemoryRouter({ initialEntries, initialIndex, ...routerInit }: MemoryHistoryOptions & Omit<RouterInit, "history">): Router;
declare function createBrowserRouter({ window, ...routerInit }: BrowserHistoryOptions & Omit<RouterInit, "history">): Router;
declare function createHashRouter({ window, ...routerInit }: HashHistoryOptions & Omit<RouterInit, "history">): Router;
export * from "./router";
import { convertRoutesToDataRoutes } from "./utils";
export type { ActionFunction, ActionFunctionArgs, DataRouteMatch, DataRouteObject, FormEncType, FormMethod, JsonFunction, LoaderFunction, LoaderFunctionArgs, ParamParseKey, Params, PathMatch, PathPattern, RedirectFunction, RouteMatch, RouteObject, ShouldRevalidateFunction, Submission, } from "./utils";
export { deferred, generatePath, getToPathname, invariant, isDeferredError, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, redirect, resolvePath, resolveTo, stripBasename, warning, } from "./utils";
export { ErrorResponse, deferred, generatePath, getToPathname, invariant, isDeferredError, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, redirect, resolvePath, resolveTo, stripBasename, warning, } from "./utils";
export type { BrowserHistory, HashHistory, History, InitialEntry, Location, MemoryHistory, Path, To, } from "./history";
export { Action, createBrowserHistory, createPath, createHashHistory, createMemoryHistory, parsePath, } from "./history";
export { createBrowserRouter, createHashRouter, createMemoryRouter };
export * from "./router";
export declare function createMemoryRouter({ initialEntries, initialIndex, ...routerInit }: MemoryHistoryOptions & Omit<RouterInit, "history">): Router;
export declare function createBrowserRouter({ window, ...routerInit }: BrowserHistoryOptions & Omit<RouterInit, "history">): Router;
export declare function createHashRouter({ window, ...routerInit }: HashHistoryOptions & Omit<RouterInit, "history">): Router;
/** @internal */
export { convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes };
import { History, Location, To } from "./history";
import { Action as HistoryAction } from "./history";
import { DataRouteMatch, DeferredData, FormEncType, FormMethod, RouteData, RouteObject } from "./utils";
import type { DataRouteMatch, DataRouteObject, FormEncType, FormMethod, RouteData, RouteObject } from "./utils";
import { DeferredData } from "./utils";
/**

@@ -13,2 +14,6 @@ * A Router instance manages all navigation and data loading/mutations

/**
* Return the routes for this router instance
*/
get routes(): DataRouteObject[];
/**
* Initialize the router, including adding history listeners and kicking off

@@ -36,11 +41,11 @@ * initial data fetches. Returns a function to cleanup listeners and abort

* Navigate forward/backward in the history stack
* @param path Delta to move in the history stack
* @param to Delta to move in the history stack
*/
navigate(path: number): void;
navigate(to: number): void;
/**
* Navigate to the given path
* @param path Path to navigate to
* @param to Path to navigate to
* @param opts Navigation options (method, submission, etc.)
*/
navigate(path: To, opts?: RouterNavigateOptions): void;
navigate(to: To, opts?: RouterNavigateOptions): void;
/**

@@ -161,2 +166,24 @@ * Trigger a fetcher load/submission

/**
* State returned from a server-side query() call
*/
export interface StaticHandlerContext {
location: RouterState["location"];
matches: RouterState["matches"];
loaderData: RouterState["loaderData"];
actionData: RouterState["actionData"];
errors: RouterState["errors"];
statusCode: number;
loaderHeaders: Record<string, Headers>;
actionHeaders: Record<string, Headers>;
_deepestRenderedBoundaryId?: string | null;
}
/**
* A StaticHandler instance manages a singular SSR navigation/fetch event
*/
export interface StaticHandler {
dataRoutes: DataRouteObject[];
query(request: Request): Promise<StaticHandlerContext | Response>;
queryRoute(request: Request, routeId?: string): Promise<any>;
}
/**
* Subscriber function signature for changes to router state

@@ -269,2 +296,8 @@ */

export declare function createRouter(init: RouterInit): Router;
export declare function unstable_createStaticHandler(routes: RouteObject[]): StaticHandler;
/**
* Given an existing StaticHandlerContext and an error thrown at render time,
* provide an updated StaticHandlerContext suitable for a second SSR render
*/
export declare function getStaticContextFromError(routes: DataRouteObject[], context: StaticHandlerContext, error: any): StaticHandlerContext;
export {};

@@ -23,2 +23,4 @@ /// <reference types="react" />

data: any;
statusCode?: number;
headers?: Headers;
}

@@ -73,3 +75,2 @@ /**

params: Params;
signal: AbortSignal;
}

@@ -175,2 +176,3 @@ /**

}
export declare function convertRoutesToDataRoutes(routes: RouteObject[], parentPath?: number[], allIds?: Set<string>): DataRouteObject[];
/**

@@ -285,2 +287,3 @@ * Matches the given routes to a location and returns the match data.

export declare const json: JsonFunction;
declare type DeferredInput = Record<string, unknown> | Array<unknown> | Promise<unknown>;
export declare class DeferredData {

@@ -290,6 +293,7 @@ private pendingKeys;

private subscriber?;
data: RouteData;
constructor(data: Record<string, any>);
data: DeferredInput | unknown;
constructor(data: DeferredInput);
private trackPromise;
private onSettle;
subscribe(fn: (aborted: boolean, key?: string, data?: any) => void): void;
subscribe(fn: (aborted: boolean) => void): void;
cancel(): void;

@@ -309,3 +313,3 @@ get done(): boolean;

export declare function isDeferredError(e: any): e is DeferredError;
export declare function deferred(data: Record<string, any>): DeferredData;
export declare function deferred(data: DeferredInput): DeferredData;
export declare type RedirectFunction = (url: string, init?: number | ResponseInit) => Response;

@@ -312,0 +316,0 @@ /**

@@ -13,30 +13,4 @@ import type {

import { createRouter } from "./router";
import { convertRoutesToDataRoutes } from "./utils";
function createMemoryRouter({
initialEntries,
initialIndex,
...routerInit
}: MemoryHistoryOptions & Omit<RouterInit, "history">): Router {
let history = createMemoryHistory({ initialEntries, initialIndex });
return createRouter({ history, ...routerInit });
}
function createBrowserRouter({
window,
...routerInit
}: BrowserHistoryOptions & Omit<RouterInit, "history">): Router {
let history = createBrowserHistory({ window });
return createRouter({ history, ...routerInit });
}
function createHashRouter({
window,
...routerInit
}: HashHistoryOptions & Omit<RouterInit, "history">): Router {
let history = createHashHistory({ window });
return createRouter({ history, ...routerInit });
}
export * from "./router";
export type {

@@ -64,2 +38,3 @@ ActionFunction,

export {
ErrorResponse,
deferred,

@@ -103,2 +78,37 @@ generatePath,

export { createBrowserRouter, createHashRouter, createMemoryRouter };
export * from "./router";
export function createMemoryRouter({
initialEntries,
initialIndex,
...routerInit
}: MemoryHistoryOptions & Omit<RouterInit, "history">): Router {
let history = createMemoryHistory({ initialEntries, initialIndex });
return createRouter({ history, ...routerInit });
}
export function createBrowserRouter({
window,
...routerInit
}: BrowserHistoryOptions & Omit<RouterInit, "history">): Router {
let history = createBrowserHistory({ window });
return createRouter({ history, ...routerInit });
}
export function createHashRouter({
window,
...routerInit
}: HashHistoryOptions & Omit<RouterInit, "history">): Router {
let history = createHashHistory({ window });
return createRouter({ history, ...routerInit });
}
///////////////////////////////////////////////////////////////////////////////
// DANGER! PLEASE READ ME!
// We consider these exports an implementation detail and do not guarantee
// against any breaking changes, regardless of the semver release. Use with
// extreme caution and only if you understand the consequences. Godspeed.
///////////////////////////////////////////////////////////////////////////////
/** @internal */
export { convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes };
{
"name": "@remix-run/router",
"version": "0.2.0-pre.4",
"version": "0.2.0-pre.5",
"description": "Nested/Data-driven/Framework-agnostic Routing",

@@ -5,0 +5,0 @@ "keywords": [

@@ -26,2 +26,4 @@ import type { Location, Path, To } from "./history";

data: any;
statusCode?: number;
headers?: Headers;
}

@@ -89,3 +91,2 @@

params: Params;
signal: AbortSignal;
}

@@ -244,2 +245,29 @@

// Walk the route tree generating unique IDs where necessary so we are working
// solely with DataRouteObject's within the Router
export function convertRoutesToDataRoutes(
routes: RouteObject[],
parentPath: number[] = [],
allIds: Set<string> = new Set<string>()
): DataRouteObject[] {
return routes.map((route, index) => {
let treePath = [...parentPath, index];
let id = typeof route.id === "string" ? route.id : treePath.join("-");
invariant(
!allIds.has(id),
`Found a route id collision on id "${id}". Route ` +
"id's must be globally unique within Data Router usages"
);
allIds.add(id);
let dataRoute: DataRouteObject = {
...route,
id,
children: route.children
? convertRoutesToDataRoutes(route.children, treePath, allIds)
: undefined,
};
return dataRoute;
});
}
/**

@@ -858,24 +886,45 @@ * Matches the given routes to a location and returns the match data.

type DeferredInput =
| Record<string, unknown>
| Array<unknown>
| Promise<unknown>;
export class DeferredData {
private pendingKeys: Set<string> = new Set<string>();
private pendingKeys: Set<string | number> = new Set<string | number>();
private cancelled: boolean = false;
private subscriber?: (aborted: boolean, key?: string, data?: any) => void =
undefined;
data: RouteData = {};
private subscriber?: (aborted: boolean) => void = undefined;
data: DeferredInput | unknown;
constructor(data: Record<string, any>) {
Object.entries(data).forEach(([key, value]) => {
// Store all data in our internal copy and track promise keys
this.data[key] = value;
if (value instanceof Promise) {
this.pendingKeys.add(key);
value.then(
(data) => this.onSettle(key, null, data),
(error) => this.onSettle(key, error)
);
}
});
constructor(data: DeferredInput) {
// Store all data in our internal copy and track promise keys
if (data instanceof Promise) {
this.data = data;
this.trackPromise("__single__", data);
} else if (Array.isArray(data)) {
this.data = [...data];
data.forEach((value, index) => this.trackPromise(index, value));
} else if (typeof data === "object") {
this.data = { ...data };
Object.entries(data).forEach(([key, value]) =>
this.trackPromise(key, value)
);
} else {
invariant(false, "Incorrect data type passed to deferred()");
}
}
private onSettle(key: string, error: any, data?: any) {
private trackPromise(
key: string | number,
value: Promise<unknown> | unknown
) {
if (value instanceof Promise) {
this.pendingKeys.add(key);
value.then(
(data) => this.onSettle(key, null, data as unknown),
(error) => this.onSettle(key, error as unknown)
);
}
}
private onSettle(key: string | number, error: unknown, data?: unknown) {
if (this.cancelled) {

@@ -885,8 +934,25 @@ return;

this.pendingKeys.delete(key);
let value = error ? new DeferredError(error) : data;
this.data[key] = value;
this.subscriber?.(false, key, value);
let value = error ? new DeferredError(error as string) : data;
if (this.data instanceof Promise) {
this.data = value;
} else if (Array.isArray(this.data)) {
invariant(typeof key === "number", "expected key to be a number");
let data = [...this.data];
data[key] = value;
this.data = data;
} else if (typeof this.data === "object") {
this.data = {
...this.data,
[key]: value,
};
} else {
invariant(false, "Incorrect data type on DeferredData");
}
this.subscriber?.(false);
}
subscribe(fn: (aborted: boolean, key?: string, data?: any) => void) {
subscribe(fn: (aborted: boolean) => void) {
this.subscriber = fn;

@@ -920,3 +986,3 @@ }

export function deferred(data: Record<string, any>) {
export function deferred(data: DeferredInput) {
return new DeferredData(data);

@@ -923,0 +989,0 @@ }

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc