@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remix-run/cli",
3
- "version": "0.3.2",
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/test": "^0.4.2",
35
- "@remix-run/terminal": "^0.1.1"
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.2.1"
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 root middleware stack and use it in controllers.
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; map middleware explicitly in each controller that needs it
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 root 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
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`, controller and middleware types, and registering routes
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 the global `compression()` middleware
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 root middleware stack
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 globally for text-like responses
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-level protection
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-level protection
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 root stack
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 global |
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-level or action-level, not global |
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()` at controller or action scope unless the entire app is private
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 Layers
178
+ ## Middleware Types
179
179
 
180
- Middleware can be applied at three levels:
180
+ Middleware has three API-owned forms:
181
181
 
182
- 1. **Router-level** — runs for every request:
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-level** — runs for the direct actions in one 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-level** — runs for a single route:
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: accountAction.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 inline middleware:
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
- router.get(routes.account.index, {
117
+ export const account = createAction(routes.account.index, {
117
118
  middleware: [requireAuth()],
118
- handler: accountAction.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-level middleware. It does not apply to other controllers.
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 middleware stack, then make it the default context used by `createAction()` and `createController()`:
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 type { MiddlewareContext, ContextWithParams, AnyParams } from 'remix/router'
345
-
346
- type RootMiddleware = [
347
- ReturnType<typeof formData>,
348
- ReturnType<typeof session>,
349
- ReturnType<typeof loadDatabase>,
350
- ReturnType<typeof loadAuth>,
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 {