From f0886be90b21b5014e83a9651cccad111b0b77fb Mon Sep 17 00:00:00 2001 From: Pawel Kosiec Date: Tue, 17 Mar 2026 20:13:46 +0100 Subject: [PATCH] chore: generate appKitTypes.d.ts during template post-processing When DATABRICKS_WAREHOUSE_ID is not set, `appkit generate-types` exits early without writing the file. This leaves templates with analytics queries missing appKitTypes.d.ts entirely, causing TS build errors. Add a fixAppKitTypes() post-processing step that either patches `result: unknown` in an existing file, or generates the file from scratch by scanning SQL files and using known result types. Co-authored-by: Isaac --- tools/generate-app-templates.ts | 95 ++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/tools/generate-app-templates.ts b/tools/generate-app-templates.ts index d429ea59..1e16619f 100644 --- a/tools/generate-app-templates.ts +++ b/tools/generate-app-templates.ts @@ -20,11 +20,12 @@ import { spawnSync } from "node:child_process"; import { existsSync, mkdirSync, + readdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs"; -import { join, resolve } from "node:path"; +import { basename, join, resolve } from "node:path"; const ROOT = resolve(import.meta.dirname, ".."); @@ -39,6 +40,17 @@ const DATABRICKS_CLI = process.env.DATABRICKS_CLI ?? "databricks"; const APPKIT_SECTION_START = ""; const APPKIT_SECTION_END = ""; +/** + * Known query result types for template SQL files. + * The type generator cannot infer these without a warehouse connection, + * so we hardcode them here to fix up `result: unknown` in appKitTypes.d.ts. + */ +const QUERY_RESULT_TYPES: Record = { + hello_world: "Array<{ value: string }>", + mocked_sales: + "Array<{ month: string; month_num: number; revenue: number; expenses: number; customers: number }>", +}; + interface AppTemplate { /** Output directory name and --name passed to databricks apps init */ name: string; @@ -191,6 +203,87 @@ function postProcess(appDir: string, app: AppTemplate): void { "host: https://your-workspace.cloud.databricks.com", ); writeFileSync(databricksYmlPath, fixedYml); + + // 4. Fix `result: unknown` in appKitTypes.d.ts — the type generator falls back + // to `unknown` when no warehouse is available during template generation. + fixAppKitTypes(appDir); +} + +/** + * Ensures appKitTypes.d.ts has correct result types. When no warehouse is + * available, the type generator either exits early (file missing) or falls + * back to `result: unknown`. This function handles both cases. + */ +function fixAppKitTypes(appDir: string): void { + const typesPath = join(appDir, "client/src/appKitTypes.d.ts"); + const queriesDir = join(appDir, "config/queries"); + + if (!existsSync(queriesDir)) return; + + if (existsSync(typesPath)) { + // Patch existing file: replace `result: unknown` with known types + let content = readFileSync(typesPath, "utf-8"); + let replaced = false; + + for (const [queryName, resultType] of Object.entries(QUERY_RESULT_TYPES)) { + const pattern = new RegExp( + `(${queryName}:\\s*\\{[\\s\\S]*?)result:\\s*unknown;`, + ); + if (pattern.test(content)) { + content = content.replace(pattern, `$1result: ${resultType};`); + replaced = true; + } + } + + if (replaced) { + writeFileSync(typesPath, content); + console.log(` Fixed appKitTypes.d.ts result types`); + } + } else { + // File missing (no warehouse ID → type generator exited early). + // Generate it from scratch by scanning SQL files. + const sqlFiles = readdirSync(queriesDir).filter((f) => f.endsWith(".sql")); + if (sqlFiles.length === 0) return; + + const queryEntries = sqlFiles.map((file) => { + const queryName = basename(file, ".sql"); + const sql = readFileSync(join(queriesDir, file), "utf-8"); + + // Extract :paramName placeholders (same regex as the real type generator) + const params = [ + ...new Set([...sql.matchAll(/:([a-zA-Z_]\w*)/g)].map((m) => m[1])), + ]; + + const paramLines = params + .map( + (p) => + ` /** any - use sql.*() */\n ${p}: SQLTypeMarker;`, + ) + .join("\n"); + const paramSection = params.length + ? `{\n${paramLines}\n }` + : "Record"; + + const resultType = QUERY_RESULT_TYPES[queryName] ?? "unknown"; + + return ` ${queryName}: {\n name: "${queryName}";\n parameters: ${paramSection};\n result: ${resultType};\n }`; + }); + + const content = `// Auto-generated by AppKit - DO NOT EDIT +// Generated by 'npx @databricks/appkit generate-types' or Vite plugin during build +import "@databricks/appkit-ui/react"; +import type { SQLTypeMarker, SQLStringMarker, SQLNumberMarker, SQLBooleanMarker, SQLBinaryMarker, SQLDateMarker, SQLTimestampMarker } from "@databricks/appkit-ui/js"; + +declare module "@databricks/appkit-ui/react" { + interface QueryRegistry { +${queryEntries.join(";\n")};\n } +} +`; + + mkdirSync(join(appDir, "client/src"), { recursive: true }); + writeFileSync(typesPath, content); + console.log(` Generated appKitTypes.d.ts with ${sqlFiles.length} queries`); + } } /**