n8n/packages/@n8n/node-cli/src/utils/filesystem.ts

117 lines
3.2 KiB
TypeScript

import { camelCase } from 'change-case';
import fs from 'node:fs/promises';
import path from 'node:path';
import { isEnoentError } from './validation';
export async function folderExists(dir: string) {
try {
const stat = await fs.stat(dir);
return stat.isDirectory();
} catch (error: unknown) {
return false;
}
}
export async function copyFolder({
source: source,
destination,
ignore = [],
}: { source: string; destination: string; ignore?: string[] }): Promise<void> {
const ignoreSet = new Set(ignore);
async function walkAndCopy(currentSrc: string, currentDest: string): Promise<void> {
const entries = await fs.readdir(currentSrc, { withFileTypes: true });
await Promise.all(
entries.map(async (entry) => {
if (ignoreSet.has(entry.name)) return;
const srcPath = path.join(currentSrc, entry.name);
const destPath = path.join(currentDest, entry.name);
if (entry.isDirectory()) {
await fs.mkdir(destPath, { recursive: true });
await walkAndCopy(srcPath, destPath);
} else {
await fs.copyFile(srcPath, destPath);
}
}),
);
}
await fs.mkdir(destination, { recursive: true });
await walkAndCopy(source, destination);
}
export async function delayAtLeast<T>(promise: Promise<T>, minMs: number): Promise<T> {
const delayPromise = new Promise((res) => setTimeout(res, minMs));
const [result] = await Promise.all([promise, delayPromise]);
return result;
}
export async function writeFileSafe(
filePath: string,
contents: string | Uint8Array,
): Promise<void> {
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, contents);
}
export async function ensureFolder(dir: string) {
return await fs.mkdir(dir, { recursive: true });
}
export async function renameFilesInDirectory(
dirPath: string,
oldName: string,
newName: string,
): Promise<void> {
const files = await fs.readdir(dirPath);
for (const file of files) {
const oldPath = path.resolve(dirPath, file);
const oldFileName = path.basename(oldPath);
const newFileName = oldFileName
.replace(oldName, newName)
.replace(camelCase(oldName), camelCase(newName));
if (newFileName !== oldFileName) {
const newPath = path.resolve(dirPath, newFileName);
await fs.rename(oldPath, newPath);
}
}
}
export async function renameDirectory(oldDirPath: string, newDirName: string): Promise<string> {
const parentDir = path.dirname(oldDirPath);
const newDirPath = path.resolve(parentDir, newDirName);
await fs.rename(oldDirPath, newDirPath);
return newDirPath;
}
export async function createSymlink(target: string, linkPath: string): Promise<void> {
await fs.mkdir(path.dirname(linkPath), { recursive: true });
try {
const stats = await fs.lstat(linkPath);
if (stats.isSymbolicLink() || stats.isFile()) {
await fs.unlink(linkPath);
} else if (stats.isDirectory()) {
await fs.rm(linkPath, { recursive: true, force: true });
}
} catch (error) {
if (!isEnoentError(error)) {
throw error;
}
}
try {
const targetStats = await fs.stat(target);
const type = targetStats.isDirectory() ? 'dir' : 'file';
await fs.symlink(target, linkPath, type);
} catch {
await fs.symlink(target, linkPath, 'junction');
}
}