UNPKG

remix-typedjson

Version:

This package is a replacement for superjson to use in your Remix app. It handles a subset of types that `superjson` supports, but is faster and smaller.

162 lines (161 loc) 5.84 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.redirect = exports.deserializeRemix = exports.stringifyRemix = exports.useTypedRouteLoaderData = exports.useTypedFetcher = exports.useTypedActionData = exports.useTypedLoaderData = exports.TypedAwait = exports.typeddefer = exports.typedjson = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("@remix-run/react"); const server_runtime_1 = require("@remix-run/server-runtime"); const _typedjson = __importStar(require("./typedjson")); const typedjson = (data, 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/json; charset=utf-8'); } return new Response(stringifyRemix(data), { ...responseInit, headers, }); }; exports.typedjson = typedjson; const typeddefer = (data, init = {}) => { // wrap any Promises in the data with new Promises that will serialize the // resolved data and add the meta to the response Object.entries(data).forEach(([key, value]) => { if (value instanceof Promise) { ; data[key] = value.then(resolvedData => { const { meta } = _typedjson.serialize(resolvedData); if (meta) { ; resolvedData['$$meta'] = meta; } return resolvedData; }); } else { const { meta } = _typedjson.serialize(data); if (meta) { ; data['$$meta'] = meta; } } }); let responseInit = typeof init === 'number' ? { status: init } : init; return (0, server_runtime_1.defer)(data, responseInit); }; exports.typeddefer = typeddefer; function TypedAwait(props) { if (!props.children) return null; return ((0, jsx_runtime_1.jsx)(react_1.Await, { ...props, children: data => { if (data === null) return null; let deserializedData = deserializeRemix(data); return props.children(deserializedData); } })); } exports.TypedAwait = TypedAwait; function useTypedLoaderData() { const data = (0, react_1.useLoaderData)(); return deserializeRemix(data); } exports.useTypedLoaderData = useTypedLoaderData; function useTypedActionData() { const data = (0, react_1.useActionData)(); return deserializeRemix(data); } exports.useTypedActionData = useTypedActionData; function useTypedFetcher(opts) { const fetcher = (0, react_1.useFetcher)(opts); if (fetcher.data) { const newData = deserializeRemix(fetcher.data); fetcher.data = newData ?? undefined; } return fetcher; } exports.useTypedFetcher = useTypedFetcher; function useTypedRouteLoaderData(id) { const match = (0, react_1.useMatches)().find(match => match.id === id); if (!match) return undefined; return deserializeRemix(match.data); } exports.useTypedRouteLoaderData = useTypedRouteLoaderData; function stringifyRemix(data) { // prevent double JSON stringification let { json, meta } = _typedjson.serialize(data); if (json && meta) { if (json.startsWith('{')) { json = `${json.substring(0, json.length - 1)},\"$$meta\":${JSON.stringify(meta)}}`; } else if (json.startsWith('[')) { json = `{"$$obj":${json},"$$meta":${JSON.stringify(meta)}}`; } } return json; } exports.stringifyRemix = stringifyRemix; function deserializeRemix(data) { if (!data) return data; if (data.$$obj) { // handle arrays wrapped in an object return data.$$meta ? _typedjson.applyMeta(data.$$obj, data.$$meta) : data.$$obj; } else if (data.$$meta) { // handle object with $$meta key // remove before applying meta const meta = data.$$meta; delete data.$$meta; return _typedjson.applyMeta(data, meta); } return data; } exports.deserializeRemix = deserializeRemix; /** * A redirect response. Sets the status code and the `Location` header. * Defaults to "302 Found". * * @see https://remix.run/api/remix#redirect */ const redirect = (url, init = 302) => { let responseInit = init; if (typeof responseInit === 'number') { responseInit = { status: responseInit }; } else if (typeof responseInit.status === 'undefined') { responseInit.status = 302; } let headers = new Headers(responseInit.headers); headers.set('Location', url); return new Response(null, { ...responseInit, headers, }); }; exports.redirect = redirect;