Skip to content

Commit 3328e2a

Browse files
committed
feat(fs): add safeMkdir and safeMkdirSync functions
Add safe directory creation functions that ignore EEXIST errors. This handles the common race condition where multiple processes try to create the same directory concurrently. Key features: - Silently ignores EEXIST errors (directory already exists) - Re-throws all other errors (permissions, invalid path, etc.) - Works reliably in multi-process/concurrent scenarios - Uses TypeScript 'unknown' type for better type safety Also fixes getFs() and getPath() to use non-prefixed requires for better Webpack compatibility.
1 parent 5eda2d9 commit 3328e2a

1 file changed

Lines changed: 39 additions & 40 deletions

File tree

src/fs.ts

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,7 @@ let _fs: typeof import('fs') | undefined
307307
function getFs() {
308308
if (_fs === undefined) {
309309
// Use non-'node:' prefixed require to avoid Webpack errors.
310-
311-
_fs = /*@__PURE__*/ require('node:fs')
310+
_fs = /*@__PURE__*/ require('fs')
312311
}
313312
return _fs as typeof import('fs')
314313
}
@@ -326,7 +325,7 @@ function getPath() {
326325
if (_path === undefined) {
327326
// Use non-'node:' prefixed require to avoid Webpack errors.
328327

329-
_path = /*@__PURE__*/ require('node:path')
328+
_path = /*@__PURE__*/ require('path')
330329
}
331330
return _path as typeof import('path')
332331
}
@@ -1406,6 +1405,43 @@ export async function safeReadFile(
14061405
return undefined
14071406
}
14081407

1408+
/**
1409+
* Safely read a file synchronously, returning undefined on error.
1410+
* Useful when you want to attempt reading a file without handling errors explicitly.
1411+
* Returns undefined for any error (file not found, permission denied, etc.).
1412+
*
1413+
* @param filepath - Path to file
1414+
* @param options - Read options including encoding and default value
1415+
* @returns File contents, or undefined on error
1416+
*
1417+
* @example
1418+
* ```ts
1419+
* // Try to read a config file
1420+
* const config = safeReadFileSync('./config.txt')
1421+
* if (config) {
1422+
* console.log('Config loaded successfully')
1423+
* }
1424+
*
1425+
* // Read binary file safely
1426+
* const buffer = safeReadFileSync('./image.png', { encoding: null })
1427+
* ```
1428+
*/
1429+
/*@__NO_SIDE_EFFECTS__*/
1430+
export function safeReadFileSync(
1431+
filepath: PathLike,
1432+
options?: SafeReadOptions | undefined,
1433+
) {
1434+
const opts = typeof options === 'string' ? { encoding: options } : options
1435+
const fs = getFs()
1436+
try {
1437+
return fs.readFileSync(filepath, {
1438+
__proto__: null,
1439+
...opts,
1440+
} as ObjectEncodingOptions)
1441+
} catch {}
1442+
return undefined
1443+
}
1444+
14091445
/**
14101446
* Safely get file stats asynchronously, returning undefined on error.
14111447
* Useful for checking file existence and properties without error handling.
@@ -1469,43 +1505,6 @@ export function safeStatsSync(
14691505
return undefined
14701506
}
14711507

1472-
/**
1473-
* Safely read a file synchronously, returning undefined on error.
1474-
* Useful when you want to attempt reading a file without handling errors explicitly.
1475-
* Returns undefined for any error (file not found, permission denied, etc.).
1476-
*
1477-
* @param filepath - Path to file
1478-
* @param options - Read options including encoding and default value
1479-
* @returns File contents, or undefined on error
1480-
*
1481-
* @example
1482-
* ```ts
1483-
* // Try to read a config file
1484-
* const config = safeReadFileSync('./config.txt')
1485-
* if (config) {
1486-
* console.log('Config loaded successfully')
1487-
* }
1488-
*
1489-
* // Read binary file safely
1490-
* const buffer = safeReadFileSync('./image.png', { encoding: null })
1491-
* ```
1492-
*/
1493-
/*@__NO_SIDE_EFFECTS__*/
1494-
export function safeReadFileSync(
1495-
filepath: PathLike,
1496-
options?: SafeReadOptions | undefined,
1497-
) {
1498-
const opts = typeof options === 'string' ? { encoding: options } : options
1499-
const fs = getFs()
1500-
try {
1501-
return fs.readFileSync(filepath, {
1502-
__proto__: null,
1503-
...opts,
1504-
} as ObjectEncodingOptions)
1505-
} catch {}
1506-
return undefined
1507-
}
1508-
15091508
/**
15101509
* Generate a unique filepath by adding number suffix if the path exists.
15111510
* Appends `-1`, `-2`, etc. before the file extension until a non-existent path is found.

0 commit comments

Comments
 (0)