mono/packages/fs/dist/utils/tree_walker.js
2025-01-23 07:22:43 +01:00

101 lines
3.4 KiB
JavaScript

import { Readable } from 'node:stream';
import * as pathUtil from 'node:path';
import { sync as inspectSync, async as inspectASync } from '../inspect.js';
import { ENodeType } from '../interfaces.js';
import { sync as listSync, async as listASync } from '../list.js';
// ---------------------------------------------------------
// SYNC
// ---------------------------------------------------------
export function sync(path, options, callback, currentLevel) {
const item = inspectSync(path, options.inspectOptions);
if (options.maxLevelsDeep === undefined) {
options.maxLevelsDeep = Infinity;
}
if (currentLevel === undefined) {
currentLevel = 0;
}
let children = [];
const hasChildren = item && item.type === ENodeType.DIR && currentLevel < options.maxLevelsDeep;
if (hasChildren) {
children = listSync(path);
}
callback(path, item);
if (hasChildren) {
children.forEach(child => sync(path + pathUtil.sep + child, options, callback, currentLevel + 1));
}
}
export function stream(path, options) {
const rs = new Readable({ objectMode: true });
let nextTreeNode = {
path: path,
parent: undefined,
level: 0
};
let running = false;
let readSome;
const error = (err) => {
rs.emit('error', err);
};
const findNextUnprocessedNode = (node) => {
if (node.nextSibling) {
return node.nextSibling;
}
else if (node.parent) {
return findNextUnprocessedNode(node.parent);
}
return undefined;
};
const pushAndContinueMaybe = (data) => {
const theyWantMore = rs.push(data);
running = false;
if (!nextTreeNode) {
// Previous was the last node. The job is done.
rs.push(null);
}
else if (theyWantMore) {
readSome();
}
};
if (options.maxLevelsDeep === undefined) {
options.maxLevelsDeep = Infinity;
}
readSome = () => {
const theNode = nextTreeNode;
running = true;
inspectASync(theNode.path, options.inspectOptions)
.then((inspected) => {
theNode.inspected = inspected;
if (inspected && inspected.type === ENodeType.DIR && theNode.level < options.maxLevelsDeep) {
listASync(theNode.path)
.then((childrenNames) => {
const children = childrenNames.map((name) => {
return {
name: name,
path: theNode.path + pathUtil.sep + name,
parent: theNode,
level: theNode.level + 1
};
});
children.forEach((child, index) => {
child.nextSibling = children[index + 1];
});
nextTreeNode = children[0] || findNextUnprocessedNode(theNode);
pushAndContinueMaybe({ path: theNode.path, item: inspected });
})
.catch(error);
}
else {
nextTreeNode = findNextUnprocessedNode(theNode);
pushAndContinueMaybe({ path: theNode.path, item: inspected });
}
})
.catch(error);
};
rs['_read'] = () => {
if (!running) {
readSome();
}
};
return rs;
}