Files
smartfile/ts/classes.smartfile.ts

335 lines
8.4 KiB
TypeScript

import * as plugins from './plugins.js';
export interface ISmartfileConstructorOptions {
path: string;
contentBuffer: Buffer;
base: string;
}
/**
* an vinyl file compatible in memory file class
* Use SmartFileFactory to create instances of this class
*/
export class SmartFile extends plugins.smartjson.Smartjson {
// ========
// INSTANCE
// ========
/**
* Reference to the SmartFs instance for filesystem operations
*/
private smartFs?: any;
/**
* the relative path of the file
*/
@plugins.smartjson.foldDec()
public path: string;
/**
* a parsed path
*/
public get parsedPath(): plugins.path.ParsedPath {
return plugins.path.parse(this.path);
}
public get absolutePath() {
return plugins.path.join(this.base, this.path);
}
public get absoluteParsedPath() {
return plugins.path.parse(this.absolutePath);
}
/**
* the content of the file as Buffer
*/
@plugins.smartjson.foldDec()
public contentBuffer: Buffer;
/**
* The current working directory of the file
* Note:this is similar to gulp and different from native node path base
*/
@plugins.smartjson.foldDec()
public base: string;
/**
* sync the file with disk
*/
@plugins.smartjson.foldDec()
public sync: boolean;
/**
* the constructor of Smartfile
* @param optionsArg
* @param smartFs optional SmartFs instance for filesystem operations
*/
constructor(optionsArg: ISmartfileConstructorOptions, smartFs?: any) {
super();
if (optionsArg.contentBuffer) {
this.contentBuffer = optionsArg.contentBuffer;
} else {
console.log('created empty Smartfile?');
}
this.path = optionsArg.path;
this.base = optionsArg.base;
this.smartFs = smartFs;
}
/**
* set contents from string
* @param contentString
*/
public setContentsFromString(
contentString: string,
encodingArg: 'utf8' | 'binary' = 'utf8',
) {
this.contents = Buffer.from(contentString, encodingArg);
}
/**
* write file to disk at its original location
* Behaviours:
* - no argument write to exactly where the file was picked up
* - Requires SmartFs instance (create via SmartFileFactory)
*/
public async write() {
if (!this.smartFs) {
throw new Error('No SmartFs instance available. Create SmartFile through SmartFileFactory.');
}
const writePath = plugins.smartpath.transform.makeAbsolute(
this.path,
this.base,
);
console.log(`writing to ${writePath}`);
await this.smartFs.file(writePath).write(this.contentBuffer);
}
/**
* writes the file to path given as argument
* note: if the path is not absolute, takes process.cwd() as base
* @param filePathArg
*/
public async writeToDiskAtPath(filePathArg: string) {
if (!this.smartFs) {
throw new Error('No SmartFs instance available. Create SmartFile through SmartFileFactory.');
}
if (!plugins.path.isAbsolute(filePathArg)) {
filePathArg = plugins.path.join(process.cwd(), filePathArg);
}
await this.smartFs.file(filePathArg).write(this.contentBuffer);
}
/**
* writes the file to a directory combined with the relative path portion
* @param dirPathArg
* @returns
*/
public async writeToDir(dirPathArg: string) {
if (!this.smartFs) {
throw new Error('No SmartFs instance available. Create SmartFile through SmartFileFactory.');
}
dirPathArg = plugins.smartpath.transform.toAbsolute(dirPathArg) as string;
const filePath = plugins.path.join(dirPathArg, this.path);
await this.smartFs.file(filePath).write(this.contentBuffer);
return filePath;
}
/**
* read file from disk
*/
public async read() {
if (!this.smartFs) {
throw new Error('No SmartFs instance available. Create SmartFile through SmartFileFactory.');
}
const filePath = plugins.path.join(this.base, this.path);
const content = await this.smartFs.file(filePath).read();
this.contentBuffer = Buffer.from(content);
}
/**
* deletes the file from disk at its original location
*/
public async delete() {
if (!this.smartFs) {
throw new Error('No SmartFs instance available. Create SmartFile through SmartFileFactory.');
}
const filePath = plugins.path.join(this.base, this.path);
await this.smartFs.file(filePath).delete();
}
/**
* Renames the file to the specified new name.
* - Updates the `path` property with the new name.
* - Writes the file to the new location if it exists on disk.
* @param newName The new name of the file (including extension if applicable).
* @param writeToDisk (optional) If true, also renames the file on the disk.
* @returns The updated file path after renaming.
*/
public async rename(
newName: string,
writeToDisk: boolean = false,
): Promise<string> {
// Validate the new name
if (!newName || typeof newName !== 'string') {
throw new Error('Invalid new name provided.');
}
// Extract the directory path
const oldFilePath = this.path;
const dirPath = plugins.path.dirname(this.path);
// Create the new file path
const newFilePath = plugins.path.join(dirPath, newName);
// Update the `path` property
this.path = newFilePath;
// Optionally write the renamed file to disk
if (writeToDisk) {
const oldAbsolutePath = plugins.smartpath.transform.makeAbsolute(
oldFilePath,
this.base,
);
const newAbsolutePath = plugins.smartpath.transform.makeAbsolute(
newFilePath,
this.base,
);
// Rename the file on disk
await plugins.fsPromises.rename(oldAbsolutePath, newAbsolutePath);
}
// Return the new path
return this.path;
}
// -----------------------------------------------
// vinyl compatibility
// -----------------------------------------------
/**
* vinyl-compatibility: alias of this.contentBuffer
*/
get contents(): Buffer {
return this.contentBuffer;
}
set contents(contentsArg) {
this.contentBuffer = contentsArg;
}
/**
* vinyl-compatibility
*/
public get cwd() {
return process.cwd();
}
/**
* return relative path of file
*/
public get relative(): string {
return this.path;
}
/**
* return truw when the file has content
*/
public isNull(): boolean {
if (!this.contentBuffer) {
return true;
}
return false;
}
/**
* return true if contents are Buffer
*/
public isBuffer(): boolean {
if (this.contents instanceof Buffer) {
return true;
}
return false;
}
public isDirectory() {
return false;
}
public isStream() {
return false;
}
public isSymbolic() {
return false;
}
public async getHash(typeArg: 'path' | 'content' | 'all' = 'all') {
const pathHash = await plugins.smarthash.sha256FromString(this.path);
const contentHash = await plugins.smarthash.sha256FromBuffer(
this.contentBuffer,
);
const combinedHash = await plugins.smarthash.sha256FromString(
pathHash + contentHash,
);
switch (typeArg) {
case 'path':
return pathHash;
case 'content':
return contentHash;
case 'all':
default:
return combinedHash;
}
}
// update things
public updateFileName(fileNameArg: string) {
const oldFileName = this.parsedPath.base;
this.path = this.path.replace(new RegExp(oldFileName + '$'), fileNameArg);
}
public async editContentAsString(
editFuncArg: (fileStringArg: string) => Promise<string>,
) {
const newFileString = await editFuncArg(this.contentBuffer.toString());
this.contentBuffer = Buffer.from(newFileString);
}
/**
* Returns a ReadableStream from the file's content buffer
*/
public getStream(): plugins.stream.Readable {
const stream = new plugins.stream.Readable();
stream.push(this.contentBuffer); // Push the content buffer to the stream
stream.push(null); // Push null to signify the end of the stream (EOF)
return stream;
}
/**
* Returns the size of the file in bytes
*/
public async getSize(): Promise<number> {
return this.contentBuffer.length;
}
/**
* Parse content as string with specified encoding
*/
public parseContentAsString(encodingArg: BufferEncoding = 'utf8'): string {
return this.contentBuffer.toString(encodingArg);
}
/**
* Parse content as buffer
*/
public parseContentAsBuffer(): Buffer {
return this.contentBuffer;
}
}