Server/project/src/utils/ImporterUtil.ts
TheSparta 418d9f2a8f Import path alias on the whole project (!157)
- Ability to use @spt-aki path alias on the whole project.
- Swapped all imports from relative paths, for imports using the path alias.

Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/157
Co-authored-by: TheSparta <thesparta@noreply.dev.sp-tarkov.com>
Co-committed-by: TheSparta <thesparta@noreply.dev.sp-tarkov.com>
2023-10-19 17:21:17 +00:00

194 lines
6.8 KiB
TypeScript

import { inject, injectable } from "tsyringe";
import { Queue } from "@spt-aki/utils/collections/queue/Queue";
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { VFS } from "@spt-aki/utils/VFS";
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable @typescript-eslint/brace-style */
@injectable()
export class ImporterUtil
{
constructor(
@inject("VFS") protected vfs: VFS,
@inject("JsonUtil") protected jsonUtil: JsonUtil
)
{}
/**
* Load files into js objects recursively (asynchronous)
* @param filepath Path to folder with files
* @returns Promise<T> return T type associated with this class
*/
public async loadRecursiveAsync<T>(
filepath: string,
onReadCallback: (fileWithPath: string, data: string) => void = () => {},
onObjectDeserialized: (fileWithPath: string, object: any) => void = () => {}
): Promise<T>
{
const result = {} as T;
// get all filepaths
const files = this.vfs.getFiles(filepath);
const directories = this.vfs.getDirs(filepath);
// add file content to result
for (const file of files)
{
if (this.vfs.getFileExtension(file) === "json")
{
const filename = this.vfs.stripExtension(file);
const filePathAndName = `${filepath}${file}`;
await this.vfs.readFileAsync(filePathAndName).then(fileData =>
{
onReadCallback(filePathAndName, fileData);
const fileDeserialized = this.jsonUtil.deserializeWithCacheCheck(fileData, filePathAndName);
onObjectDeserialized(filePathAndName, fileDeserialized);
result[filename] = fileDeserialized;
});
}
}
// deep tree search
for (const dir of directories)
{
result[dir] = this.loadRecursiveAsync(`${filepath}${dir}/`);
}
// set all loadRecursive to be executed asynchronously
const resEntries = Object.entries(result);
const resResolved = await Promise.all(resEntries.map(ent => ent[1]));
for (let resIdx = 0; resIdx < resResolved.length; resIdx++)
{
resEntries[resIdx][1] = resResolved[resIdx];
}
// return the result of all async fetch
return Object.fromEntries(resEntries) as T;
}
/**
* Load files into js objects recursively (synchronous)
* @param filepath Path to folder with files
* @returns
*/
public loadRecursive<T>(
filepath: string,
onReadCallback: (fileWithPath: string, data: string) => void = () => {},
onObjectDeserialized: (fileWithPath: string, object: any) => void = () => {}
): T
{
const result = {} as T;
// get all filepaths
const files = this.vfs.getFiles(filepath);
const directories = this.vfs.getDirs(filepath);
// add file content to result
for (const file of files)
{
if (this.vfs.getFileExtension(file) === "json")
{
const filename = this.vfs.stripExtension(file);
const filePathAndName = `${filepath}${file}`;
const fileData = this.vfs.readFile(filePathAndName);
onReadCallback(filePathAndName, fileData);
const fileDeserialized = this.jsonUtil.deserializeWithCacheCheck(fileData, filePathAndName);
onObjectDeserialized(filePathAndName, fileDeserialized);
result[filename] = fileDeserialized;
}
}
// deep tree search
for (const dir of directories)
{
result[dir] = this.loadRecursive(`${filepath}${dir}/`);
}
return result;
}
public async loadAsync<T>(
filepath: string,
strippablePath = "",
onReadCallback: (fileWithPath: string, data: string) => void = () => {},
onObjectDeserialized: (fileWithPath: string, object: any) => void = () => {}
): Promise<T>
{
const directoriesToRead = new Queue<string>();
const filesToProcess = new Queue<VisitNode>();
const promises = new Array<Promise<any>>();
const result = {} as T;
const files = this.vfs.getFiles(filepath);
const directories = this.vfs.getDirs(filepath);
directoriesToRead.enqueueAll(directories.map(d => `${filepath}${d}`));
filesToProcess.enqueueAll(files.map(f => new VisitNode(filepath, f)));
while (!directoriesToRead.isEmpty())
{
const directory = directoriesToRead.dequeue();
filesToProcess.enqueueAll(this.vfs.getFiles(directory).map(f => new VisitNode(`${directory}/`, f)));
directoriesToRead.enqueueAll(this.vfs.getDirs(directory).map(d => `${directory}/${d}`));
}
while (!filesToProcess.isEmpty())
{
const fileNode = filesToProcess.dequeue();
if (this.vfs.getFileExtension(fileNode.fileName) === "json")
{
const filePathAndName = `${fileNode.filePath}${fileNode.fileName}`;
promises.push(
this.vfs.readFileAsync(filePathAndName)
.then(async fileData => {
onReadCallback(filePathAndName, fileData);
return this.jsonUtil.deserializeWithCacheCheckAsync<any>(fileData, filePathAndName);
})
.then(async fileDeserialized => {
onObjectDeserialized(filePathAndName, fileDeserialized);
const strippedFilePath = this.vfs.stripExtension(filePathAndName).replace(filepath, "");
this.placeObject(fileDeserialized, strippedFilePath, result, strippablePath);
})
);
}
}
await Promise.all(promises).catch(e => console.error(e));
return result;
}
protected placeObject<T>(fileDeserialized: any, strippedFilePath: string, result: T, strippablePath: string):void {
const strippedFinalPath = strippedFilePath.replace(strippablePath, "");
let temp = result;
const propertiesToVisit = strippedFinalPath.split("/");
for (let i = 0; i < propertiesToVisit.length; i++)
{
const property = propertiesToVisit[i];
if (i === (propertiesToVisit.length - 1))
{
temp[property] = fileDeserialized;
}
else
{
if (!temp[property])
temp[property] = {};
temp = temp[property];
}
}
}
}
class VisitNode
{
constructor(
public filePath: string,
public fileName: string
){}
}