source: imaps-frontend/node_modules/readdirp/esm/index.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 2 weeks ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 9.4 KB
Line 
1import { stat, lstat, readdir, realpath } from 'node:fs/promises';
2import { Readable } from 'node:stream';
3import { resolve as presolve, relative as prelative, join as pjoin, sep as psep } from 'node:path';
4export const EntryTypes = {
5 FILE_TYPE: 'files',
6 DIR_TYPE: 'directories',
7 FILE_DIR_TYPE: 'files_directories',
8 EVERYTHING_TYPE: 'all',
9};
10const defaultOptions = {
11 root: '.',
12 fileFilter: (_entryInfo) => true,
13 directoryFilter: (_entryInfo) => true,
14 type: EntryTypes.FILE_TYPE,
15 lstat: false,
16 depth: 2147483648,
17 alwaysStat: false,
18 highWaterMark: 4096,
19};
20Object.freeze(defaultOptions);
21const RECURSIVE_ERROR_CODE = 'READDIRP_RECURSIVE_ERROR';
22const NORMAL_FLOW_ERRORS = new Set(['ENOENT', 'EPERM', 'EACCES', 'ELOOP', RECURSIVE_ERROR_CODE]);
23const ALL_TYPES = [
24 EntryTypes.DIR_TYPE,
25 EntryTypes.EVERYTHING_TYPE,
26 EntryTypes.FILE_DIR_TYPE,
27 EntryTypes.FILE_TYPE,
28];
29const DIR_TYPES = new Set([
30 EntryTypes.DIR_TYPE,
31 EntryTypes.EVERYTHING_TYPE,
32 EntryTypes.FILE_DIR_TYPE,
33]);
34const FILE_TYPES = new Set([
35 EntryTypes.EVERYTHING_TYPE,
36 EntryTypes.FILE_DIR_TYPE,
37 EntryTypes.FILE_TYPE,
38]);
39const isNormalFlowError = (error) => NORMAL_FLOW_ERRORS.has(error.code);
40const wantBigintFsStats = process.platform === 'win32';
41const emptyFn = (_entryInfo) => true;
42const normalizeFilter = (filter) => {
43 if (filter === undefined)
44 return emptyFn;
45 if (typeof filter === 'function')
46 return filter;
47 if (typeof filter === 'string') {
48 const fl = filter.trim();
49 return (entry) => entry.basename === fl;
50 }
51 if (Array.isArray(filter)) {
52 const trItems = filter.map((item) => item.trim());
53 return (entry) => trItems.some((f) => entry.basename === f);
54 }
55 return emptyFn;
56};
57/** Readable readdir stream, emitting new files as they're being listed. */
58export class ReaddirpStream extends Readable {
59 constructor(options = {}) {
60 super({
61 objectMode: true,
62 autoDestroy: true,
63 highWaterMark: options.highWaterMark,
64 });
65 const opts = { ...defaultOptions, ...options };
66 const { root, type } = opts;
67 this._fileFilter = normalizeFilter(opts.fileFilter);
68 this._directoryFilter = normalizeFilter(opts.directoryFilter);
69 const statMethod = opts.lstat ? lstat : stat;
70 // Use bigint stats if it's windows and stat() supports options (node 10+).
71 if (wantBigintFsStats) {
72 this._stat = (path) => statMethod(path, { bigint: true });
73 }
74 else {
75 this._stat = statMethod;
76 }
77 this._maxDepth = opts.depth ?? defaultOptions.depth;
78 this._wantsDir = type ? DIR_TYPES.has(type) : false;
79 this._wantsFile = type ? FILE_TYPES.has(type) : false;
80 this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
81 this._root = presolve(root);
82 this._isDirent = !opts.alwaysStat;
83 this._statsProp = this._isDirent ? 'dirent' : 'stats';
84 this._rdOptions = { encoding: 'utf8', withFileTypes: this._isDirent };
85 // Launch stream with one parent, the root dir.
86 this.parents = [this._exploreDir(root, 1)];
87 this.reading = false;
88 this.parent = undefined;
89 }
90 async _read(batch) {
91 if (this.reading)
92 return;
93 this.reading = true;
94 try {
95 while (!this.destroyed && batch > 0) {
96 const par = this.parent;
97 const fil = par && par.files;
98 if (fil && fil.length > 0) {
99 const { path, depth } = par;
100 const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path));
101 const awaited = await Promise.all(slice);
102 for (const entry of awaited) {
103 if (!entry) {
104 batch--;
105 return;
106 }
107 if (this.destroyed)
108 return;
109 const entryType = await this._getEntryType(entry);
110 if (entryType === 'directory' && this._directoryFilter(entry)) {
111 if (depth <= this._maxDepth) {
112 this.parents.push(this._exploreDir(entry.fullPath, depth + 1));
113 }
114 if (this._wantsDir) {
115 this.push(entry);
116 batch--;
117 }
118 }
119 else if ((entryType === 'file' || this._includeAsFile(entry)) &&
120 this._fileFilter(entry)) {
121 if (this._wantsFile) {
122 this.push(entry);
123 batch--;
124 }
125 }
126 }
127 }
128 else {
129 const parent = this.parents.pop();
130 if (!parent) {
131 this.push(null);
132 break;
133 }
134 this.parent = await parent;
135 if (this.destroyed)
136 return;
137 }
138 }
139 }
140 catch (error) {
141 this.destroy(error);
142 }
143 finally {
144 this.reading = false;
145 }
146 }
147 async _exploreDir(path, depth) {
148 let files;
149 try {
150 files = await readdir(path, this._rdOptions);
151 }
152 catch (error) {
153 this._onError(error);
154 }
155 return { files, depth, path };
156 }
157 async _formatEntry(dirent, path) {
158 let entry;
159 const basename = this._isDirent ? dirent.name : dirent;
160 try {
161 const fullPath = presolve(pjoin(path, basename));
162 entry = { path: prelative(this._root, fullPath), fullPath, basename };
163 entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
164 }
165 catch (err) {
166 this._onError(err);
167 return;
168 }
169 return entry;
170 }
171 _onError(err) {
172 if (isNormalFlowError(err) && !this.destroyed) {
173 this.emit('warn', err);
174 }
175 else {
176 this.destroy(err);
177 }
178 }
179 async _getEntryType(entry) {
180 // entry may be undefined, because a warning or an error were emitted
181 // and the statsProp is undefined
182 if (!entry && this._statsProp in entry) {
183 return '';
184 }
185 const stats = entry[this._statsProp];
186 if (stats.isFile())
187 return 'file';
188 if (stats.isDirectory())
189 return 'directory';
190 if (stats && stats.isSymbolicLink()) {
191 const full = entry.fullPath;
192 try {
193 const entryRealPath = await realpath(full);
194 const entryRealPathStats = await lstat(entryRealPath);
195 if (entryRealPathStats.isFile()) {
196 return 'file';
197 }
198 if (entryRealPathStats.isDirectory()) {
199 const len = entryRealPath.length;
200 if (full.startsWith(entryRealPath) && full.substr(len, 1) === psep) {
201 const recursiveError = new Error(`Circular symlink detected: "${full}" points to "${entryRealPath}"`);
202 // @ts-ignore
203 recursiveError.code = RECURSIVE_ERROR_CODE;
204 return this._onError(recursiveError);
205 }
206 return 'directory';
207 }
208 }
209 catch (error) {
210 this._onError(error);
211 return '';
212 }
213 }
214 }
215 _includeAsFile(entry) {
216 const stats = entry && entry[this._statsProp];
217 return stats && this._wantsEverything && !stats.isDirectory();
218 }
219}
220/**
221 * Streaming version: Reads all files and directories in given root recursively.
222 * Consumes ~constant small amount of RAM.
223 * @param root Root directory
224 * @param options Options to specify root (start directory), filters and recursion depth
225 */
226export function readdirp(root, options = {}) {
227 // @ts-ignore
228 let type = options.entryType || options.type;
229 if (type === 'both')
230 type = EntryTypes.FILE_DIR_TYPE; // backwards-compatibility
231 if (type)
232 options.type = type;
233 if (!root) {
234 throw new Error('readdirp: root argument is required. Usage: readdirp(root, options)');
235 }
236 else if (typeof root !== 'string') {
237 throw new TypeError('readdirp: root argument must be a string. Usage: readdirp(root, options)');
238 }
239 else if (type && !ALL_TYPES.includes(type)) {
240 throw new Error(`readdirp: Invalid type passed. Use one of ${ALL_TYPES.join(', ')}`);
241 }
242 options.root = root;
243 return new ReaddirpStream(options);
244}
245/**
246 * Promise version: Reads all files and directories in given root recursively.
247 * Compared to streaming version, will consume a lot of RAM e.g. when 1 million files are listed.
248 * @returns array of paths and their entry infos
249 */
250export function readdirpPromise(root, options = {}) {
251 return new Promise((resolve, reject) => {
252 const files = [];
253 readdirp(root, options)
254 .on('data', (entry) => files.push(entry))
255 .on('end', () => resolve(files))
256 .on('error', (error) => reject(error));
257 });
258}
259export default readdirp;
Note: See TracBrowser for help on using the repository browser.