Initial commit
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020 steelbrain
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
# ScanDir
|
||||
|
||||
`sb-scandir` is a node module that supports simple file scanning with some sugar features.
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
npm install --save sb-scandir
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
```js
|
||||
interface Result {
|
||||
files: Array<string>
|
||||
directories: Array<string>
|
||||
}
|
||||
|
||||
interface FileSystem {
|
||||
join(pathA: string, pathB: string): string
|
||||
basename(path: string): string
|
||||
stat(path: string): Promise<fs.Stats>
|
||||
readdir(path: string): Promise<string[]>
|
||||
}
|
||||
|
||||
type Validate = (path: string) => boolean
|
||||
|
||||
export const defaultFilesystem: FileSystem;
|
||||
|
||||
export default async function scanDirectory(
|
||||
path: string,
|
||||
{
|
||||
recursive = true,
|
||||
validate = null,
|
||||
concurrency = Infinity,
|
||||
fileSystem = defaultFilesystem,
|
||||
}: {
|
||||
recursive?: boolean
|
||||
validate?: Validate | null
|
||||
concurrency?: number
|
||||
fileSystem?: Partial<FileSystem>
|
||||
} = {},
|
||||
): Promise<Result>;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
import Path from 'path'
|
||||
import scandir, { defaultFilesystem } from 'sb-scandir'
|
||||
// or
|
||||
const { default: scandir, defaultFilesystem } = require('sb-scandir')
|
||||
|
||||
// Scan all files except the dot ones
|
||||
scandir(__dirname).then(function(result) {
|
||||
console.log('files', result.files)
|
||||
console.log('directories', result.directories)
|
||||
})
|
||||
|
||||
// Scan all top level files except dot ones
|
||||
scandir(__dirname, { recursive: false }).then(function(files) {
|
||||
console.log('files', result.files)
|
||||
console.log('directories', result.directories)
|
||||
})
|
||||
|
||||
// Scan all files even the dot ones
|
||||
scandir(__dirname, { recursive: true, validate(path) {
|
||||
return true
|
||||
}}).then(function(files) {
|
||||
console.log('files', result.files)
|
||||
console.log('directories', result.directories)
|
||||
})
|
||||
|
||||
// Scan all files except in .git and node_modules
|
||||
scandir(__dirname, { recursive: true, validate(path) {
|
||||
const baseName = Path.basename(path)
|
||||
return baseName !== '.git' && baseName !== 'node_modules'
|
||||
}}).then(function(files) {
|
||||
console.log('files', result.files)
|
||||
console.log('directories', result.directories)
|
||||
})
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the terms of MIT License. See the LICENSE file for more info.
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
"use strict";
|
||||
/* @flow */
|
||||
var __assign = (this && this.__assign) || function () {
|
||||
__assign = Object.assign || function(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||
t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (_) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.defaultFilesystem = void 0;
|
||||
var fs_1 = __importDefault(require("fs"));
|
||||
var path_1 = __importDefault(require("path"));
|
||||
var assert_1 = __importDefault(require("assert"));
|
||||
var sb_promise_queue_1 = require("sb-promise-queue");
|
||||
exports.defaultFilesystem = {
|
||||
join: function (pathA, pathB) {
|
||||
return path_1.default.join(pathA, pathB);
|
||||
},
|
||||
basename: function (path) {
|
||||
return path_1.default.basename(path);
|
||||
},
|
||||
stat: function (path) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
fs_1.default.stat(path, function (err, res) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
else {
|
||||
resolve(res);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
readdir: function (path) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
fs_1.default.readdir(path, function (err, res) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
else {
|
||||
resolve(res);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
function scanDirectoryInternal(_a) {
|
||||
var path = _a.path, recursive = _a.recursive, validate = _a.validate, result = _a.result, fileSystem = _a.fileSystem, queue = _a.queue, reject = _a.reject;
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var itemStat, contents;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0: return [4 /*yield*/, fileSystem.stat(path)];
|
||||
case 1:
|
||||
itemStat = _b.sent();
|
||||
if (itemStat.isFile()) {
|
||||
result.files.push(path);
|
||||
}
|
||||
else if (itemStat.isDirectory()) {
|
||||
result.directories.push(path);
|
||||
}
|
||||
if (!itemStat.isDirectory() || recursive === 'none') {
|
||||
return [2 /*return*/];
|
||||
}
|
||||
return [4 /*yield*/, fileSystem.readdir(path)];
|
||||
case 2:
|
||||
contents = _b.sent();
|
||||
contents.forEach(function (item) {
|
||||
var itemPath = fileSystem.join(path, item);
|
||||
if (!validate(itemPath)) {
|
||||
return;
|
||||
}
|
||||
queue
|
||||
.add(function () {
|
||||
return scanDirectoryInternal({
|
||||
path: itemPath,
|
||||
recursive: recursive === 'shallow' ? 'none' : 'deep',
|
||||
validate: validate,
|
||||
result: result,
|
||||
fileSystem: fileSystem,
|
||||
queue: queue,
|
||||
reject: reject,
|
||||
});
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function scanDirectory(path, _a) {
|
||||
var _b = _a === void 0 ? {} : _a, _c = _b.recursive, recursive = _c === void 0 ? true : _c, _d = _b.validate, validate = _d === void 0 ? null : _d, _e = _b.concurrency, concurrency = _e === void 0 ? Infinity : _e, _f = _b.fileSystem, fileSystem = _f === void 0 ? exports.defaultFilesystem : _f;
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var queue, result, mergedFileSystem;
|
||||
return __generator(this, function (_g) {
|
||||
switch (_g.label) {
|
||||
case 0:
|
||||
assert_1.default(path && typeof path === 'string', 'path must be a valid string');
|
||||
assert_1.default(typeof recursive === 'boolean', 'options.recursive must be a valid boolean');
|
||||
assert_1.default(validate === null || typeof validate === 'function', 'options.validate must be a valid function');
|
||||
assert_1.default(typeof concurrency === 'number', 'options.concurrency must be a valid number');
|
||||
assert_1.default(fileSystem !== null && typeof fileSystem === 'object', 'options.fileSystem must be a valid object');
|
||||
queue = new sb_promise_queue_1.PromiseQueue({
|
||||
concurrency: concurrency,
|
||||
});
|
||||
result = { files: [], directories: [] };
|
||||
mergedFileSystem = __assign(__assign({}, exports.defaultFilesystem), fileSystem);
|
||||
return [4 /*yield*/, new Promise(function (resolve, reject) {
|
||||
scanDirectoryInternal({
|
||||
path: path,
|
||||
recursive: recursive ? 'deep' : 'shallow',
|
||||
validate: validate != null ? validate : function (item) { return mergedFileSystem.basename(item).slice(0, 1) !== '.'; },
|
||||
result: result,
|
||||
fileSystem: mergedFileSystem,
|
||||
queue: queue,
|
||||
reject: reject,
|
||||
})
|
||||
.then(function () { return queue.waitTillIdle(); })
|
||||
.then(resolve, reject);
|
||||
})];
|
||||
case 1:
|
||||
_g.sent();
|
||||
return [2 /*return*/, result];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.default = scanDirectory;
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/* @flow */
|
||||
import fs from 'fs';
|
||||
import fsPath from 'path';
|
||||
import invariant from 'assert';
|
||||
import { PromiseQueue } from 'sb-promise-queue';
|
||||
export const defaultFilesystem = {
|
||||
join(pathA, pathB) {
|
||||
return fsPath.join(pathA, pathB);
|
||||
},
|
||||
basename(path) {
|
||||
return fsPath.basename(path);
|
||||
},
|
||||
stat(path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.stat(path, (err, res) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
else {
|
||||
resolve(res);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
readdir(path) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.readdir(path, (err, res) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
else {
|
||||
resolve(res);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
async function scanDirectoryInternal({ path, recursive, validate, result, fileSystem, queue, reject, }) {
|
||||
const itemStat = await fileSystem.stat(path);
|
||||
if (itemStat.isFile()) {
|
||||
result.files.push(path);
|
||||
}
|
||||
else if (itemStat.isDirectory()) {
|
||||
result.directories.push(path);
|
||||
}
|
||||
if (!itemStat.isDirectory() || recursive === 'none') {
|
||||
return;
|
||||
}
|
||||
const contents = await fileSystem.readdir(path);
|
||||
contents.forEach((item) => {
|
||||
const itemPath = fileSystem.join(path, item);
|
||||
if (!validate(itemPath)) {
|
||||
return;
|
||||
}
|
||||
queue
|
||||
.add(() => scanDirectoryInternal({
|
||||
path: itemPath,
|
||||
recursive: recursive === 'shallow' ? 'none' : 'deep',
|
||||
validate,
|
||||
result,
|
||||
fileSystem,
|
||||
queue,
|
||||
reject,
|
||||
}))
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
async function scanDirectory(path, { recursive = true, validate = null, concurrency = Infinity, fileSystem = defaultFilesystem, } = {}) {
|
||||
invariant(path && typeof path === 'string', 'path must be a valid string');
|
||||
invariant(typeof recursive === 'boolean', 'options.recursive must be a valid boolean');
|
||||
invariant(validate === null || typeof validate === 'function', 'options.validate must be a valid function');
|
||||
invariant(typeof concurrency === 'number', 'options.concurrency must be a valid number');
|
||||
invariant(fileSystem !== null && typeof fileSystem === 'object', 'options.fileSystem must be a valid object');
|
||||
const queue = new PromiseQueue({
|
||||
concurrency,
|
||||
});
|
||||
const result = { files: [], directories: [] };
|
||||
const mergedFileSystem = { ...defaultFilesystem, ...fileSystem };
|
||||
await new Promise((resolve, reject) => {
|
||||
scanDirectoryInternal({
|
||||
path,
|
||||
recursive: recursive ? 'deep' : 'shallow',
|
||||
validate: validate != null ? validate : (item) => mergedFileSystem.basename(item).slice(0, 1) !== '.',
|
||||
result,
|
||||
fileSystem: mergedFileSystem,
|
||||
queue,
|
||||
reject,
|
||||
})
|
||||
.then(() => queue.waitTillIdle())
|
||||
.then(resolve, reject);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
export default scanDirectory;
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/// <reference types="node" />
|
||||
import fs from 'fs';
|
||||
interface Result {
|
||||
files: Array<string>;
|
||||
directories: Array<string>;
|
||||
}
|
||||
interface FileSystem {
|
||||
join(pathA: string, pathB: string): string;
|
||||
basename(path: string): string;
|
||||
stat(path: string): Promise<fs.Stats>;
|
||||
readdir(path: string): Promise<string[]>;
|
||||
}
|
||||
declare type Validate = (path: string) => boolean;
|
||||
export declare const defaultFilesystem: FileSystem;
|
||||
declare function scanDirectory(path: string, { recursive, validate, concurrency, fileSystem, }?: {
|
||||
recursive?: boolean;
|
||||
validate?: Validate | null;
|
||||
concurrency?: number;
|
||||
fileSystem?: Partial<FileSystem>;
|
||||
}): Promise<Result>;
|
||||
export default scanDirectory;
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"name": "sb-scandir",
|
||||
"version": "3.1.1",
|
||||
"description": "File scanning module for Node.js",
|
||||
"main": "lib/cjs/index.js",
|
||||
"typings": "lib/typings/index.d.ts",
|
||||
"module": "lib/esm/index.mjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/typings/index.d.ts",
|
||||
"import": "./lib/esm/index.mjs",
|
||||
"require": "./lib/cjs/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"type": "commonjs",
|
||||
"scripts": {
|
||||
"test": "ava",
|
||||
"lint": "(tsc -p . --noEmit) && (eslint . --ext .ts) && (prettier --list-different src/*.ts)",
|
||||
"prepare": "yarn build:clean ; yarn build:esm ; yarn build:cjs ; yarn build:typings",
|
||||
"build:clean": "rm -rf lib",
|
||||
"build:esm": "tsc --module es2015 --target es2018 --outDir lib/esm && mv lib/esm/index.js lib/esm/index.mjs",
|
||||
"build:cjs": "tsc --module commonjs --target es5 --outDir lib/cjs",
|
||||
"build:typings": "tsc --declaration --outDir lib/typings --emitDeclarationOnly"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/steelbrain/scandir.git"
|
||||
},
|
||||
"keywords": [
|
||||
"scandir",
|
||||
"steelbrain",
|
||||
"files",
|
||||
"scan"
|
||||
],
|
||||
"author": "steelbrain",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/steelbrain/scandir/issues"
|
||||
},
|
||||
"files": [
|
||||
"lib/*"
|
||||
],
|
||||
"homepage": "https://github.com/steelbrain/scandir#readme",
|
||||
"devDependencies": {
|
||||
"ava": "^3.11.1",
|
||||
"eslint-config-steelbrain": "^10.0.0-beta2",
|
||||
"ts-node": "^8.10.2",
|
||||
"typescript": "^3.9.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"sb-promise-queue": "^2.1.0"
|
||||
},
|
||||
"ava": {
|
||||
"files": [
|
||||
"test/*-test.ts"
|
||||
],
|
||||
"extensions": [
|
||||
"ts"
|
||||
],
|
||||
"require": [
|
||||
"ts-node/register/transpile-only"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
}
|
||||
Reference in New Issue
Block a user