关于 marko-run
marko-run 是面向 Marko Run 项目的开发技能资料,介绍基于 Vite 的文件路由、流式 SSR、处理器、验证与部署,供处理 Marko 应用的 Agent 参考。
主要功能
- 01文件路由与布局
- 02流式 SSR 和数据加载
- 03类型化处理器与 URL 辅助工具
- 04请求验证与中间件
- 05Vite 配置与部署适配器
适用场景
- 01实现路由和嵌套布局
- 02添加经过验证的请求处理器
- 03在数据加载时保持流式响应
- 04配置构建和部署
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ svallory/ marko-ui --skill marko-run安装后在 Claude Code 中输入/marko-run确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/deploy.mdhandlers.mdrouting.md详细介绍
Marko Run
内容摘自 https://markojs.com/docs/marko-run。基于 Vite 的文件路由、流式 SSR、零配置。references/routing.md(文件、路径、扁平路由、执行顺序)、references/handlers.md(动词辅助函数、验证、数据加载、上下文、类型化 URL、嵌入)、references/deploy.md(Vite 插件、适配器、CLI)。
心智模型
src/routes/**— 只有以+为前缀的文件才可路由;其他所有文件都会被忽略(可自由地将组件和测试文件放在同一目录)。- 请求 → 中间件(从根到叶)→ 处理器 → 布局(从根到叶)→ 页面。布局和页面会编译为一个组件。
ctx(处理器/中间件)===$global(模板)。默认情况下,ctx.params、ctx.url会序列化到浏览器;可通过ctx.serializedGlobals添加其他内容。- 数据通过
next({ ... })向下传递 →ctx.data/$global.data(浅合并)。传递 Promise,在页面中用<await>等待它们——在调用next前等待会破坏流式传输。 Run是全局对象(无需导入):Run.GET/HEAD/POST/PUT/DELETE/PATCH/OPTIONS/QUERY/ALL、Run.href、类型Run.Context、Run.Platform。
快速参考
文件(路由下任意深度)
| 文件 | 作用 |
|---|---|
+page.marko | GET → HTML。每个路径一个。 |
+layout.marko | 包装嵌套布局/页面;input.content = 子项。$global = ctx。 |
+handler.ts | export const GET/POST/... = Run.X(...)。每个路径一个。返回 Response,或返回 undefined → 自动调用 next()。缺少 HEAD 时 → 使用 GET,但不含响应正文。 |
+middleware.ts | export default Run.ALL(...)(所有动词)或 Run.POST(...)(单个动词);在处理器之前运行;仅含选项的形式 Run.ALL({ search }) 会附加下文所述的验证。 |
+meta.json / +meta.ts | 静态 ctx.meta;动词键("POST": {...})会按方法进行浅覆盖。 |
+404.marko、+500.marko | 仅限根目录;当 Accept 包含 text/html 时使用;由根布局包装。 |
路径段
users 静态 · _admin 无路径(在 URL 中忽略,用于限定布局/中间件作用范围)· $id 参数(单独的 $ = 匹配,不捕获)· $$rest 捕获至路径末尾($$ 不捕获;其内部不能再嵌套内容)。
扁平路由:projects.$projectId.members+page.marko;目录也可使用这种形式(projects.$projectId/members+page.marko);并与嵌套目录合并。多路径 members,people+page.marko;分组 (members,people);可选项 (home,) 或 (home,_x)。用反引号转义控制字符:`sitemap.xml`/+handler.ts。
处理器与验证
export const POST = Run.POST({ json: NoteSchema }, async (ctx, next) => {
const [note, issues] = await ctx.body; // Standard Schema → [value, issues]; fn validator → its return
if (issues) return Response.json({ issues }, { status: 422 });
return ctx.redirect("/thanks"); // PRG on success; next({ issues }) to re-render form with errors
});
- 选项:
params、search(函数或同步 Standard Schema;惰性执行,首次访问时缓存)、json(validator、maxBytes1MiB)、form(validator、maxBytes、maxParts1000、maxFiles20、maxFileBytes1MiB、onFile(ctx, file);重复字段 → 数组)。表单:Run.POST(options, [auth, handler]),数组会组合执行。 ctx.body是一个 Promise(await ctx.body),惰性求值;未设置json/form选项时为undefined。不要调用ctx.request.json()——这会绕过验证/限制。- 选项会在中间件和处理器之间合并(最后一个验证器生效,对象进行浅合并)。
ctx方法:fetch(url, init)(内部重新请求,并设置parent)、render(template, input, init)、redirect(to, status=302)、back(fallback="/", status)。Run.href("/projects/$projectId/members", { params: { projectId: 42 }, search: {...}, hash })— 当.marko-run/routes.d.ts位于 tsconfig 的include中时,会根据实际路由进行类型检查;在客户端编译时重写。
类型
生成的 .marko-run/routes.d.ts(需要根目录下有 tsconfig;添加到 include)会针对每个文件缩窄 Run.Context(params、meta、search、body、上游 data)。全局扩展:declare module "@marko/run" { interface Context { db: DB } interface Platform { ... } } — 仅用于应用范围的属性;每个路由的数据应通过 next() 传递。
配置 / 部署
- 无需
vite.config。需要时:import marko from "@marko/run/vite"; plugins: [marko({ adapter, routesDir, trailingSlashes })](以及所有@marko/vite选项)。trailingSlashes:RedirectWithout(默认)|RedirectWith|RewriteWithout|RewriteWith|Ignore。 - 适配器解析顺序:选项 → 第一个名称匹配
@marko/run-adapter*/*marko-run-adapter*的依赖 → 内置 Node。软件包:@marko/run-adapter-node(Express 中间件:routerMiddleware()、matchMiddleware()→req.route.invoke)、@marko/run-adapter-static(urls: [...] | async fn,会抓取链接)、@marko/run-adapter-netlify({ edge: true }+netlify.toml中的functions/edge_functions = "dist",发布dist/public)。 - 嵌入:
import * as router from "@marko/run/router"→router.fetch(request, platform)、router.match(method, path)+router.invoke(route, request, platform)。 - CLI(
marko-run,通过bun run/bunx运行):dev(默认,-p)、build(-o)、preview(-o -p -f);都支持-c config、-e dotenv。
常见错误
- 在
GET中先等待数据再调用next()→ 整个响应都会被阻塞。应传递 promise。 - 成功的
POST中返回next()→ 刷新页面会重新提交,URL 无法分享。应改用重定向。 - 异步 Standard Schema 验证器会在运行时抛错——只能使用同步验证器。
ctx.body()——它是一个 promise,不是函数。- 始终返回自身
Response的处理程序不会渲染页面(其对应的 HTTP 方法会从页面的类型化上下文中移除)。 - 在
$$catch-all目录中嵌套路由文件——永远不会遍历这些文件。 - 不要期望客户端上有
$global.session——默认只会序列化params/url;请将其添加到ctx.serializedGlobals中(绝不要添加机密信息)。