-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from atjn/asyncify
Upgrade to promise-based API
- Loading branch information
Showing
15 changed files
with
3,947 additions
and
371 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
name: Test | ||
|
||
on: [push, pull_request] | ||
|
||
jobs: | ||
|
||
test: | ||
|
||
strategy: | ||
fail-fast: false | ||
matrix: | ||
node-version: [14.x] | ||
os: [ubuntu-latest, windows-latest, macOS-latest] | ||
include: | ||
- node-version: 15.x | ||
os: ubuntu-latest | ||
|
||
runs-on: ${{ matrix.os }} | ||
|
||
steps: | ||
|
||
- name: Checkout code | ||
uses: actions/checkout@v2 | ||
|
||
- name: Set up Node ${{ matrix.node-version }} | ||
uses: actions/setup-node@v2 | ||
with: | ||
node-version: ${{ matrix.node-version }} | ||
|
||
- name: Use npm cache | ||
uses: actions/cache@v2 | ||
with: | ||
path: node_modules | ||
key: ${{ runner.os }}-node${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }} | ||
|
||
- name: Install dependencies | ||
run: npm install | ||
|
||
- name: Test code | ||
run: npm test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
node_modules | ||
.nyc_output |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,14 @@ | ||
'use strict'; | ||
|
||
const path = require('path'); | ||
const getSize = require('./'); | ||
import path from 'path'; | ||
import getFolderSize from './index.js'; | ||
|
||
if (!process.env.FOLDER) { | ||
throw new Error('FOLDER env var needed'); | ||
} | ||
|
||
getSize(path.resolve(process.env.FOLDER), (err, size) => { | ||
if (err) { throw err; } | ||
getFolderSize.strict(path.resolve(process.env.FOLDER)).then(size => { | ||
|
||
console.log(size + ' bytes'); | ||
console.log((size / 1024 / 1024).toFixed(2) + ' Mb'); | ||
console.log((size / 1000 / 1000).toFixed(2) + ' MB'); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,85 @@ | ||
'use strict'; | ||
|
||
const fs = require('fs'); | ||
const path = require('path'); | ||
const eachAsync = require('tiny-each-async'); | ||
import { join as joinPaths } from 'path'; | ||
|
||
function readSizeRecursive(seen, item, ignoreRegEx, callback) { | ||
let cb; | ||
let ignoreRegExp; | ||
/** | ||
* Returns an object containing the size of the folder and a list of errors encountered while traversing the folder. | ||
* | ||
* If any errors are returned, the returned folder size is likely smaller than the real folder size. | ||
* | ||
* @param {string} itemPath - Path of the folder. | ||
* @param {object} [options] - Options. | ||
* @param {object} [options.ignore] - If a file's path matches this regex object, its size is not counted. | ||
* @param {object} [options.fs] - The filesystem that should be used. Uses node fs by default. | ||
* | ||
* @returns {Promise<{size: number, errors: Array<Error> | null}>} - An object containing the size of the folder in bytes and a list of encountered errors. | ||
*/ | ||
export default async function getFolderSize (itemPath, options) { return await core(itemPath, options, {errors: true}) } | ||
|
||
if (!callback) { | ||
cb = ignoreRegEx; | ||
ignoreRegExp = null; | ||
} else { | ||
cb = callback; | ||
ignoreRegExp = ignoreRegEx; | ||
} | ||
/** | ||
* Returns the size of the folder. If any errors are encountered while traversing the folder, they are silently ignored. | ||
* | ||
* The returned folder size might be smaller than the real folder size. It is impossible to know for sure, since errors are ignored. | ||
* | ||
* @param {string} itemPath - Path of the folder. | ||
* @param {object} [options] - Options. | ||
* @param {object} [options.ignore] - If a file's path matches this regex object, its size is not counted. | ||
* @param {object} [options.fs] - The filesystem that should be used. Uses node fs by default. | ||
* | ||
* @returns {Promise<number>} - The size of the folder in bytes. | ||
*/ | ||
getFolderSize.loose = async (itemPath, options) => await core(itemPath, options); | ||
|
||
fs.lstat(item, function lstat(e, stats) { | ||
let total = !e ? (stats.size || 0) : 0; | ||
/** | ||
* Returns the size of the folder. If any errors are encountered while traversing the folder, this method will throw an error. | ||
* | ||
* Because errors will otherwise make this method fail, the returned folder size will always be accurate. | ||
* | ||
* @param {string} itemPath - Path of the folder. | ||
* @param {object} [options] - Options. | ||
* @param {object} [options.ignore] - If a file's path matches this regex object, its size is not counted. | ||
* @param {object} [options.fs] - The filesystem that should be used. Uses node fs by default. | ||
* | ||
* @returns {Promise<number>} - The size of the folder in bytes. | ||
*/ | ||
getFolderSize.strict = async (itemPath, options) => await core(itemPath, options, {strict: true}); | ||
|
||
if (stats) { | ||
if (seen.has(stats.ino)) { return cb(null, 0); } | ||
|
||
seen.add(stats.ino); | ||
} | ||
|
||
if (!e && stats.isDirectory()) { | ||
fs.readdir(item, (err, list) => { | ||
if (err) { return cb(err); } | ||
async function core (rootItemPath, options = {}, returnType = {}) { | ||
const fs = options.fs || await import('fs/promises'); | ||
|
||
const fileSizes = new Map(); | ||
const errors = []; | ||
|
||
await processItem(rootItemPath); | ||
|
||
eachAsync( | ||
list, | ||
5000, | ||
(dirItem, next) => { | ||
readSizeRecursive( | ||
seen, | ||
path.join(item, dirItem), | ||
ignoreRegExp, | ||
(error, size) => { | ||
if (!error) { total += size; } | ||
async function processItem(itemPath) { | ||
if(options.ignore?.test(itemPath)) return; | ||
|
||
next(error); | ||
} | ||
); | ||
}, | ||
(finalErr) => { | ||
cb(finalErr, total); | ||
} | ||
); | ||
}); | ||
} else { | ||
if (ignoreRegExp && ignoreRegExp.test(item)) { | ||
total = 0; | ||
} | ||
const stats = returnType.strict ? await fs.lstat(itemPath) : await fs.lstat(itemPath).catch(error => errors.push(error)); | ||
if(typeof stats !== 'object') return; | ||
fileSizes.set(stats.ino, stats.size); | ||
|
||
cb(e, total); | ||
if(stats.isDirectory()) { | ||
const directoryItems = returnType.strict ? await fs.readdir(itemPath) : await fs.readdir(itemPath).catch(error => errors.push(error)); | ||
if(typeof directoryItems !== 'object') return; | ||
await Promise.all( | ||
directoryItems.map(directoryItem => | ||
processItem(joinPaths(itemPath, directoryItem)) | ||
) | ||
); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
module.exports = (...args) => { | ||
args.unshift(new Set()); | ||
const folderSize = Array.from(fileSizes.values()).reduce((total, fileSize) => total + fileSize, 0); | ||
|
||
if (returnType.errors) { | ||
return { | ||
size: folderSize, | ||
errors: errors.length > 0 ? errors : null, | ||
} | ||
} else { | ||
return folderSize; | ||
} | ||
|
||
return readSizeRecursive(...args); | ||
}; | ||
} |
Oops, something went wrong.