BREAKING CHANGE(core): Migrate filesystem to smartfs (async) and add Elasticsearch service support; refactor format/commit/meta modules

This commit is contained in:
2025-11-27 21:32:34 +00:00
parent 2f3d67f9e3
commit e1d28bc10a
30 changed files with 2217 additions and 995 deletions

View File

@@ -65,15 +65,15 @@ export abstract class BaseFormatter {
normalizedPath = './' + filepath;
}
await plugins.smartfile.memory.toFs(content, normalizedPath);
await plugins.smartfs.file(normalizedPath).encoding('utf8').write(content);
}
protected async createFile(filepath: string, content: string): Promise<void> {
await plugins.smartfile.memory.toFs(content, filepath);
await plugins.smartfs.file(filepath).encoding('utf8').write(content);
}
protected async deleteFile(filepath: string): Promise<void> {
await plugins.smartfile.fs.remove(filepath);
await plugins.smartfs.file(filepath).delete();
}
protected async shouldProcessFile(filepath: string): Promise<boolean> {

View File

@@ -25,7 +25,7 @@ export class ChangeCache {
}
async initialize(): Promise<void> {
await plugins.smartfile.fs.ensureDir(this.cacheDir);
await plugins.smartfs.directory(this.cacheDir).recursive().create();
}
async getManifest(): Promise<ICacheManifest> {
@@ -35,13 +35,16 @@ export class ChangeCache {
files: [],
};
const exists = await plugins.smartfile.fs.fileExists(this.manifestPath);
const exists = await plugins.smartfs.file(this.manifestPath).exists();
if (!exists) {
return defaultManifest;
}
try {
const content = plugins.smartfile.fs.toStringSync(this.manifestPath);
const content = (await plugins.smartfs
.file(this.manifestPath)
.encoding('utf8')
.read()) as string;
const manifest = JSON.parse(content);
// Validate the manifest structure
@@ -57,7 +60,7 @@ export class ChangeCache {
);
// Try to delete the corrupted file
try {
await plugins.smartfile.fs.remove(this.manifestPath);
await plugins.smartfs.file(this.manifestPath).delete();
} catch (removeError) {
// Ignore removal errors
}
@@ -72,11 +75,14 @@ export class ChangeCache {
}
// Ensure directory exists
await plugins.smartfile.fs.ensureDir(this.cacheDir);
await plugins.smartfs.directory(this.cacheDir).recursive().create();
// Write directly with proper JSON stringification
const jsonContent = JSON.stringify(manifest, null, 2);
await plugins.smartfile.memory.toFs(jsonContent, this.manifestPath);
await plugins.smartfs
.file(this.manifestPath)
.encoding('utf8')
.write(jsonContent);
}
async hasFileChanged(filePath: string): Promise<boolean> {
@@ -85,20 +91,23 @@ export class ChangeCache {
: plugins.path.join(paths.cwd, filePath);
// Check if file exists
const exists = await plugins.smartfile.fs.fileExists(absolutePath);
const exists = await plugins.smartfs.file(absolutePath).exists();
if (!exists) {
return true; // File doesn't exist, so it's "changed" (will be created)
}
// Get current file stats
const stats = await plugins.smartfile.fs.stat(absolutePath);
const stats = await plugins.smartfs.file(absolutePath).stat();
// Skip directories
if (stats.isDirectory()) {
if (stats.isDirectory) {
return false; // Directories are not processed
}
const content = plugins.smartfile.fs.toStringSync(absolutePath);
const content = (await plugins.smartfs
.file(absolutePath)
.encoding('utf8')
.read()) as string;
const currentChecksum = this.calculateChecksum(content);
// Get cached info
@@ -113,7 +122,7 @@ export class ChangeCache {
return (
cachedFile.checksum !== currentChecksum ||
cachedFile.size !== stats.size ||
cachedFile.modified !== stats.mtimeMs
cachedFile.modified !== stats.mtime.getTime()
);
}
@@ -123,14 +132,17 @@ export class ChangeCache {
: plugins.path.join(paths.cwd, filePath);
// Get current file stats
const stats = await plugins.smartfile.fs.stat(absolutePath);
const stats = await plugins.smartfs.file(absolutePath).stat();
// Skip directories
if (stats.isDirectory()) {
if (stats.isDirectory) {
return; // Don't cache directories
}
const content = plugins.smartfile.fs.toStringSync(absolutePath);
const content = (await plugins.smartfs
.file(absolutePath)
.encoding('utf8')
.read()) as string;
const checksum = this.calculateChecksum(content);
// Update manifest
@@ -140,7 +152,7 @@ export class ChangeCache {
const cacheEntry: IFileCache = {
path: filePath,
checksum,
modified: stats.mtimeMs,
modified: stats.mtime.getTime(),
size: stats.size,
};
@@ -176,7 +188,7 @@ export class ChangeCache {
? file.path
: plugins.path.join(paths.cwd, file.path);
if (await plugins.smartfile.fs.fileExists(absolutePath)) {
if (await plugins.smartfs.file(absolutePath).exists()) {
validFiles.push(file);
}
}

View File

@@ -21,14 +21,15 @@ export class DiffReporter {
}
try {
const exists = await plugins.smartfile.fs.fileExists(change.path);
const exists = await plugins.smartfs.file(change.path).exists();
if (!exists) {
return null;
}
const currentContent = await plugins.smartfile.fs.toStringSync(
change.path,
);
const currentContent = (await plugins.smartfs
.file(change.path)
.encoding('utf8')
.read()) as string;
// For planned changes, we need the new content
if (!change.content) {
@@ -107,10 +108,10 @@ export class DiffReporter {
})),
};
await plugins.smartfile.memory.toFs(
JSON.stringify(report, null, 2),
outputPath,
);
await plugins.smartfs
.file(outputPath)
.encoding('utf8')
.write(JSON.stringify(report, null, 2));
logger.log('info', `Diff report saved to ${outputPath}`);
}

View File

@@ -192,10 +192,10 @@ export class FormatStats {
moduleStats: Array.from(this.stats.moduleStats.values()),
};
await plugins.smartfile.memory.toFs(
JSON.stringify(report, null, 2),
outputPath,
);
await plugins.smartfs
.file(outputPath)
.encoding('utf8')
.write(JSON.stringify(report, null, 2));
logger.log('info', `Statistics report saved to ${outputPath}`);
}

View File

@@ -36,21 +36,27 @@ export class RollbackManager {
: plugins.path.join(paths.cwd, filepath);
// Check if file exists
const exists = await plugins.smartfile.fs.fileExists(absolutePath);
const exists = await plugins.smartfs.file(absolutePath).exists();
if (!exists) {
// File doesn't exist yet (will be created), so we skip backup
return;
}
// Read file content and metadata
const content = plugins.smartfile.fs.toStringSync(absolutePath);
const stats = await plugins.smartfile.fs.stat(absolutePath);
const content = (await plugins.smartfs
.file(absolutePath)
.encoding('utf8')
.read()) as string;
const stats = await plugins.smartfs.file(absolutePath).stat();
const checksum = this.calculateChecksum(content);
// Create backup
const backupPath = this.getBackupPath(operationId, filepath);
await plugins.smartfile.fs.ensureDir(plugins.path.dirname(backupPath));
await plugins.smartfile.memory.toFs(content, backupPath);
await plugins.smartfs
.directory(plugins.path.dirname(backupPath))
.recursive()
.create();
await plugins.smartfs.file(backupPath).encoding('utf8').write(content);
// Update operation
operation.files.push({
@@ -84,7 +90,10 @@ export class RollbackManager {
// Verify backup integrity
const backupPath = this.getBackupPath(operationId, file.path);
const backupContent = plugins.smartfile.fs.toStringSync(backupPath);
const backupContent = await plugins.smartfs
.file(backupPath)
.encoding('utf8')
.read();
const backupChecksum = this.calculateChecksum(backupContent);
if (backupChecksum !== file.checksum) {
@@ -92,7 +101,10 @@ export class RollbackManager {
}
// Restore file
await plugins.smartfile.memory.toFs(file.originalContent, absolutePath);
await plugins.smartfs
.file(absolutePath)
.encoding('utf8')
.write(file.originalContent);
// Restore permissions
const mode = parseInt(file.permissions, 8);
@@ -129,7 +141,7 @@ export class RollbackManager {
'operations',
operation.id,
);
await plugins.smartfile.fs.remove(operationDir);
await plugins.smartfs.directory(operationDir).recursive().delete();
// Remove from manifest
manifest.operations = manifest.operations.filter(
@@ -148,13 +160,16 @@ export class RollbackManager {
for (const file of operation.files) {
const backupPath = this.getBackupPath(operationId, file.path);
const exists = await plugins.smartfile.fs.fileExists(backupPath);
const exists = await plugins.smartfs.file(backupPath).exists();
if (!exists) {
return false;
}
const content = plugins.smartfile.fs.toStringSync(backupPath);
const content = await plugins.smartfs
.file(backupPath)
.encoding('utf8')
.read();
const checksum = this.calculateChecksum(content);
if (checksum !== file.checksum) {
@@ -171,10 +186,11 @@ export class RollbackManager {
}
private async ensureBackupDir(): Promise<void> {
await plugins.smartfile.fs.ensureDir(this.backupDir);
await plugins.smartfile.fs.ensureDir(
plugins.path.join(this.backupDir, 'operations'),
);
await plugins.smartfs.directory(this.backupDir).recursive().create();
await plugins.smartfs
.directory(plugins.path.join(this.backupDir, 'operations'))
.recursive()
.create();
}
private generateOperationId(): string {
@@ -204,13 +220,16 @@ export class RollbackManager {
private async getManifest(): Promise<{ operations: IFormatOperation[] }> {
const defaultManifest = { operations: [] };
const exists = await plugins.smartfile.fs.fileExists(this.manifestPath);
const exists = await plugins.smartfs.file(this.manifestPath).exists();
if (!exists) {
return defaultManifest;
}
try {
const content = plugins.smartfile.fs.toStringSync(this.manifestPath);
const content = (await plugins.smartfs
.file(this.manifestPath)
.encoding('utf8')
.read()) as string;
const manifest = JSON.parse(content);
// Validate the manifest structure
@@ -228,7 +247,7 @@ export class RollbackManager {
);
// Try to delete the corrupted file
try {
await plugins.smartfile.fs.remove(this.manifestPath);
await plugins.smartfs.file(this.manifestPath).delete();
} catch (removeError) {
// Ignore removal errors
}
@@ -249,7 +268,10 @@ export class RollbackManager {
// Write directly with proper JSON stringification
const jsonContent = JSON.stringify(manifest, null, 2);
await plugins.smartfile.memory.toFs(jsonContent, this.manifestPath);
await plugins.smartfs
.file(this.manifestPath)
.encoding('utf8')
.write(jsonContent);
}
private async getOperation(

View File

@@ -13,12 +13,12 @@ const filesToDelete = [
export const run = async (projectArg: Project) => {
for (const relativeFilePath of filesToDelete) {
const fileExists = plugins.smartfile.fs.fileExistsSync(relativeFilePath);
const fileExists = await plugins.smartfs.file(relativeFilePath).exists();
if (fileExists) {
logger.log('info', `Found ${relativeFilePath}! Removing it!`);
plugins.smartfile.fs.removeSync(
plugins.path.join(paths.cwd, relativeFilePath),
);
await plugins.smartfs
.file(plugins.path.join(paths.cwd, relativeFilePath))
.delete();
} else {
logger.log('info', `Project is free of ${relativeFilePath}`);
}

View File

@@ -24,7 +24,12 @@ export const run = async (projectArg: Project) => {
try {
// Handle glob patterns
const files = await plugins.smartfile.fs.listFileTree('.', pattern.from);
const entries = await plugins.smartfs
.directory('.')
.recursive()
.filter(pattern.from)
.list();
const files = entries.map((entry) => entry.path);
for (const file of files) {
const sourcePath = file;
@@ -46,10 +51,13 @@ export const run = async (projectArg: Project) => {
}
// Ensure destination directory exists
await plugins.smartfile.fs.ensureDir(plugins.path.dirname(destPath));
await plugins.smartfs
.directory(plugins.path.dirname(destPath))
.recursive()
.create();
// Copy file
await plugins.smartfile.fs.copy(sourcePath, destPath);
await plugins.smartfs.file(sourcePath).copy(destPath);
logger.log('info', `Copied ${sourcePath} to ${destPath}`);
}
} catch (error) {

View File

@@ -7,13 +7,15 @@ import { logger } from '../gitzone.logging.js';
const gitignorePath = plugins.path.join(paths.cwd, './.gitignore');
export const run = async (projectArg: Project) => {
const gitignoreExists = await plugins.smartfile.fs.fileExists(gitignorePath);
const gitignoreExists = await plugins.smartfs.file(gitignorePath).exists();
let customContent = '';
if (gitignoreExists) {
// lets get the existing gitignore file
const existingGitIgnoreString =
plugins.smartfile.fs.toStringSync(gitignorePath);
const existingGitIgnoreString = (await plugins.smartfs
.file(gitignorePath)
.encoding('utf8')
.read()) as string;
// Check for different custom section markers
const customMarkers = ['#------# custom', '# custom'];
@@ -34,12 +36,17 @@ export const run = async (projectArg: Project) => {
// Append the custom content if it exists
if (customContent) {
const newGitignoreContent =
plugins.smartfile.fs.toStringSync(gitignorePath);
const newGitignoreContent = (await plugins.smartfs
.file(gitignorePath)
.encoding('utf8')
.read()) as string;
// The template already ends with "#------# custom", so just append the content
const finalContent =
newGitignoreContent.trimEnd() + '\n' + customContent + '\n';
await plugins.smartfile.fs.toFs(finalContent, gitignorePath);
await plugins.smartfs
.file(gitignorePath)
.encoding('utf8')
.write(finalContent);
logger.log('info', 'Updated .gitignore while preserving custom section!');
} else {
logger.log('info', 'Added a .gitignore!');

View File

@@ -7,9 +7,9 @@ import { logger } from '../gitzone.logging.js';
const incompatibleLicenses: string[] = ['AGPL', 'GPL', 'SSPL'];
export const run = async (projectArg: Project) => {
const nodeModulesInstalled = await plugins.smartfile.fs.isDirectory(
plugins.path.join(paths.cwd, 'node_modules'),
);
const nodeModulesInstalled = await plugins.smartfs
.directory(plugins.path.join(paths.cwd, 'node_modules'))
.exists();
if (!nodeModulesInstalled) {
logger.log('warn', 'No node_modules found. Skipping license check');
return;

View File

@@ -174,9 +174,11 @@ export const run = async (projectArg: Project) => {
);
// set overrides
const overrides = plugins.smartfile.fs.toObjectSync(
plugins.path.join(paths.assetsDir, 'overrides.json'),
);
const overridesContent = (await plugins.smartfs
.file(plugins.path.join(paths.assetsDir, 'overrides.json'))
.encoding('utf8')
.read()) as string;
const overrides = JSON.parse(overridesContent);
packageJson.pnpm = packageJson.pnpm || {};
packageJson.pnpm.overrides = overrides;

View File

@@ -6,25 +6,22 @@ export const run = async () => {
const readmeHintsPath = plugins.path.join(paths.cwd, 'readme.hints.md');
// Check and initialize readme.md if it doesn't exist
const readmeExists = await plugins.smartfile.fs.fileExists(readmePath);
const readmeExists = await plugins.smartfs.file(readmePath).exists();
if (!readmeExists) {
await plugins.smartfile.fs.toFs(
'# Project Readme\n\nThis is the initial readme file.',
readmePath,
);
await plugins.smartfs.file(readmePath)
.encoding('utf8')
.write('# Project Readme\n\nThis is the initial readme file.');
console.log('Initialized readme.md');
} else {
console.log('readme.md already exists');
}
// Check and initialize readme.hints.md if it doesn't exist
const readmeHintsExists =
await plugins.smartfile.fs.fileExists(readmeHintsPath);
const readmeHintsExists = await plugins.smartfs.file(readmeHintsPath).exists();
if (!readmeHintsExists) {
await plugins.smartfile.fs.toFs(
'# Project Readme Hints\n\nThis is the initial readme hints file.',
readmeHintsPath,
);
await plugins.smartfs.file(readmeHintsPath)
.encoding('utf8')
.write('# Project Readme Hints\n\nThis is the initial readme hints file.');
console.log('Initialized readme.hints.md');
} else {
console.log('readme.hints.md already exists');

View File

@@ -7,10 +7,11 @@ import { Project } from '../classes.project.js';
export const run = async (projectArg: Project) => {
// lets care about tsconfig.json
logger.log('info', 'Formatting tsconfig.json...');
const tsconfigSmartfile = await plugins.smartfile.SmartFile.fromFilePath(
const factory = plugins.smartfile.SmartFileFactory.nodeFs();
const tsconfigSmartfile = await factory.fromFilePath(
plugins.path.join(paths.cwd, 'tsconfig.json'),
);
const tsconfigObject = JSON.parse(tsconfigSmartfile.contentBuffer.toString());
const tsconfigObject = JSON.parse(tsconfigSmartfile.parseContentAsString());
tsconfigObject.compilerOptions = tsconfigObject.compilerOptions || {};
tsconfigObject.compilerOptions.baseUrl = '.';
tsconfigObject.compilerOptions.paths = {};
@@ -23,8 +24,8 @@ export const run = async (projectArg: Project) => {
`./${publishModule}/index.js`,
];
}
tsconfigSmartfile.setContentsFromString(
JSON.stringify(tsconfigObject, null, 2),
);
await tsconfigSmartfile.editContentAsString(async () => {
return JSON.stringify(tsconfigObject, null, 2);
});
await tsconfigSmartfile.write();
};

View File

@@ -20,7 +20,7 @@ export class CleanupFormatter extends BaseFormatter {
];
for (const file of filesToRemove) {
const exists = await plugins.smartfile.fs.fileExists(file);
const exists = await plugins.smartfs.file(file).exists();
if (exists) {
changes.push({
type: 'delete',

View File

@@ -41,16 +41,23 @@ export class PrettierFormatter extends BaseFormatter {
// Add files from TypeScript directories
for (const dir of includeDirs) {
const globPattern = `${dir}/**/*.${extensions}`;
const dirFiles = await plugins.smartfile.fs.listFileTree(
'.',
globPattern,
);
const dirEntries = await plugins.smartfs
.directory('.')
.recursive()
.filter(globPattern)
.list();
const dirFiles = dirEntries.map((entry) => entry.path);
allFiles.push(...dirFiles);
}
// Add root config files
for (const pattern of rootConfigFiles) {
const rootFiles = await plugins.smartfile.fs.listFileTree('.', pattern);
const rootEntries = await plugins.smartfs
.directory('.')
.recursive()
.filter(pattern)
.list();
const rootFiles = rootEntries.map((entry) => entry.path);
// Only include files at root level (no slashes in path)
const rootLevelFiles = rootFiles.filter((f) => !f.includes('/'));
allFiles.push(...rootLevelFiles);
@@ -66,8 +73,8 @@ export class PrettierFormatter extends BaseFormatter {
const validFiles: string[] = [];
for (const file of files) {
try {
const stats = await plugins.smartfile.fs.stat(file);
if (!stats.isDirectory()) {
const stats = await plugins.smartfs.file(file).stat();
if (!stats.isDirectory) {
validFiles.push(file);
}
} catch (error) {
@@ -148,7 +155,10 @@ export class PrettierFormatter extends BaseFormatter {
}
// Read current content
const content = plugins.smartfile.fs.toStringSync(change.path);
const content = (await plugins.smartfs
.file(change.path)
.encoding('utf8')
.read()) as string;
// Format with prettier
const prettier = await import('prettier');

View File

@@ -101,7 +101,12 @@ export let run = async (
// Plan phase
logger.log('info', 'Analyzing project for format operations...');
let plan = options.fromPlan
? JSON.parse(await plugins.smartfile.fs.toStringSync(options.fromPlan))
? JSON.parse(
(await plugins.smartfs
.file(options.fromPlan)
.encoding('utf8')
.read()) as string,
)
: await planner.planFormat(activeFormatters);
// Display plan
@@ -109,10 +114,10 @@ export let run = async (
// Save plan if requested
if (options.savePlan) {
await plugins.smartfile.memory.toFs(
JSON.stringify(plan, null, 2),
options.savePlan,
);
await plugins.smartfs
.file(options.savePlan)
.encoding('utf8')
.write(JSON.stringify(plan, null, 2));
logger.log('info', `Plan saved to ${options.savePlan}`);
}