@meekojs/meeko 0.0.3 → 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.
Files changed (188) hide show
  1. package/README.md +35 -16
  2. package/dist/autoimports-ts.d.ts +26 -0
  3. package/dist/autoimports-ts.js +183 -0
  4. package/dist/autoimports-ts.js.map +1 -0
  5. package/dist/autoimports.d.ts +21 -0
  6. package/dist/autoimports.js +301 -0
  7. package/dist/autoimports.js.map +1 -0
  8. package/dist/check.js +74 -33
  9. package/dist/check.js.map +1 -0
  10. package/dist/cli.js +67 -5
  11. package/dist/cli.js.map +1 -0
  12. package/dist/compiler/events.d.ts +2 -0
  13. package/dist/compiler/events.js +6 -0
  14. package/dist/compiler/events.js.map +1 -0
  15. package/dist/compiler/import-graph.d.ts +8 -0
  16. package/dist/compiler/import-graph.js +33 -0
  17. package/dist/compiler/import-graph.js.map +1 -0
  18. package/dist/compiler/mapping.js +1 -0
  19. package/dist/compiler/mapping.js.map +1 -0
  20. package/dist/compiler/project-root.d.ts +2 -0
  21. package/dist/compiler/project-root.js +18 -0
  22. package/dist/compiler/project-root.js.map +1 -0
  23. package/dist/compiler/props.d.ts +12 -0
  24. package/dist/compiler/props.js +32 -0
  25. package/dist/compiler/props.js.map +1 -0
  26. package/dist/compiler/sfc.js +1 -0
  27. package/dist/compiler/sfc.js.map +1 -0
  28. package/dist/compiler/sourcemap.d.ts +3 -0
  29. package/dist/compiler/sourcemap.js +75 -0
  30. package/dist/compiler/sourcemap.js.map +1 -0
  31. package/dist/compiler/template.js +1 -0
  32. package/dist/compiler/template.js.map +1 -0
  33. package/dist/compiler.js +66 -17
  34. package/dist/compiler.js.map +1 -0
  35. package/dist/component-errors.d.ts +18 -0
  36. package/dist/component-errors.js +65 -0
  37. package/dist/component-errors.js.map +1 -0
  38. package/dist/devtools-ui/index.js +1 -0
  39. package/dist/devtools-ui/index.js.map +1 -0
  40. package/dist/devtools.js +3 -4
  41. package/dist/devtools.js.map +1 -0
  42. package/dist/dom.d.ts +18 -5
  43. package/dist/dom.js +295 -90
  44. package/dist/dom.js.map +1 -0
  45. package/dist/error-observer.d.ts +6 -0
  46. package/dist/error-observer.js +41 -0
  47. package/dist/error-observer.js.map +1 -0
  48. package/dist/framework/check-app.d.ts +5 -0
  49. package/dist/framework/check-app.js +214 -0
  50. package/dist/framework/check-app.js.map +1 -0
  51. package/dist/framework/check-project.d.ts +4 -0
  52. package/dist/framework/check-project.js +42 -0
  53. package/dist/framework/check-project.js.map +1 -0
  54. package/dist/framework/client.d.ts +48 -0
  55. package/dist/framework/client.js +249 -0
  56. package/dist/framework/client.js.map +1 -0
  57. package/dist/framework/clients.d.ts +82 -0
  58. package/dist/framework/clients.js +190 -0
  59. package/dist/framework/clients.js.map +1 -0
  60. package/dist/framework/config-type.d.ts +10 -0
  61. package/dist/framework/config-type.js +4 -0
  62. package/dist/framework/config-type.js.map +1 -0
  63. package/dist/framework/config.d.ts +15 -0
  64. package/dist/framework/config.js +138 -0
  65. package/dist/framework/config.js.map +1 -0
  66. package/dist/framework/contracts-generate.d.ts +8 -0
  67. package/dist/framework/contracts-generate.js +290 -0
  68. package/dist/framework/contracts-generate.js.map +1 -0
  69. package/dist/framework/generate.d.ts +7 -0
  70. package/dist/framework/generate.js +67 -0
  71. package/dist/framework/generate.js.map +1 -0
  72. package/dist/framework/init.d.ts +2 -0
  73. package/dist/framework/init.js +183 -0
  74. package/dist/framework/init.js.map +1 -0
  75. package/dist/framework/middleware.d.ts +14 -0
  76. package/dist/framework/middleware.js +16 -0
  77. package/dist/framework/middleware.js.map +1 -0
  78. package/dist/framework/modules.d.ts +22 -0
  79. package/dist/framework/modules.js +29 -0
  80. package/dist/framework/modules.js.map +1 -0
  81. package/dist/framework/page-contracts-check.d.ts +8 -0
  82. package/dist/framework/page-contracts-check.js +110 -0
  83. package/dist/framework/page-contracts-check.js.map +1 -0
  84. package/dist/framework/page-contracts-generate.d.ts +2 -0
  85. package/dist/framework/page-contracts-generate.js +94 -0
  86. package/dist/framework/page-contracts-generate.js.map +1 -0
  87. package/dist/framework/page.d.ts +64 -0
  88. package/dist/framework/page.js +44 -0
  89. package/dist/framework/page.js.map +1 -0
  90. package/dist/framework/prerender.d.ts +14 -0
  91. package/dist/framework/prerender.js +110 -0
  92. package/dist/framework/prerender.js.map +1 -0
  93. package/dist/framework/project-templates.d.ts +3 -0
  94. package/dist/framework/project-templates.js +119 -0
  95. package/dist/framework/project-templates.js.map +1 -0
  96. package/dist/framework/project.d.ts +9 -0
  97. package/dist/framework/project.js +272 -0
  98. package/dist/framework/project.js.map +1 -0
  99. package/dist/framework/router.d.ts +41 -0
  100. package/dist/framework/router.js +259 -0
  101. package/dist/framework/router.js.map +1 -0
  102. package/dist/framework/server.d.ts +65 -0
  103. package/dist/framework/server.js +239 -0
  104. package/dist/framework/server.js.map +1 -0
  105. package/dist/framework/socket-contracts-generate.d.ts +5 -0
  106. package/dist/framework/socket-contracts-generate.js +162 -0
  107. package/dist/framework/socket-contracts-generate.js.map +1 -0
  108. package/dist/framework/socket.d.ts +72 -0
  109. package/dist/framework/socket.js +252 -0
  110. package/dist/framework/socket.js.map +1 -0
  111. package/dist/framework/standard-schema-types.d.ts +7 -0
  112. package/dist/framework/standard-schema-types.js +69 -0
  113. package/dist/framework/standard-schema-types.js.map +1 -0
  114. package/dist/framework/state.d.ts +22 -0
  115. package/dist/framework/state.js +136 -0
  116. package/dist/framework/state.js.map +1 -0
  117. package/dist/framework/styles.d.ts +2 -0
  118. package/dist/framework/styles.js +6 -0
  119. package/dist/framework/styles.js.map +1 -0
  120. package/dist/framework/type-declarations.d.ts +5 -0
  121. package/dist/framework/type-declarations.js +84 -0
  122. package/dist/framework/type-declarations.js.map +1 -0
  123. package/dist/framework-server/auto-clients.d.ts +3 -0
  124. package/dist/framework-server/auto-clients.js +27 -0
  125. package/dist/framework-server/auto-clients.js.map +1 -0
  126. package/dist/framework-server/context.d.ts +21 -0
  127. package/dist/framework-server/context.js +22 -0
  128. package/dist/framework-server/context.js.map +1 -0
  129. package/dist/framework-server/contracts.d.ts +73 -0
  130. package/dist/framework-server/contracts.js +184 -0
  131. package/dist/framework-server/contracts.js.map +1 -0
  132. package/dist/framework-server/discovery.d.ts +43 -0
  133. package/dist/framework-server/discovery.js +257 -0
  134. package/dist/framework-server/discovery.js.map +1 -0
  135. package/dist/framework-server/local.d.ts +21 -0
  136. package/dist/framework-server/local.js +100 -0
  137. package/dist/framework-server/local.js.map +1 -0
  138. package/dist/framework-server/middleware.d.ts +4 -0
  139. package/dist/framework-server/middleware.js +56 -0
  140. package/dist/framework-server/middleware.js.map +1 -0
  141. package/dist/framework-server/node-upgrade.d.ts +23 -0
  142. package/dist/framework-server/node-upgrade.js +96 -0
  143. package/dist/framework-server/node-upgrade.js.map +1 -0
  144. package/dist/framework-server/node-websocket.d.ts +16 -0
  145. package/dist/framework-server/node-websocket.js +127 -0
  146. package/dist/framework-server/node-websocket.js.map +1 -0
  147. package/dist/framework-server/openapi.d.ts +17 -0
  148. package/dist/framework-server/openapi.js +118 -0
  149. package/dist/framework-server/openapi.js.map +1 -0
  150. package/dist/framework-server/session-stores.d.ts +73 -0
  151. package/dist/framework-server/session-stores.js +122 -0
  152. package/dist/framework-server/session-stores.js.map +1 -0
  153. package/dist/framework-server/sessions.d.ts +70 -0
  154. package/dist/framework-server/sessions.js +167 -0
  155. package/dist/framework-server/sessions.js.map +1 -0
  156. package/dist/framework-server/socket.d.ts +83 -0
  157. package/dist/framework-server/socket.js +196 -0
  158. package/dist/framework-server/socket.js.map +1 -0
  159. package/dist/framework-server.d.ts +23 -0
  160. package/dist/framework-server.js +13 -0
  161. package/dist/framework-server.js.map +1 -0
  162. package/dist/framework.d.ts +31 -0
  163. package/dist/framework.js +14 -0
  164. package/dist/framework.js.map +1 -0
  165. package/dist/hmr.js +75 -24
  166. package/dist/hmr.js.map +1 -0
  167. package/dist/index.d.ts +5 -3
  168. package/dist/index.js +3 -1
  169. package/dist/index.js.map +1 -0
  170. package/dist/init.js +31 -3
  171. package/dist/init.js.map +1 -0
  172. package/dist/internal.d.ts +2 -2
  173. package/dist/internal.js +3 -2
  174. package/dist/internal.js.map +1 -0
  175. package/dist/node.d.ts +16 -0
  176. package/dist/node.js +122 -0
  177. package/dist/node.js.map +1 -0
  178. package/dist/reactivity.d.ts +22 -1
  179. package/dist/reactivity.js +132 -18
  180. package/dist/reactivity.js.map +1 -0
  181. package/dist/server.js +1 -0
  182. package/dist/server.js.map +1 -0
  183. package/dist/types.d.ts +2 -0
  184. package/dist/types.js +26 -9
  185. package/dist/types.js.map +1 -0
  186. package/dist/vite.js +112 -7
  187. package/dist/vite.js.map +1 -0
  188. package/package.json +15 -4
