remix-utils
Advanced tools
Comparing version 5.3.0 to 6.0.0
@@ -1,2 +0,1 @@ | ||
import type { PromiseValue } from "type-fest"; | ||
/** | ||
@@ -7,3 +6,3 @@ * @see https://twitter.com/buildsghost/status/1507109734519750680 | ||
export declare type AwaitedPromiseHash<Hash extends PromiseHash> = { | ||
[Key in keyof Hash]: PromiseValue<Hash[Key]>; | ||
[Key in keyof Hash]: Awaited<Hash[Key]>; | ||
}; | ||
@@ -14,3 +13,3 @@ /** | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return json( | ||
@@ -22,5 +21,5 @@ * promiseHash({ | ||
* ); | ||
* }; | ||
* } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return json( | ||
@@ -38,4 +37,4 @@ * promiseHash({ | ||
* ); | ||
* }; | ||
* } | ||
*/ | ||
export declare function promiseHash<Hash extends PromiseHash>(hash: Hash): Promise<AwaitedPromiseHash<Hash>>; |
@@ -5,3 +5,3 @@ /** | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return json( | ||
@@ -13,5 +13,5 @@ * promiseHash({ | ||
* ); | ||
* }; | ||
* } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return json( | ||
@@ -29,3 +29,3 @@ * promiseHash({ | ||
* ); | ||
* }; | ||
* } | ||
*/ | ||
@@ -32,0 +32,0 @@ export async function promiseHash(hash) { |
@@ -8,3 +8,2 @@ export * from "./react/cache-assets"; | ||
export * from "./react/structured-data"; | ||
export * from "./react/use-data-refresh"; | ||
export * from "./react/use-event-source"; | ||
@@ -14,3 +13,2 @@ export * from "./react/use-global-pending-state"; | ||
export * from "./react/use-locales"; | ||
export * from "./react/use-route-data"; | ||
export * from "./react/use-should-hydrate"; |
@@ -8,3 +8,2 @@ export * from "./react/cache-assets"; | ||
export * from "./react/structured-data"; | ||
export * from "./react/use-data-refresh"; | ||
export * from "./react/use-event-source"; | ||
@@ -14,3 +13,2 @@ export * from "./react/use-global-pending-state"; | ||
export * from "./react/use-locales"; | ||
export * from "./react/use-route-data"; | ||
export * from "./react/use-should-hydrate"; |
@@ -11,3 +11,3 @@ import type { Locales } from "../server/get-client-locales"; | ||
* // in the root loader | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let locales = getClientLocales(request); | ||
@@ -17,3 +17,3 @@ * return json({ locales }); | ||
* // in any route (including root!) | ||
* export default function Screen() { | ||
* export default function Component() { | ||
* let locales = useLocales(); | ||
@@ -20,0 +20,0 @@ * let date = new Date(); |
@@ -11,3 +11,3 @@ import { useMatches } from "@remix-run/react"; | ||
* // in the root loader | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let locales = getClientLocales(request); | ||
@@ -17,3 +17,3 @@ * return json({ locales }); | ||
* // in any route (including root!) | ||
* export default function Screen() { | ||
* export default function Component() { | ||
* let locales = useLocales(); | ||
@@ -20,0 +20,0 @@ * let date = new Date(); |
@@ -8,6 +8,8 @@ export * from "./server/cors"; | ||
export * from "./server/named-action"; | ||
export * from "./server/preload-route-assets"; | ||
export * from "./server/json-hash"; | ||
export * from "./server/responses"; | ||
export * from "./server/rolling-cookie"; | ||
export * from "./server/safe-redirect"; | ||
export * from "./server/typed-cookie"; | ||
export * from "./server/typed-session"; | ||
export * from "./server/preload-route-assets"; |
@@ -8,6 +8,8 @@ export * from "./server/cors"; | ||
export * from "./server/named-action"; | ||
export * from "./server/preload-route-assets"; | ||
export * from "./server/json-hash"; | ||
export * from "./server/responses"; | ||
export * from "./server/rolling-cookie"; | ||
export * from "./server/safe-redirect"; | ||
export * from "./server/typed-cookie"; | ||
export * from "./server/typed-session"; | ||
export * from "./server/preload-route-assets"; |
@@ -64,3 +64,3 @@ import { Promisable } from "type-fest"; | ||
* // Create a response, then setup CORS for it | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -72,3 +72,3 @@ * let response = json<LoaderData>(data); | ||
* // Create response and setup CORS in a single line | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -87,3 +87,3 @@ * return await cors(request, json<LoaderData>(data)); | ||
* // Pass a configuration object to setup CORS | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -96,3 +96,3 @@ * return await cors(request, json<LoaderData>(data), { | ||
* // Mutate response and then return it | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -99,0 +99,0 @@ * let response = json<LoaderData>(data); |
@@ -144,3 +144,3 @@ const DEFAULT_OPTIONS = { | ||
* // Create a response, then setup CORS for it | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -152,3 +152,3 @@ * let response = json<LoaderData>(data); | ||
* // Create response and setup CORS in a single line | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -167,3 +167,3 @@ * return await cors(request, json<LoaderData>(data)); | ||
* // Pass a configuration object to setup CORS | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -176,3 +176,3 @@ * return await cors(request, json<LoaderData>(data), { | ||
* // Mutate response and then return it | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -179,0 +179,0 @@ * let response = json<LoaderData>(data); |
@@ -17,3 +17,3 @@ import { Session } from "@remix-run/server-runtime"; | ||
* @example | ||
* let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let session = await getSession(request.headers.get("Cookie")); | ||
@@ -24,3 +24,3 @@ * await verifyAuthenticityToken(request, session); | ||
* @example | ||
* let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let session = await getSession(request.headers.get("Cookie")); | ||
@@ -31,3 +31,3 @@ * await verifyAuthenticityToken(request, session, "csrfToken"); | ||
* @example | ||
* let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let session = await getSession(request.headers.get("Cookie")); | ||
@@ -34,0 +34,0 @@ * let formData = await unstable_parseMultipartFormData(request, uploadHandler); |
@@ -25,3 +25,3 @@ import { v4 as uuid } from "uuid"; | ||
* @example | ||
* let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let session = await getSession(request.headers.get("Cookie")); | ||
@@ -32,3 +32,3 @@ * await verifyAuthenticityToken(request, session); | ||
* @example | ||
* let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let session = await getSession(request.headers.get("Cookie")); | ||
@@ -39,3 +39,3 @@ * await verifyAuthenticityToken(request, session, "csrfToken"); | ||
* @example | ||
* let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let session = await getSession(request.headers.get("Cookie")); | ||
@@ -42,0 +42,0 @@ * let formData = await unstable_parseMultipartFormData(request, uploadHandler); |
@@ -9,3 +9,3 @@ export declare type Locales = string[] | undefined; | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let locales = getClientLocales(request) | ||
@@ -12,0 +12,0 @@ * let date = new Date().toLocaleDateString(locales, { |
@@ -6,3 +6,3 @@ /** | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request) | ||
@@ -9,0 +9,0 @@ * let headers = new Headers() |
@@ -1,2 +0,7 @@ | ||
declare type Actions = Record<string, () => Promise<Response>>; | ||
import type { TypedResponse } from "@remix-run/server-runtime"; | ||
declare type ActionsRecord = Record<string, () => Promise<TypedResponse<unknown>>>; | ||
declare type ResponsesRecord<Actions extends ActionsRecord> = { | ||
[Action in keyof Actions]: Actions[Action] extends () => Promise<TypedResponse<infer Result>> ? Result : never; | ||
}; | ||
declare type ResponsesUnion<Actions extends ActionsRecord> = ResponsesRecord<Actions>[keyof Actions]; | ||
/** | ||
@@ -10,6 +15,6 @@ * Runs an action based on the request's action name | ||
*/ | ||
export declare function namedAction(request: Request, actions: Actions): Promise<Response>; | ||
export declare function namedAction(url: URL, actions: Actions): Promise<Response>; | ||
export declare function namedAction(searchParams: URLSearchParams, actions: Actions): Promise<Response>; | ||
export declare function namedAction(formData: FormData, actions: Actions): Promise<Response>; | ||
export declare function namedAction<Actions extends ActionsRecord>(request: Request, actions: Actions): Promise<TypedResponse<ResponsesUnion<Actions>>>; | ||
export declare function namedAction<Actions extends ActionsRecord>(url: URL, actions: Actions): Promise<TypedResponse<ResponsesUnion<Actions>>>; | ||
export declare function namedAction<Actions extends ActionsRecord>(searchParams: URLSearchParams, actions: Actions): Promise<TypedResponse<ResponsesUnion<Actions>>>; | ||
export declare function namedAction<Actions extends ActionsRecord>(formData: FormData, actions: Actions): Promise<TypedResponse<ResponsesUnion<Actions>>>; | ||
export {}; |
export async function namedAction(input, actions) { | ||
let name = await getActionName(input); | ||
if (name && name in actions) | ||
if (name && name in actions) { | ||
return actions[name](); | ||
if (name === null && "default" in actions) | ||
} | ||
if (name === null && "default" in actions) { | ||
return actions["default"](); | ||
} | ||
if (name === null) | ||
@@ -8,0 +10,0 @@ throw new ReferenceError("Action name not found"); |
@@ -57,3 +57,3 @@ /** | ||
export function preloadLinkedAssets(context, headers) { | ||
let links = context.matches | ||
let links = context.staticHandlerContext.matches | ||
.flatMap((match) => { | ||
@@ -115,4 +115,5 @@ let route = context.routeModules[match.route.id]; | ||
]; | ||
for (let match of context.matches) { | ||
urls.push(match.route.module, ...((_a = match.route.imports) !== null && _a !== void 0 ? _a : [])); | ||
for (let match of context.staticHandlerContext.matches) { | ||
let route = context.manifest.routes[match.route.id]; | ||
urls.push(route.module, ...((_a = route.imports) !== null && _a !== void 0 ? _a : [])); | ||
} | ||
@@ -119,0 +120,0 @@ for (let url of urls) { |
@@ -5,3 +5,3 @@ /// <reference types="node" /> | ||
* @example | ||
* export let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let result = await doSomething(request); | ||
@@ -18,3 +18,3 @@ * return created(result); | ||
* @example | ||
* export let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* await doSomething(request); | ||
@@ -33,3 +33,3 @@ * // If the user was on `/search?query=something` we redirect to that URL | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -43,3 +43,3 @@ * throw badRequest<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -53,3 +53,3 @@ * throw unauthorized<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -63,3 +63,3 @@ * if (!user.idAdmin) throw forbidden<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request, params }) => { | ||
* export async function loader({ request, params }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -73,3 +73,3 @@ * if (!db.exists(params.id)) throw notFound<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request, params }) => { | ||
* export async function loader({ request, params }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -83,3 +83,3 @@ * throw unprocessableEntity<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -94,3 +94,3 @@ * throw serverError<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return notModified(); | ||
@@ -107,3 +107,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return javascript("console.log('Hello World')"); | ||
@@ -120,3 +120,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return css("body { color: red; }"); | ||
@@ -133,3 +133,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return pdf(await generatePDF(request.formData())); | ||
@@ -146,3 +146,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return html("<h1>Hello World</h1>"); | ||
@@ -152,2 +152,29 @@ * } | ||
export declare function html(content: string, init?: number | ResponseInit): Response; | ||
/** | ||
* Create a response with a XML file response. | ||
* It receives a string with the XML content and set the Content-Type header to | ||
* `application/xml; charset=utf-8` always. | ||
* | ||
* This is useful to dynamically create a XML file from a Resource Route. | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* return xml("<?xml version='1.0'?><catalog></catalog>"); | ||
* } | ||
*/ | ||
export declare function xml(content: string, init?: number | ResponseInit): Response; | ||
/** | ||
* Create a response with a TXT file response. | ||
* It receives a string with the TXT content and set the Content-Type header to | ||
* `text/plain; charset=utf-8` always. | ||
* | ||
* This is useful to dynamically create a TXT file from a Resource Route. | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* return txt(` | ||
* User-agent: * | ||
* Allow: / | ||
* `); | ||
* } | ||
*/ | ||
export declare function txt(content: string, init?: number | ResponseInit): Response; | ||
export declare type ImageType = "image/jpeg" | "image/png" | "image/gif" | "image/svg+xml" | "image/webp" | "image/bmp" | "image/avif"; | ||
@@ -161,3 +188,3 @@ /** | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return image(await takeScreenshot(), { type: "image/avif" }); | ||
@@ -164,0 +191,0 @@ * } |
@@ -5,3 +5,3 @@ import { json, redirect } from "@remix-run/server-runtime"; | ||
* @example | ||
* export let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let result = await doSomething(request); | ||
@@ -20,3 +20,3 @@ * return created(result); | ||
* @example | ||
* export let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* await doSomething(request); | ||
@@ -36,3 +36,3 @@ * // If the user was on `/search?query=something` we redirect to that URL | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -48,3 +48,3 @@ * throw badRequest<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -60,3 +60,3 @@ * throw unauthorized<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -72,3 +72,3 @@ * if (!user.idAdmin) throw forbidden<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request, params }) => { | ||
* export async function loader({ request, params }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -84,3 +84,3 @@ * if (!db.exists(params.id)) throw notFound<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request, params }) => { | ||
* export async function loader({ request, params }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -96,3 +96,3 @@ * throw unprocessableEntity<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -109,3 +109,3 @@ * throw serverError<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return notModified(); | ||
@@ -124,3 +124,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return javascript("console.log('Hello World')"); | ||
@@ -147,3 +147,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return css("body { color: red; }"); | ||
@@ -170,3 +170,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return pdf(await generatePDF(request.formData())); | ||
@@ -193,3 +193,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return html("<h1>Hello World</h1>"); | ||
@@ -210,2 +210,49 @@ * } | ||
/** | ||
* Create a response with a XML file response. | ||
* It receives a string with the XML content and set the Content-Type header to | ||
* `application/xml; charset=utf-8` always. | ||
* | ||
* This is useful to dynamically create a XML file from a Resource Route. | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* return xml("<?xml version='1.0'?><catalog></catalog>"); | ||
* } | ||
*/ | ||
export function xml(content, init = {}) { | ||
let responseInit = typeof init === "number" ? { status: init } : init; | ||
let headers = new Headers(responseInit.headers); | ||
if (!headers.has("Content-Type")) { | ||
headers.set("Content-Type", "application/xml; charset=utf-8"); | ||
} | ||
return new Response(content, { | ||
...responseInit, | ||
headers, | ||
}); | ||
} | ||
/** | ||
* Create a response with a TXT file response. | ||
* It receives a string with the TXT content and set the Content-Type header to | ||
* `text/plain; charset=utf-8` always. | ||
* | ||
* This is useful to dynamically create a TXT file from a Resource Route. | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* return txt(` | ||
* User-agent: * | ||
* Allow: / | ||
* `); | ||
* } | ||
*/ | ||
export function txt(content, init = {}) { | ||
let responseInit = typeof init === "number" ? { status: init } : init; | ||
let headers = new Headers(responseInit.headers); | ||
if (!headers.has("Content-Type")) { | ||
headers.set("Content-Type", "text/plain; charset=utf-8"); | ||
} | ||
return new Response(content, { | ||
...responseInit, | ||
headers, | ||
}); | ||
} | ||
/** | ||
* Create a response with a image file response. | ||
@@ -217,3 +264,3 @@ * It receives a Buffer, ArrayBuffer or ReadableStream with the image content | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return image(await takeScreenshot(), { type: "image/avif" }); | ||
@@ -220,0 +267,0 @@ * } |
@@ -1,2 +0,1 @@ | ||
import type { PromiseValue } from "type-fest"; | ||
/** | ||
@@ -7,3 +6,3 @@ * @see https://twitter.com/buildsghost/status/1507109734519750680 | ||
export declare type AwaitedPromiseHash<Hash extends PromiseHash> = { | ||
[Key in keyof Hash]: PromiseValue<Hash[Key]>; | ||
[Key in keyof Hash]: Awaited<Hash[Key]>; | ||
}; | ||
@@ -14,3 +13,3 @@ /** | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return json( | ||
@@ -22,5 +21,5 @@ * promiseHash({ | ||
* ); | ||
* }; | ||
* } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return json( | ||
@@ -38,4 +37,4 @@ * promiseHash({ | ||
* ); | ||
* }; | ||
* } | ||
*/ | ||
export declare function promiseHash<Hash extends PromiseHash>(hash: Hash): Promise<AwaitedPromiseHash<Hash>>; |
@@ -8,3 +8,3 @@ "use strict"; | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return json( | ||
@@ -16,5 +16,5 @@ * promiseHash({ | ||
* ); | ||
* }; | ||
* } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return json( | ||
@@ -32,3 +32,3 @@ * promiseHash({ | ||
* ); | ||
* }; | ||
* } | ||
*/ | ||
@@ -35,0 +35,0 @@ async function promiseHash(hash) { |
@@ -8,3 +8,2 @@ export * from "./react/cache-assets"; | ||
export * from "./react/structured-data"; | ||
export * from "./react/use-data-refresh"; | ||
export * from "./react/use-event-source"; | ||
@@ -14,3 +13,2 @@ export * from "./react/use-global-pending-state"; | ||
export * from "./react/use-locales"; | ||
export * from "./react/use-route-data"; | ||
export * from "./react/use-should-hydrate"; |
@@ -24,3 +24,2 @@ "use strict"; | ||
__exportStar(require("./react/structured-data"), exports); | ||
__exportStar(require("./react/use-data-refresh"), exports); | ||
__exportStar(require("./react/use-event-source"), exports); | ||
@@ -30,3 +29,2 @@ __exportStar(require("./react/use-global-pending-state"), exports); | ||
__exportStar(require("./react/use-locales"), exports); | ||
__exportStar(require("./react/use-route-data"), exports); | ||
__exportStar(require("./react/use-should-hydrate"), exports); |
@@ -11,3 +11,3 @@ import type { Locales } from "../server/get-client-locales"; | ||
* // in the root loader | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let locales = getClientLocales(request); | ||
@@ -17,3 +17,3 @@ * return json({ locales }); | ||
* // in any route (including root!) | ||
* export default function Screen() { | ||
* export default function Component() { | ||
* let locales = useLocales(); | ||
@@ -20,0 +20,0 @@ * let date = new Date(); |
@@ -14,3 +14,3 @@ "use strict"; | ||
* // in the root loader | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let locales = getClientLocales(request); | ||
@@ -20,3 +20,3 @@ * return json({ locales }); | ||
* // in any route (including root!) | ||
* export default function Screen() { | ||
* export default function Component() { | ||
* let locales = useLocales(); | ||
@@ -23,0 +23,0 @@ * let date = new Date(); |
@@ -8,6 +8,8 @@ export * from "./server/cors"; | ||
export * from "./server/named-action"; | ||
export * from "./server/preload-route-assets"; | ||
export * from "./server/json-hash"; | ||
export * from "./server/responses"; | ||
export * from "./server/rolling-cookie"; | ||
export * from "./server/safe-redirect"; | ||
export * from "./server/typed-cookie"; | ||
export * from "./server/typed-session"; | ||
export * from "./server/preload-route-assets"; |
@@ -24,6 +24,8 @@ "use strict"; | ||
__exportStar(require("./server/named-action"), exports); | ||
__exportStar(require("./server/preload-route-assets"), exports); | ||
__exportStar(require("./server/json-hash"), exports); | ||
__exportStar(require("./server/responses"), exports); | ||
__exportStar(require("./server/rolling-cookie"), exports); | ||
__exportStar(require("./server/safe-redirect"), exports); | ||
__exportStar(require("./server/typed-cookie"), exports); | ||
__exportStar(require("./server/typed-session"), exports); | ||
__exportStar(require("./server/preload-route-assets"), exports); |
@@ -64,3 +64,3 @@ import { Promisable } from "type-fest"; | ||
* // Create a response, then setup CORS for it | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -72,3 +72,3 @@ * let response = json<LoaderData>(data); | ||
* // Create response and setup CORS in a single line | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -87,3 +87,3 @@ * return await cors(request, json<LoaderData>(data)); | ||
* // Pass a configuration object to setup CORS | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -96,3 +96,3 @@ * return await cors(request, json<LoaderData>(data), { | ||
* // Mutate response and then return it | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -99,0 +99,0 @@ * let response = json<LoaderData>(data); |
@@ -147,3 +147,3 @@ "use strict"; | ||
* // Create a response, then setup CORS for it | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -155,3 +155,3 @@ * let response = json<LoaderData>(data); | ||
* // Create response and setup CORS in a single line | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -170,3 +170,3 @@ * return await cors(request, json<LoaderData>(data)); | ||
* // Pass a configuration object to setup CORS | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -179,3 +179,3 @@ * return await cors(request, json<LoaderData>(data), { | ||
* // Mutate response and then return it | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request); | ||
@@ -182,0 +182,0 @@ * let response = json<LoaderData>(data); |
@@ -17,3 +17,3 @@ import { Session } from "@remix-run/server-runtime"; | ||
* @example | ||
* let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let session = await getSession(request.headers.get("Cookie")); | ||
@@ -24,3 +24,3 @@ * await verifyAuthenticityToken(request, session); | ||
* @example | ||
* let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let session = await getSession(request.headers.get("Cookie")); | ||
@@ -31,3 +31,3 @@ * await verifyAuthenticityToken(request, session, "csrfToken"); | ||
* @example | ||
* let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let session = await getSession(request.headers.get("Cookie")); | ||
@@ -34,0 +34,0 @@ * let formData = await unstable_parseMultipartFormData(request, uploadHandler); |
@@ -29,3 +29,3 @@ "use strict"; | ||
* @example | ||
* let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let session = await getSession(request.headers.get("Cookie")); | ||
@@ -36,3 +36,3 @@ * await verifyAuthenticityToken(request, session); | ||
* @example | ||
* let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let session = await getSession(request.headers.get("Cookie")); | ||
@@ -43,3 +43,3 @@ * await verifyAuthenticityToken(request, session, "csrfToken"); | ||
* @example | ||
* let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let session = await getSession(request.headers.get("Cookie")); | ||
@@ -46,0 +46,0 @@ * let formData = await unstable_parseMultipartFormData(request, uploadHandler); |
@@ -9,3 +9,3 @@ export declare type Locales = string[] | undefined; | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let locales = getClientLocales(request) | ||
@@ -12,0 +12,0 @@ * let date = new Date().toLocaleDateString(locales, { |
@@ -6,3 +6,3 @@ /** | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let data = await getData(request) | ||
@@ -9,0 +9,0 @@ * let headers = new Headers() |
@@ -1,2 +0,7 @@ | ||
declare type Actions = Record<string, () => Promise<Response>>; | ||
import type { TypedResponse } from "@remix-run/server-runtime"; | ||
declare type ActionsRecord = Record<string, () => Promise<TypedResponse<unknown>>>; | ||
declare type ResponsesRecord<Actions extends ActionsRecord> = { | ||
[Action in keyof Actions]: Actions[Action] extends () => Promise<TypedResponse<infer Result>> ? Result : never; | ||
}; | ||
declare type ResponsesUnion<Actions extends ActionsRecord> = ResponsesRecord<Actions>[keyof Actions]; | ||
/** | ||
@@ -10,6 +15,6 @@ * Runs an action based on the request's action name | ||
*/ | ||
export declare function namedAction(request: Request, actions: Actions): Promise<Response>; | ||
export declare function namedAction(url: URL, actions: Actions): Promise<Response>; | ||
export declare function namedAction(searchParams: URLSearchParams, actions: Actions): Promise<Response>; | ||
export declare function namedAction(formData: FormData, actions: Actions): Promise<Response>; | ||
export declare function namedAction<Actions extends ActionsRecord>(request: Request, actions: Actions): Promise<TypedResponse<ResponsesUnion<Actions>>>; | ||
export declare function namedAction<Actions extends ActionsRecord>(url: URL, actions: Actions): Promise<TypedResponse<ResponsesUnion<Actions>>>; | ||
export declare function namedAction<Actions extends ActionsRecord>(searchParams: URLSearchParams, actions: Actions): Promise<TypedResponse<ResponsesUnion<Actions>>>; | ||
export declare function namedAction<Actions extends ActionsRecord>(formData: FormData, actions: Actions): Promise<TypedResponse<ResponsesUnion<Actions>>>; | ||
export {}; |
@@ -6,6 +6,8 @@ "use strict"; | ||
let name = await getActionName(input); | ||
if (name && name in actions) | ||
if (name && name in actions) { | ||
return actions[name](); | ||
if (name === null && "default" in actions) | ||
} | ||
if (name === null && "default" in actions) { | ||
return actions["default"](); | ||
} | ||
if (name === null) | ||
@@ -12,0 +14,0 @@ throw new ReferenceError("Action name not found"); |
@@ -61,3 +61,3 @@ "use strict"; | ||
function preloadLinkedAssets(context, headers) { | ||
let links = context.matches | ||
let links = context.staticHandlerContext.matches | ||
.flatMap((match) => { | ||
@@ -120,4 +120,5 @@ let route = context.routeModules[match.route.id]; | ||
]; | ||
for (let match of context.matches) { | ||
urls.push(match.route.module, ...((_a = match.route.imports) !== null && _a !== void 0 ? _a : [])); | ||
for (let match of context.staticHandlerContext.matches) { | ||
let route = context.manifest.routes[match.route.id]; | ||
urls.push(route.module, ...((_a = route.imports) !== null && _a !== void 0 ? _a : [])); | ||
} | ||
@@ -124,0 +125,0 @@ for (let url of urls) { |
@@ -5,3 +5,3 @@ /// <reference types="node" /> | ||
* @example | ||
* export let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let result = await doSomething(request); | ||
@@ -18,3 +18,3 @@ * return created(result); | ||
* @example | ||
* export let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* await doSomething(request); | ||
@@ -33,3 +33,3 @@ * // If the user was on `/search?query=something` we redirect to that URL | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -43,3 +43,3 @@ * throw badRequest<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -53,3 +53,3 @@ * throw unauthorized<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -63,3 +63,3 @@ * if (!user.idAdmin) throw forbidden<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request, params }) => { | ||
* export async function loader({ request, params }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -73,3 +73,3 @@ * if (!db.exists(params.id)) throw notFound<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request, params }) => { | ||
* export async function loader({ request, params }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -83,3 +83,3 @@ * throw unprocessableEntity<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -94,3 +94,3 @@ * throw serverError<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return notModified(); | ||
@@ -107,3 +107,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return javascript("console.log('Hello World')"); | ||
@@ -120,3 +120,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return css("body { color: red; }"); | ||
@@ -133,3 +133,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return pdf(await generatePDF(request.formData())); | ||
@@ -146,3 +146,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return html("<h1>Hello World</h1>"); | ||
@@ -152,2 +152,29 @@ * } | ||
export declare function html(content: string, init?: number | ResponseInit): Response; | ||
/** | ||
* Create a response with a XML file response. | ||
* It receives a string with the XML content and set the Content-Type header to | ||
* `application/xml; charset=utf-8` always. | ||
* | ||
* This is useful to dynamically create a XML file from a Resource Route. | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* return xml("<?xml version='1.0'?><catalog></catalog>"); | ||
* } | ||
*/ | ||
export declare function xml(content: string, init?: number | ResponseInit): Response; | ||
/** | ||
* Create a response with a TXT file response. | ||
* It receives a string with the TXT content and set the Content-Type header to | ||
* `text/plain; charset=utf-8` always. | ||
* | ||
* This is useful to dynamically create a TXT file from a Resource Route. | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* return txt(` | ||
* User-agent: * | ||
* Allow: / | ||
* `); | ||
* } | ||
*/ | ||
export declare function txt(content: string, init?: number | ResponseInit): Response; | ||
export declare type ImageType = "image/jpeg" | "image/png" | "image/gif" | "image/svg+xml" | "image/webp" | "image/bmp" | "image/avif"; | ||
@@ -161,3 +188,3 @@ /** | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return image(await takeScreenshot(), { type: "image/avif" }); | ||
@@ -164,0 +191,0 @@ * } |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.image = exports.html = exports.pdf = exports.stylesheet = exports.javascript = exports.notModified = exports.serverError = exports.unprocessableEntity = exports.notFound = exports.forbidden = exports.unauthorized = exports.badRequest = exports.redirectBack = exports.created = void 0; | ||
exports.image = exports.txt = exports.xml = exports.html = exports.pdf = exports.stylesheet = exports.javascript = exports.notModified = exports.serverError = exports.unprocessableEntity = exports.notFound = exports.forbidden = exports.unauthorized = exports.badRequest = exports.redirectBack = exports.created = void 0; | ||
const server_runtime_1 = require("@remix-run/server-runtime"); | ||
@@ -8,3 +8,3 @@ /** | ||
* @example | ||
* export let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* let result = await doSomething(request); | ||
@@ -24,3 +24,3 @@ * return created(result); | ||
* @example | ||
* export let action: ActionFunction = async ({ request }) => { | ||
* export async function action({ request }: ActionArgs) { | ||
* await doSomething(request); | ||
@@ -41,3 +41,3 @@ * // If the user was on `/search?query=something` we redirect to that URL | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -54,3 +54,3 @@ * throw badRequest<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -67,3 +67,3 @@ * throw unauthorized<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -80,3 +80,3 @@ * if (!user.idAdmin) throw forbidden<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request, params }) => { | ||
* export async function loader({ request, params }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -93,3 +93,3 @@ * if (!db.exists(params.id)) throw notFound<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request, params }) => { | ||
* export async function loader({ request, params }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -106,3 +106,3 @@ * throw unprocessableEntity<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* let user = await getUser(request); | ||
@@ -120,3 +120,3 @@ * throw serverError<BoundaryData>({ user }); | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return notModified(); | ||
@@ -136,3 +136,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return javascript("console.log('Hello World')"); | ||
@@ -160,3 +160,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return css("body { color: red; }"); | ||
@@ -184,3 +184,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return pdf(await generatePDF(request.formData())); | ||
@@ -208,3 +208,3 @@ * } | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return html("<h1>Hello World</h1>"); | ||
@@ -226,2 +226,51 @@ * } | ||
/** | ||
* Create a response with a XML file response. | ||
* It receives a string with the XML content and set the Content-Type header to | ||
* `application/xml; charset=utf-8` always. | ||
* | ||
* This is useful to dynamically create a XML file from a Resource Route. | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* return xml("<?xml version='1.0'?><catalog></catalog>"); | ||
* } | ||
*/ | ||
function xml(content, init = {}) { | ||
let responseInit = typeof init === "number" ? { status: init } : init; | ||
let headers = new Headers(responseInit.headers); | ||
if (!headers.has("Content-Type")) { | ||
headers.set("Content-Type", "application/xml; charset=utf-8"); | ||
} | ||
return new Response(content, { | ||
...responseInit, | ||
headers, | ||
}); | ||
} | ||
exports.xml = xml; | ||
/** | ||
* Create a response with a TXT file response. | ||
* It receives a string with the TXT content and set the Content-Type header to | ||
* `text/plain; charset=utf-8` always. | ||
* | ||
* This is useful to dynamically create a TXT file from a Resource Route. | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* return txt(` | ||
* User-agent: * | ||
* Allow: / | ||
* `); | ||
* } | ||
*/ | ||
function txt(content, init = {}) { | ||
let responseInit = typeof init === "number" ? { status: init } : init; | ||
let headers = new Headers(responseInit.headers); | ||
if (!headers.has("Content-Type")) { | ||
headers.set("Content-Type", "text/plain; charset=utf-8"); | ||
} | ||
return new Response(content, { | ||
...responseInit, | ||
headers, | ||
}); | ||
} | ||
exports.txt = txt; | ||
/** | ||
* Create a response with a image file response. | ||
@@ -233,3 +282,3 @@ * It receives a Buffer, ArrayBuffer or ReadableStream with the image content | ||
* @example | ||
* export let loader: LoaderFunction = async ({ request }) => { | ||
* export async function loader({ request }: LoaderArgs) { | ||
* return image(await takeScreenshot(), { type: "image/avif" }); | ||
@@ -236,0 +285,0 @@ * } |
{ | ||
"name": "remix-utils", | ||
"version": "5.3.0", | ||
"version": "6.0.0", | ||
"license": "MIT", | ||
@@ -43,4 +43,4 @@ "engines": { | ||
"peerDependencies": { | ||
"@remix-run/react": "^1.5.1", | ||
"@remix-run/server-runtime": "^1.5.1", | ||
"@remix-run/react": "^1.10.0", | ||
"@remix-run/server-runtime": "^1.10.0", | ||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0", | ||
@@ -55,5 +55,5 @@ "zod": "^3.19.1" | ||
"@jest/types": "^27.2.5", | ||
"@remix-run/node": "^1.5.1", | ||
"@remix-run/react": "^1.5.1", | ||
"@remix-run/server-runtime": "^1.5.1", | ||
"@remix-run/node": "^1.10.0", | ||
"@remix-run/react": "^1.10.0", | ||
"@remix-run/server-runtime": "^1.10.0", | ||
"@testing-library/jest-dom": "^5.15.0", | ||
@@ -60,0 +60,0 @@ "@testing-library/react": "^12.1.2", |
323
README.md
@@ -20,3 +20,3 @@ # Remix Utils | ||
```ts | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
return json( | ||
@@ -28,3 +28,3 @@ promiseHash({ | ||
); | ||
}; | ||
} | ||
``` | ||
@@ -35,3 +35,3 @@ | ||
```ts | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
return json( | ||
@@ -49,3 +49,3 @@ promiseHash({ | ||
); | ||
}; | ||
} | ||
``` | ||
@@ -94,3 +94,3 @@ | ||
export default function View() { | ||
export default function Component() { | ||
return ( | ||
@@ -127,7 +127,7 @@ <ClientOnly fallback={<SimplerStaticVersion />}> | ||
```ts | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
let data = await getData(request); | ||
let response = json<LoaderData>(data); | ||
return await cors(request, response); | ||
}; | ||
} | ||
``` | ||
@@ -138,6 +138,6 @@ | ||
```ts | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
let data = await getData(request); | ||
return await cors(request, json<LoaderData>(data)); | ||
}; | ||
} | ||
``` | ||
@@ -148,3 +148,3 @@ | ||
```ts | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
let data = await getData(request); | ||
@@ -154,3 +154,3 @@ let response = json<LoaderData>(data); | ||
return response; // so you can return it here | ||
}; | ||
} | ||
``` | ||
@@ -202,3 +202,2 @@ | ||
```ts | ||
import type { LoaderFunction } from "remix"; | ||
import { createAuthenticityToken, json } from "remix-utils"; | ||
@@ -211,3 +210,3 @@ import { getSession, commitSession } from "~/services/session.server"; | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
let session = await getSession(request.headers.get("cookie")); | ||
@@ -219,3 +218,3 @@ let token = createAuthenticityToken(session); | ||
); | ||
}; | ||
} | ||
``` | ||
@@ -230,6 +229,6 @@ | ||
```tsx | ||
import { Outlet, useLoaderData } from "remix"; | ||
import { Outlet, useLoaderData } from "@remix-run/react"; | ||
import { Document } from "~/components/document"; | ||
export default function Root() { | ||
export default function Component() { | ||
let { csrf } = useLoaderData<LoaderData>(); | ||
@@ -253,6 +252,6 @@ return ( | ||
```tsx | ||
import { Form } from "remix"; | ||
import { Form } from "@remix-run/react"; | ||
import { AuthenticityTokenInput } from "remix-utils"; | ||
export default function SomeRoute() { | ||
export default function Component() { | ||
return ( | ||
@@ -298,8 +297,7 @@ <Form method="post"> | ||
```ts | ||
import type { ActionFunction } from "remix"; | ||
```t | ||
import { verifyAuthenticityToken, redirectBack } from "remix-utils"; | ||
import { getSession, commitSession } from "~/services/session.server"; | ||
export let action: ActionFunction = async ({ request }) => { | ||
export async function action({ request }: ActionArgs) { | ||
let session = await getSession(request.headers.get("Cookie")); | ||
@@ -309,3 +307,3 @@ await verifyAuthenticityToken(request, session); | ||
return redirectBack(request, { fallback: "/fallback" }); | ||
}; | ||
} | ||
``` | ||
@@ -509,41 +507,2 @@ | ||
### useDataRefresh | ||
This hook lets you trigger a refresh of the loaders in the current URL. | ||
The way this works is by sending a fetcher.submit to `/dev/null` to trigger all loaders to run. | ||
You need to create `app/routes/dev/null.ts` and define an action that returns null. | ||
```ts | ||
// app/routes/dev/null.ts | ||
export function action() { | ||
return null; | ||
} | ||
``` | ||
This Hook is mostly useful if you want to trigger the refresh manually from an effect, examples of this are: | ||
- Set an interval to trigger the refresh | ||
- Refresh when the browser tab is focused again | ||
- Refresh when the user is online again | ||
```ts | ||
import { useDataRefresh } from "remix-utils"; | ||
function useDataRefreshOnInterval() { | ||
let { refresh } = useDataRefresh(); | ||
useEffect(() => { | ||
let interval = setInterval(refresh, 5000); | ||
return () => clearInterval(interval); | ||
}, [refresh]); | ||
} | ||
``` | ||
The return value of `useDataRefresh` is an object with the following keys: | ||
- refresh: a function that trigger the refresh | ||
- type: a string which can be `init`, `refreshRedirect` or `refresh` | ||
- status: a string which can be `loading` or `idle` | ||
### useGlobalTransitionStates | ||
@@ -662,9 +621,9 @@ | ||
// in the root loader | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
let locales = getClientLocales(request); | ||
return json({ locales }); | ||
}; | ||
} | ||
// in any route (including root!) | ||
export default function Screen() { | ||
export default function Component() { | ||
let locales = useLocales(); | ||
@@ -680,31 +639,2 @@ let date = new Date(); | ||
### useRouteData | ||
This hook lets you access the data of any route in the current page. This can include child or parent routes. | ||
To use it, call `useRouteData` in your component and pass the route ID as a string. As an example, if you had the following routes: | ||
``` | ||
routes/articles/$slug.tsx | ||
routes/articles/index.tsx | ||
routes/articles.tsx | ||
``` | ||
Then you need to pass `useRouteData("routes/articles")` to get the data of `routes/articles.tsx`, `useRouteData("routes/articles/index")` to get the data of `routes/articles/index.tsx` and `routes/articles/$slug` to get the data of `routes/articles/$slug.tsx`. | ||
As you can see, the ID is the route file without the extension. | ||
```ts | ||
let parentData = useRouteData("routes/articles"); | ||
let indexData = useRouteData("routes/articles/index"); | ||
``` | ||
The `useRouteData` hook receives a generic to be used as the type of the route data. Because the route may not be found the return type is `Data | undefined`. This means if you do the following: | ||
```ts | ||
let data = useRouteData<ArticleShowData>("routes/articles"); | ||
``` | ||
The type of `data` will be `ArticleShowData | undefined`, so you will need to check if it's not undefined before being able to use it. | ||
### useShouldHydrate | ||
@@ -718,3 +648,3 @@ | ||
import type { ReactNode } from "react"; | ||
import { Links, LiveReload, Meta, Scripts } from "remix"; | ||
import { Links, LiveReload, Meta, Scripts } from "@remix-run/react"; | ||
import { useShouldHydrate } from "remix-utils"; | ||
@@ -773,3 +703,3 @@ | ||
```ts | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
// using the request | ||
@@ -779,3 +709,3 @@ let ipAddress = getClientIPAddress(request); | ||
let ipAddress = getClientIPAddress(request.headers); | ||
}; | ||
} | ||
``` | ||
@@ -806,3 +736,3 @@ | ||
```ts | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
// using the request | ||
@@ -812,3 +742,3 @@ let locales = getClientLocales(request); | ||
let locales = getClientLocales(request.headers); | ||
}; | ||
} | ||
``` | ||
@@ -822,3 +752,3 @@ | ||
import { getClientLocales } from "remix-utils"; | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
let locales = getClientLocales(request); | ||
@@ -832,3 +762,3 @@ let nowDate = new Date(); | ||
return json({ now: formatter.format(nowDate) }); | ||
}; | ||
} | ||
``` | ||
@@ -845,3 +775,3 @@ | ||
```ts | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
let data = await getData(request); | ||
@@ -855,3 +785,3 @@ let headers = new Headers(); | ||
return json(data, { headers }); | ||
}; | ||
} | ||
``` | ||
@@ -869,7 +799,6 @@ | ||
import { redirectBack } from "remix-utils"; | ||
import type { ActionFunction } from "remix"; | ||
export let action: ActionFunction = async ({ request }) => { | ||
export async function action({ request }: ActionArgs) { | ||
await redirectBack(request, { fallback: "/" }); | ||
}; | ||
} | ||
``` | ||
@@ -885,8 +814,7 @@ | ||
import { created } from "remix-utils"; | ||
import type { ActionFunction } from "remix"; | ||
export let action: ActionFunction = async ({ request }) => { | ||
export async function action({ request }: ActionArgs) { | ||
let result = await doSomething(request); | ||
return created(result); | ||
}; | ||
} | ||
``` | ||
@@ -900,7 +828,6 @@ | ||
import { badRequest } from "remix-utils"; | ||
import type { ActionFunction } from "remix"; | ||
export let action: ActionFunction = async () => { | ||
export async function action() { | ||
throw badRequest({ message: "You forgot something in the form." }); | ||
}; | ||
} | ||
``` | ||
@@ -914,8 +841,7 @@ | ||
import { unauthorized } from "remix-utils"; | ||
import type { LoaderFunction } from "remix"; | ||
export let loader: LoaderFunction = async () => { | ||
export async function loader() { | ||
// usually what you really want is to throw a redirect to the login page | ||
throw unauthorized({ message: "You need to login." }); | ||
}; | ||
} | ||
``` | ||
@@ -929,7 +855,6 @@ | ||
import { forbidden } from "remix-utils"; | ||
import type { LoaderFunction } from "remix"; | ||
export let loader: LoaderFunction = async () => { | ||
export async function loader() { | ||
throw forbidden({ message: "You don't have access for this." }); | ||
}; | ||
} | ||
``` | ||
@@ -943,7 +868,6 @@ | ||
import { notFound } from "remix-utils"; | ||
import type { LoaderFunction } from "remix"; | ||
export let loader: LoaderFunction = async () => { | ||
export async function loader() { | ||
throw notFound({ message: "This doesn't exists." }); | ||
}; | ||
} | ||
``` | ||
@@ -957,7 +881,6 @@ | ||
import { unprocessableEntity } from "remix-utils"; | ||
import type { LoaderFunction } from "remix"; | ||
export let loader: LoaderFunction = async () => { | ||
export async function loader() { | ||
throw unprocessableEntity({ message: "This doesn't exists." }); | ||
}; | ||
} | ||
``` | ||
@@ -973,7 +896,6 @@ | ||
import { serverError } from "remix-utils"; | ||
import type { LoaderFunction } from "remix"; | ||
export let loader: LoaderFunction = async () => { | ||
export async function loader() { | ||
throw serverError({ message: "Something unexpected happened." }); | ||
}; | ||
} | ||
``` | ||
@@ -986,5 +908,5 @@ | ||
```ts | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
return notModified(); | ||
}; | ||
} | ||
``` | ||
@@ -999,5 +921,5 @@ | ||
```ts | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
return javascript("console.log('Hello World')"); | ||
}; | ||
} | ||
``` | ||
@@ -1012,5 +934,5 @@ | ||
```ts | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
return stylesheet("body { color: red; }"); | ||
}; | ||
} | ||
``` | ||
@@ -1025,5 +947,5 @@ | ||
```ts | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
return pdf(await generatePDF(request.formData())); | ||
}; | ||
} | ||
``` | ||
@@ -1038,7 +960,34 @@ | ||
```ts | ||
export let loader: LoaderFunction = async ({ request }) => { | ||
export async function loader({ request }: LoaderArgs) { | ||
return html("<h1>Hello World</h1>"); | ||
}; | ||
} | ||
``` | ||
#### XML | ||
Helper function to create a XML file response with any header. | ||
This is useful to create XML files based on data inside a Resource Route. | ||
```ts | ||
export async function loader({ request }: LoaderArgs) { | ||
return xml("<?xml version='1.0'?><catalog></catalog>"); | ||
} | ||
``` | ||
#### TXT | ||
Helper function to create a TXT file response with any header. | ||
This is useful to create TXT files based on data inside a Resource Route. | ||
```ts | ||
export async function loader({ request }: LoaderArgs) { | ||
return txt(` | ||
User-agent: * | ||
Allow: / | ||
`); | ||
} | ||
``` | ||
### Typed Cookies | ||
@@ -1049,3 +998,3 @@ | ||
```ts | ||
import { createCookie } from "remix"; | ||
import { createCookie } from "@remix-run/node"; | ||
import { createTypedCookie } from "remix-utils"; | ||
@@ -1117,3 +1066,3 @@ import { z } from "zod"; | ||
```ts | ||
import { createCookieSessionStorage } from "remix"; | ||
import { createCookieSessionStorage } from "@remix-run/node"; | ||
import { createTypedSessionStorage } from "remix-utils"; | ||
@@ -1240,3 +1189,3 @@ import { z } from "zod"; | ||
export default function handleRequest( | ||
export default function Component( | ||
request: Request, | ||
@@ -1370,3 +1319,3 @@ responseStatusCode: number, | ||
// entry.server.tsx | ||
export default function handleRequest( | ||
export default function Component( | ||
request: Request, | ||
@@ -1399,2 +1348,96 @@ statusCode: number, | ||
### Safe Redirects | ||
When performing a redirect, if the URL is user provided we can't trust it, if you do you're opening a vulnerability to phishing scam by allowing bad actors to redirect the user to malicious websites. | ||
``` | ||
https://remix.utills/?redirectTo=https://malicious.app | ||
``` | ||
To help you prevent this Remix Utils gives you a `safeRedirect` function which can be used to check if the URL is "safe". | ||
> **Note**: In this context, safe means the URL starts with `/` but not `//`, this means the URL is a pathname inside the same app and not an external link. | ||
```ts | ||
export async function loader({ request }: LoaderArgs) { | ||
let { searchParams } = new URL(request.url); | ||
let redirectTo = searchParams.get("redirectTo"); | ||
return redirect(safeRedirect(redirectTo, "/home")); | ||
} | ||
``` | ||
The second argumento of `safeRedirect` is the default redirect which by when not configured is `/`, this lets you tell `safeRedirect` where to redirect the user if the value is not safe. | ||
### JSON Hash Response | ||
When returning a `json` from a `loader` function, you may need to get data from different DB queries or API requests, typically you would something like this | ||
```ts | ||
export async function loader({ params }: LoaderData) { | ||
let postId = z.string().parse(params.postId); | ||
let [post, comments] = await Promise.all([getPost(), getComments()]); | ||
return json({ post, comments }); | ||
async function getPost() { | ||
/* … */ | ||
} | ||
async function getComments() { | ||
/* … */ | ||
} | ||
} | ||
``` | ||
The `jsonHash` function lets you define those functions directly in the `json`, reducing the need to create extra functions and variables. | ||
```ts | ||
export async function loader({ params }: LoaderData) { | ||
let postId = z.string().parse(params.postId); | ||
return jsonHash({ | ||
async post() { | ||
// Implement me | ||
}, | ||
async comments() { | ||
// Implement me | ||
}, | ||
}); | ||
} | ||
``` | ||
It also calls your functions using `Promise.all` so you can be sure the data is retrieved in parallel. | ||
Additionally, you can pass non-async functions, values and promises. | ||
```ts | ||
export async function loader({ params }: LoaderData) { | ||
let postId = z.string().parse(params.postId); | ||
return jsonHash({ | ||
postId, // value | ||
comments: getComments(), // Promise | ||
slug() { | ||
// Non-async function | ||
return postId.split("-").at(1); // get slug from postId param | ||
}, | ||
async post() { | ||
// Async function | ||
// Implement me | ||
}, | ||
}); | ||
async function getComments() { | ||
/* … */ | ||
} | ||
} | ||
``` | ||
The result of `jsonHash` is a `TypedResponse` and it's correctly typed so using it with `typeof loader` works flawlessly. | ||
```ts | ||
export default function Component() { | ||
// all correctly typed | ||
let { postId, comments, slug, post } = useLoaderData<typeof loader>(); | ||
// more code… | ||
} | ||
``` | ||
## Author | ||
@@ -1401,0 +1444,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
238148
5209
1404