@marko/run 0.0.1-beta8 → 0.1.0

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 CHANGED
@@ -1,226 +1,76 @@
1
- > **Warning**
2
- > This project is in BETA - use at your own peril, but please do provide helpful feedback.
1
+
3
2
 
4
3
  <div align="center">
5
- <!-- Logo -->
6
- <h1>
7
- <img alt="" src="https://user-images.githubusercontent.com/4985201/115444712-ca550500-a1c9-11eb-9897-238ece59129c.png" height="118"/>
8
- <br/>
9
- @marko/run
10
- </h1>
11
-
12
- <!-- Language -->
13
- <a href="https://www.typescriptlang.org">
14
- <img src="https://img.shields.io/badge/%3C%2F%3E-typescript-blue.svg" alt="TypeScript"/>
15
- </a>
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>
16
10
  </div>
17
11
 
18
- `@marko/run` will help you get up and *running* with [Marko](https://markojs.com)
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
19
 
20
- - Vite plugin that encapsulates [`@marko/vite`](https://github.com/marko-js/vite)
21
- - CLI to simplify build modes
22
- - File-based routing with layouts and middleware
23
- - Efficient routing using a compiled static trie
24
- - [Designed with web standards](https://developer.mozilla.org/en-US/docs/Web/API/URLPattern/URLPattern) to run anywhere
25
- - TypeScript support
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
26
 
27
27
  ## Installation
28
+ > **Warning**
29
+ > This project is in BETA - use at your own peril, but please do provide helpful feedback.
30
+
28
31
 
29
32
  ```sh
30
33
  npm install @marko/run
31
34
  ```
32
35
 
33
- ## CLI
34
-
35
- The package provides a command line tool `marko-run` which can be run using scripts in your package.json or with npx.
36
-
37
36
  ### Getting Started / Zero Config
38
37
 
38
+ `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.
39
39
 
40
+ To get started from a template:
41
+ 1. `npm init marko -- -t basic`
42
+ 2. `cd ./<PROJECT_NAME>`
43
+ 4. `npm run dev`
40
44
 
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:
45
+ Or manually create a project:
42
46
  1. Install `@marko/run`
43
47
  2. Create file `src/routes/+page.marko`
44
- 3. Run `npx marko-run`
45
- 4. Open browser to `http://localhost:3000`
46
-
47
- ### Commands
48
-
49
- **`dev`** - Start development server in watch mode
50
- ```bash
51
- > npx marko-run dev
52
- ```
53
- or (default command)
54
- ```bash
55
- > npx marko-run
56
- ```
57
-
58
-
59
- **`build`** - Create a production build
60
- ```bash
61
- > npx marko-run build
62
- ```
63
-
64
- **`serve`** - Create a production build and serve
65
- ```bash
66
- > npx marko-run serve
67
- ```
68
- ## Vite Plugin
69
-
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.
71
-
72
- ```ts
73
- // vite.config.ts
74
- import { defineConfig } from "vite";
75
- import marko from "@marko/run/vite"; // Import the Vite plugin
76
-
77
- export default defineConfig({
78
- plugins: [marko()] // Register the Vite plugin
79
- })
80
- ```
81
-
82
- ## Adapters
83
-
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)
108
-
109
- ## Runtime
110
-
111
- Generally, when using an adapter, this runtime will be abstracted away.
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
-
116
- ```ts
117
- import * as Run from '@marko/run/router`;
118
- ```
119
-
120
- ### Emdedding in Existing Server
121
-
122
-
123
-
124
- ### `Run.fetch`
125
-
126
- ```ts
127
- async function fetch<T>(request: Request, platform: T) => Promise<Response | void>;
128
- ```
129
-
130
-
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";
48
+ 3. Run `npm exec marko-run`
138
49
 
139
- express()
140
- .use(async (req, res, next) => {
141
- const request = // ...code to create a WHATWG Request from `req`
50
+ Finally open `http://localhost:3000` 🚀
142
51
 
143
- const response = await Run.fetch(request, {
144
- req,
145
- res
146
- });
52
+ ### CLI
147
53
 
148
- if (response) {
149
- // ...code to apply response to `res`
150
- } else {
151
- next();
152
- }
153
- })
154
- .listen(3000);
54
+ **`dev`** - Start a development server in watch mode
55
+ ```sh
56
+ > npm exec marko-run
155
57
  ```
