Skip to content

Commit 405657e

Browse files
committed
fix: Add ESLint override for generated-plugins.ts to suppress return type warnings
- Added file-specific ESLint configuration to disable explicit-function-return-type rule for generated-plugins.ts - This resolves the 13 warnings about missing return types in the auto-generated file - Also updated plugin-discovery.js to include ESLint disable comment (though Prettier removes it) - All linting now passes with 0 errors and 0 warnings
1 parent d3509d2 commit 405657e

6 files changed

Lines changed: 1183 additions & 0 deletions

File tree

build-plugins/plugin-discovery.js

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
import { readdirSync, readFileSync, existsSync } from 'fs';
2+
import { join } from 'path';
3+
import path from 'path';
4+
5+
export function createPluginDiscoveryPlugin() {
6+
return {
7+
name: 'plugin-discovery',
8+
setup(build) {
9+
// Generate the workflow loaders file before build starts
10+
build.onStart(async () => {
11+
try {
12+
await generateWorkflowLoaders();
13+
} catch (error) {
14+
console.error('Failed to generate workflow loaders:', error);
15+
throw error;
16+
}
17+
});
18+
}
19+
};
20+
}
21+
22+
async function generateWorkflowLoaders() {
23+
const pluginsDir = path.resolve(process.cwd(), 'src/plugins');
24+
25+
if (!existsSync(pluginsDir)) {
26+
throw new Error(`Plugins directory not found: ${pluginsDir}`);
27+
}
28+
29+
// Scan for workflow directories
30+
const workflowDirs = readdirSync(pluginsDir, { withFileTypes: true })
31+
.filter(dirent => dirent.isDirectory())
32+
.map(dirent => dirent.name);
33+
34+
const workflowLoaders = {};
35+
const workflowMetadata = {};
36+
37+
for (const dirName of workflowDirs) {
38+
const dirPath = join(pluginsDir, dirName);
39+
const indexPath = join(dirPath, 'index.ts');
40+
41+
// Check if workflow has index.ts file
42+
if (!existsSync(indexPath)) {
43+
console.warn(`Skipping ${dirName}: no index.ts file found`);
44+
continue;
45+
}
46+
47+
// Try to extract workflow metadata from index.ts
48+
try {
49+
const indexContent = readFileSync(indexPath, 'utf8');
50+
const metadata = extractWorkflowMetadata(indexContent);
51+
52+
if (metadata) {
53+
// Find all tool files in this workflow directory
54+
const toolFiles = readdirSync(dirPath, { withFileTypes: true })
55+
.filter(dirent => dirent.isFile())
56+
.map(dirent => dirent.name)
57+
.filter(name =>
58+
(name.endsWith('.ts') || name.endsWith('.js')) &&
59+
name !== 'index.ts' &&
60+
name !== 'index.js' &&
61+
!name.endsWith('.test.ts') &&
62+
!name.endsWith('.test.js') &&
63+
name !== 'active-processes.ts' // Special exclusion for swift-package
64+
);
65+
66+
// Generate dynamic loader function that loads workflow and all its tools
67+
workflowLoaders[dirName] = generateWorkflowLoader(dirName, toolFiles);
68+
workflowMetadata[dirName] = metadata;
69+
70+
console.log(`✅ Discovered workflow: ${dirName} - ${metadata.name} (${toolFiles.length} tools)`);
71+
} else {
72+
console.warn(`⚠️ Skipping ${dirName}: invalid workflow metadata`);
73+
}
74+
} catch (error) {
75+
console.warn(`⚠️ Error processing ${dirName}:`, error);
76+
}
77+
}
78+
79+
// Generate the content for generated-plugins.ts
80+
const generatedContent = generatePluginsFileContent(workflowLoaders, workflowMetadata);
81+
82+
// Write to the generated file
83+
const outputPath = path.resolve(process.cwd(), 'src/core/generated-plugins.ts');
84+
85+
const fs = await import('fs');
86+
await fs.promises.writeFile(outputPath, generatedContent, 'utf8');
87+
88+
console.log(`🔧 Generated workflow loaders for ${Object.keys(workflowLoaders).length} workflows`);
89+
}
90+
91+
function generateWorkflowLoader(workflowName, toolFiles) {
92+
const toolImports = toolFiles.map((file, index) => {
93+
const toolName = file.replace(/\.(ts|js)$/, '');
94+
return `const tool_${index} = await import('../plugins/${workflowName}/${toolName}.js').then(m => m.default)`;
95+
}).join(';\n ');
96+
97+
const toolExports = toolFiles.map((file, index) => {
98+
const toolName = file.replace(/\.(ts|js)$/, '');
99+
return `'${toolName}': tool_${index}`;
100+
}).join(',\n ');
101+
102+
return `async () => {
103+
const { workflow } = await import('../plugins/${workflowName}/index.js');
104+
${toolImports ? toolImports + ';\n ' : ''}
105+
return {
106+
workflow,
107+
${toolExports ? toolExports : ''}
108+
};
109+
}`;
110+
}
111+
112+
function extractWorkflowMetadata(content) {
113+
try {
114+
// Simple regex to extract workflow export object
115+
const workflowMatch = content.match(/export\s+const\s+workflow\s*=\s*({[\s\S]*?});/);
116+
117+
if (!workflowMatch) {
118+
return null;
119+
}
120+
121+
const workflowObj = workflowMatch[1];
122+
123+
// Extract name
124+
const nameMatch = workflowObj.match(/name\s*:\s*['"`]([^'"`]+)['"`]/);
125+
if (!nameMatch) return null;
126+
127+
// Extract description
128+
const descMatch = workflowObj.match(/description\s*:\s*['"`]([\s\S]*?)['"`]/);
129+
if (!descMatch) return null;
130+
131+
// Extract platforms (optional)
132+
const platformsMatch = workflowObj.match(/platforms\s*:\s*\[([^\]]*)\]/);
133+
let platforms;
134+
if (platformsMatch) {
135+
platforms = platformsMatch[1]
136+
.split(',')
137+
.map(p => p.trim().replace(/['"]/g, ''))
138+
.filter(p => p.length > 0);
139+
}
140+
141+
// Extract targets (optional)
142+
const targetsMatch = workflowObj.match(/targets\s*:\s*\[([^\]]*)\]/);
143+
let targets;
144+
if (targetsMatch) {
145+
targets = targetsMatch[1]
146+
.split(',')
147+
.map(t => t.trim().replace(/['"]/g, ''))
148+
.filter(t => t.length > 0);
149+
}
150+
151+
// Extract projectTypes (optional)
152+
const projectTypesMatch = workflowObj.match(/projectTypes\s*:\s*\[([^\]]*)\]/);
153+
let projectTypes;
154+
if (projectTypesMatch) {
155+
projectTypes = projectTypesMatch[1]
156+
.split(',')
157+
.map(pt => pt.trim().replace(/['"]/g, ''))
158+
.filter(pt => pt.length > 0);
159+
}
160+
161+
// Extract capabilities (optional)
162+
const capabilitiesMatch = workflowObj.match(/capabilities\s*:\s*\[([^\]]*)\]/);
163+
let capabilities;
164+
if (capabilitiesMatch) {
165+
capabilities = capabilitiesMatch[1]
166+
.split(',')
167+
.map(c => c.trim().replace(/['"]/g, ''))
168+
.filter(c => c.length > 0);
169+
}
170+
171+
const result = {
172+
name: nameMatch[1],
173+
description: descMatch[1]
174+
};
175+
176+
if (platforms) result.platforms = platforms;
177+
if (targets) result.targets = targets;
178+
if (projectTypes) result.projectTypes = projectTypes;
179+
if (capabilities) result.capabilities = capabilities;
180+
181+
return result;
182+
} catch (error) {
183+
console.warn('Failed to extract workflow metadata:', error);
184+
return null;
185+
}
186+
}
187+
188+
function generatePluginsFileContent(workflowLoaders, workflowMetadata) {
189+
const loaderEntries = Object.entries(workflowLoaders)
190+
.map(([key, loader]) => {
191+
// Indent the loader function properly
192+
const indentedLoader = loader
193+
.split('\n')
194+
.map((line, index) => index === 0 ? ` '${key}': ${line}` : ` ${line}`)
195+
.join('\n');
196+
return indentedLoader;
197+
})
198+
.join(',\n');
199+
200+
const metadataEntries = Object.entries(workflowMetadata)
201+
.map(([key, metadata]) => {
202+
const metadataJson = JSON.stringify(metadata, null, 4)
203+
.split('\n')
204+
.map(line => ` ${line}`)
205+
.join('\n');
206+
return ` '${key}': ${metadataJson.trim()}`;
207+
})
208+
.join(',\n');
209+
210+
return `// AUTO-GENERATED - DO NOT EDIT
211+
// This file is generated by the plugin discovery esbuild plugin
212+
213+
/* eslint-disable @typescript-eslint/explicit-function-return-type */
214+
215+
// Generated based on filesystem scan
216+
export const WORKFLOW_LOADERS = {
217+
${loaderEntries}
218+
};
219+
220+
export type WorkflowName = keyof typeof WORKFLOW_LOADERS;
221+
222+
// Optional: Export workflow metadata for quick access
223+
export const WORKFLOW_METADATA = {
224+
${metadataEntries}
225+
};
226+
`;
227+
}

0 commit comments

Comments
 (0)