@@ -0,0 +1,67 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join, relative, resolve } from "node:path";
3
+ import { discoverConfiguredRoutes } from "./config.js";
4
+ function paramsFor(id) {
5
+ const params = [];
6
+ for (const part of id.split("/").filter(Boolean)) {
7
+ const match = /^\[(?:\.\.\.)?([A-Za-z_$][\w$]*)\]$/.exec(part);
8
+ if (!match)
9
+ continue;
10
+ const name = match[1];
11
+ if (params.includes(name))
12
+ throw new Error(`Duplicate route parameter ${name} in ${id}`);
13
+ params.push(name);
14
+ }
15
+ return params;
16
+ }
17
+ function portable(root, file) {
18
+ const path = relative(root, file).replaceAll("\\", "/");
19
+ if (path.startsWith("../") || path === "..")
20
+ throw new Error(`Route file escapes project root: ${file}`);
21
+ return path;
22
+ }
23
+ /** Generates public route types and a portable manifest from one route discovery pass. */
24
+ export async function generateRoutes(projectRoot) {
25
+ const root = resolve(projectRoot);
26
+ const routes = await discoverConfiguredRoutes(root);
27
+ const types = join(root, ".meeko", "types", "routes.d.ts");
28
+ const manifest = join(root, ".meeko", "manifests", "routes.json");
29
+ const virtualTypes = join(root, ".meeko", "types", "virtual-pages.d.ts");
30
+ const lines = routes.map((route) => {
31
+ const params = paramsFor(route.id);
32
+ const shape = params.length ? `{ ${params.map((param) => `${JSON.stringify(param)}: string`).join("; ")} }` : "Record<never, never>";
33
+ return ` ${JSON.stringify(route.id)}: ${shape};`;
34
+ });
35
+ const declarations = [
36
+ "// Generated by Meeko. Edit app/pages instead.",
37
+ 'import "@meekojs/meeko/framework";',
38
+ 'declare module "@meekojs/meeko/framework" {',
39
+ " interface GeneratedRoutes {",
40
+ ...lines,
41
+ " }",
42
+ "}",
43
+ `export type RouteId = ${routes.length ? routes.map((route) => JSON.stringify(route.id)).join(" | ") : "never"};`,
44
+ 'export type RouteParams<Id extends RouteId> = import("@meekojs/meeko/framework").GeneratedRoutes[Id];',
45
+ "export {};",
46
+ "",
47
+ ].join("\n");
48
+ const portableRoutes = routes.map((route) => ({
49
+ id: route.id,
50
+ file: portable(root, route.file),
51
+ layouts: route.layouts.map((layout) => portable(root, layout)),
52
+ ...(route.error ? { error: portable(root, route.error) } : {}),
53
+ ...(route.sidecar ? { sidecar: portable(root, route.sidecar) } : {}),
54
+ }));
55
+ await mkdir(join(root, ".meeko", "types"), { recursive: true });
56
+ await mkdir(join(root, ".meeko", "manifests"), { recursive: true });
57
+ await writeFile(types, declarations, "utf8");
58
+ await writeFile(virtualTypes, [
59
+ 'declare module "virtual:meeko-client-pages" { export const modules: Record<string, () => Promise<unknown>>; }',
60
+ 'declare module "virtual:meeko-server-pages" { export const modules: Record<string, unknown>; }',
61
+ 'declare module "virtual:meeko-server-routes" { export const modules: Record<string, unknown>; }',
62
+ "",
63
+ ].join("\n"), "utf8");
64
+ await writeFile(manifest, `${JSON.stringify({ version: 1, routes: portableRoutes }, null, 2)}\n`, "utf8");
65
+ return { types, manifest, count: routes.length };
66
+ }
67
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/framework/generate.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { join, relative, resolve } from \"node:path\";\nimport { discoverConfiguredRoutes } from \"./config.js\";\n\nexport interface GeneratedRouteFiles {\n readonly types: string;\n readonly manifest: string;\n readonly count: number;\n}\n\nfunction paramsFor(id: string): readonly string[] {\n const params: string[] = [];\n for (const part of id.split(\"/\").filter(Boolean)) {\n const match = /^\\[(?:\\.\\.\\.)?([A-Za-z_$][\\w$]*)\\]$/.exec(part);\n if (!match) continue;\n const name = match[1]!;\n if (params.includes(name)) throw new Error(`Duplicate route parameter ${name} in ${id}`);\n params.push(name);\n }\n return params;\n}\n\nfunction portable(root: string, file: string): string {\n const path = relative(root, file).replaceAll(\"\\\\\", \"/\");\n if (path.startsWith(\"../\") || path === \"..\") throw new Error(`Route file escapes project root: ${file}`);\n return path;\n}\n\n/** Generates public route types and a portable manifest from one route discovery pass. */\nexport async function generateRoutes(projectRoot: string): Promise<GeneratedRouteFiles> {\n const root = resolve(projectRoot);\n const routes = await discoverConfiguredRoutes(root);\n const types = join(root, \".meeko\", \"types\", \"routes.d.ts\");\n const manifest = join(root, \".meeko\", \"manifests\", \"routes.json\");\n const virtualTypes = join(root, \".meeko\", \"types\", \"virtual-pages.d.ts\");\n const lines = routes.map((route) => {\n const params = paramsFor(route.id);\n const shape = params.length ? `{ ${params.map((param) => `${JSON.stringify(param)}: string`).join(\"; \")} }` : \"Record<never, never>\";\n return ` ${JSON.stringify(route.id)}: ${shape};`;\n });\n const declarations = [\n \"// Generated by Meeko. Edit app/pages instead.\",\n 'import \"@meekojs/meeko/framework\";',\n 'declare module \"@meekojs/meeko/framework\" {',\n \" interface GeneratedRoutes {\",\n ...lines,\n \" }\",\n \"}\",\n `export type RouteId = ${routes.length ? routes.map((route) => JSON.stringify(route.id)).join(\" | \") : \"never\"};`,\n 'export type RouteParams<Id extends RouteId> = import(\"@meekojs/meeko/framework\").GeneratedRoutes[Id];',\n \"export {};\",\n \"\",\n ].join(\"\\n\");\n const portableRoutes = routes.map((route) => ({\n id: route.id,\n file: portable(root, route.file),\n layouts: route.layouts.map((layout) => portable(root, layout)),\n ...(route.error ? { error: portable(root, route.error) } : {}),\n ...(route.sidecar ? { sidecar: portable(root, route.sidecar) } : {}),\n }));\n await mkdir(join(root, \".meeko\", \"types\"), { recursive: true });\n await mkdir(join(root, \".meeko\", \"manifests\"), { recursive: true });\n await writeFile(types, declarations, \"utf8\");\n await writeFile(virtualTypes, [\n 'declare module \"virtual:meeko-client-pages\" { export const modules: Record<string, () => Promise<unknown>>; }',\n 'declare module \"virtual:meeko-server-pages\" { export const modules: Record<string, unknown>; }',\n 'declare module \"virtual:meeko-server-routes\" { export const modules: Record<string, unknown>; }',\n \"\",\n ].join(\"\\n\"), \"utf8\");\n await writeFile(manifest, `${JSON.stringify({ version: 1, routes: portableRoutes }, null, 2)}\\n`, \"utf8\");\n return { types, manifest, count: routes.length };\n}\n"]}
@@ -0,0 +1,2 @@
1
+ /** Creates a framework project without overwriting unrelated user files. */
2
+ export declare function initFrameworkProject(directory: string): Promise<string>;
@@ -0,0 +1,183 @@
1
+ import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
2
+ import { basename, dirname, join, resolve } from "node:path";
3
+ const allowedEntries = new Set(["package.json", "package-lock.json", "pnpm-lock.yaml", "pnpm-workspace.yaml", "yarn.lock", "node_modules", ".npmrc", ".git"]);
4
+ const files = {
5
+ ".gitignore": "node_modules/\n.meeko/\n",
6
+ "public/.gitkeep": "",
7
+ "app/styles/main.css": `:root { color-scheme: light dark; font-family: system-ui, sans-serif; }
8
+ * { box-sizing: border-box; }
9
+ body { margin: 0; }
10
+ `,
11
+ "meeko.config.ts": `import { defineMeekoConfig } from "@meekojs/meeko/framework";
12
+
13
+ export default defineMeekoConfig({
14
+ extends: [],
15
+ modules: [],
16
+ imports: { serverDirs: [] },
17
+ });
18
+ `,
19
+ "tsconfig.json": `{
20
+ "compilerOptions": {
21
+ "target": "ES2022",
22
+ "module": "NodeNext",
23
+ "moduleResolution": "NodeNext",
24
+ "lib": ["ES2022", "DOM"],
25
+ "types": ["node", "vite/client"],
26
+ "strict": true,
27
+ "noEmit": true,
28
+ "allowArbitraryExtensions": true,
29
+ "skipLibCheck": true
30
+ },
31
+ "files": [],
32
+ "references": [
33
+ { "path": "./.meeko/tsconfig.app.json" },
34
+ { "path": "./.meeko/tsconfig.server.json" },
35
+ { "path": "./.meeko/tsconfig.shared.json" }
36
+ ]
37
+ }
38
+ `,
39
+ "app/pages/index.meeko": `<script lang="ts">
40
+ import { signal } from "@meekojs/meeko";
41
+
42
+ export interface Props {
43
+ params: Record<string, string>;
44
+ search: Record<string, string | string[]>;
45
+ data?: unknown;
46
+ }
47
+
48
+ const count = signal(0);
49
+ </script>
50
+
51
+ <template>
52
+ <section>
53
+ <h1>Welcome to Meeko</h1>
54
+ <p>File routes, server rendering, and hydration are ready.</p>
55
+ <button type="button" on:click="() => count.value++">Count: {count.value}</button>
56
+ </section>
57
+ </template>
58
+ `,
59
+ "app/pages/index.route.ts": `import { definePage } from "@meekojs/meeko/framework";
60
+
61
+ export const route = definePage({ render: "prerender" });
62
+ `,
63
+ "app/app.meeko": `<script lang="ts">
64
+ import type { Component } from "@meekojs/meeko";
65
+
66
+ export interface Props {
67
+ outlet: Component<Record<string, unknown>>;
68
+ }
69
+
70
+ const props = defineProps<Props>();
71
+ </script>
72
+
73
+ <template>
74
+ <div class="site-root"><component is="props.outlet"></component></div>
75
+ </template>
76
+ `,
77
+ "app/layouts/default.meeko": `<script lang="ts">
78
+ import type { Component } from "@meekojs/meeko";
79
+
80
+ export interface Props {
81
+ outlet: Component<Record<string, unknown>>;
82
+ }
83
+
84
+ const props = defineProps<Props>();
85
+ </script>
86
+
87
+ <template>
88
+ <div class="app-shell">
89
+ <header><a href="/">Meeko</a></header>
90
+ <main><component is="props.outlet"></component></main>
91
+ </div>
92
+ </template>
93
+
94
+ <style scoped>
95
+ .app-shell { max-width: 64rem; margin: 0 auto; padding: 2rem; font-family: system-ui, sans-serif; }
96
+ header { margin-bottom: 2rem; }
97
+ </style>
98
+ `,
99
+ "server/session.ts": `import { MemorySessionStore, SessionManager } from "@meekojs/meeko/server";
100
+
101
+ export function createSessionManager(production: boolean) {
102
+ if (production) throw new Error("Configure a persistent SessionStore in server/session.ts before production startup");
103
+ return new SessionManager({ store: new MemorySessionStore(), production: false });
104
+ }
105
+ `,
106
+ "README.md": `# Meeko framework starter
107
+
108
+ Requires Node 24.
109
+
110
+ \`\`\`sh
111
+ npm install
112
+ npm run dev
113
+ \`\`\`
114
+
115
+ Routes live in \`app/pages\`. \`app/layouts/default.meeko\` keeps the shared shell during client navigation. Put shared route guards in \`app/middleware\` with \`defineRouteMiddleware\`, and Node request middleware in \`server/middleware\` with \`defineServerMiddleware\`; see the framework guide for their execution and security boundaries. Global styles in \`app/styles/main.css\` load automatically; component styles stay in \`<style>\` blocks. Run \`npm run check\` for types, then \`npm run build\` and \`npm start\` for production SSR. Generated files live under \`.meeko/\`.
116
+ `,
117
+ };
118
+ function record(value) {
119
+ return typeof value === "object" && value !== null && !Array.isArray(value);
120
+ }
121
+ /** Creates a framework project without overwriting unrelated user files. */
122
+ export async function initFrameworkProject(directory) {
123
+ const target = resolve(directory);
124
+ const name = basename(target).toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[._-]+|[._-]+$/g, "") || "meeko-app";
125
+ const metadata = JSON.parse(await readFile(new URL("../../package.json", import.meta.url), "utf8"));
126
+ if (!record(metadata) || typeof metadata.version !== "string")
127
+ throw new Error("Meeko package version is unavailable");
128
+ let entries = [];
129
+ try {
130
+ if (!(await stat(target)).isDirectory())
131
+ throw new Error(`Destination is not a directory: ${target}`);
132
+ entries = await readdir(target);
133
+ }
134
+ catch (error) {
135
+ if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
136
+ throw error;
137
+ }
138
+ const unexpected = entries.filter((entry) => !allowedEntries.has(entry));
139
+ if (unexpected.length || (entries.length && !entries.includes("package.json"))) {
140
+ throw new Error(`Destination is not empty: ${target}`);
141
+ }
142
+ const existing = entries.includes("package.json") ? JSON.parse(await readFile(join(target, "package.json"), "utf8")) : {};
143
+ if (!record(existing) || (existing.scripts !== undefined && !record(existing.scripts))
144
+ || (existing.dependencies !== undefined && !record(existing.dependencies))
145
+ || (existing.devDependencies !== undefined && !record(existing.devDependencies))) {
146
+ throw new Error(`Invalid package.json in ${target}`);
147
+ }
148
+ const scripts = {
149
+ dev: "meeko dev .",
150
+ check: "meeko check .",
151
+ build: "meeko build .",
152
+ start: "meeko start .",
153
+ };
154
+ const oldScripts = existing.scripts;
155
+ for (const command of Object.keys(scripts))
156
+ if (oldScripts && command in oldScripts) {
157
+ throw new Error(`Destination already defines the ${command} script: ${target}`);
158
+ }
159
+ const manifest = {
160
+ ...existing,
161
+ name: typeof existing.name === "string" ? existing.name : name,
162
+ private: true,
163
+ type: "module",
164
+ engines: { ...(record(existing.engines) ? existing.engines : {}), node: ">=24" },
165
+ scripts: { ...oldScripts, ...scripts },
166
+ dependencies: { ...existing.dependencies, "@meekojs/meeko": metadata.version },
167
+ devDependencies: {
168
+ ...existing.devDependencies,
169
+ "@types/node": "^24.0.0", typescript: "^7.0.2", vite: "^8.3.5",
170
+ },
171
+ };
172
+ await mkdir(target, { recursive: true });
173
+ for (const [filename, content] of Object.entries(files)) {
174
+ const output = join(target, filename);
175
+ await mkdir(dirname(output), { recursive: true });
176
+ await writeFile(output, content, { encoding: "utf8", flag: "wx" });
177
+ }
178
+ await writeFile(join(target, "package.json"), JSON.stringify(manifest, null, 2) + "\n", {
179
+ encoding: "utf8", flag: entries.includes("package.json") ? "w" : "wx",
180
+ });
181
+ return target;
182
+ }
183
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/framework/init.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, readFile, readdir, stat, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, join, resolve } from \"node:path\";\n\nconst allowedEntries = new Set([\"package.json\", \"package-lock.json\", \"pnpm-lock.yaml\", \"pnpm-workspace.yaml\", \"yarn.lock\", \"node_modules\", \".npmrc\", \".git\"]);\n\nconst files: Readonly<Record<string, string>> = {\n \".gitignore\": \"node_modules/\\n.meeko/\\n\",\n \"public/.gitkeep\": \"\",\n \"app/styles/main.css\": `:root { color-scheme: light dark; font-family: system-ui, sans-serif; }\n* { box-sizing: border-box; }\nbody { margin: 0; }\n`,\n \"meeko.config.ts\": `import { defineMeekoConfig } from \"@meekojs/meeko/framework\";\n\nexport default defineMeekoConfig({\n extends: [],\n modules: [],\n imports: { serverDirs: [] },\n});\n`,\n \"tsconfig.json\": `{\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"module\": \"NodeNext\",\n \"moduleResolution\": \"NodeNext\",\n \"lib\": [\"ES2022\", \"DOM\"],\n \"types\": [\"node\", \"vite/client\"],\n \"strict\": true,\n \"noEmit\": true,\n \"allowArbitraryExtensions\": true,\n \"skipLibCheck\": true\n },\n \"files\": [],\n \"references\": [\n { \"path\": \"./.meeko/tsconfig.app.json\" },\n { \"path\": \"./.meeko/tsconfig.server.json\" },\n { \"path\": \"./.meeko/tsconfig.shared.json\" }\n ]\n}\n`,\n \"app/pages/index.meeko\": `<script lang=\"ts\">\nimport { signal } from \"@meekojs/meeko\";\n\nexport interface Props {\n params: Record<string, string>;\n search: Record<string, string | string[]>;\n data?: unknown;\n}\n\nconst count = signal(0);\n</script>\n\n<template>\n <section>\n <h1>Welcome to Meeko</h1>\n <p>File routes, server rendering, and hydration are ready.</p>\n <button type=\"button\" on:click=\"() => count.value++\">Count: {count.value}</button>\n </section>\n</template>\n`,\n \"app/pages/index.route.ts\": `import { definePage } from \"@meekojs/meeko/framework\";\n\nexport const route = definePage({ render: \"prerender\" });\n`,\n \"app/app.meeko\": `<script lang=\"ts\">\nimport type { Component } from \"@meekojs/meeko\";\n\nexport interface Props {\n outlet: Component<Record<string, unknown>>;\n}\n\nconst props = defineProps<Props>();\n</script>\n\n<template>\n <div class=\"site-root\"><component is=\"props.outlet\"></component></div>\n</template>\n`,\n \"app/layouts/default.meeko\": `<script lang=\"ts\">\nimport type { Component } from \"@meekojs/meeko\";\n\nexport interface Props {\n outlet: Component<Record<string, unknown>>;\n}\n\nconst props = defineProps<Props>();\n</script>\n\n<template>\n <div class=\"app-shell\">\n <header><a href=\"/\">Meeko</a></header>\n <main><component is=\"props.outlet\"></component></main>\n </div>\n</template>\n\n<style scoped>\n.app-shell { max-width: 64rem; margin: 0 auto; padding: 2rem; font-family: system-ui, sans-serif; }\nheader { margin-bottom: 2rem; }\n</style>\n`,\n \"server/session.ts\": `import { MemorySessionStore, SessionManager } from \"@meekojs/meeko/server\";\n\nexport function createSessionManager(production: boolean) {\n if (production) throw new Error(\"Configure a persistent SessionStore in server/session.ts before production startup\");\n return new SessionManager({ store: new MemorySessionStore(), production: false });\n}\n`,\n \"README.md\": `# Meeko framework starter\n\nRequires Node 24.\n\n\\`\\`\\`sh\nnpm install\nnpm run dev\n\\`\\`\\`\n\nRoutes live in \\`app/pages\\`. \\`app/layouts/default.meeko\\` keeps the shared shell during client navigation. Put shared route guards in \\`app/middleware\\` with \\`defineRouteMiddleware\\`, and Node request middleware in \\`server/middleware\\` with \\`defineServerMiddleware\\`; see the framework guide for their execution and security boundaries. Global styles in \\`app/styles/main.css\\` load automatically; component styles stay in \\`<style>\\` blocks. Run \\`npm run check\\` for types, then \\`npm run build\\` and \\`npm start\\` for production SSR. Generated files live under \\`.meeko/\\`.\n`,\n};\n\nfunction record(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Creates a framework project without overwriting unrelated user files. */\nexport async function initFrameworkProject(directory: string): Promise<string> {\n const target = resolve(directory);\n const name = basename(target).toLowerCase().replace(/[^a-z0-9._-]+/g, \"-\").replace(/^[._-]+|[._-]+$/g, \"\") || \"meeko-app\";\n const metadata: unknown = JSON.parse(await readFile(new URL(\"../../package.json\", import.meta.url), \"utf8\"));\n if (!record(metadata) || typeof metadata.version !== \"string\") throw new Error(\"Meeko package version is unavailable\");\n let entries: string[] = [];\n try {\n if (!(await stat(target)).isDirectory()) throw new Error(`Destination is not a directory: ${target}`);\n entries = await readdir(target);\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n }\n const unexpected = entries.filter((entry) => !allowedEntries.has(entry));\n if (unexpected.length || (entries.length && !entries.includes(\"package.json\"))) {\n throw new Error(`Destination is not empty: ${target}`);\n }\n const existing: unknown = entries.includes(\"package.json\") ? JSON.parse(await readFile(join(target, \"package.json\"), \"utf8\")) : {};\n if (!record(existing) || (existing.scripts !== undefined && !record(existing.scripts))\n || (existing.dependencies !== undefined && !record(existing.dependencies))\n || (existing.devDependencies !== undefined && !record(existing.devDependencies))) {\n throw new Error(`Invalid package.json in ${target}`);\n }\n const scripts = {\n dev: \"meeko dev .\",\n check: \"meeko check .\",\n build: \"meeko build .\",\n start: \"meeko start .\",\n };\n const oldScripts = existing.scripts as Record<string, unknown> | undefined;\n for (const command of Object.keys(scripts)) if (oldScripts && command in oldScripts) {\n throw new Error(`Destination already defines the ${command} script: ${target}`);\n }\n const manifest = {\n ...existing,\n name: typeof existing.name === \"string\" ? existing.name : name,\n private: true,\n type: \"module\",\n engines: { ...(record(existing.engines) ? existing.engines : {}), node: \">=24\" },\n scripts: { ...oldScripts, ...scripts },\n dependencies: { ...(existing.dependencies as Record<string, unknown> | undefined), \"@meekojs/meeko\": metadata.version },\n devDependencies: {\n ...(existing.devDependencies as Record<string, unknown> | undefined),\n \"@types/node\": \"^24.0.0\", typescript: \"^7.0.2\", vite: \"^8.3.5\",\n },\n };\n await mkdir(target, { recursive: true });\n for (const [filename, content] of Object.entries(files)) {\n const output = join(target, filename);\n await mkdir(dirname(output), { recursive: true });\n await writeFile(output, content, { encoding: \"utf8\", flag: \"wx\" });\n }\n await writeFile(join(target, \"package.json\"), JSON.stringify(manifest, null, 2) + \"\\n\", {\n encoding: \"utf8\", flag: entries.includes(\"package.json\") ? \"w\" : \"wx\",\n });\n return target;\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import type { RouteMatch } from "./router.js";
2
+ /** Runs for matched page requests and browser navigations before the loader. */
3
+ export interface RouteMiddlewareContext {
4
+ readonly url: URL;
5
+ readonly match: RouteMatch;
6
+ readonly signal: AbortSignal;
7
+ readonly phase: "server" | "client";
8
+ /** Only present during SSR. Never serialize this request to the browser. */
9
+ readonly request?: Request;
10
+ }
11
+ export type RouteMiddleware = (context: RouteMiddlewareContext) => void | Response | Promise<void | Response>;
12
+ export declare function defineRouteMiddleware(middleware: RouteMiddleware): RouteMiddleware;
13
+ /** Stops at the first response; middleware files are supplied in filename order. */
14
+ export declare function runRouteMiddleware(middlewares: readonly RouteMiddleware[], context: RouteMiddlewareContext): Promise<Response | undefined>;
@@ -0,0 +1,16 @@
1
+ export function defineRouteMiddleware(middleware) {
2
+ return middleware;
3
+ }
4
+ /** Stops at the first response; middleware files are supplied in filename order. */
5
+ export async function runRouteMiddleware(middlewares, context) {
6
+ for (const middleware of middlewares) {
7
+ const result = await middleware(context);
8
+ if (result === undefined)
9
+ continue;
10
+ if (!(result instanceof Response))
11
+ throw new TypeError("Route middleware must return a Response or nothing");
12
+ return result;
13
+ }
14
+ return undefined;
15
+ }
16
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/framework/middleware.ts"],"names":[],"mappings":"AAcA,MAAM,UAAU,qBAAqB,CAAC,UAA2B;IAC/D,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAAuC,EACvC,OAA+B;IAE/B,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,MAAM,KAAK,SAAS;YAAE,SAAS;QACnC,IAAI,CAAC,CAAC,MAAM,YAAY,QAAQ,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,oDAAoD,CAAC,CAAC;QAC7G,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["import type { RouteMatch } from \"./router.js\";\n\n/** Runs for matched page requests and browser navigations before the loader. */\nexport interface RouteMiddlewareContext {\n readonly url: URL;\n readonly match: RouteMatch;\n readonly signal: AbortSignal;\n readonly phase: \"server\" | \"client\";\n /** Only present during SSR. Never serialize this request to the browser. */\n readonly request?: Request;\n}\n\nexport type RouteMiddleware = (context: RouteMiddlewareContext) => void | Response | Promise<void | Response>;\n\nexport function defineRouteMiddleware(middleware: RouteMiddleware): RouteMiddleware {\n return middleware;\n}\n\n/** Stops at the first response; middleware files are supplied in filename order. */\nexport async function runRouteMiddleware(\n middlewares: readonly RouteMiddleware[],\n context: RouteMiddlewareContext,\n): Promise<Response | undefined> {\n for (const middleware of middlewares) {\n const result = await middleware(context);\n if (result === undefined) continue;\n if (!(result instanceof Response)) throw new TypeError(\"Route middleware must return a Response or nothing\");\n return result;\n }\n return undefined;\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import type { RouteRecord } from "./router.js";
2
+ export interface RouteLayer {
3
+ /** Stable identifier used in diagnostics and generated manifests. */
4
+ readonly name: string;
5
+ /** Directory containing the layer's app/pages tree. */
6
+ readonly root: string;
7
+ }
8
+ export interface RouteModule {
9
+ /** Stable identifier for a module contributing explicit route records. */
10
+ readonly name: string;
11
+ routes(): readonly RouteRecord[] | Promise<readonly RouteRecord[]>;
12
+ }
13
+ export interface ComposeRoutesOptions {
14
+ readonly projectRoot: string;
15
+ readonly layers?: readonly RouteLayer[];
16
+ readonly modules?: readonly RouteModule[];
17
+ }
18
+ /**
19
+ * Composes explicit route sources. Every URL collision is an error; no source
20
+ * silently overrides the app, a layer, or another module.
21
+ */
22
+ export declare function composeFrameworkRoutes(options: ComposeRoutesOptions): Promise<readonly RouteRecord[]>;
@@ -0,0 +1,29 @@
1
+ import { resolve } from "node:path";
2
+ import { createRouteManifest } from "./router.js";
3
+ import { discoverRoutes } from "./server.js";
4
+ /**
5
+ * Composes explicit route sources. Every URL collision is an error; no source
6
+ * silently overrides the app, a layer, or another module.
7
+ */
8
+ export async function composeFrameworkRoutes(options) {
9
+ const names = new Set(["app"]);
10
+ const roots = new Set([resolve(options.projectRoot).toLowerCase()]);
11
+ for (const source of [...options.layers ?? [], ...options.modules ?? []]) {
12
+ if (!/^[a-z][a-z0-9-]*$/.test(source.name) || names.has(source.name)) {
13
+ throw new Error(`Invalid or duplicate route source name: ${source.name}`);
14
+ }
15
+ names.add(source.name);
16
+ }
17
+ const routes = [...await discoverRoutes(options.projectRoot)];
18
+ for (const layer of options.layers ?? []) {
19
+ const root = resolve(layer.root);
20
+ if (roots.has(root.toLowerCase()))
21
+ throw new Error(`Duplicate route root: ${root}`);
22
+ roots.add(root.toLowerCase());
23
+ routes.push(...await discoverRoutes(root));
24
+ }
25
+ for (const module of options.modules ?? [])
26
+ routes.push(...await module.routes());
27
+ return createRouteManifest(routes);
28
+ }
29
+ //# sourceMappingURL=modules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modules.js","sourceRoot":"","sources":["../../src/framework/modules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAqB7C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,OAA6B;IACxE,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACpE,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC;QACzE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YACrE,MAAM,IAAI,KAAK,CAAC,2CAA2C,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAC5E,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IACD,MAAM,MAAM,GAAkB,CAAC,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;IAC7E,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;QACpF,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,IAAI,EAAE;QAAE,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAClF,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;AACrC,CAAC","sourcesContent":["import { resolve } from \"node:path\";\nimport { createRouteManifest } from \"./router.js\";\nimport type { RouteRecord } from \"./router.js\";\nimport { discoverRoutes } from \"./server.js\";\n\nexport interface RouteLayer {\n /** Stable identifier used in diagnostics and generated manifests. */\n readonly name: string;\n /** Directory containing the layer's app/pages tree. */\n readonly root: string;\n}\n\nexport interface RouteModule {\n /** Stable identifier for a module contributing explicit route records. */\n readonly name: string;\n routes(): readonly RouteRecord[] | Promise<readonly RouteRecord[]>;\n}\n\nexport interface ComposeRoutesOptions {\n readonly projectRoot: string;\n readonly layers?: readonly RouteLayer[];\n readonly modules?: readonly RouteModule[];\n}\n\n/**\n * Composes explicit route sources. Every URL collision is an error; no source\n * silently overrides the app, a layer, or another module.\n */\nexport async function composeFrameworkRoutes(options: ComposeRoutesOptions): Promise<readonly RouteRecord[]> {\n const names = new Set([\"app\"]);\n const roots = new Set([resolve(options.projectRoot).toLowerCase()]);\n for (const source of [...options.layers ?? [], ...options.modules ?? []]) {\n if (!/^[a-z][a-z0-9-]*$/.test(source.name) || names.has(source.name)) {\n throw new Error(`Invalid or duplicate route source name: ${source.name}`);\n }\n names.add(source.name);\n }\n const routes: RouteRecord[] = [...await discoverRoutes(options.projectRoot)];\n for (const layer of options.layers ?? []) {\n const root = resolve(layer.root);\n if (roots.has(root.toLowerCase())) throw new Error(`Duplicate route root: ${root}`);\n roots.add(root.toLowerCase());\n routes.push(...await discoverRoutes(root));\n }\n for (const module of options.modules ?? []) routes.push(...await module.routes());\n return createRouteManifest(routes);\n}\n"]}
@@ -0,0 +1,8 @@
1
+ export interface PageContractDiagnostic {
2
+ readonly filename: string;
3
+ readonly offset: number;
4
+ readonly code: "MEEK_PAGE_PROPS" | "MEEK_LAYOUT_PROPS" | "MEEK_ERROR_PROPS";
5
+ readonly message: string;
6
+ }
7
+ /** Checks generated route/component contracts after a saved project change. */
8
+ export declare function checkFrameworkPageContracts(projectRoot: string): Promise<PageContractDiagnostic[]>;
@@ -0,0 +1,110 @@
1
+ import { execFile } from "node:child_process";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { promisify } from "node:util";
6
+ import ts from "@typescript/typescript6";
7
+ import { parseSfc } from "../compiler/sfc.js";
8
+ import { prepareFrameworkTypeDeclarations } from "./type-declarations.js";
9
+ const execute = promisify(execFile);
10
+ const typescriptPackage = fileURLToPath(import.meta.resolve("typescript/package.json"));
11
+ function contractSourceOffset(source, filename) {
12
+ const script = parseSfc(source, filename).script;
13
+ const ast = ts.createSourceFile(filename, script.content, ts.ScriptTarget.Latest, true);
14
+ for (const statement of ast.statements) {
15
+ if (!ts.isVariableStatement(statement))
16
+ continue;
17
+ for (const declaration of statement.declarationList.declarations) {
18
+ const call = declaration.initializer;
19
+ if (call && ts.isCallExpression(call) && ts.isIdentifier(call.expression) && call.expression.text === "defineProps") {
20
+ return script.contentStart + (call.typeArguments?.[0]?.getStart(ast) ?? call.getStart(ast));
21
+ }
22
+ }
23
+ }
24
+ return script.contentStart;
25
+ }
26
+ function contractAliases(source, filename) {
27
+ const ast = ts.createSourceFile(filename, source, ts.ScriptTarget.Latest, true);
28
+ const aliases = new Map();
29
+ const literal = (node) => node && ts.isLiteralTypeNode(node) && ts.isStringLiteral(node.literal) ? node.literal.text : undefined;
30
+ for (const statement of ast.statements) {
31
+ if (!ts.isTypeAliasDeclaration(statement) || !ts.isTypeReferenceNode(statement.type))
32
+ continue;
33
+ const check = statement.type.typeArguments?.[0];
34
+ if (!check || !ts.isTypeReferenceNode(check))
35
+ continue;
36
+ const file = literal(check.typeArguments?.[2]);
37
+ if (!file)
38
+ continue;
39
+ const kind = statement.name.text.startsWith("__layout") ? "MEEK_LAYOUT_PROPS"
40
+ : statement.name.text.startsWith("__error") ? "MEEK_ERROR_PROPS" : "MEEK_PAGE_PROPS";
41
+ const sidecar = kind === "MEEK_PAGE_PROPS" ? literal(check.typeArguments?.[3]) : undefined;
42
+ aliases.set(ast.getLineAndCharacterOfPosition(statement.getStart(ast)).line + 1, {
43
+ file, kind, ...(sidecar === undefined ? {} : { sidecar }),
44
+ });
45
+ }
46
+ return aliases;
47
+ }
48
+ function samePath(left, right) {
49
+ const a = resolve(left);
50
+ const b = resolve(right);
51
+ return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
52
+ }
53
+ /** Checks generated route/component contracts after a saved project change. */
54
+ export async function checkFrameworkPageContracts(projectRoot) {
55
+ const root = resolve(projectRoot);
56
+ await prepareFrameworkTypeDeclarations(root);
57
+ const generated = join(root, ".meeko", "generated", "page-contracts.server.ts");
58
+ const aliases = contractAliases(await readFile(generated, "utf8"), generated);
59
+ // The editor's isolated consumer may not install the optional Vite peer or
60
+ // @types/node. The contract itself only needs component declarations.
61
+ const config = join(root, ".meeko", "tsconfig.page-contracts.json");
62
+ const configSource = `${JSON.stringify({
63
+ compilerOptions: {
64
+ target: "ES2022", module: "NodeNext", moduleResolution: "NodeNext", lib: ["ES2022", "DOM"],
65
+ types: [], strict: true, noEmit: true, skipLibCheck: true, allowArbitraryExtensions: true,
66
+ rootDirs: ["..", "./types"],
67
+ },
68
+ include: ["generated/page-contracts.server.ts", "types/**/*.d.meeko.ts", "types/routes.d.ts"],
69
+ }, null, 2)}\n`;
70
+ try {
71
+ if (await readFile(config, "utf8") !== configSource)
72
+ await writeFile(config, configSource);
73
+ }
74
+ catch (error) {
75
+ if (error.code === "ENOENT")
76
+ await writeFile(config, configSource);
77
+ else
78
+ throw error;
79
+ }
80
+ const compiler = join(dirname(typescriptPackage), "bin", "tsc");
81
+ let output = "";
82
+ try {
83
+ await execute(process.execPath, [compiler, "-p", config, "--noEmit", "--pretty", "false"]);
84
+ }
85
+ catch (error) {
86
+ const failure = error;
87
+ output = `${failure.stdout ?? ""}${failure.stderr ?? ""}`;
88
+ if (!output.trim())
89
+ throw error;
90
+ }
91
+ const diagnostics = [];
92
+ for (const line of output.split(/\r?\n/)) {
93
+ const match = /^(.*page-contracts\.server\.ts)\((\d+),\d+\): error TS2344: (.*)$/.exec(line);
94
+ if (!match || !samePath(match[1], generated))
95
+ continue;
96
+ const alias = aliases.get(Number(match[2]));
97
+ if (!alias)
98
+ continue;
99
+ const filename = resolve(root, alias.file);
100
+ const source = await readFile(filename, "utf8");
101
+ const unsafe = match[3].includes("props cannot be any, never or unknown");
102
+ const title = alias.kind === "MEEK_PAGE_PROPS" ? "Page" : alias.kind === "MEEK_LAYOUT_PROPS" ? "Layout" : "Error page";
103
+ const message = unsafe ? `${title} ${alias.file} props cannot be any, never or unknown`
104
+ : alias.kind === "MEEK_PAGE_PROPS" ? `Page ${alias.file} props do not match loader ${alias.sidecar ?? "(no sidecar)"}`
105
+ : `${title} ${alias.file} props do not match the values supplied by Meeko`;
106
+ diagnostics.push({ filename, offset: contractSourceOffset(source, filename), code: alias.kind, message });
107
+ }
108
+ return diagnostics;
109
+ }
110
+ //# sourceMappingURL=page-contracts-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-contracts-check.js","sourceRoot":"","sources":["../../src/framework/page-contracts-check.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFile } from \"node:child_process\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { promisify } from \"node:util\";\nimport ts from \"@typescript/typescript6\";\nimport { parseSfc } from \"../compiler/sfc.js\";\nimport { prepareFrameworkTypeDeclarations } from \"./type-declarations.js\";\n\nconst execute = promisify(execFile);\nconst typescriptPackage = fileURLToPath(import.meta.resolve(\"typescript/package.json\"));\n\nexport interface PageContractDiagnostic {\n readonly filename: string;\n readonly offset: number;\n readonly code: \"MEEK_PAGE_PROPS\" | \"MEEK_LAYOUT_PROPS\" | \"MEEK_ERROR_PROPS\";\n readonly message: string;\n}\n\nfunction contractSourceOffset(source: string, filename: string): number {\n const script = parseSfc(source, filename).script;\n const ast = ts.createSourceFile(filename, script.content, ts.ScriptTarget.Latest, true);\n for (const statement of ast.statements) {\n if (!ts.isVariableStatement(statement)) continue;\n for (const declaration of statement.declarationList.declarations) {\n const call = declaration.initializer;\n if (call && ts.isCallExpression(call) && ts.isIdentifier(call.expression) && call.expression.text === \"defineProps\") {\n return script.contentStart + (call.typeArguments?.[0]?.getStart(ast) ?? call.getStart(ast));\n }\n }\n }\n return script.contentStart;\n}\n\nfunction contractAliases(source: string, filename: string): Map<number, { file: string; kind: PageContractDiagnostic[\"code\"]; sidecar?: string }> {\n const ast = ts.createSourceFile(filename, source, ts.ScriptTarget.Latest, true);\n const aliases = new Map<number, { file: string; kind: PageContractDiagnostic[\"code\"]; sidecar?: string }>();\n const literal = (node: ts.TypeNode | undefined): string | undefined =>\n node && ts.isLiteralTypeNode(node) && ts.isStringLiteral(node.literal) ? node.literal.text : undefined;\n for (const statement of ast.statements) {\n if (!ts.isTypeAliasDeclaration(statement) || !ts.isTypeReferenceNode(statement.type)) continue;\n const check = statement.type.typeArguments?.[0];\n if (!check || !ts.isTypeReferenceNode(check)) continue;\n const file = literal(check.typeArguments?.[2]);\n if (!file) continue;\n const kind = statement.name.text.startsWith(\"__layout\") ? \"MEEK_LAYOUT_PROPS\"\n : statement.name.text.startsWith(\"__error\") ? \"MEEK_ERROR_PROPS\" : \"MEEK_PAGE_PROPS\";\n const sidecar = kind === \"MEEK_PAGE_PROPS\" ? literal(check.typeArguments?.[3]) : undefined;\n aliases.set(ast.getLineAndCharacterOfPosition(statement.getStart(ast)).line + 1, {\n file, kind, ...(sidecar === undefined ? {} : { sidecar }),\n });\n }\n return aliases;\n}\n\nfunction samePath(left: string, right: string): boolean {\n const a = resolve(left);\n const b = resolve(right);\n return process.platform === \"win32\" ? a.toLowerCase() === b.toLowerCase() : a === b;\n}\n\n/** Checks generated route/component contracts after a saved project change. */\nexport async function checkFrameworkPageContracts(projectRoot: string): Promise<PageContractDiagnostic[]> {\n const root = resolve(projectRoot);\n await prepareFrameworkTypeDeclarations(root);\n const generated = join(root, \".meeko\", \"generated\", \"page-contracts.server.ts\");\n const aliases = contractAliases(await readFile(generated, \"utf8\"), generated);\n // The editor's isolated consumer may not install the optional Vite peer or\n // @types/node. The contract itself only needs component declarations.\n const config = join(root, \".meeko\", \"tsconfig.page-contracts.json\");\n const configSource = `${JSON.stringify({\n compilerOptions: {\n target: \"ES2022\", module: \"NodeNext\", moduleResolution: \"NodeNext\", lib: [\"ES2022\", \"DOM\"],\n types: [], strict: true, noEmit: true, skipLibCheck: true, allowArbitraryExtensions: true,\n rootDirs: [\"..\", \"./types\"],\n },\n include: [\"generated/page-contracts.server.ts\", \"types/**/*.d.meeko.ts\", \"types/routes.d.ts\"],\n }, null, 2)}\\n`;\n try { if (await readFile(config, \"utf8\") !== configSource) await writeFile(config, configSource); }\n catch (error) { if ((error as NodeJS.ErrnoException).code === \"ENOENT\") await writeFile(config, configSource); else throw error; }\n const compiler = join(dirname(typescriptPackage), \"bin\", \"tsc\");\n let output = \"\";\n try { await execute(process.execPath, [compiler, \"-p\", config, \"--noEmit\", \"--pretty\", \"false\"]); }\n catch (error) {\n const failure = error as Error & { stdout?: string; stderr?: string };\n output = `${failure.stdout ?? \"\"}${failure.stderr ?? \"\"}`;\n if (!output.trim()) throw error;\n }\n const diagnostics: PageContractDiagnostic[] = [];\n for (const line of output.split(/\\r?\\n/)) {\n const match = /^(.*page-contracts\\.server\\.ts)\\((\\d+),\\d+\\): error TS2344: (.*)$/.exec(line);\n if (!match || !samePath(match[1]!, generated)) continue;\n const alias = aliases.get(Number(match[2]));\n if (!alias) continue;\n const filename = resolve(root, alias.file);\n const source = await readFile(filename, \"utf8\");\n const unsafe = match[3]!.includes(\"props cannot be any, never or unknown\");\n const title = alias.kind === \"MEEK_PAGE_PROPS\" ? \"Page\" : alias.kind === \"MEEK_LAYOUT_PROPS\" ? \"Layout\" : \"Error page\";\n const message = unsafe ? `${title} ${alias.file} props cannot be any, never or unknown`\n : alias.kind === \"MEEK_PAGE_PROPS\" ? `Page ${alias.file} props do not match loader ${alias.sidecar ?? \"(no sidecar)\"}`\n : `${title} ${alias.file} props do not match the values supplied by Meeko`;\n diagnostics.push({ filename, offset: contractSourceOffset(source, filename), code: alias.kind, message });\n }\n return diagnostics;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ /** Checks that each page accepts its route params, search and loader data. */
2
+ export declare function generatePageContracts(projectRoot: string): Promise<string>;