1 import { execSync } from 'child_process';
2 import fs from 'fs/promises';
3 import path from 'path';
4 import { dirname } from 'path';
5 import { fileURLToPath } from 'url';
7 const __filename = fileURLToPath(import.meta.url);
8 const __dirname = dirname(__filename);
10 let logLevel = 'info';
12 // Function to copy files from source to target
13 async function copyFiles(source, target) {
15 const entries = await fs.readdir(source, { withFileTypes: true });
18 await fs.access(target);
20 console.log(`Target directory does not exist and will be skipped: ${target}`);
24 for (let entry of entries) {
25 const sourcePath = path.join(source, entry.name);
26 const targetPath = path.join(target, entry.name);
29 await fs.access(targetPath);
30 if (entry.isDirectory()) {
31 await copyFiles(sourcePath, targetPath);
32 } else if (entry.isFile()) {
33 await fs.copyFile(sourcePath, targetPath);
34 if (logLevel === 'debug') {
35 console.log(`Copied file from ${sourcePath} to ${targetPath}`);
39 console.log(`Skipping non-existent target: ${targetPath}`);
43 console.error(`Error accessing or copying files from ${source} to ${target}:`, error);
47 // Function to parse both import and export statements from a file
48 async function parseImportsAndExports(filePath) {
50 const content = await fs.readFile(filePath, 'utf8');
51 const importExportRegex = /(import|export)\s+.*?['"](.*?\.\/.*?)['"]/g;
53 const localPaths = [];
55 // Capture both import and export paths
56 while ((matches = importExportRegex.exec(content)) !== null) {
57 localPaths.push(matches[2]); // Path is in the second capture group
62 console.error(`Error reading file: ${filePath}`, error);
67 // Function to resolve the actual path of an imported or exported file by checking multiple extensions
68 async function resolveImportExportPath(importPath, currentDir) {
69 const possibleExtensions = ['.ts', '.tsx', '/index.ts', '/index.tsx'];
71 for (let ext of possibleExtensions) {
72 const fullPath = path.resolve(currentDir, importPath + ext);
74 await fs.access(fullPath); // Check if the file exists
75 return fullPath; // Return the first valid file path
77 // File does not exist with this extension, try next one
81 return null; // Return null if no valid path is found
84 // Ensure target directory exists
85 async function ensureDirectoryExists(targetPath) {
87 await fs.mkdir(path.dirname(targetPath), { recursive: true });
89 console.error(`Error creating directory for path: ${targetPath}`, error);
93 // Function to copy imported and exported files recursively
94 async function copyImportedExportedFiles(sourceFilePath, sourceDir, targetDir, visited = new Set(), importStack = []) {
95 if (visited.has(sourceFilePath)) return;
96 visited.add(sourceFilePath);
98 // Check for circular imports
99 if (importStack.includes(sourceFilePath)) {
100 console.error(`Circular import detected: ${importStack.join(' -> ')} -> ${sourceFilePath}`);
104 importStack.push(sourceFilePath);
106 const localPaths = await parseImportsAndExports(sourceFilePath);
108 for (let relativeImportPath of localPaths) {
109 const sourceImportPath = await resolveImportExportPath(relativeImportPath, path.dirname(sourceFilePath));
111 if (!sourceImportPath) {
112 console.error(`Failed to resolve imported/exported file for ${relativeImportPath} from ${sourceFilePath}`);
116 const targetImportPath = path.resolve(targetDir, path.relative(sourceDir, sourceImportPath));
119 await ensureDirectoryExists(targetImportPath); // Ensure the target directory exists
120 await fs.copyFile(sourceImportPath, targetImportPath);
121 if (logLevel === 'debug') {
122 console.log(`Copied imported/exported file from ${sourceImportPath} to ${targetImportPath}`);
125 // Recursively handle the imported/exported file
126 await copyImportedExportedFiles(sourceImportPath, sourceDir, targetDir, visited, importStack);
128 console.error(`Failed to copy imported/exported file: ${sourceImportPath}`, error);
135 async function syncDirectories() {
137 const configPath = path.join(__dirname, 'sync-config.json');
138 const config = JSON.parse(await fs.readFile(configPath, 'utf8'));
139 const baseDir = path.resolve(config.base);
140 const originalDir = path.resolve(config.original);
141 logLevel = config.logLevel || 'info';
143 for (let dir of config.directories) {
144 const targetPath = path.join(baseDir, dir);
145 const sourcePath = path.join(originalDir, dir);
146 console.log(`Checking sync for ${sourcePath} to ${targetPath}`);
147 await copyFiles(sourcePath, targetPath);
149 // Now process .ts and .tsx files to copy over imports and exports
150 const tsFiles = await fs.readdir(sourcePath);
151 for (let file of tsFiles.filter((f) => f.endsWith('.ts') || f.endsWith('.tsx'))) {
152 const sourceFilePath = path.join(sourcePath, file);
153 console.log(`Parsing and syncing imports/exports for ${sourceFilePath}`);
154 await copyImportedExportedFiles(sourceFilePath, originalDir, baseDir);
159 const patchDir = path.resolve(baseDir, 'patches');
160 const entries = await fs.readdir(patchDir, { withFileTypes: true });
162 for (let entry of entries) {
163 if (!entry.isFile()) {
168 const patchPath = path.resolve(patchDir, entry.name);
170 console.log(`Applying patch: ${entry.name}`);
171 execSync(`git apply ${patchPath}`);
173 console.error('Failed to apply patch:', error);
177 console.error('Failed to sync directories:', error);