openpetswithchatandmcp/website/runboard-ts-morph-split.ts
OpenPets Dev 65ac83849e Add 'website/' from commit '966aea480ffe1c340553aa878def30131d2af827'
git-subtree-dir: website
git-subtree-mainline: 8d3849caea
git-subtree-split: 966aea480f
2026-06-17 20:48:39 +00:00

109 lines
4 KiB
TypeScript
Executable file

import { Project, SyntaxKind } from 'ts-morph';
import * as fs from 'fs';
import * as path from 'path';
const project = new Project({
tsConfigFilePath: 'C:/ScriptoriumAI/scriptoriumai-ui/tsconfig.json'
});
const sourceFile = project.getSourceFileOrThrow('src/pages/Runboard.tsx');
const destFilePath = 'src/utils/runboard-route-logic.tsx';
let destFile = project.getSourceFile(destFilePath);
if (destFile) destFile.deleteImmediatelySync();
destFile = project.createSourceFile(destFilePath);
// Find export function Runboard
const runboardNode = sourceFile.getFunction('Runboard');
if (!runboardNode) throw new Error('Runboard not found!');
const runboardPos = runboardNode.getStart();
// Collect all statements before Runboard
const statements = sourceFile.getStatements();
const toMove = statements.filter(stmt => stmt.getStart() < runboardPos && stmt.getKindName() !== 'ImportDeclaration');
const movedNames: string[] = [];
toMove.forEach(stmt => {
// If it's a dec we can export it
if (stmt.isKind(SyntaxKind.VariableStatement) ||
stmt.isKind(SyntaxKind.FunctionDeclaration) ||
stmt.isKind(SyntaxKind.TypeAliasDeclaration) ||
stmt.isKind(SyntaxKind.InterfaceDeclaration)) {
// Force export
if (!stmt.hasExportKeyword()) {
// stmt.setIsExported(true); this requires casting
(stmt as any).setIsExported?.(true);
}
if (stmt.isKind(SyntaxKind.VariableStatement)) {
stmt.getDeclarations().forEach(d => movedNames.push(d.getName()));
} else {
const name = (stmt as any).getName?.();
if (name) movedNames.push(name);
}
destFile.addStatements(stmt.getText());
stmt.remove();
}
});
// Update imports in destFile (lazy way: just copy them all and let eslint/ts-morph sort out unused later, but we can do it explicitly)
const imports = sourceFile.getImportDeclarations();
imports.forEach(imp => {
// We adjust relative paths up one level
const moduleSpec = imp.getModuleSpecifierValue();
let newModule = moduleSpec;
if (newModule.startsWith('./')) {
newModule = '../pages/' + newModule.substring(2);
}
// Just copy all imports to destFile
destFile.addImportDeclaration({
isTypeOnly: imp.isTypeOnly(),
defaultImport: imp.getDefaultImport()?.getText(),
namedImports: imp.getNamedImports().map(ni => ({ name: ni.getName(), alias: ni.getAliasNode()?.getText() })),
moduleSpecifier: newModule
});
});
// Now import the extracted names back into Runboard
const uniqueNames = Array.from(new Set(movedNames));
sourceFile.addImportDeclaration({
namedImports: uniqueNames,
moduleSpecifier: '../utils/runboard-route-logic'
});
// Replace test imports
const testFiles = project.getSourceFiles('src/__tests__/**/*.{ts,tsx}');
testFiles.forEach(testFile => {
const testImports = testFile.getImportDeclarations();
testImports.forEach(imp => {
const mod = imp.getModuleSpecifierValue();
if (mod === '../pages/Runboard' || mod === '../../pages/Runboard') {
// Check if it imports Runboard itself
const named = imp.getNamedImports();
const hasRunboard = named.some(n => n.getName() === 'Runboard');
const otherImports = named.filter(n => n.getName() !== 'Runboard');
if (otherImports.length > 0) {
// Add new import for logic
const logicMod = mod.replace('pages/Runboard', 'utils/runboard-route-logic');
testFile.addImportDeclaration({
namedImports: otherImports.map(n => ({ name: n.getName(), alias: n.getAliasNode()?.getText() })),
moduleSpecifier: logicMod
});
}
if (hasRunboard) {
// Remove other imports from existing
otherImports.forEach(n => n.remove());
} else {
imp.remove();
}
}
});
});
project.saveSync();
console.log('Successfully factored Runboard dependencies using ts-morph!');