@marko/run 0.0.1-beta3 → 0.0.1-beta4
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 +102 -74
- 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/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 +3 -5
- package/dist/vite/index.cjs +23 -15
- package/dist/vite/index.d.ts +1 -1
- package/dist/vite/index.js +23 -15
- 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,33 @@
|
|
|
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
|
-
Vite plugin for Marko with these features
|
|
15
|
-
|
|
15
|
+
`@marko/serve` is a Vite plugin for [Marko](https://markojs.com), with these features:
|
|
16
|
+
|
|
17
|
+
- Encapsulates [`@marko/vite`](https://github.com/marko-js/vite)
|
|
16
18
|
- File-based routing with layouts and middleware
|
|
17
19
|
- Efficient routing using a compiled static trie
|
|
18
|
-
- Designed with web standards to run anywhere
|
|
20
|
+
- [Designed with web standards](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern/URLPattern) to run anywhere
|
|
19
21
|
|
|
20
|
-
##
|
|
22
|
+
## Installation
|
|
21
23
|
|
|
22
|
-
```
|
|
24
|
+
```sh
|
|
23
25
|
npm install @marko/run
|
|
24
26
|
```
|
|
25
27
|
|
|
26
28
|
## Vite Plugin
|
|
27
29
|
|
|
28
|
-
This package
|
|
30
|
+
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
31
|
|
|
30
32
|
```ts
|
|
31
33
|
// vite.config.ts
|
|
@@ -35,27 +37,32 @@ import marko from "@marko/run/vite"; // Import the Vite plugin
|
|
|
35
37
|
export default defineConfig({
|
|
36
38
|
plugins: [marko()], // Register the Vite plugin
|
|
37
39
|
build: {
|
|
38
|
-
sourcemap: true, // Generate sourcemaps for all builds
|
|
39
|
-
emptyOutDir: false, // Avoid server & client deleting files from each other.
|
|
40
|
+
sourcemap: true, // Generate sourcemaps for all builds
|
|
41
|
+
emptyOutDir: false, // Avoid server & client deleting files from each other. TODO: do we have to make the user set this themselves?
|
|
40
42
|
}
|
|
41
43
|
})
|
|
42
44
|
```
|
|
43
45
|
|
|
44
46
|
## Runtime
|
|
45
47
|
|
|
46
|
-
Generally
|
|
48
|
+
Generally, using one of the adapters is more convenient <!-- TODO: is an “adapter” the same thing as using the Vite plugin above? -->, but this package also provides a runtime API:
|
|
47
49
|
|
|
48
50
|
```ts
|
|
49
51
|
import { router, getMatchedRoute } from '@marko/run`;
|
|
50
52
|
```
|
|
51
53
|
|
|
52
54
|
### `router`
|
|
55
|
+
|
|
53
56
|
```ts
|
|
54
57
|
(request: Request) => Promise<Response>;
|
|
55
58
|
```
|
|
56
|
-
|
|
59
|
+
|
|
60
|
+
This asynchronous function takes a [WHATWG `Request` object](https://fetch.spec.whatwg.org/#request-class) and returns a [`Response` object](https://fetch.spec.whatwg.org/#response-class) generated by executing any matched route files.
|
|
61
|
+
|
|
62
|
+
If no match is found, returns a response with a `404` status code. If an unhandled error occurs, returns a response with a `500` status code.
|
|
57
63
|
|
|
58
64
|
### `getMatchedRoute`
|
|
65
|
+
|
|
59
66
|
```ts
|
|
60
67
|
(method: string, url: URL) => {
|
|
61
68
|
params: Record<string, string>;
|
|
@@ -63,20 +70,25 @@ This asynchronouse function takes a [WHATWG request](https://fetch.spec.whatwg.o
|
|
|
63
70
|
invoke(request: Request): Promise<Response>;
|
|
64
71
|
} | null;
|
|
65
72
|
```
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- `
|
|
73
|
+
|
|
74
|
+
This synchronous function takes an HTTP method + URL, then returns an object representing the best match — or `null` if no match is found.
|
|
75
|
+
|
|
76
|
+
- `params` - a `{ key: value }` collection of any path parameters for the route
|
|
77
|
+
- `meta` - metadata for the route
|
|
78
|
+
- `invoke` - an asynchronous function that takes a `Request` and returns the `Response` generated from matched route files.
|
|
79
|
+
> **Note**: unlike the top-level `router` function, errors will not be caught.
|
|
70
80
|
|
|
71
81
|
## File-based Routing
|
|
72
82
|
|
|
73
|
-
|
|
83
|
+
### Nested Routing
|
|
74
84
|
|
|
75
|
-
|
|
85
|
+
*🎗 TODO: provide a quick overview*
|
|
76
86
|
|
|
77
87
|
### Routes Directory
|
|
78
88
|
|
|
79
|
-
|
|
89
|
+
The plugin looks for route files in the configured **routes directory**. By default, that’s `./src/routes`, relative to the Vite config file.
|
|
90
|
+
|
|
91
|
+
To change what directory routes are found in:
|
|
80
92
|
|
|
81
93
|
```ts
|
|
82
94
|
// vite.config.ts
|
|
@@ -86,55 +98,56 @@ import marko from "@marko/run/vite";
|
|
|
86
98
|
export default defineConfig({
|
|
87
99
|
plugins: [marko({
|
|
88
100
|
routesDir: 'src/pages' // Use `./src/pages` (relative to this file) as the routes directory
|
|
89
|
-
})]
|
|
90
|
-
//...
|
|
101
|
+
})]
|
|
91
102
|
})
|
|
92
103
|
```
|
|
93
104
|
|
|
94
105
|
### Routeable Files
|
|
95
106
|
|
|
96
|
-
|
|
107
|
+
To allow for colocation of files that shouldn’t be served (like tests, assets, etc.), the router only recognizes certain filenames.
|
|
108
|
+
|
|
109
|
+
The following filenames will be discovered in any directory inside your application’s [routes directory](#routes-directory).
|
|
97
110
|
|
|
98
111
|
#### `+page.marko`
|
|
99
|
-
|
|
112
|
+
|
|
113
|
+
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
114
|
|
|
101
115
|
#### `+layout.marko`
|
|
102
|
-
These files provide a layout component which will wrap all nested layouts and pages.
|
|
103
116
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
</div>
|
|
116
|
-
```
|
|
117
|
-
</details>
|
|
117
|
+
These files provide a **layout component**, which will wrap all nested layouts and pages.
|
|
118
|
+
|
|
119
|
+
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.
|
|
120
|
+
|
|
121
|
+
```marko
|
|
122
|
+
<main>
|
|
123
|
+
<h1>My Products</h1>
|
|
124
|
+
|
|
125
|
+
${input.renderBody} // render the page or layout here
|
|
126
|
+
</main>
|
|
127
|
+
```
|
|
118
128
|
|
|
119
129
|
#### `+handler.*`
|
|
120
|
-
|
|
130
|
+
|
|
131
|
+
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? -->
|
|
132
|
+
|
|
133
|
+
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
134
|
|
|
122
135
|
<details>
|
|
123
136
|
<summary>More Info</summary>
|
|
124
137
|
|
|
125
|
-
- Valid exports are
|
|
126
|
-
- Each
|
|
127
|
-
|
|
128
|
-
|
|
138
|
+
- Valid exports are functions named `get`, `post`, `put`, or `del`.
|
|
139
|
+
- Each export receives a `context` and `next` argument, and should return a WHATWG `Response` either synchronously or asynchronously.
|
|
140
|
+
- The `context` argument contains the WHATWG request object, path parameters, URL, and route metadata.
|
|
141
|
+
- The `next` argument will call the page for get requests where applicable or return a `204` response.
|
|
129
142
|
|
|
130
|
-
```
|
|
143
|
+
```js
|
|
131
144
|
export function post(context, next) {
|
|
132
145
|
const { request, params, url, meta } = context;
|
|
133
|
-
return new Response('updated', { status: 200 });
|
|
146
|
+
return new Response('Successfully updated', { status: 200 });
|
|
134
147
|
}
|
|
135
148
|
|
|
136
149
|
export function put(context, next) {
|
|
137
|
-
return new Response('created', { status: 201 }); // handle the request
|
|
150
|
+
return new Response('Successfully created', { status: 201 }); // handle the request
|
|
138
151
|
}
|
|
139
152
|
|
|
140
153
|
export function get(context, next) {
|
|
@@ -142,29 +155,33 @@ These files establish a route at the current directory path which can handle req
|
|
|
142
155
|
}
|
|
143
156
|
|
|
144
157
|
export function del(context, next) {
|
|
145
|
-
return new Response('removed', { status: 204 });
|
|
158
|
+
return new Response('Successfully removed', { status: 204 });
|
|
146
159
|
}
|
|
147
160
|
```
|
|
148
161
|
</details>
|
|
149
162
|
|
|
150
163
|
|
|
151
|
-
#### `+
|
|
152
|
-
|
|
164
|
+
#### `+middleware.*`
|
|
165
|
+
|
|
166
|
+
These files are like layouts, but for handlers. Middleware get called before handlers and let you perform arbitrary work before and after.
|
|
167
|
+
|
|
168
|
+
> **Note**: Unlike handlers, middleware run for all HTTP methods.
|
|
153
169
|
|
|
154
170
|
<details>
|
|
155
171
|
<summary>More Info</summary>
|
|
156
172
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
- The `
|
|
173
|
+
Expects a `default` export that receives a `context` and `next` argument, and should return a WHATWG response either synchronously or asynchronously.
|
|
174
|
+
|
|
175
|
+
- The `context` argument contains the WHATWG `Request` object, path parameters, URL, and route metadata.
|
|
176
|
+
- The `next` argument will call the next middleware, handler, or page for the route.
|
|
160
177
|
|
|
161
178
|
```ts
|
|
162
179
|
export default async function(context, next) {
|
|
163
|
-
const requestName = `${ctx.request.method} ${ctx.url.href}`;
|
|
180
|
+
const requestName = `${ctx.request.method} ${ctx.url.href}`; // TODO: could this be just `ctx.url` with a `toString` that then grabs `.href`?
|
|
164
181
|
let success = true;
|
|
165
182
|
console.log(`${requestName} request started`)
|
|
166
183
|
try {
|
|
167
|
-
return await next(); //
|
|
184
|
+
return await next(); // Wait for subsequent middleware/handler/page
|
|
168
185
|
} catch (err) {
|
|
169
186
|
success = false;
|
|
170
187
|
throw err;
|
|
@@ -176,30 +193,42 @@ These files are analagous to layouts for handlers. Middlware get called before h
|
|
|
176
193
|
</details>
|
|
177
194
|
|
|
178
195
|
#### `+meta.*`
|
|
179
|
-
|
|
196
|
+
|
|
197
|
+
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
198
|
|
|
181
199
|
### Special Files
|
|
182
200
|
|
|
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
|
|
201
|
+
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” -->
|
|
202
|
+
|
|
203
|
+
These special pages are subject to a root layout file (`pages/+layout.marko` in the default configuration).
|
|
184
204
|
|
|
185
205
|
#### `+404.marko`
|
|
186
|
-
|
|
206
|
+
|
|
207
|
+
This special page responds to any request where:
|
|
208
|
+
|
|
209
|
+
- The `Accept` request header includes `text/html`
|
|
210
|
+
- *And* no other handler or page rendered the request
|
|
211
|
+
|
|
212
|
+
Responses with this page will have a `404` status code.
|
|
187
213
|
|
|
188
214
|
#### `+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
215
|
|
|
216
|
+
This special page responds to any request where:
|
|
217
|
+
|
|
218
|
+
- The `Accept` request header includes `text/html`
|
|
219
|
+
- *And* an uncaught error occurs while serving the request
|
|
220
|
+
|
|
221
|
+
Responses with this page will have a `500` status code.
|
|
191
222
|
|
|
192
223
|
### Execution Order
|
|
193
224
|
|
|
194
|
-
For a matched route the routable files execute in the following order
|
|
225
|
+
For a matched route, the routable files execute in the following order:
|
|
226
|
+
|
|
195
227
|
1. Middlewares from root-most to leaf-most
|
|
196
228
|
2. Handler
|
|
197
229
|
3. Layouts from root-most to leaf-most
|
|
198
230
|
4. Page
|
|
199
231
|
|
|
200
|
-
<details>
|
|
201
|
-
<summary>Diagram</summary>
|
|
202
|
-
|
|
203
232
|
```mermaid
|
|
204
233
|
sequenceDiagram
|
|
205
234
|
participant Middleware1
|
|
@@ -218,13 +247,12 @@ sequenceDiagram
|
|
|
218
247
|
Handler-->>Middleware2: Response
|
|
219
248
|
Middleware2-->>Middleware1: Response
|
|
220
249
|
```
|
|
221
|
-
</details>
|
|
222
250
|
|
|
223
251
|
### Path Structure
|
|
224
252
|
|
|
225
|
-
Within the _routes directory_ the directory structure will determine the path the route will be served. There are four types of directory names:
|
|
253
|
+
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
254
|
|
|
227
|
-
|
|
255
|
+
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
256
|
|
|
229
257
|
Examples:
|
|
230
258
|
```
|
|
@@ -233,7 +261,7 @@ Within the _routes directory_ the directory structure will determine the path th
|
|
|
233
261
|
/projects
|
|
234
262
|
```
|
|
235
263
|
|
|
236
|
-
|
|
264
|
+
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
265
|
|
|
238
266
|
Examples:
|
|
239
267
|
```
|
|
@@ -242,7 +270,7 @@ Within the _routes directory_ the directory structure will determine the path th
|
|
|
242
270
|
/index
|
|
243
271
|
```
|
|
244
272
|
|
|
245
|
-
|
|
273
|
+
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
274
|
|
|
247
275
|
Examples:
|
|
248
276
|
```
|
|
@@ -251,9 +279,9 @@ Within the _routes directory_ the directory structure will determine the path th
|
|
|
251
279
|
/$
|
|
252
280
|
```
|
|
253
281
|
|
|
254
|
-
|
|
282
|
+
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
283
|
|
|
256
|
-
Because catch-all directories
|
|
284
|
+
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
285
|
|
|
258
286
|
Examples:
|
|
259
287
|
```
|
|
@@ -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
package/dist/adapter/index.js
CHANGED
|
@@ -4,7 +4,173 @@ import { fileURLToPath } from "url";
|
|
|
4
4
|
|
|
5
5
|
// src/adapter/dev-server.ts
|
|
6
6
|
import { createServer } from "vite";
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
// src/adapter/middleware.ts
|
|
9
|
+
import * as webStream from "stream/web";
|
|
10
|
+
import installCrypto from "@hattip/polyfills/crypto";
|
|
11
|
+
installCrypto();
|
|
12
|
+
for (const key of Object.keys(webStream)) {
|
|
13
|
+
if (!(key in global)) {
|
|
14
|
+
global[key] = webStream[key];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function getForwardedHeader(req, name) {
|
|
18
|
+
const value = req.headers["x-forwarded-" + name];
|
|
19
|
+
if (value) {
|
|
20
|
+
if (typeof value === "string") {
|
|
21
|
+
const index = value.indexOf(",");
|
|
22
|
+
return index < 0 ? value : value.slice(0, index);
|
|
23
|
+
}
|
|
24
|
+
return value[0];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function getOrigin(req, protocol, host, trustProxy) {
|
|
28
|
+
var _a;
|
|
29
|
+
protocol ?? (protocol = req.protocol || trustProxy && getForwardedHeader(req, "proto") || ((_a = req.socket) == null ? void 0 : _a.encrypted) && "https" || "http");
|
|
30
|
+
host ?? (host = trustProxy && getForwardedHeader(req, "host") || req.headers.host);
|
|
31
|
+
if (!host) {
|
|
32
|
+
if (process.env.NODE_ENV !== "production") {
|
|
33
|
+
host = "localhost";
|
|
34
|
+
console.warn(
|
|
35
|
+
`Could not automatically determine the origin host, using 'localhost'. Use the 'origin' option or the 'ORIGIN' environment variable to set the origin explicitly.`
|
|
36
|
+
);
|
|
37
|
+
} else {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Could not automatically determine the origin host. Use the 'origin' option or the 'ORIGIN' environment variable to set the origin explicitly.`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return `${protocol}://${host}`;
|
|
44
|
+
}
|
|
45
|
+
function createMiddleware(router, options = {}) {
|
|
46
|
+
const { trustProxy = process.env.TRUST_PROXY === "1" } = options;
|
|
47
|
+
let { origin = process.env.ORIGIN } = options;
|
|
48
|
+
let protocol;
|
|
49
|
+
let host;
|
|
50
|
+
if (origin) {
|
|
51
|
+
({ protocol, host } = new URL(origin));
|
|
52
|
+
protocol = protocol.slice(0, -1);
|
|
53
|
+
}
|
|
54
|
+
return async (req, res, next) => {
|
|
55
|
+
origin ?? (origin = getOrigin(req, protocol, host, trustProxy));
|
|
56
|
+
const url = new URL(req.url, origin);
|
|
57
|
+
const ip = req.ip || trustProxy && getForwardedHeader(req, "for") || req.socket.remoteAddress || "";
|
|
58
|
+
const requestContext = {
|
|
59
|
+
method: req.method,
|
|
60
|
+
url,
|
|
61
|
+
platform: {
|
|
62
|
+
ip,
|
|
63
|
+
request: req,
|
|
64
|
+
response: res,
|
|
65
|
+
setCookie(cookie) {
|
|
66
|
+
res.appendHeader("set-cookie", cookie);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
Object.defineProperty(requestContext, "request", {
|
|
71
|
+
get() {
|
|
72
|
+
const headers = req.headers;
|
|
73
|
+
const body = req.method === "GET" || req.method === "HEAD" ? void 0 : req.socket ? req : new ReadableStream({
|
|
74
|
+
start(controller) {
|
|
75
|
+
req.on("data", (chunk) => controller.enqueue(chunk));
|
|
76
|
+
req.on("end", () => controller.close());
|
|
77
|
+
req.on("error", (err) => controller.error(err));
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
const request = new Request(url, {
|
|
81
|
+
method: req.method,
|
|
82
|
+
headers,
|
|
83
|
+
body,
|
|
84
|
+
duplex: "half"
|
|
85
|
+
});
|
|
86
|
+
Object.defineProperty(this, "request", {
|
|
87
|
+
value: request,
|
|
88
|
+
enumerable: true,
|
|
89
|
+
configurable: true
|
|
90
|
+
});
|
|
91
|
+
return request;
|
|
92
|
+
},
|
|
93
|
+
enumerable: true,
|
|
94
|
+
configurable: true
|
|
95
|
+
});
|
|
96
|
+
const response = await router(requestContext);
|
|
97
|
+
if (!response) {
|
|
98
|
+
if (next) {
|
|
99
|
+
next();
|
|
100
|
+
} else {
|
|
101
|
+
res.statusCode = 404;
|
|
102
|
+
res.setHeader("content-length", "0");
|
|
103
|
+
res.end();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
res.statusCode = response.status;
|
|
109
|
+
for (const [key, value] of response.headers) {
|
|
110
|
+
if (key === "set-cookie") {
|
|
111
|
+
let sepIndex = value.indexOf(",") + 1;
|
|
112
|
+
if (!sepIndex) {
|
|
113
|
+
res.setHeader(key, value);
|
|
114
|
+
} else {
|
|
115
|
+
let index = 0;
|
|
116
|
+
do {
|
|
117
|
+
res.appendHeader(key, value.slice(index, sepIndex - 1));
|
|
118
|
+
index = sepIndex;
|
|
119
|
+
sepIndex = value.indexOf(",", sepIndex) + 1;
|
|
120
|
+
} while (sepIndex);
|
|
121
|
+
res.appendHeader(key, value.slice(index));
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
res.setHeader(key, value);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (!response.body) {
|
|
128
|
+
if (!response.headers.has("content-length")) {
|
|
129
|
+
res.setHeader("content-length", "0");
|
|
130
|
+
}
|
|
131
|
+
res.end();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const reader = response.body.getReader();
|
|
135
|
+
if (res.destroyed) {
|
|
136
|
+
reader.cancel();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
res.on("close", cancel);
|
|
140
|
+
res.on("error", cancel);
|
|
141
|
+
write();
|
|
142
|
+
function cancel(error) {
|
|
143
|
+
res.off("close", cancel);
|
|
144
|
+
res.off("error", cancel);
|
|
145
|
+
reader.cancel(error).catch(() => {
|
|
146
|
+
});
|
|
147
|
+
error && res.destroy(error);
|
|
148
|
+
}
|
|
149
|
+
async function write() {
|
|
150
|
+
try {
|
|
151
|
+
while (true) {
|
|
152
|
+
const { done, value } = await reader.read();
|
|
153
|
+
if (done) {
|
|
154
|
+
res.end();
|
|
155
|
+
return;
|
|
156
|
+
} else if (!res.write(value)) {
|
|
157
|
+
res.once("drain", write);
|
|
158
|
+
return;
|
|
159
|
+
} else if (res.flush) {
|
|
160
|
+
res.flush();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} catch (err) {
|
|
164
|
+
const error = err instanceof Error ? err : new Error("Error while writing to node response", {
|
|
165
|
+
cause: err
|
|
166
|
+
});
|
|
167
|
+
cancel(error);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/adapter/dev-server.ts
|
|
8
174
|
function createViteDevMiddleware(devServer, load, factory) {
|
|
9
175
|
let value;
|
|
10
176
|
let middleware;
|
|
@@ -79,20 +79,19 @@ function createMiddleware(router, options = {}) {
|
|
|
79
79
|
origin ?? (origin = getOrigin(req, protocol, host, trustProxy));
|
|
80
80
|
const url = new URL(req.url, origin);
|
|
81
81
|
const ip = req.ip || trustProxy && getForwardedHeader(req, "for") || req.socket.remoteAddress || "";
|
|
82
|
-
const
|
|
83
|
-
ip,
|
|
84
|
-
request: req,
|
|
85
|
-
response: res,
|
|
86
|
-
setCookie(cookie) {
|
|
87
|
-
res.appendHeader("set-cookie", cookie);
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
const context = {
|
|
82
|
+
const requestContext = {
|
|
91
83
|
method: req.method,
|
|
92
84
|
url,
|
|
93
|
-
platform
|
|
85
|
+
platform: {
|
|
86
|
+
ip,
|
|
87
|
+
request: req,
|
|
88
|
+
response: res,
|
|
89
|
+
setCookie(cookie) {
|
|
90
|
+
res.appendHeader("set-cookie", cookie);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
94
93
|
};
|
|
95
|
-
Object.defineProperty(
|
|
94
|
+
Object.defineProperty(requestContext, "request", {
|
|
96
95
|
get() {
|
|
97
96
|
const headers = req.headers;
|
|
98
97
|
const body = req.method === "GET" || req.method === "HEAD" ? void 0 : req.socket ? req : new ReadableStream({
|
|
@@ -118,7 +117,7 @@ function createMiddleware(router, options = {}) {
|
|
|
118
117
|
enumerable: true,
|
|
119
118
|
configurable: true
|
|
120
119
|
});
|
|
121
|
-
const response = await router(
|
|
120
|
+
const response = await router(requestContext);
|
|
122
121
|
if (!response) {
|
|
123
122
|
if (next) {
|
|
124
123
|
next();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Router } from "
|
|
1
|
+
import type { Router } from "../runtime";
|
|
2
2
|
import type { IncomingMessage, ServerResponse } from "http";
|
|
3
3
|
declare module "net" {
|
|
4
4
|
interface Socket {
|
|
@@ -14,10 +14,6 @@ declare module "http" {
|
|
|
14
14
|
appendHeader(key: string, value: string | string[]): this;
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
declare module "@marko/run" {
|
|
18
|
-
interface Platform extends NodePlatformInfo {
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
17
|
export interface NodePlatformInfo {
|
|
22
18
|
ip: string;
|
|
23
19
|
request: IncomingMessage;
|
|
@@ -48,20 +48,19 @@ function createMiddleware(router, options = {}) {
|
|
|
48
48
|
origin ?? (origin = getOrigin(req, protocol, host, trustProxy));
|
|
49
49
|
const url = new URL(req.url, origin);
|
|
50
50
|
const ip = req.ip || trustProxy && getForwardedHeader(req, "for") || req.socket.remoteAddress || "";
|
|
51
|
-
const
|
|
52
|
-
ip,
|
|
53
|
-
request: req,
|
|
54
|
-
response: res,
|
|
55
|
-
setCookie(cookie) {
|
|
56
|
-
res.appendHeader("set-cookie", cookie);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
const context = {
|
|
51
|
+
const requestContext = {
|
|
60
52
|
method: req.method,
|
|
61
53
|
url,
|
|
62
|
-
platform
|
|
54
|
+
platform: {
|
|
55
|
+
ip,
|
|
56
|
+
request: req,
|
|
57
|
+
response: res,
|
|
58
|
+
setCookie(cookie) {
|
|
59
|
+
res.appendHeader("set-cookie", cookie);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
63
62
|
};
|
|
64
|
-
Object.defineProperty(
|
|
63
|
+
Object.defineProperty(requestContext, "request", {
|
|
65
64
|
get() {
|
|
66
65
|
const headers = req.headers;
|
|
67
66
|
const body = req.method === "GET" || req.method === "HEAD" ? void 0 : req.socket ? req : new ReadableStream({
|
|
@@ -87,7 +86,7 @@ function createMiddleware(router, options = {}) {
|
|
|
87
86
|
enumerable: true,
|
|
88
87
|
configurable: true
|
|
89
88
|
});
|
|
90
|
-
const response = await router(
|
|
89
|
+
const response = await router(requestContext);
|
|
91
90
|
if (!response) {
|
|
92
91
|
if (next) {
|
|
93
92
|
next();
|
package/dist/cli/index.mjs
CHANGED
|
@@ -8,14 +8,18 @@ import { build as viteBuild, resolveConfig } from "vite";
|
|
|
8
8
|
import sade from "sade";
|
|
9
9
|
|
|
10
10
|
// src/vite/utils/config.ts
|
|
11
|
-
var
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
var PluginConfigKey = "__MARKO_RUN_PLUGIN_CONFIG__";
|
|
12
|
+
var AdapterConfigKey = "__MARKO_RUN_ADAPTER_CONFIG__";
|
|
13
|
+
function getConfig(obj, key) {
|
|
14
|
+
return obj[key];
|
|
14
15
|
}
|
|
15
|
-
function
|
|
16
|
-
|
|
17
|
-
return
|
|
16
|
+
function setConfig(obj, key, value) {
|
|
17
|
+
obj[key] = value;
|
|
18
|
+
return obj;
|
|
18
19
|
}
|
|
20
|
+
var getExternalPluginOptions = (viteConfig) => getConfig(viteConfig, PluginConfigKey);
|
|
21
|
+
var setExternalPluginOptions = (viteConfig, value) => setConfig(viteConfig, PluginConfigKey, value);
|
|
22
|
+
var setExternalAdapterOptions = (viteConfig, value) => setConfig(viteConfig, AdapterConfigKey, value);
|
|
19
23
|
|
|
20
24
|
// src/cli/index.ts
|
|
21
25
|
import { MemoryStore } from "@marko/vite";
|
|
@@ -91,18 +95,20 @@ function sleep(ms) {
|
|
|
91
95
|
var __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
92
96
|
var cwd = process.cwd();
|
|
93
97
|
var defaultPort = +process.env.PORT || 3e3;
|
|
94
|
-
var
|
|
95
|
-
|
|
98
|
+
var defaultConfigFileBases = ["serve.config", "vite.config"];
|
|
99
|
+
var defaultConfigFileExts = [".js", ".cjs", ".mjs", ".ts", ".mts"];
|
|
100
|
+
var prog = sade("marko-run").version("0.0.1").option("-c, --config", `Provide path to a Vite config file (by default looks for a file starting with ${defaultConfigFileBases.join(" or ")} with one of these extensions: ${defaultConfigFileExts.join(", ")})`).option("-e, --env", "Provide path to a dotenv file");
|
|
101
|
+
prog.command("serve [entry]", "", { default: true }).describe("Start a production-like server for already-built app files").option("-o, --output", "Directory to serve files from, and write asset files to if `--build` (default: )").option("-p, --port", "Port the server should listen on (defaults: `$PORT` env variable or 3000)").option("-f, --file", "Output file to start").action(async (entry, opts) => {
|
|
96
102
|
const config2 = await getViteConfig(cwd, opts.config);
|
|
97
103
|
await build(entry, config2, opts.output, false, opts.env);
|
|
98
104
|
await preview(opts.entry, config2, opts.port, opts.output, opts.env);
|
|
99
105
|
});
|
|
100
|
-
prog.command("dev [entry]").describe("Start
|
|
106
|
+
prog.command("dev [entry]").describe("Start development server in watch mode").option("-p, --port", "Port the dev server should listen on (defaults: 'preview.port' in config, or `$PORT` env variable, or 3000)").example("dev --config vite.config.js").action(async (entry, opts) => {
|
|
101
107
|
const cmd = opts._.length ? `${entry} ${opts._.join(" ")}` : entry ? `node ${entry}` : void 0;
|
|
102
108
|
const config2 = await getViteConfig(cwd, opts.config);
|
|
103
109
|
await dev(cmd, config2, opts.port, opts.env);
|
|
104
110
|
});
|
|
105
|
-
prog.command("build [entry]").describe("Build the application").option("-o, --output", "Directory to
|
|
111
|
+
prog.command("build [entry]").describe("Build the application (without serving it)").option("-o, --output", "Directory to write built files (default: )").option("--skip-client", "Skip the client-side build").example("build --config vite.config.js").action(async (entry, opts) => {
|
|
106
112
|
const config2 = await getViteConfig(cwd, opts.config);
|
|
107
113
|
await build(entry, config2, opts.ouput, opts["skip-client"], opts.env);
|
|
108
114
|
});
|
|
@@ -117,9 +123,9 @@ async function preview(entry, configFile, port, outDir, envFile) {
|
|
|
117
123
|
}
|
|
118
124
|
const adapter = await resolveAdapter(resolvedConfig);
|
|
119
125
|
if (!adapter) {
|
|
120
|
-
throw new Error("No adapter specified for serve command");
|
|
126
|
+
throw new Error("No adapter specified for 'serve' command");
|
|
121
127
|
} else if (!adapter.startPreview) {
|
|
122
|
-
throw new Error(`Adapter ${adapter.name} does not support serve command`);
|
|
128
|
+
throw new Error(`Adapter ${adapter.name} does not support 'serve' command`);
|
|
123
129
|
}
|
|
124
130
|
const dir = path.resolve(cwd, resolvedConfig.build.outDir);
|
|
125
131
|
const entryFile = entry ? path.join(dir, entry) : await findFileWithExt(dir, "index", [".mjs", ".js"]);
|
|
@@ -145,10 +151,10 @@ async function dev(cmd, configFile, port, envFile) {
|
|
|
145
151
|
const adapter = await resolveAdapter(resolvedConfig);
|
|
146
152
|
if (!adapter) {
|
|
147
153
|
throw new Error(
|
|
148
|
-
"No adapter specified for dev command without custom target"
|
|
154
|
+
"No adapter specified for 'dev' command without custom target"
|
|
149
155
|
);
|
|
150
156
|
} else if (!adapter.startDev) {
|
|
151
|
-
throw new Error(`Adapter ${adapter.name} does not support serve command`);
|
|
157
|
+
throw new Error(`Adapter '${adapter.name}' does not support 'serve' command`);
|
|
152
158
|
} else {
|
|
153
159
|
await adapter.startDev(configFile, port, envFile);
|
|
154
160
|
}
|
|
@@ -156,38 +162,39 @@ async function dev(cmd, configFile, port, envFile) {
|
|
|
156
162
|
}
|
|
157
163
|
async function build(entry, configFile, outDir, skipClient = false, envFile) {
|
|
158
164
|
var _a;
|
|
165
|
+
const resolvedConfig = await resolveConfig(
|
|
166
|
+
{ root: cwd, configFile },
|
|
167
|
+
"build"
|
|
168
|
+
);
|
|
169
|
+
const adapter = await resolveAdapter(resolvedConfig);
|
|
170
|
+
if (!adapter) {
|
|
171
|
+
throw new Error("No adapter specified for build command without entry");
|
|
172
|
+
}
|
|
159
173
|
if (!entry) {
|
|
160
|
-
const resolvedConfig = await resolveConfig(
|
|
161
|
-
{ root: cwd, configFile },
|
|
162
|
-
"build"
|
|
163
|
-
);
|
|
164
|
-
const adapter = await resolveAdapter(resolvedConfig);
|
|
165
|
-
if (!adapter) {
|
|
166
|
-
throw new Error("No adapter specified for build command without entry");
|
|
167
|
-
}
|
|
168
174
|
entry = await ((_a = adapter.getEntryFile) == null ? void 0 : _a.call(adapter));
|
|
169
175
|
if (!entry) {
|
|
170
176
|
throw new Error(
|
|
171
|
-
`Adapter ${adapter.name} does not support
|
|
177
|
+
`Adapter '${adapter.name}' does not support building without an entry`
|
|
172
178
|
);
|
|
173
179
|
}
|
|
174
180
|
}
|
|
175
181
|
if (envFile) {
|
|
176
182
|
envFile = path.resolve(cwd, envFile);
|
|
177
183
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
outDir
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
store: new MemoryStore()
|
|
184
|
+
let buildConfig = {
|
|
185
|
+
root: cwd,
|
|
186
|
+
configFile,
|
|
187
|
+
build: {
|
|
188
|
+
ssr: false,
|
|
189
|
+
outDir
|
|
189
190
|
}
|
|
190
|
-
|
|
191
|
+
};
|
|
192
|
+
buildConfig = setExternalPluginOptions(buildConfig, {
|
|
193
|
+
store: new MemoryStore()
|
|
194
|
+
});
|
|
195
|
+
buildConfig = setExternalAdapterOptions(buildConfig, {
|
|
196
|
+
envFile
|
|
197
|
+
});
|
|
191
198
|
await viteBuild({
|
|
192
199
|
...buildConfig,
|
|
193
200
|
build: {
|
|
@@ -211,7 +218,7 @@ async function build(entry, configFile, outDir, skipClient = false, envFile) {
|
|
|
211
218
|
});
|
|
212
219
|
}
|
|
213
220
|
}
|
|
214
|
-
function findFileWithExt(dir, base, extensions =
|
|
221
|
+
function findFileWithExt(dir, base, extensions = defaultConfigFileExts) {
|
|
215
222
|
for (const ext of extensions) {
|
|
216
223
|
const filePath = path.join(dir, base + ext);
|
|
217
224
|
if (fs2.existsSync(filePath)) {
|
|
@@ -220,10 +227,11 @@ function findFileWithExt(dir, base, extensions = [".js", ".cjs", ".mjs", ".ts",
|
|
|
220
227
|
}
|
|
221
228
|
return void 0;
|
|
222
229
|
}
|
|
223
|
-
async function getViteConfig(dir, configFile, bases =
|
|
230
|
+
async function getViteConfig(dir, configFile, bases = defaultConfigFileBases) {
|
|
224
231
|
if (configFile) {
|
|
225
|
-
|
|
226
|
-
|
|
232
|
+
const configFilePath = path.join(dir, configFile);
|
|
233
|
+
if (!fs2.existsSync(configFilePath)) {
|
|
234
|
+
throw new Error(`No config file found at '${configFilePath}'`);
|
|
227
235
|
}
|
|
228
236
|
return configFile;
|
|
229
237
|
}
|
|
@@ -236,9 +244,9 @@ async function getViteConfig(dir, configFile, bases = ["serve.config", "vite.con
|
|
|
236
244
|
return path.join(__dirname, "default.config.mjs");
|
|
237
245
|
}
|
|
238
246
|
async function resolveAdapter(config2) {
|
|
239
|
-
const options =
|
|
247
|
+
const options = getExternalPluginOptions(config2);
|
|
240
248
|
if (!options) {
|
|
241
|
-
throw new Error("Unable to resolve
|
|
249
|
+
throw new Error("Unable to resolve @marko/serve options");
|
|
242
250
|
}
|
|
243
251
|
return options.adapter;
|
|
244
252
|
}
|
package/dist/runtime/router.cjs
CHANGED
|
@@ -26,7 +26,9 @@ __export(router_exports, {
|
|
|
26
26
|
});
|
|
27
27
|
module.exports = __toCommonJS(router_exports);
|
|
28
28
|
function notImplemented() {
|
|
29
|
-
throw new Error(
|
|
29
|
+
throw new Error(
|
|
30
|
+
"This should have been replaced by the @marko/run plugin at build/dev time"
|
|
31
|
+
);
|
|
30
32
|
}
|
|
31
33
|
var router = notImplemented;
|
|
32
34
|
var matchRoute = notImplemented;
|
package/dist/runtime/router.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare const router:
|
|
1
|
+
import type { MatchRoute, RequestContext, Route } from "./types";
|
|
2
|
+
export declare const router: <T>(context: RequestContext<T>) => Promise<Response | void>;
|
|
3
3
|
export declare const matchRoute: MatchRoute;
|
|
4
|
-
export declare const invokeRoute:
|
|
4
|
+
export declare const invokeRoute: <T>(route: Route | null, context: RequestContext<T>) => Promise<Response | void>;
|
package/dist/runtime/router.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// src/runtime/router.ts
|
|
2
2
|
function notImplemented() {
|
|
3
|
-
throw new Error(
|
|
3
|
+
throw new Error(
|
|
4
|
+
"This should have been replaced by the @marko/run plugin at build/dev time"
|
|
5
|
+
);
|
|
4
6
|
}
|
|
5
7
|
var router = notImplemented;
|
|
6
8
|
var matchRoute = notImplemented;
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -5,11 +5,9 @@ declare type Combine<T> = T extends object ? {
|
|
|
5
5
|
} : T;
|
|
6
6
|
export interface RouteContextExtensions {
|
|
7
7
|
}
|
|
8
|
-
export interface Platform {
|
|
9
|
-
}
|
|
10
8
|
export declare type ParamsObject = Record<string, string>;
|
|
11
9
|
export declare type InputObject = Record<PropertyKey, any>;
|
|
12
|
-
export interface RequestContext<T =
|
|
10
|
+
export interface RequestContext<T = unknown> {
|
|
13
11
|
url: URL;
|
|
14
12
|
method: string;
|
|
15
13
|
request: Request;
|
|
@@ -32,6 +30,6 @@ export interface RouteWithHandler<Params extends ParamsObject = {}, Meta = unkno
|
|
|
32
30
|
handler: RouteHandler<this>;
|
|
33
31
|
}
|
|
34
32
|
export declare type MatchRoute = (method: string, pathname: string) => RouteWithHandler | null;
|
|
35
|
-
export declare type Router<T =
|
|
36
|
-
export declare type InvokeRoute<T =
|
|
33
|
+
export declare type Router<T = unknown> = (context: RequestContext<T>) => Promise<Response | void>;
|
|
34
|
+
export declare type InvokeRoute<T = unknown> = (route: Route | null, context: RequestContext<T>) => Promise<Response | void>;
|
|
37
35
|
export {};
|
package/dist/vite/index.cjs
CHANGED
|
@@ -1219,14 +1219,18 @@ function prettyPath(path3) {
|
|
|
1219
1219
|
}
|
|
1220
1220
|
|
|
1221
1221
|
// src/vite/utils/config.ts
|
|
1222
|
-
var
|
|
1223
|
-
|
|
1224
|
-
|
|
1222
|
+
var PluginConfigKey = "__MARKO_RUN_PLUGIN_CONFIG__";
|
|
1223
|
+
var AdapterConfigKey = "__MARKO_RUN_ADAPTER_CONFIG__";
|
|
1224
|
+
function getConfig(obj, key) {
|
|
1225
|
+
return obj[key];
|
|
1225
1226
|
}
|
|
1226
|
-
function
|
|
1227
|
-
|
|
1228
|
-
return
|
|
1227
|
+
function setConfig(obj, key, value) {
|
|
1228
|
+
obj[key] = value;
|
|
1229
|
+
return obj;
|
|
1229
1230
|
}
|
|
1231
|
+
var getExternalPluginOptions = (viteConfig) => getConfig(viteConfig, PluginConfigKey);
|
|
1232
|
+
var setExternalPluginOptions = (viteConfig, value) => setConfig(viteConfig, PluginConfigKey, value);
|
|
1233
|
+
var getExternalAdapterOptions = (viteConfig) => getConfig(viteConfig, AdapterConfigKey);
|
|
1230
1234
|
|
|
1231
1235
|
// src/vite/plugin.ts
|
|
1232
1236
|
var import_url = require("url");
|
|
@@ -1267,10 +1271,8 @@ function markoServe(opts = {}) {
|
|
|
1267
1271
|
"{.tsconfig*,tsconfig*.json}"
|
|
1268
1272
|
))) {
|
|
1269
1273
|
const filepath = import_path2.default.join(typesDir, "routes.d.ts");
|
|
1270
|
-
const
|
|
1271
|
-
|
|
1272
|
-
import_path2.default.relative(typesDir, routesDir)
|
|
1273
|
-
);
|
|
1274
|
+
const adapterTypeInfo = (adapter == null ? void 0 : adapter.writeTypeInfo) && await (adapter == null ? void 0 : adapter.writeTypeInfo());
|
|
1275
|
+
const data = renderRouteTypeInfo(routes, import_path2.default.relative(typesDir, routesDir)) + adapterTypeInfo;
|
|
1274
1276
|
if (data !== typesFile || !import_fs2.default.existsSync(filepath)) {
|
|
1275
1277
|
await ensureDir(typesDir);
|
|
1276
1278
|
await import_fs2.default.promises.writeFile(filepath, typesFile = data);
|
|
@@ -1335,13 +1337,19 @@ function markoServe(opts = {}) {
|
|
|
1335
1337
|
enforce: "pre",
|
|
1336
1338
|
async config(config2, env) {
|
|
1337
1339
|
var _a, _b, _c;
|
|
1338
|
-
const externalPluginOptions =
|
|
1340
|
+
const externalPluginOptions = getExternalPluginOptions(config2);
|
|
1339
1341
|
if (externalPluginOptions) {
|
|
1340
1342
|
opts = (0, import_vite.mergeConfig)(opts, externalPluginOptions);
|
|
1341
1343
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1344
|
+
if (adapter) {
|
|
1345
|
+
const externalAdapterConfig = getExternalAdapterOptions(config2);
|
|
1346
|
+
if (externalAdapterConfig && adapter.configure) {
|
|
1347
|
+
adapter.configure(externalAdapterConfig);
|
|
1348
|
+
}
|
|
1349
|
+
const adapterOptions = await ((_a = adapter.pluginOptions) == null ? void 0 : _a.call(adapter, opts));
|
|
1350
|
+
if (adapterOptions) {
|
|
1351
|
+
opts = (0, import_vite.mergeConfig)(opts, adapterOptions);
|
|
1352
|
+
}
|
|
1345
1353
|
}
|
|
1346
1354
|
root = (0, import_vite.normalizePath)(config2.root || process.cwd());
|
|
1347
1355
|
store = opts.store || new import_vite2.FileStore(
|
|
@@ -1365,7 +1373,7 @@ function markoServe(opts = {}) {
|
|
|
1365
1373
|
if (adapterConfig) {
|
|
1366
1374
|
pluginConfig = (0, import_vite.mergeConfig)(pluginConfig, adapterConfig);
|
|
1367
1375
|
}
|
|
1368
|
-
return
|
|
1376
|
+
return setExternalPluginOptions(pluginConfig, opts);
|
|
1369
1377
|
},
|
|
1370
1378
|
configResolved(config2) {
|
|
1371
1379
|
resolvedConfig = config2;
|
package/dist/vite/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { default } from "./plugin";
|
|
2
2
|
export { getAvailablePort, isPortInUse, loadEnv, parseEnv, spawnServer, } from "./utils/server";
|
|
3
3
|
export type { SpawnedServer } from "./utils/server";
|
|
4
|
-
export type { Adapter, Options, BuiltRoutes, HttpVerb, ParamInfo, Route, RoutableFile, RoutableFileType, } from "./types";
|
|
4
|
+
export type { Adapter, AdapterConfig, Options, BuiltRoutes, HttpVerb, ParamInfo, Route, RoutableFile, RoutableFileType, } from "./types";
|
package/dist/vite/index.js
CHANGED
|
@@ -1182,14 +1182,18 @@ function prettyPath(path3) {
|
|
|
1182
1182
|
}
|
|
1183
1183
|
|
|
1184
1184
|
// src/vite/utils/config.ts
|
|
1185
|
-
var
|
|
1186
|
-
|
|
1187
|
-
|
|
1185
|
+
var PluginConfigKey = "__MARKO_RUN_PLUGIN_CONFIG__";
|
|
1186
|
+
var AdapterConfigKey = "__MARKO_RUN_ADAPTER_CONFIG__";
|
|
1187
|
+
function getConfig(obj, key) {
|
|
1188
|
+
return obj[key];
|
|
1188
1189
|
}
|
|
1189
|
-
function
|
|
1190
|
-
|
|
1191
|
-
return
|
|
1190
|
+
function setConfig(obj, key, value) {
|
|
1191
|
+
obj[key] = value;
|
|
1192
|
+
return obj;
|
|
1192
1193
|
}
|
|
1194
|
+
var getExternalPluginOptions = (viteConfig) => getConfig(viteConfig, PluginConfigKey);
|
|
1195
|
+
var setExternalPluginOptions = (viteConfig, value) => setConfig(viteConfig, PluginConfigKey, value);
|
|
1196
|
+
var getExternalAdapterOptions = (viteConfig) => getConfig(viteConfig, AdapterConfigKey);
|
|
1193
1197
|
|
|
1194
1198
|
// src/vite/plugin.ts
|
|
1195
1199
|
import { fileURLToPath } from "url";
|
|
@@ -1229,10 +1233,8 @@ function markoServe(opts = {}) {
|
|
|
1229
1233
|
"{.tsconfig*,tsconfig*.json}"
|
|
1230
1234
|
))) {
|
|
1231
1235
|
const filepath = path2.join(typesDir, "routes.d.ts");
|
|
1232
|
-
const
|
|
1233
|
-
|
|
1234
|
-
path2.relative(typesDir, routesDir)
|
|
1235
|
-
);
|
|
1236
|
+
const adapterTypeInfo = (adapter == null ? void 0 : adapter.writeTypeInfo) && await (adapter == null ? void 0 : adapter.writeTypeInfo());
|
|
1237
|
+
const data = renderRouteTypeInfo(routes, path2.relative(typesDir, routesDir)) + adapterTypeInfo;
|
|
1236
1238
|
if (data !== typesFile || !fs2.existsSync(filepath)) {
|
|
1237
1239
|
await ensureDir(typesDir);
|
|
1238
1240
|
await fs2.promises.writeFile(filepath, typesFile = data);
|
|
@@ -1297,13 +1299,19 @@ function markoServe(opts = {}) {
|
|
|
1297
1299
|
enforce: "pre",
|
|
1298
1300
|
async config(config2, env) {
|
|
1299
1301
|
var _a, _b, _c;
|
|
1300
|
-
const externalPluginOptions =
|
|
1302
|
+
const externalPluginOptions = getExternalPluginOptions(config2);
|
|
1301
1303
|
if (externalPluginOptions) {
|
|
1302
1304
|
opts = mergeConfig(opts, externalPluginOptions);
|
|
1303
1305
|
}
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1306
|
+
if (adapter) {
|
|
1307
|
+
const externalAdapterConfig = getExternalAdapterOptions(config2);
|
|
1308
|
+
if (externalAdapterConfig && adapter.configure) {
|
|
1309
|
+
adapter.configure(externalAdapterConfig);
|
|
1310
|
+
}
|
|
1311
|
+
const adapterOptions = await ((_a = adapter.pluginOptions) == null ? void 0 : _a.call(adapter, opts));
|
|
1312
|
+
if (adapterOptions) {
|
|
1313
|
+
opts = mergeConfig(opts, adapterOptions);
|
|
1314
|
+
}
|
|
1307
1315
|
}
|
|
1308
1316
|
root = normalizePath(config2.root || process.cwd());
|
|
1309
1317
|
store = opts.store || new FileStore(
|
|
@@ -1327,7 +1335,7 @@ function markoServe(opts = {}) {
|
|
|
1327
1335
|
if (adapterConfig) {
|
|
1328
1336
|
pluginConfig = mergeConfig(pluginConfig, adapterConfig);
|
|
1329
1337
|
}
|
|
1330
|
-
return
|
|
1338
|
+
return setExternalPluginOptions(pluginConfig, opts);
|
|
1331
1339
|
},
|
|
1332
1340
|
configResolved(config2) {
|
|
1333
1341
|
resolvedConfig = config2;
|
package/dist/vite/types.d.ts
CHANGED
|
@@ -3,14 +3,19 @@ import type { Options as MarkoViteOptions } from "@marko/vite";
|
|
|
3
3
|
import type { ResolvedConfig, UserConfig } from "vite";
|
|
4
4
|
export type { RoutableFileType, HttpVerb };
|
|
5
5
|
export declare type StartServer = (port?: number) => Promise<void>;
|
|
6
|
+
export interface AdapterConfig {
|
|
7
|
+
[name: PropertyKey]: any;
|
|
8
|
+
}
|
|
6
9
|
export interface Adapter {
|
|
7
10
|
readonly name: string;
|
|
11
|
+
configure?(config: AdapterConfig): void;
|
|
8
12
|
pluginOptions?(options: Options): Promise<Options> | Options | undefined;
|
|
9
13
|
viteConfig?(config: UserConfig): Promise<UserConfig> | UserConfig | undefined;
|
|
10
14
|
getEntryFile?(): Promise<string> | string;
|
|
11
15
|
startDev?(configFile: string, port: number, envFile?: string): Promise<void> | void;
|
|
12
16
|
startPreview?(dir: string, entry?: string, port?: number, envFile?: string): Promise<void> | void;
|
|
13
17
|
buildEnd?(config: ResolvedConfig, routes: Route[], builtEntries: string[], sourceEntries: string[]): Promise<void> | void;
|
|
18
|
+
writeTypeInfo?(): Promise<string> | string;
|
|
14
19
|
}
|
|
15
20
|
export interface RouterOptions {
|
|
16
21
|
trailingSlashes: 'Ignore' | 'RedirectWithout' | 'RedirectWith' | 'RewriteWithout' | 'RewriteWith';
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import type { Options } from '../types';
|
|
2
|
-
export declare
|
|
3
|
-
export declare
|
|
1
|
+
import type { Options, AdapterConfig } from '../types';
|
|
2
|
+
export declare const getExternalPluginOptions: <T>(viteConfig: T) => Readonly<Options> | undefined;
|
|
3
|
+
export declare const setExternalPluginOptions: <T>(viteConfig: T, value: Options) => T;
|
|
4
|
+
export declare const getExternalAdapterOptions: <T>(viteConfig: T) => Readonly<AdapterConfig> | undefined;
|
|
5
|
+
export declare const setExternalAdapterOptions: <T>(viteConfig: T, value: AdapterConfig) => T;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { HttpVerb, Route } from "../types";
|
|
2
|
-
export declare function getVerbs(route: Route): ("get" | "
|
|
2
|
+
export declare function getVerbs(route: Route): ("get" | "delete" | "post" | "put")[];
|
|
3
3
|
export declare function hasVerb(route: Route, verb: HttpVerb): boolean | import("../types").RoutableFile | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marko/run",
|
|
3
|
-
"version": "0.0.1-
|
|
3
|
+
"version": "0.0.1-beta4",
|
|
4
4
|
"description": "File-based routing for Marko based on Vite",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"author": "Ryan Turnquist <rturnq@gmail.com>",
|
|
@@ -47,19 +47,19 @@
|
|
|
47
47
|
"typesVersions": {
|
|
48
48
|
"*": {
|
|
49
49
|
"*": [
|
|
50
|
-
"./
|
|
50
|
+
"./dist/runtime/index.d.ts"
|
|
51
51
|
],
|
|
52
52
|
"router": [
|
|
53
|
-
"./
|
|
53
|
+
"./dist/runtime/router.d.ts"
|
|
54
54
|
],
|
|
55
55
|
"vite": [
|
|
56
|
-
"./
|
|
56
|
+
"./dist/vite/index.d.ts"
|
|
57
57
|
],
|
|
58
58
|
"adapter/middleware": [
|
|
59
|
-
"./
|
|
59
|
+
"./dist/adapter/middleware.d.ts"
|
|
60
60
|
],
|
|
61
61
|
"adapter": [
|
|
62
|
-
"./
|
|
62
|
+
"./dist/adapter/index.d.ts"
|
|
63
63
|
]
|
|
64
64
|
}
|
|
65
65
|
},
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"acorn": "^8.8.0",
|
|
82
82
|
"cross-env": "^7.0.3",
|
|
83
83
|
"esbuild": "^0.15.7",
|
|
84
|
-
"marko": "^5.
|
|
84
|
+
"marko": "^5.22.4",
|
|
85
85
|
"mocha": "^10.0.0",
|
|
86
86
|
"mocha-snap": "^4.3.0",
|
|
87
87
|
"prettier": "^2.7.1",
|
package/dist/runtime/utils.d.ts
DELETED