nc.fs
Files and folders without try/catch: read and write text or JSON (atomically), copy, move, delete, list, glob, watch, hash.
Paths: absolute paths are used as they are. Paths starting with ./ or ../ start from the file that calls the function, so a script works wherever it's run from. Other relative paths start from the working directory, like Node's fs.
Everyday failures don't throw: you get undefined when something doesn't exist and false when an operation fails.
const { fs } = require("@ix-xs/node-comfort");
import { getEnv } from "@ix-xs/node-comfort/fs";These functions are also available at the top level: nc.getEnv() is nc.fs.getEnv().
nc.writeJSON("./data/settings.json", { theme: "dark" });
const settings = nc.readJSON("./data/settings.json", {});
const sources = nc.glob("src/**\/*.{js,ts}");nc.fs.Functions
nc.fs.getEnv()
getEnv(name: string, fallback?: string): string | undefinedReads an environment variable. For typed and checked values, see nc.env.
Parameters
| Name | Type |
|---|---|
name | string |
fallbackoptional | string |
Returns string | undefined
Example
nc.getEnv("REGION", "eu-west-1");nc.fs.createPath()
createPath(path?: string): stringThe absolute version of a path, whether it exists or not.
Parameters
| Name | Type | Description |
|---|---|---|
pathoptional | string | Defaults to the working directory. |
Returns string
Example
nc.createPath("./config/app.json"); // next to the calling file
nc.createPath("logs/app.log"); // from the working directorync.fs.getFolder()
getFolder(path?: string): string | undefinedThe absolute path of a folder, or undefined if it doesn't exist.
Parameters
| Name | Type | Description |
|---|---|---|
pathoptional | string | Defaults to the working directory. |
Returns string | undefined
Example
if (!nc.getFolder("./uploads")) nc.createFolder("./uploads");nc.fs.getFile()
getFile(path: string): string | undefinedThe absolute path of a file, or undefined if it doesn't exist.
Parameters
| Name | Type |
|---|---|
path | string |
Returns string | undefined
nc.fs.getFoldersIn()
getFoldersIn(path?: string, recursive?: boolean | ListOptions): string[] | undefinedThe folders inside a folder, nested ones included by default. node_modules is skipped.
Parameters
| Name | Type | Description |
|---|---|---|
pathoptional | string | Defaults to the working directory. |
recursiveoptional | boolean | ListOptions | Default: true |
Returns string[] | undefined: Absolute paths, or undefined if the folder doesn't exist.
Example
nc.getFoldersIn("./src"); // everything
nc.getFoldersIn("./src", false); // direct children onlync.fs.getFilesIn()
getFilesIn(path?: string, recursive?: boolean | ListOptions): string[] | undefinedThe files inside a folder, nested ones included by default. node_modules is skipped.
Parameters
| Name | Type | Description |
|---|---|---|
pathoptional | string | Defaults to the working directory. |
recursiveoptional | boolean | ListOptions | Default: true |
Returns string[] | undefined: Absolute paths, or undefined if the folder doesn't exist.
Example
nc.getFilesIn("./src").filter((file) => file.endsWith(".js"));nc.fs.glob()
glob(pattern: string | string[], options?: GlobOptions): string[]Finds paths matching a glob pattern: * (anything but /), ** (any depth), ?, [abc] and {js,ts}.
A pattern starting from the root of the disk searches there, and its results are absolute.
Parameters
| Name | Type | Description |
|---|---|---|
pattern | string | string[] | One or more patterns. |
optionsoptional | GlobOptions |
Returns string[]: Sorted, relative to cwd with / separators, unless the pattern is absolute or absolute is set.
Example
nc.glob("src/**\/*.js"); // ["src/index.js", "src/lib/a.js"]
nc.glob("**\/*.{png,jpg}", { cwd: "./assets", absolute: true });
nc.glob("packages/*", { type: "folders" });
nc.glob(`${folder}/**\/*.json`); // absolute pattern, absolute resultsnc.fs.find()
find(name: string, options?: FindOptions): string | undefinedSearches a folder tree for the first file or folder with this name, closest first.
Parameters
| Name | Type |
|---|---|
name | string |
optionsoptional | FindOptions |
Returns string | undefined: The absolute path.
Example
nc.find("config.json");
nc.find("fixtures", { type: "folder", cwd: "./test" });nc.fs.readFile()2 overloads
readFile(path: string, encoding: "buffer"): Buffer<ArrayBufferLike> | undefined
readFile(path: string, encoding?: BufferEncoding): string | undefinedReads a file as text, or as a Buffer with "buffer".
Parameters
| Name | Type |
|---|---|
path | string |
encoding | "buffer" |
Returns Buffer | undefined: undefined if it can't be read.
Example
const text = nc.readFile("./README.md");
const bytes = nc.readFile("./logo.png", "buffer");nc.fs.readLines()
readLines(path: string): string[] | undefinedReads a text file as lines. A final newline doesn't add an empty line.
Parameters
| Name | Type |
|---|---|
path | string |
Returns string[] | undefined: undefined if it can't be read.
Example
for (const url of nc.readLines("./urls.txt") ?? []) await check(url);nc.fs.readJSON()
readJSON<T = any>(path: string, fallback?: T, options?: ReadJSONOptions): TReads a JSON file. If it's missing or invalid you get fallback, never an error.
Parameters
| Name | Type |
|---|---|
path | string |
fallbackoptional | T |
optionsoptional | ReadJSONOptions |
Returns T
Example
const settings = nc.readJSON("./settings.json", { theme: "light" });
const tsconfig = nc.readJSON("./tsconfig.json", {}, { comments: true });nc.fs.writeFile()
writeFile(path: string, data: string | object | Buffer<ArrayBufferLike> | Uint8Array<ArrayBufferLike>, options?: WriteFileOptions): booleanWrites a file atomically, creating its folders. Objects are written as JSON.
Parameters
| Name | Type |
|---|---|
path | string |
data | string | Buffer | Uint8Array | object |
optionsoptional | WriteFileOptions |
Returns boolean: false if it failed.
Example
nc.writeFile("./dist/index.html", html);
nc.writeFile("./.secrets/token", token, { mode: 0o600 });nc.fs.writeJSON()
writeJSON(path: string, data: unknown, options?: WriteJSONOptions): booleanWrites a value as JSON, atomically, creating folders.
Parameters
| Name | Type |
|---|---|
path | string |
data | unknown |
optionsoptional | WriteJSONOptions |
Returns boolean: false if it failed, for example on a circular structure.
Example
nc.writeJSON("./data/users.json", users);nc.fs.appendFile()
appendFile(path: string, data: string | Buffer<ArrayBufferLike>): booleanAppends to a file, creating it and its folders if needed.
Parameters
| Name | Type |
|---|---|
path | string |
data | string | Buffer |
Returns boolean
Example
nc.appendFile("./logs/audit.log", `${new Date().toISOString()} login ${user}\n`);nc.fs.createFile()
createFile(path: string, force?: boolean, data?: string | object | Buffer<ArrayBufferLike> | null): boolean | undefinedCreates a file and its folders.
Parameters
| Name | Type | Description |
|---|---|---|
path | string | |
forceoptional | boolean | Replace the file if it exists.Default: false |
dataoptional | string | Buffer | object | null | Objects are written as JSON. |
Returns boolean | undefined: false if it already existed or failed.
Example
nc.createFile("./notes.txt", false, "hello"); // only if it doesn't exist
nc.createFile("./data.json", true, { ok: true }); // replace itnc.fs.createFolder()
createFolder(path: string, force?: boolean): boolean | undefinedCreates a folder and its parents.
Parameters
| Name | Type | Description |
|---|---|---|
path | string | |
forceoptional | boolean | Delete and recreate it if it exists.Default: false |
Returns boolean | undefined: false if it already existed or failed.
Example
nc.createFolder("./uploads");
nc.createFolder("./cache", true); // start from an empty foldernc.fs.ensureFolder()
ensureFolder(path: string): stringMakes sure a folder exists and returns its absolute path.
Parameters
| Name | Type |
|---|---|
path | string |
Returns string
Throws Error If it can't be created.
Example
const dir = nc.ensureFolder("./storage/uploads");nc.fs.ensureFile()
ensureFile(path: string): stringMakes sure a file exists (an empty one if needed) and returns its absolute path. An existing file is left as is.
Parameters
| Name | Type |
|---|---|
path | string |
Returns string
Throws Error If it can't be created.
nc.fs.touch()
touch(path: string): booleanUpdates a file's modification time, creating the file if needed, like the Unix touch command.
Parameters
| Name | Type |
|---|---|
path | string |
Returns boolean
nc.fs.deleteFolder()
deleteFolder(path: string): boolean | undefinedDeletes a folder and everything in it.
Parameters
| Name | Type |
|---|---|
path | string |
Returns boolean | undefined: undefined if it doesn't exist.
nc.fs.deleteFile()
deleteFile(path: string): boolean | undefinedDeletes a file.
Parameters
| Name | Type |
|---|---|
path | string |
Returns boolean | undefined: undefined if it doesn't exist.
nc.fs.remove()
remove(path: string): booleanDeletes a file or a folder, whichever it is. Nothing happens if the path doesn't exist.
Parameters
| Name | Type |
|---|---|
path | string |
Returns boolean: true if something was deleted.
Example
nc.remove("./dist");nc.fs.deleteFoldersIn()
deleteFoldersIn(path?: string, filter?: ((folder: string) => boolean) | undefined): number | undefinedDeletes the folders inside a folder that pass a test.
Parameters
| Name | Type | Description |
|---|---|---|
pathoptional | string | Defaults to the working directory. |
filteroptional | (folder: string) => boolean | Gets absolute paths. Defaults to every folder. |
Returns number | undefined: How many were deleted.
Example
nc.deleteFoldersIn("./packages", (folder) => folder.endsWith("dist"));nc.fs.deleteFilesIn()
deleteFilesIn(path?: string, recursive?: boolean, filter?: ((file: string) => boolean) | undefined): number | undefinedDeletes the files inside a folder that pass a test.
Parameters
| Name | Type | Description |
|---|---|---|
pathoptional | string | Defaults to the working directory. |
recursiveoptional | boolean | Default: false |
filteroptional | (file: string) => boolean | Gets absolute paths. Defaults to every file. |
Returns number | undefined: How many were deleted.
Example
nc.deleteFilesIn("./logs", false, (file) => file.endsWith(".log"));nc.fs.emptyFolder()
emptyFolder(path: string): boolean | undefinedDeletes everything inside a folder, keeping the folder.
Parameters
| Name | Type |
|---|---|
path | string |
Returns boolean | undefined: undefined if it doesn't exist.
nc.fs.copy()
copy(source: string, destination: string, options?: CopyOptions): booleanCopies a file or a whole folder, like cp -r.
Parameters
| Name | Type |
|---|---|
source | string |
destination | string |
optionsoptional | CopyOptions |
Returns boolean: false if the source is missing or the copy failed.
Example
nc.copy("./templates", "./dist/templates");
nc.copy("./.env.example", "./.env", { overwrite: false });nc.fs.move()
move(source: string, destination: string, options?: { overwrite?: boolean; } | undefined): booleanMoves or renames a file or folder, across drives too.
Parameters
| Name | Type | Description |
|---|---|---|
source | string | |
destination | string | |
optionsoptional | { overwrite?: boolean } | Replace an existing destination. Defaults to true. |
Returns boolean
Example
nc.move("./report.pdf", "./archive/2024/report.pdf");nc.fs.copyFoldersIn()
copyFoldersIn(options: CopyFoldersOptions): number | undefinedCopies the folders inside a folder into another one. copy() is simpler for most cases.
Parameters
| Name | Type |
|---|---|
options | CopyFoldersOptions |
Returns number | undefined: How many were copied.
nc.fs.copyFolder()
copyFolder(options: CopyFolderOptions): boolean | undefinedCopies a folder. Without recursive or withFiles, only the empty folder is created; copy() copies everything in one call.
Parameters
| Name | Type |
|---|---|
options | CopyFolderOptions |
Returns boolean | undefined
nc.fs.copyFilesIn()
copyFilesIn(options: CopyFilesOptions): number | undefinedCopies the files inside a folder into another one, keeping the tree.
Parameters
| Name | Type |
|---|---|
options | CopyFilesOptions |
Returns number | undefined: How many were copied.
Example
nc.copyFilesIn({ path: "./src", dest: "./dist", filter: (file) => file.endsWith(".css") });nc.fs.copyFile()
copyFile(options: CopyFileOptions): boolean | undefinedCopies one file. If dest ends with /, the file keeps its name inside that folder.
Parameters
| Name | Type |
|---|---|
options | CopyFileOptions |
Returns boolean | undefined
Example
nc.copyFile({ path: "./src/index.js", dest: "./dist/" });nc.fs.moveFoldersIn()
moveFoldersIn(options: CopyFoldersOptions): number | undefinedMoves the folders inside a folder.
Parameters
| Name | Type |
|---|---|
options | CopyFoldersOptions |
Returns number | undefined: How many were moved.
nc.fs.moveFolder()
moveFolder(options: CopyFolderOptions & { path: string; }): boolean | undefinedMoves a folder.
Parameters
| Name | Type |
|---|---|
options | CopyFolderOptions & { path: string } |
Returns boolean | undefined
nc.fs.moveFilesIn()
moveFilesIn(options: CopyFilesOptions): number | undefinedMoves the files inside a folder.
Parameters
| Name | Type |
|---|---|
options | CopyFilesOptions |
Returns number | undefined: How many were moved.
nc.fs.moveFile()
moveFile(options: CopyFileOptions): boolean | undefinedMoves one file.
Parameters
| Name | Type |
|---|---|
options | CopyFileOptions |
Returns boolean | undefined
Example
nc.moveFile({ path: "./logs/app.log", dest: "./logs/archive/" });nc.fs.exists()
exists(path: string): booleanDoes a file or folder exist at this path?
Parameters
| Name | Type |
|---|---|
path | string |
Returns boolean
nc.fs.isFile()
isFile(path: string): booleanIs it an existing file?
Parameters
| Name | Type |
|---|---|
path | string |
Returns boolean
nc.fs.isFolder()
isFolder(path: string): booleanIs it an existing folder?
Parameters
| Name | Type |
|---|---|
path | string |
Returns boolean
nc.fs.stat()
stat(path: string): Stats | undefinedSize, dates and type of a path.
Parameters
| Name | Type |
|---|---|
path | string |
Returns fs.Stats | undefined: undefined if it doesn't exist.
Example
nc.stat("./video.mp4")?.mtime;nc.fs.fileSize()
fileSize(path: string): number | undefinedA file's size in bytes.
Parameters
| Name | Type |
|---|---|
path | string |
Returns number | undefined: undefined if it doesn't exist.
Example
nc.num.formatBytes(nc.fileSize("./backup.zip") ?? 0); // "1.2 GB"nc.fs.folderSize()
folderSize(path: string): number | undefinedThe total size of every file in a folder.
Parameters
| Name | Type |
|---|---|
path | string |
Returns number | undefined: undefined if it doesn't exist.
nc.fs.hashFile()
hashFile(path: string, options?: { algorithm?: string; encoding?: "base64" | "base64url" | "hex"; } | undefined): string | undefinedHashes a file in chunks, so huge files are fine.
Parameters
| Name | Type | Description |
|---|---|---|
path | string | |
optionsoptional | { algorithm?: string, encoding?: "hex" | "base64" | "base64url" } | Defaults to sha256 in hex. |
Returns string | undefined: undefined if it can't be read.
Example
nc.hashFile("./release.zip"); // sha256, hex
nc.hashFile("./release.zip", { algorithm: "md5" });nc.fs.tempFolder()
tempFolder(prefix?: string): stringCreates a new, unique folder in the system's temp directory.
Parameters
| Name | Type | Description |
|---|---|---|
prefixoptional | string | Default: "nc-" |
Returns string
Example
const dir = nc.tempFolder("export-");
try { await exportTo(dir); } finally { nc.remove(dir); }nc.fs.sanitizeFilename()
sanitizeFilename(name: string, options?: { replacement?: string; } | undefined): stringTurns any text into a file name that works on Windows, macOS and Linux: forbidden characters are replaced, reserved names like CON are prefixed, and the length is capped.
Parameters
| Name | Type | Description |
|---|---|---|
name | string | |
optionsoptional | { replacement?: string } | Defaults to "_". |
Returns string
Example
nc.sanitizeFilename('Report: Q1/Q2 "final"?.pdf'); // "Report_ Q1_Q2 _final__.pdf"
nc.sanitizeFilename("CON.txt"); // "_CON.txt"nc.fs.watch()
watch(options?: WatchOptions): Watcher | undefinedWatches a file or folder for changes.
Parameters
| Name | Type |
|---|---|
optionsoptional | WatchOptions |
Returns Watcher | undefined: undefined if the path doesn't exist.
Example
const watcher = nc.watch({ path: "./src", recursive: true, debounce: 100 })
.on("change", (file) => nc.info(`Changed: ${file}`))
.on("rename", (file) => nc.info(`Added or removed: ${file}`));
watcher.stop();Types
Import any of them in TypeScript with import type { CopyFileOptions } from "@ix-xs/node-comfort", or in JavaScript with import("@ix-xs/node-comfort").CopyFileOptions.
CopyFileOptions
Options for copyFile() and moveFile().
| Property | Type | Description |
|---|---|---|
path | string | Source file. |
dest | string | Destination file, or a folder if it ends with /. |
forceoptional | boolean | Delete the destination first. |
CopyFilesOptions
Options for copyFilesIn() and moveFilesIn().
| Property | Type | Description |
|---|---|---|
dest | string | Destination folder. |
pathoptional | string | Source folder. Defaults to the working directory. |
recursiveoptional | boolean | Include files from nested folders, keeping the tree. Defaults to true. |
forceoptional | boolean | Delete existing destination files first. |
filteroptional | ((file: string) => boolean) | Keep a file when it returns true. Gets absolute paths. |
CopyFolderOptions
Options for copyFolder() and moveFolder().
| Property | Type | Description |
|---|---|---|
dest | string | Destination folder. |
pathoptional | string | Source folder. Defaults to the working directory. |
recursiveoptional | boolean | Copy nested folders too. |
withFilesoptional | boolean | Copy files too. copy() does all of this in one call. |
forceoptional | boolean | Delete an existing destination first. |
CopyFoldersOptions
Options for copyFoldersIn() and moveFoldersIn().
| Property | Type | Description |
|---|---|---|
dest | string | Destination folder. |
pathoptional | string | Source folder. Defaults to the working directory. |
recursiveoptional | boolean | Include nested folders. Defaults to true. |
withFilesoptional | boolean | Copy the files in each folder too. |
forceoptional | boolean | Delete an existing destination folder first. |
filteroptional | ((folder: string) => boolean) | Keep a folder when it returns true. Gets absolute paths. |
CopyOptions
Options for copy().
| Property | Type | Description |
|---|---|---|
overwriteoptional | boolean | Replace existing files. Defaults to true. |
filteroptional | ((source: string, destination: string) => boolean) | Return false to skip a file or folder. |
preserveTimestampsoptional | boolean | Keep modification times. |
FindOptions
Options for find().
| Property | Type | Description |
|---|---|---|
cwdoptional | string | Where to start. Defaults to the working directory. |
typeoptional | "file" | "folder" | "any" | Defaults to "file". |
ignoreoptional | string[] | Folder names to skip. Defaults to node_modules and .git. |
GlobOptions
Options for glob().
| Property | Type | Description |
|---|---|---|
cwdoptional | string | Where the pattern starts. Defaults to the working directory. |
absoluteoptional | boolean | Return absolute paths. |
dotoptional | boolean | Let * and ** match names starting with a dot. |
ignoreoptional | string[] | Patterns to leave out. Defaults to node_modules and .git. |
typeoptional | "files" | "folders" | "all" | Defaults to "files". |
ListOptions
Options for getFilesIn() and getFoldersIn().
| Property | Type | Description |
|---|---|---|
recursiveoptional | boolean | Go into nested folders. Defaults to true. |
hiddenoptional | boolean | Include names starting with a dot. Defaults to true. |
ignoreoptional | string[] | Folder names to skip. Defaults to ["node_modules"]. |
ReadJSONOptions
Options for readJSON().
| Property | Type | Description |
|---|---|---|
commentsoptional | boolean | Allow comments and trailing commas, like in tsconfig.json. |
reviveroptional | ((key: string, value: any) => any) | Passed to JSON.parse. |
Watcher
Returned by watch().
| Property | Type | Description |
|---|---|---|
on | <E extends "change" | "rename" | "all">(event: E, callback: E extends "all" ? (event: "rename" | "change", file: string) => void : (file: string) => void) => Watcher | Listens for changes. "all" also gets the event name. |
stop | () => void | Stops watching. |
pause | () => Watcher | Ignores events until resume(). |
resume | () => Watcher |
WatchOptions
Options for watch().
| Property | Type | Description |
|---|---|---|
pathoptional | string | What to watch. Defaults to the working directory. |
recursiveoptional | boolean | Watch nested folders too. |
filteroptional | ((event: "rename" | "change", file: string) => boolean) | Return false to ignore an event. |
debounceoptional | number | Merge events on the same file within this many ms. Editors often save in several steps. |
WriteFileOptions
Options for writeFile().
| Property | Type | Description |
|---|---|---|
atomicoptional | boolean | Write to a temporary file and rename it, so nobody reads a half-written file and a crash can't corrupt it. Defaults to true. |
encodingoptional | BufferEncoding | Defaults to "utf8". |
modeoptional | number | Permissions, like 0o600 for secrets. |
spacesoptional | number | Indentation when writing an object as JSON. Defaults to 2. |
WriteJSONOptions
Options for writeJSON().
| Property | Type | Description |
|---|---|---|
spacesoptional | number | Defaults to 2. |
atomicoptional | boolean | Defaults to true. |
replaceroptional | ((key: string, value: unknown) => unknown) | Passed to JSON.stringify. |