@ykdz/template 0.0.17 → 0.0.19

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 (80) hide show
  1. package/dist/cli.js +61 -24
  2. package/node_modules/@ykdz/template-builtin-source/dist/templates/registry.js +0 -2
  3. package/node_modules/@ykdz/template-builtin-source/dist/templates/ts-lib/projection.js +4 -4
  4. package/node_modules/@ykdz/template-builtin-source/dist/templates/vike-app/projection.js +13 -2
  5. package/node_modules/@ykdz/template-builtin-source/dist/templates/vue-app/projection.js +6 -74
  6. package/node_modules/@ykdz/template-builtin-source/dist/templates/vue-hono-app/projection.js +23 -22
  7. package/node_modules/@ykdz/template-builtin-source/package.json +41 -8
  8. package/node_modules/@ykdz/template-builtin-source/templates/preset-source.json +69 -282
  9. package/node_modules/@ykdz/template-builtin-source/templates/rust-bin/src/main.rs +5 -2
  10. package/node_modules/@ykdz/template-builtin-source/templates/rust-bin/turbo.json +2 -1
  11. package/node_modules/@ykdz/template-builtin-source/templates/ts-lib/turbo.json +2 -1
  12. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{drizzle.config.ts → db/drizzle.config.ts} +2 -2
  13. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{database → db/src}/db.ts +1 -1
  14. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/db/src/index.ts +1 -0
  15. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{database → db/src}/queries/todos.ts +4 -8
  16. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{test → db/test}/todos.test.ts +2 -2
  17. package/node_modules/@ykdz/template-builtin-source/templates/{hono-api → vike-app/db}/turbo.json +2 -1
  18. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{pages → web/pages}/index/+Page.telefunc.ts +3 -1
  19. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{pages → web/pages}/index/+Page.vue +1 -2
  20. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/web/playwright.config.ts +43 -0
  21. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/web/scripts/run-playwright.ts +62 -0
  22. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{server → web/server}/app.ts +1 -1
  23. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{test → web/test}/e2e/app.spec.ts +1 -1
  24. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/web/turbo.json +12 -0
  25. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{types → web/types}/global.d.ts +1 -1
  26. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{vite.config.ts → web/vite.config.ts} +1 -0
  27. package/node_modules/@ykdz/template-builtin-source/templates/vue-app/playwright.config.ts +16 -3
  28. package/node_modules/@ykdz/template-builtin-source/templates/vue-app/scripts/run-playwright.ts +38 -1
  29. package/node_modules/@ykdz/template-builtin-source/templates/vue-app/turbo.json +1 -0
  30. package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/api/turbo.json +1 -0
  31. package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/web/playwright.config.ts +7 -9
  32. package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/web/scripts/run-playwright.ts +79 -1
  33. package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/web/src/App.vue +4 -5
  34. package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/web/src/api.ts +9 -3
  35. package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/web/test/e2e/app.spec.ts +1 -1
  36. package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/web/turbo.json +1 -0
  37. package/node_modules/@ykdz/template-core/dist/Cargo.lock +16 -0
  38. package/node_modules/@ykdz/template-core/dist/Cargo.toml +12 -0
  39. package/node_modules/@ykdz/template-core/dist/dependency-catalog.d.ts +8 -0
  40. package/node_modules/@ykdz/template-core/dist/dependency-catalog.js +63 -2
  41. package/node_modules/@ykdz/template-core/dist/generated-scenarios.d.ts +1 -1
  42. package/node_modules/@ykdz/template-core/dist/generated-scenarios.js +104 -26
  43. package/node_modules/@ykdz/template-core/dist/module-graph.d.ts +1 -0
  44. package/node_modules/@ykdz/template-core/dist/module-graph.js +19 -12
  45. package/node_modules/@ykdz/template-core/dist/next-step-instructions.d.ts +8 -0
  46. package/node_modules/@ykdz/template-core/dist/next-step-instructions.js +65 -0
  47. package/node_modules/@ykdz/template-core/dist/package-addition.js +6 -12
  48. package/node_modules/@ykdz/template-core/dist/package-linking.d.ts +24 -0
  49. package/node_modules/@ykdz/template-core/dist/package-linking.js +35 -0
  50. package/node_modules/@ykdz/template-core/dist/preset-source.d.ts +2 -2
  51. package/node_modules/@ykdz/template-core/dist/projection-capabilities.js +240 -153
  52. package/node_modules/@ykdz/template-core/dist/template-boundary-check.js +46 -10
  53. package/node_modules/@ykdz/template-core/package.json +9 -5
  54. package/node_modules/@ykdz/template-shared/dist/index.d.ts +3 -152
  55. package/node_modules/@ykdz/template-shared/dist/index.js +49 -305
  56. package/node_modules/@ykdz/template-shared/package.json +8 -4
  57. package/package.json +5 -5
  58. package/node_modules/@ykdz/template-builtin-source/dist/templates/hono-api/projection.d.ts +0 -6
  59. package/node_modules/@ykdz/template-builtin-source/dist/templates/hono-api/projection.js +0 -213
  60. package/node_modules/@ykdz/template-builtin-source/templates/hono-api/.github/dependabot.yml +0 -23
  61. package/node_modules/@ykdz/template-builtin-source/templates/hono-api/.github/workflows/check.yml +0 -19
  62. package/node_modules/@ykdz/template-builtin-source/templates/hono-api/src/app.ts +0 -5
  63. package/node_modules/@ykdz/template-builtin-source/templates/hono-api/src/server.ts +0 -15
  64. package/node_modules/@ykdz/template-builtin-source/templates/hono-api/test/app.test.ts +0 -10
  65. package/node_modules/@ykdz/template-builtin-source/templates/hono-api/vitest.config.ts +0 -15
  66. package/node_modules/@ykdz/template-builtin-source/templates/vike-app/playwright.config.ts +0 -23
  67. package/node_modules/@ykdz/template-builtin-source/templates/vue-app/playwright.package-addition.config.ts +0 -22
  68. /package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{database → db/src}/schema.ts +0 -0
  69. /package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{+server.ts → web/+server.ts} +0 -0
  70. /package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{.env.example → web/.env.example} +0 -0
  71. /package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{assets → web/assets}/logo.svg +0 -0
  72. /package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{components → web/components}/CounterButton.vue +0 -0
  73. /package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{components → web/components}/PageShell.vue +0 -0
  74. /package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{pages → web/pages}/+Head.vue +0 -0
  75. /package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{pages → web/pages}/+Layout.vue +0 -0
  76. /package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{pages → web/pages}/+config.ts +0 -0
  77. /package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{pages → web/pages}/tailwind.css +0 -0
  78. /package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{server → web/server}/api.ts +0 -0
  79. /package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{types → web/types}/env.d.ts +0 -0
  80. /package/node_modules/@ykdz/template-builtin-source/templates/vike-app/{vitest.config.ts → web/vitest.config.ts} +0 -0
