diff --git a/.release-it.json b/.release-it.json index 76443ed4..980cc513 100644 --- a/.release-it.json +++ b/.release-it.json @@ -20,7 +20,7 @@ "npm": false, "hooks": { "after:bump": "tsx tools/sync-versions.ts ${version} && pnpm build:notice && git add NOTICE.md", - "before:release": "pnpm build && pnpm --filter=@databricks/appkit dist && pnpm --filter=@databricks/appkit-ui dist", + "before:release": "pnpm build && pnpm --filter=docs build && pnpm --filter=@databricks/appkit dist && pnpm --filter=@databricks/appkit-ui dist", "after:release": "npm publish packages/appkit/tmp --access public --provenance && npm publish packages/appkit-ui/tmp --access public --provenance" }, "plugins": { diff --git a/apps/dev-playground/client/src/appKitTypes.d.ts b/apps/dev-playground/client/src/appKitTypes.d.ts index fc9dd815..0e0ae0b0 100644 --- a/apps/dev-playground/client/src/appKitTypes.d.ts +++ b/apps/dev-playground/client/src/appKitTypes.d.ts @@ -1,5 +1,5 @@ // Auto-generated by AppKit - DO NOT EDIT -// Generated by 'npx appkit-generate-types' or Vite plugin during build +// 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"; diff --git a/apps/dev-playground/client/src/routes/type-safety.route.tsx b/apps/dev-playground/client/src/routes/type-safety.route.tsx index d2df3ff9..07084c53 100644 --- a/apps/dev-playground/client/src/routes/type-safety.route.tsx +++ b/apps/dev-playground/client/src/routes/type-safety.route.tsx @@ -525,13 +525,13 @@ export default defineConfig({ diff --git a/docs/docs/development/llm-guide.mdx b/docs/docs/development/llm-guide.md similarity index 100% rename from docs/docs/development/llm-guide.mdx rename to docs/docs/development/llm-guide.md diff --git a/docs/docs/development/project-setup.mdx b/docs/docs/development/project-setup.md similarity index 100% rename from docs/docs/development/project-setup.mdx rename to docs/docs/development/project-setup.md diff --git a/docs/docs/development/type-generation.mdx b/docs/docs/development/type-generation.md similarity index 89% rename from docs/docs/development/type-generation.mdx rename to docs/docs/development/type-generation.md index 2de17706..ef277f12 100644 --- a/docs/docs/development/type-generation.mdx +++ b/docs/docs/development/type-generation.md @@ -42,13 +42,13 @@ export default defineConfig({ When the frontend is served through AppKit in dev mode, AppKit's dev server already includes `appKitTypesPlugin()` internally. You still want it in your client build pipeline if you run `vite build` separately. -## CLI: `appkit-generate-types` +## CLI: `npx @databricks/appkit generate-types` For manual type generation or CI/CD pipelines, use the CLI command: ```bash # Requires DATABRICKS_WAREHOUSE_ID (or pass as 3rd arg) -npx appkit-generate-types [rootDir] [outFile] [warehouseId] +npx @databricks/appkit generate-types [rootDir] [outFile] [warehouseId] ``` ### Examples @@ -56,19 +56,19 @@ npx appkit-generate-types [rootDir] [outFile] [warehouseId] - Generate types using warehouse ID from environment ```bash - npx appkit-generate-types . client/src/appKitTypes.d.ts + npx @databricks/appkit generate-types . client/src/appKitTypes.d.ts ``` - Generate types using warehouse ID explicitly ```bash - npx appkit-generate-types . client/src/appKitTypes.d.ts abc123... + npx @databricks/appkit generate-types . client/src/appKitTypes.d.ts abc123... ``` - Force regeneration (skip cache) ```bash - npx appkit-generate-types --no-cache + npx @databricks/appkit generate-types --no-cache ``` ## How it works diff --git a/package.json b/package.json index 4b1fb018..db0eca29 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "format": "biome format --write .", "lint:fix": "biome lint --write .", "lint": "biome lint .", - "pack:sdk": "pnpm build && pnpm -r tarball", + "pack:sdk": "pnpm build && pnpm --filter=docs build && pnpm -r tarball", "prepare": "husky", "release": "release-it", "release:dry": "release-it --dry-run", diff --git a/packages/appkit-ui/package.json b/packages/appkit-ui/package.json index 37b95ccd..2a1776e2 100644 --- a/packages/appkit-ui/package.json +++ b/packages/appkit-ui/package.json @@ -11,8 +11,8 @@ "dist", "bin", "scripts", + "docs", "CLAUDE.md", - "AGENTS.md", "llms.txt", "README.md", "DCO", @@ -39,7 +39,7 @@ "clean:full": "rm -rf dist node_modules tmp", "clean": "rm -rf dist tmp", "dist": "tsx ../../tools/dist.ts", - "tarball": "rm -rf tmp && tsx ../../tools/dist.ts && npm pack ./tmp --pack-destination ./tmp", + "tarball": "rm -rf tmp && pnpm dist && npm pack ./tmp --pack-destination ./tmp", "typecheck": "tsc --noEmit" }, "dependencies": { diff --git a/packages/appkit/bin/generate-types.js b/packages/appkit/bin/generate-types.js deleted file mode 100755 index dabe63ba..00000000 --- a/packages/appkit/bin/generate-types.js +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env node -import path from "node:path"; - -import { generateFromEntryPoint } from "../dist/type-generator/index.js"; - -// Parse arguments -const args = process.argv.slice(2); -const noCache = args.includes("--no-cache"); -const positionalArgs = args.filter((arg) => !arg.startsWith("--")); - -const rootDir = positionalArgs[0] || process.cwd(); -const outFile = - positionalArgs[1] || path.join(process.cwd(), "client/src/appKitTypes.d.ts"); - -const queryFolder = path.join(rootDir, "config/queries"); - -const warehouseId = positionalArgs[2] || process.env.DATABRICKS_WAREHOUSE_ID; -if (!warehouseId) { - throw new Error("DATABRICKS_WAREHOUSE_ID is not set"); -} - -await generateFromEntryPoint({ - queryFolder, - outFile, - warehouseId, - noCache, -}); diff --git a/packages/appkit/package.json b/packages/appkit/package.json index 718a2fac..5153fb99 100644 --- a/packages/appkit/package.json +++ b/packages/appkit/package.json @@ -13,9 +13,9 @@ "dist", "bin", "scripts", + "docs", "CLAUDE.md", "llms.txt", - "AGENTS.md", "README.md", "DCO", "NOTICE.md" @@ -25,23 +25,23 @@ "development": "./src/index.ts", "default": "./dist/index.js" }, + "./type-generator": { + "types": "./dist/type-generator/index.d.ts", + "development": "./src/type-generator/index.ts", + "default": "./dist/type-generator/index.js" + }, "./package.json": "./package.json" }, - "bin": { - "appkit-generate-types": "./bin/generate-types.js", - "appkit-lint": "./bin/appkit-lint.js" - }, "scripts": { "build:package": "tsdown --config tsdown.config.ts", "build:watch": "tsdown --config tsdown.config.ts --watch", "clean:full": "rm -rf dist node_modules tmp", "clean": "rm -rf dist tmp", "dist": "tsx ../../tools/dist.ts", - "tarball": "rm -rf tmp && tsx ../../tools/dist.ts && npm pack ./tmp --pack-destination ./tmp", + "tarball": "rm -rf tmp && pnpm dist && npm pack ./tmp --pack-destination ./tmp", "typecheck": "tsc --noEmit" }, "dependencies": { - "@ast-grep/napi": "^0.37.0", "@databricks/sdk-experimental": "^0.15.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.208.0", @@ -81,6 +81,7 @@ "publishConfig": { "exports": { ".": "./dist/index.js", + "./type-generator": "./dist/type-generator/index.js", "./package.json": "./package.json" } } diff --git a/packages/appkit/src/index.ts b/packages/appkit/src/index.ts index 1c055071..f020c097 100644 --- a/packages/appkit/src/index.ts +++ b/packages/appkit/src/index.ts @@ -42,5 +42,5 @@ export { type TelemetryConfig, } from "./telemetry"; -// Vite plugin +// Vite plugin and type generation export { appKitTypesPlugin } from "./type-generator/vite-plugin"; diff --git a/packages/appkit/src/type-generator/index.ts b/packages/appkit/src/type-generator/index.ts index 4ecd0b56..c41bc577 100644 --- a/packages/appkit/src/type-generator/index.ts +++ b/packages/appkit/src/type-generator/index.ts @@ -28,7 +28,7 @@ function generateTypeDeclarations(querySchemas: QuerySchema[] = []): string { const querySection = queryEntries ? `\n${queryEntries};\n ` : ""; return `// Auto-generated by AppKit - DO NOT EDIT -// Generated by 'npx appkit-generate-types' or Vite plugin during build +// 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"; diff --git a/packages/shared/bin/appkit.js b/packages/shared/bin/appkit.js new file mode 100644 index 00000000..35b00ed4 --- /dev/null +++ b/packages/shared/bin/appkit.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +import "../dist/cli/index.js"; diff --git a/packages/shared/package.json b/packages/shared/package.json index cc43ae46..d7558056 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -8,6 +8,10 @@ "development": "./src/index.ts", "default": "./dist/index.js" }, + "./cli": { + "development": "./src/cli/index.ts", + "default": "./dist/cli/index.js" + }, "./package.json": "./package.json" }, "scripts": { @@ -29,10 +33,12 @@ "publishConfig": { "exports": { ".": "./dist/index.js", + "./cli": "./dist/cli/index.js", "./package.json": "./package.json" } }, "dependencies": { - "fast-glob": "^3.3.3" + "@ast-grep/napi": "^0.37.0", + "commander": "^12.1.0" } } diff --git a/packages/shared/scripts/postinstall.js b/packages/shared/scripts/postinstall.js index 3db18ec1..22f1c09d 100644 --- a/packages/shared/scripts/postinstall.js +++ b/packages/shared/scripts/postinstall.js @@ -2,5 +2,5 @@ console.log(""); console.log("[@databricks/appkit] To setup AI assistant instructions, run:"); console.log(""); -console.log(" npx appkit-setup --write"); +console.log(" npx appkit setup --write"); console.log(""); diff --git a/packages/shared/src/cli/commands/docs.ts b/packages/shared/src/cli/commands/docs.ts new file mode 100644 index 00000000..cac88afd --- /dev/null +++ b/packages/shared/src/cli/commands/docs.ts @@ -0,0 +1,66 @@ +import { Command } from "commander"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +function findPackageRoot(): string { + let dir = __dirname; + while (dir !== path.parse(dir).root) { + if (fs.existsSync(path.join(dir, "package.json"))) { + return dir; + } + dir = path.dirname(dir); + } + throw new Error("Could not find package root"); +} + +function runDocs(docPath?: string) { + const packageRoot = findPackageRoot(); + + if (!docPath) { + // Display llms.txt by default + const llmsPath = path.join(packageRoot, "llms.txt"); + + if (!fs.existsSync(llmsPath)) { + console.error("Error: llms.txt not found in package"); + process.exit(1); + } + + const content = fs.readFileSync(llmsPath, "utf-8"); + console.log(content); + return; + } + + // Handle path - remove leading ./ and / first, then strip prefixes + let normalizedPath = docPath; + + // Strip leading ./ or / + normalizedPath = normalizedPath.replace(/^\.\//, ""); + normalizedPath = normalizedPath.replace(/^\//, ""); + + // Remove /appkit/docs/ or docs/ prefix since files are in packageRoot/docs/ + normalizedPath = normalizedPath.replace(/^appkit\/docs\//, ""); + normalizedPath = normalizedPath.replace(/^docs\//, ""); + + const fullPath = path.join(packageRoot, "docs", normalizedPath); + + if (!fs.existsSync(fullPath)) { + console.error(`Error: Documentation file not found: ${docPath}`); + console.error(`Tried: ${fullPath}`); + process.exit(1); + } + + const content = fs.readFileSync(fullPath, "utf-8"); + console.log(content); +} + +export const docsCommand = new Command("docs") + .description("Display embedded documentation") + .argument( + "[path]", + "Path to specific documentation file (e.g., /appkit/docs/api/appkit-ui/components/Sidebar.md)", + ) + .action(runDocs); diff --git a/packages/shared/src/cli/commands/generate-types.ts b/packages/shared/src/cli/commands/generate-types.ts new file mode 100644 index 00000000..9c81a3a8 --- /dev/null +++ b/packages/shared/src/cli/commands/generate-types.ts @@ -0,0 +1,65 @@ +import { Command } from "commander"; +import path from "node:path"; + +/** + * Generate types command implementation + */ +async function runGenerateTypes( + rootDir?: string, + outFile?: string, + warehouseId?: string, + options?: { noCache?: boolean }, +) { + try { + // Try to import the type generator from @databricks/appkit + const { generateFromEntryPoint } = await import( + "@databricks/appkit/type-generator" + ); + + const resolvedRootDir = rootDir || process.cwd(); + const resolvedOutFile = + outFile || path.join(process.cwd(), "client/src/appKitTypes.d.ts"); + + const queryFolder = path.join(resolvedRootDir, "config/queries"); + + const resolvedWarehouseId = + warehouseId || process.env.DATABRICKS_WAREHOUSE_ID; + if (!resolvedWarehouseId) { + console.error( + "Error: DATABRICKS_WAREHOUSE_ID is not set. Please provide it as an argument or environment variable.", + ); + process.exit(1); + } + + await generateFromEntryPoint({ + queryFolder, + outFile: resolvedOutFile, + warehouseId: resolvedWarehouseId, + noCache: options?.noCache || false, + }); + } catch (error) { + if ( + error instanceof Error && + error.message.includes("Cannot find module") + ) { + console.error( + "Error: The 'generate-types' command is only available in @databricks/appkit.", + ); + console.error("Please install @databricks/appkit to use this command."); + process.exit(1); + } + throw error; + } +} + +export const generateTypesCommand = new Command("generate-types") + .description("Generate TypeScript types from SQL queries") + .argument("[rootDir]", "Root directory of the project", process.cwd()) + .argument( + "[outFile]", + "Output file path", + path.join(process.cwd(), "client/src/appKitTypes.d.ts"), + ) + .argument("[warehouseId]", "Databricks warehouse ID") + .option("--no-cache", "Disable caching for type generation") + .action(runGenerateTypes); diff --git a/packages/appkit/bin/appkit-lint.js b/packages/shared/src/cli/commands/lint.ts old mode 100755 new mode 100644 similarity index 79% rename from packages/appkit/bin/appkit-lint.js rename to packages/shared/src/cli/commands/lint.ts index 22ce2661..9a23acc6 --- a/packages/appkit/bin/appkit-lint.js +++ b/packages/shared/src/cli/commands/lint.ts @@ -1,14 +1,17 @@ -#!/usr/bin/env node -/** - * AST-based linting using ast-grep. - * Catches patterns that ESLint/TypeScript miss or handle poorly. - * Usage: npx appkit-lint - */ +import { Command } from "commander"; import { parse, Lang } from "@ast-grep/napi"; import fs from "node:fs"; import path from "node:path"; -const rules = [ +interface Rule { + id: string; + pattern: string; + message: string; + includeTests?: boolean; + filter?: (code: string) => boolean; +} + +const rules: Rule[] = [ { id: "no-double-type-assertion", pattern: "$X as unknown as $Y", @@ -37,13 +40,13 @@ const rules = [ }, ]; -function isTestFile(filePath) { +function isTestFile(filePath: string): boolean { return ( /\.(test|spec)\.(ts|tsx)$/.test(filePath) || filePath.includes("/tests/") ); } -function findTsFiles(dir, files = []) { +function findTsFiles(dir: string, files: string[] = []): string[] { const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { @@ -61,8 +64,17 @@ function findTsFiles(dir, files = []) { return files; } -function lintFile(filePath, rules) { - const violations = []; +interface Violation { + file: string; + line: number; + column: number; + rule: string; + message: string; + code: string; +} + +function lintFile(filePath: string, rules: Rule[]): Violation[] { + const violations: Violation[] = []; const content = fs.readFileSync(filePath, "utf-8"); const lang = filePath.endsWith(".tsx") ? Lang.Tsx : Lang.TypeScript; const testFile = isTestFile(filePath); @@ -96,13 +108,16 @@ function lintFile(filePath, rules) { return violations; } -function main() { +/** + * Lint command implementation + */ +function runLint() { const rootDir = process.cwd(); const files = findTsFiles(rootDir); console.log(`Scanning ${files.length} TypeScript files...\n`); - const allViolations = []; + const allViolations: Violation[] = []; for (const file of files) { const violations = lintFile(file, rules); @@ -126,4 +141,6 @@ function main() { process.exit(1); } -main(); +export const lintCommand = new Command("lint") + .description("Run AST-based linting on TypeScript files") + .action(runLint); diff --git a/packages/shared/bin/setup-claude.js b/packages/shared/src/cli/commands/setup.ts similarity index 72% rename from packages/shared/bin/setup-claude.js rename to packages/shared/src/cli/commands/setup.ts index cfbb669c..b6eaece8 100644 --- a/packages/shared/bin/setup-claude.js +++ b/packages/shared/src/cli/commands/setup.ts @@ -1,16 +1,4 @@ -#!/usr/bin/env node - -/** - * CLI tool to setup CLAUDE.md for Databricks AppKit packages. - * - * This bin is included in both @databricks/appkit and @databricks/appkit-ui - * so it's available regardless of which package the user installs. - * - * Usage: - * npx appkit-setup # Show detected packages and content - * npx appkit-setup --write # Create or update CLAUDE.md file - */ - +import { Command } from "commander"; import fs from "node:fs"; import path from "node:path"; @@ -26,15 +14,20 @@ const SECTION_START = ""; const SECTION_END = ""; /** - * Find which AppKit packages are installed by checking for CLAUDE.md + * Find which AppKit packages are installed by checking for package.json */ function findInstalledPackages() { const cwd = process.cwd(); const installed = []; for (const pkg of PACKAGES) { - const claudePath = path.join(cwd, "node_modules", pkg.name, "package.json"); - if (fs.existsSync(claudePath)) { + const packagePath = path.join( + cwd, + "node_modules", + pkg.name, + "package.json", + ); + if (fs.existsSync(packagePath)) { installed.push(pkg); } } @@ -45,7 +38,7 @@ function findInstalledPackages() { /** * Generate the AppKit section content */ -function generateSection(packages) { +function generateSection(packages: typeof PACKAGES) { const links = packages .map((pkg) => { const docPath = `./node_modules/${pkg.name}/CLAUDE.md`; @@ -65,7 +58,7 @@ ${SECTION_END}`; /** * Generate standalone CLAUDE.md content (when no existing file) */ -function generateStandalone(packages) { +function generateStandalone(packages: typeof PACKAGES) { const links = packages .map((pkg) => { const docPath = `./node_modules/${pkg.name}/CLAUDE.md`; @@ -88,7 +81,7 @@ ${SECTION_END} /** * Update existing content with AppKit section */ -function updateContent(existingContent, packages) { +function updateContent(existingContent: string, packages: typeof PACKAGES) { const newSection = generateSection(packages); // Check if AppKit section already exists @@ -107,27 +100,10 @@ function updateContent(existingContent, packages) { } /** - * Main CLI logic + * Setup command implementation */ -function main() { - const args = process.argv.slice(2); - const shouldWrite = args.includes("--write") || args.includes("-w"); - const help = args.includes("--help") || args.includes("-h"); - - if (help) { - console.log(` -Usage: npx appkit-setup [options] - -Options: - --write, -w Create or update CLAUDE.md file in current directory - --help, -h Show this help message - -Examples: - npx appkit-setup # Show detected packages and preview content - npx appkit-setup --write # Create or update CLAUDE.md -`); - return; - } +function runSetup(options: { write?: boolean }) { + const shouldWrite = options.write; // Find installed packages const installed = findInstalledPackages(); @@ -151,8 +127,8 @@ Examples: ? fs.readFileSync(claudePath, "utf-8") : null; - let finalContent; - let action; + let finalContent: string; + let action: string; if (existingContent) { finalContent = updateContent(existingContent, installed); @@ -168,7 +144,7 @@ Examples: console.log(` Path: ${claudePath}`); } else { console.log("\nTo create/update CLAUDE.md, run:"); - console.log(" npx appkit-setup --write\n"); + console.log(" npx appkit setup --write\n"); if (existingContent) { console.log( @@ -187,4 +163,7 @@ Examples: } } -main(); +export const setupCommand = new Command("setup") + .description("Setup CLAUDE.md with AppKit package references") + .option("-w, --write", "Create or update CLAUDE.md file in current directory") + .action(runSetup); diff --git a/packages/shared/src/cli/commands/type-generator.d.ts b/packages/shared/src/cli/commands/type-generator.d.ts new file mode 100644 index 00000000..debda666 --- /dev/null +++ b/packages/shared/src/cli/commands/type-generator.d.ts @@ -0,0 +1,9 @@ +// Type declarations for optional @databricks/appkit/type-generator module +declare module "@databricks/appkit/type-generator" { + export function generateFromEntryPoint(options: { + queryFolder: string; + outFile: string; + warehouseId: string; + noCache: boolean; + }): Promise; +} diff --git a/packages/shared/src/cli/index.ts b/packages/shared/src/cli/index.ts new file mode 100644 index 00000000..7ab2eecf --- /dev/null +++ b/packages/shared/src/cli/index.ts @@ -0,0 +1,18 @@ +#!/usr/bin/env node + +import { Command } from "commander"; +import { setupCommand } from "./commands/setup.js"; +import { generateTypesCommand } from "./commands/generate-types.js"; +import { lintCommand } from "./commands/lint.js"; +import { docsCommand } from "./commands/docs.js"; + +const cmd = new Command(); + +cmd.name("appkit").description("CLI tools for Databricks AppKit"); + +cmd.addCommand(setupCommand); +cmd.addCommand(generateTypesCommand); +cmd.addCommand(lintCommand); +cmd.addCommand(docsCommand); + +cmd.parse(); diff --git a/packages/shared/tsdown.config.ts b/packages/shared/tsdown.config.ts index 57384635..b1fdb9c5 100644 --- a/packages/shared/tsdown.config.ts +++ b/packages/shared/tsdown.config.ts @@ -2,17 +2,18 @@ import { defineConfig } from "tsdown"; export default defineConfig({ name: "shared", - entry: "src/index.ts", + entry: ["src/index.ts", "src/cli/index.ts"], outDir: "dist", minify: false, format: "esm", - platform: "neutral", + platform: "node", // Required for bin commands sourcemap: false, unbundle: true, dts: true, clean: false, hash: false, skipNodeModulesBundle: true, + external: [/^@databricks\//], tsconfig: "./tsconfig.json", exports: { devExports: "development", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f5662d6..b9b2bcae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -233,9 +233,6 @@ importers: packages/appkit: dependencies: - '@ast-grep/napi': - specifier: ^0.37.0 - version: 0.37.0 '@databricks/sdk-experimental': specifier: ^0.15.0 version: 0.15.0 @@ -493,9 +490,12 @@ importers: packages/shared: dependencies: - fast-glob: - specifier: ^3.3.3 - version: 3.3.3 + '@ast-grep/napi': + specifier: ^0.37.0 + version: 0.37.0 + commander: + specifier: ^12.1.0 + version: 12.1.0 devDependencies: '@types/dependency-tree': specifier: ^8.1.4 diff --git a/template/package.json b/template/package.json index 71db79cd..918f5e55 100644 --- a/template/package.json +++ b/template/package.json @@ -12,7 +12,7 @@ "typecheck": "tsc -p ./tsconfig.server.json --noEmit && tsc -p ./tsconfig.client.json --noEmit", "lint": "eslint .", "lint:fix": "eslint . --fix", - "lint:ast-grep": "appkit-lint", + "lint:ast-grep": "appkit lint", "format": "prettier --check .", "format:fix": "prettier --write .", "test": "vitest run && npm run test:smoke", @@ -20,8 +20,8 @@ "test:e2e:ui": "playwright test --ui", "test:smoke": "playwright install chromium && playwright test tests/smoke.spec.ts", "clean": "rm -rf client/dist dist build node_modules .smoke-test test-results playwright-report", - "typegen": "appkit-generate-types", - "setup": "appkit-setup --write" + "typegen": "appkit generate-types", + "setup": "appkit setup --write" }, "keywords": [], "author": "", diff --git a/tools/dist.ts b/tools/dist.ts index 9faeee6f..7ff61958 100644 --- a/tools/dist.ts +++ b/tools/dist.ts @@ -3,6 +3,11 @@ import path from "node:path"; const __dirname = path.dirname(new URL(import.meta.url).pathname); +// Read CLI dependencies from shared package +const sharedPkgPath = path.join(__dirname, "../packages/shared/package.json"); +const sharedPkg = JSON.parse(fs.readFileSync(sharedPkgPath, "utf-8")); +const CLI_DEPENDENCIES = sharedPkg.dependencies; + fs.mkdirSync("tmp", { recursive: true }); const pkg = JSON.parse(fs.readFileSync("package.json", "utf-8")); @@ -13,25 +18,26 @@ pkg.exports = pkg.publishConfig.exports; delete pkg.publishConfig.exports; const isAppKitPackage = pkg.name?.startsWith("@databricks/appkit"); -const sharedBin = path.join( - __dirname, - "../packages/shared/bin/setup-claude.js", -); +const sharedBin = path.join(__dirname, "../packages/shared/bin/appkit.js"); const sharedPostinstall = path.join( __dirname, "../packages/shared/scripts/postinstall.js", ); -// Add appkit-setup bin and postinstall for @databricks/appkit* packages +// Add appkit bin and postinstall for @databricks/appkit* packages if (isAppKitPackage) { if (fs.existsSync(sharedBin)) { pkg.bin = pkg.bin || {}; - pkg.bin["appkit-setup"] = "./bin/setup-claude.js"; + pkg.bin.appkit = "./bin/appkit.js"; } if (fs.existsSync(sharedPostinstall)) { pkg.scripts = pkg.scripts || {}; pkg.scripts.postinstall = "node scripts/postinstall.js"; } + + // Add CLI dependencies from shared package (required for bin commands to work) + pkg.dependencies = pkg.dependencies || {}; + Object.assign(pkg.dependencies, CLI_DEPENDENCIES); } fs.writeFileSync("tmp/package.json", JSON.stringify(pkg, null, 2)); @@ -46,7 +52,15 @@ if (fs.existsSync("bin")) { if (isAppKitPackage) { if (fs.existsSync(sharedBin)) { fs.mkdirSync("tmp/bin", { recursive: true }); - fs.copyFileSync(sharedBin, "tmp/bin/setup-claude.js"); + fs.copyFileSync(sharedBin, "tmp/bin/appkit.js"); + + // Copy CLI code from shared/dist/cli to tmp/dist/cli + const sharedCliDist = path.join(__dirname, "../packages/shared/dist/cli"); + if (fs.existsSync(sharedCliDist)) { + const tmpCliDist = "tmp/dist/cli"; + fs.mkdirSync(tmpCliDist, { recursive: true }); + fs.cpSync(sharedCliDist, tmpCliDist, { recursive: true }); + } } if (fs.existsSync(sharedPostinstall)) { fs.mkdirSync("tmp/scripts", { recursive: true }); @@ -54,15 +68,63 @@ if (isAppKitPackage) { } } -if (fs.existsSync("llms.txt")) { - fs.copyFileSync("llms.txt", "tmp/llms.txt"); -} else { - fs.copyFileSync(path.join(__dirname, "../llms.txt"), "tmp/llms.txt"); +// Copy documentation from docs/build into tmp/docs/ +const docsBuildPath = path.join(__dirname, "../docs/build"); + +// Copy all .md files and docs/ subdirectory from docs/build to tmp/docs +fs.mkdirSync("tmp/docs", { recursive: true }); + +// Copy all files and directories we want, preserving structure +const itemsToCopy = fs.readdirSync(docsBuildPath); +for (const item of itemsToCopy) { + const sourcePath = path.join(docsBuildPath, item); + const stat = fs.statSync(sourcePath); + + // Copy .md files and docs directory + if (item.endsWith(".md") || item === "docs") { + const destPath = path.join("tmp/docs", item); + if (stat.isDirectory()) { + fs.cpSync(sourcePath, destPath, { recursive: true }); + } else { + fs.copyFileSync(sourcePath, destPath); + } + } } -// Copy llms.txt as CLAUDE.md and AGENTS.md (npm pack doesn't support symlinks) +// Process llms.txt (keep existing logic but update path replacement) +const llmsSourcePath = path.join(docsBuildPath, "llms.txt"); +let llmsContent = fs.readFileSync(llmsSourcePath, "utf-8"); + +// Replace /appkit/ with ./docs/ to match new structure +llmsContent = llmsContent.replace(/\/appkit\//g, "./docs/"); + +// Prepend AI agent guidance for navigating documentation +const agentGuidance = `## For AI Agents/Assistants + +To view specific documentation files referenced below, use the appkit CLI: + +\`\`\`bash +npx @databricks/appkit docs +\`\`\` + +Examples: +- View main documentation: \`npx @databricks/appkit docs\` +- View specific file: \`npx @databricks/appkit docs ./docs/docs.md\` +- View API reference: \`npx @databricks/appkit docs ./docs/docs/api.md\` +- View component docs: \`npx @databricks/appkit docs ./docs/docs/api/appkit-ui/components/Sidebar.md\` + +The CLI will display the documentation content directly in the terminal. + +--- + +`; + +llmsContent = agentGuidance + llmsContent; + +fs.writeFileSync("tmp/llms.txt", llmsContent); + +// Copy llms.txt as CLAUDE.md (npm pack doesn't support symlinks) fs.copyFileSync("tmp/llms.txt", "tmp/CLAUDE.md"); -fs.copyFileSync("tmp/llms.txt", "tmp/AGENTS.md"); fs.copyFileSync(path.join(__dirname, "../README.md"), "tmp/README.md"); fs.copyFileSync(path.join(__dirname, "../LICENSE"), "tmp/LICENSE"); diff --git a/tools/generate-component-mdx.ts b/tools/generate-component-mdx.ts index 4e3b8357..1f11bac7 100644 --- a/tools/generate-component-mdx.ts +++ b/tools/generate-component-mdx.ts @@ -562,7 +562,10 @@ function main() { const entries = fs.readdirSync(outputDir, { withFileTypes: true }); for (const entry of entries) { if (entry.isDirectory()) { - fs.rmSync(path.join(outputDir, entry.name), { recursive: true }); + fs.rmSync(path.join(outputDir, entry.name), { + recursive: true, + force: true, + }); } } }