Skip to content

Commit 255a9b3

Browse files
committed
feat: add msw 2 upgrade recipe
1 parent 0520c96 commit 255a9b3

File tree

35 files changed

+2273
-0
lines changed

35 files changed

+2273
-0
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Dependencies
2+
node_modules/
3+
npm-debug.log*
4+
yarn-debug.log*
5+
yarn-error.log*
6+
7+
# Build artifacts
8+
target/
9+
dist/
10+
build/
11+
12+
# Temporary files
13+
*.tmp
14+
*.temp
15+
.cache/
16+
17+
# Environment files
18+
.env
19+
.env.local
20+
21+
# IDE files
22+
.vscode/
23+
.idea/
24+
*.swp
25+
*.swo
26+
27+
# OS files
28+
.DS_Store
29+
Thumbs.db
30+
31+
# Package bundles
32+
*.tar.gz
33+
*.tgz
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
# msw-2-upgrade-recipe
2+
3+
Transform MSW v1 code to v2 patterns
4+
5+
## Installation
6+
7+
```bash
8+
# Install from registry
9+
codemod run msw-2-upgrade-recipe
10+
11+
# Or run locally
12+
codemod run -w workflow.yaml
13+
```
14+
15+
## Usage
16+
17+
This codemod transforms TypeScript code by upgrading your project from MSW v1 to v2. The codemod automatically handles all major breaking changes:
18+
19+
- Updates import statements to new locations and names
20+
- Fixes generic type arguments order
21+
- Modernizes request object usage patterns
22+
- Replaces `ctx.fetch()` with new `fetch(bypass())` pattern
23+
- Updates passthrough method calls
24+
- Converts response patterns to new `HttpResponse` API
25+
- Updates handler callback signatures
26+
- Modernizes event callback signatures
27+
- Replaces deprecated handler printing methods
28+
29+
## Transformations Applied
30+
31+
### 1. Import Updates (`imports`)
32+
33+
Updates import statements to match MSW v2 locations and naming:
34+
35+
- `rest``http`
36+
- `RestHandler``HttpHandler`
37+
- `setupWorker` now imported from `msw/browser`
38+
39+
**Before:**
40+
41+
```typescript
42+
import { rest as caller, RestHandler } from "msw";
43+
44+
const handlers: RestHandler[] = [
45+
caller.get("/user", (req, res, ctx) => {
46+
return res(ctx.json({ firstName: "John" }));
47+
}),
48+
];
49+
```
50+
51+
**After:**
52+
53+
```typescript
54+
import { http as caller, HttpHandler, HttpResponse } from "msw";
55+
56+
const handlers: HttpHandler[] = [
57+
caller.get("/user", () => {
58+
return HttpResponse.json({ firstName: "John" });
59+
}),
60+
];
61+
```
62+
63+
### 2. Context Fetch (`ctx-fetch`)
64+
65+
Replaces `ctx.fetch(req)` with `fetch(bypass(req))`:
66+
67+
**Before:**
68+
69+
```typescript
70+
import { rest } from "msw";
71+
72+
rest.get("/user", async (req, res, ctx) => {
73+
const originalRequest = await ctx.fetch(req);
74+
return res(ctx.json({ firstName: "John" }));
75+
});
76+
```
77+
78+
**After:**
79+
80+
```typescript
81+
import { bypass, http, HttpResponse } from "msw";
82+
83+
http.get("/user", async ({ request }) => {
84+
let req = request;
85+
const originalRequest = await fetch(bypass(req));
86+
return HttpResponse.json({ firstName: "John" });
87+
});
88+
```
89+
90+
### 3. Request Passthrough (`req-passthrough`)
91+
92+
Updates passthrough calls to use the exported function:
93+
94+
**Before:**
95+
96+
```typescript
97+
import { rest } from "msw";
98+
99+
rest.get("/resource", (req, res, ctx) => {
100+
return req.passthrough();
101+
});
102+
```
103+
104+
**After:**
105+
106+
```typescript
107+
import { http, passthrough } from "msw";
108+
109+
http.get("/resource", () => {
110+
return passthrough();
111+
});
112+
```
113+
114+
### 4. Request Changes (`request-changes`)
115+
116+
Modernizes request object usage patterns:
117+
118+
- `req.url``new URL(request.url)`
119+
- `req.params` → direct destructuring from callback argument
120+
- `req.cookies` → direct destructuring from callback argument
121+
- `req.body` → removed (use `request.json()` instead)
122+
123+
**Before:**
124+
125+
```typescript
126+
import { rest } from "msw";
127+
128+
rest.get("/user", (req, res, ctx) => {
129+
const search = req.url.searchParams;
130+
const { cookies, body: reqBody, thing } = req;
131+
const userCookies = req.cookies.user;
132+
const requestParams = req.params.thing;
133+
return res(ctx.json({ firstName: "John" }));
134+
});
135+
```
136+
137+
**After:**
138+
139+
```typescript
140+
import { http, HttpResponse } from "msw";
141+
142+
http.get("/user", async ({ request, cookies }) => {
143+
let req = request;
144+
const search = new URL(req.url).searchParams;
145+
const body = await req.clone().json();
146+
const { thing } = req;
147+
const userCookies = cookies.user;
148+
const requestParams = params.thing;
149+
return HttpResponse.json({ firstName: "John" });
150+
});
151+
```
152+
153+
### 5. Response Usage (`response-usages`)
154+
155+
Converts old response patterns to new `HttpResponse` API:
156+
157+
**Before:**
158+
159+
```typescript
160+
import { rest } from "msw";
161+
162+
rest.get("/user", (req, res, ctx) => {
163+
return res(
164+
ctx.json({ id: "abc-123" }),
165+
ctx.cookie("roses", "red"),
166+
ctx.cookie("violets", "blue"),
167+
ctx.set("X-Custom", "value")
168+
);
169+
});
170+
```
171+
172+
**After:**
173+
174+
```typescript
175+
import { http, HttpResponse } from "msw";
176+
177+
http.get("/user", () => {
178+
return HttpResponse.json(
179+
{ id: "abc-123" },
180+
{
181+
headers: { "X-Custom": "value", "Set-Cookie": "roses=red;violets=blue;" },
182+
}
183+
);
184+
});
185+
```
186+
187+
### 6. Callback Signature (`callback-signature`)
188+
189+
Updates handler callback signatures and removes unused variables:
190+
191+
**Before:**
192+
193+
```typescript
194+
import { rest } from "msw";
195+
196+
rest.get("/resource", (req, res, ctx) => {
197+
const userCookie = cookies.user;
198+
const url = new URL(request.url);
199+
doSomething(url);
200+
userCookie.doSomething();
201+
return HttpResponse.json({ id: "abc-123" });
202+
});
203+
```
204+
205+
**After:**
206+
207+
```typescript
208+
import { http, HttpResponse } from "msw";
209+
210+
http.get("/resource", ({ request, cookies }) => {
211+
let req = request;
212+
const userCookie = cookies.user;
213+
const url = new URL(req.url);
214+
doSomething(url);
215+
userCookie.doSomething();
216+
return HttpResponse.json({ id: "abc-123" });
217+
});
218+
```
219+
220+
### 7. Lifecycle Events (`lifecycle-events-signature`)
221+
222+
Updates lifecycle event callback signatures:
223+
224+
**Before:**
225+
226+
```typescript
227+
import { server } from "msw";
228+
229+
server.events.on("request:start", (req, reqId) => {
230+
doStuff(req, reqId);
231+
});
232+
```
233+
234+
**After:**
235+
236+
```typescript
237+
import { server } from "msw";
238+
239+
server.events.on("request:start", ({ request, reqId }) => {
240+
let req = request;
241+
doStuff(req, reqId);
242+
});
243+
```
244+
245+
### 8. Print Handlers (`print-handler`)
246+
247+
Replaces deprecated `printHandlers()` with new method:
248+
249+
**Before:**
250+
251+
```typescript
252+
worker.printHandlers();
253+
```
254+
255+
**After:**
256+
257+
```typescript
258+
worker.forEach((handler) => {
259+
console.log(handler.info.header);
260+
});
261+
```
262+
263+
### 9. Type Arguments (`type-args`)
264+
265+
Fixes generic type argument order for type safety:
266+
267+
**Before:**
268+
269+
```typescript
270+
import { rest } from "msw";
271+
272+
rest.get("/resource", (req, res, ctx) => {
273+
return res(ctx.json({ firstName: "John" }));
274+
});
275+
```
276+
277+
**After:**
278+
279+
```typescript
280+
import { http, HttpResponse } from "msw";
281+
282+
http.get("/resource", () => {
283+
return HttpResponse.json({ firstName: "John" });
284+
});
285+
```
286+
287+
## Important Notes
288+
289+
⚠️ **Custom Factory Functions**: This codemod does not change signatures of MSW handlers when called through custom factory functions. You'll need to update these manually.
290+
291+
⚠️ **Request Body Usage**: If you were using `req.body`, the codemod assumes you want `await request.json()`. You may need to adjust for other body types manually.
292+
293+
⚠️ **Complete Migration**: This codemod performs all necessary transformations in the correct order to ensure your code properly migrates to MSW v2.
294+
295+
## Development
296+
297+
```bash
298+
# Test the transformation
299+
npm test
300+
301+
# Validate the workflow
302+
codemod validate -w workflow.yaml
303+
304+
# Publish to registry
305+
codemod login
306+
codemod publish
307+
```
308+
309+
## Resources
310+
311+
- [MSW v2 Migration Guide](https://mswjs.io/docs/migrations/1.x-to-2.x)
312+
- [MSW v2 Documentation](https://mswjs.io/docs)
313+
314+
## License
315+
316+
MIT
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
schema_version: "1.0"
2+
3+
name: "msw-2-upgrade-recipe"
4+
version: "0.1.5"
5+
description: "Transform legacy code patterns"
6+
author: "Author <[email protected]>"
7+
license: "MIT"
8+
workflow: "workflow.yaml"
9+
category: "migration"
10+
repository: "https://github.com/codemod/msw-codemods/tree/main/codemods/msw-2-upgrade-recipe"
11+
12+
targets:
13+
languages: ["typescript"]
14+
15+
keywords: ["transformation", "migration"]
16+
17+
registry:
18+
access: "public"
19+
visibility: "public"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "msw-2-upgrade-recipe",
3+
"version": "0.1.0",
4+
"description": "Transform legacy code patterns",
5+
"type": "module",
6+
"devDependencies": {
7+
"@codemod.com/jssg-types": "^1.0.3",
8+
"typescript": "^5.8.3"
9+
},
10+
"scripts": {
11+
"test": "npx codemod@latest jssg test -l typescript ./scripts/codemod.ts",
12+
"check-types": "npx tsc --noEmit"
13+
}
14+
}

0 commit comments

Comments
 (0)