@marko/run 0.0.1-beta1 → 0.0.1-beta10

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.
Files changed (45) hide show
  1. package/README.md +385 -131
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/adapter/default-entry.mjs +14 -5
  4. package/dist/adapter/dev-server.d.ts +1 -1
  5. package/dist/adapter/index.cjs +34 -10
  6. package/dist/adapter/index.d.ts +1 -0
  7. package/dist/adapter/index.js +34 -10
  8. package/dist/adapter/middleware.cjs +237 -0
  9. package/dist/adapter/middleware.d.ts +55 -0
  10. package/dist/adapter/middleware.js +209 -0
  11. package/dist/adapter/polyfill.d.ts +6 -0
  12. package/dist/cli/default.config.mjs +2 -2
  13. package/dist/cli/index.mjs +90 -59
  14. package/dist/runtime/index.cjs +0 -16
  15. package/dist/runtime/index.d.ts +21 -2
  16. package/dist/runtime/index.js +0 -7
  17. package/dist/runtime/internal.cjs +160 -0
  18. package/dist/runtime/internal.d.ts +11 -0
  19. package/dist/runtime/internal.js +126 -0
  20. package/dist/runtime/router.cjs +12 -10
  21. package/dist/runtime/router.d.ts +3 -4
  22. package/dist/runtime/router.js +9 -7
  23. package/dist/runtime/types.d.ts +39 -16
  24. package/dist/vite/codegen/index.d.ts +4 -3
  25. package/dist/vite/codegen/writer.d.ts +1 -1
  26. package/dist/vite/constants.d.ts +3 -2
  27. package/dist/vite/index.cjs +715 -312
  28. package/dist/vite/index.d.ts +4 -3
  29. package/dist/vite/index.js +712 -312
  30. package/dist/vite/types.d.ts +14 -7
  31. package/dist/vite/utils/config.d.ts +5 -3
  32. package/dist/vite/utils/route.d.ts +1 -1
  33. package/dist/vite/utils/server.d.ts +3 -1
  34. package/package.json +39 -17
  35. package/dist/adapter/server-old.d.ts +0 -3
  36. package/dist/adapter/server.d.ts +0 -6
  37. package/dist/adapters/node/index.d.ts +0 -5
  38. package/dist/adapters/node/server.d.ts +0 -3
  39. package/dist/adapters/static/crawler.d.ts +0 -9
  40. package/dist/adapters/static/default-entry.mjs +0 -1
  41. package/dist/adapters/static/index.cjs +0 -371
  42. package/dist/adapters/static/index.d.ts +0 -5
  43. package/dist/adapters/static/index.js +0 -341
  44. package/dist/adapters/static/server.d.ts +0 -3
  45. package/dist/runtime/request.d.ts +0 -4
