ruch
Version:
Revolutionary React TypeScript CLI with hexagonal architecture & AI-powered development assistance. Create maintainable, scalable applications with domain-driven design and integrated AI tooling.
164 lines (139 loc) • 4.77 kB
text/typescript
import fs from 'fs-extra';
import path from 'path';
import { logger } from '../utils/logger';
import { getDomainsSourceDirectoryPath } from '../utils/paths';
import type { FileSystem } from '../utils/file-operations';
import {
validateFileExists,
readDirectory,
getFileMetadata
} from '../utils/file-operations';
import type { Logger } from '../utils/logging';
import {
logError,
logNoDomainsFound,
logDomainsListed
} from '../utils/logging';
// Types
interface DomainStructure {
name: string;
path: string;
folders: string[];
}
interface DomainDisplay {
icon: string;
name: string;
folders: Array<{
icon: string;
name: string;
}>;
}
// Pure functions
const buildDomainDirectoryPath = (basePath: string, domainName: string): string =>
path.join(basePath, domainName);
const isDirectory = (stats: fs.Stats): boolean =>
stats.isDirectory();
const sortDomainFolders = (folders: string[]): string[] => {
const folderOrder = ['entities', 'ports', 'services', 'adapters', 'hooks', 'ui'];
return folders.sort((a, b) => {
const aIndex = folderOrder.indexOf(a);
const bIndex = folderOrder.indexOf(b);
if (aIndex === -1 && bIndex === -1) return a.localeCompare(b);
if (aIndex === -1) return 1;
if (bIndex === -1) return -1;
return aIndex - bIndex;
});
};
const getDomainDisplayIcon = (index: number, total: number): string =>
index === total - 1 ? '└──' : '├──';
const getFolderDisplayIcon = (folderIndex: number, totalFolders: number, isLastDomain: boolean): string => {
if (folderIndex === totalFolders - 1 && isLastDomain) return ' └──';
if (folderIndex === totalFolders - 1) return '│ └──';
if (isLastDomain) return ' ├──';
return '│ ├──';
};
// Domain operations
const getDomainFolderStructure = async (
fileSystem: FileSystem,
domainPath: string
): Promise<string[]> => {
const directoryEntries = await fileSystem.readdir(domainPath);
const domainFolders: string[] = [];
for (const entry of directoryEntries) {
const entryPath = path.join(domainPath, entry);
const stats = await fileSystem.stat(entryPath);
if (isDirectory(stats)) {
domainFolders.push(entry);
}
}
return sortDomainFolders(domainFolders);
};
const analyzeDomainStructures = async (
fileSystem: FileSystem,
basePath: string,
entries: string[]
): Promise<DomainStructure[]> => {
const domainStructures: DomainStructure[] = [];
for (const entry of entries) {
const entryPath = buildDomainDirectoryPath(basePath, entry);
const stats = await fileSystem.stat(entryPath);
if (isDirectory(stats)) {
const folders = await getDomainFolderStructure(fileSystem, entryPath);
domainStructures.push({ name: entry, path: entryPath, folders });
}
}
return domainStructures;
};
// Display operations
const formatDomainDisplay = (
domain: DomainStructure,
index: number,
total: number
): DomainDisplay => {
const icon = getDomainDisplayIcon(index, total);
const folders = domain.folders.map((folder, folderIndex) => ({
icon: getFolderDisplayIcon(folderIndex, domain.folders.length, index === total - 1),
name: folder
}));
return { icon, name: domain.name, folders };
};
const displayDomainStructure = (log: Logger, display: DomainDisplay): void => {
log.log(`${display.icon} 📁 ${display.name}`);
display.folders.forEach(folder => {
log.log(`${folder.icon} 📂 ${folder.name}`);
});
};
const displayDomainSeparator = (log: Logger, index: number, total: number): void => {
if (index < total - 1) {
log.log('│');
}
};
// Main function
export const listDomains = async (
fileSystem: FileSystem = fs,
log: Logger = logger
): Promise<void> => {
const domainsSourcePath = getDomainsSourceDirectoryPath();
try {
if (!(await fileSystem.exists(domainsSourcePath))) {
logNoDomainsFound(log, domainsSourcePath);
return;
}
const directoryEntries = await fileSystem.readdir(domainsSourcePath);
const domainStructures = await analyzeDomainStructures(fileSystem, domainsSourcePath, directoryEntries);
if (domainStructures.length === 0) {
logNoDomainsFound(log, domainsSourcePath);
return;
}
logDomainsListed(log, domainStructures.length, domainsSourcePath);
log.log('');
domainStructures.forEach((domain, index) => {
const display = formatDomainDisplay(domain, index, domainStructures.length);
displayDomainStructure(log, display);
displayDomainSeparator(log, index, domainStructures.length);
});
log.log('');
} catch (error: unknown) {
logError(log, `Error reading domains: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
};