-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinspect.ts
More file actions
192 lines (182 loc) · 5.24 KB
/
inspect.ts
File metadata and controls
192 lines (182 loc) · 5.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/**
* @fileoverview Filesystem inspection helpers — `stat` / `lstat`
* wrappers that return `undefined` instead of throwing, and the
* directory / symlink / emptiness predicates layered on top.
*
* Every entry point catches errors and reduces them to a falsy result;
* callers that need the underlying error code should use `node:fs`
* directly.
*/
/* oxlint-disable socket/prefer-exists-sync -- this module IS the stat-wrapper surface; callers use these for type discrimination (isFile/isDirectory) or metadata, not pure existence checks. */
import { defaultIgnore } from '../globs/defaults'
import { getGlobMatcher } from '../globs/matcher'
import { getNodeFs } from '../node/fs'
import { pathLikeToString } from '../paths/normalize'
import type { PathLike, StatSyncOptions } from 'node:fs'
import type { IsDirEmptyOptions } from './types'
/**
* Check if a path is a directory asynchronously.
* Returns `true` for directories, `false` for files or non-existent paths.
*
* @param filepath - Path to check
* @returns `true` if path is a directory, `false` otherwise
*
* @example
* ```ts
* if (await isDir('./src')) {
* console.log('src is a directory')
* }
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export async function isDir(filepath: PathLike) {
return !!(await safeStat(filepath))?.isDirectory()
}
/**
* Check if a directory is empty synchronously.
* A directory is considered empty if it contains no files after applying ignore patterns.
* Uses glob patterns to filter ignored files.
*
* @param dirname - Directory path to check
* @param options - Options including ignore patterns
* @returns `true` if directory is empty (or doesn't exist), `false` otherwise
*
* @example
* ```ts
* // Check if directory is completely empty
* isDirEmptySync('./build')
*
* // Check if directory is empty, ignoring .DS_Store files
* isDirEmptySync('./cache', { ignore: ['.DS_Store'] })
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function isDirEmptySync(
dirname: PathLike,
options?: IsDirEmptyOptions | undefined,
) {
const { ignore = defaultIgnore } = {
__proto__: null,
...options,
} as IsDirEmptyOptions
const fs = getNodeFs()
try {
const files = fs.readdirSync(dirname)
const { length } = files
if (length === 0) {
return true
}
const matcher = getGlobMatcher(
ignore as string[],
{
cwd: pathLikeToString(dirname),
} as { cwd?: string; dot?: boolean; ignore?: string[]; nocase?: boolean },
)
let ignoredCount = 0
for (let i = 0; i < length; i += 1) {
const file = files[i]
if (file && matcher(file)) {
ignoredCount += 1
}
}
return ignoredCount === length
} catch {
// Return false for non-existent paths or other errors.
return false
}
}
/**
* Check if a path is a directory synchronously.
* Returns `true` for directories, `false` for files or non-existent paths.
*
* @param filepath - Path to check
* @returns `true` if path is a directory, `false` otherwise
*
* @example
* ```ts
* if (isDirSync('./src')) {
* console.log('src is a directory')
* }
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function isDirSync(filepath: PathLike) {
return !!safeStatSync(filepath)?.isDirectory()
}
/**
* Check if a path is a symbolic link synchronously.
* Uses `lstat` to check the link itself, not the target.
*
* @param filepath - Path to check
* @returns `true` if path is a symbolic link, `false` otherwise
*
* @example
* ```ts
* if (isSymlinkSync('./my-link')) {
* console.log('Path is a symbolic link')
* }
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function isSymlinkSync(filepath: PathLike) {
const fs = getNodeFs()
try {
return fs.lstatSync(filepath).isSymbolicLink()
} catch {}
return false
}
/**
* Safely get file stats asynchronously, returning undefined on error.
* Useful for checking file existence and properties without error handling.
* Returns undefined for any error (file not found, permission denied, etc.).
*
* @param filepath - Path to check
* @returns Promise resolving to Stats object, or undefined on error
*
* @example
* ```ts
* // Check if file exists and get its stats
* const stats = await safeStat('./file.txt')
* if (stats) {
* console.log('File size:', stats.size)
* console.log('Modified:', stats.mtime)
* }
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export async function safeStat(filepath: PathLike) {
const fs = getNodeFs()
try {
return await fs.promises.stat(filepath)
} catch {}
return undefined
}
/**
* Safely get file stats synchronously, returning undefined on error.
* Useful for checking file existence and properties without error handling.
* Returns undefined for any error (file not found, permission denied, etc.).
*
* @param filepath - Path to check
* @returns Stats object, or undefined on error
*
* @example
* ```ts
* // Check if file exists and get its size
* const stats = safeStatSync('./file.txt')
* if (stats) {
* console.log('File size:', stats.size)
* console.log('Is directory:', stats.isDirectory())
* }
* ```
*/
/*@__NO_SIDE_EFFECTS__*/
export function safeStatSync(filepath: PathLike) {
const fs = getNodeFs()
try {
return fs.statSync(filepath, {
__proto__: null,
throwIfNoEntry: false,
} as StatSyncOptions)
} catch {}
return undefined
}