fix(smartfile): Stream and filesystem integrations: remove fs-extra, switch to fs/promises.rename, add Web WritableStream compatibility, and use smartFs recursive directory methods; update plugins exports and README.
This commit is contained in:
@@ -1,95 +1,181 @@
|
||||
/**
|
||||
* Mock SmartFs implementation for testing until @push.rocks/smartfs is available
|
||||
* This wraps fs-extra to provide the SmartFs interface
|
||||
* Mock SmartFs implementation for testing
|
||||
* Provides fluent API matching @push.rocks/smartfs using native Node.js fs
|
||||
*/
|
||||
import { ensureDir, pathExists, remove, copy } from 'fs-extra';
|
||||
import { promises as fsPromises, createReadStream, createWriteStream } from 'fs';
|
||||
import * as path from 'path';
|
||||
import { Readable, Writable } from 'stream';
|
||||
|
||||
export class MockSmartFs {
|
||||
public file(filePath: string) {
|
||||
return {
|
||||
async read(): Promise<string | Buffer> {
|
||||
return await fsPromises.readFile(filePath);
|
||||
},
|
||||
async write(content: string | Buffer): Promise<void> {
|
||||
await ensureDir(path.dirname(filePath));
|
||||
await fsPromises.writeFile(filePath, content);
|
||||
},
|
||||
async exists(): Promise<boolean> {
|
||||
return await pathExists(filePath);
|
||||
},
|
||||
async delete(): Promise<void> {
|
||||
await remove(filePath);
|
||||
},
|
||||
async stat(): Promise<any> {
|
||||
return await fsPromises.stat(filePath);
|
||||
},
|
||||
async readStream(): Promise<Readable> {
|
||||
return Promise.resolve(createReadStream(filePath));
|
||||
},
|
||||
async writeStream(): Promise<Writable> {
|
||||
await ensureDir(path.dirname(filePath));
|
||||
return Promise.resolve(createWriteStream(filePath));
|
||||
},
|
||||
async copy(dest: string): Promise<void> {
|
||||
await copy(filePath, dest);
|
||||
},
|
||||
};
|
||||
/**
|
||||
* Mock SmartFsFile - Fluent file operations builder
|
||||
*/
|
||||
class MockSmartFsFile {
|
||||
private filePath: string;
|
||||
private options: {
|
||||
encoding?: BufferEncoding;
|
||||
recursive?: boolean;
|
||||
} = {};
|
||||
|
||||
constructor(filePath: string) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public directory(dirPath: string) {
|
||||
return {
|
||||
async list(options?: { recursive?: boolean }): Promise<Array<{ path: string; isFile: boolean; isDirectory: boolean }>> {
|
||||
const entries: Array<{ path: string; isFile: boolean; isDirectory: boolean }> = [];
|
||||
// Configuration methods (return this for chaining)
|
||||
public encoding(encoding: BufferEncoding): this {
|
||||
this.options.encoding = encoding;
|
||||
return this;
|
||||
}
|
||||
|
||||
if (options?.recursive) {
|
||||
// Recursive listing
|
||||
const walk = async (dir: string) => {
|
||||
const items = await fsPromises.readdir(dir);
|
||||
for (const item of items) {
|
||||
const fullPath = path.join(dir, item);
|
||||
const stats = await fsPromises.stat(fullPath);
|
||||
public recursive(): this {
|
||||
this.options.recursive = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
if (stats.isFile()) {
|
||||
entries.push({ path: fullPath, isFile: true, isDirectory: false });
|
||||
} else if (stats.isDirectory()) {
|
||||
entries.push({ path: fullPath, isFile: false, isDirectory: true });
|
||||
await walk(fullPath);
|
||||
}
|
||||
}
|
||||
};
|
||||
await walk(dirPath);
|
||||
} else {
|
||||
// Non-recursive listing
|
||||
const items = await fsPromises.readdir(dirPath);
|
||||
for (const item of items) {
|
||||
const fullPath = path.join(dirPath, item);
|
||||
const stats = await fsPromises.stat(fullPath);
|
||||
entries.push({
|
||||
path: fullPath,
|
||||
isFile: stats.isFile(),
|
||||
isDirectory: stats.isDirectory(),
|
||||
});
|
||||
}
|
||||
}
|
||||
// Action methods (return Promises)
|
||||
public async read(): Promise<string | Buffer> {
|
||||
if (this.options.encoding) {
|
||||
return await fsPromises.readFile(this.filePath, this.options.encoding);
|
||||
}
|
||||
return await fsPromises.readFile(this.filePath);
|
||||
}
|
||||
|
||||
return entries;
|
||||
},
|
||||
async create(options?: { recursive?: boolean }): Promise<void> {
|
||||
if (options?.recursive) {
|
||||
await ensureDir(dirPath);
|
||||
} else {
|
||||
await fsPromises.mkdir(dirPath);
|
||||
}
|
||||
},
|
||||
async exists(): Promise<boolean> {
|
||||
return await pathExists(dirPath);
|
||||
},
|
||||
async delete(): Promise<void> {
|
||||
await remove(dirPath);
|
||||
},
|
||||
};
|
||||
public async write(content: string | Buffer): Promise<void> {
|
||||
// Ensure directory exists
|
||||
const dirPath = path.dirname(this.filePath);
|
||||
await fsPromises.mkdir(dirPath, { recursive: true });
|
||||
|
||||
if (this.options.encoding && typeof content === 'string') {
|
||||
await fsPromises.writeFile(this.filePath, content, this.options.encoding);
|
||||
} else {
|
||||
await fsPromises.writeFile(this.filePath, content);
|
||||
}
|
||||
}
|
||||
|
||||
public async exists(): Promise<boolean> {
|
||||
try {
|
||||
await fsPromises.access(this.filePath);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async delete(): Promise<void> {
|
||||
await fsPromises.unlink(this.filePath);
|
||||
}
|
||||
|
||||
public async stat(): Promise<any> {
|
||||
return await fsPromises.stat(this.filePath);
|
||||
}
|
||||
|
||||
public async readStream(): Promise<Readable> {
|
||||
return createReadStream(this.filePath);
|
||||
}
|
||||
|
||||
public async writeStream(): Promise<Writable> {
|
||||
// Ensure directory exists
|
||||
const dirPath = path.dirname(this.filePath);
|
||||
await fsPromises.mkdir(dirPath, { recursive: true });
|
||||
return createWriteStream(this.filePath);
|
||||
}
|
||||
|
||||
public async copy(dest: string): Promise<void> {
|
||||
await fsPromises.copyFile(this.filePath, dest);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock SmartFsDirectory - Fluent directory operations builder
|
||||
*/
|
||||
class MockSmartFsDirectory {
|
||||
private dirPath: string;
|
||||
private options: {
|
||||
recursive?: boolean;
|
||||
} = {};
|
||||
|
||||
constructor(dirPath: string) {
|
||||
this.dirPath = dirPath;
|
||||
}
|
||||
|
||||
// Configuration methods (return this for chaining)
|
||||
public recursive(): this {
|
||||
this.options.recursive = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Action methods (return Promises)
|
||||
public async list(): Promise<Array<{ path: string; isFile: boolean; isDirectory: boolean }>> {
|
||||
const entries: Array<{ path: string; isFile: boolean; isDirectory: boolean }> = [];
|
||||
|
||||
if (this.options.recursive) {
|
||||
// Recursive listing
|
||||
const walk = async (dir: string) => {
|
||||
const items = await fsPromises.readdir(dir);
|
||||
for (const item of items) {
|
||||
const fullPath = path.join(dir, item);
|
||||
const stats = await fsPromises.stat(fullPath);
|
||||
|
||||
if (stats.isFile()) {
|
||||
entries.push({ path: fullPath, isFile: true, isDirectory: false });
|
||||
} else if (stats.isDirectory()) {
|
||||
entries.push({ path: fullPath, isFile: false, isDirectory: true });
|
||||
await walk(fullPath);
|
||||
}
|
||||
}
|
||||
};
|
||||
await walk(this.dirPath);
|
||||
} else {
|
||||
// Non-recursive listing
|
||||
const items = await fsPromises.readdir(this.dirPath);
|
||||
for (const item of items) {
|
||||
const fullPath = path.join(this.dirPath, item);
|
||||
const stats = await fsPromises.stat(fullPath);
|
||||
entries.push({
|
||||
path: fullPath,
|
||||
isFile: stats.isFile(),
|
||||
isDirectory: stats.isDirectory(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
public async create(): Promise<void> {
|
||||
if (this.options.recursive) {
|
||||
await fsPromises.mkdir(this.dirPath, { recursive: true });
|
||||
} else {
|
||||
await fsPromises.mkdir(this.dirPath);
|
||||
}
|
||||
}
|
||||
|
||||
public async exists(): Promise<boolean> {
|
||||
try {
|
||||
const stats = await fsPromises.stat(this.dirPath);
|
||||
return stats.isDirectory();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async delete(): Promise<void> {
|
||||
if (this.options.recursive) {
|
||||
await fsPromises.rm(this.dirPath, { recursive: true, force: true });
|
||||
} else {
|
||||
await fsPromises.rmdir(this.dirPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock SmartFs - Main class matching @push.rocks/smartfs API
|
||||
*/
|
||||
export class MockSmartFs {
|
||||
public file(filePath: string): MockSmartFsFile {
|
||||
return new MockSmartFsFile(filePath);
|
||||
}
|
||||
|
||||
public directory(dirPath: string): MockSmartFsDirectory {
|
||||
return new MockSmartFsDirectory(dirPath);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user