@@ -0,0 +1,62 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createServer } from "node:net";
3
+
4
+ async function availablePort(): Promise<string> {
5
+ return await new Promise((resolve, reject) => {
6
+ const server = createServer();
7
+
8
+ server.once("error", reject);
9
+ server.listen(0, "127.0.0.1", () => {
10
+ const address = server.address();
11
+
12
+ if (typeof address !== "object" || address === null) {
13
+ server.close(() => {
14
+ reject(new Error("Could not allocate a Playwright web port"));
15
+ });
16
+ return;
17
+ }
18
+
19
+ const port = String(address.port);
20
+ server.close((error) => {
21
+ if (error) {
22
+ reject(error);
23
+ return;
24
+ }
25
+
26
+ resolve(port);
27
+ });
28
+ });
29
+ });
30
+ }
31
+
32
+ async function playwrightEnv(): Promise<NodeJS.ProcessEnv> {
33
+ return {
34
+ ...process.env,
35
+ PLAYWRIGHT_WEB_PORT:
36
+ process.env.PLAYWRIGHT_WEB_PORT ?? (await availablePort()),
37
+ };
38
+ }
39
+
40
+ const env = await playwrightEnv();
41
+ delete env.NO_COLOR;
42
+
43
+ const command = process.platform === "win32" ? "playwright.cmd" : "playwright";
44
+ const child = spawn(command, ["test", ...process.argv.slice(2)], {
45
+ env,
46
+ stdio: "inherit",
47
+ });
48
+
49
+ child.once("error", (error: Error) => {
50
+ console.error(error);
51
+ process.exitCode = 1;
52
+ });
53
+
54
+ child.once("exit", (code: number | null, signal: NodeJS.Signals | null) => {
55
+ if (signal) {
56
+ console.error(`Playwright exited with signal ${signal}`);
57
+ process.exitCode = 1;
58
+ return;
59
+ }
60
+
61
+ process.exitCode = code ?? 1;
62
+ });
@@ -2,7 +2,7 @@ import vike from "@vikejs/hono";
2
2
  import { Hono } from "hono";
