2024-04-02 18:53:02 +00:00
|
|
|
import * as plugins from './plugins.js';
|
2023-11-04 19:07:43 +00:00
|
|
|
import * as fs from './fs.js';
|
|
|
|
import * as memory from './memory.js';
|
2017-03-04 20:10:46 +00:00
|
|
|
|
|
|
|
export interface ISmartfileConstructorOptions {
|
2021-12-01 00:14:07 +00:00
|
|
|
path: string;
|
|
|
|
contentBuffer: Buffer;
|
|
|
|
base: string;
|
2017-03-04 20:10:46 +00:00
|
|
|
}
|
2016-09-20 15:56:49 +00:00
|
|
|
|
2017-04-27 14:48:08 +00:00
|
|
|
/**
|
2023-11-04 19:07:43 +00:00
|
|
|
* an vinyl file compatible in memory file class
|
2017-04-27 14:48:08 +00:00
|
|
|
*/
|
2023-11-04 19:07:43 +00:00
|
|
|
export class SmartFile extends plugins.smartjson.Smartjson {
|
2019-01-27 02:11:10 +00:00
|
|
|
// ======
|
|
|
|
// STATIC
|
|
|
|
// ======
|
|
|
|
|
|
|
|
/**
|
|
|
|
* creates a Smartfile from a filePath
|
|
|
|
* @param filePath
|
|
|
|
*/
|
2021-12-01 00:14:07 +00:00
|
|
|
public static async fromFilePath(filePath: string, baseArg: string = process.cwd()) {
|
2019-01-27 02:11:10 +00:00
|
|
|
filePath = plugins.path.resolve(filePath);
|
2020-08-10 20:58:22 +00:00
|
|
|
const fileBuffer = fs.toBufferSync(filePath);
|
2023-11-04 19:07:43 +00:00
|
|
|
const smartfile = new SmartFile({
|
2020-08-10 20:58:22 +00:00
|
|
|
contentBuffer: fileBuffer,
|
2021-12-01 00:14:07 +00:00
|
|
|
base: baseArg,
|
|
|
|
path: plugins.path.relative(baseArg, filePath),
|
2019-01-27 02:11:10 +00:00
|
|
|
});
|
|
|
|
return smartfile;
|
|
|
|
}
|
|
|
|
|
2021-12-01 00:14:07 +00:00
|
|
|
public static async fromBuffer(
|
|
|
|
filePath: string,
|
|
|
|
contentBufferArg: Buffer,
|
|
|
|
baseArg: string = process.cwd()
|
|
|
|
) {
|
2023-11-04 19:07:43 +00:00
|
|
|
const smartfile = new SmartFile({
|
2020-08-10 20:58:22 +00:00
|
|
|
contentBuffer: contentBufferArg,
|
2021-12-01 00:14:07 +00:00
|
|
|
base: baseArg,
|
|
|
|
path: plugins.path.relative(baseArg, filePath),
|
2020-08-10 20:58:22 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
return smartfile;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static async fromString(
|
|
|
|
filePath: string,
|
|
|
|
contentStringArg: string,
|
2021-12-01 00:14:07 +00:00
|
|
|
encodingArg: 'utf8' | 'binary',
|
|
|
|
baseArg = process.cwd()
|
2020-08-10 20:58:22 +00:00
|
|
|
) {
|
2023-11-04 19:07:43 +00:00
|
|
|
const smartfile = new SmartFile({
|
2020-08-10 20:58:22 +00:00
|
|
|
contentBuffer: Buffer.from(contentStringArg, encodingArg),
|
2021-12-01 00:14:07 +00:00
|
|
|
base: baseArg,
|
|
|
|
path: plugins.path.relative(baseArg, filePath),
|
2020-08-10 20:58:22 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
return smartfile;
|
|
|
|
}
|
|
|
|
|
2021-04-26 08:24:36 +00:00
|
|
|
public static async fromFoldedJson(foldedJsonArg: string) {
|
2023-11-04 19:07:43 +00:00
|
|
|
return new SmartFile(plugins.smartjson.parse(foldedJsonArg));
|
2021-04-26 08:24:36 +00:00
|
|
|
}
|
|
|
|
|
2023-11-03 00:25:37 +00:00
|
|
|
/**
|
|
|
|
* creates a Smartfile from a ReadableStream
|
|
|
|
* @param stream a readable stream that provides file content
|
|
|
|
* @param filePath the file path to associate with the content
|
|
|
|
* @param baseArg the base path to use for the file
|
|
|
|
*/
|
|
|
|
public static async fromStream(
|
|
|
|
stream: plugins.stream.Readable,
|
|
|
|
filePath: string,
|
|
|
|
baseArg: string = process.cwd()
|
2023-11-04 19:07:43 +00:00
|
|
|
): Promise<SmartFile> {
|
|
|
|
return new Promise<SmartFile>((resolve, reject) => {
|
2023-11-03 00:25:37 +00:00
|
|
|
const chunks: Buffer[] = [];
|
|
|
|
stream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
|
|
|
|
stream.on('error', (error) => reject(error));
|
|
|
|
stream.on('end', () => {
|
|
|
|
const contentBuffer = Buffer.concat(chunks);
|
2023-11-04 19:07:43 +00:00
|
|
|
const smartfile = new SmartFile({
|
2023-11-03 00:25:37 +00:00
|
|
|
contentBuffer: contentBuffer,
|
|
|
|
base: baseArg,
|
|
|
|
path: plugins.path.relative(baseArg, filePath),
|
|
|
|
});
|
|
|
|
resolve(smartfile);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-11-24 18:28:59 +00:00
|
|
|
public static async fromUrl (urlArg: string) {
|
2023-11-24 18:15:41 +00:00
|
|
|
const response = await plugins.smartrequest.getBinary(urlArg);
|
|
|
|
const smartfile = await SmartFile.fromBuffer(urlArg, response.body);
|
|
|
|
return smartfile;
|
|
|
|
}
|
|
|
|
|
2019-01-27 02:11:10 +00:00
|
|
|
// ========
|
|
|
|
// INSTANCE
|
|
|
|
// ========
|
2017-04-27 14:48:08 +00:00
|
|
|
/**
|
2021-12-01 00:19:49 +00:00
|
|
|
* the relative path of the file
|
2017-04-27 14:48:08 +00:00
|
|
|
*/
|
2020-10-05 16:20:57 +00:00
|
|
|
@plugins.smartjson.foldDec()
|
2020-08-10 20:58:22 +00:00
|
|
|
public path: string;
|
2017-04-27 14:48:08 +00:00
|
|
|
|
2017-05-07 21:00:56 +00:00
|
|
|
/**
|
2020-10-06 00:57:47 +00:00
|
|
|
* a parsed path
|
2017-05-07 21:00:56 +00:00
|
|
|
*/
|
2022-03-11 08:46:54 +00:00
|
|
|
public get parsedPath(): plugins.path.ParsedPath {
|
2021-12-01 09:47:29 +00:00
|
|
|
return plugins.path.parse(this.path);
|
2022-03-11 08:46:54 +00:00
|
|
|
}
|
|
|
|
public get absolutePath() {
|
2021-12-01 09:47:29 +00:00
|
|
|
return plugins.path.join(this.base, this.path);
|
|
|
|
}
|
|
|
|
|
|
|
|
public get absoluteParsedPath() {
|
|
|
|
return plugins.path.parse(this.absolutePath);
|
|
|
|
}
|
2017-05-07 21:00:56 +00:00
|
|
|
|
2017-04-29 15:20:09 +00:00
|
|
|
/**
|
|
|
|
* the content of the file as Buffer
|
|
|
|
*/
|
2020-10-06 00:57:47 +00:00
|
|
|
@plugins.smartjson.foldDec()
|
2020-08-10 20:58:22 +00:00
|
|
|
public contentBuffer: Buffer;
|
2017-04-29 15:20:09 +00:00
|
|
|
|
2017-04-27 14:48:08 +00:00
|
|
|
/**
|
|
|
|
* The current working directory of the file
|
2018-07-03 06:55:09 +00:00
|
|
|
* Note:this is similar to gulp and different from native node path base
|
2017-04-27 14:48:08 +00:00
|
|
|
*/
|
2020-10-06 00:57:47 +00:00
|
|
|
@plugins.smartjson.foldDec()
|
2020-08-10 20:58:22 +00:00
|
|
|
public base: string;
|
2017-04-27 14:48:08 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* sync the file with disk
|
|
|
|
*/
|
2020-10-06 00:57:47 +00:00
|
|
|
@plugins.smartjson.foldDec()
|
2020-08-10 20:58:22 +00:00
|
|
|
public sync: boolean;
|
2017-04-27 14:48:08 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* the constructor of Smartfile
|
|
|
|
* @param optionsArg
|
|
|
|
*/
|
2017-05-07 21:00:56 +00:00
|
|
|
|
2018-07-03 06:55:09 +00:00
|
|
|
constructor(optionsArg: ISmartfileConstructorOptions) {
|
2020-10-05 16:20:57 +00:00
|
|
|
super();
|
2017-03-04 20:10:46 +00:00
|
|
|
if (optionsArg.contentBuffer) {
|
2018-07-03 06:55:09 +00:00
|
|
|
this.contentBuffer = optionsArg.contentBuffer;
|
2017-04-29 15:20:09 +00:00
|
|
|
} else {
|
2018-07-03 06:55:09 +00:00
|
|
|
console.log('created empty Smartfile?');
|
2016-09-20 15:56:49 +00:00
|
|
|
}
|
2018-07-03 06:55:09 +00:00
|
|
|
this.path = optionsArg.path;
|
|
|
|
this.base = optionsArg.base;
|
2017-04-27 14:48:08 +00:00
|
|
|
}
|
|
|
|
|
2017-03-04 20:10:46 +00:00
|
|
|
/**
|
|
|
|
* set contents from string
|
|
|
|
* @param contentString
|
|
|
|
*/
|
2020-08-10 20:58:22 +00:00
|
|
|
public setContentsFromString(contentString: string, encodingArg: 'utf8' | 'binary' = 'utf8') {
|
2023-11-03 01:31:57 +00:00
|
|
|
this.contents = Buffer.from(contentString, encodingArg);
|
2017-03-04 20:10:46 +00:00
|
|
|
}
|
2017-04-27 14:48:08 +00:00
|
|
|
|
|
|
|
/**
|
2020-10-09 15:15:47 +00:00
|
|
|
* write file to disk at its original location
|
2018-02-16 20:57:44 +00:00
|
|
|
* Behaviours:
|
|
|
|
* - no argument write to exactly where the file was picked up
|
2017-04-27 14:48:08 +00:00
|
|
|
*/
|
2020-10-09 15:15:47 +00:00
|
|
|
public async write() {
|
2024-04-12 12:51:23 +00:00
|
|
|
let writePath = plugins.smartpath.transform.makeAbsolute(this.path, this.base);
|
2024-04-12 13:00:55 +00:00
|
|
|
console.log(`writing to ${writePath}`);
|
2024-04-12 12:44:12 +00:00
|
|
|
await memory.toFs(this.contentBuffer, writePath);
|
2020-10-09 15:15:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* writes the file to path given as argument
|
2021-12-01 00:14:07 +00:00
|
|
|
* note: if the path is not absolute, takes process.cwd() as base
|
2020-10-09 15:15:47 +00:00
|
|
|
* @param filePathArg
|
|
|
|
*/
|
|
|
|
public async writeToDiskAtPath(filePathArg: string) {
|
|
|
|
if (!plugins.path.isAbsolute(filePathArg)) {
|
|
|
|
filePathArg = plugins.path.join(process.cwd(), filePathArg);
|
|
|
|
}
|
|
|
|
await memory.toFs(this.contentBuffer, filePathArg);
|
|
|
|
}
|
|
|
|
|
2021-11-30 15:34:35 +00:00
|
|
|
/**
|
|
|
|
* writes the file to a directory combined with the relative path portion
|
|
|
|
* @param dirPathArg
|
|
|
|
* @returns
|
|
|
|
*/
|
2020-10-09 15:15:47 +00:00
|
|
|
public async writeToDir(dirPathArg: string) {
|
2022-06-07 13:43:28 +00:00
|
|
|
dirPathArg = plugins.smartpath.transform.toAbsolute(dirPathArg) as string;
|
2020-10-11 15:34:24 +00:00
|
|
|
const filePath = plugins.path.join(dirPathArg, this.path);
|
2020-10-09 15:15:47 +00:00
|
|
|
await memory.toFs(this.contentBuffer, filePath);
|
2020-10-11 15:34:24 +00:00
|
|
|
return filePath;
|
2017-04-27 14:48:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* read file from disk
|
|
|
|
*/
|
2021-11-30 15:34:35 +00:00
|
|
|
public async read() {
|
2021-12-01 00:14:07 +00:00
|
|
|
this.contentBuffer = await fs.toBuffer(plugins.path.join(this.base, this.path));
|
2021-11-30 15:34:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* deletes the file from disk at its original location
|
|
|
|
*/
|
|
|
|
public async delete() {
|
2021-12-01 00:14:07 +00:00
|
|
|
await fs.remove(plugins.path.join(this.base, this.path));
|
2021-11-30 15:34:35 +00:00
|
|
|
}
|
2017-04-30 16:13:17 +00:00
|
|
|
|
|
|
|
// -----------------------------------------------
|
|
|
|
// vinyl compatibility
|
|
|
|
// -----------------------------------------------
|
|
|
|
/**
|
|
|
|
* vinyl-compatibility: alias of this.contentBuffer
|
|
|
|
*/
|
2018-07-03 06:55:09 +00:00
|
|
|
get contents(): Buffer {
|
|
|
|
return this.contentBuffer;
|
2017-04-30 16:13:17 +00:00
|
|
|
}
|
2018-07-03 06:55:09 +00:00
|
|
|
set contents(contentsArg) {
|
|
|
|
this.contentBuffer = contentsArg;
|
2017-04-30 16:13:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* vinyl-compatibility
|
|
|
|
*/
|
2020-08-10 20:58:22 +00:00
|
|
|
public get cwd() {
|
2018-07-03 06:55:09 +00:00
|
|
|
return process.cwd();
|
2017-04-30 16:13:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* return relative path of file
|
|
|
|
*/
|
2020-08-10 20:58:22 +00:00
|
|
|
public get relative(): string {
|
2021-12-01 00:14:07 +00:00
|
|
|
return this.path;
|
2017-04-30 16:13:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* return truw when the file has content
|
|
|
|
*/
|
2020-08-10 20:58:22 +00:00
|
|
|
public isNull(): boolean {
|
2017-04-30 16:13:17 +00:00
|
|
|
if (!this.contentBuffer) {
|
2018-07-03 06:55:09 +00:00
|
|
|
return true;
|
2017-04-30 16:13:17 +00:00
|
|
|
}
|
2018-07-03 06:55:09 +00:00
|
|
|
return false;
|
2017-04-30 16:13:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* return true if contents are Buffer
|
|
|
|
*/
|
2020-08-10 20:58:22 +00:00
|
|
|
public isBuffer(): boolean {
|
2017-04-30 16:13:17 +00:00
|
|
|
if (this.contents instanceof Buffer) {
|
2018-07-03 06:55:09 +00:00
|
|
|
return true;
|
2017-04-30 16:13:17 +00:00
|
|
|
}
|
2018-07-03 06:55:09 +00:00
|
|
|
return false;
|
2017-04-30 16:13:17 +00:00
|
|
|
}
|
|
|
|
|
2020-08-10 20:58:22 +00:00
|
|
|
public isDirectory() {
|
2018-07-03 06:55:09 +00:00
|
|
|
return false;
|
2017-04-30 16:13:17 +00:00
|
|
|
}
|
|
|
|
|
2020-08-10 20:58:22 +00:00
|
|
|
public isStream() {
|
2018-07-03 06:55:09 +00:00
|
|
|
return false;
|
2017-04-30 16:13:17 +00:00
|
|
|
}
|
2017-05-01 20:07:25 +00:00
|
|
|
|
2020-08-10 20:58:22 +00:00
|
|
|
public isSymbolic() {
|
2018-07-03 06:55:09 +00:00
|
|
|
return false;
|
2017-05-01 20:07:25 +00:00
|
|
|
}
|
2017-05-27 21:47:39 +00:00
|
|
|
|
2023-01-09 14:32:37 +00:00
|
|
|
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);
|
2023-01-09 14:34:05 +00:00
|
|
|
switch (typeArg) {
|
2023-01-09 14:32:37 +00:00
|
|
|
case 'path':
|
|
|
|
return pathHash;
|
|
|
|
case 'content':
|
|
|
|
return contentHash;
|
|
|
|
case 'all':
|
|
|
|
default:
|
|
|
|
return combinedHash;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-27 21:47:39 +00:00
|
|
|
// update things
|
2020-08-10 20:58:22 +00:00
|
|
|
public updateFileName(fileNameArg: string) {
|
|
|
|
const oldFileName = this.parsedPath.base;
|
2018-07-03 06:55:09 +00:00
|
|
|
this.path = this.path.replace(new RegExp(oldFileName + '$'), fileNameArg);
|
2017-05-27 21:47:39 +00:00
|
|
|
}
|
2022-07-24 21:04:51 +00:00
|
|
|
|
|
|
|
public async editContentAsString(editFuncArg: (fileStringArg: string) => Promise<string>) {
|
|
|
|
const newFileString = await editFuncArg(this.contentBuffer.toString());
|
|
|
|
this.contentBuffer = Buffer.from(newFileString);
|
|
|
|
}
|
2023-11-03 01:24:36 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
2024-06-07 15:13:07 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the size of the file in bytes
|
|
|
|
*/
|
|
|
|
public async getSize(): Promise<number> {
|
|
|
|
return this.contentBuffer.length;
|
|
|
|
}
|
2016-09-20 15:56:49 +00:00
|
|
|
}
|