kit: expose path and file database modules

This commit is contained in:
DH
2025-08-17 08:52:30 +03:00
parent 360c336bab
commit c64e5c92a5
3 changed files with 334 additions and 328 deletions

View File

@@ -0,0 +1,141 @@
import { Stats } from "fs";
import { parse, toNative } from "./path.js";
import * as fs from 'fs/promises';
export type Timestamp = { timestamp: number };
export type FileWithTimestamp = Timestamp & {
content: string;
};
export function mergeTimestamps<T extends Timestamp>(timestamps: T[]) {
return timestamps.reduce((a, b) => a.timestamp > b.timestamp ? a : b);
}
export async function calcTimestamp(path: string[] | string): Promise<Timestamp> {
if (typeof path === 'string') {
return { timestamp: (await fs.stat(toNative(path))).mtimeMs };
}
return { timestamp: (await Promise.all(path.map(async x => (await fs.stat(toNative(x))).mtimeMs))).reduce((x, y) => Math.max(x, y)) };
}
export class FileDb {
private generated: Record<string, FileWithTimestamp> = {};
private cache: Record<string, FileWithTimestamp> = {};
private selfTimestamp: number | undefined = undefined;
private createFileImpl(path: string) {
// console.log(`filedb: creating ${path}`);
this.generated[path] = { content: "", timestamp: 0 };
return this.generated[path];
}
async readFile(path: string, ts?: Timestamp) {
const fileTimestamp = ts ? ts.timestamp : (await fs.stat(toNative(path))).mtimeMs;
const cached = this.cache[path];
if (fileTimestamp <= cached?.timestamp) {
// console.log(`filedb: reading cached ${path}`);
return cached;
}
// console.log(`filedb: reading ${path}`);
const content = await fs.readFile(toNative(path), "utf8");
if (!cached) {
const result = {
content,
timestamp: fileTimestamp
};
this.cache[path] = result;
return result;
}
cached.content = content;
cached.timestamp = fileTimestamp;
return cached;
}
createFile(path: string, sourceTimestamp: Timestamp): Promise<FileWithTimestamp | undefined>;
createFile(path: string): Promise<FileWithTimestamp>;
async createFile(filePath: string, sourceTimestamp: Timestamp | undefined = undefined) {
if (filePath in this.generated) {
throw new Error(`file '${filePath}' was already generated`);
}
if (!sourceTimestamp) {
return this.createFileImpl(filePath);
}
let stat: Stats;
try {
stat = await fs.stat(toNative(filePath));
} catch {
return this.createFileImpl(filePath);
}
if (this.selfTimestamp == undefined) {
try {
this.selfTimestamp = (await fs.stat(import.meta.filename)).mtimeMs;
} catch {
this.selfTimestamp = 0;
}
}
const timestamp = Math.max(sourceTimestamp.timestamp, this.selfTimestamp);
if (timestamp > 0 && stat.mtimeMs > timestamp) {
// console.log(`filedb: skipping ${path}`);
return undefined;
}
return this.createFileImpl(filePath);
}
async commit() {
const files = (await Promise.all(Object.keys(this.generated).map(async path => {
const file = this.generated[path];
if (file.timestamp > 0 && (await calcTimestamp(path)).timestamp >= file.timestamp) {
return undefined;
}
return { ...file, path };
}))).filter(file => file != undefined);
const dirs = new Set<string>();
files.forEach(file => {
dirs.add(parse(file.path).dir);
});
await Promise.all([...dirs].map(async dir => {
await fs.mkdir(toNative(dir), { recursive: true });
}));
await Promise.all(files.map(async file => {
await fs.writeFile(toNative(file.path), file.content, "utf8");
this.cache[file.path] = {
content: file.content,
timestamp: Date.now()
};
}));
this.generated = {};
}
dump() {
const files = (Object.keys(this.generated).map(path => {
return { ...this.generated[path], path };
}));
files.forEach(file => {
console.log(file.path, file.content);
});
}
clear() {
this.generated = {};
}
}

File diff suppressed because it is too large Load Diff

33
rpcsx-ui-kit/src/path.ts Normal file
View File

@@ -0,0 +1,33 @@
import * as posix_path from 'path/posix';
import * as native_path from 'path';
export const sep = posix_path.sep;
export function toUnix(path: string) {
return native_path.sep == posix_path.sep ? path : path.replaceAll(native_path.sep, posix_path.sep);
}
export function toNative(path: string) {
return native_path.sep == posix_path.sep ? path : path.replaceAll(posix_path.sep, native_path.sep);
}
export function resolve(...paths: string[]) {
return native_path.sep == posix_path.sep ? native_path.resolve(...paths) : toUnix(native_path.resolve(...paths.map(x => toNative(x))));
}
export function join(...paths: string[]) {
return native_path.sep == posix_path.sep ? native_path.join(...paths) : toUnix(native_path.join(...paths.map(x => toNative(x))));
}
export function relative(from: string, to: string) {
return native_path.sep == posix_path.sep ? native_path.relative(from, to) : toUnix(native_path.relative(toNative(from), toNative(to)));
}
export function parse(path: string) {
if (native_path.sep == posix_path.sep)
return native_path.parse(path);
const result = native_path.parse(toNative(path));
result.dir = toUnix(result.dir);
return result;
}