@ykdz/template 0.0.21 → 0.0.22
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/node_modules/@ykdz/template-builtin-source/templates/preset-source.json +4 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/.github/dependabot.yml +4 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/.github/workflows/check.yml +8 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/db/drizzle/migrations/20260709120325_old_captain_flint/migration.sql +6 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/db/drizzle/migrations/20260709120325_old_captain_flint/snapshot.json +67 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/db/scripts/seed-example.ts +7 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/db/src/db.ts +2 -1
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/db/src/index.ts +1 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/db/src/queries/todos.ts +17 -1
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/db/src/readiness.ts +22 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/db/src/schema.ts +1 -1
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/db/src/seed/example.ts +23 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/db/test/todos.test.ts +2 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/web/+server.ts +2 -1
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/web/Dockerfile +47 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/web/Dockerfile.dockerignore +14 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/web/pages/index/+Page.vue +6 -2
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/web/playwright.config.ts +7 -7
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/web/scripts/run-playwright.ts +25 -6
- package/node_modules/@ykdz/template-builtin-source/templates/vike-app/web/server/app.ts +22 -11
- package/node_modules/@ykdz/template-core/dist/preset-projection.d.ts +2 -1
- package/node_modules/@ykdz/template-core/dist/project-github.d.ts +9 -0
- package/node_modules/@ykdz/template-core/dist/project-github.js +9 -3
- package/node_modules/@ykdz/template-core/dist/projection-capabilities.js +54 -6
- package/node_modules/@ykdz/template-core/dist/renderer.js +3 -0
- package/package.json +1 -1
|
@@ -251,6 +251,8 @@
|
|
|
251
251
|
"sourceFiles": [
|
|
252
252
|
"web/+server.ts",
|
|
253
253
|
"web/.env.example",
|
|
254
|
+
"web/Dockerfile",
|
|
255
|
+
"web/Dockerfile.dockerignore",
|
|
254
256
|
"web/assets/logo.svg",
|
|
255
257
|
"web/components/CounterButton.vue",
|
|
256
258
|
"web/components/PageShell.vue",
|
|
@@ -306,6 +308,8 @@
|
|
|
306
308
|
"vike-app/db/turbo.json",
|
|
307
309
|
"vike-app/web/+server.ts",
|
|
308
310
|
"vike-app/web/.env.example",
|
|
311
|
+
"vike-app/web/Dockerfile",
|
|
312
|
+
"vike-app/web/Dockerfile.dockerignore",
|
|
309
313
|
"vike-app/web/playwright.config.ts",
|
|
310
314
|
"vike-app/web/vite.config.ts",
|
|
311
315
|
"vike-app/web/turbo.json",
|
package/node_modules/@ykdz/template-builtin-source/templates/vike-app/.github/workflows/check.yml
CHANGED
|
@@ -18,3 +18,11 @@ jobs:
|
|
|
18
18
|
- run: pnpm install
|
|
19
19
|
- run: pnpm --filter ./apps/web exec playwright install --with-deps chromium
|
|
20
20
|
- run: pnpm run check
|
|
21
|
+
|
|
22
|
+
docker-image:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v7
|
|
26
|
+
- uses: docker/setup-buildx-action@v3
|
|
27
|
+
- run: docker buildx build --load --file apps/web/Dockerfile --target runtime --tag vike-app:check .
|
|
28
|
+
- run: docker buildx build --load --file apps/web/Dockerfile --target database-preparation --tag vike-app-db-prepare:check .
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "7",
|
|
3
|
+
"dialect": "sqlite",
|
|
4
|
+
"id": "0a8f7496-a1ed-412a-a634-7989609bcbe0",
|
|
5
|
+
"prevIds": ["00000000-0000-0000-0000-000000000000"],
|
|
6
|
+
"ddl": [
|
|
7
|
+
{
|
|
8
|
+
"name": "todos",
|
|
9
|
+
"entityType": "tables"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"type": "integer",
|
|
13
|
+
"notNull": false,
|
|
14
|
+
"autoincrement": true,
|
|
15
|
+
"default": null,
|
|
16
|
+
"generated": null,
|
|
17
|
+
"name": "id",
|
|
18
|
+
"entityType": "columns",
|
|
19
|
+
"table": "todos"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"type": "text",
|
|
23
|
+
"notNull": true,
|
|
24
|
+
"autoincrement": false,
|
|
25
|
+
"default": null,
|
|
26
|
+
"generated": null,
|
|
27
|
+
"name": "title",
|
|
28
|
+
"entityType": "columns",
|
|
29
|
+
"table": "todos"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"type": "integer",
|
|
33
|
+
"notNull": true,
|
|
34
|
+
"autoincrement": false,
|
|
35
|
+
"default": "false",
|
|
36
|
+
"generated": null,
|
|
37
|
+
"name": "completed",
|
|
38
|
+
"entityType": "columns",
|
|
39
|
+
"table": "todos"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"type": "text",
|
|
43
|
+
"notNull": true,
|
|
44
|
+
"autoincrement": false,
|
|
45
|
+
"default": "(CURRENT_TIMESTAMP)",
|
|
46
|
+
"generated": null,
|
|
47
|
+
"name": "created_at",
|
|
48
|
+
"entityType": "columns",
|
|
49
|
+
"table": "todos"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"columns": ["id"],
|
|
53
|
+
"nameExplicit": false,
|
|
54
|
+
"name": "todos_pk",
|
|
55
|
+
"table": "todos",
|
|
56
|
+
"entityType": "pks"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"columns": ["title"],
|
|
60
|
+
"nameExplicit": false,
|
|
61
|
+
"name": "todos_title_unique",
|
|
62
|
+
"entityType": "uniques",
|
|
63
|
+
"table": "todos"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"renames": []
|
|
67
|
+
}
|
|
@@ -7,11 +7,12 @@ import { defineRelations } from "drizzle-orm/relations";
|
|
|
7
7
|
import * as schema from "#db/schema";
|
|
8
8
|
|
|
9
9
|
const relations = defineRelations(schema);
|
|
10
|
+
const defaultDatabaseFile = "./data/app.sqlite";
|
|
10
11
|
|
|
11
12
|
export type Database = ReturnType<typeof createDatabase>;
|
|
12
13
|
|
|
13
14
|
export function createDatabase(databaseFile = process.env.DATABASE_FILE) {
|
|
14
|
-
const file = databaseFile ??
|
|
15
|
+
const file = databaseFile ?? defaultDatabaseFile;
|
|
15
16
|
mkdirSync(path.dirname(file), { recursive: true });
|
|
16
17
|
return drizzle(file, { relations });
|
|
17
18
|
}
|
package/node_modules/@ykdz/template-builtin-source/templates/vike-app/db/src/queries/todos.ts
CHANGED
|
@@ -3,8 +3,20 @@ import { desc, eq } from "drizzle-orm";
|
|
|
3
3
|
import type { Database } from "#db/db";
|
|
4
4
|
import { todos, type NewTodo } from "#db/schema";
|
|
5
5
|
|
|
6
|
+
const listTodosStatements = new WeakMap<
|
|
7
|
+
Database,
|
|
8
|
+
ReturnType<typeof prepareListTodos>
|
|
9
|
+
>();
|
|
10
|
+
|
|
6
11
|
export async function listTodos(db: Database) {
|
|
7
|
-
|
|
12
|
+
let statement = listTodosStatements.get(db);
|
|
13
|
+
|
|
14
|
+
if (!statement) {
|
|
15
|
+
statement = prepareListTodos(db);
|
|
16
|
+
listTodosStatements.set(db, statement);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return statement.all();
|
|
8
20
|
}
|
|
9
21
|
|
|
10
22
|
export async function createTodo(db: Database, values: NewTodo) {
|
|
@@ -26,3 +38,7 @@ export async function completeTodo(db: Database, id: number) {
|
|
|
26
38
|
|
|
27
39
|
return todo;
|
|
28
40
|
}
|
|
41
|
+
|
|
42
|
+
function prepareListTodos(db: Database) {
|
|
43
|
+
return db.select().from(todos).orderBy(desc(todos.createdAt)).prepare();
|
|
44
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { sql } from "drizzle-orm";
|
|
2
|
+
|
|
3
|
+
import type { Database } from "#db/db";
|
|
4
|
+
import { todos } from "#db/schema";
|
|
5
|
+
|
|
6
|
+
export function assertDatabaseReady(db: Database) {
|
|
7
|
+
try {
|
|
8
|
+
db.select({ ready: sql`1` })
|
|
9
|
+
.from(todos)
|
|
10
|
+
.limit(1)
|
|
11
|
+
.all();
|
|
12
|
+
} catch (cause) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
[
|
|
15
|
+
"Database is not ready.",
|
|
16
|
+
"For local development, run `pnpm --dir packages/db run db:prepare:dev`.",
|
|
17
|
+
"For deployment, run `pnpm --dir packages/db run db:prepare:deploy` before starting the app.",
|
|
18
|
+
].join(" "),
|
|
19
|
+
{ cause },
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -3,7 +3,7 @@ import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
|
|
3
3
|
|
|
4
4
|
export const todos = sqliteTable("todos", {
|
|
5
5
|
id: integer({ mode: "number" }).primaryKey({ autoIncrement: true }),
|
|
6
|
-
title: text().notNull(),
|
|
6
|
+
title: text().notNull().unique(),
|
|
7
7
|
completed: integer({ mode: "boolean" }).default(false).notNull(),
|
|
8
8
|
createdAt: text("created_at")
|
|
9
9
|
.default(sql`(CURRENT_TIMESTAMP)`)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
2
|
+
|
|
3
|
+
import type { Database } from "#db/db";
|
|
4
|
+
import { todos } from "#db/schema";
|
|
5
|
+
|
|
6
|
+
const exampleTodos = [
|
|
7
|
+
"Read the generated TODO.md",
|
|
8
|
+
"Try the Telefunc todo flow",
|
|
9
|
+
] as const;
|
|
10
|
+
|
|
11
|
+
export async function seedExampleData(db: Database) {
|
|
12
|
+
for (const title of exampleTodos) {
|
|
13
|
+
const existing = await db
|
|
14
|
+
.select({ id: todos.id })
|
|
15
|
+
.from(todos)
|
|
16
|
+
.where(eq(todos.title, title))
|
|
17
|
+
.limit(1);
|
|
18
|
+
|
|
19
|
+
if (existing.length === 0) {
|
|
20
|
+
await db.insert(todos).values({ title });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -2,10 +2,12 @@ import { describe, expect, it } from "vitest";
|
|
|
2
2
|
|
|
3
3
|
import { createDatabase } from "#db/db";
|
|
4
4
|
import { createTodo, listTodos } from "#db/queries/todos";
|
|
5
|
+
import { assertDatabaseReady } from "#db/readiness";
|
|
5
6
|
|
|
6
7
|
describe("todo queries", () => {
|
|
7
8
|
it("creates and lists todos through Drizzle SQLite", async () => {
|
|
8
9
|
const db = createDatabase();
|
|
10
|
+
assertDatabaseReady(db);
|
|
9
11
|
const title = `发布模板 ${Date.now()}`;
|
|
10
12
|
|
|
11
13
|
await expect(createTodo(db, { title })).resolves.toMatchObject({
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1.7
|
|
2
|
+
|
|
3
|
+
FROM node:{{NODE_VERSION}}-bookworm-slim AS base
|
|
4
|
+
ARG PACKAGE_MANAGER_PIN="{{PACKAGE_MANAGER_PIN}}"
|
|
5
|
+
ENV PNPM_HOME="/pnpm"
|
|
6
|
+
ENV PATH="$PNPM_HOME:$PATH"
|
|
7
|
+
WORKDIR /repo
|
|
8
|
+
RUN corepack enable && corepack prepare "$PACKAGE_MANAGER_PIN" --activate
|
|
9
|
+
|
|
10
|
+
FROM base AS pruner
|
|
11
|
+
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
12
|
+
RUN pnpm fetch
|
|
13
|
+
COPY package.json turbo.json ./
|
|
14
|
+
COPY apps/web/package.json apps/web/package.json
|
|
15
|
+
COPY packages/db/package.json packages/db/package.json
|
|
16
|
+
RUN pnpm install --offline --frozen-lockfile
|
|
17
|
+
COPY . .
|
|
18
|
+
RUN pnpm exec turbo prune {{WEB_PACKAGE_NAME}} --docker
|
|
19
|
+
|
|
20
|
+
FROM base AS build
|
|
21
|
+
COPY --from=pruner /repo/out/json/pnpm-lock.yaml ./pnpm-lock.yaml
|
|
22
|
+
COPY --from=pruner /repo/out/json/pnpm-workspace.yaml ./pnpm-workspace.yaml
|
|
23
|
+
RUN pnpm fetch
|
|
24
|
+
COPY --from=pruner /repo/out/json/ ./
|
|
25
|
+
RUN pnpm install --offline --frozen-lockfile
|
|
26
|
+
COPY --from=pruner /repo/out/full/ ./
|
|
27
|
+
RUN pnpm --filter ./apps/web run build:run
|
|
28
|
+
|
|
29
|
+
FROM base AS database-preparation
|
|
30
|
+
ENV NODE_ENV="production"
|
|
31
|
+
COPY --from=pruner /repo/out/json/pnpm-lock.yaml ./pnpm-lock.yaml
|
|
32
|
+
COPY --from=pruner /repo/out/json/pnpm-workspace.yaml ./pnpm-workspace.yaml
|
|
33
|
+
RUN pnpm fetch
|
|
34
|
+
COPY --from=pruner /repo/out/json/ ./
|
|
35
|
+
RUN pnpm install --offline --frozen-lockfile
|
|
36
|
+
COPY --from=pruner /repo/out/full/ ./
|
|
37
|
+
CMD ["pnpm", "--dir", "packages/db", "run", "db:prepare:deploy"]
|
|
38
|
+
|
|
39
|
+
FROM node:{{NODE_VERSION}}-bookworm-slim AS runtime
|
|
40
|
+
ENV NODE_ENV="production"
|
|
41
|
+
WORKDIR /app
|
|
42
|
+
RUN groupadd --system --gid 1001 nodejs \
|
|
43
|
+
&& useradd --system --uid 1001 --gid nodejs --home-dir /app --shell /usr/sbin/nologin app
|
|
44
|
+
COPY --from=build --chown=app:nodejs /repo/apps/web/dist ./dist
|
|
45
|
+
USER app
|
|
46
|
+
EXPOSE 3000
|
|
47
|
+
CMD ["node", "dist/server/index.mjs"]
|
package/node_modules/@ykdz/template-builtin-source/templates/vike-app/web/pages/index/+Page.vue
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { ref } from "vue";
|
|
2
|
+
import { onMounted, ref } from "vue";
|
|
3
3
|
|
|
4
4
|
import CounterButton from "#/components/CounterButton.vue";
|
|
5
5
|
|
|
@@ -32,6 +32,10 @@ async function submitTodo() {
|
|
|
32
32
|
title.value = "";
|
|
33
33
|
await refreshTodos();
|
|
34
34
|
}
|
|
35
|
+
|
|
36
|
+
onMounted(() => {
|
|
37
|
+
void refreshTodos();
|
|
38
|
+
});
|
|
35
39
|
</script>
|
|
36
40
|
|
|
37
41
|
<template>
|
|
@@ -65,7 +69,7 @@ async function submitTodo() {
|
|
|
65
69
|
type="button"
|
|
66
70
|
@click="refreshTodos"
|
|
67
71
|
>
|
|
68
|
-
|
|
72
|
+
刷新待办事项
|
|
69
73
|
</button>
|
|
70
74
|
|
|
71
75
|
<p
|
package/node_modules/@ykdz/template-builtin-source/templates/vike-app/web/playwright.config.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { fileURLToPath } from "node:url";
|
|
2
|
-
|
|
3
1
|
import { defineConfig, devices } from "@playwright/test";
|
|
4
2
|
|
|
5
3
|
function requiredPort(name: string): number {
|
|
4
|
+
return Number(requiredEnv(name));
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function requiredEnv(name: string): string {
|
|
6
8
|
const value = process.env[name];
|
|
7
9
|
|
|
8
10
|
if (!value) {
|
|
9
11
|
throw new Error(`${name} must be set by scripts/run-playwright.ts`);
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
return
|
|
14
|
+
return value;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
const previewPort = requiredPort("PLAYWRIGHT_WEB_PORT");
|
|
16
18
|
const previewUrl = `http://127.0.0.1:${previewPort}`;
|
|
17
|
-
const databaseFile =
|
|
18
|
-
new URL("./node_modules/.tmp/e2e.sqlite", import.meta.url),
|
|
19
|
-
);
|
|
19
|
+
const databaseFile = requiredEnv("DATABASE_FILE");
|
|
20
20
|
|
|
21
21
|
function shellValue(value: string): string {
|
|
22
22
|
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
@@ -30,7 +30,7 @@ export default defineConfig({
|
|
|
30
30
|
trace: "on-first-retry",
|
|
31
31
|
},
|
|
32
32
|
webServer: {
|
|
33
|
-
command: `DATABASE_FILE=${shellValue(databaseFile)} pnpm --dir ../../packages/db run db:
|
|
33
|
+
command: `DATABASE_FILE=${shellValue(databaseFile)} pnpm --dir ../../packages/db run db:prepare:test && PORT=${previewPort} node dist/server/index.mjs`,
|
|
34
34
|
reuseExistingServer: !process.env.CI,
|
|
35
35
|
url: previewUrl,
|
|
36
36
|
},
|
package/node_modules/@ykdz/template-builtin-source/templates/vike-app/web/scripts/run-playwright.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { rm } from "node:fs/promises";
|
|
2
3
|
import { createServer } from "node:net";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const databaseFile = fileURLToPath(
|
|
7
|
+
new URL("../node_modules/.tmp/e2e.sqlite", import.meta.url),
|
|
8
|
+
);
|
|
3
9
|
|
|
4
10
|
async function availablePort(): Promise<string> {
|
|
5
11
|
return await new Promise((resolve, reject) => {
|
|
@@ -32,6 +38,7 @@ async function availablePort(): Promise<string> {
|
|
|
32
38
|
async function playwrightEnv(): Promise<NodeJS.ProcessEnv> {
|
|
33
39
|
return {
|
|
34
40
|
...process.env,
|
|
41
|
+
DATABASE_FILE: process.env.DATABASE_FILE ?? databaseFile,
|
|
35
42
|
PLAYWRIGHT_WEB_PORT:
|
|
36
43
|
process.env.PLAYWRIGHT_WEB_PORT ?? (await availablePort()),
|
|
37
44
|
};
|
|
@@ -39,6 +46,7 @@ async function playwrightEnv(): Promise<NodeJS.ProcessEnv> {
|
|
|
39
46
|
|
|
40
47
|
const env = await playwrightEnv();
|
|
41
48
|
delete env.NO_COLOR;
|
|
49
|
+
await rm(env.DATABASE_FILE!, { force: true });
|
|
42
50
|
|
|
43
51
|
const command = process.platform === "win32" ? "playwright.cmd" : "playwright";
|
|
44
52
|
const child = spawn(command, ["test", ...process.argv.slice(2)], {
|
|
@@ -52,11 +60,22 @@ child.once("error", (error: Error) => {
|
|
|
52
60
|
});
|
|
53
61
|
|
|
54
62
|
child.once("exit", (code: number | null, signal: NodeJS.Signals | null) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
void rm(env.DATABASE_FILE!, { force: true }).finally(() => {
|
|
64
|
+
if (signal) {
|
|
65
|
+
console.error(`Playwright exited with signal ${signal}`);
|
|
66
|
+
process.exitCode = 1;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
60
69
|
|
|
61
|
-
|
|
70
|
+
process.exitCode = code ?? 1;
|
|
71
|
+
});
|
|
62
72
|
});
|
|
73
|
+
|
|
74
|
+
for (const signal of ["SIGINT", "SIGTERM"] as const) {
|
|
75
|
+
process.once(signal, () => {
|
|
76
|
+
child.kill(signal);
|
|
77
|
+
void rm(env.DATABASE_FILE!, { force: true }).finally(() => {
|
|
78
|
+
process.exitCode = 1;
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
@@ -7,19 +7,30 @@ import { api } from "#/server/api";
|
|
|
7
7
|
|
|
8
8
|
const telefunc = new Telefunc();
|
|
9
9
|
|
|
10
|
-
export
|
|
10
|
+
export function createApp() {
|
|
11
|
+
try {
|
|
12
|
+
assertDatabaseReady(createDatabase());
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.error(error);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
11
17
|
|
|
12
|
-
app
|
|
18
|
+
const app = new Hono();
|
|
13
19
|
|
|
14
|
-
app.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
app.route("/api", api);
|
|
21
|
+
|
|
22
|
+
app.all("/_telefunc", async (c) => {
|
|
23
|
+
const response = await telefunc.serve({
|
|
24
|
+
request: c.req.raw,
|
|
25
|
+
context: {
|
|
26
|
+
db: createDatabase(),
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return response ?? c.notFound();
|
|
20
31
|
});
|
|
21
32
|
|
|
22
|
-
|
|
23
|
-
});
|
|
33
|
+
vike(app);
|
|
24
34
|
|
|
25
|
-
|
|
35
|
+
return app;
|
|
36
|
+
}
|
|
@@ -2,7 +2,7 @@ import type { BuiltInPreset, PackageRole, PackageSourcePreset, ProjectBlueprint
|
|
|
2
2
|
import type { GenerationContext } from "./generation-context.js";
|
|
3
3
|
import { type CheckPlan, type FixPlan } from "./module-graph.js";
|
|
4
4
|
import { type NextStepInstruction } from "./next-step-instructions.js";
|
|
5
|
-
import type { DependencyMaintenancePolicy } from "./project-github.js";
|
|
5
|
+
import type { CiDockerImageBuild, DependencyMaintenancePolicy } from "./project-github.js";
|
|
6
6
|
import type { RenderOperation } from "./renderer.js";
|
|
7
7
|
export type PresetPackageAdditionOptions = {
|
|
8
8
|
readonly root: string;
|
|
@@ -35,6 +35,7 @@ export type PresetProjectionPlan = {
|
|
|
35
35
|
readonly operations: readonly RenderOperation[];
|
|
36
36
|
readonly checkPlan: CheckPlan;
|
|
37
37
|
readonly fixPlan: FixPlan;
|
|
38
|
+
readonly ciDockerImageBuild?: CiDockerImageBuild | undefined;
|
|
38
39
|
readonly dependencyMaintenancePolicy: DependencyMaintenancePolicy;
|
|
39
40
|
readonly packageScripts: Record<string, string>;
|
|
40
41
|
readonly capabilities: {
|
|
@@ -12,11 +12,19 @@ export type PnpmTaskLayer = {
|
|
|
12
12
|
readonly installCommand: "pnpm install";
|
|
13
13
|
readonly checkCommand: "pnpm run check";
|
|
14
14
|
};
|
|
15
|
+
export type CiDockerImageBuild = {
|
|
16
|
+
readonly dockerfile: string;
|
|
17
|
+
readonly targets: readonly {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly tag: string;
|
|
20
|
+
}[];
|
|
21
|
+
};
|
|
15
22
|
export type DependencyEcosystem = "npm" | "cargo" | "github-actions" | "docker" | "rust-toolchain";
|
|
16
23
|
export type DependabotDirectory = `/${string}`;
|
|
17
24
|
export type DependencyMaintenancePolicy = {
|
|
18
25
|
readonly ecosystems: DependencyEcosystem[];
|
|
19
26
|
readonly directories?: Partial<Record<DependencyEcosystem, DependabotDirectory>>;
|
|
27
|
+
readonly extraDirectories?: Partial<Record<DependencyEcosystem, readonly DependabotDirectory[]>>;
|
|
20
28
|
readonly interval: "weekly";
|
|
21
29
|
};
|
|
22
30
|
type ProjectCheckWorkflowOptions = {
|
|
@@ -24,6 +32,7 @@ type ProjectCheckWorkflowOptions = {
|
|
|
24
32
|
readonly capability?: CiCapability | undefined;
|
|
25
33
|
readonly environmentPreparation?: Partial<CiEnvironmentPreparation> | undefined;
|
|
26
34
|
readonly taskLayer?: PnpmTaskLayer | undefined;
|
|
35
|
+
readonly dockerImageBuild?: CiDockerImageBuild | undefined;
|
|
27
36
|
};
|
|
28
37
|
export declare function projectCheckWorkflow(options: ProjectCheckWorkflowOptions): string;
|
|
29
38
|
export declare function projectDependabotConfig(policy: DependencyMaintenancePolicy): string;
|
|
@@ -40,6 +40,9 @@ export function projectCheckWorkflow(options) {
|
|
|
40
40
|
lines.push(` - run: ${taskLayer.installCommand}`);
|
|
41
41
|
lines.push(...options.checkPlan.environmentNeeds.map((need) => ` - run: ${renderCiEnvironmentNeedCommand(need)}`));
|
|
42
42
|
lines.push(` - run: ${taskLayer.checkCommand}`, "");
|
|
43
|
+
if (options.dockerImageBuild !== undefined) {
|
|
44
|
+
lines.push(" docker-image:", ` runs-on: ${capability.runner}`, " steps:", " - uses: actions/checkout@v7", " - uses: docker/setup-buildx-action@v3", ...options.dockerImageBuild.targets.map((target) => ` - run: docker buildx build --load --file ${options.dockerImageBuild.dockerfile} --target ${target.name} --tag ${target.tag} .`), "");
|
|
45
|
+
}
|
|
43
46
|
return lines.join("\n");
|
|
44
47
|
}
|
|
45
48
|
export function projectDependabotConfig(policy) {
|
|
@@ -47,8 +50,11 @@ export function projectDependabotConfig(policy) {
|
|
|
47
50
|
"version: 2",
|
|
48
51
|
"",
|
|
49
52
|
"updates:",
|
|
50
|
-
...policy.ecosystems.flatMap((ecosystem) =>
|
|
51
|
-
|
|
53
|
+
...policy.ecosystems.flatMap((ecosystem) => [
|
|
54
|
+
policy.directories?.[ecosystem] ??
|
|
55
|
+
defaultDependabotDirectory(ecosystem),
|
|
56
|
+
...(policy.extraDirectories?.[ecosystem] ?? []),
|
|
57
|
+
].flatMap((directory) => renderDependabotUpdate(ecosystem, directory, policy.interval))),
|
|
52
58
|
"",
|
|
53
59
|
].join("\n");
|
|
54
60
|
}
|
|
@@ -62,7 +68,7 @@ function renderDependabotUpdate(ecosystem, directory, interval) {
|
|
|
62
68
|
if (ecosystem === "npm") {
|
|
63
69
|
lines.push(" groups:", " drizzle:", " patterns:", ' - "drizzle-*"', ' - "drizzle-orm"', " ignore:", ' - dependency-name: "@types/node"', " update-types:", " - version-update:semver-major");
|
|
64
70
|
}
|
|
65
|
-
if (ecosystem === "docker") {
|
|
71
|
+
if (ecosystem === "docker" && directory === "/.devcontainer") {
|
|
66
72
|
lines.push(" ignore:", " - dependency-name: mcr.microsoft.com/devcontainers/typescript-node", " update-types:", " - version-update:semver-major");
|
|
67
73
|
}
|
|
68
74
|
return lines;
|
|
@@ -77,6 +77,19 @@ const capabilityInterpreters = {
|
|
|
77
77
|
state.nodeWorkspace = capability;
|
|
78
78
|
state.sourceRoot = templateSourceRootForPreset(state, sourceRootPreset(capability));
|
|
79
79
|
state.operationFactories.push(workspaceNodePackagesOperations);
|
|
80
|
+
if (hasVikePackage(capability)) {
|
|
81
|
+
state.dependencyMaintenanceExtraDirectories.docker = ["/apps/web"];
|
|
82
|
+
state.ciDockerImageBuild = {
|
|
83
|
+
dockerfile: "apps/web/Dockerfile",
|
|
84
|
+
targets: [
|
|
85
|
+
{ name: "runtime", tag: "vike-app:check" },
|
|
86
|
+
{
|
|
87
|
+
name: "database-preparation",
|
|
88
|
+
tag: "vike-app-db-prepare:check",
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
80
93
|
if (capability.packages.length > 1) {
|
|
81
94
|
state.rootScriptFragments.dev = "turbo run dev --parallel";
|
|
82
95
|
}
|
|
@@ -285,6 +298,7 @@ export function interpretPresetProjectionDeclaration(options) {
|
|
|
285
298
|
const packageScriptsByPath = packageScriptsByWorkspacePath(state);
|
|
286
299
|
const dependencyMaintenancePolicy = {
|
|
287
300
|
ecosystems: uniqueValues(state.dependencyMaintenanceEcosystems),
|
|
301
|
+
extraDirectories: state.dependencyMaintenanceExtraDirectories,
|
|
288
302
|
...(state.rustWorkspace === undefined
|
|
289
303
|
? {}
|
|
290
304
|
: {
|
|
@@ -308,6 +322,7 @@ export function interpretPresetProjectionDeclaration(options) {
|
|
|
308
322
|
})),
|
|
309
323
|
checkPlan,
|
|
310
324
|
fixPlan,
|
|
325
|
+
ciDockerImageBuild: state.ciDockerImageBuild,
|
|
311
326
|
dependencyMaintenancePolicy,
|
|
312
327
|
packageScripts,
|
|
313
328
|
capabilities: completePlanCapabilityFlags(state.flags),
|
|
@@ -392,6 +407,8 @@ function createProjectionCompositionState(sourceRoots) {
|
|
|
392
407
|
packageDependencies: new Set(),
|
|
393
408
|
packageDevDependencies: new Set(),
|
|
394
409
|
dependencyMaintenanceEcosystems: [],
|
|
410
|
+
dependencyMaintenanceExtraDirectories: {},
|
|
411
|
+
ciDockerImageBuild: undefined,
|
|
395
412
|
editorCustomizationCapabilities: [],
|
|
396
413
|
operationFactories: [],
|
|
397
414
|
flags: {},
|
|
@@ -1110,6 +1127,10 @@ function vikeDbPackageJson(context) {
|
|
|
1110
1127
|
default: "./src/queries/todos.ts",
|
|
1111
1128
|
types: "./src/queries/todos.ts",
|
|
1112
1129
|
},
|
|
1130
|
+
"./readiness": {
|
|
1131
|
+
default: "./src/readiness.ts",
|
|
1132
|
+
types: "./src/readiness.ts",
|
|
1133
|
+
},
|
|
1113
1134
|
},
|
|
1114
1135
|
dependencies: {
|
|
1115
1136
|
"drizzle-orm": "catalog:",
|
|
@@ -1132,15 +1153,19 @@ function vikeDbPackageJson(context) {
|
|
|
1132
1153
|
function vikeDbPackageScripts() {
|
|
1133
1154
|
return {
|
|
1134
1155
|
"build:run": "tsc -p tsconfig.json --noEmit",
|
|
1156
|
+
"db:generate": "drizzle-kit generate",
|
|
1157
|
+
"db:migrate": "drizzle-kit migrate",
|
|
1158
|
+
"db:prepare:deploy": "pnpm run db:migrate",
|
|
1159
|
+
"db:prepare:dev": "pnpm run db:push && pnpm run db:seed:example",
|
|
1160
|
+
"db:prepare:test": 'rm -f "${DATABASE_FILE:-./node_modules/.tmp/test.sqlite}" && pnpm run db:push && pnpm run db:seed:example',
|
|
1135
1161
|
"db:push": "mkdir -p data node_modules/.tmp && drizzle-kit push",
|
|
1136
|
-
"
|
|
1137
|
-
"
|
|
1138
|
-
"drizzle:studio": "drizzle-kit studio",
|
|
1162
|
+
"db:seed:example": "node --experimental-strip-types scripts/seed-example.ts",
|
|
1163
|
+
"db:studio": "drizzle-kit studio",
|
|
1139
1164
|
"format:check:run": "oxfmt --list-different --config ../../oxfmt.config.ts .",
|
|
1140
1165
|
"format:write:run": "oxfmt --write --config ../../oxfmt.config.ts .",
|
|
1141
1166
|
"lint:run": "oxlint --quiet --format=unix --config ../../oxlint.config.ts .",
|
|
1142
1167
|
"lint:fix:run": "oxlint --format=unix --config ../../oxlint.config.ts . --fix",
|
|
1143
|
-
"test:run": "DATABASE_FILE=./node_modules/.tmp/test.sqlite pnpm run db:
|
|
1168
|
+
"test:run": "DATABASE_FILE=./node_modules/.tmp/test.sqlite pnpm run db:prepare:test && DATABASE_FILE=./node_modules/.tmp/test.sqlite vitest run --reporter=agent --silent=passed-only; status=$?; rm -f ./node_modules/.tmp/test.sqlite; exit $status",
|
|
1144
1169
|
"typecheck:run": "tsc -p tsconfig.json --noEmit --pretty false",
|
|
1145
1170
|
};
|
|
1146
1171
|
}
|
|
@@ -1565,7 +1590,7 @@ function nodePackageScripts(nodePackage, workspace) {
|
|
|
1565
1590
|
};
|
|
1566
1591
|
}
|
|
1567
1592
|
if (nodePackage.kind === "vike-app") {
|
|
1568
|
-
const dbCommand = "DATABASE_FILE=../../apps/web/data/app.sqlite pnpm --dir ../../packages/db run db:
|
|
1593
|
+
const dbCommand = "DATABASE_FILE=../../apps/web/data/app.sqlite pnpm --dir ../../packages/db run db:prepare:dev";
|
|
1569
1594
|
return {
|
|
1570
1595
|
"build:run": "vike build",
|
|
1571
1596
|
dev: `${dbCommand} && vike dev`,
|
|
@@ -1836,6 +1861,21 @@ function vikeTemplateSourceOperation(options) {
|
|
|
1836
1861
|
from: options.sourceFile,
|
|
1837
1862
|
to,
|
|
1838
1863
|
};
|
|
1864
|
+
if (options.nodePackage.kind === "vike-app" &&
|
|
1865
|
+
options.sourceFile === "web/Dockerfile") {
|
|
1866
|
+
return [
|
|
1867
|
+
{
|
|
1868
|
+
kind: "writeTextTemplate",
|
|
1869
|
+
from: options.sourceFile,
|
|
1870
|
+
to,
|
|
1871
|
+
replacements: {
|
|
1872
|
+
NODE_VERSION: options.context.toolchain.nodeLtsMajor.value,
|
|
1873
|
+
PACKAGE_MANAGER_PIN: options.context.toolchain.packageManagerPin.value,
|
|
1874
|
+
WEB_PACKAGE_NAME: nodePackageName(options.context, options.nodePackage),
|
|
1875
|
+
},
|
|
1876
|
+
},
|
|
1877
|
+
];
|
|
1878
|
+
}
|
|
1839
1879
|
if (options.nodePackage.kind === "vike-app" &&
|
|
1840
1880
|
[
|
|
1841
1881
|
"web/pages/index/+Page.telefunc.ts",
|
|
@@ -1845,7 +1885,10 @@ function vikeTemplateSourceOperation(options) {
|
|
|
1845
1885
|
const dbPackageName = vikeDbPackageName(options.context);
|
|
1846
1886
|
const replacements = {
|
|
1847
1887
|
"web/pages/index/+Page.telefunc.ts": `import { createTodo, listTodos } from "${dbPackageName}/queries/todos";`,
|
|
1848
|
-
"web/server/app.ts":
|
|
1888
|
+
"web/server/app.ts": [
|
|
1889
|
+
`import { createDatabase } from "${dbPackageName}";`,
|
|
1890
|
+
`import { assertDatabaseReady } from "${dbPackageName}/readiness";`,
|
|
1891
|
+
].join("\n"),
|
|
1849
1892
|
"web/types/global.d.ts": `import type { Database } from "${dbPackageName}";`,
|
|
1850
1893
|
};
|
|
1851
1894
|
return [
|
|
@@ -1865,9 +1908,14 @@ function vikeTemplateSourceOperation(options) {
|
|
|
1865
1908
|
const vikeDbSourceFiles = [
|
|
1866
1909
|
"db/turbo.json",
|
|
1867
1910
|
"db/drizzle.config.ts",
|
|
1911
|
+
"db/drizzle/migrations/20260709120325_old_captain_flint/migration.sql",
|
|
1912
|
+
"db/drizzle/migrations/20260709120325_old_captain_flint/snapshot.json",
|
|
1913
|
+
"db/scripts/seed-example.ts",
|
|
1868
1914
|
"db/src/db.ts",
|
|
1869
1915
|
"db/src/index.ts",
|
|
1870
1916
|
"db/src/queries/todos.ts",
|
|
1917
|
+
"db/src/readiness.ts",
|
|
1918
|
+
"db/src/seed/example.ts",
|
|
1871
1919
|
"db/src/schema.ts",
|
|
1872
1920
|
"db/test/todos.test.ts",
|
|
1873
1921
|
];
|
|
@@ -216,6 +216,9 @@ function assertTextTemplatePath(relativePath) {
|
|
|
216
216
|
if (/^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+\/(?:playwright|vite|vitest|oxfmt|oxlint)\.config\.ts$/.test(normalizedPath)) {
|
|
217
217
|
return;
|
|
218
218
|
}
|
|
219
|
+
if (/^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+\/Dockerfile$/.test(normalizedPath)) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
219
222
|
assertFoundationTextPath(relativePath);
|
|
220
223
|
}
|
|
221
224
|
async function renderWriteText(operation, options) {
|