filehound 2.0.0-beta.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/README.md +39 -11
  2. package/dist/arrays.d.ts +1 -0
  3. package/dist/arrays.js +4 -0
  4. package/dist/file.d.ts +108 -0
  5. package/dist/file.js +184 -0
  6. package/dist/filehound.d.ts +356 -0
  7. package/dist/filehound.js +498 -0
  8. package/dist/files.d.ts +4 -0
  9. package/dist/files.js +37 -0
  10. package/dist/filetype.d.ts +10 -0
  11. package/dist/filetype.js +9 -0
  12. package/dist/index.d.ts +5 -0
  13. package/dist/index.js +6 -0
  14. package/dist/matchers.d.ts +324 -0
  15. package/dist/matchers.js +419 -0
  16. package/dist/walker/async.d.ts +4 -0
  17. package/dist/walker/async.js +237 -0
  18. package/dist/walker/heap.d.ts +7 -0
  19. package/dist/walker/heap.js +49 -0
  20. package/dist/walker/sync.d.ts +4 -0
  21. package/dist/walker/sync.js +66 -0
  22. package/dist/walker/walk.d.ts +33 -0
  23. package/dist/walker/walk.js +80 -0
  24. package/package.json +45 -48
  25. package/.github/stale.yml +0 -42
  26. package/.travis.yml +0 -8
  27. package/.vscode/launch.json +0 -21
  28. package/.vscode/settings.json +0 -3
  29. package/CONTRIBUTING.md +0 -8
  30. package/_config.yml +0 -1
  31. package/coverage/coverage.raw.json +0 -1
  32. package/coverage/lcov-report/arrays.ts.html +0 -98
  33. package/coverage/lcov-report/base.css +0 -212
  34. package/coverage/lcov-report/filehound.ts.html +0 -2078
  35. package/coverage/lcov-report/files.ts.html +0 -260
  36. package/coverage/lcov-report/functions.ts.html +0 -122
  37. package/coverage/lcov-report/index.html +0 -132
  38. package/coverage/lcov-report/prettify.css +0 -1
  39. package/coverage/lcov-report/prettify.js +0 -1
  40. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  41. package/coverage/lcov-report/sorter.js +0 -158
  42. package/coverage/lcov.info +0 -378
  43. package/dist/src/arrays.js +0 -15
  44. package/dist/src/arrays.js.map +0 -1
  45. package/dist/src/asyncWalker.js +0 -59
  46. package/dist/src/asyncWalker.js.map +0 -1
  47. package/dist/src/bind.js +0 -15
  48. package/dist/src/bind.js.map +0 -1
  49. package/dist/src/filehound.js +0 -597
  50. package/dist/src/filehound.js.map +0 -1
  51. package/dist/src/files.js +0 -60
  52. package/dist/src/files.js.map +0 -1
  53. package/dist/src/matcher.js +0 -51
  54. package/dist/src/matcher.js.map +0 -1
  55. package/dist/src/syncWalker.js +0 -47
  56. package/dist/src/syncWalker.js.map +0 -1
  57. package/dist/src/walkAsync.js +0 -33
  58. package/dist/src/walkAsync.js.map +0 -1
  59. package/dist/src/walkSync.js +0 -21
  60. package/dist/src/walkSync.js.map +0 -1
  61. package/dist/src/walker/walk.js +0 -68
  62. package/dist/src/walker/walk.js.map +0 -1
  63. package/dist/src/walker/walkAsync.js +0 -33
  64. package/dist/src/walker/walkAsync.js.map +0 -1
  65. package/dist/src/walker/walkSync.js +0 -21
  66. package/dist/src/walker/walkSync.js.map +0 -1
  67. package/dist/test/filehound.js +0 -872
  68. package/dist/test/filehound.js.map +0 -1
  69. package/dist/test/files.js +0 -29
  70. package/dist/test/files.js.map +0 -1
  71. package/docconfig/jsdoc.json +0 -26
  72. package/docconfig/template/LICENSE +0 -61
  73. package/docconfig/template/README.md +0 -86
  74. package/docconfig/template/package.json +0 -22
  75. package/docconfig/template/publish.js +0 -676
  76. package/docconfig/template/static/fonts/OpenSans-Bold-webfont.eot +0 -0
  77. package/docconfig/template/static/fonts/OpenSans-Bold-webfont.svg +0 -1830
  78. package/docconfig/template/static/fonts/OpenSans-Bold-webfont.woff +0 -0
  79. package/docconfig/template/static/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
  80. package/docconfig/template/static/fonts/OpenSans-BoldItalic-webfont.svg +0 -1830
  81. package/docconfig/template/static/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
  82. package/docconfig/template/static/fonts/OpenSans-Italic-webfont.eot +0 -0
  83. package/docconfig/template/static/fonts/OpenSans-Italic-webfont.svg +0 -1830
  84. package/docconfig/template/static/fonts/OpenSans-Italic-webfont.woff +0 -0
  85. package/docconfig/template/static/fonts/OpenSans-Light-webfont.eot +0 -0
  86. package/docconfig/template/static/fonts/OpenSans-Light-webfont.svg +0 -1831
  87. package/docconfig/template/static/fonts/OpenSans-Light-webfont.woff +0 -0
  88. package/docconfig/template/static/fonts/OpenSans-LightItalic-webfont.eot +0 -0
  89. package/docconfig/template/static/fonts/OpenSans-LightItalic-webfont.svg +0 -1835
  90. package/docconfig/template/static/fonts/OpenSans-LightItalic-webfont.woff +0 -0
  91. package/docconfig/template/static/fonts/OpenSans-Regular-webfont.eot +0 -0
  92. package/docconfig/template/static/fonts/OpenSans-Regular-webfont.svg +0 -1831
  93. package/docconfig/template/static/fonts/OpenSans-Regular-webfont.woff +0 -0
  94. package/docconfig/template/static/fonts/OpenSans-Semibold-webfont.eot +0 -0
  95. package/docconfig/template/static/fonts/OpenSans-Semibold-webfont.svg +0 -1830
  96. package/docconfig/template/static/fonts/OpenSans-Semibold-webfont.ttf +0 -0
  97. package/docconfig/template/static/fonts/OpenSans-Semibold-webfont.woff +0 -0
  98. package/docconfig/template/static/fonts/OpenSans-SemiboldItalic-webfont.eot +0 -0
  99. package/docconfig/template/static/fonts/OpenSans-SemiboldItalic-webfont.svg +0 -1830
  100. package/docconfig/template/static/fonts/OpenSans-SemiboldItalic-webfont.ttf +0 -0
  101. package/docconfig/template/static/fonts/OpenSans-SemiboldItalic-webfont.woff +0 -0
  102. package/docconfig/template/static/scripts/linenumber.js +0 -25
  103. package/docconfig/template/static/scripts/prettify/Apache-License-2.0.txt +0 -202
  104. package/docconfig/template/static/scripts/prettify/lang-css.js +0 -2
  105. package/docconfig/template/static/scripts/prettify/prettify.js +0 -28
  106. package/docconfig/template/static/styles/jsdoc-default.css +0 -605
  107. package/docconfig/template/static/styles/prettify-jsdoc.css +0 -111
  108. package/docconfig/template/static/styles/prettify-tomorrow.css +0 -132
  109. package/docconfig/template/tmpl/augments.tmpl +0 -10
  110. package/docconfig/template/tmpl/container.tmpl +0 -183
  111. package/docconfig/template/tmpl/details.tmpl +0 -143
  112. package/docconfig/template/tmpl/example.tmpl +0 -2
  113. package/docconfig/template/tmpl/examples.tmpl +0 -13
  114. package/docconfig/template/tmpl/exceptions.tmpl +0 -32
  115. package/docconfig/template/tmpl/layout.tmpl +0 -47
  116. package/docconfig/template/tmpl/mainpage.tmpl +0 -10
  117. package/docconfig/template/tmpl/members.tmpl +0 -38
  118. package/docconfig/template/tmpl/method.tmpl +0 -105
  119. package/docconfig/template/tmpl/params.tmpl +0 -124
  120. package/docconfig/template/tmpl/properties.tmpl +0 -108
  121. package/docconfig/template/tmpl/returns.tmpl +0 -19
  122. package/docconfig/template/tmpl/source.tmpl +0 -8
  123. package/docconfig/template/tmpl/tutorial.tmpl +0 -19
  124. package/docconfig/template/tmpl/type.tmpl +0 -7
  125. package/docs/FileHound.html +0 -3079
  126. package/docs/FileHound_modified.html +0 -245
  127. package/docs/_config.yml +0 -1
  128. package/docs/filehound.js.html +0 -697
  129. package/docs/fonts/OpenSans-Bold-webfont.eot +0 -0
  130. package/docs/fonts/OpenSans-Bold-webfont.svg +0 -1830
  131. package/docs/fonts/OpenSans-Bold-webfont.woff +0 -0
  132. package/docs/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
  133. package/docs/fonts/OpenSans-BoldItalic-webfont.svg +0 -1830
  134. package/docs/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
  135. package/docs/fonts/OpenSans-Italic-webfont.eot +0 -0
  136. package/docs/fonts/OpenSans-Italic-webfont.svg +0 -1830
  137. package/docs/fonts/OpenSans-Italic-webfont.woff +0 -0
  138. package/docs/fonts/OpenSans-Light-webfont.eot +0 -0
  139. package/docs/fonts/OpenSans-Light-webfont.svg +0 -1831
  140. package/docs/fonts/OpenSans-Light-webfont.woff +0 -0
  141. package/docs/fonts/OpenSans-LightItalic-webfont.eot +0 -0
  142. package/docs/fonts/OpenSans-LightItalic-webfont.svg +0 -1835
  143. package/docs/fonts/OpenSans-LightItalic-webfont.woff +0 -0
  144. package/docs/fonts/OpenSans-Regular-webfont.eot +0 -0
  145. package/docs/fonts/OpenSans-Regular-webfont.svg +0 -1831
  146. package/docs/fonts/OpenSans-Regular-webfont.woff +0 -0
  147. package/docs/fonts/OpenSans-Semibold-webfont.eot +0 -0
  148. package/docs/fonts/OpenSans-Semibold-webfont.svg +0 -1830
  149. package/docs/fonts/OpenSans-Semibold-webfont.ttf +0 -0
  150. package/docs/fonts/OpenSans-Semibold-webfont.woff +0 -0
  151. package/docs/fonts/OpenSans-SemiboldItalic-webfont.eot +0 -0
  152. package/docs/fonts/OpenSans-SemiboldItalic-webfont.svg +0 -1830
  153. package/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf +0 -0
  154. package/docs/fonts/OpenSans-SemiboldItalic-webfont.woff +0 -0
  155. package/docs/global.html +0 -288
  156. package/docs/index.html +0 -154
  157. package/docs/scripts/linenumber.js +0 -25
  158. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -202
  159. package/docs/scripts/prettify/lang-css.js +0 -2
  160. package/docs/scripts/prettify/prettify.js +0 -28
  161. package/docs/styles/jsdoc-default.css +0 -605
  162. package/docs/styles/prettify-jsdoc.css +0 -111
  163. package/docs/styles/prettify-tomorrow.css +0 -132
  164. package/docs.md +0 -164
  165. package/index.js +0 -1
  166. package/issue_template.md +0 -6
  167. package/test/filehound.ts +0 -1076
  168. package/test/files.ts +0 -29
  169. package/test/fixtures/custom/failed.txt +0 -0
  170. package/test/fixtures/custom/passed.txt +0 -0
  171. package/test/fixtures/deeplyNested/c.json +0 -0
  172. package/test/fixtures/deeplyNested/d.json +0 -0
  173. package/test/fixtures/deeplyNested/mydir/e.json +0 -0
  174. package/test/fixtures/deeplyNested/mydir/mydir2/f.json +0 -0
  175. package/test/fixtures/deeplyNested/mydir/mydir2/mydir3/mydir4/failed.json +0 -0
  176. package/test/fixtures/deeplyNested/mydir/mydir2/mydir3/z.json +0 -0
  177. package/test/fixtures/deeplyNested/mydir/mydir2/y.json +0 -0
  178. package/test/fixtures/deeplyNestedWithHiddenDir/c.json +0 -0
  179. package/test/fixtures/deeplyNestedWithHiddenDir/d.json +0 -0
  180. package/test/fixtures/deeplyNestedWithHiddenDir/mydir/e.json +0 -0
  181. package/test/fixtures/deeplyNestedWithHiddenDir/mydir/mydir2/f.json +0 -0
  182. package/test/fixtures/deeplyNestedWithHiddenDir/mydir/mydir2/mydir3/mydir4/.mydir5/.keep +0 -0
  183. package/test/fixtures/deeplyNestedWithHiddenDir/mydir/mydir2/mydir3/mydir4/failed.json +0 -0
  184. package/test/fixtures/deeplyNestedWithHiddenDir/mydir/mydir2/mydir3/z.json +0 -0
  185. package/test/fixtures/deeplyNestedWithHiddenDir/mydir/mydir2/y.json +0 -0
  186. package/test/fixtures/ext/Dummy.java +0 -0
  187. package/test/fixtures/ext/Dumpy.go +0 -0
  188. package/test/fixtures/ext/dummy.json +0 -0
  189. package/test/fixtures/ext/dummy.txt +0 -0
  190. package/test/fixtures/justFiles/a.json +0 -0
  191. package/test/fixtures/justFiles/b.json +0 -0
  192. package/test/fixtures/justFiles/dummy.txt +0 -0
  193. package/test/fixtures/mixed/a.json +0 -0
  194. package/test/fixtures/mixed/aabbcc.json +0 -0
  195. package/test/fixtures/mixed/ab.json +0 -0
  196. package/test/fixtures/mixed/z.json +0 -0
  197. package/test/fixtures/nested/.hidden1/bad.txt +0 -0
  198. package/test/fixtures/nested/c.json +0 -0
  199. package/test/fixtures/nested/d.json +0 -0
  200. package/test/fixtures/nested/mydir/e.json +0 -0
  201. package/test/fixtures/sizes/10b.txt +0 -0
  202. package/test/fixtures/sizes/1b.txt +0 -0
  203. package/test/fixtures/sizes/1k.txt +0 -0
  204. package/test/fixtures/sizes/2k.txt +0 -0
  205. package/test/fixtures/visibility/.hidden/.hidden.json +0 -0
  206. package/test/fixtures/visibility/.hidden/visible.json +0 -3
  207. package/test/fixtures/visibility/.hidden.json +0 -3
  208. package/test/fixtures/visibility/visible/.hidden.json +0 -3
  209. package/test/fixtures/visibility/visible.json +0 -3
  210. package/test/mocha.opts +0 -4
  211. package/tsconfig.json +0 -16
  212. package/tslint.json +0 -44
