mono/packages/fs/dist/dir.js
2025-02-21 12:15:15 +01:00

186 lines
5.8 KiB
JavaScript

import * as fs from 'node:fs';
import * as pathUtil from 'node:path';
import { stat, statSync, readdirSync, readdir } from 'fs';
import { promisify } from 'util';
import { sync as removeSync, async as removeAsync } from './remove.js';
import { normalizeFileMode as modeUtil } from './utils/mode.js';
import { validateArgument, validateOptions } from './utils/validate.js';
import { ErrNoDirectory } from './errors.js';
import { EError } from './interfaces.js';
import { sync as mkdirp } from 'mkdirp';
export const validateInput = (methodName, path, options) => {
const methodSignature = methodName + '(path, [criteria])';
validateArgument(methodSignature, 'path', path, ['string']);
validateOptions(methodSignature, 'criteria', options, {
empty: ['boolean'],
mode: ['string', 'number']
});
};
const defaults = (options) => {
const result = options || {};
if (typeof result.empty !== 'boolean') {
result.empty = false;
}
if (result.mode !== undefined) {
result.mode = modeUtil(result.mode);
}
return result;
};
// ---------------------------------------------------------
// Sync
// ---------------------------------------------------------
const dirStatsSync = (path) => {
let _stat;
try {
_stat = statSync(path);
}
catch (err) {
// Detection if path already exists
if (err.code !== EError.NOEXISTS) {
throw err;
}
}
if (_stat && !_stat.isDirectory()) {
throw ErrNoDirectory(path);
}
return _stat;
};
function mkdirSync(path, criteria) {
mkdirp(path, { mode: criteria.mode, fs: null });
}
function checkDirSync(path, _stat, options) {
const check = function () {
if (options.mode !== undefined) {
fs.chmodSync(path, options.mode);
}
};
const checkEmptiness = function () {
let list;
if (options.empty) {
// Delete everything inside this directory
list = readdirSync(path);
list.forEach(function (filename) {
removeSync(pathUtil.resolve(path, filename));
});
}
};
check();
checkEmptiness();
}
export const sync = (path, options) => {
const criteria = defaults(options);
const _stat = dirStatsSync(path);
if (_stat) {
checkDirSync(path, _stat, criteria);
}
else {
mkdirSync(path, criteria);
}
};
// ---------------------------------------------------------
// Async
// ---------------------------------------------------------
const promisedStat = promisify(stat);
const promisedReaddir = promisify(readdir);
const dirStatAsync = (path) => {
return new Promise((resolve, reject) => {
promisedStat(path)
.then((_stat) => {
if (_stat.isDirectory()) {
resolve(_stat);
}
else {
reject(ErrNoDirectory(path));
}
})
.catch((err) => (err.code === EError.NOEXISTS ? resolve(undefined) : reject(err)));
});
};
// Delete all files and directores inside given directory
const emptyAsync = (path) => {
return new Promise((resolve, reject) => {
promisedReaddir(path)
.then(function (list) {
const doOne = function (index) {
let subPath;
if (index === list.length) {
resolve(1);
}
else {
subPath = pathUtil.resolve(path, list[index]);
removeAsync(subPath).then(() => doOne(index + 1));
}
};
doOne(0);
})
.catch(reject);
});
};
const checkMode = function (criteria, _stat, path) {
if (criteria.mode !== undefined) {
return promisify(fs.chmod)(path, criteria.mode);
}
return Promise.resolve(null);
};
const checkDirAsync = (path, _stat, options) => {
return new Promise((resolve, reject) => {
const checkEmptiness = function () {
if (options.empty) {
return emptyAsync(path);
}
return Promise.resolve();
};
checkMode(options, _stat, path)
.then(checkEmptiness)
.then(resolve, reject);
});
};
const mkdirAsync = (path, criteria) => {
const options = criteria || {};
return new Promise((resolve, reject) => {
promisify(fs.mkdir)(path, options.mode)
.then(resolve)
.catch((err) => {
if (err.code === 'ENOENT') {
// Parent directory doesn't exist. Need to create it first.
mkdirAsync(pathUtil.dirname(path), options)
.then(() => {
// Now retry creating this directory.
return promisify(fs.mkdir)(path, options.mode);
})
.then(resolve)
.catch((err2) => {
if (err2.code === 'EEXIST') {
// Hmm, something other have already created the directory?
// No problem for us.
resolve(1);
}
else {
reject(err2);
}
});
}
else if (err.code === 'EEXIST') {
// The path already exists. We're fine.
resolve(1);
}
else {
reject(err);
}
});
});
};
export const async = (path, passedCriteria) => {
const criteria = defaults(passedCriteria);
return new Promise((resolve, reject) => {
dirStatAsync(path)
.then((_stat) => {
if (_stat !== undefined) {
return checkDirAsync(path, _stat, criteria);
}
return mkdirAsync(path, criteria);
})
.then(resolve, reject);
});
};