diff --git a/.eslintignore b/.eslintignore index f6b9638..108b0e8 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ node_modules/ coverage/ -tmp/ \ No newline at end of file +tmp/ +dist/ diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..b658fd5 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,26 @@ +module.exports = { + root: true, + env: { + commonjs: true, + es6: true, + node: true + }, + extends: [ + 'standard' + ], + globals: { + Atomics: 'readonly', + SharedArrayBuffer: 'readonly' + }, + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2018 + }, + plugins: [ + '@typescript-eslint' + ], + rules: { + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': ['error'] + } +} diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 91288aa..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "hexo", - "root": true -} \ No newline at end of file diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml new file mode 100644 index 0000000..16e61fa --- /dev/null +++ b/.github/workflows/nodejs.yml @@ -0,0 +1,22 @@ +name: Node.js CI + +on: [push, pull_request] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [10.x, 12.x, 14.x] + + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - run: npm run build --if-present + - run: npm run eslint diff --git a/.gitignore b/.gitignore index 1b7fadf..11e33eb 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ tmp/ .idea/ .nyc_output/ package-lock.json +yarn.lock +dist diff --git a/.travis.yml b/.travis.yml index 99cfc58..4535a4e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,6 @@ cache: node_js: - "10" - "12" - - "13" - "14" script: diff --git a/lib/fs.js b/lib/fs.js deleted file mode 100644 index 93ac68d..0000000 --- a/lib/fs.js +++ /dev/null @@ -1,604 +0,0 @@ -'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, callback) { - if (!path) throw new TypeError('path is required!'); - - const promise = fsPromises.access(path).then(() => true, err => { - if (err.code !== 'ENOENT') throw err; - return false; - }).then(exist => { - if (typeof callback === 'function') callback(exist); - return exist; - }); - - 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, callback) { - if (!path) throw new TypeError('path is required!'); - - return Promise.resolve(fsPromises.mkdir(path, { recursive: true })).asCallback(callback); -} - -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, callback) { - if (!path) throw new TypeError('path is required!'); - - if (!data) data = ''; - - if (!callback && typeof options === 'function') { - callback = options; - options = {}; - } - - return checkParent(path).then(() => fsPromises.writeFile(path, data, options)).asCallback(callback); -} - -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, callback) { - if (!path) throw new TypeError('path is required!'); - - if (!callback && typeof options === 'function') { - callback = options; - options = {}; - } - - return checkParent(path).then(() => fsPromises.appendFile(path, data, options)).asCallback(callback); -} - -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, callback) { - if (!src) throw new TypeError('src is required!'); - if (!dest) throw new TypeError('dest is required!'); - if (typeof flags === 'function') { - callback = flags; - flags = undefined; - } - - return checkParent(dest).then(() => fsPromises.copyFile(src, dest, flags)).asCallback(callback); -} - -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)); -} - -function reduceFiles(result, item) { - if (Array.isArray(item)) { - return result.concat(item); - } - - result.push(item); - return result; -} - -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)); -} - -async function _copyDir(src, dest, options, parent) { - const entrys = await _readAndFilterDir(src, options); - return Promise.reduce(entrys.map(item => { - const childSrc = join(src, item.name); - const childDest = join(dest, item.name); - const currentPath = join(parent, item.name); - - if (item.isDirectory()) { - return _copyDir(childSrc, childDest, options, currentPath); - } - - return copyFile(childSrc, childDest).thenReturn(currentPath); - }), reduceFiles, []); -} - -function copyDir(src, dest, options = {}, callback) { - if (!src) throw new TypeError('src is required!'); - if (!dest) throw new TypeError('dest is required!'); - - if (!callback && typeof options === 'function') { - callback = options; - options = {}; - } - - return checkParent(dest).then(() => _copyDir(src, dest, options, '')).asCallback(callback); -} - -async function _listDir(path, options, parent) { - const entrys = await _readAndFilterDir(path, options); - return Promise.reduce(entrys.map(item => { - const currentPath = join(parent, item.name); - - if (item.isDirectory()) { - return _listDir(join(path, item.name), options, currentPath); - } - - return currentPath; - }), reduceFiles, []); -} - -function listDir(path, options = {}, callback) { - if (!path) throw new TypeError('path is required!'); - - if (!callback && typeof options === 'function') { - callback = options; - options = {}; - } - - return Promise.resolve(_listDir(path, options, '')).asCallback(callback); -} - -function _listDirSync(path, options, parent) { - return _readAndFilterDirSync(path, options).map(item => { - const currentPath = join(parent, item.name); - - if (item.isDirectory()) { - return _listDirSync(join(path, item.name), options, currentPath); - } - - return currentPath; - }).reduce(reduceFiles, []); -} - -function listDirSync(path, options = {}) { - if (!path) throw new TypeError('path is required!'); - - return _listDirSync(path, options, ''); -} - -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 = {}, callback) { - if (!path) throw new TypeError('path is required!'); - - if (!callback && typeof options === 'function') { - callback = options; - options = {}; - } - - return Promise.resolve(_readFile(path, options)).asCallback(callback); -} - -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, options, parent) { - const entrys = (await _readAndFilterDir(path, options)) - .filter(ignoreExcludeFiles(options.exclude, parent)); - return Promise.reduce(entrys.map(item => { - const fullPath = join(path, item.name); - const currentPath = join(parent, item.name); - - if (item.isDirectory()) { - return _emptyDir(fullPath, options, currentPath).then(files => { - if (!files.length) return fsPromises.rmdir(fullPath).then(() => files); - return files; - }); - } - - return fsPromises.unlink(fullPath).then(() => currentPath); - }), reduceFiles, []); -} - -function emptyDir(path, options = {}, callback) { - if (!path) throw new TypeError('path is required!'); - - if (!callback && typeof options === 'function') { - callback = options; - options = {}; - } - - return Promise.resolve(_emptyDir(path, options, '')).asCallback(callback); -} - -function _emptyDirSync(path, options, parent) { - return _readAndFilterDirSync(path, options) - .filter(ignoreExcludeFiles(options.exclude, parent)) - .map(item => { - 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); - } - - return removed; - } - - fs.unlinkSync(childPath); - return currentPath; - }).reduce(reduceFiles, []); -} - -function emptyDirSync(path, options = {}) { - if (!path) throw new TypeError('path is required!'); - - return _emptyDirSync(path, options, ''); -} - -async function _rmdir(path) { - const files = await fsPromises.readdir(path, { withFileTypes: true }); - await Promise.all(files.map(item => { - const childPath = join(path, item.name); - - return item.isDirectory() ? _rmdir(childPath) : fsPromises.unlink(childPath); - })); - return fsPromises.rmdir(path); -} - -function rmdir(path, callback) { - if (!path) throw new TypeError('path is required!'); - - return Promise.resolve(_rmdir(path)).asCallback(callback); -} - -function _rmdirSync(path) { - const files = fs.readdirSync(path, { withFileTypes: true }); - - for (let i = 0, len = files.length; i < len; i++) { - const item = files[i]; - 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, callback) { - if (!path) throw new TypeError('path is required!'); - - if (!callback && typeof options === 'function') { - callback = options; - options = {}; - } - - const watcher = chokidar.watch(path, options); - - return new Promise((resolve, reject) => { - watcher.on('ready', resolve); - watcher.on('error', reject); - }).thenReturn(watcher).asCallback(callback); -} - -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, callback) { - if (!path) throw new TypeError('path is required!'); - - return Promise.resolve(_ensurePath(path)).asCallback(callback); -} - -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, callback) { - if (!path) throw new TypeError('path is required!'); - - if (!callback && typeof options === 'function') { - callback = options; - options = {}; - } - - return checkParent(path).then(() => fs.createWriteStream(path, options)).asCallback(callback); -} - -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; diff --git a/lib/fs.ts b/lib/fs.ts new file mode 100644 index 0000000..cc502d7 --- /dev/null +++ b/lib/fs.ts @@ -0,0 +1,638 @@ +import { Dirent, NoParamCallback, WriteFileOptions, WriteStream } from 'fs' +import chokidar, { FSWatcher, WatchOptions } from 'chokidar' +import BlueBirdPromise from 'bluebird' +import { dirname, join, extname, basename } from 'path' +import { escapeRegExp } from 'hexo-util' + +import fs from 'graceful-fs' + +const fsPromises = fs.promises + +const rEOL = /\r\n/g + +function exists (path: string, callback?: (exist: boolean) => any) { + if (!path) throw new TypeError('path is required!') + const promise = fsPromises.access(path) + .then(() => true, error => { + if (error.code !== 'ENOENT') throw error + return false + }) + .then(exist => { + if (typeof callback === 'function') callback(exist) + return exist + }) + + return Promise.resolve(promise) +} + +function existsSync (path: string) { + 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: string, callback: (path?: string) => void) { + if (!path) throw new TypeError('path is required!') + + return BlueBirdPromise.resolve(fsPromises.mkdir(path, { recursive: true })).asCallback(callback) +} + +function mkdirsSync (path: string) { + if (!path) throw new TypeError('path is required!') + + fs.mkdirSync(path, { recursive: true }) +} + +const checkParent = (path: string) => BlueBirdPromise.resolve(fsPromises.mkdir(dirname(path), { recursive: true })) + +function writeFile (path: string, data: any, options?: WriteFileOptions, callback?: (buffer: Buffer) => void) { + if (!path) throw new TypeError('path is required!') + + if (!data) data = '' + + if (!callback && typeof options === 'function') { + callback = options + options = {} + } + + return checkParent(path).then(() => fsPromises.writeFile(path, data, options)).asCallback(callback) +} + +function writeFileSync (path: string, data: any, options?: WriteFileOptions) { + if (!path) throw new TypeError('path is required!') + + fs.mkdirSync(dirname(path), { recursive: true }) + fs.writeFileSync(path, data, options) +} + +function appendFile (path: string, data: any, options?: WriteFileOptions, callback?: NoParamCallback) { + if (!path) throw new TypeError('path is required!') + + if (!callback && typeof options === 'function') { + callback = options + options = {} + } + + return checkParent(path).then(() => fsPromises.appendFile(path, data, options)).asCallback(callback) +} + +function appendFileSync (path: string, data: any, options?: WriteFileOptions) { + if (!path) throw new TypeError('path is required!') + + fs.mkdirSync(dirname(path), { recursive: true }) + fs.appendFileSync(path, data, options) +} + +function copyFile (src: string, dest: string, flags?: number, callback?: NoParamCallback) { + if (!src) throw new TypeError('src is required!') + if (!dest) throw new TypeError('dest is required!') + if (typeof flags === 'function') { + callback = flags + flags = undefined + } + + return checkParent(dest).then(() => fsPromises.copyFile(src, dest, flags)).asCallback(callback) +} + +const trueFn = () => true + +function ignoreHiddenFiles (ignore?: boolean) { + if (!ignore) return trueFn + + return ({ name }) => !name.startsWith('.') +} + +function ignoreFilesRegex (regex?: RegExp) { + if (!regex) return trueFn + + return ({ name }) => !regex.test(name) +} + +function ignoreExcludeFiles (arr: any[], parent: string) { + if (!arr || !arr.length) return trueFn + + const set = new Set(arr) + + return ({ name }) => !set.has(join(parent, name)) +} + +type ReadDirOptions = { + encoding?: BufferEncoding | null + withFileTypes?: false + ignoreHidden?: boolean + ignorePattern?: RegExp +} + +async function _readAndFilterDir (path: string, options: ReadDirOptions = {}): Promise { + const { ignoreHidden = true, ignorePattern } = options + return (await fsPromises.readdir(path, { ...options, withFileTypes: true })) + .filter(ignoreHiddenFiles(ignoreHidden)) + .filter(ignoreFilesRegex(ignorePattern)) +} + +function _readAndFilterDirSync (path: string, options?: ReadDirOptions) { + const { ignoreHidden = true, ignorePattern } = options + return fs.readdirSync(path, { ...options, withFileTypes: true }) + .filter(ignoreHiddenFiles(ignoreHidden)) + .filter(ignoreFilesRegex(ignorePattern)) +} + +async function _copyDirWalker (src: string, dest: string, results: any[], parent: string, options: ReadDirOptions) { + return BlueBirdPromise.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: string, dest: string, options: ReadDirOptions = {}, callback: (res: any) => void) { + if (!src) throw new TypeError('src is required!') + if (!dest) throw new TypeError('dest is required!') + + if (!callback && typeof options === 'function') { + callback = options + options = {} + } + const results = [] + + return checkParent(dest).then(() => _copyDirWalker(src, dest, results, '', options)).return(results).asCallback(callback) +} + +async function _listDirWalker (path: string, results: any[], parent?: string, options?: ReadDirOptions) { + 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: string, options: ReadDirOptions = {}, callback: (res: any) => void) { + if (!path) throw new TypeError('path is required!') + + if (!callback && typeof options === 'function') { + callback = options + options = {} + } + const results = [] + + return BlueBirdPromise.resolve(_listDirWalker(path, results, '', options)).return(results).asCallback(callback) +} + +function _listDirSyncWalker (path: string, results: any[], parent: string, options: ReadDirOptions) { + 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: string, options: ReadDirOptions = {}) { + if (!path) throw new TypeError('path is required!') + const results = [] + + _listDirSyncWalker(path, results, '', options) + + return results +} + +function escapeEOL (str: string) { + return str.replace(rEOL, '\n') +} + +function escapeBOM (str: string) { + return str.charCodeAt(0) === 0xFEFF ? str.substring(1) : str +} + +function escapeFileContent (content) { + return escapeBOM(escapeEOL(content)) +} + +type ReadFileOptions = { encoding?: string | null; flag?: string; escape?: string } + +async function _readFile (path: string, options: ReadFileOptions | null = {}) { + 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: string, options?: ReadFileOptions | null, callback?: (value: any) => void) { + if (!path) throw new TypeError('path is required!') + + if (!callback && typeof options === 'function') { + callback = options + options = {} + } + + return BlueBirdPromise.resolve(_readFile(path, options)).asCallback(callback) +} + +function readFileSync (path: string, options: ReadFileOptions | null = {}) { + 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: string, parent?: string, options?: ReadDirOptions & { exclude?: any[] }) { + const entrys = (await _readAndFilterDir(path, options)) + .filter(ignoreExcludeFiles(options.exclude, parent)) + const results = [] + + await BlueBirdPromise.map(entrys, (item: Dirent) => { + 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: string, options: ReadDirOptions & { exclude?: any[] } = {}, callback?: (value: any) => void) { + if (!path) throw new TypeError('path is required!') + + if (!callback && typeof options === 'function') { + callback = options + options = {} + } + + return BlueBirdPromise.resolve(_emptyDir(path, '', options)).asCallback(callback) +} + +function _emptyDirSync (path: string, options: ReadDirOptions & { exclude?: any[] }, parent?: string) { + 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: string, options: ReadDirOptions & { exclude?: any[] } = {}) { + if (!path) throw new TypeError('path is required!') + + return _emptyDirSync(path, options, '') +} + +async function _rmdir (path: string) { + const files = fsPromises.readdir(path, { withFileTypes: true }) + await BlueBirdPromise.map(files, (item: Dirent) => { + const childPath = join(path, item.name) + + return item.isDirectory() ? _rmdir(childPath) : fsPromises.unlink(childPath) + }) + return fsPromises.rmdir(path) +} + +function rmdir (path: string, callback: (value: any) => void) { + if (!path) throw new TypeError('path is required!') + + return BlueBirdPromise.resolve(_rmdir(path)).asCallback(callback) +} + +function _rmdirSync (path: string) { + const files = fs.readdirSync(path, { withFileTypes: true }) + + for (let i = 0, len = files.length; i < len; i++) { + const item = files[i] + const childPath = join(path, item.name) + + if (item.isDirectory()) { + _rmdirSync(childPath) + } else { + fs.unlinkSync(childPath) + } + } + + fs.rmdirSync(path) +} + +function rmdirSync (path: string) { + if (!path) throw new TypeError('path is required!') + + _rmdirSync(path) +} + +function watch (path: string | ReadonlyArray, options?: WatchOptions, callback?: (watcher: FSWatcher) => void) { + if (!path) throw new TypeError('path is required!') + + if (!callback && typeof options === 'function') { + callback = options + options = {} + } + + const watcher = chokidar.watch(path, options) + + return new BlueBirdPromise((resolve, reject) => { + watcher.on('ready', resolve) + watcher.on('error', reject) + }).thenReturn(watcher).asCallback(callback) +} + +function _findUnusedPath (path: string, files: string[]): string { + 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: string): Promise { + if (!await exists(path)) return path + + const files = await fsPromises.readdir(dirname(path)) + return _findUnusedPath(path, files) +} + +function ensurePath (path: string, callback: (res: string) => void) { + if (!path) throw new TypeError('path is required!') + + return BlueBirdPromise.resolve(_ensurePath(path)).asCallback(callback) +} + +function ensurePathSync (path: string) { + 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: string, options?: string | { + flags?: string; + encoding?: string; + fd?: number; + mode?: number; + autoClose?: boolean; + emitClose?: boolean; + start?: number; + highWaterMark?: number; +}, callback?: (stream: WriteStream) => void) { + if (!path) throw new TypeError('path is required!') + + if (!callback && typeof options === 'function') { + callback = options + options = {} + } + + return checkParent(path).then(() => fs.createWriteStream(path, options)).asCallback(callback) +} + +function ensureWriteStreamSync (path: string, options?: string | { + flags?: string; + encoding?: string; + fd?: number; + mode?: number; + autoClose?: boolean; + emitClose?: boolean; + start?: number; + highWaterMark?: number; +}) { + 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 = BlueBirdPromise.promisify(fs.access) +exports.accessSync = fs.accessSync + +// appendFile +exports.appendFile = appendFile +exports.appendFileSync = appendFileSync + +// chmod +exports.chmod = BlueBirdPromise.promisify(fs.chmod) +exports.chmodSync = fs.chmodSync +exports.fchmod = BlueBirdPromise.promisify(fs.fchmod) +exports.fchmodSync = fs.fchmodSync +exports.lchmod = BlueBirdPromise.promisify(fs.lchmod) +exports.lchmodSync = fs.lchmodSync + +// chown +exports.chown = BlueBirdPromise.promisify(fs.chown) +exports.chownSync = fs.chownSync +exports.fchown = BlueBirdPromise.promisify(fs.fchown) +exports.fchownSync = fs.fchownSync +exports.lchown = BlueBirdPromise.promisify(fs.lchown) +exports.lchownSync = fs.lchownSync + +// close +exports.close = BlueBirdPromise.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 = BlueBirdPromise.promisify(fs.fsync) +exports.fsyncSync = fs.fsyncSync + +// link +exports.link = BlueBirdPromise.promisify(fs.link) +exports.linkSync = fs.linkSync + +// listDir +exports.listDir = listDir +exports.listDirSync = listDirSync + +// mkdir +exports.mkdir = BlueBirdPromise.promisify(fs.mkdir) +exports.mkdirSync = fs.mkdirSync + +// mkdirs +exports.mkdirs = mkdirs +exports.mkdirsSync = mkdirsSync + +// open +exports.open = BlueBirdPromise.promisify(fs.open) +exports.openSync = fs.openSync + +// symlink +exports.symlink = BlueBirdPromise.promisify(fs.symlink) +exports.symlinkSync = fs.symlinkSync + +// read +exports.read = BlueBirdPromise.promisify(fs.read) +exports.readSync = fs.readSync + +// readdir +exports.readdir = BlueBirdPromise.promisify(fs.readdir) +exports.readdirSync = fs.readdirSync + +// readFile +exports.readFile = readFile +exports.readFileSync = readFileSync + +// readlink +exports.readlink = BlueBirdPromise.promisify(fs.readlink) +exports.readlinkSync = fs.readlinkSync + +// realpath +exports.realpath = BlueBirdPromise.promisify(fs.realpath) +exports.realpathSync = fs.realpathSync + +// rename +exports.rename = BlueBirdPromise.promisify(fs.rename) +exports.renameSync = fs.renameSync + +// rmdir +exports.rmdir = rmdir +exports.rmdirSync = rmdirSync + +// stat +exports.stat = BlueBirdPromise.promisify(fs.stat) +exports.statSync = fs.statSync +exports.fstat = BlueBirdPromise.promisify(fs.fstat) +exports.fstatSync = fs.fstatSync +exports.lstat = BlueBirdPromise.promisify(fs.lstat) +exports.lstatSync = fs.lstatSync + +// truncate +exports.truncate = BlueBirdPromise.promisify(fs.truncate) +exports.truncateSync = fs.truncateSync +exports.ftruncate = BlueBirdPromise.promisify(fs.ftruncate) +exports.ftruncateSync = fs.ftruncateSync + +// unlink +exports.unlink = BlueBirdPromise.promisify(fs.unlink) +exports.unlinkSync = fs.unlinkSync + +// utimes +exports.utimes = BlueBirdPromise.promisify(fs.utimes) +exports.utimesSync = fs.utimesSync +exports.futimes = BlueBirdPromise.promisify(fs.futimes) +exports.futimesSync = fs.futimesSync + +// watch +exports.watch = watch +exports.watchFile = fs.watchFile +exports.unwatchFile = fs.unwatchFile + +// write +exports.write = BlueBirdPromise.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 diff --git a/package.json b/package.json index 93e12dd..587677a 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,11 @@ "name": "hexo-fs", "version": "3.0.0", "description": "File system module for Hexo.", - "main": "lib/fs.js", + "main": "dist/fs.js", "scripts": { + "build": "tsc -p tsconfig.json", "eslint": "eslint .", - "test": "mocha test/index.js", + "test": "mocha -r ts-node/register test/index.js", "test-cov": "nyc npm run test" }, "directories": { @@ -34,13 +35,24 @@ "hexo-util": "^2.0.0" }, "devDependencies": { + "@types/bluebird": "^3.5.30", + "@types/node": "^13.13.4", + "@typescript-eslint/eslint-plugin": "^2.29.0", + "@typescript-eslint/parser": "^2.29.0", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", - "eslint": "^6.0.1", + "eslint": "^6.8.0", "eslint-config-hexo": "^4.1.0", + "eslint-config-standard": "^14.1.1", + "eslint-plugin-import": "^2.20.2", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-standard": "^4.0.1", "iferr": "^1.0.2", "mocha": "^7.0.0", - "nyc": "^15.0.0" + "nyc": "^15.0.0", + "ts-node": "^8.9.1", + "typescript": "^3.8.3" }, "engines": { "node": ">=10.13.0" diff --git a/test/index.js b/test/index.js index 555b5a1..9e918db 100644 --- a/test/index.js +++ b/test/index.js @@ -1,12 +1,12 @@ -'use strict'; +'use strict' -require('chai').use(require('chai-as-promised')).should(); -const { should } = require('chai'); +require('chai').use(require('chai-as-promised')).should() +const { should } = require('chai') -const { join, dirname } = require('path'); -const Promise = require('bluebird'); -const fs = require('../lib/fs'); -const { tiferr } = require('iferr'); +const { join, dirname } = require('path') +const Promise = require('bluebird') +const fs = require('../lib/fs.ts') +const { tiferr } = require('iferr') function createDummyFolder(path, callback) { const filesMap = { @@ -25,333 +25,333 @@ function createDummyFolder(path, callback) { [join('folder', 'i.js')]: 'i', // A hidden files in a normal folder [join('folder', '.j')]: 'j' - }; - return Promise.map(Object.keys(filesMap), key => fs.writeFile(join(path, key), filesMap[key])).asCallback(callback); + } + return Promise.map(Object.keys(filesMap), key => fs.writeFile(join(path, key), filesMap[key])).asCallback(callback) } describe('fs', () => { - const tmpDir = join(__dirname, 'fs_tmp'); + const tmpDir = join(__dirname, 'fs_tmp') - before(() => fs.mkdirs(tmpDir)); + before(() => fs.mkdirs(tmpDir)) - after(() => fs.rmdir(tmpDir)); + after(() => fs.rmdir(tmpDir)) it('exists()', async () => { - const exist = await fs.exists(tmpDir); - exist.should.eql(true); - }); + const exist = await fs.exists(tmpDir) + exist.should.eql(true) + }) it('exists() - callback', callback => { fs.exists(tmpDir, exist => { try { - exist.should.be.true; + exist.should.be.true } catch (e) { - callback(e); - return; + callback(e) + return } - callback(); - }); - }); + callback() + }) + }) it('exists() - path is required', async () => { try { - await fs.exists(); - should.fail(); + await fs.exists() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('mkdirs()', async () => { - const target = join(tmpDir, 'a', 'b', 'c'); + const target = join(tmpDir, 'a', 'b', 'c') - await fs.mkdirs(target); - const exist = await fs.exists(target); - exist.should.eql(true); + await fs.mkdirs(target) + const exist = await fs.exists(target) + exist.should.eql(true) - await fs.rmdir(join(tmpDir, 'a')); - }); + await fs.rmdir(join(tmpDir, 'a')) + }) it('mkdirs() - callback', callback => { - const target = join(tmpDir, 'a', 'b', 'c'); + const target = join(tmpDir, 'a', 'b', 'c') fs.mkdirs(target, tiferr(callback, () => { fs.exists(target, exist => { - exist.should.be.true; - fs.rmdir(join(tmpDir, 'a'), callback); - }); - })); - }); + exist.should.be.true + fs.rmdir(join(tmpDir, 'a'), callback) + }) + })) + }) it('mkdirs() - path is required', async () => { try { - await fs.mkdirs(); - should.fail(); + await fs.mkdirs() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('mkdirsSync()', async () => { - const target = join(tmpDir, 'a', 'b', 'c'); + const target = join(tmpDir, 'a', 'b', 'c') - fs.mkdirsSync(target); + fs.mkdirsSync(target) - const exist = await fs.exists(target); - exist.should.eql(true); + const exist = await fs.exists(target) + exist.should.eql(true) - await fs.rmdir(join(tmpDir, 'a')); - }); + await fs.rmdir(join(tmpDir, 'a')) + }) it('mkdirsSync() - path is required', () => { try { - fs.mkdirsSync(); - should.fail(); + fs.mkdirsSync() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('writeFile()', async () => { - const target = join(tmpDir, 'a', 'b', 'test.txt'); - const body = 'foo'; + const target = join(tmpDir, 'a', 'b', 'test.txt') + const body = 'foo' - await fs.writeFile(target, body); - const result = await fs.readFile(target); + await fs.writeFile(target, body) + const result = await fs.readFile(target) - result.should.eql(body); + result.should.eql(body) - await fs.rmdir(join(tmpDir, 'a')); - }); + await fs.rmdir(join(tmpDir, 'a')) + }) it('writeFile() - callback', callback => { - const target = join(tmpDir, 'a', 'b', 'test.txt'); - const body = 'foo'; + const target = join(tmpDir, 'a', 'b', 'test.txt') + const body = 'foo' fs.writeFile(target, body, tiferr(callback, () => { fs.readFile(target, tiferr(callback, content => { - content.should.eql(body); - fs.rmdir(join(tmpDir, 'a'), callback); - })); - })); - }); + content.should.eql(body) + fs.rmdir(join(tmpDir, 'a'), callback) + })) + })) + }) it('writeFile() - path is required', async () => { try { - await fs.writeFile(); - should.fail(); + await fs.writeFile() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('writeFileSync()', async () => { - const target = join(tmpDir, 'a', 'b', 'test.txt'); - const body = 'foo'; + const target = join(tmpDir, 'a', 'b', 'test.txt') + const body = 'foo' - fs.writeFileSync(target, body); + fs.writeFileSync(target, body) - const result = await fs.readFile(target); - result.should.eql(body); + const result = await fs.readFile(target) + result.should.eql(body) - await fs.rmdir(join(tmpDir, 'a')); - }); + await fs.rmdir(join(tmpDir, 'a')) + }) it('writeFileSync() - path is required', () => { try { - fs.writeFileSync(); - should.fail(); + fs.writeFileSync() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('appendFile()', async () => { - const target = join(tmpDir, 'a', 'b', 'test.txt'); - const body = 'foo'; - const body2 = 'bar'; + const target = join(tmpDir, 'a', 'b', 'test.txt') + const body = 'foo' + const body2 = 'bar' - await fs.writeFile(target, body); - await fs.appendFile(target, body2); + await fs.writeFile(target, body) + await fs.appendFile(target, body2) - const result = await fs.readFile(target); + const result = await fs.readFile(target) - result.should.eql(body + body2); + result.should.eql(body + body2) - await fs.rmdir(join(tmpDir, 'a')); - }); + await fs.rmdir(join(tmpDir, 'a')) + }) it('appendFile() - callback', callback => { - const target = join(tmpDir, 'a', 'b', 'test.txt'); - const body = 'foo'; - const body2 = 'bar'; + const target = join(tmpDir, 'a', 'b', 'test.txt') + const body = 'foo' + const body2 = 'bar' fs.writeFile(target, body, tiferr(callback, () => { fs.appendFile(target, body2, tiferr(callback, () => { fs.readFile(target, tiferr(callback, content => { - content.should.eql(body + body2); - fs.rmdir(join(tmpDir, 'a'), callback); - })); - })); - })); - }); + content.should.eql(body + body2) + fs.rmdir(join(tmpDir, 'a'), callback) + })) + })) + })) + }) it('appendFile() - path is required', async () => { try { - await fs.appendFile(); - should.fail(); + await fs.appendFile() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('appendFileSync()', async () => { - const target = join(tmpDir, 'a', 'b', 'test.txt'); - const body = 'foo'; - const body2 = 'bar'; + const target = join(tmpDir, 'a', 'b', 'test.txt') + const body = 'foo' + const body2 = 'bar' - await fs.writeFile(target, body); - fs.appendFileSync(target, body2); + await fs.writeFile(target, body) + fs.appendFileSync(target, body2) - const result = await fs.readFile(target); - result.should.eql(body + body2); + const result = await fs.readFile(target) + result.should.eql(body + body2) - await fs.rmdir(join(tmpDir, 'a')); - }); + await fs.rmdir(join(tmpDir, 'a')) + }) it('appendFileSync() - path is required', () => { try { - fs.appendFileSync(); - should.fail(); + fs.appendFileSync() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('copyFile()', async () => { - const src = join(tmpDir, 'test.txt'); - const dest = join(tmpDir, 'a', 'b', 'test.txt'); - const body = 'foo'; + const src = join(tmpDir, 'test.txt') + const dest = join(tmpDir, 'a', 'b', 'test.txt') + const body = 'foo' - await fs.writeFile(src, body); - await fs.copyFile(src, dest); + await fs.writeFile(src, body) + await fs.copyFile(src, dest) - const result = await fs.readFile(dest); - result.should.eql(body); + const result = await fs.readFile(dest) + result.should.eql(body) await Promise.all([ fs.unlink(src), fs.rmdir(join(tmpDir, 'a')) - ]); - }); + ]) + }) it('copyFile() - callback', callback => { - const src = join(tmpDir, 'test.txt'); - const dest = join(tmpDir, 'a', 'b', 'test.txt'); - const body = 'foo'; + const src = join(tmpDir, 'test.txt') + const dest = join(tmpDir, 'a', 'b', 'test.txt') + const body = 'foo' fs.writeFile(src, body, tiferr(callback, () => { fs.copyFile(src, dest, tiferr(callback, () => { fs.readFile(dest, tiferr(callback, content => { - content.should.eql(body); + content.should.eql(body) Promise.all([ fs.unlink(src), fs.rmdir(join(tmpDir, 'a')) - ]).asCallback(callback); - })); - })); - })); - }); + ]).asCallback(callback) + })) + })) + })) + }) it('copyFile() - src is required', async () => { try { - await fs.copyFile(); - should.fail(); + await fs.copyFile() + should.fail() } catch (err) { - err.message.should.eql('src is required!'); + err.message.should.eql('src is required!') } - }); + }) it('copyFile() - dest is required', async () => { try { - await fs.copyFile('123'); - should.fail(); + await fs.copyFile('123') + should.fail() } catch (err) { - err.message.should.eql('dest is required!'); + err.message.should.eql('dest is required!') } - }); + }) it('copyDir()', async () => { - const src = join(tmpDir, 'a'); - const dest = join(tmpDir, 'b'); + const src = join(tmpDir, 'a') + const dest = join(tmpDir, 'b') const filenames = [ 'e.txt', 'f.js', join('folder', 'h.txt'), join('folder', 'i.js') - ]; + ] - await createDummyFolder(src); - const files = await fs.copyDir(src, dest); - files.should.eql(filenames); + await createDummyFolder(src) + const files = await fs.copyDir(src, dest) + files.should.eql(filenames) - const result = []; + const result = [] for (const file of files) { - const output = await fs.readFile(join(dest, file)); - result.push(output); + const output = await fs.readFile(join(dest, file)) + result.push(output) } - result.should.eql(['e', 'f', 'h', 'i']); + result.should.eql(['e', 'f', 'h', 'i']) - await Promise.all([fs.rmdir(src), fs.rmdir(dest)]); - }); + await Promise.all([fs.rmdir(src), fs.rmdir(dest)]) + }) it('copyDir() - callback', callback => { - const src = join(tmpDir, 'a'); - const dest = join(tmpDir, 'b'); + const src = join(tmpDir, 'a') + const dest = join(tmpDir, 'b') const finenames = [ 'e.txt', 'f.js', join('folder', 'h.txt'), join('folder', 'i.js') - ]; + ] createDummyFolder(src, tiferr(callback, () => { fs.copyDir(src, dest, tiferr(callback, files => { - files.should.have.members(finenames); + files.should.have.members(finenames) fs.rmdir(src, tiferr(callback, () => { Promise.map(finenames, path => fs.readFile(join(dest, path))).asCallback(tiferr(callback, result => { - result.should.eql(['e', 'f', 'h', 'i']); - fs.rmdir(dest, callback); - })); - })); - })); - })); - }); + result.should.eql(['e', 'f', 'h', 'i']) + fs.rmdir(dest, callback) + })) + })) + })) + })) + }) it('copyDir() - src is required', async () => { try { - await fs.copyDir(); - should.fail(); + await fs.copyDir() + should.fail() } catch (err) { - err.message.should.eql('src is required!'); + err.message.should.eql('src is required!') } - }); + }) it('copyDir() - dest is required', async () => { try { - await fs.copyDir('123'); - should.fail(); + await fs.copyDir('123') + should.fail() } catch (err) { - err.message.should.eql('dest is required!'); + err.message.should.eql('dest is required!') } - }); + }) it('copyDir() - ignoreHidden off', async () => { - const src = join(tmpDir, 'a'); - const dest = join(tmpDir, 'b'); + const src = join(tmpDir, 'a') + const dest = join(tmpDir, 'b') const filenames = [ join('.hidden', 'a.txt'), @@ -363,41 +363,41 @@ describe('fs', () => { join('folder', 'h.txt'), join('folder', 'i.js'), join('folder', '.j') - ]; + ] - await createDummyFolder(src); - const files = await fs.copyDir(src, dest, { ignoreHidden: false }); - files.should.have.members(filenames); + await createDummyFolder(src) + const files = await fs.copyDir(src, dest, { ignoreHidden: false }) + files.should.have.members(filenames) - const result = []; + const result = [] for (const file of files) { - const output = await fs.readFile(join(dest, file)); - result.push(output); + const output = await fs.readFile(join(dest, file)) + result.push(output) } - result.should.have.members(['a', 'b', 'd', 'e', 'f', 'g', 'h', 'i', 'j']); + result.should.have.members(['a', 'b', 'd', 'e', 'f', 'g', 'h', 'i', 'j']) - await Promise.all([fs.rmdir(src), fs.rmdir(dest)]); - }); + await Promise.all([fs.rmdir(src), fs.rmdir(dest)]) + }) it('copyDir() - ignorePattern', async () => { - const src = join(tmpDir, 'a'); - const dest = join(tmpDir, 'b'); + const src = join(tmpDir, 'a') + const dest = join(tmpDir, 'b') - const filenames = ['e.txt', join('folder', 'h.txt')]; + const filenames = ['e.txt', join('folder', 'h.txt')] - await createDummyFolder(src); - const files = await fs.copyDir(src, dest, { ignorePattern: /\.js/ }); - files.should.eql(filenames); + await createDummyFolder(src) + const files = await fs.copyDir(src, dest, { ignorePattern: /\.js/ }) + files.should.eql(filenames) - const result = []; + const result = [] for (const file of files) { - const output = await fs.readFile(join(dest, file)); - result.push(output); + const output = await fs.readFile(join(dest, file)) + result.push(output) } - result.should.eql(['e', 'h']); + result.should.eql(['e', 'h']) - await Promise.all([fs.rmdir(src), fs.rmdir(dest)]); - }); + await Promise.all([fs.rmdir(src), fs.rmdir(dest)]) + }) it('listDir()', async () => { const expected = [ @@ -405,45 +405,45 @@ describe('fs', () => { 'f.js', join('folder', 'h.txt'), join('folder', 'i.js') - ]; - const target = join(tmpDir, 'test'); + ] + const target = join(tmpDir, 'test') - await createDummyFolder(target); - const dir = await fs.listDir(target); - dir.should.eql(expected); + await createDummyFolder(target) + const dir = await fs.listDir(target) + dir.should.eql(expected) - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('listDir() - callback', callback => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') const filenames = [ 'e.txt', 'f.js', join('folder', 'h.txt'), join('folder', 'i.js') - ]; + ] createDummyFolder(target, tiferr(callback, () => { fs.listDir(target, tiferr(callback, paths => { - paths.should.have.members(filenames); - fs.rmdir(target, callback); - })); - })); - }); + paths.should.have.members(filenames) + fs.rmdir(target, callback) + })) + })) + }) it('listDir() - path is required', async () => { try { - await fs.listDir(); - should.fail(); + await fs.listDir() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('listDir() - ignoreHidden off', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') const filenames = [ join('.hidden', 'a.txt'), @@ -455,53 +455,53 @@ describe('fs', () => { join('folder', 'h.txt'), join('folder', 'i.js'), join('folder', '.j') - ]; + ] - await createDummyFolder(target); - const dir = await fs.listDir(target, { ignoreHidden: false }); - dir.should.have.members(filenames); + await createDummyFolder(target) + const dir = await fs.listDir(target, { ignoreHidden: false }) + dir.should.have.members(filenames) - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('listDir() - ignorePattern', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') - await createDummyFolder(target); - const dir = await fs.listDir(target, { ignorePattern: /\.js/ }); - dir.should.eql(['e.txt', join('folder', 'h.txt')]); + await createDummyFolder(target) + const dir = await fs.listDir(target, { ignorePattern: /\.js/ }) + dir.should.eql(['e.txt', join('folder', 'h.txt')]) - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('listDirSync()', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') const filenames = [ 'e.txt', 'f.js', join('folder', 'h.txt'), join('folder', 'i.js') - ]; + ] - await createDummyFolder(target); - const files = fs.listDirSync(target); - files.should.eql(filenames); + await createDummyFolder(target) + const files = fs.listDirSync(target) + files.should.eql(filenames) - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('listDirSync() - path is required', () => { try { - fs.listDirSync(); - should.fail(); + fs.listDirSync() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('listDirSync() - ignoreHidden off', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') const filenames = [ join('.hidden', 'a.txt'), @@ -513,136 +513,136 @@ describe('fs', () => { join('folder', 'h.txt'), join('folder', 'i.js'), join('folder', '.j') - ]; + ] - await createDummyFolder(target); - const files = fs.listDirSync(target, { ignoreHidden: false }); - files.should.have.members(filenames); + await createDummyFolder(target) + const files = fs.listDirSync(target, { ignoreHidden: false }) + files.should.have.members(filenames) - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('listDirSync() - ignorePattern', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') - await createDummyFolder(target); - const files = fs.listDirSync(target, {ignorePattern: /\.js/}); - files.should.eql(['e.txt', join('folder', 'h.txt')]); + await createDummyFolder(target) + const files = fs.listDirSync(target, { ignorePattern: /\.js/ }) + files.should.eql(['e.txt', join('folder', 'h.txt')]) - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('readFile()', async () => { - const target = join(tmpDir, 'test.txt'); - const body = 'test'; + const target = join(tmpDir, 'test.txt') + const body = 'test' - await fs.writeFile(target, body); - const result = await fs.readFile(target); - result.should.eql(body); + await fs.writeFile(target, body) + const result = await fs.readFile(target) + result.should.eql(body) - await fs.unlink(target); - }); + await fs.unlink(target) + }) it('readFile() - callback', callback => { - const target = join(tmpDir, 'test.txt'); - const body = 'test'; + const target = join(tmpDir, 'test.txt') + const body = 'test' fs.writeFile(target, body, tiferr(callback, () => { fs.readFile(target, tiferr(callback, content => { - content.should.eql(body); - fs.unlink(target, callback); - })); - })); - }); + content.should.eql(body) + fs.unlink(target, callback) + })) + })) + }) it('readFile() - path is required', async () => { try { - await fs.readFile(); - should.fail(); + await fs.readFile() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('readFile() - escape BOM', async () => { - const target = join(tmpDir, 'test.txt'); - const body = '\ufefffoo'; + const target = join(tmpDir, 'test.txt') + const body = '\ufefffoo' - await fs.writeFile(target, body); - const result = await fs.readFile(target); + await fs.writeFile(target, body) + const result = await fs.readFile(target) - result.should.eql('foo'); + result.should.eql('foo') - await fs.unlink(target); - }); + await fs.unlink(target) + }) it('readFile() - escape Windows line ending', async () => { - const target = join(tmpDir, 'test.txt'); - const body = 'foo\r\nbar'; + const target = join(tmpDir, 'test.txt') + const body = 'foo\r\nbar' - await fs.writeFile(target, body); - const result = await fs.readFile(target); - result.should.eql('foo\nbar'); + await fs.writeFile(target, body) + const result = await fs.readFile(target) + result.should.eql('foo\nbar') - await fs.unlink(target); - }); + await fs.unlink(target) + }) it('readFileSync()', async () => { - const target = join(tmpDir, 'test.txt'); - const body = 'test'; + const target = join(tmpDir, 'test.txt') + const body = 'test' - await fs.writeFile(target, body); - const result = fs.readFileSync(target); - result.should.eql(body); + await fs.writeFile(target, body) + const result = fs.readFileSync(target) + result.should.eql(body) - await fs.unlink(target); - }); + await fs.unlink(target) + }) it('readFileSync() - path is required', () => { try { - fs.readFileSync(); - should.fail(); + fs.readFileSync() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('readFileSync() - escape BOM', async () => { - const target = join(tmpDir, 'test.txt'); - const body = '\ufefffoo'; + const target = join(tmpDir, 'test.txt') + const body = '\ufefffoo' - await fs.writeFile(target, body); - const result = fs.readFileSync(target); - result.should.eql('foo'); + await fs.writeFile(target, body) + const result = fs.readFileSync(target) + result.should.eql('foo') - await fs.unlink(target); - }); + await fs.unlink(target) + }) it('readFileSync() - escape Windows line ending', async () => { - const target = join(tmpDir, 'test.txt'); - const body = 'foo\r\nbar'; + const target = join(tmpDir, 'test.txt') + const body = 'foo\r\nbar' - await fs.writeFile(target, body); - const result = fs.readFileSync(target); - result.should.eql('foo\nbar'); + await fs.writeFile(target, body) + const result = fs.readFileSync(target) + result.should.eql('foo\nbar') - await fs.unlink(target); - }); + await fs.unlink(target) + }) it('unlink()', async () => { - const target = join(tmpDir, 'test-unlink'); + const target = join(tmpDir, 'test-unlink') - await fs.writeFile(target, ''); - let exist = await fs.exists(target); - exist.should.eql(true); + await fs.writeFile(target, '') + let exist = await fs.exists(target) + exist.should.eql(true) - await fs.unlink(target); - exist = await fs.exists(target); - exist.should.eql(false); - }); + await fs.unlink(target) + exist = await fs.exists(target) + exist.should.eql(false) + }) it('emptyDir()', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') const checkExistsMap = { [join('.hidden', 'a.txt')]: true, @@ -654,28 +654,28 @@ describe('fs', () => { [join('folder', 'h.txt')]: false, [join('folder', 'i.js')]: false, [join('folder', '.j')]: true - }; + } - await createDummyFolder(target); - const files = await fs.emptyDir(target); + await createDummyFolder(target) + const files = await fs.emptyDir(target) files.should.eql([ 'e.txt', 'f.js', join('folder', 'h.txt'), join('folder', 'i.js') - ]); + ]) - const paths = Object.keys(checkExistsMap); + const paths = Object.keys(checkExistsMap) for (const path of paths) { - const exist = await fs.exists(join(target, path)); - exist.should.eql(checkExistsMap[path]); + const exist = await fs.exists(join(target, path)) + exist.should.eql(checkExistsMap[path]) } - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('emptyDir() - callback', callback => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') const checkExistsMap = { [join('.hidden', 'a.txt')]: true, @@ -687,7 +687,7 @@ describe('fs', () => { [join('folder', 'h.txt')]: false, [join('folder', 'i.js')]: false, [join('folder', '.j')]: true - }; + } createDummyFolder(target, tiferr(callback, () => { fs.emptyDir(target, tiferr(callback, files => { @@ -696,28 +696,28 @@ describe('fs', () => { 'f.js', join('folder', 'h.txt'), join('folder', 'i.js') - ]); + ]) return Promise.map(Object.keys(checkExistsMap), path => { - return fs.exists(join(target, path)).should.become(checkExistsMap[path]); + return fs.exists(join(target, path)).should.become(checkExistsMap[path]) }).asCallback(tiferr(callback, () => { - fs.rmdir(target, callback); - })); - })); - })); - }); + fs.rmdir(target, callback) + })) + })) + })) + }) it('emptyDir() - path is required', async () => { try { - await fs.emptyDir(); - should.fail(); + await fs.emptyDir() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('emptyDir() - ignoreHidden off', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') const filenames = [ join('.hidden', 'a.txt'), @@ -729,22 +729,22 @@ describe('fs', () => { join('folder', 'h.txt'), join('folder', 'i.js'), join('folder', '.j') - ]; + ] - await createDummyFolder(target); - const files = await fs.emptyDir(target, { ignoreHidden: false }); - files.should.have.members(filenames); + await createDummyFolder(target) + const files = await fs.emptyDir(target, { ignoreHidden: false }) + files.should.have.members(filenames) for (const file of files) { - const exist = await fs.exists(join(target, file)); - exist.should.eql(false); + const exist = await fs.exists(join(target, file)) + exist.should.eql(false) } - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('emptyDir() - ignorePattern', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') const checkExistsMap = { [join('.hidden', 'a.txt')]: true, @@ -756,23 +756,23 @@ describe('fs', () => { [join('folder', 'h.txt')]: false, [join('folder', 'i.js')]: true, [join('folder', '.j')]: true - }; + } - await createDummyFolder(target); - const files = await fs.emptyDir(target, { ignorePattern: /\.js/ }); - files.should.eql(['e.txt', join('folder', 'h.txt')]); + await createDummyFolder(target) + const files = await fs.emptyDir(target, { ignorePattern: /\.js/ }) + files.should.eql(['e.txt', join('folder', 'h.txt')]) - const paths = Object.keys(checkExistsMap); + const paths = Object.keys(checkExistsMap) for (const path of paths) { - const exist = await fs.exists(join(target, path)); - exist.should.eql(checkExistsMap[path]); + const exist = await fs.exists(join(target, path)) + exist.should.eql(checkExistsMap[path]) } - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('emptyDir() - exclude', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') const checkExistsMap = { [join('.hidden', 'a.txt')]: true, @@ -784,23 +784,23 @@ describe('fs', () => { [join('folder', 'h.txt')]: false, [join('folder', 'i.js')]: true, [join('folder', '.j')]: true - }; + } - await createDummyFolder(target); - const files = await fs.emptyDir(target, { exclude: ['e.txt', join('folder', 'i.js')] }); - files.should.eql(['f.js', join('folder', 'h.txt')]); + await createDummyFolder(target) + const files = await fs.emptyDir(target, { exclude: ['e.txt', join('folder', 'i.js')] }) + files.should.eql(['f.js', join('folder', 'h.txt')]) - const paths = Object.keys(checkExistsMap); + const paths = Object.keys(checkExistsMap) for (const path of paths) { - const exist = await fs.exists(join(target, path)); - exist.should.eql(checkExistsMap[path]); + const exist = await fs.exists(join(target, path)) + exist.should.eql(checkExistsMap[path]) } - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('emptyDirSync()', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') const checkExistsMap = { [join('.hidden', 'a.txt')]: true, @@ -812,37 +812,37 @@ describe('fs', () => { [join('folder', 'h.txt')]: false, [join('folder', 'i.js')]: false, [join('folder', '.j')]: true - }; + } - await createDummyFolder(target); - const files = fs.emptyDirSync(target); + await createDummyFolder(target) + const files = fs.emptyDirSync(target) files.should.eql([ 'e.txt', 'f.js', join('folder', 'h.txt'), join('folder', 'i.js') - ]); + ]) - const paths = Object.keys(checkExistsMap); + const paths = Object.keys(checkExistsMap) for (const path of paths) { - const exist = await fs.exists(join(target, path)); - exist.should.eql(checkExistsMap[path]); + const exist = await fs.exists(join(target, path)) + exist.should.eql(checkExistsMap[path]) } - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('emptyDirSync() - path is required', () => { try { - fs.emptyDirSync(); - should.fail(); + fs.emptyDirSync() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('emptyDirSync() - ignoreHidden off', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') const filenames = [ join('.hidden', 'a.txt'), @@ -854,22 +854,22 @@ describe('fs', () => { join('folder', 'h.txt'), join('folder', 'i.js'), join('folder', '.j') - ]; + ] - await createDummyFolder(target); - const files = fs.emptyDirSync(target, { ignoreHidden: false }); - files.should.have.members(filenames); + await createDummyFolder(target) + const files = fs.emptyDirSync(target, { ignoreHidden: false }) + files.should.have.members(filenames) for (const file of files) { - const exist = await fs.exists(join(target, file)); - exist.should.eql(false); + const exist = await fs.exists(join(target, file)) + exist.should.eql(false) } - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('emptyDirSync() - ignorePattern', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') const checkExistsMap = { [join('.hidden', 'a.txt')]: true, @@ -881,23 +881,23 @@ describe('fs', () => { [join('folder', 'h.txt')]: false, [join('folder', 'i.js')]: true, [join('folder', '.j')]: true - }; + } - await createDummyFolder(target); - const files = fs.emptyDirSync(target, { ignorePattern: /\.js/ }); - files.should.eql(['e.txt', join('folder', 'h.txt')]); + await createDummyFolder(target) + const files = fs.emptyDirSync(target, { ignorePattern: /\.js/ }) + files.should.eql(['e.txt', join('folder', 'h.txt')]) - const paths = Object.keys(checkExistsMap); + const paths = Object.keys(checkExistsMap) for (const path of paths) { - const exist = await fs.exists(join(target, path)); - exist.should.eql(checkExistsMap[path]); + const exist = await fs.exists(join(target, path)) + exist.should.eql(checkExistsMap[path]) } - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('emptyDirSync() - exclude', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') const checkExistsMap = { [join('.hidden', 'a.txt')]: true, @@ -909,211 +909,211 @@ describe('fs', () => { [join('folder', 'h.txt')]: false, [join('folder', 'i.js')]: true, [join('folder', '.j')]: true - }; + } - await createDummyFolder(target); - const files = fs.emptyDirSync(target, { exclude: ['e.txt', join('folder', 'i.js')] }); - files.should.eql(['f.js', join('folder', 'h.txt')]); + await createDummyFolder(target) + const files = fs.emptyDirSync(target, { exclude: ['e.txt', join('folder', 'i.js')] }) + files.should.eql(['f.js', join('folder', 'h.txt')]) - const paths = Object.keys(checkExistsMap); + const paths = Object.keys(checkExistsMap) for (const path of paths) { - const exist = await fs.exists(join(target, path)); - exist.should.eql(checkExistsMap[path]); + const exist = await fs.exists(join(target, path)) + exist.should.eql(checkExistsMap[path]) } - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('rmdir()', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') - await createDummyFolder(target); - await fs.rmdir(target); - const exist = await fs.exists(target); - exist.should.eql(false); - }); + await createDummyFolder(target) + await fs.rmdir(target) + const exist = await fs.exists(target) + exist.should.eql(false) + }) it('rmdir() - callback', callback => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') createDummyFolder(target, tiferr(callback, () => { fs.rmdir(target, tiferr(callback, () => { fs.exists(target, exist => { try { - exist.should.be.false; + exist.should.be.false } catch (e) { - callback(e); - return; + callback(e) + return } - callback(); - }); - })); - })); - }); + callback() + }) + })) + })) + }) it('rmdir() - path is required', async () => { try { - await fs.rmdir(); - should.fail(); + await fs.rmdir() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('rmdirSync()', async () => { - const target = join(tmpDir, 'test'); + const target = join(tmpDir, 'test') - await createDummyFolder(target); - fs.rmdirSync(target); - const exist = await fs.exists(target); - exist.should.eql(false); - }); + await createDummyFolder(target) + fs.rmdirSync(target) + const exist = await fs.exists(target) + exist.should.eql(false) + }) it('rmdirSync() - path is required', () => { try { - fs.rmdirSync(); - should.fail(); + fs.rmdirSync() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('watch()', async () => { - const target = join(tmpDir, 'test.txt'); + const target = join(tmpDir, 'test.txt') const testerWrap = _watcher => new Promise((resolve, reject) => { - _watcher.on('add', resolve).on('error', reject); - }); + _watcher.on('add', resolve).on('error', reject) + }) - const watcher = await fs.watch(tmpDir); + const watcher = await fs.watch(tmpDir) const result = await Promise.all([ testerWrap(watcher), fs.writeFile(target, 'test') - ]); - result[0].should.eql(target); + ]) + result[0].should.eql(target) if (watcher) { - watcher.close(); + watcher.close() } - await fs.unlink(target); - }); + await fs.unlink(target) + }) it('watch() - path is required', async () => { try { - await fs.watch(); - should.fail(); + await fs.watch() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('ensurePath() - file exists', async () => { - const target = join(tmpDir, 'test'); - const filenames = ['foo.txt', 'foo-1.txt', 'foo-2.md', 'bar.txt']; + const target = join(tmpDir, 'test') + const filenames = ['foo.txt', 'foo-1.txt', 'foo-2.md', 'bar.txt'] - await Promise.map(filenames, path => fs.writeFile(join(target, path))); - const result = await fs.ensurePath(join(target, 'foo.txt')); - result.should.eql(join(target, 'foo-2.txt')); + await Promise.map(filenames, path => fs.writeFile(join(target, path))) + const result = await fs.ensurePath(join(target, 'foo.txt')) + result.should.eql(join(target, 'foo-2.txt')) - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('ensurePath() - file not exist', async () => { - const target = join(tmpDir, 'foo.txt'); - const result = await fs.ensurePath(target); - result.should.eql(target); - }); + const target = join(tmpDir, 'foo.txt') + const result = await fs.ensurePath(target) + result.should.eql(target) + }) it('ensurePath() - callback', callback => { - const target = join(tmpDir, 'test'); - const filenames = ['foo.txt', 'foo-1.txt', 'foo-2.md', 'bar.txt']; + const target = join(tmpDir, 'test') + const filenames = ['foo.txt', 'foo-1.txt', 'foo-2.md', 'bar.txt'] Promise.map(filenames, path => fs.writeFile(join(target, path))).asCallback(tiferr(callback, () => { fs.ensurePath(join(target, 'foo.txt'), tiferr(callback, path => { - path.should.eql(join(target, 'foo-2.txt')); - fs.rmdir(target, callback); - })); - })); - }); + path.should.eql(join(target, 'foo-2.txt')) + fs.rmdir(target, callback) + })) + })) + }) it('ensurePath() - path is required', async () => { try { - await fs.ensurePath(); - should.fail(); + await fs.ensurePath() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('ensurePathSync() - file exists', async () => { - const target = join(tmpDir, 'test'); - const filenames = ['foo.txt', 'foo-1.txt', 'foo-2.md', 'bar.txt']; + const target = join(tmpDir, 'test') + const filenames = ['foo.txt', 'foo-1.txt', 'foo-2.md', 'bar.txt'] - await Promise.map(filenames, path => fs.writeFile(join(target, path))); - const path = fs.ensurePathSync(join(target, 'foo.txt')); - path.should.eql(join(target, 'foo-2.txt')); + await Promise.map(filenames, path => fs.writeFile(join(target, path))) + const path = fs.ensurePathSync(join(target, 'foo.txt')) + path.should.eql(join(target, 'foo-2.txt')) - await fs.rmdir(target); - }); + await fs.rmdir(target) + }) it('ensurePathSync() - file not exist', () => { - const target = join(tmpDir, 'foo.txt'); - const path = fs.ensurePathSync(target); + const target = join(tmpDir, 'foo.txt') + const path = fs.ensurePathSync(target) - path.should.eql(target); - }); + path.should.eql(target) + }) it('ensurePathSync() - path is required', () => { try { - fs.ensurePathSync(); - should.fail(); + fs.ensurePathSync() + should.fail() } catch (err) { - err.message.should.eql('path is required!'); + err.message.should.eql('path is required!') } - }); + }) it('ensureWriteStream()', async () => { - const { promisify } = require('util'); - const streamFn = require('stream'); - const finished = promisify(streamFn.finished); + const { promisify } = require('util') + const streamFn = require('stream') + const finished = promisify(streamFn.finished) - const target = join(tmpDir, 'foo', 'bar.txt'); + const target = join(tmpDir, 'foo', 'bar.txt') - const stream = await fs.ensureWriteStream(target); - stream.path.should.eql(target); + const stream = await fs.ensureWriteStream(target) + stream.path.should.eql(target) - stream.end(); - await finished(stream); + stream.end() + await finished(stream) - await fs.unlink(target); - }); + await fs.unlink(target) + }) it('ensureWriteStream() - callback', callback => { - const target = join(tmpDir, 'foo', 'bar.txt'); + const target = join(tmpDir, 'foo', 'bar.txt') fs.ensureWriteStream(target, tiferr(callback, stream => { - stream.path.should.eql(target); + stream.path.should.eql(target) - stream.on('error', callback); + stream.on('error', callback) stream.on('close', () => { - fs.unlink(target, callback); - }); + fs.unlink(target, callback) + }) - stream.end(); - })); - }); + stream.end() + })) + }) it('ensureWriteStreamSync()', callback => { - const target = join(tmpDir, 'foo', 'bar.txt'); - const stream = fs.ensureWriteStreamSync(target); + const target = join(tmpDir, 'foo', 'bar.txt') + const stream = fs.ensureWriteStreamSync(target) - stream.path.should.eql(target); + stream.path.should.eql(target) - stream.on('error', callback); + stream.on('error', callback) stream.on('close', () => { - fs.rmdir(dirname(target), callback); - }); + fs.rmdir(dirname(target), callback) + }) - stream.end(); - }); -}); + stream.end() + }) +}) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d287dc5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "sourceMap": true, + "outDir": "dist", + "esModuleInterop": true + }, + "include": [ + "lib/fs.ts" + ], + "exclude": [ + "node_modules" + ] +}