@remix-run/cli 0.3.2 → 0.3.3
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/package.json +4 -4
- package/template/.agents/skills/remix/SKILL.md +9 -9
- package/template/.agents/skills/remix/references/auth-and-sessions.md +3 -3
- package/template/.agents/skills/remix/references/middleware-and-server.md +16 -11
- package/template/.agents/skills/remix/references/routing-and-controllers.md +15 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remix-run/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Command-line interface for Remix",
|
|
5
5
|
"author": "Michael Jackson <mjijackson@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"semver": "^7.7.4",
|
|
34
|
-
"@remix-run/
|
|
35
|
-
"@remix-run/
|
|
34
|
+
"@remix-run/terminal": "^0.1.1",
|
|
35
|
+
"@remix-run/test": "^0.5.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^24.6.0",
|
|
39
39
|
"@types/semver": "^7.5.8",
|
|
40
40
|
"@typescript/native-preview": "7.0.0-dev.20251125.1",
|
|
41
|
-
"@remix-run/assert": "^0.
|
|
41
|
+
"@remix-run/assert": "^0.3.0"
|
|
42
42
|
},
|
|
43
43
|
"keywords": [
|
|
44
44
|
"remix",
|
|
@@ -9,7 +9,7 @@ Use this skill for end-to-end Remix app work. This skill helps you choose the ri
|
|
|
9
9
|
|
|
10
10
|
## Full Package Documentation
|
|
11
11
|
|
|
12
|
-
This skill is the quick guide. When you need fuller API documentation, examples, or package-specific details for a `remix/*` subpath, first look for a README next to the relevant generated source file in the published `remix` package: `node_modules/remix/src/<subpath>/README.md`. If that README does not exist, look for the nearest parent README because some subpaths share their parent package documentation.
|
|
12
|
+
This skill is the quick guide. When you need fuller API documentation, examples, or package-specific details for a `remix/*` subpath, first look for a README next to the relevant generated source file in the published `remix` package: `node_modules/remix/src/<subpath>/README.md`. These published README files are generated mirrors; in the Remix source repository, the canonical README lives in the owning `packages/*` package and the `packages/remix/src/**/README.md` mirrors are intentionally ignored. If that README does not exist, look for the nearest parent README because some subpaths share their parent package documentation.
|
|
13
13
|
|
|
14
14
|
Examples:
|
|
15
15
|
|
|
@@ -71,7 +71,7 @@ Common bundles:
|
|
|
71
71
|
2. **Start from the server contract.** Add or update `app/routes.ts` before wiring handlers or UI.
|
|
72
72
|
3. **Put code in the narrowest owner.** Favor route-local code first, then promote only when reuse is real.
|
|
73
73
|
4. **Make the server path correct before adding browser behavior.** A route should return the right `Response` via `router.fetch(...)` before you add `clientEntry(...)`, animations, or DOM effects.
|
|
74
|
-
5. **Add middleware deliberately.** Keep fast-exit middleware early and request-enriching middleware later. Export a typed `AppContext` from the
|
|
74
|
+
5. **Add middleware deliberately.** Keep fast-exit middleware early and request-enriching middleware later. Export a typed `AppContext` from the middleware stack and use it in controllers.
|
|
75
75
|
6. **Validate input at the boundary.** Parse and validate `Request`, `FormData`, params, cookies, and external payloads before they reach rendering or persistence logic.
|
|
76
76
|
7. **Hydrate only when necessary.** Prefer server-rendered UI. Use `clientEntry(...)` and `run(...)` only for real browser interactivity or browser-only APIs.
|
|
77
77
|
8. **Test the narrowest meaningful layer.** Prefer router tests for route behavior. Use component tests when the behavior is truly interactive or DOM-specific.
|
|
@@ -113,7 +113,7 @@ When code could live in multiple places:
|
|
|
113
113
|
|
|
114
114
|
- Put top-level leaf actions in `app/actions/controller.tsx`
|
|
115
115
|
- A controller's `actions` object contains only direct leaf route keys from the route map passed to `router.map(...)`
|
|
116
|
-
- Add `app/actions/<route-key>/controller.tsx` for each nested route map that needs actions or middleware, and map it explicitly with `router.map(routes.<routeKey>, controller)`
|
|
116
|
+
- Add `app/actions/<route-key>/controller.tsx` for each nested route map that needs actions or controller middleware, and map it explicitly with `router.map(routes.<routeKey>, controller)`
|
|
117
117
|
- Name directories under `app/actions/` after route-map keys, not URL path segments
|
|
118
118
|
- Keep route-local UI and helpers next to the controller that owns them
|
|
119
119
|
- Move shared cross-route UI to `app/ui/`
|
|
@@ -135,7 +135,7 @@ When code could live in multiple places:
|
|
|
135
135
|
- Do not create standalone root action files; put root route actions in `app/actions/controller.tsx`
|
|
136
136
|
- Do not put nested route-map keys in a controller's `actions`
|
|
137
137
|
- Do not register normal app leaf routes directly in `app/router.ts` when they belong in a controller
|
|
138
|
-
- Do not rely on middleware from one controller to protect another controller;
|
|
138
|
+
- Do not rely on controller middleware from one controller to protect another controller; add controller middleware explicitly in each controller that needs it
|
|
139
139
|
- Do not put middleware or persistence helpers in `app/utils/` when they have a clearer home
|
|
140
140
|
|
|
141
141
|
## Core Remix Rules
|
|
@@ -147,7 +147,7 @@ When code could live in multiple places:
|
|
|
147
147
|
- Model HTTP behavior explicitly. Status codes, headers, redirects, cache rules, and content types are part of the route contract
|
|
148
148
|
- Make the server route correct first. A POST should already return the right HTML, redirect, or error response on its own before `clientEntry(...)` layers interactivity on top
|
|
149
149
|
- Validate input at the boundary using `remix/data-schema` (and `remix/data-schema/form-data` for forms). `parseSafe` makes the failure path a return value instead of an exception
|
|
150
|
-
- Derive `AppContext` from the
|
|
150
|
+
- Derive `AppContext` from the middleware stack so `get(Database)`, `get(Session)`, `get(Auth)`, and similar keys stay typed. If the controller never reads from context, it doesn't need the harness
|
|
151
151
|
- Outside actions and controllers, only use `getContext()` when `asyncContext()` is in the middleware stack
|
|
152
152
|
- Remix Component is not React: write `function Name(handle: Handle<Props>) { return () => ... }`, read props from `handle.props`, keep state in setup-scope variables, call `handle.update()` explicitly, and do DOM-sensitive work in event handlers or `queueTask(...)`, not in render
|
|
153
153
|
- Prefer host-element mixins via `mix={mixin(...)}` for behavior and styling instead of inventing custom host prop conventions. Use `mix={[...]}` only when composing multiple mixins
|
|
@@ -202,14 +202,14 @@ Use this map to find the right package quickly. Each entry says what the package
|
|
|
202
202
|
|
|
203
203
|
### Routing, Server, and Responses
|
|
204
204
|
|
|
205
|
-
- `remix/router` — the router itself. Use for `createRouter`,
|
|
205
|
+
- `remix/router` — the router itself. Use for `createRouter`, controllers, middleware types, and registering routes
|
|
206
206
|
- `remix/routes` — declarative route builders. Use for `route`, `get`, `post`, `put`, `del`, `form`, `resources` when defining `app/routes.ts`
|
|
207
207
|
- `remix/node-fetch-server` — default Node adapter for new apps. Use `createRequestListener` with `node:http`, `node:https`, or `node:http2` in `server.ts` when booting the template-style app
|
|
208
208
|
- `remix/assets` — browser asset server. Use for `createAssetServer` when serving compiled scripts and styles, getting public hrefs, and emitting preloads. Configure a `basePath`, and keep `fileMap` URL patterns relative to it. Shared compiler options such as `target`, `sourceMaps`, `sourceMapSourcePaths`, and `minify` live at the top level
|
|
209
209
|
- `remix/headers` — `SuperHeaders` plus typed header parsers and builders. Use the default export when you want a `Headers` subclass with typed accessors like `headers.contentType`, `headers.cacheControl`, and `headers.setCookie`; use named classes such as `CacheControl`, `ContentDisposition`, and `Vary` when working with individual header values
|
|
210
210
|
- `remix/response/redirect` — `redirect(href, status?)`. Use for the canonical "POST then redirect" pattern and other location changes
|
|
211
211
|
- `remix/response/html` — `createHtmlResponse`. Use when you need an HTML `Response` from a string or stream without rendering through `remix/ui`
|
|
212
|
-
- `remix/response/compress` — `compressResponse`. Use when compressing one-off responses outside
|
|
212
|
+
- `remix/response/compress` — `compressResponse`. Use when compressing one-off responses outside `compression()` middleware
|
|
213
213
|
- `remix/response/file` — file-download responses. Use for `Content-Disposition: attachment` responses
|
|
214
214
|
- `remix/route-pattern` — low-level URL matching and generation. Use `RoutePattern` or `createMatcher` when working with raw patterns outside the router. `href(...)` encodes pathname and search params for you, and `match(...)` returns decoded params
|
|
215
215
|
- `remix/route-pattern/specificity` — pattern ranking helpers. Use only when building custom matcher or reporting logic outside the normal router/matcher APIs
|
|
@@ -232,7 +232,7 @@ Use this map to find the right package quickly. Each entry says what the package
|
|
|
232
232
|
### Auth, Sessions, and Cookies
|
|
233
233
|
|
|
234
234
|
- `remix/session` — the `Session` object: `get`, `set`, `flash`, `unset`, `regenerateId`. Use for any per-browser state where tampering would be a bug (login, "I submitted this form already", cart, flash messages)
|
|
235
|
-
- `remix/middleware/session` — `session(cookie, storage)`. Use to wire a session cookie and storage backend into the
|
|
235
|
+
- `remix/middleware/session` — `session(cookie, storage)`. Use to wire a session cookie and storage backend into the middleware stack
|
|
236
236
|
- `remix/session-storage/fs`, `remix/session-storage/memory`, `remix/session-storage/cookie` — storage backends. Use `fs-storage` for single-process apps, `memory-storage` for tests, `cookie-storage` for stateless deployments where data fits in a cookie
|
|
237
237
|
- `remix/session-storage/redis` — Redis-backed storage. Use for multi-process or multi-host deployments
|
|
238
238
|
- `remix/session-storage/memcache` — Memcache-backed storage. Same multi-host use case as Redis
|
|
@@ -258,7 +258,7 @@ Use this map to find the right package quickly. Each entry says what the package
|
|
|
258
258
|
- `remix/middleware/form-data` — `formData()`. Use to parse `FormData` once and expose it via `get(FormData)` instead of calling `await request.formData()` in each action
|
|
259
259
|
- `remix/form-data-parser` — lower-level `parseFormData`, `FileUpload`. Use when implementing custom upload handlers. Upload handler errors propagate directly
|
|
260
260
|
- `remix/multipart-parser` and `remix/multipart-parser/node` — low-level multipart stream parsing. `MultipartPart.headers` is a plain object keyed by lower-case header name; read values with bracket notation such as `part.headers['content-type']`
|
|
261
|
-
- `remix/middleware/compression` — `compression()`. Use
|
|
261
|
+
- `remix/middleware/compression` — `compression()`. Use for text-like responses
|
|
262
262
|
- `remix/middleware/logger` — `logger()`. Use in development for request logs; pass `colors` to force terminal color output on or off
|
|
263
263
|
- `remix/middleware/method-override` — `methodOverride()`. Use when HTML forms need `PUT`, `PATCH`, or `DELETE`
|
|
264
264
|
- `remix/middleware/async-context` — `asyncContext()`, `getContext()`. Use when helpers outside actions need request context without threading it through every call
|
|
@@ -335,9 +335,9 @@ async function refreshGoogleTokens({ get }) {
|
|
|
335
335
|
|
|
336
336
|
## Protecting Routes
|
|
337
337
|
|
|
338
|
-
### Controller
|
|
338
|
+
### Controller middleware protection
|
|
339
339
|
|
|
340
|
-
Apply `requireAuth()` to every action in one controller:
|
|
340
|
+
Apply `requireAuth()` as controller middleware to every action in one controller:
|
|
341
341
|
|
|
342
342
|
```typescript
|
|
343
343
|
import { createController } from 'remix/router'
|
|
@@ -389,7 +389,7 @@ export default createController(routes.admin, {
|
|
|
389
389
|
})
|
|
390
390
|
```
|
|
391
391
|
|
|
392
|
-
### Action
|
|
392
|
+
### Action middleware protection
|
|
393
393
|
|
|
394
394
|
Apply middleware to a single route:
|
|
395
395
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
How to compose the request lifecycle and bridge the router to a runtime. Read this when the task involves:
|
|
6
6
|
|
|
7
|
-
- Choosing or ordering built-in middleware in the
|
|
7
|
+
- Choosing or ordering built-in middleware in the stack
|
|
8
8
|
- Writing custom middleware that sets typed context values
|
|
9
9
|
- Adding fast-exit handling (static files, CORS preflights) versus request-enriching layers (sessions, auth, data loading)
|
|
10
10
|
- Choosing when to keep the generated Node server versus switching server adapters
|
|
@@ -50,7 +50,7 @@ let router = createRouter({ middleware })
|
|
|
50
50
|
| Middleware | Import | Use when | Notes |
|
|
51
51
|
| -------------------------- | ---------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
52
52
|
| `staticFiles(dir, opts?)` | `remix/middleware/static` | Serve files from `public/` or another directory exactly as they exist on disk | Fast exit; usually near the top |
|
|
53
|
-
| `compression()` | `remix/middleware/compression` | Compress text-like responses | Usually
|
|
53
|
+
| `compression()` | `remix/middleware/compression` | Compress text-like responses | Usually app-wide |
|
|
54
54
|
| `logger()` | `remix/middleware/logger` | Log requests and responses | Often development-only; `colors` can force color output on/off |
|
|
55
55
|
| `cors(opts?)` | `remix/middleware/cors` | Endpoints must serve cross-origin browsers or preflight `OPTIONS` requests | Usually early so preflights can short-circuit |
|
|
56
56
|
| `cop(opts?)` | `remix/middleware/cop` | Reject unsafe cross-origin browser requests without synchronizer tokens | Put before session or CSRF when used |
|
|
@@ -60,7 +60,7 @@ let router = createRouter({ middleware })
|
|
|
60
60
|
| `csrf(opts?)` | `remix/middleware/csrf` | Session-backed form workflows need synchronizer-token CSRF protection | Requires `session()` before it |
|
|
61
61
|
| `asyncContext()` | `remix/middleware/async-context` | Helpers outside handlers need request context via `getContext()` | Add before helpers rely on it |
|
|
62
62
|
| `auth({ schemes })` | `remix/middleware/auth` | Resolve auth state into `context.get(Auth)` | Run after `session()` for session-backed auth |
|
|
63
|
-
| `requireAuth()` | `remix/middleware/auth` | A controller or action must reject anonymous access | Usually controller
|
|
63
|
+
| `requireAuth()` | `remix/middleware/auth` | A controller or action must reject anonymous access | Usually controller middleware or action middleware |
|
|
64
64
|
|
|
65
65
|
### Static files vs browser modules
|
|
66
66
|
|
|
@@ -73,7 +73,7 @@ let router = createRouter({ middleware })
|
|
|
73
73
|
- Parse request bodies before middleware that depends on them, such as `methodOverride()` and form field token extraction in `csrf()`
|
|
74
74
|
- Run `session()` before `csrf()` and before session-backed `auth()`
|
|
75
75
|
- Add `asyncContext()` before helpers or shared code call `getContext()`
|
|
76
|
-
- Keep route protection like `requireAuth()`
|
|
76
|
+
- Keep route protection like `requireAuth()` as controller middleware or action middleware unless the entire app is private
|
|
77
77
|
|
|
78
78
|
### Common stacks
|
|
79
79
|
|
|
@@ -175,17 +175,17 @@ export function getCurrentUserSafely() {
|
|
|
175
175
|
}
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
-
## Middleware
|
|
178
|
+
## Middleware Types
|
|
179
179
|
|
|
180
|
-
Middleware
|
|
180
|
+
Middleware has three API-owned forms:
|
|
181
181
|
|
|
182
|
-
1. **Router
|
|
182
|
+
1. **Router middleware** — runs for every request:
|
|
183
183
|
|
|
184
184
|
```typescript
|
|
185
|
-
let router = createRouter({ middleware: [
|
|
185
|
+
let router = createRouter({ middleware: [logger(), session(cookie, storage)] })
|
|
186
186
|
```
|
|
187
187
|
|
|
188
|
-
2. **Controller
|
|
188
|
+
2. **Controller middleware** — runs for the direct actions in one controller:
|
|
189
189
|
|
|
190
190
|
```typescript
|
|
191
191
|
export default createController(routes.account, {
|
|
@@ -196,14 +196,19 @@ Middleware can be applied at three levels:
|
|
|
196
196
|
|
|
197
197
|
Controller middleware does not flow into other controllers. Add the middleware to each controller that needs it.
|
|
198
198
|
|
|
199
|
-
3. **Action
|
|
199
|
+
3. **Action middleware** — runs for a single action:
|
|
200
|
+
|
|
200
201
|
```typescript
|
|
201
202
|
router.get(routes.account.index, {
|
|
202
203
|
middleware: [requireAuth()],
|
|
203
|
-
handler
|
|
204
|
+
handler(context) {
|
|
205
|
+
return render(<AccountPage identity={context.auth.identity} />)
|
|
206
|
+
},
|
|
204
207
|
})
|
|
205
208
|
```
|
|
206
209
|
|
|
210
|
+
Prefer inline arrays for `middleware` options. Use `RouterContext<typeof router>` to derive an app context from a router that uses inline middleware. Use `createMiddleware()` only when a chain is stored in a variable and its exact tuple type needs to be preserved, such as when deriving `MiddlewareContext<typeof rootMiddleware>` without a router value, exporting a reusable chain, or returning a chain from a factory.
|
|
211
|
+
|
|
207
212
|
## Node Server Setup
|
|
208
213
|
|
|
209
214
|
New apps already include a `server.ts` that adapts the app router with `remix/node-fetch-server`. Keep that generated server unless the task specifically needs to change runtime behavior such as host/protocol handling, TLS, HTTP/2, WebSockets, deployment lifecycle, or test-only server setup.
|
|
@@ -108,14 +108,17 @@ The handler receives a context object with:
|
|
|
108
108
|
- `url` — the request URL
|
|
109
109
|
- `request` — the raw `Request`
|
|
110
110
|
|
|
111
|
-
Actions with
|
|
111
|
+
Actions with action middleware:
|
|
112
112
|
|
|
113
113
|
```typescript
|
|
114
|
+
import { createAction } from 'remix/router'
|
|
114
115
|
import { requireAuth } from 'remix/middleware/auth'
|
|
115
116
|
|
|
116
|
-
|
|
117
|
+
export const account = createAction(routes.account.index, {
|
|
117
118
|
middleware: [requireAuth()],
|
|
118
|
-
handler
|
|
119
|
+
handler(context) {
|
|
120
|
+
return render(<AccountPage />)
|
|
121
|
+
},
|
|
119
122
|
})
|
|
120
123
|
```
|
|
121
124
|
|
|
@@ -305,7 +308,7 @@ router.map(routes.account.settings, accountSettingsController)
|
|
|
305
308
|
|
|
306
309
|
### Controller middleware
|
|
307
310
|
|
|
308
|
-
The `middleware` array on a controller runs only for the direct actions in that controller, before action
|
|
311
|
+
The `middleware` array on a controller runs only for the direct actions in that controller, before action middleware. It does not apply to other controllers.
|
|
309
312
|
|
|
310
313
|
```typescript
|
|
311
314
|
export default createController(routes.admin, {
|
|
@@ -338,22 +341,16 @@ router.post(routes.logout, logoutAction)
|
|
|
338
341
|
|
|
339
342
|
## Typed Context
|
|
340
343
|
|
|
341
|
-
Define an `AppContext` type from your
|
|
344
|
+
Define an `AppContext` type from your router, then make it the default context used by `createAction()` and `createController()`:
|
|
342
345
|
|
|
343
346
|
```typescript
|
|
344
|
-
import
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
]
|
|
352
|
-
|
|
353
|
-
export type AppContext<params extends AnyParams = {}> = ContextWithParams<
|
|
354
|
-
MiddlewareContext<RootMiddleware>,
|
|
355
|
-
params
|
|
356
|
-
>
|
|
347
|
+
import { createRouter, type RouterContext } from 'remix/router'
|
|
348
|
+
|
|
349
|
+
export const router = createRouter({
|
|
350
|
+
middleware: [formData(), session(cookie, storage), loadDatabase(), loadAuth()],
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
export type AppContext = RouterContext<typeof router>
|
|
357
354
|
|
|
358
355
|
declare module 'remix/router' {
|
|
359
356
|
interface RouterTypes {
|