156
-
157
-
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
161
-
162
- ### `Run.match`
163
-
164
- ```ts
165
- interface interface Route {
166
- params: Record<string, string>;
167
- meta: unknown;
168
- }
169
-
170
- function match(method: string, pathname: string) => Route | null;
58
+ or (with explicit sub command)
59
+ ```sh
60
+ > npm exec marko-run dev
171
61
  ```
172
62
 
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.
174
63
 
175
- - `params` - a `{ key: value }` collection of any path parameters for the route
176
- - `meta` - metadata for the route
177
-
178
- ### `Run.invoke`
179
-
180
- ```ts
181
- async function invoke<T>(route: Route, request: Request, platform: T) => Promise<Response | void>;
64
+ **`build`** - Create a production build
65
+ ```sh
66
+ > npm exec marko-run build
182
67
  ```
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
68
 
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);
69
+ **`preview`** - Create a production build and start the preview server
70
+ ```sh
71
+ > npm exec marko-run preview
220
72
  ```
221
73
 
222
-
223
-
224
74
  ## File-based Routing
225
75
 
226
76
  <!-- ### Nested Routing
@@ -247,25 +97,23 @@ export default defineConfig({
247
97
 
248
98
  ### Routeable Files
249
99
 
250
- To allow for colocation of files that shouldn’t be served (like tests, assets, etc.), the router only recognizes certain filenames.
251
-
252
- The following filenames will be discovered in any directory inside your application’s [routes directory](#routes-directory).
100
+ The router only recognizes certain filenames which are all prefixed with `+` ([Why?](#What-about-markoserve)). The following filenames will be discovered in any directory inside your application’s [routes directory](#routes-directory).
253
101
 
254
102
  #### `+page.marko`
255
103
 
256
- 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.
104
+ 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 exist for any served path.
257
105
 
258
106
  #### `+layout.marko`
259
107
 
260
108
  These files provide a **layout component**, which will wrap all nested layouts and pages.
261
109
 
262
- 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.
110
+ 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 refers to the nested page that is being rendered.
263
111
 
264
112
  ```marko
265
113
  <main>
266
114
  <h1>My Products</h1>
267
115
 
268
- ${input.renderBody} // render the page or layout here
116
+ <${input.renderBody}/> // render the page or layout here
269
117
  </main>
270
118
  ```
271
119
 
@@ -286,36 +134,36 @@ Typically, these will be `.js` or `.ts` files depending on your project. Like pa
286
134
  - Handler functions are synchronous or asynchronous functions that
287
135
  - Receives a `context` and `next` argument,
288
136
  - 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.
291
-
292
- ```js
293
- export function POST(context, next) {
294
- const { request, params, url, meta } = context;
295
- return new Response('Successfully updated', { status: 200 });
296
- }
297
-
298
- export function PUT(context, next) {
299
- // `next` will be called for you by the runtime
300
- }
301
-
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;
307
- }
308
-
309
- export function DELETE(context, next) {
310
- return new Response('Successfully removed', { status: 204 });
311
- }
312
- ```
137
+ - The `next` argument will call the page for `GET` requests where applicable or return a `204` response.
138
+ - Return a WHATWG response, throw a WHATWG response, and return undefined. If the function returns undefined the `next` argument with be automatically called and used as the response.
139
+
140
+ ```js
141
+ export function POST(context, next) {
142
+ const { request, params, url, meta } = context;
143
+ return new Response('Successfully updated', { status: 200 });
144
+ }
145
+
146
+ export function PUT(context, next) {
147
+ // `next` will be called for you by the runtime
148
+ }
149
+
150
+ export async function GET(context, next) {
151
+ // do something before calling `next`
152
+ const response = await next();
153
+ // do something with the response from `next`
154
+ return response;
155
+ }
156
+
157
+ export function DELETE(context, next) {
158
+ return new Response('Successfully removed', { status: 204 });
159
+ }
160
+ ```
313
161
  </details>
314
162
 
315
163
 
316
164
  #### `+middleware.*`
317
165
 
318
- These files are like layouts, but for handlers. Middleware get called before handlers and let you perform arbitrary work before and after.
166
+ These files are like layouts, but for handlers. Middleware files are called before handlers and let you perform arbitrary work before and after.
319
167
 
320
168
  > **Note**: Unlike handlers, middleware run for all HTTP methods.
321
169
 
@@ -329,35 +177,33 @@ These files are like layouts, but for handlers. Middleware get called before han
329
177
  - Handler functions are synchronous or asynchronous functions that
330
178
  - Receives a `context` and `next` argument,
331
179
  - 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.
334
-
335
- ```ts
336
- export default async function(context, next) {
337
- const requestName = `${context.request.method} ${context.url.href}`;
338
- let success = true;
339
- console.log(`${requestName} request started`)
340
- try {
341
- return await next(); // Wait for subsequent middleware, handler and page
342
- } catch (err) {
343
- success = false;
344
- throw err;
345
- } finally {
346
- console.log(`${requestName} completed ${success ? 'successfully' : 'with errors'}`);
347
- }
348
- }
349
- ```
180
+ - The `next` argument will call the page for `GET` requests where applicable or return a `204` response.
181
+ - Return a WHATWG response, throw a WHATWG response, and return undefined. If the function returns undefined the `next` argument with be automatically called and used as the response.
182
+
183
+ ```ts
184
+ export default async function(context, next) {
185
+ const requestName = `${context.request.method} ${context.url.href}`;
186
+ let success = true;
187
+ console.log(`${requestName} request started`)
188
+ try {
189
+ return await next(); // Wait for subsequent middleware, handler, and page
190
+ } catch (err) {
191
+ success = false;
192
+ throw err;
193
+ } finally {
194
+ console.log(`${requestName} completed ${success ? 'successfully' : 'with errors'}`);
195
+ }
196
+ }
197
+ ```
350
198
  </details>
351
199
 
352
200
  #### `+meta.*`
353
201
 
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
-
202
+ These files represent static metadata to attach to the route. This metadata will be automatically provided on the route `context` when invoking a route.
357
203
 
358
204
  ### Special Files
359
205
 
360
- 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” -->
206
+ In addition to the files above which can be defined in any directory under the [routes directory](#routes-directory), some special files can only be defined at its top level. <!-- 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” -->
361
207
 
362
208
  These special pages are subject to a root layout file (`pages/+layout.marko` in the default configuration).
363
209
 
@@ -395,7 +241,7 @@ routes/
395
241
  +page.marko
396
242
  </pre>
397
243
 
398
- When the path `"/about"` is requested, the routable files execute in the following order:
244
+ When the path `/about` is requested, the routable files execute in the following order:
399
245
 
400
246
  1. Middlewares from root-most to leaf-most
401
247
  2. Handler
@@ -423,82 +269,254 @@ sequenceDiagram
423
269
 
424
270
  ### Path Structure
425
271
 
426
- 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.
272
+ Within the [routes directory](#routes-directory), the directory structure determines the path from which the route is served. There are four types of directory names: **static**, **pathless**, **dynamic**, and **catch-all**.
427
273
 
428
- 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.
274
+ 1. **Static directories** - The most common type, and the default behavior. 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 is seen as a static directory.
429
275
 
430
- Examples:
431
- ```
432
- /foo
433
- /users
434
- /projects
435
- ```
276
+ Examples:
277
+ ```
278
+ /foo
279
+ /users
280
+ /projects
281
+ ```
436
282
 
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.
283
+ 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 ignored when parsing the route.
438
284
 
439
- Examples:
440
- ```
441
- /_users
442
- /_public
443
- ```
285
+ Examples:
286
+ ```
287
+ /_users
288
+ /_public
289
+ ```
444
290
 
445
291
  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.
446
292
 
447
- Examples:
448
- ```
449
- /$id
450
- /$name
451
- /$
452
- ```
293
+ Examples:
294
+ ```
295
+ /$id
296
+ /$name
297
+ /$
298
+ ```
453
299
 
454
300
  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.
455
301
 
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.
302
+ 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.
457
303
 
458
- Examples:
459
- ```
460
- /$$all
461
- /$$rest
462
- /$$
463
- ```
304
+ Examples:
305
+ ```
306
+ /$$all
307
+ /$$rest
308
+ /$$
309
+ ```
464
310
 
465
311
  <!-- ### Match Ranking
466
312
 
467
313
  *TODO: Write some things* -->
468
314
 
469
315
 
316
+
317
+
318
+ ## Vite Plugin
319
+
320
+ This package’s Vite plugin discovers your route files, generates the routing code, and registers the `@marko/vite` plugin to compile your `.marko` files.
321
+
322
+ ```ts
323
+ // vite.config.ts
324
+ import { defineConfig } from "vite";
325
+ import marko from "@marko/run/vite"; // Import the Vite plugin
326
+
327
+ export default defineConfig({
328
+ plugins: [marko()] // Register the Vite plugin
329
+ })
330
+ ```
331
+
332
+ ### Adapters
333
+
334
+ 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.
335
+
336
+ Specify your adapter in the Vite config when registering the `@marko/run` plugin
337
+
338
+ ```ts
339
+ // vite.config.ts
340
+ import { defineConfig } from "vite";
341
+ import marko from "@marko/run/vite";
342
+ import netlify from "@marko/run-adapter-netlify" // Import the adapter
343
+
344
+ export default defineConfig({
345
+ plugins: [marko({
346
+ adapter: netlify({ edge: true }) // Configure and apply the adapter
347
+ })]
348
+ })
349
+ ```
350
+
351
+ #### Available Adapters
352
+
353
+ - [@marko/run-adapter-node](https://github.com/marko-js/run/blob/main/packages/adapters/node/README.md)
354
+ - [@marko/run-adapter-netlify](https://github.com/marko-js/run/blob/main/packages/adapters/netlify/README.md)
355
+ - [@marko/run-adapter-static](https://github.com/marko-js/run/blob/main/packages/adapters/static/README.md)
356
+ ## Runtime
357
+
358
+ Generally, when using an adapter, this runtime will be abstracted away.
359
+
360
+ <!-- TODO: Add examples -->
361
+ <!-- TODO: Split fetch and match + invoke in two sections and explain why you might use one or the other -->
362
+
363
+ ```ts
364
+ import * as Run from '@marko/run/router`;
365
+ ```
366
+
367
+ ### Emdedding in Existing Server
368
+
369
+
370
+
371
+ ### `Run.fetch`
372
+
373
+ ```ts
374
+ async function fetch<T>(request: Request, platform: T) => Promise<Response | void>;
375
+ ```
376
+
377
+
378
+
379
+ This asynchronous function takes a [WHATWG `Request` object](https://fetch.spec.whatwg.org/#request-class) and an object containing any platform-specific data you may want access to, and returns any of
380
+
381
+ - a [WHATWG `Response` object](https://fetch.spec.whatwg.org/#response-class) (generated from executing any matched route files)
382
+ - `undefined` (if the request was not explicitly handled)
383
+ - a `404` status code response (if no route matches the requested path)
384
+ - a `500` status code response (if an error occurs)
385
+
386
+ Express example:
387
+ ```ts
388
+ import express from "express";
389
+ import * as Run from "@marko/run/router";
390
+
391
+ express()
392
+ .use(async (req, res, next) => {
393
+ const request = // ...code to create a WHATWG Request from `req`
394
+
395
+ const response = await Run.fetch(request, {
396
+ req,
397
+ res
398
+ });
399
+
400
+ if (response) {
401
+ // ...code to apply a response to `res`
402
+ } else {
403
+ next();
404
+ }
405
+ })
406
+ .listen(3000);
407
+ ```
408
+
409
+
410
+ ### Other APIs
411
+
412
+ In some cases, you might want more control over when route matching and invocation (creating a response) occur. For instance, you may have middleware in your server which needs to know if there is a matched route. The runtime provides these additional methods:
413
+
414
+ ### `Run.match`
415
+
416
+ ```ts
417
+ interface Route {
418
+ params: Record<string, string>;
419
+ meta: unknown;
420
+ }
421
+
422
+ function match(method: string, pathname: string) => Route | null;
423
+ ```
424
+
425
+ This synchronous function takes an HTTP method and path name and returns an object representing the best match, or `null` if no match is found.
426
+
427
+ - `params` - a `{ key: value }` collection of any path parameters for the route
428
+ - `meta` - metadata for the route
429
+
430
+ ### `Run.invoke`
431
+
432
+ ```ts
433
+ async function invoke(route: Route, request: Request, platform: any) => Promise<Response | void>;
434
+ ```
435
+ 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.
436
+
437
+ Express example:
438
+ ```ts
439
+ import express from "express";
440
+ import * as Run from "@marko/run/router";
441
+
442
+ express()
443
+ .use((req, res) => {
444
+ const matchedRoute = Run.match(req.method, req.path);
445
+ if (matchedRoute) {
446
+ req.match = matchedRoute;
447
+ }
448
+ })
449
+
450
+ // ...other middleware
451
+
452
+ .use(async (req, res, next) => {
453
+ // Check if a route was previously matched
454
+ if (!req.match) {
455
+ next();
456
+ return;
457
+ }
458
+
459
+ const request = // ...code to create a WHATWG Request from `req`
460
+ const response = await Run.invoke(req.match, request, {
461
+ req,
462
+ res
463
+ });
464
+
465
+ if (response) {
466
+ // ...code to apply a response to `res`
467
+ } else {
468
+ next();
469
+ }
470
+ })
471
+ .listen(3000);
472
+ ```
473
+
474
+
475
+
470
476
  ## TypeScript
471
477
 
472
478
 
473
479
  ### Global Namespace
474
- marko/run provides a global namespace `MarkoRun` with the folling types:
480
+ `marko/run` provides a global namespace `MarkoRun` with the following types:
475
481
 
476
482
  **`MarkoRun.Handler`** - Type that represents a handler function to be exported by a +handler or +middleware file
477
483
 
478
- **`MarkoRun.CurrentRoute`** - Type of the route's params and meta data
484
+ **`MarkoRun.CurrentRoute`** - Type of the route's params and metadata
479
485
 
480
486
  **`MarkoRun.CurrentContext`** - Type of the request context object in a handler and `out.global` in your Marko files
481
487
 
482
488
 
483
489
  ### 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).
490
+ 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.
491
+ > **Note** TypeScript will not include this file by default. You should use the [Marko VSCode plugin](https://marketplace.visualstudio.com/items?itemName=Marko-JS.marko-vscode) and [add it in your tsconfig](https://www.typescriptlang.org/tsconfig#include).
486
492
 
487
- These types are replaced with more specific versions per routeable file:
493
+ These types are replaced with more specific versions per routable file:
488
494
 
489
495
  **`MarkoRun.Handler`**
490
496
  - Overrides context with specific MarkoRun.CurrentContext
491
497
 
492
498
  **`MarkoRun.CurrentRoute`**
493
499
  - 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
500
+ - In middleware and layouts which are used in many routes, this type will be a union of all possible routes that the file will see
495
501
 
496
502
  **`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.
503
+ - In middleware and layouts which are used in many routes, this type will be a union of all possible routes that the file will see.
498
504
  - When an adapter is used, it can provide types for the platform
499
505
 
500
506
  ## Beta Roadmap
501
507
 
502
508
  - Error handling
503
509
  - Error component
504
- - Redirect component
510
+ - Redirect component
511
+
512
+ ## What about @marko/serve?
513
+
514
+ Once stable @marko/run will replace @marko/serve and improves upon that project in several critical ways.
515
+
516
+ 1. Special "route files" (e.g. `+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 alongside the page components. With `@marko/serve` it was far too easy to "accidentally" serve some of your test fixtures :see-no-evil:.
517
+ 2. @marko/serve was built around Webpack. Since Webpack doesn't have great support for SSR, 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 its first-class SSR support, things all come together much more smoothly. <!-- TODO: And we're in good company (LINK TO OTHER META FRAMEWORKS ON VITE)! -->
518
+ 3. @marko/serve was primarily designed with a node target in mind. @marko/run instead supports an "adapter" 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.
519
+ 4. The programmatic 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 applications at eBay (the largest consumer of Marko) were not able to bring in `@marko/serve`. With `@marko/run` we've worked from the ground up to ensure a flexible enough programmatic 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!
520
+ 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 for after Marko 6 is out :eyes:.
521
+
522
+ There's more of course, but we're committed to making `@marko/run` the _best way_ to build a Marko application.