UNPKG

@remediator/core

Version:
202 lines (186 loc) 7.76 kB
import fs from "fs"; import path from "path"; const generateItems = async (options) => { const { path: searchPath = "app", includeFileNames = [ "*.mediator.ts", "*.mediator.server.ts", "*.server.mediator.ts", ], outputDir = "remediator", } = options; console.log("reMediator: Generating client..."); try { // Find all mediator files const allFiles = []; for (const pattern of includeFileNames) { const files = await findFilesRecursively(searchPath, [pattern]); allFiles.push(...files); } if (allFiles.length === 0) { console.warn("reMediator: No files found for auto-registration"); return; } console.log(`reMediator: Found ${allFiles.length} mediator files`); // Generate manifest const manifest = { generatedAt: new Date().toISOString(), files: allFiles.map((filePath) => ({ path: path.relative(process.cwd(), filePath).replace(/\\/g, "/"), name: path.basename(filePath), })), }; // --- Start of new logic --- const outputDirAbs = path.resolve(process.cwd(), outputDir); // 1. Generate imports with paths relative to the output directory const imports = allFiles.map((filePath) => { // Get path relative to the output directory for the import statement const importPath = path .relative(outputDirAbs, filePath) .replace(/\\/g, "/") .replace(/\.ts$/, ""); // Get a unique module name from the path relative to the project root const moduleName = getModuleName(path.relative(process.cwd(), filePath)); return `import * as ${moduleName} from "${importPath}";`; }); const moduleNames = allFiles.map((filePath) => getModuleName(path.relative(process.cwd(), filePath))); // 2. Generate registration logic that aggregates all exports first const registrationLogic = ` const allExports = new Map(); const allModules = [${moduleNames.join(", ")}]; for (const module of allModules) { for (const [key, value] of Object.entries(module)) { if (!allExports.has(key)) { allExports.set(key, value); } } } //console.log('reMediator: All found exports:', Array.from(allExports.keys())); const registeredItems: { Type: string; Name: string; Details: string }[] = []; // Register middleware before handlers allExports.forEach((value, key) => { // Convention: middleware functions end with 'Middleware' if (key.endsWith('Middleware') && typeof value === 'function') { reMediator.use(value as any); registeredItems.push({ Type: 'Middleware', Name: key, Details: 'Registered' }); } }); allExports.forEach((value, key) => { // Convention: handler classes end with 'Handler' if (key.endsWith('Handler')) { const handler = value; if (handler !== null && typeof handler === 'function') { const requestKey = key.replace('Handler', ''); const requestCtor = allExports.get(requestKey); if (requestCtor && typeof requestCtor === 'function') { reMediator.register(requestCtor as any, new (handler as any)()); registeredItems.push({ Type: 'Handler', Name: key, Details: \`Handles -> \${requestKey}\` }); } } } }); console.table(registeredItems); `; // 3. Generate the client code const clientCode = `// Auto-generated by reMediator plugin // Generated at: ${new Date().toISOString()} // Files found: ${allFiles.length} ${imports.join("\n")} import { reMediator as ReMediatorClass } from '@remediator/core'; // Create a new instance for the client export const reMediator = new ReMediatorClass(); // Auto-registration function function registerAllHandlers() { console.log('reMediator: Auto-registering ${allFiles.length} modules...'); ${registrationLogic} console.log('reMediator: Auto-registration complete'); } // Auto-register when this code runs registerAllHandlers(); // No need to export reMediator again, it's already exported as a const`; // --- End of new logic --- // Create output directory if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } // Write files fs.writeFileSync(path.join(outputDir, "manifest.json"), JSON.stringify(manifest, null, 2)); fs.writeFileSync(path.join(outputDir, "client.ts"), clientCode); console.log(`reMediator: Generated client at ${outputDir}/client.ts`); console.log(`reMediator: Generated manifest at ${outputDir}/manifest.json`); } catch (error) { console.error("reMediator: Error generating client:", error); } }; export function reMediatorPlugin(options = {}) { const { path: searchPath = "app", includeFileNames = [ "*.mediator.ts", "*.mediator.server.ts", "*.server.mediator.ts", ], outputDir = "remediator", } = options; return { name: "remediator-auto-registration", async buildStart() { await generateItems(options); }, async configureServer(server) { // Generate client on server start await generateItems(options); // In development, watch for changes and regenerate const watchPatterns = includeFileNames; watchPatterns.forEach((pattern) => { server.watcher.add(`${searchPath}/**/${pattern}`); }); server.watcher.on("change", async (file) => { // Check if the changed file matches any of our patterns const fileName = path.basename(file); const fileMatchesPattern = includeFileNames.some((pattern) => matchesPattern(fileName, pattern)); if (fileMatchesPattern) { console.log("reMediator: File changed, regenerating client..."); await generateItems(options); } }); }, }; } // Simple recursive file search async function findFilesRecursively(dirPath, patterns) { const files = []; try { const items = fs.readdirSync(dirPath); for (const item of items) { const fullPath = path.join(dirPath, item); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { if (item === "node_modules" || item === "dist" || item === "build" || item === ".git") { continue; } const subFiles = await findFilesRecursively(fullPath, patterns); files.push(...subFiles); } else if (stat.isFile()) { const fileName = item; if (patterns.some((pattern) => matchesPattern(fileName, pattern))) { files.push(fullPath); } } } } catch (error) { // Only log if the directory doesn't exist, not for other errors if (error.code !== "ENOENT") { console.warn(`reMediator: Error reading directory ${dirPath}:`, error.message); } } return files; } // Simple pattern matching function matchesPattern(fileName, pattern) { const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*"); const regex = new RegExp(`^${regexPattern}$`); return regex.test(fileName); } function getModuleName(modulePath) { return modulePath.replace(/[^a-zA-Z0-9]/g, "_"); }