package/README.md CHANGED
@@ -1,82 +1,88 @@
1
- <h1 align="center">
2
- <!-- Logo -->
3
- <img src="https://user-images.githubusercontent.com/4985201/115444712-ca550500-a1c9-11eb-9897-238ece59129c.png" height="118"/>
4
- <br/>
5
- @marko/run
6
- <br/>
7
-
8
- <!-- Language -->
9
- <a href="http://typescriptlang.org">
10
- <img src="https://img.shields.io/badge/%3C%2F%3E-typescript-blue.svg" alt="TypeScript"/>
11
- </a>
12
- </h1>
13
-
14
- Vite plugin for Marko with these features
15
- - Encapsulates [@marko/vite](https://github.com/marko-js/vite)
16
- - File-based routing with layouts and middleware
17
- - Efficient routing using a compiled static trie
18
- - Designed with web standards to run anywhere
19
-
20
- ## Intallation
21
1
 
22
- ```
2
+
3
+ <div align="center">
4
+ <picture>
5
+ <source media="(prefers-color-scheme: dark)" srcset="https://github.com/marko-js/run/raw/main/assets/marko-run-darkmode.png">
6
+ <source media="(prefers-color-scheme: light)" srcset="https://github.com/marko-js/run/raw/main/assets/marko-run.png">
7
+ <img alt="Marko Run Logo" src="https://github.com/marko-js/run/raw/main/assets/marko-run.png" width="400">
8
+ </picture>
9
+ <h4>Get your app up and <em>running</em> with <a href="https://markojs.com">Marko</a>!</h4>
10
+ </div>
11
+
12
+ ## Features
13
+ * 🚀 Fastest way to build a Marko app
14
+ * 💖 Scales from zero configuration
15
+ * ⚡️ Pages live-reload as you make changes
16
+ * 📁 Directory-based routes, layouts and middleware
17
+ * 🖌️ TypeScript powered editor support
18
+ * 🧬 [Designed with web standards](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern/URLPattern) to run anywhere
19
+
20
+ And when you build your production-ready app:
21
+
22
+ * 🔥 Blazing fast server-side rendering
23
+ * 🌊 Streams content to users ASAP
24
+ * 📦 Partial hydration & automatic code splitting
25
+ * 🚢 Deploy to multiple platforms
26
+
27
+ ## Installation
28
+
29
+ > **Warning**
30
+ > This project is in BETA - use at your own peril, but please do provide helpful feedback.
31
+
32
+
33
+ ```sh
23
34
  npm install @marko/run
24
35
  ```
25
36
 
26
- ## Vite Plugin
37
+ ### Getting Started / Zero Config
27
38
 
28
- This package provides both a Vite plugin and a runtime import. The Vite plugin is responsible for discovering your route files, generating the routing code and registering the @marko/vite plugin which builds your .marko files.
39
+ `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.
29
40
 
30
- ```ts
31
- // vite.config.ts
32
- import { defineConfig } from "vite";
33
- import marko from "@marko/run/vite"; // Import the Vite plugin
41
+ To get started from a template:
42
+ 1. `npm init marko -- -t basic`
43
+ 2. `cd ./<PROJECT_NAME>`
44
+ 4. `npm run dev`
34
45
 
35
- export default defineConfig({
36
- 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
- })
42
- ```
46
+ Or manually create a project:
47
+ 1. Install `@marko/run`
48
+ 2. Create file `src/routes/+page.marko`
49
+ 3. Run `npm exec marko-run`
43
50
 
44
- ## Runtime
51
+ Finally open `http://localhost:3000` 🚀
45
52
 
46
- Generally you'll want to use one of the adapters to provide a more convenient experience but this package provides the following runtime interface:
53
+ ### CLI
47
54
 
48
- ```ts
49
- import { router, getMatchedRoute } from '@marko/run`;
55
+ **`dev`** - Start development server in watch mode
56
+ ```bash
57
+ > npm exec marko-run
58
+ ```
59
+ or (with explicit sub command)
60
+ ```bash
61
+ > npm exec marko-run dev
50
62
  ```
51
63
 
52
- ### `router`
53
- ```ts
54
- (request: Request) => Promise<Response>;
64
+
65
+ **`build`** - Create a production build
66
+ ```bash
67
+ > npm exec marko-run build
55
68
  ```
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
69
 
58
- ### `getMatchedRoute`
59
- ```ts
60
- (method: string, url: URL) => {
61
- params: Record<string, string>;
62
- meta: unknown;
63
- invoke(request: Request): Promise<Response>;
64
- } | null;
70
+ **`preview`** - Create a production build and start preview server
71
+ ```bash
72
+ > npm exec marko-run preview
65
73
  ```
66
- This synchronous function takes a HTTP verb method, and URL and returns an object representing the best match or null if no match is found.
67
- - `params` - a key-value collection of any path parameters for the route
68
- - `meta` - meta data for the route
69
- - `invoke` - an asynchronouse function that takes a WHATWG request and returns the response generated by executing the matched route files. Note: unlike the top-level `router` function errors will not be caught.
70
74
 
71
75
  ## File-based Routing
72
76
 
73
- ## Nested Routing
77
+ <!-- ### Nested Routing
74
78
 
75
- *TODO: provide a quick overview*
79
+ *🎗 TODO: provide a quick overview* -->
76
80
 
77
81
  ### Routes Directory
78
82
 
79
- By default, the plugin will look for files in the configured _routes directory_. By default it will look in `./src/routes` (relative to the Vite config file) which can be configured:
83
+ The plugin looks for route files in the configured **routes directory**. By default, that’s `./src/routes`, relative to the Vite config file.
84
+
85
+ To change what directory routes are found in:
80
86
 
81
87
  ```ts
82
88
  // vite.config.ts
@@ -86,85 +92,104 @@ import marko from "@marko/run/vite";
86
92
  export default defineConfig({
87
93
  plugins: [marko({
88
94
  routesDir: 'src/pages' // Use `./src/pages` (relative to this file) as the routes directory
89
- })],
90
- //...
95
+ })]
91
96
  })
92
97
  ```
93
98
 
94
99
  ### Routeable Files
95
100
 
96
- In order to allow for co-location of files that should not be served (e.g. tests, stories, assets), the router only recognizes a specific set of file names. The following files will be discovered in any directory within your application's _routes directory_.
101
+ To allow for colocation of files that shouldn’t be served (like tests, assets, etc.), the router only recognizes certain filenames.
102
+
103
+ The following filenames will be discovered in any directory inside your application’s [routes directory](#routes-directory).
97
104
 
98
105
  #### `+page.marko`
99
- 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.
106
+
107
+ 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
108
 
101
109
  #### `+layout.marko`
102
- These files provide a layout component which will wrap all nested layouts and pages.
103
110
 
104
- <details>
105
- <summary>More Info</summary>
106
-
107
- Layouts are just like any other Marko component with no extra constraints. Each layout receives the request, path params, URL and route meta data as input as well as a renderBody which will be the subsequent layout or page to project.
108
-
109
- ```marko
110
- <div>
111
- <h1>My Products</h1>
112
- <main>
113
- ${input.renderBody} // project the page or layout here
114
- </main>
115
- </div>
116
- ```
117
- </details>
111
+ These files provide a **layout component**, which will wrap all nested layouts and pages.
112
+
113
+ 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.
114
+
115
+ ```marko
116
+ <main>
117
+ <h1>My Products</h1>
118
+
119
+ <${input.renderBody}/> // render the page or layout here
120
+ </main>
121
+ ```
118
122
 
119
123
  #### `+handler.*`
120
- These files establish a route at the current directory path which can handle requests for GET, POST, PUT and DELETE HTTP verbs. Typically these will be .js or .ts files depending on your project. Just like pages, only one handler may exist for any served path. A handler should export functions
124
+
125
+ 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? -->
126
+
127
+ 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
128
 
122
129
  <details>
123
130
  <summary>More Info</summary>
124
131
 
125
- - Valid exports are: `get`, `post`, `put`, `del`.
126
- - Each method takes a `context` and `next` argument and should return a WHATWG response either synchronously or asynchronously.
127
- - The `context` argument contains the WHATWG request object, path parameters, URL and route meta data.
128
- - The `next` argument will call the page for get requests where applicable or return a 204 response.
129
-
130
- ```ts
131
- export function post(context, next) {
132
+ - Valid exports are functions named `GET`, `POST`, `PUT`, or `DELETE`.
133
+ - Exports can be one of the following
134
+ - Handler function (see below)
135
+ - Array of handler functions - will be composed by calling them in order
136
+ - Promise that resolves to a handler function or array of handler functions
137
+ - Handler functions are synchronous or asynchronous functions that
138
+ - Receives a `context` and `next` argument,
139
+ - The `context` argument contains the WHATWG request object, path parameters, URL, and route metadata.
140
+ - The `next` argument will call the page for get requests where applicable or return a `204` response.
141
+ - 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.
142
+
143
+ ```js
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
- export function put(context, next) {
137
- return new Response('created', { status: 201 }); // handle the request
149
+ export function PUT(context, next) {
150
+ // `next` will be called for you by the runtime
138
151
  }
139
152
 
140
- export function get(context, next) {
141
- return next(); // Call the next handler
153
+ export async function GET(context, next) {
154
+ // do something before calling `next`
155
+ const response = await next();
156
+ // do something with the response from `next`
157
+ return response;
142
158
  }
143
159
 
144
- export function del(context, next) {
145
- return new Response('removed', { status: 204 });
160
+ export function DELETE(context, next) {
161
+ return new Response('Successfully removed', { status: 204 });
146
162
  }
147
163
  ```
148
164
  </details>
149
165
 
150
166
 
151
- #### `+middlware.*`
152
- These files are analagous to layouts for handlers. Middlware get called before handlers and let you perform arbitrary work before and after. Unlike handlers, middleware run for all HTTP verbs.
167
+ #### `+middleware.*`
168
+
169
+ These files are like layouts, but for handlers. Middleware get called before handlers and let you perform arbitrary work before and after.
170
+
171
+ > **Note**: Unlike handlers, middleware run for all HTTP methods.
153
172
 
154
173
  <details>
155
174
  <summary>More Info</summary>
156
175
 
157
- - Expects default export that takes a `context` and `next` argument and should return a WHATWG response either synchronously or asynchronously.
158
- - The `context` argument contains the WHATWG request object, path parameters, URL and route meta data.
159
- - The `next` argument will call the next middleware, handler or page as applicable for the route.
176
+ - Expects a `default` export that can be one of the following
177
+ - Handler function (see below)
178
+ - Array of handler functions - will be composed by calling them in order
179
+ - Promise that resolves to a handler function or array of handler functions
180
+ - Handler functions are synchronous or asynchronous functions that
181
+ - Receives a `context` and `next` argument,
182
+ - The `context` argument contains the WHATWG request object, path parameters, URL, and route metadata.
183
+ - The `next` argument will call the page for get requests where applicable or return a `204` response.
184
+ - 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.
160
185
 
161
186
  ```ts
162
187
  export default async function(context, next) {
163
- const requestName = `${ctx.request.method} ${ctx.url.href}`;
188
+ const requestName = `${context.request.method} ${context.url.href}`;
164
189
  let success = true;
165
190
  console.log(`${requestName} request started`)
166
191
  try {
167
- return await next(); // wait for subsequent middleware/handler/page
192
+ return await next(); // Wait for subsequent middleware, handler and page
168
193
  } catch (err) {
169
194
  success = false;
170
195
  throw err;
@@ -176,55 +201,80 @@ These files are analagous to layouts for handlers. Middlware get called before h
176
201
  </details>
177
202
 
178
203
  #### `+meta.*`
179
- These files represent some meta data to attach to the route. This meta data will be automatically imported by the router and provided on the the route context when invoking a route.
204
+
205
+ 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.
180
206
 
181
207
  ### Special Files
182
208
 
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 within the _routes directory_:
209
+ 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” -->
210
+
211
+ These special pages are subject to a root layout file (`pages/+layout.marko` in the default configuration).
184
212
 
185
213
  #### `+404.marko`
186
- This is a special page wich will be rendered for any request whose `Accept` header includes `text/html` if no other handler or page handled 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 `404` status code.
214
+
215
+ This special page responds to any request where:
216
+
217
+ - The `Accept` request header includes `text/html`
218
+ - *And* no other handler or page rendered the request
219
+
220
+ Responses with this page will have a `404` status code.
187
221
 
188
222
  #### `+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
223
 
224
+ This special page responds to any request where:
225
+
226
+ - The `Accept` request header includes `text/html`
227
+ - *And* an uncaught error occurs while serving the request
228
+
229
+ Responses with this page will have a `500` status code.
191
230
 
192
231
  ### Execution Order
193
232
 
194
- For a matched route the routable files execute in the following order
233
+ Given the following routes directory structure
234
+
235
+ <pre>
236
+ routes/
237
+ about/
238
+ +handler.js
239
+ +layout.marko
240
+ +middleware.js
241
+ +page.marko
242
+ +layout.marko
243
+ +middleware.js
244
+ +page.marko
245
+ </pre>
246
+
247
+ When the path `"/about"` is requested, the routable files execute in the following order:
248
+
195
249
  1. Middlewares from root-most to leaf-most
196
250
  2. Handler
197
251
  3. Layouts from root-most to leaf-most
198
252
  4. Page
199
253
 
200
- <details>
201
- <summary>Diagram</summary>
202
-
203
254
  ```mermaid
204
255
  sequenceDiagram
205
- participant Middleware1
206
- participant Middleware2
207
- participant Handler
208
- participant Layout1
209
- participant Layout2
210
- participant Page
211
- Note over Layout1,Page: Combined at build-time as a single component
212
- Middleware1->>Middleware2: next()
213
- Middleware2->>Handler: next()
214
- Handler->>Layout1: next()
215
- Layout1->Layout2: ${input.renderBody}
216
- Layout2->Page: ${input.renderBody}
217
- Layout1-->>Handler: Stream Response
218
- Handler-->>Middleware2: Response
219
- Middleware2-->>Middleware1: Response
256
+ participant MW1 as routes/+middleware.js
257
+ participant MW2 as routes/about/+middleware.js
258
+ participant H as routes/about/+handler.js
259
+ participant L1 as routes/+layout.marko
260
+ participant L2 as routes/about/+layout.marko
261
+ participant P as routes/about/+page.marko
262
+ Note over L1,P: Combined at build-time as a single component
263
+ MW1->>MW2: next()
264
+ MW2->>H: next()
265
+ H->>L1: next()
266
+ L1->L2: ${input.renderBody}
267
+ L2->P: ${input.renderBody}
268
+ L1-->>H: Stream Response
269
+ H-->>MW2: Response
270
+ MW2-->>MW1: Response
220
271
  ```
221
- </details>
222
272
 
223
273
  ### Path Structure
224
274
 
225
- Within the _routes directory_ the directory structure will determine the path the route will be served. There are four types of directory names:
275
+ 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
276
 
227
- - **Static directories** - The most common type of directory. Each static directory will contribute its name as a segment in the route's served path like a traditional file server. This is the default and unless the directory matches the requirements for one of the below types, it will be a static directory.
277
+ 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
278
 
229
279
  Examples:
230
280
  ```
@@ -233,16 +283,15 @@ Within the _routes directory_ the directory structure will determine the path th
233
283
  /projects
234
284
  ```
235
285
 
236
- - **Pathless directories** - These directories do not contribute their name to the route's served path. Directory names that start with an underscore ('_') as well as directories named 'index' will be a pathless directory.
286
+ 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.
237
287
 
238
288
  Examples:
239
289
  ```
240
290
  /_users
241
291
  /_public
242
- /index
243
292
  ```
244
293
 
245
- - **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. In the case the directory name is '$', the parameter will not exist at runtime but will be matched.
294
+ 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.
246
295
 
247
296
  Examples:
248
297
  ```
@@ -251,9 +300,9 @@ Within the _routes directory_ the directory structure will determine the path th
251
300
  /$
252
301
  ```
253
302
 
254
- - **Catcha-all directories** - These directories are similar to dynamic directories and introduce a dynamic parameter but instead of matching a single sement of the path, 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 name '$$', the parameter name at runtime will be '*'. Catch-all directories can be used to make not found/404 routes at any level including the root.
303
+ 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.
255
304
 
256
- Because catch-all directories always match and consume all the remaining path, you cannot have nested routes within and no further directories will be traversed.
305
+ 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
306
 
258
307
  Examples:
259
308
  ```
@@ -262,6 +311,211 @@ Within the _routes directory_ the directory structure will determine the path th
262
311
  /$$
263
312
  ```
264
313
 
265
- ### Match Ranking
314
+ <!-- ### Match Ranking
315
+
316
+ *TODO: Write some things* -->
317
+
318
+
319
+
320
+
321
+ ## Vite Plugin
322
+
323
+ This package’s Vite plugin discovers your route files, generates the routing code, and registers the `@marko/vite` plugin to compile your `.marko` files.
324
+
325
+ ```ts
326
+ // vite.config.ts
327
+ import { defineConfig } from "vite";
328
+ import marko from "@marko/run/vite"; // Import the Vite plugin
329
+
330
+ export default defineConfig({
331
+ plugins: [marko()] // Register the Vite plugin
332
+ })
333
+ ```
334
+
335
+ ### Adapters
336
+
337
+ 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.
338
+
339
+ Specify your adapter in the Vite config when registering the `@marko/run` plugin
340
+
341
+ ```ts
342
+ // vite.config.ts
343
+ import { defineConfig } from "vite";
344
+ import marko from "@marko/run/vite";
345
+ import netlify from "@marko/run-adapter-netlify" // Import the adapter
346
+
347
+ export default defineConfig({
348
+ plugins: [marko({
349
+ adapter: netlify({ edge: true }) // Configure and apply the adapter
350
+ })]
351
+ })
352
+ ```
353
+
354
+ #### Available Adapters
355
+
356
+ - [@marko/run-adapter-node](https://github.com/marko-js/run/blob/main/packages/adapters/node/README.md)
357
+ - [@marko/run-adapter-netlify](https://github.com/marko-js/run/blob/main/packages/adapters/netlify/README.md)
358
+ - [@marko/run-adapter-static](https://github.com/marko-js/run/blob/main/packages/adapters/static/README.md)
359
+
360
+ ## Runtime
361
+
362
+ Generally, when using an adapter, this runtime will be abstracted away.
363
+
364
+ <!-- TODO: Add examples -->
365
+ <!-- TODO: Split fetch and match + invoke in two sections and explain why you might use one or the other -->
366
+
367
+ ```ts
368
+ import * as Run from '@marko/run/router`;
369
+ ```
370
+
371
+ ### Emdedding in Existing Server
372
+
373
+
374
+
375
+ ### `Run.fetch`
376
+
377
+ ```ts
378
+ async function fetch<T>(request: Request, platform: T) => Promise<Response | void>;
379
+ ```
380
+
381
+
382
+
383
+ 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.
384
+
385
+ Express example:
386
+ ```ts
387
+ import express from "express";
388
+ import * as Run from "@marko/run/router";
389
+
390
+ express()
391
+ .use(async (req, res, next) => {
392
+ const request = // ...code to create a WHATWG Request from `req`
393
+
394
+ const response = await Run.fetch(request, {
395
+ req,
396
+ res
397
+ });
398
+
399
+ if (response) {
400
+ // ...code to apply response to `res`
401
+ } else {
402
+ next();
403
+ }
404
+ })
405
+ .listen(3000);
406
+ ```
407
+
408
+
409
+ ### Other APIs
410
+
411
+ 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
412
+
413
+ ### `Run.match`
414
+
415
+ ```ts
416
+ interface interface Route {
417
+ params: Record<string, string>;
418
+ meta: unknown;
419
+ }
420
+
421
+ function match(method: string, pathname: string) => Route | null;
422
+ ```
423
+
424
+ 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.
425
+
426
+ - `params` - a `{ key: value }` collection of any path parameters for the route
427
+ - `meta` - metadata for the route
428
+
429
+ ### `Run.invoke`
430
+
431
+ ```ts
432
+ async function invoke(route: Route, request: Request, platform: any) => Promise<Response | void>;
433
+ ```
434
+ 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.
435
+
436
+ Express example:
437
+ ```ts
438
+ import express from "express";
439
+ import * as Run from "@marko/run/router";
440
+
441
+ express()
442
+ .use((req, res) => {
443
+ const matchedRoute = Run.match(req.method, req.path);
444
+ if (matchedRoute) {
445
+ req.match = matchedRoute;
446
+ }
447
+ })
448
+
449
+ // ...other middleware
450
+
451
+ .use(async (req, res, next) => {
452
+ // Check if a route was previously matched
453
+ if (!req.match) {
454
+ next();
455
+ return;
456
+ }
457
+
458
+ const request = // ...code to create a WHATWG Request from `req`
459
+ const response = await Run.invoke(req.match, request, {
460
+ req,
461
+ res
462
+ });
463
+
464
+ if (response) {
465
+ // ...code to apply response to `res`
466
+ } else {
467
+ next();
468
+ }
469
+ })
470
+ .listen(3000);
471
+ ```
472
+
473
+
474
+
475
+ ## TypeScript
476
+
477
+
478
+ ### Global Namespace
479
+ marko/run provides a global namespace `MarkoRun` with the folling types:
480
+
481
+ **`MarkoRun.Handler`** - Type that represents a handler function to be exported by a +handler or +middleware file
482
+
483
+ **`MarkoRun.CurrentRoute`** - Type of the route's params and meta data
484
+
485
+ **`MarkoRun.CurrentContext`** - Type of the request context object in a handler and `out.global` in your Marko files
486
+
487
+
488
+ ### Generated Types
489
+ 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.
490
+ > **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).
491
+
492
+ These types are replaced with more specific versions per routeable file:
493
+
494
+ **`MarkoRun.Handler`**
495
+ - Overrides context with specific MarkoRun.CurrentContext
496
+
497
+ **`MarkoRun.CurrentRoute`**
498
+ - Adds specific parameters and meta types
499
+ - In middleware and layouts which are used in many routes, this type will be a union of all possible routes that file will see
500
+
501
+ **`MarkoRun.CurrentContext`**
502
+ - In middleware and layouts which are used in many routes, this type will be a union of all possible routes that file will see.
503
+ - When an adapter is used, it can provide types for the platform
504
+
505
+ ## Beta Roadmap
506
+
507
+ - Error handling
508
+ - Error component
509
+ - Redirect component
510
+
511
+ ## What about @marko/serve?
512
+
513
+ Once stable @marko/run will replace @marko/serve and improves upon that project in several critical ways.
514
+
515
+ 1. Special "route files" (eg `+page.marko`) improve the developer ergonomics quite substantially. While they may cause a double take initially, making these explicit allows colocating additional components, tests, stories, config, utilities and whatever else you need along side the page components. With `@marko/serve` it was far to easy to "accidentally" serve some of your test fixtures :see-no-evil:.
516
+ 2. @marko/serve was built around Webpack. Since Webpack doesn't have great support for SSR'd apps it meant that a lot of this work was up to us. This was not only a maintenance burden but also lead to some rough edges such as no HMR support (just full page reloading in dev). By switching to Vite with first class SSR support things all come together much more smoothly. And we're in good company (LINK TO OTHER META FRAMEWORKS ON VITE)!
517
+ 3. @marko/serve was primarily designed with a node target in mind. @marko/run instead supports an "adaptor" model, where you can author in web standard apis and build your application to run in Node, Deno, Netlify, Cloudflare and even a static site.
518
+ 4. The programatic api of `@marko/serve` left some to be desired which made it difficult to integrate into existing development servers and projects. Because of this public facing eBay applications (The largest consumer of Marko) we're note able to bring in `@marko/serve`. With `@marko/run` we've worked from the ground up to ensure a flexible enough programatic api to allow embedding in existing complex applications. Because of this we're confident that `@marko/run` will see much more use than `@marko/serve` and more investment from us!
519
+ 5. Built in layout management. Strictly speaking Marko does not need the concept of `+layout.marko` "route file". If you've used Marko before you know it's very easy to treat layouts as normal components. But by bringing these layouts into the router we're able to reduce the amount of JavaScript naively sent to the browser, reduce the amount of boilerplate, and prime ourselves for some plans we have post Marko 6 :eyes:.
266
520
 
267
- *TODO: Write some things*
521
+ There's more of course, but we're committed to make `@marko/run` _the best way_ to build a Marko application.