3
3
  import { Telefunc } from "telefunc/node";
4
4
 
5
- import { createDatabase } from "#/database/db";
5
+ // @template-anchor db-package-import
6
6
  import { api } from "#/server/api";
7
7
 
8
8
  const telefunc = new Telefunc();
@@ -3,7 +3,7 @@ import { expect, test } from "@playwright/test";
3
3
  test("renders the Vike home page", async ({ page }) => {
4
4
  const todoTitle = `编写生成项目端到端测试 ${Date.now()}`;
5
5
 
6
- await page.goto("/");
6
+ await page.goto("/", { waitUntil: "networkidle" });
7
7
  await expect(
8
8
  page.getByRole("heading", { name: "全栈 Vike 应用" }),
9
9
  ).toBeVisible();
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": ["//"],
3
+ "tags": ["app"],
4
+ "tasks": {
5
+ "build:run": {
6
+ "outputs": ["dist/**"]
7
+ },
8
+ "test:e2e:run": {
9
+ "cache": false
10
+ }
11
+ }
12
+ }
@@ -1,4 +1,4 @@
1
- import type { Database } from "#/database/db";
1
+ // @template-anchor db-package-import
2
2
 
3
3
  declare global {
4
4
  namespace Telefunc {
@@ -14,6 +14,7 @@ export default defineConfig({
14
14
  resolve: {
15
15
  alias: {
16
16
  "#": fileURLToPath(new URL(".", import.meta.url)),
17
+ "#db": fileURLToPath(new URL("../../packages/db/src", import.meta.url)),
17
18
  },
18
19
  },
19
20
  });
@@ -1,15 +1,28 @@
1
1
  import { defineConfig, devices } from "@playwright/test";
2
2
 
3
+ function requiredPort(name: string): number {
4
+ const value = process.env[name];
5
+
6
+ if (!value) {
7
+ throw new Error(`${name} must be set by scripts/run-playwright.ts`);
8
+ }
9
+
10
+ return Number(value);
11
+ }
12
+
13
+ const previewPort = requiredPort("PLAYWRIGHT_WEB_PORT");
14
+ const previewUrl = `http://127.0.0.1:${previewPort}`;
15
+
3
16
  export default defineConfig({
4
17
  testDir: "./test/e2e",
5
18
  use: {
6
- baseURL: "http://127.0.0.1:4173",
19
+ baseURL: previewUrl,
7
20
  trace: "on-first-retry",
8
21
  },
9
22
  webServer: {
10
- command: "pnpm run preview --host 127.0.0.1 --port 4173",
23
+ command: `pnpm run preview --host 127.0.0.1 --port ${previewPort}`,
11
24
  reuseExistingServer: !process.env.CI,
12
- url: "http://127.0.0.1:4173",
25
+ url: previewUrl,
13
26
  },
14
27
  projects: [
15
28
  {
@@ -1,6 +1,43 @@
1
1
  import { spawn } from "node:child_process";
2
+ import { createServer } from "node:net";
2
3
 
3
- const env: NodeJS.ProcessEnv = { ...process.env };
4
+ async function availablePort(): Promise<string> {
5
+ return await new Promise((resolve, reject) => {
6
+ const server = createServer();
7
+
8
+ server.once("error", reject);
9
+ server.listen(0, "127.0.0.1", () => {
10
+ const address = server.address();
11
+
12
+ if (typeof address !== "object" || address === null) {
13
+ server.close(() => {
14
+ reject(new Error("Could not allocate a Playwright web port"));
15
+ });
16
+ return;
17
+ }
18
+
19
+ const port = String(address.port);
20
+ server.close((error) => {
21
+ if (error) {
22
+ reject(error);
23
+ return;
24
+ }
25
+
26
+ resolve(port);
27
+ });
28
+ });
29
+ });
30
+ }
31
+
32
+ async function playwrightEnv(): Promise<NodeJS.ProcessEnv> {
33
+ return {
34
+ ...process.env,
35
+ PLAYWRIGHT_WEB_PORT:
36
+ process.env.PLAYWRIGHT_WEB_PORT ?? (await availablePort()),
37
+ };
38
+ }
39
+
40
+ const env = await playwrightEnv();
4
41
  delete env.NO_COLOR;
5
42
 
6
43
  const command = process.platform === "win32" ? "playwright.cmd" : "playwright";
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "extends": ["//"],
3
+ "tags": ["app"],
3
4
  "tasks": {
4
5
  "build:run": {
5
6
  "outputs": ["dist/**"]
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "extends": ["//"],
3
+ "tags": ["app"],
3
4
  "tasks": {
4
5
  "build:run": {
5
6
  "outputs": ["dist/**"]
@@ -1,19 +1,17 @@
1
1
  import { defineConfig, devices } from "@playwright/test";
2
2
 
3
- function workspacePortOffset(): number {
4
- let hash = 0;
3
+ function requiredPort(name: string): number {
4
+ const value = process.env[name];
5
5
 
6
- for (const character of process.cwd()) {
7
- hash = (hash * 31 + character.charCodeAt(0)) % 5_000;
6
+ if (!value) {
7
+ throw new Error(`${name} must be set by scripts/run-playwright.ts`);
8
8
  }
9
9
 
10
- return hash * 2;
10
+ return Number(value);
11
11
  }
12
12
 
13
- const fallbackApiPort = 43_000 + workspacePortOffset();
14
- const fallbackWebPort = fallbackApiPort + 1;
15
- const apiPort = Number(process.env.PLAYWRIGHT_API_PORT ?? fallbackApiPort);
16
- const webPort = Number(process.env.PLAYWRIGHT_WEB_PORT ?? fallbackWebPort);
13
+ const apiPort = requiredPort("PLAYWRIGHT_API_PORT");
14
+ const webPort = requiredPort("PLAYWRIGHT_WEB_PORT");
17
15
  const apiBaseUrl = `http://127.0.0.1:${apiPort}`;
18
16
  const apiHealthUrl = `${apiBaseUrl}/api/health`;
19
17
  const webBaseUrl = `http://127.0.0.1:${webPort}`;
@@ -1,6 +1,84 @@
1
1
  import { spawn } from "node:child_process";
2
+ import { createServer } from "node:net";
2
3
 
3
- const env: NodeJS.ProcessEnv = { ...process.env };
4
+ async function listenOnAvailablePort(): Promise<{
5
+ readonly port: string;
6
+ readonly server: ReturnType<typeof createServer>;
7
+ }> {
8
+ return await new Promise((resolve, reject) => {
9
+ const server = createServer();
10
+
11
+ server.once("error", reject);
12
+ server.listen(0, "127.0.0.1", () => {
13
+ const address = server.address();
14
+
15
+ if (typeof address !== "object" || address === null) {
16
+ server.close(() => {
17
+ reject(new Error("Could not allocate a Playwright server port"));
18
+ });
19
+ return;
20
+ }
21
+
22
+ resolve({ port: String(address.port), server });
23
+ });
24
+ });
25
+ }
26
+
27
+ async function availablePorts(count: number): Promise<string[]> {
28
+ const leases: Array<{
29
+ readonly port: string;
30
+ readonly server: ReturnType<typeof createServer>;
31
+ }> = [];
32
+
33
+ try {
34
+ for (let index = 0; index < count; index += 1) {
35
+ leases.push(await listenOnAvailablePort());
36
+ }
37
+
38
+ return leases.map((lease) => lease.port);
39
+ } finally {
40
+ await Promise.all(
41
+ leases.map(
42
+ (lease) =>
43
+ new Promise<void>((resolve, reject) => {
44
+ lease.server.close((error) => {
45
+ if (error) {
46
+ reject(error);
47
+ return;
48
+ }
49
+
50
+ resolve();
51
+ });
52
+ }),
53
+ ),
54
+ );
55
+ }
56
+ }
57
+
58
+ async function playwrightEnv(): Promise<NodeJS.ProcessEnv> {
59
+ const missingPortNames = [
60
+ process.env.PLAYWRIGHT_API_PORT === undefined
61
+ ? "PLAYWRIGHT_API_PORT"
62
+ : undefined,
63
+ process.env.PLAYWRIGHT_WEB_PORT === undefined
64
+ ? "PLAYWRIGHT_WEB_PORT"
65
+ : undefined,
66
+ ].filter((name) => name !== undefined);
67
+ const ports = await availablePorts(missingPortNames.length);
68
+ const allocatedPorts = Object.fromEntries(
69
+ missingPortNames.map((name, index) => [name, ports[index]]),
70
+ );
71
+
72
+ return {
73
+ ...process.env,
74
+ PLAYWRIGHT_API_PORT:
75
+ process.env.PLAYWRIGHT_API_PORT ?? allocatedPorts.PLAYWRIGHT_API_PORT,
76
+ PLAYWRIGHT_WEB_PORT:
77
+ process.env.PLAYWRIGHT_WEB_PORT ?? allocatedPorts.PLAYWRIGHT_WEB_PORT,
78
+ };
79
+ }
80
+
81
+ const env = await playwrightEnv();
4
82
  delete env.NO_COLOR;
5
83
 
6
84
  const command = process.platform === "win32" ? "playwright.cmd" : "playwright";
@@ -2,7 +2,7 @@
2
2
  import { storeToRefs } from "pinia";
3
3
  import { onMounted, onUnmounted, ref } from "vue";
4
4
 
5
- import { api } from "#/api";
5
+ import { getHealth } from "#/api";
6
6
  import { useCounterStore } from "#/stores/counter";
7
7
 
8
8
  const counter = useCounterStore();
@@ -27,8 +27,7 @@ onUnmounted(() => {
27
27
  });
28
28
 
29
29
  onMounted(async () => {
30
- const response = await api.api.health.$get();
31
- const body = await response.json();
30
+ const body = await getHealth();
32
31
  apiStatus.value = body.status === "ok" ? "正常" : body.status;
33
32
  });
34
33
  </script>
@@ -41,9 +40,9 @@ onMounted(async () => {
41
40
  <p class="text-sm font-medium tracking-wide text-cyan-300 uppercase">
42
41
  Vue + Hono 应用预设
43
42
  </p>
44
- <h1 class="mt-4 text-4xl font-semibold">Vue、Hono 和类型化 RPC</h1>
43
+ <h1 class="mt-4 text-4xl font-semibold">Vue 和 Hono 工作区</h1>
45
44
  <p class="mt-4 text-lg text-slate-300">
46
- 这个生成的工作区会让 Web 包根据 API 契约进行类型检查。
45
+ Web 和 API 是独立应用包,共享代码应放入 packages/*。
47
46
  </p>
48
47
  <div class="mt-8 flex flex-wrap items-center gap-4">
49
48
  <button
@@ -1,5 +1,11 @@
1
- // @template-anchor api-type-imports
2
-
3
1
  const apiBaseUrl = import.meta.env.VITE_API_BASE_URL ?? "/";
4
2
 
5
- export const api = hc<AppType>(apiBaseUrl);
3
+ export async function getHealth(): Promise<{ status: "ok" }> {
4
+ const response = await fetch(`${apiBaseUrl.replace(/\/$/, "")}/api/health`);
5
+
6
+ if (!response.ok) {
7
+ throw new Error(`Health check failed with ${response.status}`);
8
+ }
9
+
10
+ return (await response.json()) as { status: "ok" };
11
+ }
@@ -4,7 +4,7 @@ test("renders the web app and calls the API", async ({ page }) => {
4
4
  await page.goto("/");
5
5
 
6
6
  await expect(
7
- page.getByRole("heading", { name: "Vue、Hono 和类型化 RPC" }),
7
+ page.getByRole("heading", { name: "Vue 和 Hono 工作区" }),
8
8
  ).toBeVisible();
9
9
  await expect(page.getByText("API 状态:正常")).toBeVisible();
10
10
  const counterButton = page.getByRole("button", { name: "计数:0" });
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "extends": ["//"],
3
+ "tags": ["app"],
3
4
  "tasks": {
4
5
  "build:run": {
5
6
  "outputs": ["dist/**"]
@@ -0,0 +1,16 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "anyhow"
7
+ version = "1.0.103"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
10
+
11
+ [[package]]
12
+ name = "template-cargo-dependencies"
13
+ version = "0.0.0"
14
+ dependencies = [
15
+ "anyhow",
16
+ ]
@@ -0,0 +1,12 @@
1
+ [package]
2
+ name = "template-cargo-dependencies"
3
+ version = "0.0.0"
4
+ edition = "2024"
5
+ publish = false
6
+
7
+ [[bin]]
8
+ name = "template-cargo-dependencies"
9
+ path = "packages/builtin-source/templates/rust-bin/src/main.rs"
10
+
11
+ [dependencies]
12
+ anyhow = "1.0.100"
@@ -1,4 +1,5 @@
1
1
  export type TemplateDependencyCatalog = Record<string, string>;
2
+ export type TemplateCargoDependencyVersions = Record<string, string>;
2
3
  export type GeneratedDependencyCatalogOptions = {
3
4
  readonly dependencies: readonly string[];
4
5
  readonly packages?: readonly string[];
@@ -12,6 +13,13 @@ export type GeneratedPackageManifestDependencies = {
12
13
  };
13
14
  export declare function loadTemplateDependencyCatalog(): TemplateDependencyCatalog;
14
15
  export declare function selectTemplateDependencyCatalogEntries(dependencies: readonly string[], catalog?: TemplateDependencyCatalog): TemplateDependencyCatalog;
16
+ export declare function loadTemplateCargoDependencyVersions(): TemplateCargoDependencyVersions;
17
+ export declare function renderCargoLockForPackage(options: {
18
+ readonly packageName: string;
19
+ readonly packageVersion: string;
20
+ }): string;
21
+ export declare function selectTemplateCargoDependencyVersions(dependencies: readonly string[], catalog?: TemplateCargoDependencyVersions): TemplateCargoDependencyVersions;
22
+ export declare function renderCargoDependencyTomlEntries(dependencies: readonly string[], catalog?: TemplateCargoDependencyVersions): string[];
15
23
  export declare function collectGeneratedManifestCatalogDependencies(manifests: readonly GeneratedPackageManifestDependencies[]): string[];
16
24
  export declare function collectGeneratedManifestCatalogReferences(manifests: readonly GeneratedPackageManifestDependencies[]): string[];
17
25
  export declare function pnpmWorkspaceYamlWithCatalogDependencies(workspaceYaml: string, dependencies: readonly string[], templateCatalog?: TemplateDependencyCatalog): string;
@@ -11,10 +11,10 @@ function templateRepositoryRoot() {
11
11
  ];
12
12
  return (candidates.find((candidate) => existsSync(path.join(candidate, "pnpm-workspace.yaml"))) ?? candidates[0]);
13
13
  }
14
- function parseTemplateDependencyCatalog(workspaceYaml) {
14
+ function parseTemplateCatalogSection(workspaceYaml, sectionName) {
15
15
  const catalog = {};
16
16
  const lines = workspaceYaml.split(/\r?\n/);
17
- const catalogStart = lines.findIndex((line) => line === "catalog:");
17
+ const catalogStart = lines.findIndex((line) => line === `${sectionName}:`);
18
18
  if (catalogStart === -1) {
19
19
  return catalog;
20
20
  }
@@ -35,6 +35,9 @@ function parseTemplateDependencyCatalog(workspaceYaml) {
35
35
  }
36
36
  return catalog;
37
37
  }
38
+ function parseTemplateDependencyCatalog(workspaceYaml) {
39
+ return parseTemplateCatalogSection(workspaceYaml, "catalog");
40
+ }
38
41
  export function loadTemplateDependencyCatalog() {
39
42
  return parseTemplateDependencyCatalog(readFileSync(path.join(templateRepositoryRoot(), "pnpm-workspace.yaml"), "utf8"));
40
43
  }
@@ -49,6 +52,64 @@ export function selectTemplateDependencyCatalogEntries(dependencies, catalog = l
49
52
  }
50
53
  return selected;
51
54
  }
55
+ function parseTemplateCargoDependencyVersions(cargoToml) {
56
+ const dependencies = {};
57
+ const lines = cargoToml.split(/\r?\n/);
58
+ const dependenciesStart = lines.findIndex((line) => line.trim() === "[dependencies]");
59
+ if (dependenciesStart === -1) {
60
+ return dependencies;
61
+ }
62
+ for (const line of lines.slice(dependenciesStart + 1)) {
63
+ const trimmed = line.trim();
64
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
65
+ break;
66
+ }
67
+ if (trimmed.length === 0 || trimmed.startsWith("#")) {
68
+ continue;
69
+ }
70
+ const match = /^(?:"([^"]+)"|([A-Za-z0-9_-]+))\s*=\s*"([^"]+)"\s*$/.exec(trimmed);
71
+ if (!match) {
72
+ throw new Error(`Unsupported Template Cargo dependency declaration: ${trimmed}`);
73
+ }
74
+ const dependency = match[1] ?? match[2];
75
+ const version = match[3];
76
+ if (dependency === undefined || version === undefined) {
77
+ continue;
78
+ }
79
+ dependencies[dependency] = version;
80
+ }
81
+ return dependencies;
82
+ }
83
+ export function loadTemplateCargoDependencyVersions() {
84
+ return parseTemplateCargoDependencyVersions(readFileSync(path.join(templateRepositoryRoot(), "Cargo.toml"), "utf8"));
85
+ }
86
+ function loadTemplateCargoLock() {
87
+ return readFileSync(path.join(templateRepositoryRoot(), "Cargo.lock"), "utf8");
88
+ }
89
+ export function renderCargoLockForPackage(options) {
90
+ return loadTemplateCargoLock()
91
+ .replace('name = "template-cargo-dependencies"', `name = ${JSON.stringify(options.packageName)}`)
92
+ .replace('version = "0.0.0"', `version = ${JSON.stringify(options.packageVersion)}`);
93
+ }
94
+ export function selectTemplateCargoDependencyVersions(dependencies, catalog = loadTemplateCargoDependencyVersions()) {
95
+ const selected = {};
96
+ for (const dependency of dependencies.toSorted()) {
97
+ const version = catalog[dependency];
98
+ if (version === undefined) {
99
+ throw new Error(`Template Cargo dependency versions are missing dependency: ${dependency}`);
100
+ }
101
+ selected[dependency] = version;
102
+ }
103
+ return selected;
104
+ }
105
+ function renderTomlKey(key) {
106
+ return /^[A-Za-z0-9_-]+$/.test(key) ? key : JSON.stringify(key);
107
+ }
108
+ export function renderCargoDependencyTomlEntries(dependencies, catalog = loadTemplateCargoDependencyVersions()) {
109
+ return Object.entries(selectTemplateCargoDependencyVersions(dependencies, catalog)).map(([dependency, version]) => {
110
+ return `${renderTomlKey(dependency)} = ${JSON.stringify(version)}`;
111
+ });
112
+ }
52
113
  export function collectGeneratedManifestCatalogDependencies(manifests) {
53
114
  const dependencies = new Set();
54
115
  for (const manifest of manifests) {
@@ -1,7 +1,7 @@
1
1
  import { type CheckEnvironmentNeed } from "./module-graph.js";
2
2
  import type { PresetSourceManifest } from "./preset-source.js";
3
3
  import { type PresetProjectionSourceRoots } from "./projection-capabilities.js";
4
- export type GeneratedScenarioSet = "init" | "package-addition-matrix";
4
+ export type GeneratedScenarioSet = "init" | "package-addition-matrix" | "focused";
5
5
  export type GeneratedScenario = {
6
6
  readonly set: GeneratedScenarioSet;
7
7
  readonly basePreset: string;