effect-start 0.14.0 → 0.16.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/package.json +8 -9
- package/src/Commander.test.ts +507 -245
- package/src/ContentNegotiation.test.ts +603 -0
- package/src/ContentNegotiation.ts +542 -0
- package/src/Entity.test.ts +592 -0
- package/src/Entity.ts +362 -0
- package/src/FileRouter.ts +16 -12
- package/src/{FileRouterCodegen.test.ts → FileRouterCodegen.todo.ts} +384 -219
- package/src/FileRouterCodegen.ts +6 -6
- package/src/FileRouterPattern.test.ts +93 -62
- package/src/FileRouter_files.test.ts +5 -5
- package/src/FileRouter_path.test.ts +121 -69
- package/src/FileRouter_tree.test.ts +62 -56
- package/src/FileSystemExtra.test.ts +46 -30
- package/src/Http.test.ts +319 -0
- package/src/Http.ts +167 -0
- package/src/HttpAppExtra.test.ts +39 -20
- package/src/HttpAppExtra.ts +0 -1
- package/src/HttpUtils.test.ts +35 -18
- package/src/HttpUtils.ts +2 -0
- package/src/PathPattern.test.ts +648 -0
- package/src/PathPattern.ts +485 -0
- package/src/Route.ts +266 -1069
- package/src/RouteBody.test.ts +234 -0
- package/src/RouteBody.ts +193 -0
- package/src/RouteHook.test.ts +40 -0
- package/src/RouteHook.ts +106 -0
- package/src/RouteHttp.test.ts +2906 -0
- package/src/RouteHttp.ts +427 -0
- package/src/RouteHttpTracer.ts +92 -0
- package/src/RouteMount.test.ts +481 -0
- package/src/RouteMount.ts +470 -0
- package/src/RouteSchema.test.ts +427 -0
- package/src/RouteSchema.ts +423 -0
- package/src/RouteTree.test.ts +494 -0
- package/src/RouteTree.ts +219 -0
- package/src/RouteTrie.test.ts +322 -0
- package/src/RouteTrie.ts +224 -0
- package/src/RouterPattern.test.ts +569 -548
- package/src/RouterPattern.ts +7 -7
- package/src/Start.ts +3 -3
- package/src/StreamExtra.ts +21 -1
- package/src/TuplePathPattern.ts +64 -0
- package/src/Values.test.ts +263 -0
- package/src/Values.ts +76 -0
- package/src/bun/BunBundle.test.ts +36 -42
- package/src/bun/BunBundle.ts +2 -2
- package/src/bun/BunBundle_imports.test.ts +4 -6
- package/src/bun/BunHttpServer.test.ts +183 -6
- package/src/bun/BunHttpServer.ts +72 -32
- package/src/bun/BunHttpServer_web.ts +18 -6
- package/src/bun/BunImportTrackerPlugin.test.ts +3 -3
- package/src/bun/BunRoute.test.ts +124 -442
- package/src/bun/BunRoute.ts +146 -286
- package/src/{BundleHttp.test.ts → bundler/BundleHttp.test.ts} +34 -60
- package/src/{BundleHttp.ts → bundler/BundleHttp.ts} +1 -2
- package/src/client/index.ts +1 -1
- package/src/{Effect_HttpRouter.test.ts → effect/HttpRouter.test.ts} +69 -90
- package/src/experimental/EncryptedCookies.test.ts +125 -64
- package/src/experimental/SseHttpResponse.ts +0 -1
- package/src/hyper/Hyper.ts +89 -0
- package/src/{HyperHtml.test.ts → hyper/HyperHtml.test.ts} +13 -13
- package/src/{HyperHtml.ts → hyper/HyperHtml.ts} +2 -2
- package/src/{jsx.d.ts → hyper/jsx.d.ts} +1 -1
- package/src/index.ts +3 -4
- package/src/middlewares/BasicAuthMiddleware.test.ts +29 -19
- package/src/{NodeFileSystem.ts → node/FileSystem.ts} +6 -2
- package/src/testing/TestHttpClient.test.ts +26 -26
- package/src/testing/TestLogger.test.ts +27 -14
- package/src/testing/TestLogger.ts +15 -9
- package/src/x/datastar/Datastar.test.ts +47 -48
- package/src/x/datastar/Datastar.ts +1 -1
- package/src/x/tailwind/TailwindPlugin.test.ts +56 -58
- package/src/x/tailwind/plugin.ts +1 -1
- package/src/FileHttpRouter.test.ts +0 -239
- package/src/FileHttpRouter.ts +0 -194
- package/src/Hyper.ts +0 -194
- package/src/Route.test.ts +0 -1370
- package/src/RouteRender.ts +0 -40
- package/src/Router.test.ts +0 -375
- package/src/Router.ts +0 -255
- package/src/bun/BunRoute_bundles.test.ts +0 -219
- /package/src/{Bundle.ts → bundler/Bundle.ts} +0 -0
- /package/src/{BundleFiles.ts → bundler/BundleFiles.ts} +0 -0
- /package/src/{HyperNode.ts → hyper/HyperNode.ts} +0 -0
- /package/src/{jsx-runtime.ts → hyper/jsx-runtime.ts} +0 -0
- /package/src/{NodeUtils.ts → node/Utils.ts} +0 -0
package/src/FileRouterCodegen.ts
CHANGED
|
@@ -6,8 +6,6 @@ import * as Schema from "effect/Schema"
|
|
|
6
6
|
import * as NPath from "node:path"
|
|
7
7
|
import * as FileRouter from "./FileRouter.ts"
|
|
8
8
|
import * as FileRouterPattern from "./FileRouterPattern.ts"
|
|
9
|
-
import * as Route from "./Route.ts"
|
|
10
|
-
import * as Router from "./Router.ts"
|
|
11
9
|
import * as SchemaExtra from "./SchemaExtra.ts"
|
|
12
10
|
|
|
13
11
|
export function validateRouteModule(
|
|
@@ -16,10 +14,13 @@ export function validateRouteModule(
|
|
|
16
14
|
if (typeof module !== "object" || module === null) {
|
|
17
15
|
return false
|
|
18
16
|
}
|
|
17
|
+
|
|
19
18
|
if (!("default" in module)) {
|
|
20
19
|
return false
|
|
21
20
|
}
|
|
22
|
-
|
|
21
|
+
|
|
22
|
+
// TODO: verify we're exporting a proper shape
|
|
23
|
+
return true
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export function generatePathParamsSchema(
|
|
@@ -79,7 +80,8 @@ export function validateRouteModules(
|
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
const routeSet = module.default
|
|
82
|
-
|
|
83
|
+
// extract user schema
|
|
84
|
+
const userSchema = undefined
|
|
83
85
|
|
|
84
86
|
if (
|
|
85
87
|
expectedSchema
|
|
@@ -187,8 +189,6 @@ export function generateCode(
|
|
|
187
189
|
|
|
188
190
|
return `${header}
|
|
189
191
|
|
|
190
|
-
import type { Router } from "${routerModuleId}"
|
|
191
|
-
|
|
192
192
|
export const routes = ${routesArray} as const
|
|
193
193
|
`
|
|
194
194
|
}
|
|
@@ -1,71 +1,100 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as test from "bun:test"
|
|
2
2
|
import * as FileRouterPattern from "./FileRouterPattern.ts"
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
test.it("empty path", () => {
|
|
5
|
+
test
|
|
6
|
+
.expect(FileRouterPattern.parse(""))
|
|
7
|
+
.toEqual([])
|
|
8
|
+
test
|
|
9
|
+
.expect(FileRouterPattern.parse("/"))
|
|
10
|
+
.toEqual([])
|
|
7
11
|
})
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
test.it("groups", () => {
|
|
14
|
+
test
|
|
15
|
+
.expect(FileRouterPattern.parse("(admin)"))
|
|
16
|
+
.toEqual([
|
|
17
|
+
{ _tag: "GroupSegment", name: "admin" },
|
|
18
|
+
])
|
|
19
|
+
test
|
|
20
|
+
.expect(FileRouterPattern.parse("/(admin)/users"))
|
|
21
|
+
.toEqual([
|
|
22
|
+
{ _tag: "GroupSegment", name: "admin" },
|
|
23
|
+
{ _tag: "LiteralSegment", value: "users" },
|
|
24
|
+
])
|
|
25
|
+
test
|
|
26
|
+
.expect(FileRouterPattern.parse("(auth)/login/(step1)"))
|
|
27
|
+
.toEqual([
|
|
28
|
+
{ _tag: "GroupSegment", name: "auth" },
|
|
29
|
+
{ _tag: "LiteralSegment", value: "login" },
|
|
30
|
+
{ _tag: "GroupSegment", name: "step1" },
|
|
31
|
+
])
|
|
22
32
|
})
|
|
23
33
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
test.it("handle files parsed as Literal", () => {
|
|
35
|
+
test
|
|
36
|
+
.expect(FileRouterPattern.parse("route.ts"))
|
|
37
|
+
.toEqual([
|
|
38
|
+
{ _tag: "LiteralSegment", value: "route.ts" },
|
|
39
|
+
])
|
|
40
|
+
test
|
|
41
|
+
.expect(FileRouterPattern.parse("/api/route.js"))
|
|
42
|
+
.toEqual([
|
|
43
|
+
{ _tag: "LiteralSegment", value: "api" },
|
|
44
|
+
{ _tag: "LiteralSegment", value: "route.js" },
|
|
45
|
+
])
|
|
46
|
+
test
|
|
47
|
+
.expect(FileRouterPattern.parse("layer.tsx"))
|
|
48
|
+
.toEqual([
|
|
49
|
+
{ _tag: "LiteralSegment", value: "layer.tsx" },
|
|
50
|
+
])
|
|
51
|
+
test
|
|
52
|
+
.expect(FileRouterPattern.parse("/blog/layer.jsx"))
|
|
53
|
+
.toEqual([
|
|
54
|
+
{ _tag: "LiteralSegment", value: "blog" },
|
|
55
|
+
{ _tag: "LiteralSegment", value: "layer.jsx" },
|
|
56
|
+
])
|
|
39
57
|
})
|
|
40
58
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
test.it("params and rest", () => {
|
|
60
|
+
test
|
|
61
|
+
.expect(FileRouterPattern.parse("users/[userId]/posts"))
|
|
62
|
+
.toEqual([
|
|
63
|
+
{ _tag: "LiteralSegment", value: "users" },
|
|
64
|
+
{ _tag: "ParamSegment", name: "userId" },
|
|
65
|
+
{ _tag: "LiteralSegment", value: "posts" },
|
|
66
|
+
])
|
|
67
|
+
test
|
|
68
|
+
.expect(FileRouterPattern.parse("api/[[...path]]"))
|
|
69
|
+
.toEqual([
|
|
70
|
+
{ _tag: "LiteralSegment", value: "api" },
|
|
71
|
+
{ _tag: "RestSegment", name: "path", optional: true },
|
|
72
|
+
])
|
|
52
73
|
})
|
|
53
74
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
75
|
+
test.it("invalid paths", () => {
|
|
76
|
+
test
|
|
77
|
+
.expect(() => FileRouterPattern.parse("$..."))
|
|
78
|
+
.toThrow()
|
|
79
|
+
test
|
|
80
|
+
.expect(() => FileRouterPattern.parse("invalid%char"))
|
|
81
|
+
.toThrow()
|
|
82
|
+
test
|
|
83
|
+
.expect(() => FileRouterPattern.parse("path with spaces"))
|
|
84
|
+
.toThrow()
|
|
58
85
|
})
|
|
59
86
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
87
|
+
test.it("segments with extensions (literal with dots)", () => {
|
|
88
|
+
test
|
|
89
|
+
.expect(FileRouterPattern.parse("events.json/route.ts"))
|
|
90
|
+
.toEqual([
|
|
91
|
+
{ _tag: "LiteralSegment", value: "events.json" },
|
|
92
|
+
{ _tag: "LiteralSegment", value: "route.ts" },
|
|
93
|
+
])
|
|
65
94
|
})
|
|
66
95
|
|
|
67
|
-
|
|
68
|
-
|
|
96
|
+
test.it("formatSegment", () => {
|
|
97
|
+
test
|
|
69
98
|
.expect(
|
|
70
99
|
FileRouterPattern.formatSegment({
|
|
71
100
|
_tag: "LiteralSegment",
|
|
@@ -73,31 +102,33 @@ t.it("formatSegment", () => {
|
|
|
73
102
|
}),
|
|
74
103
|
)
|
|
75
104
|
.toBe("users")
|
|
76
|
-
|
|
105
|
+
test
|
|
77
106
|
.expect(
|
|
78
107
|
FileRouterPattern.formatSegment({ _tag: "ParamSegment", name: "id" }),
|
|
79
108
|
)
|
|
80
109
|
.toBe("[id]")
|
|
81
|
-
|
|
110
|
+
test
|
|
82
111
|
.expect(
|
|
83
112
|
FileRouterPattern.formatSegment({ _tag: "GroupSegment", name: "admin" }),
|
|
84
113
|
)
|
|
85
114
|
.toBe("(admin)")
|
|
86
|
-
|
|
115
|
+
test
|
|
87
116
|
.expect(
|
|
88
117
|
FileRouterPattern.formatSegment({ _tag: "RestSegment", name: "path" }),
|
|
89
118
|
)
|
|
90
119
|
.toBe("[...path]")
|
|
91
120
|
})
|
|
92
121
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
122
|
+
test.it("format", () => {
|
|
123
|
+
test
|
|
124
|
+
.expect(FileRouterPattern.format([]))
|
|
125
|
+
.toBe("/")
|
|
126
|
+
test
|
|
96
127
|
.expect(
|
|
97
128
|
FileRouterPattern.format([{ _tag: "LiteralSegment", value: "users" }]),
|
|
98
129
|
)
|
|
99
130
|
.toBe("/users")
|
|
100
|
-
|
|
131
|
+
test
|
|
101
132
|
.expect(
|
|
102
133
|
FileRouterPattern.format([
|
|
103
134
|
{ _tag: "GroupSegment", name: "admin" },
|
|
@@ -105,7 +136,7 @@ t.it("format", () => {
|
|
|
105
136
|
]),
|
|
106
137
|
)
|
|
107
138
|
.toBe("/(admin)/users")
|
|
108
|
-
|
|
139
|
+
test
|
|
109
140
|
.expect(
|
|
110
141
|
FileRouterPattern.format([
|
|
111
142
|
{ _tag: "LiteralSegment", value: "users" },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as test from "bun:test"
|
|
2
2
|
import { MemoryFileSystem } from "effect-memfs"
|
|
3
3
|
import * as Effect from "effect/Effect"
|
|
4
4
|
import * as FileRouter from "./FileRouter.ts"
|
|
@@ -15,13 +15,13 @@ const Files = {
|
|
|
15
15
|
|
|
16
16
|
const effect = effectFn()
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
test.it("walks routes", () =>
|
|
19
19
|
effect(function*() {
|
|
20
20
|
const files = yield* FileRouter.walkRoutesDirectory("/routes").pipe(
|
|
21
21
|
Effect.provide(MemoryFileSystem.layerWith(Files)),
|
|
22
22
|
)
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
test
|
|
25
25
|
.expect(
|
|
26
26
|
files.map(v => v.modulePath),
|
|
27
27
|
)
|
|
@@ -35,7 +35,7 @@ t.it("walks routes", () =>
|
|
|
35
35
|
])
|
|
36
36
|
}))
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
test.it("walks routes with rest", () =>
|
|
39
39
|
effect(function*() {
|
|
40
40
|
const files = yield* FileRouter.walkRoutesDirectory("/routes").pipe(
|
|
41
41
|
Effect.provide(
|
|
@@ -47,7 +47,7 @@ t.it("walks routes with rest", () =>
|
|
|
47
47
|
),
|
|
48
48
|
)
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
test
|
|
51
51
|
.expect(
|
|
52
52
|
files.map(v => v.modulePath),
|
|
53
53
|
)
|
|
@@ -1,93 +1,145 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as test from "bun:test"
|
|
2
2
|
import * as FileRouter from "./FileRouter.ts"
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
test.it("empty path", () => {
|
|
5
|
+
test
|
|
6
|
+
.expect(FileRouter.parse(""))
|
|
7
|
+
.toEqual([])
|
|
8
|
+
test
|
|
9
|
+
.expect(FileRouter.parse("/"))
|
|
10
|
+
.toEqual([])
|
|
7
11
|
})
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
test.it("groups", () => {
|
|
14
|
+
test
|
|
15
|
+
.expect(FileRouter.parse("(admin)"))
|
|
16
|
+
.toEqual([
|
|
17
|
+
{ _tag: "GroupSegment", name: "admin" },
|
|
18
|
+
])
|
|
19
|
+
test
|
|
20
|
+
.expect(FileRouter.parse("/(admin)/users"))
|
|
21
|
+
.toEqual([
|
|
22
|
+
{ _tag: "GroupSegment", name: "admin" },
|
|
23
|
+
{ _tag: "LiteralSegment", value: "users" },
|
|
24
|
+
])
|
|
25
|
+
test
|
|
26
|
+
.expect(FileRouter.parse("(auth)/login/(step1)"))
|
|
27
|
+
.toEqual([
|
|
28
|
+
{ _tag: "GroupSegment", name: "auth" },
|
|
29
|
+
{ _tag: "LiteralSegment", value: "login" },
|
|
30
|
+
{ _tag: "GroupSegment", name: "step1" },
|
|
31
|
+
])
|
|
22
32
|
})
|
|
23
33
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
test.it("handle files parsed as Literal", () => {
|
|
35
|
+
test
|
|
36
|
+
.expect(FileRouter.parse("route.ts"))
|
|
37
|
+
.toEqual([
|
|
38
|
+
{ _tag: "LiteralSegment", value: "route.ts" },
|
|
39
|
+
])
|
|
40
|
+
test
|
|
41
|
+
.expect(FileRouter.parse("/api/route.js"))
|
|
42
|
+
.toEqual([
|
|
43
|
+
{ _tag: "LiteralSegment", value: "api" },
|
|
44
|
+
{ _tag: "LiteralSegment", value: "route.js" },
|
|
45
|
+
])
|
|
46
|
+
test
|
|
47
|
+
.expect(FileRouter.parse("layer.tsx"))
|
|
48
|
+
.toEqual([
|
|
49
|
+
{ _tag: "LiteralSegment", value: "layer.tsx" },
|
|
50
|
+
])
|
|
51
|
+
test
|
|
52
|
+
.expect(FileRouter.parse("/blog/layer.jsx"))
|
|
53
|
+
.toEqual([
|
|
54
|
+
{ _tag: "LiteralSegment", value: "blog" },
|
|
55
|
+
{ _tag: "LiteralSegment", value: "layer.jsx" },
|
|
56
|
+
])
|
|
39
57
|
})
|
|
40
58
|
|
|
41
|
-
|
|
59
|
+
test.it("parseRoute extracts handle from Literal", () => {
|
|
42
60
|
const route = FileRouter.parseRoute("users/route.tsx")
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
61
|
+
test
|
|
62
|
+
.expect(route.handle)
|
|
63
|
+
.toBe("route")
|
|
64
|
+
test
|
|
65
|
+
.expect(route.routePath)
|
|
66
|
+
.toBe("/users")
|
|
67
|
+
test
|
|
68
|
+
.expect(route.segments)
|
|
69
|
+
.toEqual([
|
|
70
|
+
{ _tag: "LiteralSegment", value: "users" },
|
|
71
|
+
])
|
|
48
72
|
|
|
49
73
|
const layer = FileRouter.parseRoute("api/layer.ts")
|
|
50
|
-
|
|
51
|
-
|
|
74
|
+
test
|
|
75
|
+
.expect(layer.handle)
|
|
76
|
+
.toBe("layer")
|
|
77
|
+
test
|
|
78
|
+
.expect(layer.routePath)
|
|
79
|
+
.toBe("/api")
|
|
52
80
|
})
|
|
53
81
|
|
|
54
|
-
|
|
82
|
+
test.it("parseRoute with groups", () => {
|
|
55
83
|
const route = FileRouter.parseRoute("(admin)/users/route.tsx")
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
84
|
+
test
|
|
85
|
+
.expect(route.handle)
|
|
86
|
+
.toBe("route")
|
|
87
|
+
test
|
|
88
|
+
.expect(route.routePath)
|
|
89
|
+
.toBe("/users")
|
|
90
|
+
test
|
|
91
|
+
.expect(route.segments)
|
|
92
|
+
.toEqual([
|
|
93
|
+
{ _tag: "GroupSegment", name: "admin" },
|
|
94
|
+
{ _tag: "LiteralSegment", value: "users" },
|
|
95
|
+
])
|
|
62
96
|
})
|
|
63
97
|
|
|
64
|
-
|
|
98
|
+
test.it("parseRoute with params and rest", () => {
|
|
65
99
|
const route = FileRouter.parseRoute("users/[userId]/posts/route.tsx")
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
100
|
+
test
|
|
101
|
+
.expect(route.handle)
|
|
102
|
+
.toBe("route")
|
|
103
|
+
test
|
|
104
|
+
.expect(route.routePath)
|
|
105
|
+
.toBe("/users/[userId]/posts")
|
|
106
|
+
test
|
|
107
|
+
.expect(route.segments)
|
|
108
|
+
.toEqual([
|
|
109
|
+
{ _tag: "LiteralSegment", value: "users" },
|
|
110
|
+
{ _tag: "ParamSegment", name: "userId" },
|
|
111
|
+
{ _tag: "LiteralSegment", value: "posts" },
|
|
112
|
+
])
|
|
73
113
|
|
|
74
114
|
const rest = FileRouter.parseRoute("api/[[...path]]/route.ts")
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
115
|
+
test
|
|
116
|
+
.expect(rest.handle)
|
|
117
|
+
.toBe("route")
|
|
118
|
+
test
|
|
119
|
+
.expect(rest.segments)
|
|
120
|
+
.toEqual([
|
|
121
|
+
{ _tag: "LiteralSegment", value: "api" },
|
|
122
|
+
{ _tag: "RestSegment", name: "path", optional: true },
|
|
123
|
+
])
|
|
80
124
|
})
|
|
81
125
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
126
|
+
test.it("invalid paths", () => {
|
|
127
|
+
test
|
|
128
|
+
.expect(() => FileRouter.parse("$..."))
|
|
129
|
+
.toThrow()
|
|
130
|
+
test
|
|
131
|
+
.expect(() => FileRouter.parse("invalid%char"))
|
|
132
|
+
.toThrow()
|
|
133
|
+
test
|
|
134
|
+
.expect(() => FileRouter.parse("path with spaces"))
|
|
135
|
+
.toThrow()
|
|
86
136
|
})
|
|
87
137
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
138
|
+
test.it("segments with extensions (literal with dots)", () => {
|
|
139
|
+
test
|
|
140
|
+
.expect(FileRouter.parse("events.json/route.ts"))
|
|
141
|
+
.toEqual([
|
|
142
|
+
{ _tag: "LiteralSegment", value: "events.json" },
|
|
143
|
+
{ _tag: "LiteralSegment", value: "route.ts" },
|
|
144
|
+
])
|
|
93
145
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as test from "bun:test"
|
|
2
2
|
import * as FileRouter from "./FileRouter.ts"
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
test.it("tree with root only", () => {
|
|
5
5
|
const handles = [
|
|
6
6
|
"route.tsx",
|
|
7
7
|
"layer.tsx",
|
|
@@ -9,31 +9,35 @@ t.it("tree with root only", () => {
|
|
|
9
9
|
.map(FileRouter.parseRoute)
|
|
10
10
|
const tree = FileRouter.treeFromRouteHandles(handles)
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
test
|
|
13
|
+
.expect(tree)
|
|
14
|
+
.toEqual({
|
|
15
|
+
path: "/",
|
|
16
|
+
handles: [
|
|
17
|
+
test.expect.objectContaining({
|
|
18
|
+
handle: "route",
|
|
19
|
+
}),
|
|
20
|
+
test.expect.objectContaining({
|
|
21
|
+
handle: "layer",
|
|
22
|
+
}),
|
|
23
|
+
],
|
|
24
|
+
})
|
|
23
25
|
})
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
test.it("tree without root", () => {
|
|
26
28
|
const handles = []
|
|
27
29
|
.map(FileRouter.parseRoute)
|
|
28
30
|
const tree = FileRouter.treeFromRouteHandles(handles)
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
test
|
|
33
|
+
.expect(tree)
|
|
34
|
+
.toEqual({
|
|
35
|
+
path: "/",
|
|
36
|
+
handles: [],
|
|
37
|
+
})
|
|
34
38
|
})
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
test.it("deep tree", () => {
|
|
37
41
|
const handles = [
|
|
38
42
|
"users/route.tsx",
|
|
39
43
|
"users/layer.tsx",
|
|
@@ -43,41 +47,43 @@ t.it("deep tree", () => {
|
|
|
43
47
|
.map(FileRouter.parseRoute)
|
|
44
48
|
const tree = FileRouter.treeFromRouteHandles(handles)
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
50
|
+
test
|
|
51
|
+
.expect(tree)
|
|
52
|
+
.toEqual({
|
|
53
|
+
path: "/",
|
|
54
|
+
handles: [
|
|
55
|
+
test.expect.objectContaining({
|
|
56
|
+
handle: "layer",
|
|
57
|
+
}),
|
|
58
|
+
],
|
|
59
|
+
children: [
|
|
60
|
+
{
|
|
61
|
+
path: "/users",
|
|
62
|
+
handles: [
|
|
63
|
+
test.expect.objectContaining({
|
|
64
|
+
handle: "route",
|
|
65
|
+
}),
|
|
66
|
+
test.expect.objectContaining({
|
|
67
|
+
handle: "layer",
|
|
68
|
+
}),
|
|
69
|
+
],
|
|
70
|
+
children: [
|
|
71
|
+
{
|
|
72
|
+
path: "/[userId]",
|
|
73
|
+
handles: [
|
|
74
|
+
test.expect.objectContaining({
|
|
75
|
+
handle: "route",
|
|
76
|
+
}),
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
})
|
|
77
83
|
})
|
|
78
84
|
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
test.it("throws on overlapping routes from groups", () => {
|
|
86
|
+
test
|
|
81
87
|
.expect(() => {
|
|
82
88
|
const handles = [
|
|
83
89
|
"(admin)/users/route.tsx",
|
|
@@ -92,8 +98,8 @@ t.it("throws on overlapping routes from groups", () => {
|
|
|
92
98
|
.toThrow("Conflicting routes detected at path /users")
|
|
93
99
|
})
|
|
94
100
|
|
|
95
|
-
|
|
96
|
-
|
|
101
|
+
test.it("throws on overlapping routes with same path", () => {
|
|
102
|
+
test
|
|
97
103
|
.expect(() => {
|
|
98
104
|
const handles = [
|
|
99
105
|
"about/route.tsx",
|
|
@@ -108,8 +114,8 @@ t.it("throws on overlapping routes with same path", () => {
|
|
|
108
114
|
.toThrow("Conflicting routes detected at path /about")
|
|
109
115
|
})
|
|
110
116
|
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
test.it("allows route and layer at same path", () => {
|
|
118
|
+
test
|
|
113
119
|
.expect(() => {
|
|
114
120
|
const handles = [
|
|
115
121
|
"users/route.tsx",
|