mirror of https://github.com/jkjoy/sunpeiwen.git
569 lines
14 KiB
JavaScript
569 lines
14 KiB
JavaScript
'use strict';
|
|
|
|
const Promise = require('bluebird');
|
|
const fs = require('graceful-fs');
|
|
const { dirname, join, extname, basename } = require('path');
|
|
const fsPromises = fs.promises;
|
|
const chokidar = require('chokidar');
|
|
const { escapeRegExp } = require('hexo-util');
|
|
|
|
const rEOL = /\r\n/g;
|
|
|
|
function exists(path) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
const promise = fsPromises.access(path).then(() => true, err => {
|
|
if (err.code !== 'ENOENT') throw err;
|
|
return false;
|
|
});
|
|
|
|
return Promise.resolve(promise);
|
|
}
|
|
|
|
function existsSync(path) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
try {
|
|
fs.accessSync(path);
|
|
} catch (err) {
|
|
if (err.code !== 'ENOENT') throw err;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function mkdirs(path) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
return Promise.resolve(fsPromises.mkdir(path, { recursive: true }));
|
|
}
|
|
|
|
function mkdirsSync(path) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
fs.mkdirSync(path, { recursive: true });
|
|
}
|
|
|
|
function checkParent(path) {
|
|
return Promise.resolve(fsPromises.mkdir(dirname(path), { recursive: true }));
|
|
}
|
|
|
|
function writeFile(path, data, options = {}) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
if (!data) data = '';
|
|
|
|
return checkParent(path).then(() => fsPromises.writeFile(path, data, options));
|
|
}
|
|
|
|
function writeFileSync(path, data, options) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
fs.mkdirSync(dirname(path), { recursive: true });
|
|
fs.writeFileSync(path, data, options);
|
|
}
|
|
|
|
function appendFile(path, data, options = {}) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
return checkParent(path).then(() => fsPromises.appendFile(path, data, options));
|
|
}
|
|
|
|
function appendFileSync(path, data, options) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
fs.mkdirSync(dirname(path), { recursive: true });
|
|
fs.appendFileSync(path, data, options);
|
|
}
|
|
|
|
function copyFile(src, dest, flags) {
|
|
if (!src) throw new TypeError('src is required!');
|
|
if (!dest) throw new TypeError('dest is required!');
|
|
|
|
return checkParent(dest).then(() => fsPromises.copyFile(src, dest, flags));
|
|
}
|
|
|
|
function trueFn() {
|
|
return true;
|
|
}
|
|
|
|
function ignoreHiddenFiles(ignore) {
|
|
if (!ignore) return trueFn;
|
|
|
|
return ({ name }) => !name.startsWith('.');
|
|
}
|
|
|
|
function ignoreFilesRegex(regex) {
|
|
if (!regex) return trueFn;
|
|
|
|
return ({ name }) => !regex.test(name);
|
|
}
|
|
|
|
function ignoreExcludeFiles(arr, parent) {
|
|
if (!arr || !arr.length) return trueFn;
|
|
|
|
const set = new Set(arr);
|
|
|
|
return ({ name }) => !set.has(join(parent, name));
|
|
}
|
|
|
|
async function _readAndFilterDir(path, options) {
|
|
const { ignoreHidden = true, ignorePattern } = options;
|
|
return (await fsPromises.readdir(path, { ...options, withFileTypes: true }))
|
|
.filter(ignoreHiddenFiles(ignoreHidden))
|
|
.filter(ignoreFilesRegex(ignorePattern));
|
|
}
|
|
|
|
function _readAndFilterDirSync(path, options) {
|
|
const { ignoreHidden = true, ignorePattern } = options;
|
|
return fs.readdirSync(path, { ...options, withFileTypes: true })
|
|
.filter(ignoreHiddenFiles(ignoreHidden))
|
|
.filter(ignoreFilesRegex(ignorePattern));
|
|
}
|
|
|
|
function _copyDirWalker(src, dest, results, parent, options) {
|
|
return Promise.map(_readAndFilterDir(src, options), item => {
|
|
const childSrc = join(src, item.name);
|
|
const childDest = join(dest, item.name);
|
|
const currentPath = join(parent, item.name);
|
|
|
|
if (item.isDirectory()) {
|
|
return _copyDirWalker(childSrc, childDest, results, currentPath, options);
|
|
}
|
|
results.push(currentPath);
|
|
return copyFile(childSrc, childDest);
|
|
});
|
|
}
|
|
|
|
function copyDir(src, dest, options = {}) {
|
|
if (!src) throw new TypeError('src is required!');
|
|
if (!dest) throw new TypeError('dest is required!');
|
|
|
|
const results = [];
|
|
|
|
return checkParent(dest).then(() => _copyDirWalker(src, dest, results, '', options)).return(results);
|
|
}
|
|
|
|
async function _listDirWalker(path, results, parent, options) {
|
|
const promises = [];
|
|
|
|
for (const item of await _readAndFilterDir(path, options)) {
|
|
const currentPath = join(parent, item.name);
|
|
|
|
if (item.isDirectory()) {
|
|
promises.push(_listDirWalker(join(path, item.name), results, currentPath, options));
|
|
} else {
|
|
results.push(currentPath);
|
|
}
|
|
}
|
|
|
|
await Promise.all(promises);
|
|
}
|
|
|
|
function listDir(path, options = {}) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
const results = [];
|
|
|
|
return Promise.resolve(_listDirWalker(path, results, '', options)).return(results);
|
|
}
|
|
|
|
function _listDirSyncWalker(path, results, parent, options) {
|
|
for (const item of _readAndFilterDirSync(path, options)) {
|
|
const currentPath = join(parent, item.name);
|
|
|
|
if (item.isDirectory()) {
|
|
_listDirSyncWalker(join(path, item.name), results, currentPath, options);
|
|
} else {
|
|
results.push(currentPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
function listDirSync(path, options = {}) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
const results = [];
|
|
|
|
_listDirSyncWalker(path, results, '', options);
|
|
|
|
return results;
|
|
}
|
|
|
|
function escapeEOL(str) {
|
|
return str.replace(rEOL, '\n');
|
|
}
|
|
|
|
function escapeBOM(str) {
|
|
return str.charCodeAt(0) === 0xFEFF ? str.substring(1) : str;
|
|
}
|
|
|
|
function escapeFileContent(content) {
|
|
return escapeBOM(escapeEOL(content));
|
|
}
|
|
|
|
async function _readFile(path, options) {
|
|
if (!Object.prototype.hasOwnProperty.call(options, 'encoding')) options.encoding = 'utf8';
|
|
|
|
const content = await fsPromises.readFile(path, options);
|
|
|
|
if (options.escape == null || options.escape) {
|
|
return escapeFileContent(content);
|
|
}
|
|
|
|
return content;
|
|
}
|
|
|
|
function readFile(path, options = {}) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
return Promise.resolve(_readFile(path, options));
|
|
}
|
|
|
|
function readFileSync(path, options = {}) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
if (!Object.prototype.hasOwnProperty.call(options, 'encoding')) options.encoding = 'utf8';
|
|
|
|
const content = fs.readFileSync(path, options);
|
|
|
|
if (options.escape == null || options.escape) {
|
|
return escapeFileContent(content);
|
|
}
|
|
|
|
return content;
|
|
}
|
|
|
|
async function _emptyDir(path, parent, options) {
|
|
const entrys = (await _readAndFilterDir(path, options))
|
|
.filter(ignoreExcludeFiles(options.exclude, parent));
|
|
const results = [];
|
|
|
|
await Promise.map(entrys, item => {
|
|
const fullPath = join(path, item.name);
|
|
const currentPath = join(parent, item.name);
|
|
|
|
if (item.isDirectory()) {
|
|
return _emptyDir(fullPath, currentPath, options).then(files => {
|
|
if (!files.length) {
|
|
return fsPromises.rmdir(fullPath);
|
|
}
|
|
results.push(...files);
|
|
});
|
|
}
|
|
results.push(currentPath);
|
|
return fsPromises.unlink(fullPath);
|
|
});
|
|
|
|
return results;
|
|
}
|
|
|
|
function emptyDir(path, options = {}) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
return Promise.resolve(_emptyDir(path, '', options));
|
|
}
|
|
|
|
function _emptyDirSync(path, options, parent) {
|
|
const entrys = _readAndFilterDirSync(path, options)
|
|
.filter(ignoreExcludeFiles(options.exclude, parent));
|
|
|
|
const results = [];
|
|
|
|
for (const item of entrys) {
|
|
const childPath = join(path, item.name);
|
|
const currentPath = join(parent, item.name);
|
|
|
|
if (item.isDirectory()) {
|
|
const removed = _emptyDirSync(childPath, options, currentPath);
|
|
|
|
if (!fs.readdirSync(childPath).length) {
|
|
rmdirSync(childPath);
|
|
}
|
|
|
|
results.push(...removed);
|
|
} else {
|
|
fs.unlinkSync(childPath);
|
|
results.push(currentPath);
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
function emptyDirSync(path, options = {}) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
return _emptyDirSync(path, options, '');
|
|
}
|
|
|
|
async function _rmdir(path) {
|
|
const files = fsPromises.readdir(path, { withFileTypes: true });
|
|
await Promise.map(files, item => {
|
|
const childPath = join(path, item.name);
|
|
|
|
return item.isDirectory() ? _rmdir(childPath) : fsPromises.unlink(childPath);
|
|
});
|
|
return fsPromises.rmdir(path);
|
|
}
|
|
|
|
function rmdir(path) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
return Promise.resolve(_rmdir(path));
|
|
}
|
|
|
|
function _rmdirSync(path) {
|
|
const files = fs.readdirSync(path, { withFileTypes: true });
|
|
|
|
for (const item of files) {
|
|
const childPath = join(path, item.name);
|
|
|
|
if (item.isDirectory()) {
|
|
_rmdirSync(childPath);
|
|
} else {
|
|
fs.unlinkSync(childPath);
|
|
}
|
|
}
|
|
|
|
fs.rmdirSync(path);
|
|
}
|
|
|
|
function rmdirSync(path) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
_rmdirSync(path);
|
|
}
|
|
|
|
function watch(path, options = {}) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
const watcher = chokidar.watch(path, options);
|
|
|
|
return new Promise((resolve, reject) => {
|
|
watcher.on('ready', resolve);
|
|
watcher.on('error', reject);
|
|
}).thenReturn(watcher);
|
|
}
|
|
|
|
function _findUnusedPath(path, files) {
|
|
const ext = extname(path);
|
|
const base = basename(path, ext);
|
|
const regex = new RegExp(`^${escapeRegExp(base)}(?:-(\\d+))?${escapeRegExp(ext)}$`);
|
|
let num = -1;
|
|
|
|
for (let i = 0, len = files.length; i < len; i++) {
|
|
const item = files[i];
|
|
const match = item.match(regex);
|
|
|
|
if (match == null) continue;
|
|
const matchNum = match[1] ? parseInt(match[1], 10) : 0;
|
|
|
|
if (matchNum > num) {
|
|
num = matchNum;
|
|
}
|
|
}
|
|
|
|
return join(dirname(path), `${base}-${num + 1}${ext}`);
|
|
}
|
|
|
|
async function _ensurePath(path) {
|
|
if (!await exists(path)) return path;
|
|
|
|
const files = await fsPromises.readdir(dirname(path));
|
|
return _findUnusedPath(path, files);
|
|
}
|
|
|
|
function ensurePath(path) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
return Promise.resolve(_ensurePath(path));
|
|
}
|
|
|
|
function ensurePathSync(path) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
if (!fs.existsSync(path)) return path;
|
|
|
|
const files = fs.readdirSync(dirname(path));
|
|
|
|
return _findUnusedPath(path, files);
|
|
}
|
|
|
|
function ensureWriteStream(path, options = {}) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
return checkParent(path).then(() => fs.createWriteStream(path, options));
|
|
}
|
|
|
|
function ensureWriteStreamSync(path, options) {
|
|
if (!path) throw new TypeError('path is required!');
|
|
|
|
fs.mkdirSync(dirname(path), { recursive: true });
|
|
return fs.createWriteStream(path, options);
|
|
}
|
|
|
|
// access
|
|
['F_OK', 'R_OK', 'W_OK', 'X_OK'].forEach(key => {
|
|
Object.defineProperty(exports, key, {
|
|
enumerable: true,
|
|
value: fs.constants[key],
|
|
writable: false
|
|
});
|
|
});
|
|
|
|
exports.access = Promise.promisify(fs.access);
|
|
exports.accessSync = fs.accessSync;
|
|
|
|
// appendFile
|
|
exports.appendFile = appendFile;
|
|
exports.appendFileSync = appendFileSync;
|
|
|
|
// chmod
|
|
exports.chmod = Promise.promisify(fs.chmod);
|
|
exports.chmodSync = fs.chmodSync;
|
|
exports.fchmod = Promise.promisify(fs.fchmod);
|
|
exports.fchmodSync = fs.fchmodSync;
|
|
exports.lchmod = Promise.promisify(fs.lchmod);
|
|
exports.lchmodSync = fs.lchmodSync;
|
|
|
|
// chown
|
|
exports.chown = Promise.promisify(fs.chown);
|
|
exports.chownSync = fs.chownSync;
|
|
exports.fchown = Promise.promisify(fs.fchown);
|
|
exports.fchownSync = fs.fchownSync;
|
|
exports.lchown = Promise.promisify(fs.lchown);
|
|
exports.lchownSync = fs.lchownSync;
|
|
|
|
// close
|
|
exports.close = Promise.promisify(fs.close);
|
|
exports.closeSync = fs.closeSync;
|
|
|
|
// copy
|
|
exports.copyDir = copyDir;
|
|
exports.copyFile = copyFile;
|
|
|
|
// createStream
|
|
exports.createReadStream = fs.createReadStream;
|
|
exports.createWriteStream = fs.createWriteStream;
|
|
|
|
// emptyDir
|
|
exports.emptyDir = emptyDir;
|
|
exports.emptyDirSync = emptyDirSync;
|
|
|
|
// ensurePath
|
|
exports.ensurePath = ensurePath;
|
|
exports.ensurePathSync = ensurePathSync;
|
|
|
|
// ensureWriteStream
|
|
exports.ensureWriteStream = ensureWriteStream;
|
|
exports.ensureWriteStreamSync = ensureWriteStreamSync;
|
|
|
|
// exists
|
|
exports.exists = exists;
|
|
exports.existsSync = existsSync;
|
|
|
|
// fsync
|
|
exports.fsync = Promise.promisify(fs.fsync);
|
|
exports.fsyncSync = fs.fsyncSync;
|
|
|
|
// link
|
|
exports.link = Promise.promisify(fs.link);
|
|
exports.linkSync = fs.linkSync;
|
|
|
|
// listDir
|
|
exports.listDir = listDir;
|
|
exports.listDirSync = listDirSync;
|
|
|
|
// mkdir
|
|
exports.mkdir = Promise.promisify(fs.mkdir);
|
|
exports.mkdirSync = fs.mkdirSync;
|
|
|
|
// mkdirs
|
|
exports.mkdirs = mkdirs;
|
|
exports.mkdirsSync = mkdirsSync;
|
|
|
|
// open
|
|
exports.open = Promise.promisify(fs.open);
|
|
exports.openSync = fs.openSync;
|
|
|
|
// symlink
|
|
exports.symlink = Promise.promisify(fs.symlink);
|
|
exports.symlinkSync = fs.symlinkSync;
|
|
|
|
// read
|
|
exports.read = Promise.promisify(fs.read);
|
|
exports.readSync = fs.readSync;
|
|
|
|
// readdir
|
|
exports.readdir = Promise.promisify(fs.readdir);
|
|
exports.readdirSync = fs.readdirSync;
|
|
|
|
// readFile
|
|
exports.readFile = readFile;
|
|
exports.readFileSync = readFileSync;
|
|
|
|
// readlink
|
|
exports.readlink = Promise.promisify(fs.readlink);
|
|
exports.readlinkSync = fs.readlinkSync;
|
|
|
|
// realpath
|
|
exports.realpath = Promise.promisify(fs.realpath);
|
|
exports.realpathSync = fs.realpathSync;
|
|
|
|
// rename
|
|
exports.rename = Promise.promisify(fs.rename);
|
|
exports.renameSync = fs.renameSync;
|
|
|
|
// rmdir
|
|
exports.rmdir = rmdir;
|
|
exports.rmdirSync = rmdirSync;
|
|
|
|
// stat
|
|
exports.stat = Promise.promisify(fs.stat);
|
|
exports.statSync = fs.statSync;
|
|
exports.fstat = Promise.promisify(fs.fstat);
|
|
exports.fstatSync = fs.fstatSync;
|
|
exports.lstat = Promise.promisify(fs.lstat);
|
|
exports.lstatSync = fs.lstatSync;
|
|
|
|
// truncate
|
|
exports.truncate = Promise.promisify(fs.truncate);
|
|
exports.truncateSync = fs.truncateSync;
|
|
exports.ftruncate = Promise.promisify(fs.ftruncate);
|
|
exports.ftruncateSync = fs.ftruncateSync;
|
|
|
|
// unlink
|
|
exports.unlink = Promise.promisify(fs.unlink);
|
|
exports.unlinkSync = fs.unlinkSync;
|
|
|
|
// utimes
|
|
exports.utimes = Promise.promisify(fs.utimes);
|
|
exports.utimesSync = fs.utimesSync;
|
|
exports.futimes = Promise.promisify(fs.futimes);
|
|
exports.futimesSync = fs.futimesSync;
|
|
|
|
// watch
|
|
exports.watch = watch;
|
|
exports.watchFile = fs.watchFile;
|
|
exports.unwatchFile = fs.unwatchFile;
|
|
|
|
// write
|
|
exports.write = Promise.promisify(fs.write);
|
|
exports.writeSync = fs.writeSync;
|
|
|
|
// writeFile
|
|
exports.writeFile = writeFile;
|
|
exports.writeFileSync = writeFileSync;
|
|
|
|
// Static classes
|
|
exports.Stats = fs.Stats;
|
|
exports.ReadStream = fs.ReadStream;
|
|
exports.WriteStream = fs.WriteStream;
|
|
exports.FileReadStream = fs.FileReadStream;
|
|
exports.FileWriteStream = fs.FileWriteStream;
|
|
|
|
// util
|
|
exports.escapeBOM = escapeBOM;
|
|
exports.escapeEOL = escapeEOL;
|