effect-start 0.9.0
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 +21 -0
- package/README.md +109 -0
- package/package.json +57 -0
- package/src/Bundle.ts +167 -0
- package/src/BundleFiles.ts +174 -0
- package/src/BundleHttp.test.ts +160 -0
- package/src/BundleHttp.ts +259 -0
- package/src/Commander.test.ts +1378 -0
- package/src/Commander.ts +672 -0
- package/src/Datastar.test.ts +267 -0
- package/src/Datastar.ts +68 -0
- package/src/Effect_HttpRouter.test.ts +570 -0
- package/src/EncryptedCookies.test.ts +427 -0
- package/src/EncryptedCookies.ts +451 -0
- package/src/FileHttpRouter.test.ts +207 -0
- package/src/FileHttpRouter.ts +122 -0
- package/src/FileRouter.ts +405 -0
- package/src/FileRouterCodegen.test.ts +598 -0
- package/src/FileRouterCodegen.ts +251 -0
- package/src/FileRouter_files.test.ts +64 -0
- package/src/FileRouter_path.test.ts +132 -0
- package/src/FileRouter_tree.test.ts +126 -0
- package/src/FileSystemExtra.ts +102 -0
- package/src/HttpAppExtra.ts +127 -0
- package/src/Hyper.ts +194 -0
- package/src/HyperHtml.test.ts +90 -0
- package/src/HyperHtml.ts +139 -0
- package/src/HyperNode.ts +37 -0
- package/src/JsModule.test.ts +14 -0
- package/src/JsModule.ts +116 -0
- package/src/PublicDirectory.test.ts +280 -0
- package/src/PublicDirectory.ts +108 -0
- package/src/Route.test.ts +873 -0
- package/src/Route.ts +992 -0
- package/src/Router.ts +80 -0
- package/src/SseHttpResponse.ts +55 -0
- package/src/Start.ts +133 -0
- package/src/StartApp.ts +43 -0
- package/src/StartHttp.ts +42 -0
- package/src/StreamExtra.ts +146 -0
- package/src/TestHttpClient.test.ts +54 -0
- package/src/TestHttpClient.ts +100 -0
- package/src/bun/BunBundle.test.ts +277 -0
- package/src/bun/BunBundle.ts +309 -0
- package/src/bun/BunBundle_imports.test.ts +50 -0
- package/src/bun/BunFullstackServer.ts +45 -0
- package/src/bun/BunFullstackServer_httpServer.ts +541 -0
- package/src/bun/BunImportTrackerPlugin.test.ts +77 -0
- package/src/bun/BunImportTrackerPlugin.ts +97 -0
- package/src/bun/BunTailwindPlugin.test.ts +335 -0
- package/src/bun/BunTailwindPlugin.ts +322 -0
- package/src/bun/BunVirtualFilesPlugin.ts +59 -0
- package/src/bun/index.ts +4 -0
- package/src/client/Overlay.ts +34 -0
- package/src/client/ScrollState.ts +120 -0
- package/src/client/index.ts +101 -0
- package/src/index.ts +24 -0
- package/src/jsx-datastar.d.ts +63 -0
- package/src/jsx-runtime.ts +23 -0
- package/src/jsx.d.ts +4402 -0
- package/src/testing.ts +55 -0
- package/src/x/cloudflare/CloudflareTunnel.ts +110 -0
- package/src/x/cloudflare/index.ts +1 -0
- package/src/x/datastar/Datastar.test.ts +267 -0
- package/src/x/datastar/Datastar.ts +68 -0
- package/src/x/datastar/index.ts +4 -0
- package/src/x/datastar/jsx-datastar.d.ts +63 -0
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
import * as FileSystem from "@effect/platform/FileSystem"
|
|
2
|
+
import * as t from "bun:test"
|
|
3
|
+
import { MemoryFileSystem } from "effect-memfs"
|
|
4
|
+
import * as Effect from "effect/Effect"
|
|
5
|
+
import { parseRoute } from "./FileRouter.ts"
|
|
6
|
+
import type { RouteHandle } from "./FileRouter.ts"
|
|
7
|
+
import * as FileRouterCodegen from "./FileRouterCodegen.ts"
|
|
8
|
+
import * as Route from "./Route.ts"
|
|
9
|
+
import { effectFn } from "./testing.ts"
|
|
10
|
+
|
|
11
|
+
t.it("generates code for routes only", () => {
|
|
12
|
+
const handles: RouteHandle[] = [
|
|
13
|
+
parseRoute("route.tsx"),
|
|
14
|
+
parseRoute("about/route.tsx"),
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
18
|
+
|
|
19
|
+
const expected = `/**
|
|
20
|
+
* Auto-generated by effect-start.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { Router } from "effect-start"
|
|
24
|
+
|
|
25
|
+
export const modules = [
|
|
26
|
+
{
|
|
27
|
+
path: "/",
|
|
28
|
+
segments: [],
|
|
29
|
+
load: () => import("./route.tsx"),
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
path: "/about",
|
|
33
|
+
segments: [
|
|
34
|
+
{ literal: "about" },
|
|
35
|
+
],
|
|
36
|
+
load: () => import("./about/route.tsx"),
|
|
37
|
+
},
|
|
38
|
+
] as const
|
|
39
|
+
`
|
|
40
|
+
|
|
41
|
+
t
|
|
42
|
+
.expect(code)
|
|
43
|
+
.toBe(expected)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
t.it("generates code with layers", () => {
|
|
47
|
+
const handles: RouteHandle[] = [
|
|
48
|
+
parseRoute("layer.tsx"),
|
|
49
|
+
parseRoute("route.tsx"),
|
|
50
|
+
parseRoute("about/route.tsx"),
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
54
|
+
|
|
55
|
+
const expected = `/**
|
|
56
|
+
* Auto-generated by effect-start.
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
import type { Router } from "effect-start"
|
|
60
|
+
|
|
61
|
+
export const modules = [
|
|
62
|
+
{
|
|
63
|
+
path: "/",
|
|
64
|
+
segments: [],
|
|
65
|
+
load: () => import("./route.tsx"),
|
|
66
|
+
layers: [
|
|
67
|
+
() => import("./layer.tsx"),
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
path: "/about",
|
|
72
|
+
segments: [
|
|
73
|
+
{ literal: "about" },
|
|
74
|
+
],
|
|
75
|
+
load: () => import("./about/route.tsx"),
|
|
76
|
+
layers: [
|
|
77
|
+
() => import("./layer.tsx"),
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
] as const
|
|
81
|
+
`
|
|
82
|
+
|
|
83
|
+
t
|
|
84
|
+
.expect(code)
|
|
85
|
+
.toBe(expected)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
t.it("generates code with nested layers", () => {
|
|
89
|
+
const handles: RouteHandle[] = [
|
|
90
|
+
parseRoute("layer.tsx"),
|
|
91
|
+
parseRoute("dashboard/layer.tsx"),
|
|
92
|
+
parseRoute("dashboard/route.tsx"),
|
|
93
|
+
parseRoute("dashboard/settings/route.tsx"),
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
97
|
+
|
|
98
|
+
const expected = `/**
|
|
99
|
+
* Auto-generated by effect-start.
|
|
100
|
+
*/
|
|
101
|
+
|
|
102
|
+
import type { Router } from "effect-start"
|
|
103
|
+
|
|
104
|
+
export const modules = [
|
|
105
|
+
{
|
|
106
|
+
path: "/dashboard",
|
|
107
|
+
segments: [
|
|
108
|
+
{ literal: "dashboard" },
|
|
109
|
+
],
|
|
110
|
+
load: () => import("./dashboard/route.tsx"),
|
|
111
|
+
layers: [
|
|
112
|
+
() => import("./layer.tsx"),
|
|
113
|
+
() => import("./dashboard/layer.tsx"),
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
path: "/dashboard/settings",
|
|
118
|
+
segments: [
|
|
119
|
+
{ literal: "dashboard" },
|
|
120
|
+
{ literal: "settings" },
|
|
121
|
+
],
|
|
122
|
+
load: () => import("./dashboard/settings/route.tsx"),
|
|
123
|
+
layers: [
|
|
124
|
+
() => import("./layer.tsx"),
|
|
125
|
+
() => import("./dashboard/layer.tsx"),
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
] as const
|
|
129
|
+
`
|
|
130
|
+
|
|
131
|
+
t
|
|
132
|
+
.expect(code)
|
|
133
|
+
.toBe(expected)
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
t.it("only includes group layers for routes in that group", () => {
|
|
137
|
+
const handles: RouteHandle[] = [
|
|
138
|
+
parseRoute("layer.tsx"),
|
|
139
|
+
parseRoute("(admin)/layer.ts"),
|
|
140
|
+
parseRoute("(admin)/users/route.tsx"),
|
|
141
|
+
parseRoute("movies/route.tsx"),
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
145
|
+
|
|
146
|
+
t
|
|
147
|
+
.expect(code)
|
|
148
|
+
.toContain("path: \"/users\"")
|
|
149
|
+
|
|
150
|
+
t
|
|
151
|
+
.expect(code)
|
|
152
|
+
.toContain("path: \"/movies\"")
|
|
153
|
+
|
|
154
|
+
// /users should have both root layer and (admin) layer
|
|
155
|
+
t
|
|
156
|
+
.expect(code)
|
|
157
|
+
.toContain("() => import(\"./layer.tsx\")")
|
|
158
|
+
|
|
159
|
+
t
|
|
160
|
+
.expect(code)
|
|
161
|
+
.toContain("() => import(\"./(admin)/layer.ts\")")
|
|
162
|
+
|
|
163
|
+
// /movies should only have root layer, not (admin) layer
|
|
164
|
+
const expectedMovies = ` {
|
|
165
|
+
path: "/movies",
|
|
166
|
+
segments: [
|
|
167
|
+
{ literal: "movies" },
|
|
168
|
+
],
|
|
169
|
+
load: () => import("./movies/route.tsx"),
|
|
170
|
+
layers: [
|
|
171
|
+
() => import("./layer.tsx"),
|
|
172
|
+
],
|
|
173
|
+
},`
|
|
174
|
+
|
|
175
|
+
t
|
|
176
|
+
.expect(code)
|
|
177
|
+
.toContain(expectedMovies)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
t.it("handles dynamic routes with params", () => {
|
|
181
|
+
const handles: RouteHandle[] = [
|
|
182
|
+
parseRoute("users/route.tsx"),
|
|
183
|
+
parseRoute("users/[userId]/route.tsx"),
|
|
184
|
+
parseRoute("posts/[postId]/comments/[commentId]/route.tsx"),
|
|
185
|
+
]
|
|
186
|
+
|
|
187
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
188
|
+
|
|
189
|
+
t
|
|
190
|
+
.expect(code)
|
|
191
|
+
.toContain("path: \"/users\"")
|
|
192
|
+
t
|
|
193
|
+
.expect(code)
|
|
194
|
+
.toContain("path: \"/users/[userId]\"")
|
|
195
|
+
t
|
|
196
|
+
.expect(code)
|
|
197
|
+
.toContain("{ literal: \"users\" }")
|
|
198
|
+
t
|
|
199
|
+
.expect(code)
|
|
200
|
+
.toContain("{ param: \"userId\" }")
|
|
201
|
+
t
|
|
202
|
+
.expect(code)
|
|
203
|
+
.toContain("{ param: \"postId\" }")
|
|
204
|
+
t
|
|
205
|
+
.expect(code)
|
|
206
|
+
.toContain("{ param: \"commentId\" }")
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
t.it("handles rest parameters", () => {
|
|
210
|
+
const handles: RouteHandle[] = [
|
|
211
|
+
parseRoute("docs/[[...slug]]/route.tsx"),
|
|
212
|
+
parseRoute("api/[...path]/route.tsx"),
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
216
|
+
|
|
217
|
+
t
|
|
218
|
+
.expect(code)
|
|
219
|
+
.toContain("path: \"/docs/[[...slug]]\"")
|
|
220
|
+
t
|
|
221
|
+
.expect(code)
|
|
222
|
+
.toContain("path: \"/api/[...path]\"")
|
|
223
|
+
t
|
|
224
|
+
.expect(code)
|
|
225
|
+
.toContain("{ rest: \"slug\", optional: true }")
|
|
226
|
+
t
|
|
227
|
+
.expect(code)
|
|
228
|
+
.toContain("{ rest: \"path\" }")
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
t.it("handles groups in path", () => {
|
|
232
|
+
const handles: RouteHandle[] = [
|
|
233
|
+
parseRoute("(admin)/users/route.tsx"),
|
|
234
|
+
parseRoute("(admin)/layer.tsx"),
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
238
|
+
|
|
239
|
+
t
|
|
240
|
+
.expect(code)
|
|
241
|
+
.toContain("path: \"/users\"") // groups stripped from URL
|
|
242
|
+
t
|
|
243
|
+
.expect(code)
|
|
244
|
+
.toContain("{ group: \"admin\" }")
|
|
245
|
+
t
|
|
246
|
+
.expect(code)
|
|
247
|
+
.toContain("layers: [\n () => import(\"./(admin)/layer.tsx\"),\n ]")
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
t.it("generates correct variable names for root routes", () => {
|
|
251
|
+
const handles: RouteHandle[] = [
|
|
252
|
+
parseRoute("route.tsx"),
|
|
253
|
+
]
|
|
254
|
+
|
|
255
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
256
|
+
|
|
257
|
+
t
|
|
258
|
+
.expect(code)
|
|
259
|
+
.toContain("path: \"/\"")
|
|
260
|
+
t
|
|
261
|
+
.expect(code)
|
|
262
|
+
.toContain("segments: []")
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
t.it("handles routes with dots in path segments", () => {
|
|
266
|
+
const handles: RouteHandle[] = [
|
|
267
|
+
parseRoute("events.json/route.ts"),
|
|
268
|
+
parseRoute("config.yaml.backup/route.ts"),
|
|
269
|
+
]
|
|
270
|
+
|
|
271
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
272
|
+
|
|
273
|
+
t
|
|
274
|
+
.expect(code)
|
|
275
|
+
.toContain("path: \"/events.json\"")
|
|
276
|
+
t
|
|
277
|
+
.expect(code)
|
|
278
|
+
.toContain("path: \"/config.yaml.backup\"")
|
|
279
|
+
t
|
|
280
|
+
.expect(code)
|
|
281
|
+
.toContain("{ literal: \"events.json\" }")
|
|
282
|
+
t
|
|
283
|
+
.expect(code)
|
|
284
|
+
.toContain("{ literal: \"config.yaml.backup\" }")
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
t.it("uses default module identifier", () => {
|
|
288
|
+
const handles: RouteHandle[] = [
|
|
289
|
+
parseRoute("route.tsx"),
|
|
290
|
+
]
|
|
291
|
+
|
|
292
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
293
|
+
|
|
294
|
+
t
|
|
295
|
+
.expect(code)
|
|
296
|
+
.toContain("import type { Router } from \"effect-start\"")
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
t.it("generates empty modules array when no handles provided", () => {
|
|
300
|
+
const handles: RouteHandle[] = []
|
|
301
|
+
|
|
302
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
303
|
+
|
|
304
|
+
t
|
|
305
|
+
.expect(code)
|
|
306
|
+
.toContain("export const modules = [] as const")
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
t.it("only includes routes in modules, not layers", () => {
|
|
310
|
+
const handles: RouteHandle[] = [
|
|
311
|
+
parseRoute("layer.tsx"),
|
|
312
|
+
parseRoute("users/layer.tsx"),
|
|
313
|
+
]
|
|
314
|
+
|
|
315
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
316
|
+
|
|
317
|
+
t
|
|
318
|
+
.expect(code)
|
|
319
|
+
.toContain("export const modules = [] as const")
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
t.it("complex nested routes with multiple layers", () => {
|
|
323
|
+
const handles: RouteHandle[] = [
|
|
324
|
+
parseRoute("layer.tsx"),
|
|
325
|
+
parseRoute("(auth)/layer.tsx"),
|
|
326
|
+
parseRoute("(auth)/login/route.tsx"),
|
|
327
|
+
parseRoute("(auth)/signup/route.tsx"),
|
|
328
|
+
parseRoute("dashboard/layer.tsx"),
|
|
329
|
+
parseRoute("dashboard/route.tsx"),
|
|
330
|
+
parseRoute("dashboard/settings/route.tsx"),
|
|
331
|
+
]
|
|
332
|
+
|
|
333
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
334
|
+
|
|
335
|
+
t
|
|
336
|
+
.expect(code)
|
|
337
|
+
.toContain("path: \"/login\"") // group stripped
|
|
338
|
+
t
|
|
339
|
+
.expect(code)
|
|
340
|
+
.toContain("path: \"/signup\"") // group stripped
|
|
341
|
+
t
|
|
342
|
+
.expect(code)
|
|
343
|
+
.toContain("path: \"/dashboard\"")
|
|
344
|
+
t
|
|
345
|
+
.expect(code)
|
|
346
|
+
.toContain("path: \"/dashboard/settings\"")
|
|
347
|
+
|
|
348
|
+
// Check layers are properly inherited
|
|
349
|
+
t
|
|
350
|
+
.expect(code)
|
|
351
|
+
.toContain("() => import(\"./layer.tsx\")")
|
|
352
|
+
t
|
|
353
|
+
.expect(code)
|
|
354
|
+
.toContain("() => import(\"./(auth)/layer.tsx\")")
|
|
355
|
+
t
|
|
356
|
+
.expect(code)
|
|
357
|
+
.toContain("() => import(\"./dashboard/layer.tsx\")")
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
t.it("handles routes with hyphens and underscores in path segments", () => {
|
|
361
|
+
const handles: RouteHandle[] = [
|
|
362
|
+
parseRoute("api-v1/route.ts"),
|
|
363
|
+
parseRoute("my_resource/route.ts"),
|
|
364
|
+
]
|
|
365
|
+
|
|
366
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
367
|
+
|
|
368
|
+
t
|
|
369
|
+
.expect(code)
|
|
370
|
+
.toContain("path: \"/api-v1\"")
|
|
371
|
+
t
|
|
372
|
+
.expect(code)
|
|
373
|
+
.toContain("path: \"/my_resource\"")
|
|
374
|
+
t
|
|
375
|
+
.expect(code)
|
|
376
|
+
.toContain("{ literal: \"api-v1\" }")
|
|
377
|
+
t
|
|
378
|
+
.expect(code)
|
|
379
|
+
.toContain("{ literal: \"my_resource\" }")
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
t.it("validateRouteModule returns true for valid modules", () => {
|
|
383
|
+
const validRoute = Route.text(Effect.succeed("Hello"))
|
|
384
|
+
|
|
385
|
+
t
|
|
386
|
+
.expect(
|
|
387
|
+
FileRouterCodegen.validateRouteModule({ default: validRoute }),
|
|
388
|
+
)
|
|
389
|
+
.toBe(true)
|
|
390
|
+
|
|
391
|
+
t
|
|
392
|
+
.expect(
|
|
393
|
+
FileRouterCodegen.validateRouteModule({
|
|
394
|
+
default: Route.html(Effect.succeed("<div>Hello</div>")),
|
|
395
|
+
}),
|
|
396
|
+
)
|
|
397
|
+
.toBe(true)
|
|
398
|
+
|
|
399
|
+
t
|
|
400
|
+
.expect(
|
|
401
|
+
FileRouterCodegen.validateRouteModule({
|
|
402
|
+
default: Route.json(Effect.succeed({ message: "Hello" })),
|
|
403
|
+
}),
|
|
404
|
+
)
|
|
405
|
+
.toBe(true)
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
t.it("validateRouteModule returns false for invalid modules", () => {
|
|
409
|
+
t
|
|
410
|
+
.expect(FileRouterCodegen.validateRouteModule({}))
|
|
411
|
+
.toBe(false)
|
|
412
|
+
|
|
413
|
+
t
|
|
414
|
+
.expect(
|
|
415
|
+
FileRouterCodegen.validateRouteModule({ default: {} }),
|
|
416
|
+
)
|
|
417
|
+
.toBe(false)
|
|
418
|
+
|
|
419
|
+
t
|
|
420
|
+
.expect(
|
|
421
|
+
FileRouterCodegen.validateRouteModule({ default: "not a route" }),
|
|
422
|
+
)
|
|
423
|
+
.toBe(false)
|
|
424
|
+
|
|
425
|
+
t
|
|
426
|
+
.expect(
|
|
427
|
+
FileRouterCodegen.validateRouteModule({ foo: "bar" }),
|
|
428
|
+
)
|
|
429
|
+
.toBe(false)
|
|
430
|
+
|
|
431
|
+
t
|
|
432
|
+
.expect(FileRouterCodegen.validateRouteModule(null))
|
|
433
|
+
.toBe(false)
|
|
434
|
+
|
|
435
|
+
t
|
|
436
|
+
.expect(FileRouterCodegen.validateRouteModule(undefined))
|
|
437
|
+
.toBe(false)
|
|
438
|
+
|
|
439
|
+
t
|
|
440
|
+
.expect(FileRouterCodegen.validateRouteModule("string"))
|
|
441
|
+
.toBe(false)
|
|
442
|
+
|
|
443
|
+
t
|
|
444
|
+
.expect(FileRouterCodegen.validateRouteModule(42))
|
|
445
|
+
.toBe(false)
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
t.it("mixed params and rest in same route", () => {
|
|
449
|
+
const handles: RouteHandle[] = [
|
|
450
|
+
parseRoute("users/[userId]/files/[...path]/route.tsx"),
|
|
451
|
+
]
|
|
452
|
+
|
|
453
|
+
const code = FileRouterCodegen.generateCode(handles)
|
|
454
|
+
|
|
455
|
+
t
|
|
456
|
+
.expect(code)
|
|
457
|
+
.toContain("{ param: \"userId\" }")
|
|
458
|
+
t
|
|
459
|
+
.expect(code)
|
|
460
|
+
.toContain("{ rest: \"path\" }")
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
const effect = effectFn()
|
|
464
|
+
|
|
465
|
+
const update_FilesWithRoutes = {
|
|
466
|
+
"/routes/route.tsx": "",
|
|
467
|
+
"/routes/about/route.tsx": "",
|
|
468
|
+
"/routes/_manifest.ts": "",
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
t.it("update() > writes file", () =>
|
|
472
|
+
Effect
|
|
473
|
+
.gen(function*() {
|
|
474
|
+
yield* FileRouterCodegen.update("/routes")
|
|
475
|
+
|
|
476
|
+
const fs = yield* FileSystem.FileSystem
|
|
477
|
+
const content = yield* fs.readFileString("/routes/_manifest.ts")
|
|
478
|
+
|
|
479
|
+
t
|
|
480
|
+
.expect(content)
|
|
481
|
+
.toContain("export const modules =")
|
|
482
|
+
})
|
|
483
|
+
.pipe(
|
|
484
|
+
Effect.provide(MemoryFileSystem.layerWith(update_FilesWithRoutes)),
|
|
485
|
+
Effect.runPromise,
|
|
486
|
+
))
|
|
487
|
+
|
|
488
|
+
t.it("update() > writes only when it changes", () =>
|
|
489
|
+
Effect
|
|
490
|
+
.gen(function*() {
|
|
491
|
+
yield* FileRouterCodegen.update("/routes")
|
|
492
|
+
|
|
493
|
+
const fs = yield* FileSystem.FileSystem
|
|
494
|
+
const content = yield* fs.readFileString("/routes/_manifest.ts")
|
|
495
|
+
|
|
496
|
+
yield* FileRouterCodegen.update("/routes")
|
|
497
|
+
|
|
498
|
+
const content2 = yield* fs.readFileString("/routes/_manifest.ts")
|
|
499
|
+
|
|
500
|
+
t
|
|
501
|
+
.expect(content2)
|
|
502
|
+
.not
|
|
503
|
+
.toBe("")
|
|
504
|
+
|
|
505
|
+
t
|
|
506
|
+
.expect(content2)
|
|
507
|
+
.toBe(content)
|
|
508
|
+
})
|
|
509
|
+
.pipe(
|
|
510
|
+
Effect.provide(MemoryFileSystem.layerWith(update_FilesWithRoutes)),
|
|
511
|
+
Effect.runPromise,
|
|
512
|
+
))
|
|
513
|
+
|
|
514
|
+
t.it("update() > removes deleted routes from manifest", () =>
|
|
515
|
+
Effect
|
|
516
|
+
.gen(function*() {
|
|
517
|
+
const fs = yield* FileSystem.FileSystem
|
|
518
|
+
|
|
519
|
+
yield* FileRouterCodegen.update("/routes")
|
|
520
|
+
|
|
521
|
+
const content = yield* fs.readFileString("/routes/_manifest.ts")
|
|
522
|
+
|
|
523
|
+
t
|
|
524
|
+
.expect(content)
|
|
525
|
+
.toContain("path: \"/\"")
|
|
526
|
+
|
|
527
|
+
t
|
|
528
|
+
.expect(content)
|
|
529
|
+
.toContain("path: \"/about\"")
|
|
530
|
+
|
|
531
|
+
yield* fs.remove("/routes/about/route.tsx")
|
|
532
|
+
|
|
533
|
+
yield* FileRouterCodegen.update("/routes")
|
|
534
|
+
|
|
535
|
+
const content2 = yield* fs.readFileString("/routes/_manifest.ts")
|
|
536
|
+
|
|
537
|
+
t
|
|
538
|
+
.expect(content2)
|
|
539
|
+
.toContain("path: \"/\"")
|
|
540
|
+
|
|
541
|
+
t
|
|
542
|
+
.expect(content2)
|
|
543
|
+
.not
|
|
544
|
+
.toContain("path: \"/about\"")
|
|
545
|
+
})
|
|
546
|
+
.pipe(
|
|
547
|
+
Effect.provide(MemoryFileSystem.layerWith(update_FilesWithRoutes)),
|
|
548
|
+
Effect.runPromise,
|
|
549
|
+
))
|
|
550
|
+
|
|
551
|
+
t.it("update() > removes routes when entire directory is deleted", () =>
|
|
552
|
+
Effect
|
|
553
|
+
.gen(function*() {
|
|
554
|
+
const fs = yield* FileSystem.FileSystem
|
|
555
|
+
|
|
556
|
+
yield* fs.makeDirectory("/routes/users", { recursive: true })
|
|
557
|
+
|
|
558
|
+
yield* fs.writeFileString("/routes/users/route.tsx", "")
|
|
559
|
+
|
|
560
|
+
yield* FileRouterCodegen.update("/routes")
|
|
561
|
+
|
|
562
|
+
const content = yield* fs.readFileString("/routes/_manifest.ts")
|
|
563
|
+
|
|
564
|
+
t
|
|
565
|
+
.expect(content)
|
|
566
|
+
.toContain("path: \"/\"")
|
|
567
|
+
|
|
568
|
+
t
|
|
569
|
+
.expect(content)
|
|
570
|
+
.toContain("path: \"/about\"")
|
|
571
|
+
|
|
572
|
+
t
|
|
573
|
+
.expect(content)
|
|
574
|
+
.toContain("path: \"/users\"")
|
|
575
|
+
|
|
576
|
+
yield* fs.remove("/routes/users", { recursive: true })
|
|
577
|
+
|
|
578
|
+
yield* FileRouterCodegen.update("/routes")
|
|
579
|
+
|
|
580
|
+
const content2 = yield* fs.readFileString("/routes/_manifest.ts")
|
|
581
|
+
|
|
582
|
+
t
|
|
583
|
+
.expect(content2)
|
|
584
|
+
.toContain("path: \"/\"")
|
|
585
|
+
|
|
586
|
+
t
|
|
587
|
+
.expect(content2)
|
|
588
|
+
.toContain("path: \"/about\"")
|
|
589
|
+
|
|
590
|
+
t
|
|
591
|
+
.expect(content2)
|
|
592
|
+
.not
|
|
593
|
+
.toContain("path: \"/users\"")
|
|
594
|
+
})
|
|
595
|
+
.pipe(
|
|
596
|
+
Effect.provide(MemoryFileSystem.layerWith(update_FilesWithRoutes)),
|
|
597
|
+
Effect.runPromise,
|
|
598
|
+
))
|