effect-start 0.16.0 → 0.17.1
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/dist/Commander.d.ts +103 -0
- package/dist/Commander.js +333 -0
- package/dist/ContentNegotiation.d.ts +13 -0
- package/dist/ContentNegotiation.js +364 -0
- package/dist/Development.d.ts +34 -0
- package/dist/Development.js +52 -0
- package/dist/Entity.d.ts +47 -0
- package/dist/Entity.js +224 -0
- package/dist/FileRouter.d.ts +61 -0
- package/dist/FileRouter.js +203 -0
- package/dist/FileRouterCodegen.d.ts +19 -0
- package/dist/FileRouterCodegen.js +176 -0
- package/dist/FileRouterPattern.d.ts +9 -0
- package/dist/FileRouterPattern.js +35 -0
- package/dist/Http.d.ts +37 -0
- package/dist/Http.js +92 -0
- package/dist/HttpAppExtra.d.ts +7 -0
- package/dist/HttpAppExtra.js +320 -0
- package/dist/HttpUtils.d.ts +3 -0
- package/dist/HttpUtils.js +11 -0
- package/dist/PathPattern.d.ts +134 -0
- package/dist/PathPattern.js +415 -0
- package/dist/Random.d.ts +5 -0
- package/dist/Random.js +49 -0
- package/dist/Route.d.ts +98 -0
- package/dist/Route.js +81 -0
- package/dist/RouteBody.d.ts +53 -0
- package/dist/RouteBody.js +67 -0
- package/dist/RouteHook.d.ts +12 -0
- package/dist/RouteHook.js +45 -0
- package/dist/RouteHttp.d.ts +21 -0
- package/dist/RouteHttp.js +260 -0
- package/dist/RouteHttpTracer.d.ts +10 -0
- package/dist/RouteHttpTracer.js +62 -0
- package/dist/RouteMount.d.ts +119 -0
- package/dist/RouteMount.js +77 -0
- package/dist/RouteSchema.d.ts +65 -0
- package/dist/RouteSchema.js +155 -0
- package/dist/RouteSse.d.ts +21 -0
- package/dist/RouteSse.js +85 -0
- package/dist/RouteTree.d.ts +56 -0
- package/dist/RouteTree.js +91 -0
- package/dist/RouteTrie.d.ts +20 -0
- package/dist/RouteTrie.js +157 -0
- package/dist/RouterPattern.d.ts +118 -0
- package/dist/RouterPattern.js +269 -0
- package/dist/SchemaExtra.d.ts +7 -0
- package/dist/SchemaExtra.js +74 -0
- package/dist/Start.d.ts +19 -0
- package/dist/Start.js +23 -0
- package/dist/StartApp.d.ts +19 -0
- package/dist/StartApp.js +21 -0
- package/dist/StreamExtra.d.ts +28 -0
- package/dist/StreamExtra.js +100 -0
- package/dist/TuplePathPattern.d.ts +9 -0
- package/dist/TuplePathPattern.js +63 -0
- package/dist/Values.d.ts +26 -0
- package/dist/Values.js +30 -0
- package/dist/bun/BunBundle.d.ts +12 -0
- package/dist/bun/BunBundle.js +145 -0
- package/dist/bun/BunHttpServer.d.ts +44 -0
- package/dist/bun/BunHttpServer.js +187 -0
- package/dist/bun/BunHttpServer_web.d.ts +60 -0
- package/dist/bun/BunHttpServer_web.js +252 -0
- package/dist/bun/BunImportTrackerPlugin.d.ts +13 -0
- package/dist/bun/BunImportTrackerPlugin.js +71 -0
- package/dist/bun/BunRoute.d.ts +49 -0
- package/dist/bun/BunRoute.js +131 -0
- package/dist/bun/BunRuntime.d.ts +1 -0
- package/dist/bun/BunRuntime.js +26 -0
- package/dist/bun/BunVirtualFilesPlugin.d.ts +4 -0
- package/dist/bun/BunVirtualFilesPlugin.js +40 -0
- package/dist/bun/_BunEnhancedResolve.d.ts +45 -0
- package/dist/bun/_BunEnhancedResolve.js +104 -0
- package/dist/bun/index.d.ts +4 -0
- package/dist/bun/index.js +4 -0
- package/dist/bundler/Bundle.d.ts +60 -0
- package/dist/bundler/Bundle.js +48 -0
- package/dist/bundler/BundleFiles.d.ts +13 -0
- package/dist/bundler/BundleFiles.js +94 -0
- package/dist/bundler/BundleHttp.d.ts +45 -0
- package/dist/bundler/BundleHttp.js +176 -0
- package/dist/client/Overlay.d.ts +2 -0
- package/dist/client/Overlay.js +32 -0
- package/dist/client/ScrollState.d.ts +6 -0
- package/dist/client/ScrollState.js +98 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +81 -0
- package/dist/experimental/EncryptedCookies.d.ts +51 -0
- package/dist/experimental/EncryptedCookies.js +243 -0
- package/dist/experimental/SseHttpResponse.d.ts +7 -0
- package/dist/experimental/SseHttpResponse.js +28 -0
- package/dist/experimental/index.d.ts +2 -0
- package/dist/experimental/index.js +2 -0
- package/dist/hyper/Hyper.d.ts +32 -0
- package/dist/hyper/Hyper.js +34 -0
- package/dist/hyper/HyperHtml.d.ts +23 -0
- package/dist/hyper/HyperHtml.js +144 -0
- package/dist/hyper/HyperNode.d.ts +14 -0
- package/dist/hyper/HyperNode.js +11 -0
- package/dist/hyper/HyperRoute.d.ts +8 -0
- package/dist/hyper/HyperRoute.js +32 -0
- package/dist/hyper/HyperRoute.test.d.ts +1 -0
- package/dist/hyper/HyperRoute.test.js +72 -0
- package/dist/hyper/index.d.ts +4 -0
- package/dist/hyper/index.js +4 -0
- package/dist/hyper/jsx-runtime.d.ts +7 -0
- package/dist/hyper/jsx-runtime.js +8 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/inference_check.d.ts +1 -0
- package/dist/inference_check.js +15 -0
- package/dist/middlewares/BasicAuthMiddleware.d.ts +8 -0
- package/dist/middlewares/BasicAuthMiddleware.js +22 -0
- package/dist/middlewares/index.d.ts +1 -0
- package/dist/middlewares/index.js +1 -0
- package/dist/node/FileSystem.d.ts +9 -0
- package/dist/node/FileSystem.js +440 -0
- package/dist/node/Utils.d.ts +1 -0
- package/dist/node/Utils.js +19 -0
- package/dist/repro_fail.d.ts +1 -0
- package/dist/repro_fail.js +14 -0
- package/dist/testing/TestHttpClient.d.ts +13 -0
- package/dist/testing/TestHttpClient.js +68 -0
- package/dist/testing/TestLogger.d.ts +13 -0
- package/dist/testing/TestLogger.js +29 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.js +3 -0
- package/dist/testing/utils.d.ts +9 -0
- package/dist/testing/utils.js +39 -0
- package/dist/x/cloudflare/CloudflareTunnel.d.ts +13 -0
- package/dist/x/cloudflare/CloudflareTunnel.js +43 -0
- package/dist/x/cloudflare/index.d.ts +1 -0
- package/dist/x/cloudflare/index.js +1 -0
- package/dist/x/datastar/Datastar.d.ts +6 -0
- package/dist/x/datastar/Datastar.js +46 -0
- package/dist/x/datastar/index.d.ts +2 -0
- package/dist/x/datastar/index.js +2 -0
- package/dist/x/tailwind/TailwindPlugin.d.ts +23 -0
- package/dist/x/tailwind/TailwindPlugin.js +219 -0
- package/dist/x/tailwind/compile.d.ts +19 -0
- package/dist/x/tailwind/compile.js +156 -0
- package/dist/x/tailwind/plugin.d.ts +2 -0
- package/dist/x/tailwind/plugin.js +15 -0
- package/package.json +67 -14
- package/src/Development.test.ts +119 -0
- package/src/Development.ts +137 -0
- package/src/Entity.test.ts +1 -1
- package/src/Entity.ts +3 -6
- package/src/FileRouter.ts +2 -2
- package/src/Route.ts +4 -0
- package/src/RouteBody.test.ts +43 -45
- package/src/RouteBody.ts +137 -10
- package/src/RouteHttp.test.ts +4 -1
- package/src/RouteHttp.ts +22 -6
- package/src/RouteSse.test.ts +249 -0
- package/src/RouteSse.ts +195 -0
- package/src/Values.ts +9 -7
- package/src/bun/BunBundle.ts +0 -73
- package/src/bun/BunRoute.ts +0 -2
- package/src/hyper/HyperHtml.test.ts +119 -0
- package/src/hyper/HyperHtml.ts +10 -2
- package/src/hyper/HyperNode.ts +2 -0
- package/src/hyper/HyperRoute.test.tsx +199 -0
- package/src/hyper/HyperRoute.ts +61 -0
- package/src/hyper/index.ts +4 -0
- package/src/hyper/jsx.d.ts +15 -0
- package/src/index.ts +1 -0
- package/src/node/FileSystem.ts +8 -0
- package/src/x/tailwind/compile.ts +8 -2
- package/src/FileSystemExtra.test.ts +0 -242
- package/src/FileSystemExtra.ts +0 -66
package/src/Values.ts
CHANGED
|
@@ -4,16 +4,18 @@ type JsonPrimitives =
|
|
|
4
4
|
| boolean
|
|
5
5
|
| null
|
|
6
6
|
|
|
7
|
+
export type JsonObject = {
|
|
8
|
+
[key: string]:
|
|
9
|
+
| Json
|
|
10
|
+
// undefined won't be included in JSON objects but this will allow
|
|
11
|
+
// to use Json type in functions that return object of multiple shapes
|
|
12
|
+
| undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
7
15
|
export type Json =
|
|
8
16
|
| JsonPrimitives
|
|
9
17
|
| Json[]
|
|
10
|
-
|
|
|
11
|
-
[key: string]:
|
|
12
|
-
| Json
|
|
13
|
-
// undefined won't be included in JSON objects but this will allow
|
|
14
|
-
// to use Json type in functions that return object of multiple shapes
|
|
15
|
-
| undefined
|
|
16
|
-
}
|
|
18
|
+
| JsonObject
|
|
17
19
|
|
|
18
20
|
export function isPlainObject(
|
|
19
21
|
value: unknown,
|
package/src/bun/BunBundle.ts
CHANGED
|
@@ -9,10 +9,7 @@ import {
|
|
|
9
9
|
Iterable,
|
|
10
10
|
Layer,
|
|
11
11
|
pipe,
|
|
12
|
-
PubSub,
|
|
13
12
|
Record,
|
|
14
|
-
Stream,
|
|
15
|
-
SynchronizedRef,
|
|
16
13
|
} from "effect"
|
|
17
14
|
import * as NPath from "node:path"
|
|
18
15
|
import type {
|
|
@@ -20,7 +17,6 @@ import type {
|
|
|
20
17
|
BundleManifest,
|
|
21
18
|
} from "../bundler/Bundle.ts"
|
|
22
19
|
import * as Bundle from "../bundler/Bundle.ts"
|
|
23
|
-
import * as FileSystemExtra from "../FileSystemExtra.ts"
|
|
24
20
|
import { BunImportTrackerPlugin } from "./index.ts"
|
|
25
21
|
|
|
26
22
|
export type BuildOptions = Omit<
|
|
@@ -125,75 +121,6 @@ export function layer<T>(
|
|
|
125
121
|
return Layer.effect(tag, build(config))
|
|
126
122
|
}
|
|
127
123
|
|
|
128
|
-
export function layerDev<T>(
|
|
129
|
-
tag: Context.Tag<T, BundleContext>,
|
|
130
|
-
config: BuildOptions,
|
|
131
|
-
) {
|
|
132
|
-
return Layer.scoped(
|
|
133
|
-
tag,
|
|
134
|
-
Effect.gen(function*() {
|
|
135
|
-
const loadRefKey = "_loadRef"
|
|
136
|
-
const sharedBundle = yield* build(config)
|
|
137
|
-
|
|
138
|
-
const loadRef = yield* SynchronizedRef.make(null)
|
|
139
|
-
sharedBundle[loadRefKey] = loadRef
|
|
140
|
-
sharedBundle.events = yield* PubSub.unbounded<Bundle.BundleEvent>()
|
|
141
|
-
|
|
142
|
-
yield* Effect.fork(
|
|
143
|
-
pipe(
|
|
144
|
-
FileSystemExtra.watchSource({
|
|
145
|
-
filter: FileSystemExtra.filterSourceFiles,
|
|
146
|
-
}),
|
|
147
|
-
Stream.map(v =>
|
|
148
|
-
({
|
|
149
|
-
_tag: "Change",
|
|
150
|
-
path: v.path,
|
|
151
|
-
}) as Bundle.BundleEvent
|
|
152
|
-
),
|
|
153
|
-
Stream.onError(err =>
|
|
154
|
-
Effect.logError("Error while watching files", err)
|
|
155
|
-
),
|
|
156
|
-
Stream.runForEach((v) =>
|
|
157
|
-
pipe(
|
|
158
|
-
Effect.gen(function*() {
|
|
159
|
-
yield* Effect.logDebug("Updating bundle: " + tag.key)
|
|
160
|
-
|
|
161
|
-
const newBundle = yield* build(config)
|
|
162
|
-
|
|
163
|
-
Object.assign(sharedBundle, newBundle)
|
|
164
|
-
|
|
165
|
-
// Clean old loaded bundle
|
|
166
|
-
yield* SynchronizedRef.update(loadRef, () => null)
|
|
167
|
-
|
|
168
|
-
// publish event after the built
|
|
169
|
-
if (sharedBundle.events) {
|
|
170
|
-
yield* PubSub.publish(sharedBundle.events, v)
|
|
171
|
-
}
|
|
172
|
-
}),
|
|
173
|
-
Effect.catchAll(err =>
|
|
174
|
-
Effect.gen(function*() {
|
|
175
|
-
yield* Effect.logError(
|
|
176
|
-
"Error while updating bundle",
|
|
177
|
-
err,
|
|
178
|
-
)
|
|
179
|
-
if (sharedBundle.events) {
|
|
180
|
-
yield* PubSub.publish(sharedBundle.events, {
|
|
181
|
-
_tag: "BuildError",
|
|
182
|
-
error: String(err),
|
|
183
|
-
})
|
|
184
|
-
}
|
|
185
|
-
})
|
|
186
|
-
),
|
|
187
|
-
)
|
|
188
|
-
),
|
|
189
|
-
),
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
return sharedBundle
|
|
193
|
-
}),
|
|
194
|
-
)
|
|
195
|
-
}
|
|
196
|
-
|
|
197
124
|
/**
|
|
198
125
|
* Finds common path prefix across provided paths.
|
|
199
126
|
*/
|
package/src/bun/BunRoute.ts
CHANGED
|
@@ -88,3 +88,122 @@ test.it("mixed boolean and string attributes", () => {
|
|
|
88
88
|
.expect(html)
|
|
89
89
|
.toBe("<input type=\"checkbox\" checked name=\"test\" value=\"on\">")
|
|
90
90
|
})
|
|
91
|
+
|
|
92
|
+
test.it("data-* attributes with object values are JSON stringified", () => {
|
|
93
|
+
const node = HyperNode.make("div", {
|
|
94
|
+
"data-signals": {
|
|
95
|
+
draft: "",
|
|
96
|
+
pendingDraft: "",
|
|
97
|
+
username: "User123",
|
|
98
|
+
},
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
const html = HyperHtml.renderToString(node)
|
|
102
|
+
|
|
103
|
+
test
|
|
104
|
+
.expect(html)
|
|
105
|
+
.toBe(
|
|
106
|
+
"<div data-signals=\"{"draft":"","pendingDraft":"","username":"User123"}\"></div>",
|
|
107
|
+
)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test.it("data-* attributes with array values are JSON stringified", () => {
|
|
111
|
+
const node = HyperNode.make("div", {
|
|
112
|
+
"data-items": [1, 2, 3],
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
const html = HyperHtml.renderToString(node)
|
|
116
|
+
|
|
117
|
+
test
|
|
118
|
+
.expect(html)
|
|
119
|
+
.toBe("<div data-items=\"[1,2,3]\"></div>")
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test.it("data-* attributes with nested object values", () => {
|
|
123
|
+
const node = HyperNode.make("div", {
|
|
124
|
+
"data-config": {
|
|
125
|
+
user: { name: "John", active: true },
|
|
126
|
+
settings: { theme: "dark" },
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
const html = HyperHtml.renderToString(node)
|
|
131
|
+
|
|
132
|
+
test
|
|
133
|
+
.expect(html)
|
|
134
|
+
.toBe(
|
|
135
|
+
"<div data-config=\"{"user":{"name":"John","active":true},"settings":{"theme":"dark"}}\"></div>",
|
|
136
|
+
)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test.it("data-* string values are not JSON stringified", () => {
|
|
140
|
+
const node = HyperNode.make("div", {
|
|
141
|
+
"data-value": "hello world",
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const html = HyperHtml.renderToString(node)
|
|
145
|
+
|
|
146
|
+
test
|
|
147
|
+
.expect(html)
|
|
148
|
+
.toBe("<div data-value=\"hello world\"></div>")
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
test.it("non-data attributes with object values are not JSON stringified", () => {
|
|
152
|
+
const node = HyperNode.make("div", {
|
|
153
|
+
style: "color: red",
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
const html = HyperHtml.renderToString(node)
|
|
157
|
+
|
|
158
|
+
test
|
|
159
|
+
.expect(html)
|
|
160
|
+
.toBe("<div style=\"color: red\"></div>")
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
test.it("script with function child renders as IIFE", () => {
|
|
164
|
+
const handler = (window: Window) => {
|
|
165
|
+
console.log("Hello from", window.document.title)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const node = HyperNode.make("script", {
|
|
169
|
+
children: handler,
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const html = HyperHtml.renderToString(node)
|
|
173
|
+
|
|
174
|
+
test
|
|
175
|
+
.expect(html)
|
|
176
|
+
.toBe(`<script>(${handler.toString()})(window)</script>`)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
test.it("script with arrow function child renders as IIFE", () => {
|
|
180
|
+
const node = HyperNode.make("script", {
|
|
181
|
+
children: (window: Window) => {
|
|
182
|
+
window.alert("test")
|
|
183
|
+
},
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
const html = HyperHtml.renderToString(node)
|
|
187
|
+
|
|
188
|
+
test
|
|
189
|
+
.expect(html)
|
|
190
|
+
.toContain("<script>(")
|
|
191
|
+
test
|
|
192
|
+
.expect(html)
|
|
193
|
+
.toContain(")(window)</script>")
|
|
194
|
+
test
|
|
195
|
+
.expect(html)
|
|
196
|
+
.toContain("window.alert")
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
test.it("script with string child renders normally", () => {
|
|
200
|
+
const node = HyperNode.make("script", {
|
|
201
|
+
children: "console.log('hello')",
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
const html = HyperHtml.renderToString(node)
|
|
205
|
+
|
|
206
|
+
test
|
|
207
|
+
.expect(html)
|
|
208
|
+
.toBe("<script>console.log('hello')</script>")
|
|
209
|
+
})
|
package/src/hyper/HyperHtml.ts
CHANGED
|
@@ -120,8 +120,13 @@ export function renderToString(
|
|
|
120
120
|
result += ` ${esc(key)}`
|
|
121
121
|
} else {
|
|
122
122
|
const resolvedKey = key === "className" ? "class" : key
|
|
123
|
+
const value = props[key]
|
|
123
124
|
|
|
124
|
-
|
|
125
|
+
if (key.startsWith("data-") && typeof value === "object") {
|
|
126
|
+
result += ` ${esc(resolvedKey)}="${esc(JSON.stringify(value))}"`
|
|
127
|
+
} else {
|
|
128
|
+
result += ` ${esc(resolvedKey)}="${esc(value)}"`
|
|
129
|
+
}
|
|
125
130
|
}
|
|
126
131
|
}
|
|
127
132
|
}
|
|
@@ -139,7 +144,10 @@ export function renderToString(
|
|
|
139
144
|
result += html
|
|
140
145
|
} else {
|
|
141
146
|
const children = props.children
|
|
142
|
-
|
|
147
|
+
|
|
148
|
+
if (type === "script" && typeof children === "function") {
|
|
149
|
+
result += `(${children.toString()})(window)`
|
|
150
|
+
} else if (Array.isArray(children)) {
|
|
143
151
|
for (let i = children.length - 1; i >= 0; i--) {
|
|
144
152
|
stack.push(children[i])
|
|
145
153
|
}
|
package/src/hyper/HyperNode.ts
CHANGED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/** @jsxImportSource effect-start */
|
|
2
|
+
import * as test from "bun:test"
|
|
3
|
+
import * as Effect from "effect/Effect"
|
|
4
|
+
import * as Entity from "../Entity.ts"
|
|
5
|
+
import * as Http from "../Http.ts"
|
|
6
|
+
import * as Route from "../Route.ts"
|
|
7
|
+
import * as RouteHttp from "../RouteHttp.ts"
|
|
8
|
+
import * as HyperRoute from "./HyperRoute.ts"
|
|
9
|
+
|
|
10
|
+
test.describe("HyperRoute.html", () => {
|
|
11
|
+
test.it("renders JSX to HTML string", async () => {
|
|
12
|
+
const handler = RouteHttp.toWebHandler(
|
|
13
|
+
Route.get(
|
|
14
|
+
HyperRoute.html(
|
|
15
|
+
<div>
|
|
16
|
+
Hello World
|
|
17
|
+
</div>,
|
|
18
|
+
),
|
|
19
|
+
),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
const response = await Http.fetch(handler, { path: "/" })
|
|
23
|
+
|
|
24
|
+
test.expect(response.status).toBe(200)
|
|
25
|
+
test.expect(response.headers.get("Content-Type")).toBe(
|
|
26
|
+
"text/html; charset=utf-8",
|
|
27
|
+
)
|
|
28
|
+
test.expect(await response.text()).toBe("<div>Hello World</div>")
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test.it("renders nested JSX elements", async () => {
|
|
32
|
+
const handler = RouteHttp.toWebHandler(
|
|
33
|
+
Route.get(
|
|
34
|
+
HyperRoute.html(
|
|
35
|
+
<div class="container">
|
|
36
|
+
<h1>
|
|
37
|
+
Title
|
|
38
|
+
</h1>
|
|
39
|
+
<p>
|
|
40
|
+
Paragraph
|
|
41
|
+
</p>
|
|
42
|
+
</div>,
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
const response = await Http.fetch(handler, { path: "/" })
|
|
48
|
+
|
|
49
|
+
test.expect(await response.text()).toBe(
|
|
50
|
+
"<div class=\"container\"><h1>Title</h1><p>Paragraph</p></div>",
|
|
51
|
+
)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test.it("renders JSX from Effect", async () => {
|
|
55
|
+
const handler = RouteHttp.toWebHandler(
|
|
56
|
+
Route.get(
|
|
57
|
+
HyperRoute.html(
|
|
58
|
+
Effect.succeed(
|
|
59
|
+
<span>
|
|
60
|
+
From Effect
|
|
61
|
+
</span>,
|
|
62
|
+
),
|
|
63
|
+
),
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
const response = await Http.fetch(handler, { path: "/" })
|
|
68
|
+
|
|
69
|
+
test.expect(await response.text()).toBe("<span>From Effect</span>")
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test.it("renders JSX from generator function", async () => {
|
|
73
|
+
const handler = RouteHttp.toWebHandler(
|
|
74
|
+
Route.get(
|
|
75
|
+
HyperRoute.html(
|
|
76
|
+
Effect.gen(function*() {
|
|
77
|
+
const name = yield* Effect.succeed("World")
|
|
78
|
+
return (
|
|
79
|
+
<div>
|
|
80
|
+
Hello {name}
|
|
81
|
+
</div>
|
|
82
|
+
)
|
|
83
|
+
}),
|
|
84
|
+
),
|
|
85
|
+
),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
const response = await Http.fetch(handler, { path: "/" })
|
|
89
|
+
|
|
90
|
+
test.expect(await response.text()).toBe("<div>Hello World</div>")
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test.it("renders JSX from handler function", async () => {
|
|
94
|
+
const handler = RouteHttp.toWebHandler(
|
|
95
|
+
Route.get(
|
|
96
|
+
HyperRoute.html((context) =>
|
|
97
|
+
Effect.succeed(
|
|
98
|
+
<div>
|
|
99
|
+
Request received
|
|
100
|
+
</div>,
|
|
101
|
+
)
|
|
102
|
+
),
|
|
103
|
+
),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
const response = await Http.fetch(handler, { path: "/" })
|
|
107
|
+
|
|
108
|
+
test.expect(await response.text()).toBe("<div>Request received</div>")
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test.it("renders JSX with dynamic content", async () => {
|
|
112
|
+
const items = ["Apple", "Banana", "Cherry"]
|
|
113
|
+
|
|
114
|
+
const handler = RouteHttp.toWebHandler(
|
|
115
|
+
Route.get(
|
|
116
|
+
HyperRoute.html(
|
|
117
|
+
<ul>
|
|
118
|
+
{items.map((item) => (
|
|
119
|
+
<li>
|
|
120
|
+
{item}
|
|
121
|
+
</li>
|
|
122
|
+
))}
|
|
123
|
+
</ul>,
|
|
124
|
+
),
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
const response = await Http.fetch(handler, { path: "/" })
|
|
129
|
+
|
|
130
|
+
test.expect(await response.text()).toBe(
|
|
131
|
+
"<ul><li>Apple</li><li>Banana</li><li>Cherry</li></ul>",
|
|
132
|
+
)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
test.it("handles Entity with JSX body", async () => {
|
|
136
|
+
const handler = RouteHttp.toWebHandler(
|
|
137
|
+
Route.get(
|
|
138
|
+
HyperRoute.html(
|
|
139
|
+
Entity.make(
|
|
140
|
+
<div>
|
|
141
|
+
With Entity
|
|
142
|
+
</div>,
|
|
143
|
+
{ status: 201 },
|
|
144
|
+
),
|
|
145
|
+
),
|
|
146
|
+
),
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
const response = await Http.fetch(handler, { path: "/" })
|
|
150
|
+
|
|
151
|
+
test.expect(response.status).toBe(201)
|
|
152
|
+
test.expect(await response.text()).toBe("<div>With Entity</div>")
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
test.it("renders data-* attributes with object values as JSON", async () => {
|
|
156
|
+
const handler = RouteHttp.toWebHandler(
|
|
157
|
+
Route.get(
|
|
158
|
+
HyperRoute.html(
|
|
159
|
+
<div
|
|
160
|
+
data-signals={{
|
|
161
|
+
draft: "",
|
|
162
|
+
pendingDraft: "",
|
|
163
|
+
username: "User123",
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
Content
|
|
167
|
+
</div>,
|
|
168
|
+
),
|
|
169
|
+
),
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
const response = await Http.fetch(handler, { path: "/" })
|
|
173
|
+
|
|
174
|
+
test.expect(await response.text()).toBe(
|
|
175
|
+
"<div data-signals=\"{"draft":"","pendingDraft":"","username":"User123"}\">Content</div>",
|
|
176
|
+
)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
test.it("renders script with function child as IIFE", async () => {
|
|
180
|
+
const handler = RouteHttp.toWebHandler(
|
|
181
|
+
Route.get(
|
|
182
|
+
HyperRoute.html(
|
|
183
|
+
<script>
|
|
184
|
+
{(window) => {
|
|
185
|
+
console.log("Hello from", window.document.title)
|
|
186
|
+
}}
|
|
187
|
+
</script>,
|
|
188
|
+
),
|
|
189
|
+
),
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
const response = await Http.fetch(handler, { path: "/" })
|
|
193
|
+
const text = await response.text()
|
|
194
|
+
|
|
195
|
+
test.expect(text).toContain("<script>(")
|
|
196
|
+
test.expect(text).toContain(")(window)</script>")
|
|
197
|
+
test.expect(text).toContain("window.document.title")
|
|
198
|
+
})
|
|
199
|
+
})
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect"
|
|
2
|
+
import * as Entity from "../Entity.ts"
|
|
3
|
+
import * as Route from "../Route.ts"
|
|
4
|
+
import type * as RouteBody from "../RouteBody.ts"
|
|
5
|
+
import * as HyperHtml from "./HyperHtml.ts"
|
|
6
|
+
import type { JSX } from "./jsx.d.ts"
|
|
7
|
+
|
|
8
|
+
function renderValue(
|
|
9
|
+
value: JSX.Children | Entity.Entity<JSX.Children>,
|
|
10
|
+
): string | Entity.Entity<string> {
|
|
11
|
+
if (Entity.isEntity(value)) {
|
|
12
|
+
return Entity.make(HyperHtml.renderToString(value.body), {
|
|
13
|
+
status: value.status,
|
|
14
|
+
url: value.url,
|
|
15
|
+
headers: value.headers,
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
return HyperHtml.renderToString(value)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function normalizeToEffect<B, A, E, R>(
|
|
22
|
+
handler: RouteBody.HandlerInput<B, A, E, R>,
|
|
23
|
+
context: B,
|
|
24
|
+
next: (context?: Partial<B> & Record<string, unknown>) => Entity.Entity<A>,
|
|
25
|
+
): Effect.Effect<A | Entity.Entity<A>, E, R> {
|
|
26
|
+
if (Effect.isEffect(handler)) {
|
|
27
|
+
return handler
|
|
28
|
+
}
|
|
29
|
+
if (typeof handler === "function") {
|
|
30
|
+
const result = (handler as Function)(context, next)
|
|
31
|
+
if (Effect.isEffect(result)) {
|
|
32
|
+
return result as Effect.Effect<A | Entity.Entity<A>, E, R>
|
|
33
|
+
}
|
|
34
|
+
return Effect.gen(function*() {
|
|
35
|
+
return yield* result
|
|
36
|
+
}) as Effect.Effect<A | Entity.Entity<A>, E, R>
|
|
37
|
+
}
|
|
38
|
+
return Effect.succeed(handler as A | Entity.Entity<A>)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function html<
|
|
42
|
+
D extends Route.RouteDescriptor.Any,
|
|
43
|
+
B extends {},
|
|
44
|
+
I extends Route.Route.Tuple,
|
|
45
|
+
E = never,
|
|
46
|
+
R = never,
|
|
47
|
+
>(
|
|
48
|
+
handler: RouteBody.HandlerInput<
|
|
49
|
+
NoInfer<D & B & Route.ExtractBindings<I> & { format: "html" }>,
|
|
50
|
+
JSX.Children,
|
|
51
|
+
E,
|
|
52
|
+
R
|
|
53
|
+
>,
|
|
54
|
+
) {
|
|
55
|
+
return Route.html<D, B, I, string, E, R>((context, next) =>
|
|
56
|
+
Effect.map(
|
|
57
|
+
normalizeToEffect(handler, context, next as never),
|
|
58
|
+
renderValue,
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
}
|
package/src/hyper/jsx.d.ts
CHANGED
|
@@ -1818,6 +1818,21 @@ export namespace JSX {
|
|
|
1818
1818
|
event?: string | undefined
|
|
1819
1819
|
/** @deprecated */
|
|
1820
1820
|
language?: string | undefined
|
|
1821
|
+
|
|
1822
|
+
children?: Children
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
// Separate interface for script elements with function children.
|
|
1826
|
+
// This enables TypeScript to infer the `window` parameter type.
|
|
1827
|
+
//
|
|
1828
|
+
// Using a union in a single interface (`children?: Function | Children`)
|
|
1829
|
+
// doesn't work because TS can't infer callback parameter types from unions.
|
|
1830
|
+
// By splitting into two interfaces, TS can discriminate based on children
|
|
1831
|
+
interface ScriptHTMLAttributesWithHandler<T>
|
|
1832
|
+
extends Omit<ScriptHTMLAttributes<T>, "children" | "type">
|
|
1833
|
+
{
|
|
1834
|
+
children: (window: Window) => void
|
|
1835
|
+
type?: never
|
|
1821
1836
|
}
|
|
1822
1837
|
interface SelectHTMLAttributes<T> extends HTMLAttributes<T> {
|
|
1823
1838
|
autocomplete?: HTMLAutocomplete | undefined
|
package/src/index.ts
CHANGED
package/src/node/FileSystem.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Adapted from @effect/platform
|
|
3
|
+
*/
|
|
1
4
|
import { effectify } from "@effect/platform/Effectify"
|
|
2
5
|
import * as Error from "@effect/platform/Error"
|
|
3
6
|
import type {
|
|
@@ -715,6 +718,11 @@ const makeFileSystem = Effect.map(
|
|
|
715
718
|
|
|
716
719
|
export const layer = Layer.effect(FileSystem.FileSystem, makeFileSystem)
|
|
717
720
|
|
|
721
|
+
export {
|
|
722
|
+
Error,
|
|
723
|
+
FileSystem,
|
|
724
|
+
}
|
|
725
|
+
|
|
718
726
|
export function handleErrnoException(
|
|
719
727
|
module: SystemError["module"],
|
|
720
728
|
method: string,
|
|
@@ -88,13 +88,19 @@ async function ensureSourceDetectionRootExists(compiler: {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
export async function compileAst(
|
|
91
|
+
export async function compileAst(
|
|
92
|
+
ast: AstNode[],
|
|
93
|
+
options: CompileOptions,
|
|
94
|
+
): ReturnType<typeof _compileAst> {
|
|
92
95
|
let compiler = await _compileAst(ast, createCompileOptions(options))
|
|
93
96
|
await ensureSourceDetectionRootExists(compiler)
|
|
94
97
|
return compiler
|
|
95
98
|
}
|
|
96
99
|
|
|
97
|
-
export async function compile(
|
|
100
|
+
export async function compile(
|
|
101
|
+
css: string,
|
|
102
|
+
options: CompileOptions,
|
|
103
|
+
): ReturnType<typeof _compile> {
|
|
98
104
|
let compiler = await _compile(css, createCompileOptions(options))
|
|
99
105
|
await ensureSourceDetectionRootExists(compiler)
|
|
100
106
|
return compiler
|