Skip to content

Commit

Permalink
Massive rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
paulmillr committed Jul 31, 2024
1 parent 064e47d commit c17c0f6
Show file tree
Hide file tree
Showing 7 changed files with 3,395 additions and 107 deletions.
14 changes: 5 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,14 @@
"node": ">= 18"
},
"type": "module",
"main": "lib/index.js",
"main": "lib/v4.js",
"dependencies": {
"is-binary-path": "2.1.0",
"normalize-path": "3.0.0",
"readdirp": "github:paulmillr/readdirp"
},
"devDependencies": {
"@eslint/js": "^9.3.0",
"@types/node": "20.12.12",
"chai": "4.3.4",
"eslint": "^8.57.0",
"globals": "^15.3.0",
"rimraf": "5.0.5",
"sinon": "12.0.1",
"sinon-chai": "3.7.0",
Expand All @@ -32,8 +28,8 @@
"upath": "2.0.1"
},
"files": [
"lib/*.js",
"lib/*.d.ts"
"lib/v4.js",
"lib/v4.d.ts"
],
"repository": {
"type": "git",
Expand All @@ -45,8 +41,8 @@
"license": "MIT",
"scripts": {
"build": "tsc",
"lint": "eslint .",
"test": "npm run build && npm run lint && node --test"
"lint": "eslint src/v4.ts",
"test": "npm run build && npm run lint && node --test test-v4.mjs"
},
"keywords": [
"fs",
Expand Down
71 changes: 16 additions & 55 deletions src/anymatch.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,35 @@
import normalizePath from 'normalize-path';
import path from 'node:path';
import type {Stats} from 'node:fs';
import type { Stats } from 'node:fs';

export type MatchFunction = (val: string, stats?: Stats) => boolean;
export interface MatcherObject {
path: string;
recursive?: boolean;
}
export type Matcher =
| string
| RegExp
| MatchFunction
| MatcherObject;
export type Matcher = string | RegExp | MatchFunction | MatcherObject;

function arrify<T>(item: T | T[]): T[] {
return Array.isArray(item) ? item : [item];
}

export const isMatcherObject = (matcher: Matcher): matcher is MatcherObject =>
typeof matcher === 'object' &&
matcher !== null &&
!(matcher instanceof RegExp);
typeof matcher === 'object' && matcher !== null && !(matcher instanceof RegExp);

/**
* @param {AnymatchPattern} matcher
* @returns {MatchFunction}
*/
const createPattern = (matcher: Matcher): MatchFunction => {
if (typeof matcher === 'function') {
return matcher;
}
if (typeof matcher === 'string') {
return (string) => matcher === string;
}
if (matcher instanceof RegExp) {
return (string) => matcher.test(string);
}
if (typeof matcher === 'function') return matcher;
if (typeof matcher === 'string') return (string) => matcher === string;
if (matcher instanceof RegExp) return (string) => matcher.test(string);
if (typeof matcher === 'object' && matcher !== null) {
return (string) => {
if (matcher.path === string) {
return true;
}
if (matcher.path === string) return true;
if (matcher.recursive) {
const relative = path.relative(matcher.path, string);
if (!relative) {
return false;
}
if (!relative) return false;
return !relative.startsWith('..') && !path.isAbsolute(relative);
}
return false;
Expand All @@ -60,20 +44,12 @@ const createPattern = (matcher: Matcher): MatchFunction => {
* @param {Boolean} returnIndex
* @returns {boolean|number}
*/
function matchPatterns(
patterns: MatchFunction[],
testString: string,
stats?: Stats
): boolean {
function matchPatterns(patterns: MatchFunction[], testString: string, stats?: Stats): boolean {
const path = normalizePath(testString);

for (let index = 0; index < patterns.length; index++) {
const pattern = patterns[index];
if (pattern(path, stats)) {
return true;
}
if (pattern(path, stats)) return true;
}

return false;
}

Expand All @@ -83,34 +59,19 @@ function matchPatterns(
* @param {object} options
* @returns {boolean|number|Function}
*/
function anymatch(
matchers: Matcher[],
testString: undefined
): MatchFunction;
function anymatch(
matchers: Matcher[],
testString: string
): boolean;
function anymatch(
matchers: Matcher[],
testString: string|undefined
): boolean|MatchFunction {
if (matchers == null) {
throw new TypeError('anymatch: specify first argument');
}

function anymatch(matchers: Matcher[], testString: undefined): MatchFunction;
function anymatch(matchers: Matcher[], testString: string): boolean;
function anymatch(matchers: Matcher[], testString: string | undefined): boolean | MatchFunction {
if (matchers == null) throw new TypeError('anymatch: specify first argument');
// Early cache for matchers.
const matchersArray = arrify(matchers);
const patterns = matchersArray
.map(matcher => createPattern(matcher));

const patterns = matchersArray.map((matcher) => createPattern(matcher));
if (testString == null) {
return (testString: string, stats?: Stats): boolean => {
return matchPatterns(patterns, testString, stats);
};
}

return matchPatterns(patterns, testString);
}

export {anymatch};
export { anymatch };
31 changes: 18 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from 'node:fs';
import { EventEmitter } from 'node:events';
import sysPath from 'node:path';
import readdirp from 'readdirp';
import {stat, readdir} from 'node:fs/promises';
import { stat, readdir } from 'node:fs/promises';

import NodeFsHandler from './nodefs-handler.js';
import { anymatch, MatchFunction, isMatcherObject, Matcher } from './anymatch.js';
Expand Down Expand Up @@ -83,12 +83,8 @@ const normalizeIgnored =
};

const getAbsolutePath = (path, cwd) => {
if (sysPath.isAbsolute(path)) {
return path;
}
if (path.startsWith(BANG)) {
return BANG + sysPath.join(cwd, path.slice(1));
}
if (sysPath.isAbsolute(path)) return path;
if (path.startsWith('!')) return '!' + sysPath.join(cwd, path.slice(1)); // '!' == './'
return sysPath.join(cwd, path);
};

Expand All @@ -112,7 +108,7 @@ class DirEntry {
add(item) {
const { items } = this;
if (!items) return;
if (item !== ONE_DOT && item !== TWO_DOTS) items.add(item);
if (item !== '.' && item !== '..') items.add(item);
}

async remove(item) {
Expand Down Expand Up @@ -169,10 +165,11 @@ export class WatchHelper {
constructor(path: string, follow: boolean, fsw: any) {
this.fsw = fsw;
const watchPath = path;
this.path = path = path.replace(REPLACER_RE, EMPTY_STR);
this.path = path = path.replace(REPLACER_RE, '');
this.watchPath = watchPath;
this.fullWatchPath = sysPath.resolve(watchPath);
/** @type {object|boolean} */

this.dirParts = [];
this.dirParts.forEach((parts) => {
if (parts.length > 1) parts.pop();
Expand All @@ -182,12 +179,13 @@ export class WatchHelper {
}

entryPath(entry) {
// basically sysPath.absolute
return sysPath.join(this.watchPath, sysPath.relative(this.watchPath, entry.fullPath));
}

filterPath(entry) {
const { stats } = entry;
if (stats && stats.isSymbolicLink()) return this.filterDir(entry);
if (stats && stats.isSymbolicLink()) return this.filterDir(entry); /// WUT?! symlink can be file too
const resolvedPath = this.entryPath(entry);
return this.fsw._isntIgnored(resolvedPath, stats) && this.fsw._hasReadPermissions(stats);
}
Expand Down Expand Up @@ -345,6 +343,7 @@ export class FSWatcher extends EventEmitter {
}
if (opts.ignored) opts.ignored = arrify(opts.ignored);

// Done to emit ready only once, but each 'add' will increase that
let readyCalls = 0;
this._emitReady = () => {
readyCalls++;
Expand Down Expand Up @@ -472,6 +471,7 @@ export class FSWatcher extends EventEmitter {

this._closePath(path);

// TWICE!
this._addIgnoredPath(path);
if (this._watched.has(path)) {
this._addIgnoredPath({
Expand Down Expand Up @@ -507,7 +507,7 @@ export class FSWatcher extends EventEmitter {
);
this._streams.forEach((stream) => stream.destroy());
this._userIgnored = undefined;
this._readyCount = 0;
this._readyCount = 0; // allows to re-start
this._readyEmitted = false;
this._watched.forEach((dirent) => dirent.dispose());
['closers', 'watched', 'streams', 'symlinkPaths', 'throttled'].forEach((key) => {
Expand Down Expand Up @@ -544,6 +544,7 @@ export class FSWatcher extends EventEmitter {
/**
* Normalize and emit events.
* Calling _emit DOES NOT MEAN emit() would be called!
* val1 == stats, others are unused
* @param {EventName} event Type of event
* @param {Path} path File or directory path
* @param {*=} val1 arguments to be passed with event
Expand Down Expand Up @@ -907,6 +908,7 @@ export class FSWatcher extends EventEmitter {
_closeFile(path) {
const closers = this._closers.get(path);
if (!closers) return;
// no promise handling here
closers.forEach((closer) => closer());
this._closers.delete(path);
}
Expand All @@ -931,10 +933,13 @@ export class FSWatcher extends EventEmitter {
const options = { type: EV.ALL, alwaysStat: true, lstat: true, ...opts };
let stream = readdirp(root, options);
this._streams.add(stream);
stream.once(STR_CLOSE, () => {
// possible mem leak if emited before end
stream.once('close', () => {
console.log('readdirp close');
stream = undefined;
});
stream.once(STR_END, () => {
stream.once('end', () => {
console.log('readdirp end');
if (stream) {
this._streams.delete(stream);
stream = undefined;
Expand Down
Loading

0 comments on commit c17c0f6

Please sign in to comment.