@file-services/utils
Version:
Common file system utility functions.
432 lines (429 loc) • 13.2 kB
JavaScript
// packages/utils/src/create-extended-api.ts
var returnsTrue = () => true;
var statsNoThrowOptions = { throwIfNoEntry: false };
function createFileSystem(baseFs) {
return {
...baseFs,
...createExtendedSyncActions(baseFs),
promises: {
...baseFs.promises,
...createExtendedFileSystemPromiseActions(baseFs)
}
};
}
function createSyncFileSystem(baseFs) {
return {
...baseFs,
...createExtendedSyncActions(baseFs)
};
}
function createExtendedSyncActions(baseFs) {
const { statSync, mkdirSync, writeFileSync, readdirSync, readFileSync, copyFileSync, dirname, join, resolve } = baseFs;
function fileExistsSync(filePath, statFn = statSync) {
try {
return !!statFn(filePath, statsNoThrowOptions)?.isFile();
} catch {
return false;
}
}
function readJsonFileSync(filePath, options) {
return JSON.parse(readFileSync(filePath, options || "utf8"));
}
function directoryExistsSync(directoryPath, statFn = statSync) {
try {
return !!statFn(directoryPath, statsNoThrowOptions)?.isDirectory();
} catch {
return false;
}
}
function ensureDirectorySync(directoryPath) {
try {
mkdirSync(directoryPath);
} catch (e) {
const code = e?.code;
if (code === "EISDIR") {
return;
} else if (code === "EEXIST") {
if (directoryExistsSync(directoryPath)) {
return;
} else {
throw e;
}
} else if (code === "ENOTDIR" || !code) {
throw e;
}
const parentPath = dirname(directoryPath);
if (parentPath === directoryPath) {
throw e;
}
ensureDirectorySync(parentPath);
try {
mkdirSync(directoryPath);
} catch (e2) {
const code2 = e2?.code;
const isDirectoryExistsError = code2 === "EISDIR" || code2 === "EEXIST" && directoryExistsSync(directoryPath);
if (!isDirectoryExistsError) {
throw e2;
}
}
}
}
function populateDirectorySync(directoryPath, contents) {
const filePaths = [];
ensureDirectorySync(directoryPath);
for (const [nodeName, nodeValue] of Object.entries(contents)) {
const nodePath = join(directoryPath, nodeName);
if (typeof nodeValue === "string" || nodeValue instanceof Uint8Array) {
ensureDirectorySync(dirname(nodePath));
writeFileSync(nodePath, nodeValue);
filePaths.push(nodePath);
} else {
populateDirectorySync(nodePath, nodeValue);
}
}
return filePaths;
}
function findFilesSync(rootDirectory, options = {}, filePaths = []) {
const { filterFile = returnsTrue, filterDirectory = returnsTrue } = options;
for (const item of readdirSync(rootDirectory, { withFileTypes: true })) {
const nodePath = join(rootDirectory, item.name);
const nodeDesc = { name: item.name, path: nodePath };
if (item.isFile() && filterFile(nodeDesc)) {
filePaths.push(nodePath);
} else if (item.isDirectory() && filterDirectory(nodeDesc)) {
findFilesSync(nodePath, options, filePaths);
}
}
return filePaths;
}
function findClosestFileSync(initialDirectoryPath, fileName) {
let currentPath = resolve(initialDirectoryPath);
let lastPath;
while (currentPath !== lastPath) {
const filePath = join(currentPath, fileName);
if (fileExistsSync(filePath)) {
return filePath;
}
lastPath = currentPath;
currentPath = dirname(currentPath);
}
return void 0;
}
function findFilesInAncestorsSync(initialDirectoryPath, fileName) {
const filePaths = [];
let currentPath = resolve(initialDirectoryPath);
let lastPath;
while (currentPath !== lastPath) {
const filePath = join(currentPath, fileName);
if (fileExistsSync(filePath)) {
filePaths.push(filePath);
}
lastPath = currentPath;
currentPath = dirname(currentPath);
}
return filePaths;
}
function copyDirectorySync(sourcePath, destinationPath) {
ensureDirectorySync(destinationPath);
for (const item of readdirSync(sourcePath, { withFileTypes: true })) {
const sourceItemPath = join(sourcePath, item.name);
const destinationItemPath = join(destinationPath, item.name);
if (item.isFile()) {
copyFileSync(sourceItemPath, destinationItemPath);
} else if (item.isDirectory()) {
copyDirectorySync(sourceItemPath, destinationItemPath);
}
}
}
return {
fileExistsSync,
directoryExistsSync,
// resolve path once for recursive functions
ensureDirectorySync: (directoryPath) => ensureDirectorySync(resolve(directoryPath)),
populateDirectorySync: (directoryPath, contents) => populateDirectorySync(resolve(directoryPath), contents),
findFilesSync: (rootDirectory, options) => findFilesSync(resolve(rootDirectory), options),
copyDirectorySync: (sourcePath, destinationPath) => copyDirectorySync(resolve(sourcePath), resolve(destinationPath)),
findClosestFileSync,
findFilesInAncestorsSync,
readJsonFileSync
};
}
function createAsyncFileSystem(baseFs) {
return {
...baseFs,
promises: {
...baseFs.promises,
...createExtendedFileSystemPromiseActions(baseFs)
}
};
}
function createExtendedFileSystemPromiseActions(baseFs) {
const {
dirname,
resolve,
join,
promises: { stat, mkdir, writeFile, readdir, readFile, copyFile }
} = baseFs;
async function fileExists(filePath, statFn = stat) {
try {
return (await statFn(filePath)).isFile();
} catch {
return false;
}
}
async function directoryExists(directoryPath, statFn = stat) {
try {
return (await statFn(directoryPath)).isDirectory();
} catch {
return false;
}
}
async function readJsonFile(filePath, options) {
return JSON.parse(await readFile(filePath, options || "utf8"));
}
async function ensureDirectory(directoryPath) {
try {
await mkdir(directoryPath);
} catch (e) {
const code = e?.code;
if (code === "EISDIR") {
return;
} else if (code === "EEXIST") {
if (await directoryExists(directoryPath)) {
return;
} else {
throw e;
}
} else if (code === "ENOTDIR" || !code) {
throw e;
}
const parentPath = dirname(directoryPath);
if (parentPath === directoryPath) {
throw e;
}
await ensureDirectory(parentPath);
try {
await mkdir(directoryPath);
} catch (e2) {
const code2 = e2?.code;
const isDirectoryExistsError = code2 === "EISDIR" || code2 === "EEXIST" && await directoryExists(directoryPath);
if (!isDirectoryExistsError) {
throw e2;
}
}
}
}
async function populateDirectory(directoryPath, contents) {
const filePaths = [];
await ensureDirectory(directoryPath);
for (const [nodeName, nodeValue] of Object.entries(contents)) {
const nodePath = join(directoryPath, nodeName);
if (typeof nodeValue === "string" || nodeValue instanceof Uint8Array) {
await ensureDirectory(dirname(nodePath));
await writeFile(nodePath, nodeValue);
filePaths.push(nodePath);
} else {
await populateDirectory(nodePath, nodeValue);
}
}
return filePaths;
}
async function findFiles(rootDirectory, options = {}, filePaths = []) {
const { filterFile = returnsTrue, filterDirectory = returnsTrue } = options;
for (const item of await readdir(rootDirectory, { withFileTypes: true })) {
const nodePath = join(rootDirectory, item.name);
const nodeDesc = { name: item.name, path: nodePath };
if (item.isFile() && filterFile(nodeDesc)) {
filePaths.push(nodePath);
} else if (item.isDirectory() && filterDirectory(nodeDesc)) {
await findFiles(nodePath, options, filePaths);
}
}
return filePaths;
}
async function findClosestFile(initialDirectoryPath, fileName) {
let currentPath = resolve(initialDirectoryPath);
let lastPath;
while (currentPath !== lastPath) {
const filePath = join(currentPath, fileName);
if (await fileExists(filePath)) {
return filePath;
}
lastPath = currentPath;
currentPath = dirname(currentPath);
}
return void 0;
}
async function findFilesInAncestors(initialDirectoryPath, fileName) {
const filePaths = [];
let currentPath = resolve(initialDirectoryPath);
let lastPath;
while (currentPath !== lastPath) {
const filePath = join(currentPath, fileName);
if (await fileExists(filePath)) {
filePaths.push(filePath);
}
lastPath = currentPath;
currentPath = dirname(currentPath);
}
return filePaths;
}
async function copyDirectory(sourcePath, destinationPath) {
await ensureDirectory(destinationPath);
for (const item of await readdir(sourcePath, { withFileTypes: true })) {
const sourceItemPath = join(sourcePath, item.name);
const destinationItemPath = join(destinationPath, item.name);
if (item.isFile()) {
await copyFile(sourceItemPath, destinationItemPath);
} else if (item.isDirectory()) {
await copyDirectory(sourceItemPath, destinationItemPath);
}
}
}
return {
fileExists,
directoryExists,
ensureDirectory: (directoryPath) => ensureDirectory(resolve(directoryPath)),
populateDirectory: (directoryPath, contents) => populateDirectory(resolve(directoryPath), contents),
findFiles: (rootDirectory, options) => findFiles(resolve(rootDirectory), options),
copyDirectory: (sourcePath, destinationPath) => copyDirectory(resolve(sourcePath), resolve(destinationPath)),
findClosestFile,
findFilesInAncestors,
readJsonFile
};
}
// packages/utils/src/set-multi-map.ts
var SetMultiMap = class {
constructor() {
this.map = /* @__PURE__ */ new Map();
}
get size() {
return Array.from(this.map.values()).map(({ size }) => size).reduce((sum, size) => sum + size, 0);
}
get(key) {
return this.map.get(key);
}
add(key, value) {
const valueSet = this.map.get(key);
if (valueSet) {
valueSet.add(value);
} else {
this.map.set(key, /* @__PURE__ */ new Set([value]));
}
return this;
}
clear() {
this.map.clear();
}
delete(key, value) {
const valueSet = this.map.get(key);
if (valueSet) {
const wasInSet = valueSet.delete(value);
if (valueSet.size === 0) {
this.map.delete(key);
}
return wasInSet;
}
return false;
}
deleteKey(key) {
return this.map.delete(key);
}
has(key, value) {
const valueSet = this.map.get(key);
return valueSet ? valueSet.has(value) : false;
}
hasKey(key) {
const existingSet = this.map.get(key);
return !!existingSet && existingSet.size > 0;
}
[Symbol.iterator]() {
return this.entries();
}
*entries() {
const { map } = this;
for (const [key, valueSet] of map.entries()) {
for (const value of valueSet) {
yield [key, value];
}
}
}
*values() {
const { map } = this;
for (const valueSet of map.values()) {
for (const value of valueSet) {
yield value;
}
}
}
keys() {
return this.map.keys();
}
};
// packages/utils/src/sync-to-async-fs.ts
function syncToAsyncFs(syncFs) {
return {
...syncFs,
caseSensitive: syncFs.caseSensitive,
promises: {
readFile: async function readFile(...args) {
return syncFs.readFileSync(...args);
},
async writeFile(...args) {
return syncFs.writeFileSync(...args);
},
async unlink(filePath) {
return syncFs.unlinkSync(filePath);
},
readdir: async function readdir(...args) {
return syncFs.readdirSync(...args);
},
async mkdir(directoryPath, ...args) {
return syncFs.mkdirSync(directoryPath, ...args);
},
async rmdir(directoryPath) {
return syncFs.rmdirSync(directoryPath);
},
async exists(nodePath) {
return syncFs.existsSync(nodePath);
},
async stat(nodePath) {
return syncFs.statSync(nodePath);
},
async lstat(nodePath) {
return syncFs.lstatSync(nodePath);
},
async realpath(nodePath) {
return syncFs.realpathSync(nodePath);
},
async rename(srcPath, destPath) {
return syncFs.renameSync(srcPath, destPath);
},
async copyFile(...args) {
return syncFs.copyFileSync(...args);
},
async readlink(path) {
return syncFs.readlinkSync(path);
},
async symlink(...args) {
return syncFs.symlinkSync(...args);
},
async rm(...args) {
return syncFs.rmSync(...args);
},
async chmod(...args) {
return syncFs.chmodSync(...args);
}
}
};
}
export {
SetMultiMap,
createAsyncFileSystem,
createExtendedFileSystemPromiseActions,
createExtendedSyncActions,
createFileSystem,
createSyncFileSystem,
syncToAsyncFs
};
//# sourceMappingURL=fs-utils.mjs.map