From 74da1a4e06adc8759d95a022727e3f984ad747a9 Mon Sep 17 00:00:00 2001 From: Atila Fassina Date: Thu, 12 Nov 2026 22:42:05 +0100 Subject: [PATCH 1/2] fix(template): lint-ignore generated types in shared/appkit-types `appkit generate-types` and the Vite plugin write `shared/appkit-types/*.d.ts`. With no database schema or metric views, `database.d.ts` and `metric-views.d.ts` carry empty registry interfaces, which trip `typescript/no-empty-object-type` on a fresh scaffold. Generated files are overwritten on the next run, so ignore the directory rather than lint it. Also drop `async` from the three functions in `server/example.test.ts` that never await (`typescript/require-await`), so a fresh scaffold passes its own `lint` script. Refs LA-2385 Co-authored-by: Isaac Signed-off-by: Atila Fassina --- template/.oxlintrc.json | 4 +++- template/server/example.test.ts | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) 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(); }, }); } From dc02ea84507141eb464ed8d5a68b4e6b5431dc64 Mon Sep 17 00:00:00 2001 From: Atila Fassina Date: Thu, 12 Nov 2026 22:42:12 +0100 Subject: [PATCH 2/2] test(template): lint the whole scaffold after typegen in the smoke test After the build, write the empty-registry declarations typegen emits for an app with no database schema or metric views, then run the app's own `lint` script. Fails on the current main config with no-empty-object-type in shared/appkit-types. Refs LA-2385 Co-authored-by: Isaac Signed-off-by: Atila Fassina --- tools/template-smoke-runner.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) 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"));