@marko/run 0.0.1-beta3 → 0.0.1-beta5
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/README.md +164 -82
- package/dist/adapter/dev-server.d.ts +1 -1
- package/dist/adapter/index.cjs +168 -2
- package/dist/adapter/index.d.ts +1 -0
- package/dist/adapter/index.js +167 -1
- package/dist/adapter/middleware.cjs +11 -12
- package/dist/adapter/middleware.d.ts +1 -5
- package/dist/adapter/middleware.js +11 -12
- package/dist/cli/index.mjs +49 -41
- package/dist/runtime/index.d.ts +9 -2
- package/dist/runtime/internal.cjs +2 -2
- package/dist/runtime/internal.js +2 -2
- package/dist/runtime/router.cjs +3 -1
- package/dist/runtime/router.d.ts +3 -3
- package/dist/runtime/router.js +3 -1
- package/dist/runtime/types.d.ts +11 -5
- package/dist/vite/index.cjs +145 -23
- package/dist/vite/index.d.ts +1 -1
- package/dist/vite/index.js +145 -23
- package/dist/vite/types.d.ts +5 -0
- package/dist/vite/utils/config.d.ts +5 -3
- package/dist/vite/utils/route.d.ts +1 -1
- package/package.json +7 -7
- package/dist/runtime/utils.d.ts +0 -3
package/README.md
CHANGED
|
@@ -1,31 +1,68 @@
|
|
|
1
|
-
<
|
|
1
|
+
<div align="center">
|
|
2
2
|
<!-- Logo -->
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
<h1>
|
|
4
|
+
<img alt="" src="https://user-images.githubusercontent.com/4985201/115444712-ca550500-a1c9-11eb-9897-238ece59129c.png" height="118"/>
|
|
5
|
+
<br/>
|
|
6
|
+
@marko/run
|
|
7
|
+
</h1>
|
|
7
8
|
|
|
8
9
|
<!-- Language -->
|
|
9
|
-
<a href="
|
|
10
|
+
<a href="https://www.typescriptlang.org">
|
|
10
11
|
<img src="https://img.shields.io/badge/%3C%2F%3E-typescript-blue.svg" alt="TypeScript"/>
|
|
11
12
|
</a>
|
|
12
|
-
</
|
|
13
|
+
</div>
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
`@marko/run` is an application framework for [Marko](https://markojs.com), with these features:
|
|
16
|
+
|
|
17
|
+
- Vite plugin that encapsulates [`@marko/vite`](https://github.com/marko-js/vite)
|
|
18
|
+
- CLI to simplify build modes
|
|
16
19
|
- File-based routing with layouts and middleware
|
|
17
20
|
- Efficient routing using a compiled static trie
|
|
18
|
-
- Designed with web standards to run anywhere
|
|
21
|
+
- [Designed with web standards](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern/URLPattern) to run anywhere
|
|
22
|
+
- TypeScript support
|
|
19
23
|
|
|
20
|
-
##
|
|
24
|
+
## Installation
|
|
21
25
|
|
|
22
|
-
```
|
|
26
|
+
```sh
|
|
23
27
|
npm install @marko/run
|
|
24
28
|
```
|
|
25
29
|
|
|
30
|
+
## CLI
|
|
31
|
+
|
|
32
|
+
The package provides a command line tool `marko-run` which can be run using scripts in your package.json or with npx.
|
|
33
|
+
|
|
34
|
+
### Getting Started / Zero Config
|
|
35
|
+
|
|
36
|
+
`marko-run` makes it easy to get started without little to no config. The package ships with a default Vite config and node-based adapter that means a minimal project start can be:
|
|
37
|
+
1. Install `@marko/run`
|
|
38
|
+
2. Create file `src/routes/+page.marko`
|
|
39
|
+
3. Run `npx marko-run dev`
|
|
40
|
+
4. Open browser to `http://localhost:3000`
|
|
41
|
+
|
|
42
|
+
### Commands
|
|
43
|
+
|
|
44
|
+
**`dev`** - Start development server in watch mode
|
|
45
|
+
```bash
|
|
46
|
+
> npx marko-run dev
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**`build`** - Create a production build
|
|
50
|
+
```bash
|
|
51
|
+
> npx marko-run build
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**`preview`** - Create a production build and serve
|
|
55
|
+
```bash
|
|
56
|
+
> npx marko-run preview
|
|
57
|
+
```
|
|
58
|
+
or (default command)
|
|
59
|
+
```bash
|
|
60
|
+
> npx marko-run
|
|
61
|
+
```
|
|
62
|
+
|
|
26
63
|
## Vite Plugin
|
|
27
64
|
|
|
28
|
-
This package
|
|
65
|
+
This package’s Vite plugin discovers your route files, generates the routing code, and registers the `@marko/vite` plugin to compile your `.marko` files.
|
|
29
66
|
|
|
30
67
|
```ts
|
|
31
68
|
// vite.config.ts
|
|
@@ -34,49 +71,78 @@ import marko from "@marko/run/vite"; // Import the Vite plugin
|
|
|
34
71
|
|
|
35
72
|
export default defineConfig({
|
|
36
73
|
plugins: [marko()], // Register the Vite plugin
|
|
37
|
-
build: {
|
|
38
|
-
sourcemap: true, // Generate sourcemaps for all builds.
|
|
39
|
-
emptyOutDir: false, // Avoid server & client deleting files from each other.
|
|
40
|
-
}
|
|
41
74
|
})
|
|
42
75
|
```
|
|
43
76
|
|
|
77
|
+
## Adapters
|
|
78
|
+
|
|
79
|
+
*🎗 TODO: provide a quick overview*
|
|
80
|
+
|
|
44
81
|
## Runtime
|
|
45
82
|
|
|
46
|
-
Generally
|
|
83
|
+
Generally, when using an adapter, this runtime will be abstracted away.
|
|
47
84
|
|
|
48
85
|
```ts
|
|
49
|
-
import { router,
|
|
86
|
+
import { router, matchRoute, invokeRoute } from '@marko/run/router`;
|
|
50
87
|
```
|
|
51
88
|
|
|
52
89
|
### `router`
|
|
90
|
+
|
|
53
91
|
```ts
|
|
54
|
-
|
|
92
|
+
interface RequestContext<T> {
|
|
93
|
+
url: URL;
|
|
94
|
+
method: string;
|
|
95
|
+
request: Request;
|
|
96
|
+
platform: T;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function router(context: RequestContext) => Promise<Response | void>;
|
|
55
100
|
```
|
|
56
|
-
This asynchronouse function takes a [WHATWG request](https://fetch.spec.whatwg.org/#request-class) object and returns a [response](https://fetch.spec.whatwg.org/#response-class) object generated by executing the corresponding matched route files. If no match is found, a response with a 404 status code is returned. If an unhandled error occurs, a response with a 500 status code will be returned.
|
|
57
101
|
|
|
58
|
-
|
|
102
|
+
This asynchronous function takes a context object and returns the [WHATWG `Response` object](https://fetch.spec.whatwg.org/#response-class) from executing any matched route files or undefined if the request was explicitly not handled. If no route matches the requested path, a `404` status code response will be returned. If an error occurs a `500` status code response will be returned.
|
|
103
|
+
|
|
104
|
+
The context object contains the following properties
|
|
105
|
+
- `url`: The URL reprensting the requested resource
|
|
106
|
+
- `method`: The HTTP method used
|
|
107
|
+
- `request`: [WHATWG `Request` object](https://fetch.spec.whatwg.org/#request-class)
|
|
108
|
+
- `platform`: An object containing any platform-specific data (eg Node request/response) and will vary between adapters.
|
|
109
|
+
|
|
110
|
+
### `matchRoute`
|
|
111
|
+
|
|
59
112
|
```ts
|
|
60
|
-
|
|
113
|
+
interface interface Route {
|
|
61
114
|
params: Record<string, string>;
|
|
62
115
|
meta: unknown;
|
|
63
|
-
|
|
64
|
-
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function matchRoute(method: string, pathname: string) => Route | null;
|
|
65
119
|
```
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- `
|
|
120
|
+
|
|
121
|
+
This synchronous function takes an HTTP method and path name, then returns an object representing the best match — or `null` if no match is found.
|
|
122
|
+
|
|
123
|
+
- `params` - a `{ key: value }` collection of any path parameters for the route
|
|
124
|
+
- `meta` - metadata for the route
|
|
125
|
+
|
|
126
|
+
### `invokeRoute`
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
function invokeRoute(route: Route, context: RequestContext) => Promise<Response | void>;
|
|
130
|
+
```
|
|
131
|
+
This asynchronous function takes a route object returned by [matchRoute](#matchRoute) and a context object and returns a response in the same way the [router](#router) does.
|
|
132
|
+
|
|
133
|
+
|
|
70
134
|
|
|
71
135
|
## File-based Routing
|
|
72
136
|
|
|
73
|
-
|
|
137
|
+
### Nested Routing
|
|
74
138
|
|
|
75
|
-
|
|
139
|
+
*🎗 TODO: provide a quick overview*
|
|
76
140
|
|
|
77
141
|
### Routes Directory
|
|
78
142
|
|
|
79
|
-
|
|
143
|
+
The plugin looks for route files in the configured **routes directory**. By default, that’s `./src/routes`, relative to the Vite config file.
|
|
144
|
+
|
|
145
|
+
To change what directory routes are found in:
|
|
80
146
|
|
|
81
147
|
```ts
|
|
82
148
|
// vite.config.ts
|
|
@@ -86,55 +152,56 @@ import marko from "@marko/run/vite";
|
|
|
86
152
|
export default defineConfig({
|
|
87
153
|
plugins: [marko({
|
|
88
154
|
routesDir: 'src/pages' // Use `./src/pages` (relative to this file) as the routes directory
|
|
89
|
-
})]
|
|
90
|
-
//...
|
|
155
|
+
})]
|
|
91
156
|
})
|
|
92
157
|
```
|
|
93
158
|
|
|
94
159
|
### Routeable Files
|
|
95
160
|
|
|
96
|
-
|
|
161
|
+
To allow for colocation of files that shouldn’t be served (like tests, assets, etc.), the router only recognizes certain filenames.
|
|
162
|
+
|
|
163
|
+
The following filenames will be discovered in any directory inside your application’s [routes directory](#routes-directory).
|
|
97
164
|
|
|
98
165
|
#### `+page.marko`
|
|
99
|
-
|
|
166
|
+
|
|
167
|
+
These files establish a route at the current directory path which will be served for `GET` requests with the HTML content of the page. Only one page may exists for any served path.
|
|
100
168
|
|
|
101
169
|
#### `+layout.marko`
|
|
102
|
-
These files provide a layout component which will wrap all nested layouts and pages.
|
|
103
170
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
</div>
|
|
116
|
-
```
|
|
117
|
-
</details>
|
|
171
|
+
These files provide a **layout component**, which will wrap all nested layouts and pages.
|
|
172
|
+
|
|
173
|
+
Layouts are like any other Marko component with no extra constraints. Each layout receives the request, path params, URL, and route metadata as input, as well as a `renderBody` which will be the next layout or page to project.
|
|
174
|
+
|
|
175
|
+
```marko
|
|
176
|
+
<main>
|
|
177
|
+
<h1>My Products</h1>
|
|
178
|
+
|
|
179
|
+
${input.renderBody} // render the page or layout here
|
|
180
|
+
</main>
|
|
181
|
+
```
|
|
118
182
|
|
|
119
183
|
#### `+handler.*`
|
|
120
|
-
|
|
184
|
+
|
|
185
|
+
These files establish a route at the current directory path which can handle requests for `GET`, `POST`, `PUT`, and `DELETE` HTTP methods. <!-- TODO: what about HEAD? -->
|
|
186
|
+
|
|
187
|
+
Typically, these will be `.js` or `.ts` files depending on your project. Like pages, only one handler may exist for any served path. A handler should export functions
|
|
121
188
|
|
|
122
189
|
<details>
|
|
123
190
|
<summary>More Info</summary>
|
|
124
191
|
|
|
125
|
-
- Valid exports are
|
|
126
|
-
- Each
|
|
127
|
-
|
|
128
|
-
|
|
192
|
+
- Valid exports are functions named `get`, `post`, `put`, or `del`.
|
|
193
|
+
- Each export receives a `context` and `next` argument, and should return a WHATWG `Response` either synchronously or asynchronously.
|
|
194
|
+
- The `context` argument contains the WHATWG request object, path parameters, URL, and route metadata.
|
|
195
|
+
- The `next` argument will call the page for get requests where applicable or return a `204` response.
|
|
129
196
|
|
|
130
|
-
```
|
|
197
|
+
```js
|
|
131
198
|
export function post(context, next) {
|
|
132
199
|
const { request, params, url, meta } = context;
|
|
133
|
-
return new Response('updated', { status: 200 });
|
|
200
|
+
return new Response('Successfully updated', { status: 200 });
|
|
134
201
|
}
|
|
135
202
|
|
|
136
203
|
export function put(context, next) {
|
|
137
|
-
return new Response('created', { status: 201 }); // handle the request
|
|
204
|
+
return new Response('Successfully created', { status: 201 }); // handle the request
|
|
138
205
|
}
|
|
139
206
|
|
|
140
207
|
export function get(context, next) {
|
|
@@ -142,29 +209,33 @@ These files establish a route at the current directory path which can handle req
|
|
|
142
209
|
}
|
|
143
210
|
|
|
144
211
|
export function del(context, next) {
|
|
145
|
-
return new Response('removed', { status: 204 });
|
|
212
|
+
return new Response('Successfully removed', { status: 204 });
|
|
146
213
|
}
|
|
147
214
|
```
|
|
148
215
|
</details>
|
|
149
216
|
|
|
150
217
|
|
|
151
|
-
#### `+
|
|
152
|
-
|
|
218
|
+
#### `+middleware.*`
|
|
219
|
+
|
|
220
|
+
These files are like layouts, but for handlers. Middleware get called before handlers and let you perform arbitrary work before and after.
|
|
221
|
+
|
|
222
|
+
> **Note**: Unlike handlers, middleware run for all HTTP methods.
|
|
153
223
|
|
|
154
224
|
<details>
|
|
155
225
|
<summary>More Info</summary>
|
|
156
226
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
- The `
|
|
227
|
+
Expects a `default` export that receives a `context` and `next` argument, and should return a WHATWG response either synchronously or asynchronously.
|
|
228
|
+
|
|
229
|
+
- The `context` argument contains the WHATWG `Request` object, path parameters, URL, and route metadata.
|
|
230
|
+
- The `next` argument will call the next middleware, handler, or page for the route.
|
|
160
231
|
|
|
161
232
|
```ts
|
|
162
233
|
export default async function(context, next) {
|
|
163
|
-
const requestName = `${ctx.request.method} ${ctx.url.href}`;
|
|
234
|
+
const requestName = `${ctx.request.method} ${ctx.url.href}`; // TODO: could this be just `ctx.url` with a `toString` that then grabs `.href`?
|
|
164
235
|
let success = true;
|
|
165
236
|
console.log(`${requestName} request started`)
|
|
166
237
|
try {
|
|
167
|
-
return await next(); //
|
|
238
|
+
return await next(); // Wait for subsequent middleware/handler/page
|
|
168
239
|
} catch (err) {
|
|
169
240
|
success = false;
|
|
170
241
|
throw err;
|
|
@@ -176,30 +247,42 @@ These files are analagous to layouts for handlers. Middlware get called before h
|
|
|
176
247
|
</details>
|
|
177
248
|
|
|
178
249
|
#### `+meta.*`
|
|
179
|
-
|
|
250
|
+
|
|
251
|
+
These files represent metadata to attach to the route. This metadata will be automatically provided on the the route `context` when invoking a route.
|
|
180
252
|
|
|
181
253
|
### Special Files
|
|
182
254
|
|
|
183
|
-
In addition to the files above which can be defined in any directory under the _routes directory_, there are some special files which can only be defined
|
|
255
|
+
In addition to the files above which can be defined in any directory under the _routes directory_, there are some special files which can only be defined at the top-level of the _routes directory_. <!-- TODO: do we want to keep this restriction? Having nested 404s would be handy for disambiguating things like “there’s no user with that name” or “that promotion wasn’t found, it may have expired” -->
|
|
256
|
+
|
|
257
|
+
These special pages are subject to a root layout file (`pages/+layout.marko` in the default configuration).
|
|
184
258
|
|
|
185
259
|
#### `+404.marko`
|
|
186
|
-
|
|
260
|
+
|
|
261
|
+
This special page responds to any request where:
|
|
262
|
+
|
|
263
|
+
- The `Accept` request header includes `text/html`
|
|
264
|
+
- *And* no other handler or page rendered the request
|
|
265
|
+
|
|
266
|
+
Responses with this page will have a `404` status code.
|
|
187
267
|
|
|
188
268
|
#### `+500.marko`
|
|
189
|
-
This is a special page wich will be rendered for any request whose `Accept` header includes `text/html` if an uncaught error occurs while handling the request. This page will be subject to the root layout file (ie. +layout.marko file in the _routes directory_). Responses with this page will have a `500` status code.
|
|
190
269
|
|
|
270
|
+
This special page responds to any request where:
|
|
271
|
+
|
|
272
|
+
- The `Accept` request header includes `text/html`
|
|
273
|
+
- *And* an uncaught error occurs while serving the request
|
|
274
|
+
|
|
275
|
+
Responses with this page will have a `500` status code.
|
|
191
276
|
|
|
192
277
|
### Execution Order
|
|
193
278
|
|
|
194
|
-
For a matched route the routable files execute in the following order
|
|
279
|
+
For a matched route, the routable files execute in the following order:
|
|
280
|
+
|
|
195
281
|
1. Middlewares from root-most to leaf-most
|
|
196
282
|
2. Handler
|
|
197
283
|
3. Layouts from root-most to leaf-most
|
|
198
284
|
4. Page
|
|
199
285
|
|
|
200
|
-
<details>
|
|
201
|
-
<summary>Diagram</summary>
|
|
202
|
-
|
|
203
286
|
```mermaid
|
|
204
287
|
sequenceDiagram
|
|
205
288
|
participant Middleware1
|
|
@@ -218,13 +301,12 @@ sequenceDiagram
|
|
|
218
301
|
Handler-->>Middleware2: Response
|
|
219
302
|
Middleware2-->>Middleware1: Response
|
|
220
303
|
```
|
|
221
|
-
</details>
|
|
222
304
|
|
|
223
305
|
### Path Structure
|
|
224
306
|
|
|
225
|
-
Within the _routes directory_ the directory structure will determine the path the route will be served. There are four types of directory names:
|
|
307
|
+
Within the _routes directory_, the directory structure will determine the path the route will be served. There are four types of directory names: static, pathless, dynamic, and catch-all.
|
|
226
308
|
|
|
227
|
-
|
|
309
|
+
1. **Static directories** - The most common type. Each static directory contributes its name as a segment in the route's served path, like a traditional fileserver. Unless a directory name matches the requirements for one of the below types, it defaults to a static directory.
|
|
228
310
|
|
|
229
311
|
Examples:
|
|
230
312
|
```
|
|
@@ -233,7 +315,7 @@ Within the _routes directory_ the directory structure will determine the path th
|
|
|
233
315
|
/projects
|
|
234
316
|
```
|
|
235
317
|
|
|
236
|
-
|
|
318
|
+
2. **Pathless directories** - These directories do **not** contribute their name to the route's served path. Directory names that start with an underscore (`_`) or directories named `index` will be a pathless directory.
|
|
237
319
|
|
|
238
320
|
Examples:
|
|
239
321
|
```
|
|
@@ -242,7 +324,7 @@ Within the _routes directory_ the directory structure will determine the path th
|
|
|
242
324
|
/index
|
|
243
325
|
```
|
|
244
326
|
|
|
245
|
-
|
|
327
|
+
3. **Dynamic directories** - These directories introduce a dynamic parameter to the route's served path and will match any value at that segment. Any directory name that starts with a single dollar sign (`$`) will be a dynamic directory, and the remaining directory name will be the parameter at runtime. If the directory name is exactly `$/`, the parameter will not exist at runtime but will be matched.
|
|
246
328
|
|
|
247
329
|
Examples:
|
|
248
330
|
```
|
|
@@ -251,9 +333,9 @@ Within the _routes directory_ the directory structure will determine the path th
|
|
|
251
333
|
/$
|
|
252
334
|
```
|
|
253
335
|
|
|
254
|
-
|
|
336
|
+
4. **Catch-all directories** - These directories are similar to dynamic directories and introduce a dynamic parameter, but instead of matching a single path segment, they match to the end of the path. Any directory that starts with two dollar signs (`$$`) will be a catch-all directory, and the remaining directory name will be the parameter at runtime. In the case of a directory named `$$/`, the parameter name at runtime will be `*`. Catch-all directories can be used to make `404` Not Found routes at any level, including the root.
|
|
255
337
|
|
|
256
|
-
Because catch-all directories
|
|
338
|
+
Because catch-all directories match any path segment and consume the rest of the path, you cannot nest route files in them and no further directories will be traversed.
|
|
257
339
|
|
|
258
340
|
Examples:
|
|
259
341
|
```
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ViteDevServer } from "vite";
|
|
2
|
-
import { type NodeMiddleware } from "
|
|
2
|
+
import { type NodeMiddleware } from "./middleware";
|
|
3
3
|
export declare function createViteDevMiddleware<T>(devServer: ViteDevServer, load: (prev: T | undefined) => Promise<T>, factory: (value: T) => NodeMiddleware): NodeMiddleware;
|
|
4
4
|
export declare function createDevServer(configFile?: string): Promise<import("vite").Connect.Server>;
|
package/dist/adapter/index.cjs
CHANGED
|
@@ -36,7 +36,173 @@ var import_url = require("url");
|
|
|
36
36
|
|
|
37
37
|
// src/adapter/dev-server.ts
|
|
38
38
|
var import_vite = require("vite");
|
|
39
|
-
|
|
39
|
+
|
|
40
|
+
// src/adapter/middleware.ts
|
|
41
|
+
var webStream = __toESM(require("stream/web"), 1);
|
|
42
|
+
var import_crypto = __toESM(require("@hattip/polyfills/crypto"), 1);
|
|
43
|
+
(0, import_crypto.default)();
|
|
44
|
+
for (const key of Object.keys(webStream)) {
|
|
45
|
+
if (!(key in global)) {
|
|
46
|
+
global[key] = webStream[key];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function getForwardedHeader(req, name) {
|
|
50
|
+
const value = req.headers["x-forwarded-" + name];
|
|
51
|
+
if (value) {
|
|
52
|
+
if (typeof value === "string") {
|
|
53
|
+
const index = value.indexOf(",");
|
|
54
|
+
return index < 0 ? value : value.slice(0, index);
|
|
55
|
+
}
|
|
56
|
+
return value[0];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function getOrigin(req, protocol, host, trustProxy) {
|
|
60
|
+
var _a;
|
|
61
|
+
protocol ?? (protocol = req.protocol || trustProxy && getForwardedHeader(req, "proto") || ((_a = req.socket) == null ? void 0 : _a.encrypted) && "https" || "http");
|
|
62
|
+
host ?? (host = trustProxy && getForwardedHeader(req, "host") || req.headers.host);
|
|
63
|
+
if (!host) {
|
|
64
|
+
if (process.env.NODE_ENV !== "production") {
|
|
65
|
+
host = "localhost";
|
|
66
|
+
console.warn(
|
|
67
|
+
`Could not automatically determine the origin host, using 'localhost'. Use the 'origin' option or the 'ORIGIN' environment variable to set the origin explicitly.`
|
|
68
|
+
);
|
|
69
|
+
} else {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`Could not automatically determine the origin host. Use the 'origin' option or the 'ORIGIN' environment variable to set the origin explicitly.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return `${protocol}://${host}`;
|
|
76
|
+
}
|
|
77
|
+
function createMiddleware(router, options = {}) {
|
|
78
|
+
const { trustProxy = process.env.TRUST_PROXY === "1" } = options;
|
|
79
|
+
let { origin = process.env.ORIGIN } = options;
|
|
80
|
+
let protocol;
|
|
81
|
+
let host;
|
|
82
|
+
if (origin) {
|
|
83
|
+
({ protocol, host } = new URL(origin));
|
|
84
|
+
protocol = protocol.slice(0, -1);
|
|
85
|
+
}
|
|
86
|
+
return async (req, res, next) => {
|
|
87
|
+
origin ?? (origin = getOrigin(req, protocol, host, trustProxy));
|
|
88
|
+
const url = new URL(req.url, origin);
|
|
89
|
+
const ip = req.ip || trustProxy && getForwardedHeader(req, "for") || req.socket.remoteAddress || "";
|
|
90
|
+
const requestContext = {
|
|
91
|
+
method: req.method,
|
|
92
|
+
url,
|
|
93
|
+
platform: {
|
|
94
|
+
ip,
|
|
95
|
+
request: req,
|
|
96
|
+
response: res,
|
|
97
|
+
setCookie(cookie) {
|
|
98
|
+
res.appendHeader("set-cookie", cookie);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
Object.defineProperty(requestContext, "request", {
|
|
103
|
+
get() {
|
|
104
|
+
const headers = req.headers;
|
|
105
|
+
const body = req.method === "GET" || req.method === "HEAD" ? void 0 : req.socket ? req : new ReadableStream({
|
|
106
|
+
start(controller) {
|
|
107
|
+
req.on("data", (chunk) => controller.enqueue(chunk));
|
|
108
|
+
req.on("end", () => controller.close());
|
|
109
|
+
req.on("error", (err) => controller.error(err));
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
const request = new Request(url, {
|
|
113
|
+
method: req.method,
|
|
114
|
+
headers,
|
|
115
|
+
body,
|
|
116
|
+
duplex: "half"
|
|
117
|
+
});
|
|
118
|
+
Object.defineProperty(this, "request", {
|
|
119
|
+
value: request,
|
|
120
|
+
enumerable: true,
|
|
121
|
+
configurable: true
|
|
122
|
+
});
|
|
123
|
+
return request;
|
|
124
|
+
},
|
|
125
|
+
enumerable: true,
|
|
126
|
+
configurable: true
|
|
127
|
+
});
|
|
128
|
+
const response = await router(requestContext);
|
|
129
|
+
if (!response) {
|
|
130
|
+
if (next) {
|
|
131
|
+
next();
|
|
132
|
+
} else {
|
|
133
|
+
res.statusCode = 404;
|
|
134
|
+
res.setHeader("content-length", "0");
|
|
135
|
+
res.end();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
res.statusCode = response.status;
|
|
141
|
+
for (const [key, value] of response.headers) {
|
|
142
|
+
if (key === "set-cookie") {
|
|
143
|
+
let sepIndex = value.indexOf(",") + 1;
|
|
144
|
+
if (!sepIndex) {
|
|
145
|
+
res.setHeader(key, value);
|
|
146
|
+
} else {
|
|
147
|
+
let index = 0;
|
|
148
|
+
do {
|
|
149
|
+
res.appendHeader(key, value.slice(index, sepIndex - 1));
|
|
150
|
+
index = sepIndex;
|
|
151
|
+
sepIndex = value.indexOf(",", sepIndex) + 1;
|
|
152
|
+
} while (sepIndex);
|
|
153
|
+
res.appendHeader(key, value.slice(index));
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
res.setHeader(key, value);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (!response.body) {
|
|
160
|
+
if (!response.headers.has("content-length")) {
|
|
161
|
+
res.setHeader("content-length", "0");
|
|
162
|
+
}
|
|
163
|
+
res.end();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const reader = response.body.getReader();
|
|
167
|
+
if (res.destroyed) {
|
|
168
|
+
reader.cancel();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
res.on("close", cancel);
|
|
172
|
+
res.on("error", cancel);
|
|
173
|
+
write();
|
|
174
|
+
function cancel(error) {
|
|
175
|
+
res.off("close", cancel);
|
|
176
|
+
res.off("error", cancel);
|
|
177
|
+
reader.cancel(error).catch(() => {
|
|
178
|
+
});
|
|
179
|
+
error && res.destroy(error);
|
|
180
|
+
}
|
|
181
|
+
async function write() {
|
|
182
|
+
try {
|
|
183
|
+
while (true) {
|
|
184
|
+
const { done, value } = await reader.read();
|
|
185
|
+
if (done) {
|
|
186
|
+
res.end();
|
|
187
|
+
return;
|
|
188
|
+
} else if (!res.write(value)) {
|
|
189
|
+
res.once("drain", write);
|
|
190
|
+
return;
|
|
191
|
+
} else if (res.flush) {
|
|
192
|
+
res.flush();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} catch (err) {
|
|
196
|
+
const error = err instanceof Error ? err : new Error("Error while writing to node response", {
|
|
197
|
+
cause: err
|
|
198
|
+
});
|
|
199
|
+
cancel(error);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/adapter/dev-server.ts
|
|
40
206
|
function createViteDevMiddleware(devServer, load, factory) {
|
|
41
207
|
let value;
|
|
42
208
|
let middleware;
|
|
@@ -65,7 +231,7 @@ async function createDevServer(configFile) {
|
|
|
65
231
|
const middleware = createViteDevMiddleware(
|
|
66
232
|
devServer,
|
|
67
233
|
async () => (await devServer.ssrLoadModule("@marko/run/router")).router,
|
|
68
|
-
|
|
234
|
+
createMiddleware
|
|
69
235
|
);
|
|
70
236
|
return devServer.middlewares.use(middleware);
|
|
71
237
|
}
|
package/dist/adapter/index.d.ts
CHANGED