@marko/run 0.0.1-beta6 → 0.0.1-beta8
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 +227 -72
- package/dist/adapter/default-entry.mjs +8 -5
- package/dist/adapter/index.cjs +50 -46
- package/dist/adapter/index.js +50 -46
- package/dist/adapter/middleware.cjs +34 -40
- package/dist/adapter/middleware.d.ts +4 -2
- package/dist/adapter/middleware.js +33 -40
- package/dist/cli/index.mjs +2 -2
- package/dist/runtime/index.d.ts +7 -3
- package/dist/runtime/internal.cjs +16 -13
- package/dist/runtime/internal.d.ts +2 -2
- package/dist/runtime/internal.js +14 -11
- package/dist/runtime/router.cjs +9 -9
- package/dist/runtime/router.d.ts +3 -4
- package/dist/runtime/router.js +6 -6
- package/dist/runtime/types.d.ts +11 -9
- package/dist/vite/codegen/index.d.ts +1 -1
- package/dist/vite/index.cjs +83 -58
- package/dist/vite/index.js +83 -58
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
> **Warning**
|
|
2
|
+
> This project is in BETA - use at your own peril, but please do provide helpful feedback.
|
|
3
|
+
|
|
1
4
|
<div align="center">
|
|
2
5
|
<!-- Logo -->
|
|
3
6
|
<h1>
|
|
@@ -12,7 +15,7 @@
|
|
|
12
15
|
</a>
|
|
13
16
|
</div>
|
|
14
17
|
|
|
15
|
-
`@marko/run`
|
|
18
|
+
`@marko/run` will help you get up and *running* with [Marko](https://markojs.com)
|
|
16
19
|
|
|
17
20
|
- Vite plugin that encapsulates [`@marko/vite`](https://github.com/marko-js/vite)
|
|
18
21
|
- CLI to simplify build modes
|
|
@@ -33,10 +36,12 @@ The package provides a command line tool `marko-run` which can be run using scri
|
|
|
33
36
|
|
|
34
37
|
### Getting Started / Zero Config
|
|
35
38
|
|
|
39
|
+
|
|
40
|
+
|
|
36
41
|
`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
42
|
1. Install `@marko/run`
|
|
38
43
|
2. Create file `src/routes/+page.marko`
|
|
39
|
-
3. Run `npx marko-run
|
|
44
|
+
3. Run `npx marko-run`
|
|
40
45
|
4. Open browser to `http://localhost:3000`
|
|
41
46
|
|
|
42
47
|
### Commands
|
|
@@ -45,21 +50,21 @@ The package provides a command line tool `marko-run` which can be run using scri
|
|
|
45
50
|
```bash
|
|
46
51
|
> npx marko-run dev
|
|
47
52
|
```
|
|
53
|
+
or (default command)
|
|
54
|
+
```bash
|
|
55
|
+
> npx marko-run
|
|
56
|
+
```
|
|
57
|
+
|
|
48
58
|
|
|
49
59
|
**`build`** - Create a production build
|
|
50
60
|
```bash
|
|
51
61
|
> npx marko-run build
|
|
52
62
|
```
|
|
53
63
|
|
|
54
|
-
**`
|
|
55
|
-
```bash
|
|
56
|
-
> npx marko-run preview
|
|
57
|
-
```
|
|
58
|
-
or (default command)
|
|
64
|
+
**`serve`** - Create a production build and serve
|
|
59
65
|
```bash
|
|
60
|
-
> npx marko-run
|
|
66
|
+
> npx marko-run serve
|
|
61
67
|
```
|
|
62
|
-
|
|
63
68
|
## Vite Plugin
|
|
64
69
|
|
|
65
70
|
This package’s Vite plugin discovers your route files, generates the routing code, and registers the `@marko/vite` plugin to compile your `.marko` files.
|
|
@@ -70,44 +75,91 @@ import { defineConfig } from "vite";
|
|
|
70
75
|
import marko from "@marko/run/vite"; // Import the Vite plugin
|
|
71
76
|
|
|
72
77
|
export default defineConfig({
|
|
73
|
-
plugins: [marko()]
|
|
78
|
+
plugins: [marko()] // Register the Vite plugin
|
|
74
79
|
})
|
|
75
80
|
```
|
|
76
81
|
|
|
77
82
|
## Adapters
|
|
78
83
|
|
|
79
|
-
|
|
84
|
+
Adapters provide the means to change the development, build and preview process to fit different deployment platforms and runtimes while allowing authors to write idiomatic code.
|
|
85
|
+
|
|
86
|
+
### Configure
|
|
87
|
+
|
|
88
|
+
Specify your adapter in the Vite config when registering the `@marko/run` plugin
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
// vite.config.ts
|
|
92
|
+
import { defineConfig } from "vite";
|
|
93
|
+
import marko from "@marko/run/vite";
|
|
94
|
+
import netlify from "@makor/run-adapter-netlify" // Import the adapter
|
|
95
|
+
|
|
96
|
+
export default defineConfig({
|
|
97
|
+
plugins: [marko({
|
|
98
|
+
adapter: netlify({ edge: true }) // Configure and apply the adapter
|
|
99
|
+
})]
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Adapter List
|
|
104
|
+
|
|
105
|
+
- ### [@marko/run-adapter-node](./packages/adapters/node/README.md)
|
|
106
|
+
- ### [@marko/run-adapter-netlify](./packages/adapters/netlify/README.md)
|
|
107
|
+
- ### [@marko/run-adapter-static](./packages/adapters/static/README.md)
|
|
80
108
|
|
|
81
109
|
## Runtime
|
|
82
110
|
|
|
83
111
|
Generally, when using an adapter, this runtime will be abstracted away.
|
|
84
112
|
|
|
113
|
+
<!-- TODO: Add examples -->
|
|
114
|
+
<!-- TODO: Split fetch and match + invoke in two sections and explain why you might use one or the other -->
|
|
115
|
+
|
|
85
116
|
```ts
|
|
86
|
-
import
|
|
117
|
+
import * as Run from '@marko/run/router`;
|
|
87
118
|
```
|
|
88
119
|
|
|
89
|
-
###
|
|
120
|
+
### Emdedding in Existing Server
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
### `Run.fetch`
|
|
90
125
|
|
|
91
126
|
```ts
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
request: Request;
|
|
96
|
-
platform: T;
|
|
97
|
-
}
|
|
127
|
+
async function fetch<T>(request: Request, platform: T) => Promise<Response | void>;
|
|
128
|
+
```
|
|
129
|
+
|
|
98
130
|
|
|
99
|
-
|
|
131
|
+
|
|
132
|
+
This asynchronous function takes a [WHATWG `Request` object](https://fetch.spec.whatwg.org/#request-class) object and an object containing any platform specific data you may want access to 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.
|
|
133
|
+
|
|
134
|
+
Express example:
|
|
135
|
+
```ts
|
|
136
|
+
import express from "express";
|
|
137
|
+
import * as Run from "@marko/run/router";
|
|
138
|
+
|
|
139
|
+
express()
|
|
140
|
+
.use(async (req, res, next) => {
|
|
141
|
+
const request = // ...code to create a WHATWG Request from `req`
|
|
142
|
+
|
|
143
|
+
const response = await Run.fetch(request, {
|
|
144
|
+
req,
|
|
145
|
+
res
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (response) {
|
|
149
|
+
// ...code to apply response to `res`
|
|
150
|
+
} else {
|
|
151
|
+
next();
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
.listen(3000);
|
|
100
155
|
```
|
|
101
156
|
|
|
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
157
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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.
|
|
158
|
+
### Other APIs
|
|
159
|
+
|
|
160
|
+
In some cases you might want more control over when route matching and invokation (creating a response) occur. For instance you may have middleware in your server which need to know if there is a matched route. The runtime provides these additional methods
|
|
109
161
|
|
|
110
|
-
### `
|
|
162
|
+
### `Run.match`
|
|
111
163
|
|
|
112
164
|
```ts
|
|
113
165
|
interface interface Route {
|
|
@@ -115,7 +167,7 @@ interface interface Route {
|
|
|
115
167
|
meta: unknown;
|
|
116
168
|
}
|
|
117
169
|
|
|
118
|
-
function
|
|
170
|
+
function match(method: string, pathname: string) => Route | null;
|
|
119
171
|
```
|
|
120
172
|
|
|
121
173
|
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.
|
|
@@ -123,20 +175,57 @@ This synchronous function takes an HTTP method and path name, then returns an ob
|
|
|
123
175
|
- `params` - a `{ key: value }` collection of any path parameters for the route
|
|
124
176
|
- `meta` - metadata for the route
|
|
125
177
|
|
|
126
|
-
### `
|
|
178
|
+
### `Run.invoke`
|
|
127
179
|
|
|
128
180
|
```ts
|
|
129
|
-
function
|
|
181
|
+
async function invoke<T>(route: Route, request: Request, platform: T) => Promise<Response | void>;
|
|
182
|
+
```
|
|
183
|
+
This asynchronous function takes a route object returned by [Run.match](#Run.match) the request and platform data and returns a response in the same way the [Run.fetch](#Run.fetch) does.
|
|
184
|
+
|
|
185
|
+
Express example:
|
|
186
|
+
```ts
|
|
187
|
+
import express from "express";
|
|
188
|
+
import * as Run from "@marko/run/router";
|
|
189
|
+
|
|
190
|
+
express()
|
|
191
|
+
.use((req, res) => {
|
|
192
|
+
const matchedRoute = Run.match(req.method, req.path);
|
|
193
|
+
if (matchedRoute) {
|
|
194
|
+
req.match = matchedRoute;
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
// ...other middleware
|
|
199
|
+
|
|
200
|
+
.use(async (req, res, next) => {
|
|
201
|
+
// Check if a route was previously matched
|
|
202
|
+
if (!req.match) {
|
|
203
|
+
next();
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const request = // ...code to create a WHATWG Request from `req`
|
|
208
|
+
const response = await Run.invoke(req.match, request, {
|
|
209
|
+
req,
|
|
210
|
+
res
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
if (response) {
|
|
214
|
+
// ...code to apply response to `res`
|
|
215
|
+
} else {
|
|
216
|
+
next();
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
.listen(3000);
|
|
130
220
|
```
|
|
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
221
|
|
|
133
222
|
|
|
134
223
|
|
|
135
224
|
## File-based Routing
|
|
136
225
|
|
|
137
|
-
### Nested Routing
|
|
226
|
+
<!-- ### Nested Routing
|
|
138
227
|
|
|
139
|
-
*🎗 TODO: provide a quick overview*
|
|
228
|
+
*🎗 TODO: provide a quick overview* -->
|
|
140
229
|
|
|
141
230
|
### Routes Directory
|
|
142
231
|
|
|
@@ -189,26 +278,35 @@ Typically, these will be `.js` or `.ts` files depending on your project. Like pa
|
|
|
189
278
|
<details>
|
|
190
279
|
<summary>More Info</summary>
|
|
191
280
|
|
|
192
|
-
- Valid exports are functions named `
|
|
193
|
-
-
|
|
194
|
-
-
|
|
195
|
-
-
|
|
281
|
+
- Valid exports are functions named `GET`, `POST`, `PUT`, or `DELETE`.
|
|
282
|
+
- Exports can be one of the following
|
|
283
|
+
- Handler function (see below)
|
|
284
|
+
- Array of handler functions - will be composed by calling them in order
|
|
285
|
+
- Promise that resolves to a handler function or array of handler functions
|
|
286
|
+
- Handler functions are synchronous or asynchronous functions that
|
|
287
|
+
- Receives a `context` and `next` argument,
|
|
288
|
+
- The `context` argument contains the WHATWG request object, path parameters, URL, and route metadata.
|
|
289
|
+
- The `next` argument will call the page for get requests where applicable or return a `204` response.
|
|
290
|
+
- Return a WHATWG response, throw a WHATWG response, return undefined. If the function return's undefined the `next` argument with be automatically called and used as the response.
|
|
196
291
|
|
|
197
292
|
```js
|
|
198
|
-
export function
|
|
293
|
+
export function POST(context, next) {
|
|
199
294
|
const { request, params, url, meta } = context;
|
|
200
295
|
return new Response('Successfully updated', { status: 200 });
|
|
201
296
|
}
|
|
202
297
|
|
|
203
|
-
export function
|
|
204
|
-
|
|
298
|
+
export function PUT(context, next) {
|
|
299
|
+
// `next` will be called for you by the runtime
|
|
205
300
|
}
|
|
206
301
|
|
|
207
|
-
export function
|
|
208
|
-
|
|
302
|
+
export async function GET(context, next) {
|
|
303
|
+
// do something before calling `next`
|
|
304
|
+
const response = await next();
|
|
305
|
+
// do something with the response from `next`
|
|
306
|
+
return response;
|
|
209
307
|
}
|
|
210
308
|
|
|
211
|
-
export function
|
|
309
|
+
export function DELETE(context, next) {
|
|
212
310
|
return new Response('Successfully removed', { status: 204 });
|
|
213
311
|
}
|
|
214
312
|
```
|
|
@@ -224,18 +322,23 @@ These files are like layouts, but for handlers. Middleware get called before han
|
|
|
224
322
|
<details>
|
|
225
323
|
<summary>More Info</summary>
|
|
226
324
|
|
|
227
|
-
Expects a `default` export that
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
325
|
+
- Expects a `default` export that can be one of the following
|
|
326
|
+
- Handler function (see below)
|
|
327
|
+
- Array of handler functions - will be composed by calling them in order
|
|
328
|
+
- Promise that resolves to a handler function or array of handler functions
|
|
329
|
+
- Handler functions are synchronous or asynchronous functions that
|
|
330
|
+
- Receives a `context` and `next` argument,
|
|
331
|
+
- The `context` argument contains the WHATWG request object, path parameters, URL, and route metadata.
|
|
332
|
+
- The `next` argument will call the page for get requests where applicable or return a `204` response.
|
|
333
|
+
- Return a WHATWG response, throw a WHATWG response, return undefined. If the function return's undefined the `next` argument with be automatically called and used as the response.
|
|
231
334
|
|
|
232
335
|
```ts
|
|
233
336
|
export default async function(context, next) {
|
|
234
|
-
const requestName = `${
|
|
337
|
+
const requestName = `${context.request.method} ${context.url.href}`;
|
|
235
338
|
let success = true;
|
|
236
339
|
console.log(`${requestName} request started`)
|
|
237
340
|
try {
|
|
238
|
-
return await next(); // Wait for subsequent middleware
|
|
341
|
+
return await next(); // Wait for subsequent middleware, handler and page
|
|
239
342
|
} catch (err) {
|
|
240
343
|
success = false;
|
|
241
344
|
throw err;
|
|
@@ -248,7 +351,9 @@ These files are like layouts, but for handlers. Middleware get called before han
|
|
|
248
351
|
|
|
249
352
|
#### `+meta.*`
|
|
250
353
|
|
|
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.
|
|
354
|
+
These files represent static metadata to attach to the route. This metadata will be automatically provided on the the route `context` when invoking a route.
|
|
355
|
+
|
|
356
|
+
|
|
252
357
|
|
|
253
358
|
### Special Files
|
|
254
359
|
|
|
@@ -276,7 +381,21 @@ Responses with this page will have a `500` status code.
|
|
|
276
381
|
|
|
277
382
|
### Execution Order
|
|
278
383
|
|
|
279
|
-
|
|
384
|
+
Given the following routes directory structure
|
|
385
|
+
|
|
386
|
+
<pre>
|
|
387
|
+
routes/
|
|
388
|
+
about/
|
|
389
|
+
+handler.js
|
|
390
|
+
+layout.marko
|
|
391
|
+
+middleware.js
|
|
392
|
+
+page.marko
|
|
393
|
+
+layout.marko
|
|
394
|
+
+middleware.js
|
|
395
|
+
+page.marko
|
|
396
|
+
</pre>
|
|
397
|
+
|
|
398
|
+
When the path `"/about"` is requested, the routable files execute in the following order:
|
|
280
399
|
|
|
281
400
|
1. Middlewares from root-most to leaf-most
|
|
282
401
|
2. Handler
|
|
@@ -285,21 +404,21 @@ For a matched route, the routable files execute in the following order:
|
|
|
285
404
|
|
|
286
405
|
```mermaid
|
|
287
406
|
sequenceDiagram
|
|
288
|
-
participant
|
|
289
|
-
participant
|
|
290
|
-
participant
|
|
291
|
-
participant
|
|
292
|
-
participant
|
|
293
|
-
participant
|
|
294
|
-
Note over
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
407
|
+
participant MW1 as routes/+middleware.js
|
|
408
|
+
participant MW2 as routes/about/+middleware.js
|
|
409
|
+
participant H as routes/about/+handler.js
|
|
410
|
+
participant L1 as routes/+layout.marko
|
|
411
|
+
participant L2 as routes/about/+layout.marko
|
|
412
|
+
participant P as routes/about/+page.marko
|
|
413
|
+
Note over L1,P: Combined at build-time as a single component
|
|
414
|
+
MW1->>MW2: next()
|
|
415
|
+
MW2->>H: next()
|
|
416
|
+
H->>L1: next()
|
|
417
|
+
L1->L2: ${input.renderBody}
|
|
418
|
+
L2->P: ${input.renderBody}
|
|
419
|
+
L1-->>H: Stream Response
|
|
420
|
+
H-->>MW2: Response
|
|
421
|
+
MW2-->>MW1: Response
|
|
303
422
|
```
|
|
304
423
|
|
|
305
424
|
### Path Structure
|
|
@@ -315,16 +434,15 @@ Within the _routes directory_, the directory structure will determine the path t
|
|
|
315
434
|
/projects
|
|
316
435
|
```
|
|
317
436
|
|
|
318
|
-
2. **Pathless directories** - These directories do **not** contribute their name to the route's served path. Directory names that start with an underscore (`_`)
|
|
437
|
+
2. **Pathless directories** - These directories do **not** contribute their name to the route's served path. Directory names that start with an underscore (`_`) will be a pathless directory.
|
|
319
438
|
|
|
320
439
|
Examples:
|
|
321
440
|
```
|
|
322
441
|
/_users
|
|
323
442
|
/_public
|
|
324
|
-
/index
|
|
325
443
|
```
|
|
326
444
|
|
|
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
|
|
445
|
+
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 be captured but it will be matched.
|
|
328
446
|
|
|
329
447
|
Examples:
|
|
330
448
|
```
|
|
@@ -333,7 +451,7 @@ Within the _routes directory_, the directory structure will determine the path t
|
|
|
333
451
|
/$
|
|
334
452
|
```
|
|
335
453
|
|
|
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
|
|
454
|
+
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 will not be captured but it will match. Catch-all directories can be used to make `404` Not Found routes at any level, including the root.
|
|
337
455
|
|
|
338
456
|
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.
|
|
339
457
|
|
|
@@ -344,6 +462,43 @@ Within the _routes directory_, the directory structure will determine the path t
|
|
|
344
462
|
/$$
|
|
345
463
|
```
|
|
346
464
|
|
|
347
|
-
### Match Ranking
|
|
465
|
+
<!-- ### Match Ranking
|
|
466
|
+
|
|
467
|
+
*TODO: Write some things* -->
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
## TypeScript
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
### Global Namespace
|
|
474
|
+
marko/run provides a global namespace `MarkoRun` with the folling types:
|
|
475
|
+
|
|
476
|
+
**`MarkoRun.Handler`** - Type that represents a handler function to be exported by a +handler or +middleware file
|
|
477
|
+
|
|
478
|
+
**`MarkoRun.CurrentRoute`** - Type of the route's params and meta data
|
|
479
|
+
|
|
480
|
+
**`MarkoRun.CurrentContext`** - Type of the request context object in a handler and `out.global` in your Marko files
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
### Generated Types
|
|
484
|
+
If a [TSConfig](https://www.typescriptlang.org/tsconfig) file is discovered in the project root, the Vite plugin will automatically generate a .d.ts file which provides more specific types for each of your middleware, handlers, layouts and pages. This file will be generated at `.marko-run/routes.d.ts` whenever the project is built - including dev.
|
|
485
|
+
> **Note** TypeScript will not include this file by default. If you are not using the [Marko VSCode plugin](https://marketplace.visualstudio.com/items?itemName=Marko-JS.marko-vscode) and you will need to [add it in your tsconfig](https://www.typescriptlang.org/tsconfig#include).
|
|
486
|
+
|
|
487
|
+
These types are replaced with more specific versions per routeable file:
|
|
488
|
+
|
|
489
|
+
**`MarkoRun.Handler`**
|
|
490
|
+
- Overrides context with specific MarkoRun.CurrentContext
|
|
491
|
+
|
|
492
|
+
**`MarkoRun.CurrentRoute`**
|
|
493
|
+
- Adds specific parameters and meta types
|
|
494
|
+
- In middleware and layouts which are used in many routes, this type will be a union of all possible routes that file will see
|
|
495
|
+
|
|
496
|
+
**`MarkoRun.CurrentContext`**
|
|
497
|
+
- In middleware and layouts which are used in many routes, this type will be a union of all possible routes that file will see.
|
|
498
|
+
- When an adapter is used, it can provide types for the platform
|
|
499
|
+
|
|
500
|
+
## Beta Roadmap
|
|
348
501
|
|
|
349
|
-
|
|
502
|
+
- Error handling
|
|
503
|
+
- Error component
|
|
504
|
+
- Redirect component
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import createStaticServe from "serve-static";
|
|
2
2
|
import compression from "compression";
|
|
3
3
|
import { createServer } from "http";
|
|
4
|
-
import createMiddleware from "@marko/run/adapter/middleware";
|
|
5
|
-
import {
|
|
4
|
+
import { createMiddleware } from "@marko/run/adapter/middleware";
|
|
5
|
+
import { fetch } from "@marko/run/router";
|
|
6
|
+
import { dirname } from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
10
|
|
|
7
11
|
const { PORT = 3456 } = process.env;
|
|
8
12
|
|
|
9
|
-
const
|
|
10
|
-
const middleware = createMiddleware(router);
|
|
13
|
+
const middleware = createMiddleware(fetch);
|
|
11
14
|
const compress = compression({
|
|
12
15
|
threshold: 500,
|
|
13
16
|
});
|
|
14
|
-
const staticServe = createStaticServe(
|
|
17
|
+
const staticServe = createStaticServe(__dirname, {
|
|
15
18
|
index: false,
|
|
16
19
|
immutable: true,
|
|
17
20
|
maxAge: "365 days",
|
package/dist/adapter/index.cjs
CHANGED
|
@@ -94,8 +94,8 @@ function getOrigin(req, protocol, host, trustProxy) {
|
|
|
94
94
|
}
|
|
95
95
|
return `${protocol}://${host}`;
|
|
96
96
|
}
|
|
97
|
-
function createMiddleware(
|
|
98
|
-
const { trustProxy = process.env.TRUST_PROXY === "1" } = options;
|
|
97
|
+
function createMiddleware(fetch2, options = {}) {
|
|
98
|
+
const { trustProxy = process.env.TRUST_PROXY === "1", devServer } = options;
|
|
99
99
|
let { origin = process.env.ORIGIN } = options;
|
|
100
100
|
let protocol;
|
|
101
101
|
let host;
|
|
@@ -107,45 +107,28 @@ function createMiddleware(router, options = {}) {
|
|
|
107
107
|
origin ?? (origin = getOrigin(req, protocol, host, trustProxy));
|
|
108
108
|
const url = new URL(req.url, origin);
|
|
109
109
|
const ip = req.ip || trustProxy && getForwardedHeader(req, "for") || req.socket.remoteAddress || "";
|
|
110
|
-
const
|
|
110
|
+
const headers = req.headers;
|
|
111
|
+
const body = req.method === "GET" || req.method === "HEAD" ? void 0 : req.socket ? req : new ReadableStream({
|
|
112
|
+
start(controller) {
|
|
113
|
+
req.on("data", (chunk) => controller.enqueue(chunk));
|
|
114
|
+
req.on("end", () => controller.close());
|
|
115
|
+
req.on("error", (err) => controller.error(err));
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
const request = new Request(url, {
|
|
111
119
|
method: req.method,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
+
headers,
|
|
121
|
+
body,
|
|
122
|
+
duplex: "half"
|
|
123
|
+
});
|
|
124
|
+
const response = await fetch2(request, {
|
|
125
|
+
ip,
|
|
126
|
+
request: req,
|
|
127
|
+
response: res,
|
|
128
|
+
setCookie(cookie) {
|
|
129
|
+
res.appendHeader("set-cookie", cookie);
|
|
120
130
|
}
|
|
121
|
-
};
|
|
122
|
-
Object.defineProperty(requestContext, "request", {
|
|
123
|
-
get() {
|
|
124
|
-
const headers = req.headers;
|
|
125
|
-
const body = req.method === "GET" || req.method === "HEAD" ? void 0 : req.socket ? req : new ReadableStream({
|
|
126
|
-
start(controller) {
|
|
127
|
-
req.on("data", (chunk) => controller.enqueue(chunk));
|
|
128
|
-
req.on("end", () => controller.close());
|
|
129
|
-
req.on("error", (err) => controller.error(err));
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
const request = new Request(url, {
|
|
133
|
-
method: req.method,
|
|
134
|
-
headers,
|
|
135
|
-
body,
|
|
136
|
-
duplex: "half"
|
|
137
|
-
});
|
|
138
|
-
Object.defineProperty(this, "request", {
|
|
139
|
-
value: request,
|
|
140
|
-
enumerable: true,
|
|
141
|
-
configurable: true
|
|
142
|
-
});
|
|
143
|
-
return request;
|
|
144
|
-
},
|
|
145
|
-
enumerable: true,
|
|
146
|
-
configurable: true
|
|
147
131
|
});
|
|
148
|
-
const response = await router(requestContext);
|
|
149
132
|
if (!response) {
|
|
150
133
|
if (next) {
|
|
151
134
|
next();
|
|
@@ -196,7 +179,17 @@ function createMiddleware(router, options = {}) {
|
|
|
196
179
|
res.off("error", cancel);
|
|
197
180
|
reader.cancel(error).catch(() => {
|
|
198
181
|
});
|
|
199
|
-
|
|
182
|
+
if (error) {
|
|
183
|
+
if (process.env.NODE_ENV !== "production" && devServer) {
|
|
184
|
+
res.end();
|
|
185
|
+
devServer.ws.send({
|
|
186
|
+
type: "error",
|
|
187
|
+
err: { message: error.message, stack: error.stack || "" }
|
|
188
|
+
});
|
|
189
|
+
} else {
|
|
190
|
+
res.destroy(error);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
200
193
|
}
|
|
201
194
|
async function write() {
|
|
202
195
|
try {
|
|
@@ -223,6 +216,7 @@ function createMiddleware(router, options = {}) {
|
|
|
223
216
|
}
|
|
224
217
|
|
|
225
218
|
// src/adapter/dev-server.ts
|
|
219
|
+
var fixedErrors = /* @__PURE__ */ new WeakSet();
|
|
226
220
|
function createViteDevMiddleware(devServer, load, factory) {
|
|
227
221
|
let value;
|
|
228
222
|
let middleware;
|
|
@@ -235,10 +229,16 @@ function createViteDevMiddleware(devServer, load, factory) {
|
|
|
235
229
|
}
|
|
236
230
|
await middleware(req, res, next);
|
|
237
231
|
} catch (err) {
|
|
232
|
+
res.statusCode = 500;
|
|
238
233
|
if (err instanceof Error) {
|
|
239
|
-
|
|
234
|
+
if (!fixedErrors.has(err)) {
|
|
235
|
+
fixedErrors.add(err);
|
|
236
|
+
devServer.ssrFixStacktrace(err);
|
|
237
|
+
}
|
|
238
|
+
res.end(err.stack);
|
|
239
|
+
} else {
|
|
240
|
+
res.end();
|
|
240
241
|
}
|
|
241
|
-
return next == null ? void 0 : next();
|
|
242
242
|
}
|
|
243
243
|
};
|
|
244
244
|
}
|
|
@@ -246,12 +246,16 @@ async function createDevServer(configFile) {
|
|
|
246
246
|
const devServer = await (0, import_vite.createServer)({
|
|
247
247
|
configFile,
|
|
248
248
|
appType: "custom",
|
|
249
|
-
server: { middlewareMode: true }
|
|
249
|
+
server: { middlewareMode: true },
|
|
250
|
+
resolve: {
|
|
251
|
+
dedupe: ["marko"],
|
|
252
|
+
conditions: ["worker"]
|
|
253
|
+
}
|
|
250
254
|
});
|
|
251
255
|
const middleware = createViteDevMiddleware(
|
|
252
256
|
devServer,
|
|
253
|
-
async () =>
|
|
254
|
-
createMiddleware
|
|
257
|
+
async () => await devServer.ssrLoadModule("@marko/run/router"),
|
|
258
|
+
(module2) => createMiddleware(module2.fetch, { devServer })
|
|
255
259
|
);
|
|
256
260
|
return devServer.middlewares.use(middleware);
|
|
257
261
|
}
|
|
@@ -347,8 +351,8 @@ function adapter() {
|
|
|
347
351
|
});
|
|
348
352
|
});
|
|
349
353
|
},
|
|
350
|
-
async startPreview(
|
|
351
|
-
const server = await spawnServer(`node ${entry}`, port, envFile
|
|
354
|
+
async startPreview(_dir, entry, port, envFile) {
|
|
355
|
+
const server = await spawnServer(`node ${entry}`, port, envFile);
|
|
352
356
|
console.log(`Preview server started: http://localhost:${server.port}`);
|
|
353
357
|
}
|
|
354
358
|
};
|