package/README.md CHANGED
@@ -1,44 +1,68 @@
1
1
  # Filehound
2
2
 
3
- ![npm](https://img.shields.io/npm/v/filehound.svg)
4
- [![Build Status](https://travis-ci.org/nspragg/filehound.svg)](https://travis-ci.org/nspragg/filehound) [![Coverage Status](https://coveralls.io/repos/github/nspragg/filehound/badge.svg?branch=master)](https://coveralls.io/github/nspragg/filehound?branch=master)
3
+ [![NPM downloads](https://img.shields.io/npm/dm/filehound.svg?style=flat)](https://npmjs.org/package/filehound)
4
+ [![CI](https://github.com/nspragg/filehound/actions/workflows/ci.yml/badge.svg)](https://github.com/nspragg/filehound/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/github/nspragg/filehound/badge.svg?branch=master)](https://coveralls.io/github/nspragg/filehound?branch=master)
5
5
  ![license](https://img.shields.io/badge/license-MIT-blue.svg)
6
6
  ![github-issues](https://img.shields.io/github/issues/nspragg/filehound.svg)
7
7
  ![stars](https://img.shields.io/github/stars/nspragg/filehound.svg)
8
8
  ![forks](https://img.shields.io/github/forks/nspragg/filehound.svg)
9
9
  > Flexible and fluent interface for searching the file system
10
10
 
11
- ![nodei.co](https://nodei.co/npm/filehound.png?downloads=true&downloadRank=true&stars=true)
12
-
13
-
14
11
  ## Installation
15
12
 
16
13
  ```
17
14
  npm install --save filehound
18
15
  ```
19
16
 
20
- ## Demo
17
+ Requires Node.js 24 or later.
21
18
 
22
- <img src="https://cloud.githubusercontent.com/assets/917111/13683231/7e915c2c-e6fd-11e5-9d58-e7228cf76ccf.gif" width="600"/>
19
+ Upgrading from V1? See the [migration guide](./MIGRATING.md).
23
20
 
24
21
  ## Usage
25
22
 
26
23
  The example below prints all of the files in a directory that have the `.json` file extension:
27
24
 
28
25
  ```js
29
- const FileHound = require('filehound');
26
+ import { FileHound, ext } from 'filehound';
30
27
 
31
- const files = FileHound.create()
28
+ const files = await FileHound.create()
32
29
  .paths('/some/dir')
33
- .ext('json')
30
+ .match(ext('json'))
34
31
  .find();
35
32
 
36
- files.then(console.log);
33
+ console.log(files);
34
+ ```
35
+
36
+ ### Iterating over matches
37
+
38
+ `find()` waits for the whole search to finish. To handle each match as it's found, loop over `iterate()` instead. Matches come in the same order as `find()`, and the search only runs a little ahead of the loop, so a large search doesn't hold every match in memory:
39
+
40
+ ```js
41
+ import { FileHound, ext } from 'filehound';
42
+
43
+ const query = FileHound.create()
44
+ .paths('/some/dir')
45
+ .match(ext('json'));
46
+
47
+ for await (const file of query.iterate()) {
48
+ console.log(file);
49
+ }
50
+ ```
51
+
52
+ Leaving the loop early stops the search, for example to find the first match only:
53
+
54
+ ```js
55
+ for await (const file of query.iterate()) {
56
+ console.log(`First match: ${file}`);
57
+ break;
58
+ }
37
59
  ```
38
60
 
39
61
  ## Documentation
40
62
  For more examples and API details, see [API documentation](https://nspragg.github.io/filehound/)
41
63
 
64
+ Runnable examples are in the [examples](examples/) directory. Each one searches the current directory, for example `node by-extension.mts`.
65
+
42
66
  ## Test
43
67
 
44
68
  ```
@@ -53,3 +77,7 @@ npm run coverage
53
77
  ## Contributing
54
78
 
55
79
  See [contributing guide](./CONTRIBUTING.md)
80
+
81
+ ## Related modules
82
+
83
+ - Finding files by content - [FileSniffer](https://github.com/nspragg/filesniffer)
@@ -0,0 +1 @@
1
+ export declare function from<T>(args: (T | T[])[]): T[];
package/dist/arrays.js ADDED
@@ -0,0 +1,4 @@
1
+ export function from(args) {
2
+ const [first] = args;
3
+ return Array.isArray(first) ? first : args;
4
+ }
package/dist/file.d.ts ADDED
@@ -0,0 +1,108 @@
1
+ import { type Dirent, type Stats } from 'node:fs';
2
+ export type FileKind = Pick<Stats, 'isDirectory' | 'isFile'>;
3
+ /**
4
+ * A read-only view of a file found by a search, as passed to predicates such
5
+ * as {@link applyFilter}. It has no methods that change the file system.
6
+ *
7
+ * @remarks
8
+ * Predicates may be called ahead of the matches being returned, at the same
9
+ * time as each other, and on files that are never returned, for example when
10
+ * a loop over {@link FileHound.iterate} is left early. So a predicate should
11
+ * have no side effects.
12
+ *
13
+ * A file's stats are fetched once, then shared by every predicate and by
14
+ * {@link FileHound.includeFileStats}.
15
+ */
16
+ export interface FileView {
17
+ /** The path, as found from the search path */
18
+ getName(): string;
19
+ /** The absolute path */
20
+ getAbsolutePath(): string;
21
+ /** The absolute path, normalised */
22
+ getCanonicalPath(): string;
23
+ /** The path relative to the search path */
24
+ getRelativePath(): string;
25
+ /** The extension, without the leading dot, or an empty string */
26
+ getPathExtension(): string;
27
+ /** Whether the path, relative to the search path, matches a glob pattern */
28
+ isMatch(globPattern: string): boolean;
29
+ /** Whether this is a directory, or a link to one */
30
+ isDirectory(): Promise<boolean>;
31
+ /** Whether this is a directory, or a link to one */
32
+ isDirectorySync(): boolean;
33
+ /** Whether this is a regular file, or a link to one */
34
+ isFile(): Promise<boolean>;
35
+ /** Whether this is a regular file, or a link to one */
36
+ isFileSync(): boolean;
37
+ /** Whether this is a symbolic link */
38
+ isSymbolicLink(): Promise<boolean>;
39
+ /** Whether this is a symbolic link */
40
+ isSymbolicLinkSync(): boolean;
41
+ /** Whether this is hidden: its name, or for a directory any in its path, begins with a dot */
42
+ isHidden(): Promise<boolean>;
43
+ /** Whether this is hidden: its name, or for a directory any in its path, begins with a dot */
44
+ isHiddenSync(): boolean;
45
+ /** The stats, as returned by `fs.stat` */
46
+ getStats(): Promise<Stats>;
47
+ /** The stats, as returned by `fs.statSync` */
48
+ getStatsSync(): Stats;
49
+ /** The size in bytes */
50
+ size(): Promise<number>;
51
+ /** The size in bytes */
52
+ sizeSync(): number;
53
+ /** When the contents were last modified */
54
+ lastModified(): Promise<Date>;
55
+ /** When the contents were last modified */
56
+ lastModifiedSync(): Date;
57
+ /** When the file was last accessed */
58
+ lastAccessed(): Promise<Date>;
59
+ /** When the file was last accessed */
60
+ lastAccessedSync(): Date;
61
+ /** When the file's status, such as its permissions, last changed */
62
+ lastChanged(): Promise<Date>;
63
+ /** When the file's status, such as its permissions, last changed */
64
+ lastChangedSync(): Date;
65
+ /** Whether the file exists and can be read */
66
+ exists(): Promise<boolean>;
67
+ /** Whether the file can be read */
68
+ isReadable(): Promise<boolean>;
69
+ /** Whether the file can be written to */
70
+ isWritable(): Promise<boolean>;
71
+ /** Whether the file can be executed */
72
+ isExecutable(): Promise<boolean>;
73
+ }
74
+ export declare function getKnownKind(file: File): FileKind | undefined;
75
+ export declare function getChildren(dir: File): Promise<File[]>;
76
+ export declare function getChildrenSync(dir: File): File[];
77
+ export declare class File implements FileView {
78
+ #private;
79
+ constructor(pathname: string, dirent?: Dirent, root?: string);
80
+ isDirectorySync(): boolean;
81
+ isFileSync(): boolean;
82
+ isDirectory(): Promise<boolean>;
83
+ isFile(): Promise<boolean>;
84
+ isSymbolicLinkSync(): boolean;
85
+ isSymbolicLink(): Promise<boolean>;
86
+ isHiddenSync(): boolean;
87
+ isHidden(): Promise<boolean>;
88
+ getName(): string;
89
+ getAbsolutePath(): string;
90
+ getCanonicalPath(): string;
91
+ getRelativePath(): string;
92
+ getPathExtension(): string;
93
+ lastModifiedSync(): Date;
94
+ lastAccessedSync(): Date;
95
+ lastChangedSync(): Date;
96
+ sizeSync(): number;
97
+ lastModified(): Promise<Date>;
98
+ lastAccessed(): Promise<Date>;
99
+ lastChanged(): Promise<Date>;
100
+ size(): Promise<number>;
101
+ getStatsSync(): Stats;
102
+ getStats(): Promise<Stats>;
103
+ isWritable(): Promise<boolean>;
104
+ isReadable(): Promise<boolean>;
105
+ isExecutable(): Promise<boolean>;
106
+ exists(): Promise<boolean>;
107
+ isMatch(globPattern: string): boolean;
108
+ }
package/dist/file.js ADDED
@@ -0,0 +1,184 @@
1
+ var _a;
2
+ import { constants, lstatSync, readdirSync, statSync } from 'node:fs';
3
+ import * as path from 'node:path';
4
+ import * as fsp from 'node:fs/promises';
5
+ // What only the walker needs. These are functions rather than methods, so a
6
+ // predicate can't reach them. Only code inside File can see its private
7
+ // fields, so they're set from its static block.
8
+ let knownKindOf;
9
+ let childrenOf;
10
+ let childrenOfSync;
11
+ // The kind readdir gave, so needs no I/O. Undefined for links, whose targets
12
+ // are reported, and for files made without a dirent, such as a walk's root.
13
+ export function getKnownKind(file) {
14
+ return knownKindOf(file);
15
+ }
16
+ // A directory's entries, with their kinds from readdir
17
+ export function getChildren(dir) {
18
+ return childrenOf(dir);
19
+ }
20
+ export function getChildrenSync(dir) {
21
+ return childrenOfSync(dir);
22
+ }
23
+ export class File {
24
+ #dir;
25
+ #pathname;
26
+ // Type info from readdir, saving a stat call
27
+ #dirent;
28
+ // The directory a search started from; glob patterns are matched relative to it
29
+ #root;
30
+ // Fetched once, so predicates and results share a single stat call
31
+ #stats;
32
+ static {
33
+ knownKindOf = file => file.#dirent?.isSymbolicLink() ? undefined : file.#dirent;
34
+ childrenOf = async (dir) => dir.#children();
35
+ childrenOfSync = dir => dir.#childrenSync();
36
+ }
37
+ constructor(pathname, dirent, root) {
38
+ this.#dir = process.cwd();
39
+ this.#pathname = pathname;
40
+ this.#dirent = dirent;
41
+ this.#root = root;
42
+ }
43
+ isDirectorySync() {
44
+ return this.#getKindSync().isDirectory();
45
+ }
46
+ isFileSync() {
47
+ return this.#getKindSync().isFile();
48
+ }
49
+ async isDirectory() {
50
+ return (await this.#getKind()).isDirectory();
51
+ }
52
+ async isFile() {
53
+ return (await this.#getKind()).isFile();
54
+ }
55
+ isSymbolicLinkSync() {
56
+ return this.#dirent?.isSymbolicLink() ?? lstatSync(this.#pathname).isSymbolicLink();
57
+ }
58
+ async isSymbolicLink() {
59
+ return this.#dirent?.isSymbolicLink() ?? (await fsp.lstat(this.#pathname)).isSymbolicLink();
60
+ }
61
+ isHiddenSync() {
62
+ if (!this.isDirectorySync()) {
63
+ return this.#isHiddenFile();
64
+ }
65
+ return this.#isHiddenDirectory();
66
+ }
67
+ async isHidden() {
68
+ if (!await this.isDirectory()) {
69
+ return this.#isHiddenFile();
70
+ }
71
+ return this.#isHiddenDirectory();
72
+ }
73
+ getName() {
74
+ return this.#pathname;
75
+ }
76
+ getAbsolutePath() {
77
+ return path.resolve(this.#dir, this.#pathname);
78
+ }
79
+ getCanonicalPath() {
80
+ return path.normalize(this.getAbsolutePath());
81
+ }
82
+ // Relative to the search root, or the working directory like fs.glob
83
+ getRelativePath() {
84
+ return path.relative(this.#root ?? this.#dir, this.#pathname);
85
+ }
86
+ getPathExtension() {
87
+ return path.extname(this.#pathname).substring(1);
88
+ }
89
+ lastModifiedSync() {
90
+ return this.getStatsSync().mtime;
91
+ }
92
+ lastAccessedSync() {
93
+ return this.getStatsSync().atime;
94
+ }
95
+ lastChangedSync() {
96
+ return this.getStatsSync().ctime;
97
+ }
98
+ sizeSync() {
99
+ return this.getStatsSync().size;
100
+ }
101
+ async lastModified() {
102
+ return (await this.getStats()).mtime;
103
+ }
104
+ async lastAccessed() {
105
+ return (await this.getStats()).atime;
106
+ }
107
+ async lastChanged() {
108
+ return (await this.getStats()).ctime;
109
+ }
110
+ async size() {
111
+ return (await this.getStats()).size;
112
+ }
113
+ getStatsSync() {
114
+ this.#stats ??= statSync(this.#pathname);
115
+ return this.#stats;
116
+ }
117
+ async getStats() {
118
+ this.#stats ??= await fsp.stat(this.#pathname);
119
+ return this.#stats;
120
+ }
121
+ async isWritable() {
122
+ return this.#access(constants.W_OK);
123
+ }
124
+ async isReadable() {
125
+ return this.#access(constants.R_OK);
126
+ }
127
+ async isExecutable() {
128
+ return this.#access(constants.X_OK);
129
+ }
130
+ async exists() {
131
+ return this.#access(constants.R_OK);
132
+ }
133
+ isMatch(globPattern) {
134
+ return path.matchesGlob(this.getRelativePath(), globPattern);
135
+ }
136
+ async #children() {
137
+ if (!await this.isDirectory()) {
138
+ return [];
139
+ }
140
+ const prefix = this.#childPrefix();
141
+ return (await fsp.readdir(this.#pathname, { withFileTypes: true })).map(entry => this.#child(prefix, entry));
142
+ }
143
+ #childrenSync() {
144
+ if (!this.isDirectorySync()) {
145
+ return [];
146
+ }
147
+ const prefix = this.#childPrefix();
148
+ return readdirSync(this.#pathname, { withFileTypes: true }).map(entry => this.#child(prefix, entry));
149
+ }
150
+ // path.join is the costliest step of a walk, so it runs once per directory
151
+ // rather than once per entry. Entry names never contain a separator, so
152
+ // prefix + name is exactly what path.join would give.
153
+ #childPrefix() {
154
+ return path.join(this.#pathname, '_').slice(0, -1);
155
+ }
156
+ #child(prefix, entry) {
157
+ return new _a(prefix + entry.name, entry, this.#root ?? this.#pathname);
158
+ }
159
+ #getKindSync() {
160
+ return knownKindOf(this) ?? this.getStatsSync();
161
+ }
162
+ async #getKind() {
163
+ return knownKindOf(this) ?? this.getStats();
164
+ }
165
+ #isHiddenFile() {
166
+ return (/^\./).test(path.basename(this.#pathname));
167
+ }
168
+ // Normalising first means either separator is handled on Windows
169
+ #isHiddenDirectory() {
170
+ return path.normalize(this.#pathname)
171
+ .split(path.sep)
172
+ .some(segment => segment.startsWith('.') && segment !== '.' && segment !== '..');
173
+ }
174
+ async #access(permission) {
175
+ try {
176
+ await fsp.access(this.#pathname, permission);
177
+ }
178
+ catch {
179
+ return false;
180
+ }
181
+ return true;
182
+ }
183
+ }
184
+ _a = File;