Skip to content

Commit 09085b3

Browse files
committed
Add fs methods
1 parent 51d297a commit 09085b3

File tree

2 files changed

+172
-39
lines changed

2 files changed

+172
-39
lines changed

registry/lib/fs.d.ts

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
/// <reference types="node" />
2-
import {
2+
import NPMCliPackageJson from '@npmcli/package-json'
3+
4+
import { Remap } from './objects'
5+
6+
import type { Abortable } from 'node:events'
7+
import type {
8+
BigIntStats,
39
ObjectEncodingOptions,
10+
OpenMode,
411
PathLike,
12+
PathOrFileDescriptor,
513
RmOptions,
6-
WriteFileOptions
14+
StatSyncOptions,
15+
Stats,
16+
WriteFileOptions,
17+
readFile,
18+
readFileSync
719
} from 'node:fs'
8-
9-
import NPMCliPackageJson from '@npmcli/package-json'
10-
11-
import { Remap } from './objects'
20+
import type { FileHandle } from 'node:fs/promises'
1221

1322
declare type BufferEncoding =
1423
| 'ascii'
@@ -29,9 +38,10 @@ declare type IsDirEmptyOptions = {
2938
declare type JsonContent = NPMCliPackageJson.Content
3039
declare type ReadFileOptions =
3140
| Remap<
32-
ObjectEncodingOptions & {
33-
flag?: string | undefined
34-
}
41+
ObjectEncodingOptions &
42+
Abortable & {
43+
flag?: OpenMode | undefined
44+
}
3545
>
3646
| BufferEncoding
3747
| null
@@ -68,6 +78,14 @@ declare const Fs: {
6878
dirname: string,
6979
options?: ReadDirOptions | undefined
7080
) => string[]
81+
readFileBinary(
82+
filepath: PathLike | FileHandle,
83+
options?: ReadFileOptions | undefined
84+
): Promise<Buffer>
85+
readFileUtf8(
86+
filepath: PathLike | FileHandle,
87+
options?: ReadFileOptions | undefined
88+
): Promise<string>
7189
readJson(
7290
filepath: PathLike,
7391
options?: ReadJsonOptions | undefined
@@ -78,6 +96,51 @@ declare const Fs: {
7896
): JsonContent
7997
remove(filepath: PathLike, options?: RmOptions): Promise<void>
8098
removeSync(filepath: PathLike, options?: RmOptions): void
99+
safeReadFile(
100+
filepath: PathLike | FileHandle,
101+
options?: 'utf8' | 'utf-8' | { encoding: 'utf8' | 'utf-8' } | undefined
102+
): Promise<string | undefined>
103+
safeReadFile(
104+
filepath: PathLike | FileHandle,
105+
options?: ReadFileOptions | NodeJS.BufferEncoding | undefined
106+
): Promise<Awaited<ReturnType<typeof readFile>> | undefined>
107+
safeReadFileSync(
108+
filepath: PathOrFileDescriptor,
109+
options?: 'utf8' | 'utf-8' | { encoding: 'utf8' | 'utf-8' } | undefined
110+
): string | undefined
111+
safeReadFileSync(
112+
filepath: PathOrFileDescriptor,
113+
options?:
114+
| {
115+
encoding?: NodeJS.BufferEncoding | undefined
116+
flag?: string | undefined
117+
}
118+
| NodeJS.BufferEncoding
119+
| undefined
120+
): ReturnType<typeof readFileSync> | undefined
121+
safeStatsSync(filepath: PathLike, options?: undefined): Stats | undefined
122+
safeStatsSync(
123+
filepath: PathLike,
124+
options?: StatSyncOptions & {
125+
bigint?: false | undefined
126+
}
127+
): Stats | undefined
128+
safeStatsSync(
129+
filepath: PathLike,
130+
options: StatSyncOptions & {
131+
bigint: true
132+
}
133+
): BigIntStats | undefined
134+
safeStatsSync(
135+
filepath: PathLike,
136+
options: StatSyncOptions & {
137+
bigint: boolean
138+
}
139+
): Stats | BigIntStats | undefined
140+
safeStatsSync(
141+
filepath: PathLike,
142+
options?: StatSyncOptions
143+
): Stats | BigIntStats | undefined
81144
uniqueSync(filepath: PathLike): string
82145
writeJson(
83146
filepath: PathLike,

registry/lib/fs.js

Lines changed: 100 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ function innerReadDirNames(dirents, options) {
9595
return sort ? names.sort(naturalCompare) : names
9696
}
9797

98+
/*@__NO_SIDE_EFFECTS__*/
99+
function isDirSync(filepath) {
100+
const fs = getFs()
101+
return fs.existsSync(filepath) && !!safeStatsSync(filepath)?.isDirectory()
102+
}
103+
98104
/*@__NO_SIDE_EFFECTS__*/
99105
function isDirEmptySync(dirname, options) {
100106
const { ignore = defaultIgnore } = { __proto__: null, ...options }
@@ -148,7 +154,10 @@ async function readDirNames(dirname, options) {
148154
const fs = getFs()
149155
try {
150156
return innerReadDirNames(
151-
await fs.promises.readdir(dirname, { withFileTypes: true }),
157+
await fs.promises.readdir(dirname, {
158+
__proto__: null,
159+
withFileTypes: true
160+
}),
152161
options
153162
)
154163
} catch {}
@@ -160,29 +169,48 @@ function readDirNamesSync(dirname, options) {
160169
const fs = getFs()
161170
try {
162171
return innerReadDirNames(
163-
fs.readdirSync(dirname, { withFileTypes: true }),
172+
fs.readdirSync(dirname, { __proto__: null, withFileTypes: true }),
164173
options
165174
)
166175
} catch {}
167176
return []
168177
}
169178

179+
/*@__NO_SIDE_EFFECTS__*/
180+
async function readFileBinary(filepath, options) {
181+
const fs = getFs()
182+
return await fs.promises.readFile(filepath, {
183+
signal: /*@__PURE__*/ require('./constants/abort-signal'),
184+
...options,
185+
encoding: 'binary'
186+
})
187+
}
188+
189+
/*@__NO_SIDE_EFFECTS__*/
190+
async function readFileUtf8(filepath, options) {
191+
const fs = getFs()
192+
return await fs.promises.readFile(filepath, {
193+
signal: /*@__PURE__*/ require('./constants/abort-signal'),
194+
...options,
195+
encoding: 'utf8'
196+
})
197+
}
198+
170199
/*@__NO_SIDE_EFFECTS__*/
171200
async function readJson(filepath, options) {
172201
if (typeof options === 'string') {
173202
options = { encoding: options }
174203
}
175-
const { reviver, throws, ...fsOptionsRaw } = { __proto__: null, ...options }
176-
const fsOptions = {
177-
__proto__: null,
178-
encoding: 'utf8',
179-
...fsOptionsRaw
180-
}
204+
const { reviver, throws, ...fsOptions } = { __proto__: null, ...options }
181205
const fs = getFs()
182206
const shouldThrow = throws === undefined || !!throws
183207
return parse(
184208
filepath,
185-
await fs.promises.readFile(filepath, fsOptions),
209+
await fs.promises.readFile(filepath, {
210+
__proto__: null,
211+
encoding: 'utf8',
212+
...fsOptions
213+
}),
186214
reviver,
187215
shouldThrow
188216
)
@@ -193,17 +221,16 @@ function readJsonSync(filepath, options) {
193221
if (typeof options === 'string') {
194222
options = { encoding: options }
195223
}
196-
const { reviver, throws, ...fsOptionsRaw } = { __proto__: null, ...options }
197-
const fsOptions = {
198-
__proto__: null,
199-
encoding: 'utf8',
200-
...fsOptionsRaw
201-
}
224+
const { reviver, throws, ...fsOptions } = { __proto__: null, ...options }
202225
const fs = getFs()
203226
const shouldThrow = throws === undefined || !!throws
204227
return parse(
205228
filepath,
206-
fs.readFileSync(filepath, fsOptions),
229+
fs.readFileSync(filepath, {
230+
__proto__: null,
231+
encoding: 'utf8',
232+
...fsOptions
233+
}),
207234
reviver,
208235
shouldThrow
209236
)
@@ -231,6 +258,45 @@ function removeSync(filepath, options) {
231258
})
232259
}
233260

261+
/*@__NO_SIDE_EFFECTS__*/
262+
async function safeReadFile(filepath, options) {
263+
const fs = getFs()
264+
try {
265+
return await fs.promises.readFile(filepath, {
266+
encoding: 'utf8',
267+
signal: /*@__PURE__*/ require('./constants/abort-signal'),
268+
...(typeof options === 'string' ? { encoding: options } : options)
269+
})
270+
} catch {}
271+
return undefined
272+
}
273+
274+
/*@__NO_SIDE_EFFECTS__*/
275+
function safeStatsSync(filepath, options) {
276+
const fs = getFs()
277+
try {
278+
return fs.statSync(filepath, {
279+
__proto__: null,
280+
throwIfNoEntry: false,
281+
...options
282+
})
283+
} catch {}
284+
return undefined
285+
}
286+
287+
/*@__NO_SIDE_EFFECTS__*/
288+
function safeReadFileSync(filepath, options) {
289+
const fs = getFs()
290+
try {
291+
return fs.readFileSync(filepath, {
292+
__proto__: null,
293+
encoding: 'utf8',
294+
...(typeof options === 'string' ? { encoding: options } : options)
295+
})
296+
} catch {}
297+
return undefined
298+
}
299+
234300
/*@__NO_SIDE_EFFECTS__*/
235301
function stringify(
236302
json,
@@ -261,48 +327,52 @@ async function writeJson(filepath, json, options) {
261327
if (typeof options === 'string') {
262328
options = { encoding: options }
263329
}
264-
const { EOL, finalEOL, replacer, spaces, ...fsOptionsRaw } = {
330+
const { EOL, finalEOL, replacer, spaces, ...fsOptions } = {
265331
__proto__: null,
266332
...options
267333
}
268-
const fsOptions = {
269-
__proto__: null,
270-
encoding: 'utf8',
271-
...fsOptionsRaw
272-
}
273334
const fs = getFs()
274335
const str = stringify(json, EOL, finalEOL, replacer, spaces)
275-
await fs.promises.writeFile(filepath, str, fsOptions)
336+
await fs.promises.writeFile(filepath, str, {
337+
__proto__: null,
338+
encoding: 'utf8',
339+
...fsOptions
340+
})
276341
}
277342

278343
/*@__NO_SIDE_EFFECTS__*/
279344
function writeJsonSync(filepath, json, options) {
280345
if (typeof options === 'string') {
281346
options = { encoding: options }
282347
}
283-
const { EOL, finalEOL, replacer, spaces, ...fsOptionsRaw } = {
348+
const { EOL, finalEOL, replacer, spaces, ...fsOptions } = {
284349
__proto__: null,
285350
...options
286351
}
287-
const fsOptions = {
288-
__proto__: null,
289-
encoding: 'utf8',
290-
...fsOptionsRaw
291-
}
292352
const fs = getFs()
293353
const str = stringify(json, EOL, finalEOL, replacer, spaces)
294-
fs.writeFileSync(filepath, str, fsOptions)
354+
fs.writeFileSync(filepath, str, {
355+
__proto__: null,
356+
encoding: 'utf8',
357+
...fsOptions
358+
})
295359
}
296360

297361
module.exports = {
362+
isDirSync,
298363
isDirEmptySync,
299364
isSymLinkSync,
365+
readFileBinary,
366+
readFileUtf8,
300367
readJson,
301368
readJsonSync,
302369
readDirNames,
303370
readDirNamesSync,
304371
remove,
305372
removeSync,
373+
safeReadFile,
374+
safeReadFileSync,
375+
safeStatsSync,
306376
uniqueSync,
307377
writeJson,
308378
writeJsonSync

0 commit comments

Comments
 (0)