UNPKG

@proofkit/typegen

Version:

`@proofkit/typegen` is a tool for generating TypeScript types from FileMaker database schemas, making it easier to work with FileMaker data in modern TypeScript projects.

196 lines (195 loc) 8.23 kB
import { Project, ScriptKind } from "ts-morph"; import chalk from "chalk"; import { OttoAdapter } from "@proofkit/fmdapi/adapters/otto"; import DataApi from "@proofkit/fmdapi"; import { FetchAdapter } from "@proofkit/fmdapi/adapters/fetch"; import { memoryStore } from "@proofkit/fmdapi/tokenStore/memory"; import fs from "fs-extra"; import path from "path"; import { typegenConfig } from "./types.js"; import { defaultEnvNames, commentHeader, overrideCommentHeader } from "./constants.js"; import { getLayoutMetadata } from "./getLayoutMetadata.js"; import { buildSchema, buildOverrideFile } from "./buildSchema.js"; import { buildLayoutClient } from "./buildLayoutClient.js"; import { formatAndSaveSourceFiles } from "./formatting.js"; import semver from "semver"; const generateTypedClients = async (config, options) => { const parsedConfig = typegenConfig.safeParse({ config }); if (!parsedConfig.success) { console.log(chalk.red("ERROR: Invalid config")); console.log(config); console.dir(parsedConfig.error, { depth: null }); return; } if (Array.isArray(parsedConfig.data.config)) { for (const option of parsedConfig.data.config) { await generateTypedClientsSingle(option, options); } } else { await generateTypedClientsSingle(parsedConfig.data.config, options); } }; const generateTypedClientsSingle = async (config, options) => { var _a; const { envNames, layouts, clientSuffix = "Client", generateClient = true, clearOldFiles = false, ...rest } = config; const { resetOverrides = false, cwd = process.cwd() } = options ?? {}; const validator = rest.validator ?? "zod/v4"; const rootDir = path.join(cwd, rest.path ?? "schema"); try { const packageJson = JSON.parse( fs.readFileSync(path.join(cwd, "package.json"), "utf8") ); const fmdapiVersion = (_a = packageJson.dependencies) == null ? void 0 : _a["@proofkit/fmdapi"]; if (fmdapiVersion && semver.valid(fmdapiVersion)) { const isAtLeast501 = semver.satisfies(fmdapiVersion, ">=5.0.1"); if (!isAtLeast501) { console.log( chalk.yellow( "WARNING: @proofkit/typegen will generate types only compatible with @proofkit/fmdapi version 5.0.1 or higher. Please update to the latest version of @proofkit/fmdapi" ) ); } } } catch (e) { } const project = new Project({}); const server = process.env[(envNames == null ? void 0 : envNames.server) ?? defaultEnvNames.server]; const db = process.env[(envNames == null ? void 0 : envNames.db) ?? defaultEnvNames.db]; const apiKey = ((envNames == null ? void 0 : envNames.auth) && "apiKey" in envNames.auth ? process.env[envNames.auth.apiKey ?? defaultEnvNames.apiKey] : void 0) ?? process.env[defaultEnvNames.apiKey]; const username = ((envNames == null ? void 0 : envNames.auth) && "username" in envNames.auth ? process.env[envNames.auth.username ?? defaultEnvNames.username] : void 0) ?? process.env[defaultEnvNames.username]; const password = ((envNames == null ? void 0 : envNames.auth) && "password" in envNames.auth ? process.env[envNames.auth.password ?? defaultEnvNames.password] : void 0) ?? process.env[defaultEnvNames.password]; const auth = apiKey ? { apiKey } : { username: username ?? "", password: password ?? "" }; if (!server || !db || !apiKey && !username) { console.log(chalk.red("ERROR: Could not get all required config values")); console.log("Ensure the following environment variables are set:"); if (!server) console.log(`${(envNames == null ? void 0 : envNames.server) ?? defaultEnvNames.server}`); if (!db) console.log(`${(envNames == null ? void 0 : envNames.db) ?? defaultEnvNames.db}`); if (!apiKey) { const apiKeyNameToLog = (envNames == null ? void 0 : envNames.auth) && "apiKey" in envNames.auth && envNames.auth.apiKey ? envNames.auth.apiKey : defaultEnvNames.apiKey; const usernameNameToLog = (envNames == null ? void 0 : envNames.auth) && "username" in envNames.auth && envNames.auth.username ? envNames.auth.username : defaultEnvNames.username; const passwordNameToLog = (envNames == null ? void 0 : envNames.auth) && "password" in envNames.auth && envNames.auth.password ? envNames.auth.password : defaultEnvNames.password; console.log( `${apiKeyNameToLog} (or ${usernameNameToLog} and ${passwordNameToLog})` ); } console.log(); return; } await fs.ensureDir(rootDir); if (clearOldFiles) { fs.emptyDirSync(path.join(rootDir, "client")); fs.emptyDirSync(path.join(rootDir, "generated")); } const clientIndexFilePath = path.join(rootDir, "client", "index.ts"); fs.rmSync(clientIndexFilePath, { force: true }); let successCount = 0; let errorCount = 0; let totalCount = 0; for await (const item of layouts) { totalCount++; const client = "apiKey" in auth ? DataApi({ adapter: new OttoAdapter({ auth, server, db }), layout: item.layoutName }) : DataApi({ adapter: new FetchAdapter({ auth, server, db, tokenStore: memoryStore() }), layout: item.layoutName }); const result = await getLayoutMetadata({ client, valueLists: item.valueLists }); if (!result) { errorCount++; continue; } const { schema, portalSchema, valueLists } = result; const args = { schemaName: item.schemaName, schema, layoutName: item.layoutName, portalSchema, valueLists, type: validator === "zod" || validator === "zod/v4" || validator === "zod/v3" ? validator : "ts", strictNumbers: item.strictNumbers, webviewerScriptName: config.webviewerScriptName, envNames: { auth: "apiKey" in auth ? { apiKey: (envNames == null ? void 0 : envNames.auth) && "apiKey" in envNames.auth ? envNames.auth.apiKey : defaultEnvNames.apiKey } : { username: (envNames == null ? void 0 : envNames.auth) && "username" in envNames.auth ? envNames.auth.username : defaultEnvNames.username, password: (envNames == null ? void 0 : envNames.auth) && "password" in envNames.auth ? envNames.auth.password : defaultEnvNames.password }, db: (envNames == null ? void 0 : envNames.db) ?? defaultEnvNames.db, server: (envNames == null ? void 0 : envNames.server) ?? defaultEnvNames.server } }; const schemaFile = project.createSourceFile( path.join(rootDir, "generated", `${item.schemaName}.ts`), { leadingTrivia: commentHeader }, { overwrite: true, scriptKind: ScriptKind.TS } ); buildSchema(schemaFile, args); const overrideFilePath = path.join(rootDir, `${item.schemaName}.ts`); if (!fs.existsSync(overrideFilePath) || resetOverrides) { const overrideFile = project.createSourceFile( overrideFilePath, { leadingTrivia: overrideCommentHeader }, { overwrite: true, scriptKind: ScriptKind.TS } ); buildOverrideFile(overrideFile, schemaFile, args); } if (item.generateClient ?? generateClient) { await fs.ensureDir(path.join(rootDir, "client")); const layoutClientFile = project.createSourceFile( path.join(rootDir, "client", `${item.schemaName}.ts`), { leadingTrivia: commentHeader }, { overwrite: true, scriptKind: ScriptKind.TS } ); buildLayoutClient(layoutClientFile, args); await fs.ensureFile(clientIndexFilePath); const clientIndexFile = project.addSourceFileAtPath(clientIndexFilePath); clientIndexFile.addExportDeclaration({ namedExports: [ { name: "client", alias: `${item.schemaName}${clientSuffix}` } ], moduleSpecifier: `./${item.schemaName}` }); } else { console.log( chalk.yellow( `Skipping client generation for ${item.schemaName} because generateClient is false` ) ); } successCount++; } await formatAndSaveSourceFiles(project); return { successCount, errorCount, totalCount }; }; export { generateTypedClients }; //# sourceMappingURL=typegen.js.map