UNPKG

@patchworkdev/pdk

Version:

Patchwork Development Kit

145 lines (128 loc) 5.56 kB
// import * as fs from 'fs'; import { ProjectConfig } from '@patchworkdev/common'; import fs from 'fs/promises'; import * as path from 'path'; import { Abi } from 'viem'; import { PatchworkProject } from '../../types'; import { ErrorCode, PDKError } from './error'; import { logger } from './logger'; import { SchemaModule } from './ponderSchemaMock'; import { importProjectConfig } from './project'; import { Default, tsLoader } from './tsLoader'; async function findFileUpwards(directory: string, filename: string): Promise<string | null> { const filePath = path.join(directory, filename); try { await fs.access(filePath); return filePath; } catch { // File does not exist } const parentDirectory = path.dirname(directory); if (parentDirectory === directory) { return null; } return findFileUpwards(parentDirectory, filename); } export async function findConfig() { const configFileName = 'patchwork.config.ts'; const currentDirectory = process.cwd(); return findFileUpwards(currentDirectory, configFileName); } export async function findPonderSchema() { const schemaFileName = 'ponder.schema.ts'; const currentDirectory = process.cwd(); return findFileUpwards(currentDirectory, schemaFileName); } export async function loadPonderSchema(ponderSchema: string): Promise<SchemaModule> { const mock = path.resolve(__dirname, './ponderSchemaMock'); try { return await tsLoader<SchemaModule>(ponderSchema, { compilerOptions: { strict: false, noImplicitAny: false, }, moduleOverrides: { '@ponder/core': mock, }, }); } catch (error) { logger.error(error); if (error instanceof TypeError && error.message.includes('is not a function')) { logger.error('Error: It seems a method is missing from our mock implementation.'); logger.error('Full error:', error); logger.error('Please add this method to the mockSchemaBuilder in ponderSchemaMocks.ts'); } throw new PDKError(ErrorCode.MOCK_NOT_FOUND, `Missing mock implementation in ponderSchemaMocks.ts`); } } // Imports a ProjectConfig ts file and returns a fully loaded PatchworkProject export async function importPatchworkConfig(config: string): Promise<PatchworkProject> { try { // Resolve the full path const fullPath = path.isAbsolute(config) ? config : path.resolve(process.cwd(), config); let loadedConfig = ( await tsLoader<Default<ProjectConfig>>(fullPath, { moduleOverrides: { '@patchworkdev/pdk/plugins': path.resolve(__dirname, '..', '..', 'plugins'), }, }) ).default; // TODO how can we check that this is a ProjectConfig and not a PatchworkProject? let project = importProjectConfig(loadedConfig); return project; } catch (error) { if (error instanceof Error) { throw new PDKError(ErrorCode.PROJECT_CONFIG_ERROR, error.message); } else { throw new PDKError(ErrorCode.PROJECT_CONFIG_ERROR, `Error importing ProjectConfig at ${config}`, error); } } } export async function importABIFiles(abiDir: string) { try { await fs.access(abiDir); } catch (error) { logger.error(`- ABI directory not found: ${abiDir}`); throw new PDKError(ErrorCode.DIR_NOT_FOUND, `ABI directory not found at ${abiDir}`); } const abiObjects: Record<string, Abi> = {}; try { // Read the directory const abiFiles = (await fs.readdir(abiDir)).filter((file) => file.endsWith('.abi.ts')); // Dynamically import all ABI files // -- NOTE: Seems like we should prune this? There's a LOT of artifacts // -- NOTE: Should consider only importing ABIs for project's contracts + PatchworkProtocol await Promise.all( abiFiles.map(async (file) => { const filePath = path.join(abiDir, file); const module = await tsLoader<{ [key: string]: Abi }>(filePath); const baseName = path.basename(file, '.abi.ts'); abiObjects[baseName] = module[baseName]; // Return the exported constant return { name: baseName, abi: module[baseName] }; }), ); } catch (error) { logger.error('Error importing ABI files:', error); throw new PDKError(ErrorCode.ABI_IMPORT_ERROR, `Error importing ABIs at ${abiDir}`); } if (Object.keys(abiObjects).length === 0) { logger.error(`Error: No ABI files found in ${abiDir}`); throw new PDKError(ErrorCode.ABI_IMPORT_ERROR, `Error: No ABI files found in ${abiDir}`); } return abiObjects; } export function getFragmentRelationships(projectConfig: ProjectConfig | PatchworkProject): Record<string, string[]> { const fragmentRelationships: Record<string, string[]> = {} as Record<string, string[]>; Object.entries(projectConfig.contracts).forEach(([contractName, contractConfig]) => { if (typeof contractConfig !== 'string') { contractConfig.fragments?.forEach((fragment) => { if (!fragmentRelationships[fragment]) { fragmentRelationships[fragment] = []; } fragmentRelationships[fragment].push(contractName); }); } }); return fragmentRelationships; }