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('C:/ScriptoriumAI/scriptoriumai-ui/src/pages/Runboard.tsx'); const pagesDir = 'C:/ScriptoriumAI/scriptoriumai-ui/src/pages/Runboard'; if (!fs.existsSync(pagesDir)) fs.mkdirSync(pagesDir, { recursive: true }); // 1. Extract purely logic helpers into Runboard/utils.tsx const utilsFile = project.createSourceFile(pagesDir + '/utils.tsx', '', { overwrite: true }); // Collect all top-level functions (except Runboard) const stmts = sourceFile.getStatements(); const toMove = stmts.filter(s => s.getKindName() !== 'ImportDeclaration' && s.getKindName() !== 'ExportDeclaration' && !(s.isKind(SyntaxKind.FunctionDeclaration) && s.getName() === 'Runboard') ); toMove.forEach(s => { // Ensure exported if (s.isKind(SyntaxKind.FunctionDeclaration) || s.isKind(SyntaxKind.VariableStatement) || s.isKind(SyntaxKind.TypeAliasDeclaration) || s.isKind(SyntaxKind.InterfaceDeclaration)) { if (!s.hasExportKeyword()) { (s as any).setIsExported(true); } } utilsFile.addStatements(s.getText()); s.remove(); }); // Steal imports sourceFile.getImportDeclarations().forEach(imp => { utilsFile.addImportDeclaration({ isTypeOnly: imp.isTypeOnly(), defaultImport: imp.getDefaultImport()?.getText(), namedImports: imp.getNamedImports().map(n => ({ name: n.getName(), alias: n.getAliasNode()?.getText() })), moduleSpecifier: imp.getModuleSpecifierValue().startsWith('./') ? '../../' + imp.getModuleSpecifierValue().substring(2) : (imp.getModuleSpecifierValue().startsWith('../') ? '../../../' + imp.getModuleSpecifierValue().substring(3) : imp.getModuleSpecifierValue()) }); }); // Import them back into sourceFile const namesToImport: string[] = []; utilsFile.getStatements().forEach(s => { if (s.isKind(SyntaxKind.FunctionDeclaration) || s.isKind(SyntaxKind.TypeAliasDeclaration) || s.isKind(SyntaxKind.InterfaceDeclaration)) { const n = (s as any).getName?.(); if (n) namesToImport.push(n); } if (s.isKind(SyntaxKind.VariableStatement)) { s.getDeclarations().forEach(d => namesToImport.push(d.getName())); } }); sourceFile.addImportDeclaration({ namedImports: namesToImport, moduleSpecifier: './Runboard/utils' }); // 2. Componentize the Layout shell: // Let's create Layout.tsx which exports the wrapper const layoutFile = project.createSourceFile(pagesDir + '/Layout.tsx', '', { overwrite: true }); layoutFile.addStatements(` import React from 'react'; import { PlatformAuthProvider } from '../../auth/platform-auth'; import { SubscriptionGate } from '../../components/SubscriptionGate'; export function RunboardLayout({ children }: { children: React.ReactNode }) { return ( {children} ); } `); project.saveSync(); console.log('Runboard Componentization Phase 1 done: Splitting shell utils and wrappers.');