diff --git a/template/.oxlintrc.json b/template/.oxlintrc.json index 4bca4d308..7efc703f5 100644 --- a/template/.oxlintrc.json +++ b/template/.oxlintrc.json @@ -239,6 +239,8 @@ "**/.next/**", "**/coverage/**", "client/dist/**", - "**/.databricks/**" + "**/.databricks/**", + // Written by `appkit generate-types` and the Vite plugin; edits are overwritten on the next run + "shared/appkit-types/**" ] } diff --git a/template/server/example.test.ts b/template/server/example.test.ts index 535f800fd..ecd14ca84 100644 --- a/template/server/example.test.ts +++ b/template/server/example.test.ts @@ -34,15 +34,16 @@ class GreeterPlugin extends Plugin { resources: { required: [], optional: [] }, } as PluginManifest<'greeter'>; - async setup() { + setup() { // Routes registered here are captured by createTestPluginContext().routes. this.context?.addRoute('get', '/hello', (_req, res) => { res.end(); }); + return Promise.resolve(); } // A stand-in for a streaming handler: yields SSE-style event objects. - async *greet(name: string) { + *greet(name: string) { yield { type: 'greeting_start', name }; yield { type: 'greeting_end', message: `Hello, ${name}!` }; } @@ -53,9 +54,10 @@ class GreeterPlugin extends Plugin { name: 'greet', method: 'post', path: '/greet', - handler: async (req, res) => { + handler: (req, res) => { const { name } = req.body as { name: string }; res.json({ message: `Hello, ${name}!` }); + return Promise.resolve(); }, }); } diff --git a/tools/template-smoke-runner.ts b/tools/template-smoke-runner.ts index c3d8d7dc6..52b366f38 100644 --- a/tools/template-smoke-runner.ts +++ b/tools/template-smoke-runner.ts @@ -1,7 +1,9 @@ import assert from "node:assert/strict"; import { execFileSync, spawn } from "node:child_process"; import { + existsSync, globSync, + mkdirSync, mkdtempSync, readFileSync, rmSync, @@ -15,6 +17,8 @@ import { setTimeout as delay } from "node:timers/promises"; import { parse } from "yaml"; +import { NEUTRAL_DATABASE_TYPES } from "../packages/appkit/src/type-generator/database/generate"; +import { generateMetricTypeDeclarations } from "../packages/appkit/src/type-generator/mv-registry/render-types"; import { checkTemplateLint } from "./template-lint-fixtures"; export type PackageManager = "npm" | "pnpm"; @@ -177,6 +181,18 @@ export async function runTemplateSmoke( run(process.execPath, ["--input-type=commonjs", "-e", nativeCheck]); checkTemplateLint(appDir, env); run(pm, ["run", "build"]); + // The build ran typegen. Add the declarations it writes for an app with no database schema + // or metric views (empty registry interfaces), then lint the app as a user would get it. + const typesDir = join(appDir, "shared/appkit-types"); + mkdirSync(typesDir, { recursive: true }); + for (const [file, content] of [ + ["database.d.ts", NEUTRAL_DATABASE_TYPES], + ["metric-views.d.ts", generateMetricTypeDeclarations([])], + ]) { + if (!existsSync(join(typesDir, file))) + writeFileSync(join(typesDir, file), content); + } + run(pm, ["run", "lint"]); const html = readFileSync(join(appDir, "client/dist/index.html"), "utf-8"); assert(readFileSync(join(appDir, "dist/server.js"), "utf-8").length > 0); const manifest = parse(readFileSync(join(appDir, "app.yaml"), "utf-8"));