UNPKG

create-prisma-php-app

Version:

Prisma-PHP: A Revolutionary Library Bridging PHP with Prisma ORM

247 lines (204 loc) 6.75 kB
import { Plugin } from "vite"; import path from "path"; import { writeFileSync, mkdirSync, existsSync, readFileSync } from "fs"; import ts from "typescript"; export function generateGlobalTypes(): Plugin { const dtsPath = path.resolve(process.cwd(), ".pp", "global-functions.d.ts"); return { name: "generate-global-types", buildStart() { const mainPath = path.resolve(process.cwd(), "ts", "main.ts"); if (!existsSync(mainPath)) { console.warn("⚠️ ts/main.ts not found, skipping type generation"); return; } const content = readFileSync(mainPath, "utf-8"); const globals = parseGlobalSingletons(content, mainPath); if (globals.length === 0) { console.warn("⚠️ No createGlobalSingleton calls found"); return; } generateDtsWithTypeChecker(globals, dtsPath, mainPath); }, }; } interface GlobalDeclaration { name: string; importPath: string; exportName: string; } function parseGlobalSingletons( content: string, filePath: string ): GlobalDeclaration[] { const sf = ts.createSourceFile( filePath, content, ts.ScriptTarget.Latest, true ); const globals: GlobalDeclaration[] = []; const importMap = new Map<string, { path: string; originalName: string }>(); sf.statements.forEach((stmt) => { if (ts.isImportDeclaration(stmt) && stmt.importClause) { const moduleSpecifier = (stmt.moduleSpecifier as ts.StringLiteral).text; if (stmt.importClause.namedBindings) { if (ts.isNamedImports(stmt.importClause.namedBindings)) { stmt.importClause.namedBindings.elements.forEach((element) => { const localName = element.name.text; const importedName = element.propertyName ? element.propertyName.text : localName; importMap.set(localName, { path: moduleSpecifier, originalName: importedName, }); }); } } } }); function visit(node: ts.Node) { if ( ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "createGlobalSingleton" ) { if (node.arguments.length >= 2) { const nameArg = node.arguments[0]; const valueArg = node.arguments[1]; if (ts.isStringLiteral(nameArg) && ts.isIdentifier(valueArg)) { const name = nameArg.text; const variable = valueArg.text; const importInfo = importMap.get(variable); if (importInfo) { globals.push({ name, importPath: importInfo.path, exportName: importInfo.originalName, }); } } } } ts.forEachChild(node, visit); } visit(sf); return globals; } function generateDtsWithTypeChecker( globals: GlobalDeclaration[], dtsPath: string, mainPath: string ) { const configPath = ts.findConfigFile( process.cwd(), ts.sys.fileExists, "tsconfig.json" ); const { config } = configPath ? ts.readConfigFile(configPath, ts.sys.readFile) : { config: {} }; const { options } = ts.parseJsonConfigFileContent( config, ts.sys, process.cwd() ); const program = ts.createProgram([mainPath], options); const checker = program.getTypeChecker(); const sourceFile = program.getSourceFile(mainPath); if (!sourceFile) { console.warn("⚠️ Could not load main.ts for type checking"); generateFallbackDts(globals, dtsPath); return; } const signatures = new Map<string, string>(); const importMap = new Map<string, ts.ImportDeclaration>(); sourceFile.statements.forEach((stmt) => { if (ts.isImportDeclaration(stmt) && stmt.importClause?.namedBindings) { if (ts.isNamedImports(stmt.importClause.namedBindings)) { stmt.importClause.namedBindings.elements.forEach((element) => { importMap.set(element.name.text, stmt); }); } } }); globals.forEach(({ name, exportName }) => { try { const importDecl = importMap.get(exportName); if (!importDecl || !importDecl.importClause?.namedBindings) { signatures.set(name, "(...args: any[]) => any"); return; } if (ts.isNamedImports(importDecl.importClause.namedBindings)) { const importSpec = importDecl.importClause.namedBindings.elements.find( (el) => el.name.text === exportName ); if (importSpec) { const symbol = checker.getSymbolAtLocation(importSpec.name); if (symbol) { const type = checker.getTypeOfSymbolAtLocation( symbol, importSpec.name ); const signature = checker.typeToString( type, undefined, ts.TypeFormatFlags.NoTruncation ); signatures.set(name, signature); return; } } } signatures.set(name, "(...args: any[]) => any"); } catch (error) { console.warn(`⚠️ Failed to extract type for ${name}:`, error); signatures.set(name, "(...args: any[]) => any"); } }); const declarations = globals .map(({ name }) => { const sig = signatures.get(name) || "(...args: any[]) => any"; return ` const ${name}: ${sig};`; }) .join("\n"); const windowDeclarations = globals .map(({ name }) => ` ${name}: typeof globalThis.${name};`) .join("\n"); const content = `// Auto-generated by Vite plugin // Do not edit manually - regenerate with: npm run dev or npm run build // Source: ts/main.ts declare global { ${declarations} interface Window { ${windowDeclarations} } } export {}; `; const dir = path.dirname(dtsPath); if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }); } writeFileSync(dtsPath, content, "utf-8"); console.log(`✅ Generated ${path.relative(process.cwd(), dtsPath)}`); } function generateFallbackDts(globals: GlobalDeclaration[], dtsPath: string) { const declarations = globals .map(({ name }) => ` const ${name}: (...args: any[]) => any;`) .join("\n"); const windowDeclarations = globals .map(({ name }) => ` ${name}: typeof globalThis.${name};`) .join("\n"); const content = `// Auto-generated by Vite plugin declare global { ${declarations} interface Window { ${windowDeclarations} } } export {}; `; writeFileSync(dtsPath, content, "utf-8"); }