@remix-run/render-middleware 0.0.0 → 0.1.1

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Shopify Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,99 @@
1
- # Placeholder Package
1
+ # render-middleware
2
2
 
3
- This package is a placeholder published at `0.0.0` to reserve the npm name and configure CI publish permissions.
3
+ Request-scoped renderer middleware for Remix. It stores a renderer in `fetch-router` request context so route actions can render responses without passing request-specific rendering details through every action.
4
+
5
+ ## Features
6
+
7
+ - **Generic renderers** - Render any input type to a `Response`
8
+ - **Request-scoped setup** - Create renderers from the current `RequestContext`
9
+ - **Typed context** - Typed `context.render` (or `context.get(Renderer)`)
10
+ - **Small runtime** - The package only stores a renderer in request context
11
+
12
+ ## Installation
13
+
14
+ ```sh
15
+ npm i remix
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ Use `renderWith()` to add a renderer to `context.render` and `context.get(Renderer)`.
21
+
22
+ ```ts
23
+ import { createRouter, type MiddlewareContext } from 'remix/router'
24
+ import { renderWith } from 'remix/middleware/render'
25
+
26
+ const render = renderWith(
27
+ (context) =>
28
+ function render(value: string, init?: ResponseInit) {
29
+ return new Response(`${context.url.pathname}: ${value}`, init)
30
+ },
31
+ )
32
+
33
+ type AppContext = MiddlewareContext<[typeof render]>
34
+
35
+ const router = createRouter<AppContext>({
36
+ middleware: [render],
37
+ })
38
+
39
+ router.get('/hello', (context) => {
40
+ return context.render('Hello')
41
+ })
42
+ ```
43
+
44
+ Use `context.render(...)` (or `context.get(Renderer)(...)`).
45
+
46
+ Renderers may render any value type, not just UI nodes.
47
+
48
+ ```ts
49
+ import { renderWith } from 'remix/middleware/render'
50
+
51
+ const json = renderWith(
52
+ () =>
53
+ function render(data: unknown, init?: ResponseInit) {
54
+ return Response.json(data, init)
55
+ },
56
+ )
57
+
58
+ router.get('/api', (context) => {
59
+ return context.render({ ok: true })
60
+ })
61
+ ```
62
+
63
+ For Remix UI, create a renderer that owns frame resolution and response creation.
64
+
65
+ ```tsx
66
+ import { createHtmlResponse } from 'remix/response/html'
67
+ import { renderWith } from 'remix/middleware/render'
68
+ import type { RemixNode } from 'remix/ui'
69
+ import { renderToStream } from 'remix/ui/server'
70
+
71
+ const render = renderWith(
72
+ ({ router, url }) =>
73
+ function render(node: RemixNode, init?: ResponseInit) {
74
+ let stream = renderToStream(node, {
75
+ async resolveFrame(src) {
76
+ let response = await router.fetch(new URL(src, url))
77
+
78
+ if (!response.ok) {
79
+ return `<pre>Frame error: ${response.status}</pre>`
80
+ }
81
+
82
+ return response.body ?? response.text()
83
+ },
84
+ })
85
+
86
+ return createHtmlResponse(stream, init)
87
+ },
88
+ )
89
+ ```
90
+
91
+ ## Related Packages
92
+
93
+ - [`fetch-router`](https://github.com/remix-run/remix/tree/main/packages/fetch-router) - Request routing and context
94
+ - [`ui`](https://github.com/remix-run/remix/tree/main/packages/ui) - Remix UI rendering primitives
95
+ - [`response`](https://github.com/remix-run/remix/tree/main/packages/response) - Response helpers
96
+
97
+ ## License
98
+
99
+ See [LICENSE](https://github.com/remix-run/remix/blob/main/LICENSE)
@@ -0,0 +1,3 @@
1
+ export { Renderer, renderWith } from './lib/render.ts';
2
+ export type { AnyRenderer } from './lib/render.ts';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACtD,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { Renderer, renderWith } from "./lib/render.js";
@@ -0,0 +1,37 @@
1
+ import { type Middleware, type RequestContext } from '@remix-run/fetch-router';
2
+ /**
3
+ * A function that converts application data into a `Response`.
4
+ */
5
+ export interface Renderer<input = unknown, responseInit = ResponseInit> {
6
+ /**
7
+ * Render input data into a response.
8
+ *
9
+ * @param input The value to render.
10
+ * @param init Optional renderer-specific response options.
11
+ * @returns A rendered response.
12
+ */
13
+ (input: input, init?: responseInit): Response | Promise<Response>;
14
+ }
15
+ /**
16
+ * A renderer of any input and options shape.
17
+ */
18
+ export type AnyRenderer = Renderer<never, never>;
19
+ /**
20
+ * Context key used to read the current request renderer with `context.get(Renderer)`.
21
+ * The `renderWith()` middleware also installs the renderer as `context.render`.
22
+ */
23
+ export declare const Renderer: import("../../../fetch-router/src/lib/request-context.ts").ContextKey<AnyRenderer>;
24
+ type RendererFactory<renderer extends AnyRenderer> = (context: RequestContext<any, any>) => renderer;
25
+ /**
26
+ * Adds a renderer to request context.
27
+ *
28
+ * @param createRenderer A function that creates a renderer for each request.
29
+ * @returns Middleware that stores the renderer in request context.
30
+ */
31
+ export declare function renderWith<const renderer extends AnyRenderer>(createRenderer: RendererFactory<renderer>): Middleware<{
32
+ key: typeof Renderer;
33
+ value: renderer;
34
+ property: 'render';
35
+ }>;
36
+ export {};
37
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/lib/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAEhG;;GAEG;AACH,MAAM,WAAW,QAAQ,CAAC,KAAK,GAAG,OAAO,EAAE,YAAY,GAAG,YAAY;IACpE;;;;;;OAMG;IACH,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CAClE;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;AAEhD;;;GAGG;AACH,eAAO,MAAM,QAAQ,oFAAkC,CAAA;AAEvD,KAAK,eAAe,CAAC,QAAQ,SAAS,WAAW,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,QAAQ,CAAA;AAEpG;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,CAAC,QAAQ,SAAS,WAAW,EAC3D,cAAc,EAAE,eAAe,CAAC,QAAQ,CAAC,GACxC,UAAU,CAAC;IAAE,GAAG,EAAE,OAAO,QAAQ,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC,CAI3E"}
@@ -0,0 +1,17 @@
1
+ import { createContextKey } from '@remix-run/fetch-router';
2
+ /**
3
+ * Context key used to read the current request renderer with `context.get(Renderer)`.
4
+ * The `renderWith()` middleware also installs the renderer as `context.render`.
5
+ */
6
+ export const Renderer = createContextKey();
7
+ /**
8
+ * Adds a renderer to request context.
9
+ *
10
+ * @param createRenderer A function that creates a renderer for each request.
11
+ * @returns Middleware that stores the renderer in request context.
12
+ */
13
+ export function renderWith(createRenderer) {
14
+ return (context) => {
15
+ context.set(Renderer, createRenderer(context), { property: 'render' });
16
+ };
17
+ }
package/package.json CHANGED
@@ -1,14 +1,53 @@
1
1
  {
2
2
  "name": "@remix-run/render-middleware",
3
- "version": "0.0.0",
4
- "description": "Placeholder package for Remix CI/OIDC setup",
3
+ "version": "0.1.1",
4
+ "description": "Middleware for adding request-scoped renderers to fetch-router context",
5
+ "author": "Michael Jackson <mjijackson@gmail.com>",
5
6
  "license": "MIT",
6
7
  "repository": {
7
8
  "type": "git",
8
9
  "url": "git+https://github.com/remix-run/remix.git",
9
10
  "directory": "packages/render-middleware"
10
11
  },
11
- "publishConfig": {
12
- "access": "public"
12
+ "homepage": "https://github.com/remix-run/remix/tree/main/packages/render-middleware#readme",
13
+ "files": [
14
+ "LICENSE",
15
+ "README.md",
16
+ "dist",
17
+ "src",
18
+ "!src/**/*.test.ts"
19
+ ],
20
+ "type": "module",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "default": "./dist/index.js"
25
+ },
26
+ "./package.json": "./package.json"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^24.6.0",
30
+ "@typescript/native-preview": "7.0.0-dev.20251125.1",
31
+ "@remix-run/assert": "0.2.1",
32
+ "@remix-run/fetch-router": "0.19.1",
33
+ "@remix-run/test": "0.4.1"
34
+ },
35
+ "dependencies": {
36
+ "@remix-run/fetch-router": "^0.19.1"
37
+ },
38
+ "keywords": [
39
+ "fetch",
40
+ "router",
41
+ "middleware",
42
+ "render",
43
+ "renderer",
44
+ "response"
45
+ ],
46
+ "scripts": {
47
+ "build": "tsgo -p tsconfig.build.json",
48
+ "clean": "git clean -fdX",
49
+ "test": "remix-test",
50
+ "test:bun": "bun x --bun remix-test",
51
+ "typecheck": "tsgo --noEmit"
13
52
  }
14
- }
53
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { Renderer, renderWith } from './lib/render.ts'
2
+ export type { AnyRenderer } from './lib/render.ts'
@@ -0,0 +1,42 @@
1
+ import { createContextKey, type Middleware, type RequestContext } from '@remix-run/fetch-router'
2
+
3
+ /**
4
+ * A function that converts application data into a `Response`.
5
+ */
6
+ export interface Renderer<input = unknown, responseInit = ResponseInit> {
7
+ /**
8
+ * Render input data into a response.
9
+ *
10
+ * @param input The value to render.
11
+ * @param init Optional renderer-specific response options.
12
+ * @returns A rendered response.
13
+ */
14
+ (input: input, init?: responseInit): Response | Promise<Response>
15
+ }
16
+
17
+ /**
18
+ * A renderer of any input and options shape.
19
+ */
20
+ export type AnyRenderer = Renderer<never, never>
21
+
22
+ /**
23
+ * Context key used to read the current request renderer with `context.get(Renderer)`.
24
+ * The `renderWith()` middleware also installs the renderer as `context.render`.
25
+ */
26
+ export const Renderer = createContextKey<AnyRenderer>()
27
+
28
+ type RendererFactory<renderer extends AnyRenderer> = (context: RequestContext<any, any>) => renderer
29
+
30
+ /**
31
+ * Adds a renderer to request context.
32
+ *
33
+ * @param createRenderer A function that creates a renderer for each request.
34
+ * @returns Middleware that stores the renderer in request context.
35
+ */
36
+ export function renderWith<const renderer extends AnyRenderer>(
37
+ createRenderer: RendererFactory<renderer>,
38
+ ): Middleware<{ key: typeof Renderer; value: renderer; property: 'render' }> {
39
+ return (context) => {
40
+ context.set(Renderer, createRenderer(context), { property: 'render' })
41
+ }
42
+ }