path-class 0.7.1 → 0.8.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.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # `path-class`
2
+
3
+ A semantic `Path` class for `node` and `bun`. Inspired by `bun`'s [`file(…)`](https://bun.com/docs/runtime/file-io) API, but `node`-compatible.
4
+
5
+ ## Usage examples
6
+
7
+ ````ts cli-help
8
+ import { PrintableShellCommand } from "printable-shell-command";
9
+ import { Path } from "path-class";
10
+
11
+ // Traverse files
12
+ console.log(new Path("foo/bar").parent.join("baz.txt").path);
13
+
14
+ // Functions on files
15
+ console.log(await new Path("./src").readDir());
16
+
17
+ // Read text
18
+ const knownHosts = await Path.homedir.join(".ssh/known_hosts").readText();
19
+ console.log(knownHosts);
20
+
21
+ // Resolve paths
22
+ const distDir = Path.resolve("../dist", import.meta.url);
23
+ console.log(`Building to: ${distDir}`);
24
+
25
+ // Get XDG dirs, read JSON with fallback, write JSON
26
+ const info = Path.xdg.data.join("my-tool/info.json");
27
+ const config: { counter: number } = await info.readJSON({
28
+ fallback: { counter: 0 },
29
+ });
30
+ config.counter++;
31
+ await info.writeJSON(config);
32
+
33
+ // Extensive example: create temp dirs and files, fetch into path, chaining,
34
+ // spawn subprocess, read JSON, and clean up.
35
+ //
36
+ // In this case the GitHub API supports direct file download, and you could
37
+ // unzip in memory. However, the steps are a good illustration of diverse tasks
38
+ // in a typical script.
39
+ const tempDir = await Path.makeTempDir();
40
+ const zipFile = await tempDir.join("file.zip").write(fetch("https://github.com/lgarron/path-class/archive/refs/tags/v0.7.2.zip"));
41
+ await new PrintableShellCommand("unzip", [zipFile]).shellOut({ cwd: tempDir });
42
+ const packageJSON = await tempDir.join("path-class-0.7.2/package.json").readJSON();
43
+ console.log(packageJSON.exports);
44
+ tempDir.rm_rf();
45
+ ````
46
+
47
+ ## Differences from `node` functions
48
+
49
+ This implementation differs from similar functions in `node` in a few ways:
50
+
51
+ - Function names are more clear where possible.
52
+ - Writing to a file creates intermediate directories by default.
53
+ - `.mkdir(…)` creates intermediate directories by default.
54
+ - `.rm_rf(…)` is provided as an explicit function as a convenience.
55
+ - Async `.exists(…)` is implemented. Go knock yourself out with race conditions.
56
+ - `.exists(…)` has options/variants to ensure the existing path is a dir/file.
57
+ - Supported in packages like [`printable-shell-command`](https://github.com/lgarron/printable-shell-command) and [`lockfile-mutex`](https://github.com/lgarron/lockfile-mutex).
@@ -1,5 +1,6 @@
1
- import { cp, lstat, mkdir, rm, stat, symlink, writeFile } from "node:fs/promises";
2
- import type { readDirType, readFileType } from "./modifiedNodeTypes";
1
+ import { cp, mkdir, rm, symlink, writeFile } from "node:fs/promises";
2
+ import type { lstatType, readDirType, readFileType, statType } from "./modifiedNodeTypes";
3
+ type WritableData = Parameters<typeof writeFile>[1] | ReadableStream | Response;
3
4
  export declare class Path {
4
5
  #private;
5
6
  /**
@@ -65,12 +66,22 @@ export declare class Path {
65
66
  rm_rf(options?: Parameters<typeof rm>[1]): Promise<void>;
66
67
  read: typeof readFileType;
67
68
  readText(): Promise<string>;
68
- readJSON<T>(): Promise<T>;
69
+ /**
70
+ * Reads JSON from the given file and parses it. No validation is performed
71
+ * (beyond JSON parsing).
72
+ *
73
+ * An optional `fallback` value can be specified. It will be used if (and only
74
+ * if) the file does not exist.
75
+ *
76
+ */
77
+ readJSON<T = any>(options?: {
78
+ fallback?: T;
79
+ }): Promise<T>;
69
80
  /** Creates intermediate directories if they do not exist.
70
81
  *
71
82
  * Returns the original `Path` (for chaining).
72
83
  */
73
- write(data: Parameters<typeof writeFile>[1], options?: Parameters<typeof writeFile>[2]): Promise<Path>;
84
+ write(data: WritableData | Promise<WritableData>, options?: Parameters<typeof writeFile>[2]): Promise<Path>;
74
85
  /**
75
86
  * If only `data` is provided, this is equivalent to:
76
87
  *
@@ -86,8 +97,8 @@ export declare class Path {
86
97
  readDir: typeof readDirType;
87
98
  /** Returns the destination path. */
88
99
  symlink(target: string | URL | Path, type?: Parameters<typeof symlink>[2]): Promise<Path>;
89
- stat(options?: Parameters<typeof stat>[1]): ReturnType<typeof stat>;
90
- lstat(options?: Parameters<typeof lstat>[1]): ReturnType<typeof lstat>;
100
+ stat: typeof statType;
101
+ lstat: typeof lstatType;
91
102
  static get homedir(): Path;
92
103
  static xdg: {
93
104
  cache: Path;
@@ -122,3 +133,4 @@ export declare class Path {
122
133
  */
123
134
  export declare function stringifyIfPath<T>(value: T | Path): T | string;
124
135
  export declare function mustNotHaveTrailingSlash(path: Path): void;
136
+ export {};
@@ -14,6 +14,7 @@ import {
14
14
  } from "node:fs/promises";
15
15
  import { homedir, tmpdir } from "node:os";
16
16
  import { basename, dirname, extname, join } from "node:path";
17
+ import { Readable } from "node:stream";
17
18
  import { fileURLToPath, pathToFileURL } from "node:url";
18
19
  import {
19
20
  xdgCache,
@@ -22,6 +23,16 @@ import {
22
23
  xdgRuntime,
23
24
  xdgState
24
25
  } from "xdg-basedir";
26
+ async function wrangleWritableData(data) {
27
+ data = await data;
28
+ if (data instanceof Response) {
29
+ data = data.body ? Readable.fromWeb(data.body) : new Uint8Array(0);
30
+ }
31
+ if (data instanceof ReadableStream) {
32
+ data = Readable.fromWeb(data);
33
+ }
34
+ return data;
35
+ }
25
36
  var Path = class _Path {
26
37
  // @ts-expect-error ts(2564): False positive. https://github.com/microsoft/TypeScript/issues/32194
27
38
  #path;
@@ -230,8 +241,24 @@ var Path = class _Path {
230
241
  async readText() {
231
242
  return readFile(this.#path, "utf-8");
232
243
  }
233
- async readJSON() {
234
- return JSON.parse(await this.readText());
244
+ /**
245
+ * Reads JSON from the given file and parses it. No validation is performed
246
+ * (beyond JSON parsing).
247
+ *
248
+ * An optional `fallback` value can be specified. It will be used if (and only
249
+ * if) the file does not exist.
250
+ *
251
+ */
252
+ // biome-ignore lint/suspicious/noExplicitAny: Allow a default of `any` to match `JSON.parse(…)`.
253
+ async readJSON(options) {
254
+ try {
255
+ return JSON.parse(await this.readText());
256
+ } catch (e) {
257
+ if (e.code === "ENOENT" && options && "fallback" in options) {
258
+ return options.fallback;
259
+ }
260
+ throw e;
261
+ }
235
262
  }
236
263
  /** Creates intermediate directories if they do not exist.
237
264
  *
@@ -239,7 +266,7 @@ var Path = class _Path {
239
266
  */
240
267
  async write(data, options) {
241
268
  await this.parent.mkdir();
242
- await writeFile(this.#path, data, options);
269
+ await writeFile(this.#path, await wrangleWritableData(data), options);
243
270
  return this;
244
271
  }
245
272
  /**
@@ -276,15 +303,15 @@ var Path = class _Path {
276
303
  );
277
304
  return targetPath;
278
305
  }
279
- stat(options) {
280
- return stat(this.path, options);
281
- }
306
+ // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.
307
+ stat = (...options) => stat(this.#path, ...options);
282
308
  // I don't think `lstat` is a great name, but it does match the
283
309
  // well-established canonical system call. So in this case we keep the
284
310
  // awkward abbreviation.
285
- lstat(options) {
286
- return lstat(this.path, options);
287
- }
311
+ lstat = (...options) => (
312
+ // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.
313
+ lstat(this.#path, ...options)
314
+ );
288
315
  static get homedir() {
289
316
  return new _Path(homedir());
290
317
  }
@@ -336,4 +363,4 @@ export {
336
363
  stringifyIfPath,
337
364
  mustNotHaveTrailingSlash
338
365
  };
339
- //# sourceMappingURL=chunk-VW3LCF27.js.map
366
+ //# sourceMappingURL=chunk-NONMAGMS.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/Path.ts"],
4
+ "sourcesContent": ["import {\n cp,\n lstat,\n mkdir,\n mkdtemp,\n readdir,\n readFile,\n rename,\n rm,\n stat,\n symlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { basename, dirname, extname, join } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport {\n xdgCache,\n xdgConfig,\n xdgData,\n xdgRuntime,\n xdgState,\n} from \"xdg-basedir\";\nimport type {\n lstatType,\n readDirType,\n readFileType,\n statType,\n} from \"./modifiedNodeTypes\";\n\n// Note that (non-static) functions in this file are defined using `function(\u2026)\n// { \u2026 }` rather than arrow functions, specifically because we want `this` to\n// operate on the `Path` instance.\n\ntype WritableData = Parameters<typeof writeFile>[1] | ReadableStream | Response;\nasync function wrangleWritableData(\n data: WritableData | Promise<WritableData>,\n): Promise<Parameters<typeof writeFile>[1]> {\n data = await data;\n if (data instanceof Response) {\n data = data.body ? Readable.fromWeb(data.body) : new Uint8Array(0);\n }\n if (data instanceof ReadableStream) {\n data = Readable.fromWeb(data);\n }\n return data;\n}\n\nexport class Path {\n // @ts-expect-error ts(2564): False positive. https://github.com/microsoft/TypeScript/issues/32194\n #path: string;\n /**\n * If `path` is a string starting with `file:///`, it will be parsed as a file URL.\n */\n constructor(path: string | URL | Path) {\n this.#setNormalizedPath(Path.#pathlikeToString(path));\n }\n\n /**\n * Similar to `new URL(path, base)`, but accepting and returning `Path` objects.\n * Note that `base` must be one of:\n *\n * - a valid second argument to `new URL(\u2026)`.\n * - a `Path` representing an absolute path.\n *\n */\n static resolve(path: string | URL | Path, base: string | URL | Path): Path {\n const baseURL = (() => {\n if (!(base instanceof Path)) {\n return base;\n }\n if (!base.isAbsolutePath()) {\n throw new Error(\n \"The `base` arg to `Path.resolve(\u2026)` must be an absolute path.\",\n );\n }\n return pathToFileURL(base.#path);\n })();\n return new Path(new URL(Path.#pathlikeToString(path), baseURL));\n }\n\n static #pathlikeToString(path: string | URL | Path): string {\n if (path instanceof Path) {\n return path.#path;\n }\n if (path instanceof URL) {\n return fileURLToPath(path);\n }\n if (typeof path === \"string\") {\n // TODO: allow turning off this heuristic?\n if (path.startsWith(\"file:///\")) {\n return fileURLToPath(path);\n }\n return path;\n }\n throw new Error(\"Invalid path\");\n }\n\n #setNormalizedPath(path: string): void {\n this.#path = join(path);\n }\n\n isAbsolutePath(): boolean {\n return this.#path.startsWith(\"/\");\n }\n\n toFileURL(): URL {\n if (!this.isAbsolutePath()) {\n throw new Error(\n \"Tried to convert to file URL when the path is not absolute.\",\n );\n }\n return pathToFileURL(this.#path);\n }\n\n /**\n * The `Path` can have a trailing slash, indicating that it represents a\n * directory. (If there is no trailing slash, it can represent either a file\n * or a directory.)\n *\n * Some operations will refuse to treat a directory path as a file path. This\n * function identifies such paths.\n */\n hasTrailingSlash(): boolean {\n // TODO: handle Windows semantically\n return this.#path.endsWith(\"/\");\n }\n\n /**\n * Same as `.toString()`, but more concise.\n */\n get path() {\n return this.#path;\n }\n\n toString(): string {\n return this.#path;\n }\n\n /** Constructs a new path by appending the given path segments.\n * This follows `node` semantics for absolute paths: leading slashes in the given descendant segments are ignored.\n */\n join(...segments: (string | Path)[]): Path {\n const segmentStrings = segments.map((segment) =>\n segment instanceof Path ? segment.path : segment,\n );\n return new Path(join(this.#path, ...segmentStrings));\n }\n\n extendBasename(suffix: string): Path {\n const joinedSuffix = join(suffix);\n if (joinedSuffix !== basename(joinedSuffix)) {\n throw new Error(\"Invalid suffix to extend file name.\");\n }\n // TODO: join basename and dirname instead?\n return new Path(this.#path + joinedSuffix);\n }\n\n get parent(): Path {\n return new Path(dirname(this.#path));\n }\n\n // Normally I'd stick with `node`'s name, but I think `.dirname` is a\n // particularly poor name. So we support `.dirname` for discovery but mark it\n // as deprecated, even if it will never be removed.\n /** @deprecated Alias for `.parent`. */\n get dirname(): Path {\n return this.parent;\n }\n\n get basename(): Path {\n return new Path(basename(this.#path));\n }\n\n get extension(): string {\n mustNotHaveTrailingSlash(this);\n return extname(this.#path);\n }\n\n // Normally I'd stick with `node`'s name, but I think `.extname` is a\n // particularly poor name. So we support `.extname` for discovery but mark it\n // as deprecated, even if it will never be removed.\n /** @deprecated Alias for `.extension`. */\n get extname(): string {\n return this.extension;\n }\n\n // TODO: find a neat way to dedup with the sync version?\n async exists(constraints?: {\n mustBe: \"file\" | \"directory\";\n }): Promise<boolean> {\n let stats: Awaited<ReturnType<typeof stat>>;\n try {\n stats = await stat(this.#path);\n // biome-ignore lint/suspicious/noExplicitAny: TypeScript limitation\n } catch (e: any) {\n if (e.code === \"ENOENT\") {\n return false;\n }\n throw e;\n }\n if (!constraints?.mustBe) {\n return true;\n }\n switch (constraints?.mustBe) {\n case \"file\": {\n mustNotHaveTrailingSlash(this);\n if (stats.isFile()) {\n return true;\n }\n throw new Error(`Path exists but is not a file: ${this.#path}`);\n }\n case \"directory\": {\n if (stats.isDirectory()) {\n return true;\n }\n throw new Error(`Path exists but is not a directory: ${this.#path}`);\n }\n default: {\n throw new Error(\"Invalid path type constraint\");\n }\n }\n }\n\n async existsAsFile(): Promise<boolean> {\n return this.exists({ mustBe: \"file\" });\n }\n\n async existsAsDir(): Promise<boolean> {\n return this.exists({ mustBe: \"directory\" });\n }\n\n // I don't think `mkdir` is a great name, but it does match the\n // well-established canonical commandline name. So in this case we keep the\n // awkward abbreviation.\n /** Defaults to `recursive: true`. */\n async mkdir(options?: Parameters<typeof mkdir>[1]): Promise<Path> {\n const optionsObject = (() => {\n if (typeof options === \"string\" || typeof options === \"number\") {\n return { mode: options };\n }\n return options ?? {};\n })();\n await mkdir(this.#path, { recursive: true, ...optionsObject });\n return this;\n }\n\n // TODO: check idempotency semantics when the destination exists and is a folder.\n /** Returns the destination path. */\n async cp(\n destination: string | URL | Path,\n options?: Parameters<typeof cp>[2],\n ): Promise<Path> {\n await cp(this.#path, new Path(destination).#path, options);\n return new Path(destination);\n }\n\n // TODO: check idempotency semantics when the destination exists and is a folder.\n async rename(destination: string | URL | Path): Promise<void> {\n await rename(this.#path, new Path(destination).#path);\n }\n\n /** Create a temporary dir inside the global temp dir for the current user. */\n static async makeTempDir(prefix?: string): Promise<Path> {\n return new Path(\n await mkdtemp(new Path(tmpdir()).join(prefix ?? \"js-temp-\").toString()),\n );\n }\n\n async rm(options?: Parameters<typeof rm>[1]): Promise<void> {\n await rm(this.#path, options);\n }\n\n /**\n * Equivalent to:\n *\n * .rm({ recursive: true, force: true, ...(options ?? {}) })\n *\n */\n async rm_rf(options?: Parameters<typeof rm>[1]): Promise<void> {\n await this.rm({ recursive: true, force: true, ...(options ?? {}) });\n }\n\n read: typeof readFileType = (options) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n readFile(this.#path, options as any) as any;\n\n async readText(): Promise<string> {\n return readFile(this.#path, \"utf-8\");\n }\n\n /**\n * Reads JSON from the given file and parses it. No validation is performed\n * (beyond JSON parsing).\n *\n * An optional `fallback` value can be specified. It will be used if (and only\n * if) the file does not exist.\n *\n */\n\n // biome-ignore lint/suspicious/noExplicitAny: Allow a default of `any` to match `JSON.parse(\u2026)`.\n async readJSON<T = any>(options?: { fallback?: T }): Promise<T> {\n try {\n return JSON.parse(await this.readText());\n } catch (e) {\n if (\n (e as { code?: string }).code === \"ENOENT\" &&\n options &&\n \"fallback\" in options\n ) {\n return options.fallback as T;\n }\n throw e;\n }\n }\n\n /** Creates intermediate directories if they do not exist.\n *\n * Returns the original `Path` (for chaining).\n */\n async write(\n data: WritableData | Promise<WritableData>,\n options?: Parameters<typeof writeFile>[2],\n ): Promise<Path> {\n await this.parent.mkdir();\n await writeFile(this.#path, await wrangleWritableData(data), options);\n return this;\n }\n\n /**\n * If only `data` is provided, this is equivalent to:\n *\n * .write(JSON.stringify(data, null, \" \"));\n *\n * `replacer` and `space` can also be specified, making this equivalent to:\n *\n * .write(JSON.stringify(data, replacer, space));\n *\n * Returns the original `Path` (for chaining).\n */\n async writeJSON<T>(\n data: T,\n replacer: Parameters<typeof JSON.stringify>[1] = null,\n space: Parameters<typeof JSON.stringify>[2] = \" \",\n ): Promise<Path> {\n await this.write(JSON.stringify(data, replacer, space));\n return this;\n }\n\n // Normally we'd add a `@deprecated` alias named `.readdir`, but that would\n // differ only by capitalization of a single non-leading character. This can\n // be a bit confusing, especially when autocompleting. So for this function in\n // particular we don't include an alias.\n readDir: typeof readDirType = (options) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n readdir(this.#path, options as any) as any;\n\n /** Returns the destination path. */\n async symlink(\n target: string | URL | Path,\n type?: Parameters<typeof symlink>[2],\n ): Promise<Path> {\n const targetPath = new Path(target);\n await symlink(\n this.path,\n targetPath.path,\n type as Exclude<Parameters<typeof symlink>[2], undefined>, // \uD83E\uDD37\n );\n return targetPath;\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n stat: typeof statType = (...options) => stat(this.#path, ...options) as any;\n\n // I don't think `lstat` is a great name, but it does match the\n // well-established canonical system call. So in this case we keep the\n // awkward abbreviation.\n lstat: typeof lstatType = (...options) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n lstat(this.#path, ...options) as any;\n\n static get homedir(): Path {\n return new Path(homedir());\n }\n\n static xdg = {\n cache: new Path(xdgCache ?? Path.homedir.join(\".cache\")),\n config: new Path(xdgConfig ?? Path.homedir.join(\".config\")),\n data: new Path(xdgData ?? Path.homedir.join(\".local/share\")),\n state: new Path(xdgState ?? Path.homedir.join(\".local/state\")),\n /**\n * {@link Path.xdg.runtime} does not have a default value. Consider\n * {@link Path.xdg.runtimeWithStateFallback} if you need a fallback but do not have a particular fallback in mind.\n */\n runtime: xdgRuntime ? new Path(xdgRuntime) : undefined,\n runtimeWithStateFallback: xdgRuntime\n ? new Path(xdgRuntime)\n : new Path(xdgState ?? Path.homedir.join(\".local/state\")),\n };\n\n /** Chainable function to print the path. Prints the same as:\n *\n * if (args.length > 0) {\n * console.log(...args);\n * }\n * console.log(this.path);\n *\n */\n // biome-ignore lint/suspicious/noExplicitAny: This is the correct type, based on `console.log(\u2026)`.\n debugPrint(...args: any[]): Path {\n if (args.length > 0) {\n console.log(...args);\n }\n console.log(this.#path);\n return this;\n }\n}\n\n/**\n * This function is useful to serialize any `Path`s in a structure to pass on to\n * functions that do not know about the `Path` class, e.g.\n *\n * function process(args: (string | Path)[]) {\n * const argsAsStrings = args.map(stringifyIfPath);\n * }\n *\n */\nexport function stringifyIfPath<T>(value: T | Path): T | string {\n if (value instanceof Path) {\n return value.toString();\n }\n return value;\n}\n\nexport function mustNotHaveTrailingSlash(path: Path): void {\n if (path.hasTrailingSlash()) {\n throw new Error(\n \"Path ends with a slash, which cannot be treated as a file.\",\n );\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -2,7 +2,7 @@ import {
2
2
  Path,
3
3
  mustNotHaveTrailingSlash,
4
4
  stringifyIfPath
5
- } from "./chunks/chunk-VW3LCF27.js";
5
+ } from "./chunks/chunk-NONMAGMS.js";
6
6
  export {
7
7
  Path,
8
8
  mustNotHaveTrailingSlash,
@@ -1,5 +1,5 @@
1
1
  import type { Abortable } from "node:events";
2
- import type { Dirent, ObjectEncodingOptions, OpenMode } from "node:fs";
2
+ import type { BigIntStats, Dirent, ObjectEncodingOptions, OpenMode, StatOptions, Stats } from "node:fs";
3
3
  export declare function readDirType(options?: (ObjectEncodingOptions & {
4
4
  withFileTypes?: false | undefined;
5
5
  recursive?: boolean | undefined;
@@ -33,3 +33,21 @@ export declare function readFileType(options: ({
33
33
  export declare function readFileType(options?: (ObjectEncodingOptions & Abortable & {
34
34
  flag?: OpenMode | undefined;
35
35
  }) | BufferEncoding | null): Promise<string | Buffer>;
36
+ export declare function lstatType(opts?: StatOptions & {
37
+ bigint?: false | undefined;
38
+ }): Promise<Stats>;
39
+ export declare function lstatType(opts: StatOptions & {
40
+ bigint: true;
41
+ }): Promise<BigIntStats>;
42
+ export declare function lstatType(opts?: StatOptions): Promise<Stats | BigIntStats>;
43
+ /**
44
+ * @since v10.0.0
45
+ * @return Fulfills with the {fs.Stats} object for the given `path`.
46
+ */
47
+ export declare function statType(opts?: StatOptions & {
48
+ bigint?: false | undefined;
49
+ }): Promise<Stats>;
50
+ export declare function statType(opts: StatOptions & {
51
+ bigint: true;
52
+ }): Promise<BigIntStats>;
53
+ export declare function statType(opts?: StatOptions): Promise<Stats | BigIntStats>;
@@ -1,6 +1,6 @@
1
- import { cpSync, lstatSync, mkdirSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
1
+ import { cpSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
2
2
  import "./static";
3
- import type { readDirSyncType, readFileSyncType } from "./modifiedNodeTypes";
3
+ import type { lstatSyncType, readDirSyncType, readFileSyncType, statSyncType } from "./modifiedNodeTypes";
4
4
  declare module "../Path" {
5
5
  interface Path {
6
6
  existsSync(constraints?: {
@@ -15,13 +15,15 @@ declare module "../Path" {
15
15
  rm_rfSync(options?: Parameters<typeof rmSync>[1]): void;
16
16
  readSync: typeof readFileSyncType;
17
17
  readTextSync(): string;
18
- readJSONSync<T>(): T;
18
+ readJSONSync<T>(options?: {
19
+ fallback?: T;
20
+ }): T;
19
21
  writeSync(data: Parameters<typeof writeFileSync>[1], options?: Parameters<typeof writeFileSync>[2] | undefined): Path;
20
22
  writeJSONSync<T>(data: T, replacer?: Parameters<typeof JSON.stringify>[1], space?: Parameters<typeof JSON.stringify>[2]): Path;
21
23
  readDirSync: typeof readDirSyncType;
22
24
  /** Returns the destination path. */
23
25
  symlinkSync(target: string | URL | Path, type?: Parameters<typeof symlinkSync>[2]): Path;
24
- statSync(options?: Parameters<typeof statSync>[1]): ReturnType<typeof statSync>;
25
- lstatSync(options?: Parameters<typeof lstatSync>[1]): ReturnType<typeof lstatSync>;
26
+ statSync: typeof statSyncType;
27
+ lstatSync: typeof lstatSyncType;
26
28
  }
27
29
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Path,
3
3
  mustNotHaveTrailingSlash
4
- } from "../chunks/chunk-VW3LCF27.js";
4
+ } from "../chunks/chunk-NONMAGMS.js";
5
5
 
6
6
  // src/sync/index.ts
7
7
  import {
@@ -92,8 +92,15 @@ Path.prototype.readSync = function() {
92
92
  Path.prototype.readTextSync = function() {
93
93
  return readFileSync(this.path, "utf-8");
94
94
  };
95
- Path.prototype.readJSONSync = function() {
96
- return JSON.parse(this.readTextSync());
95
+ Path.prototype.readJSONSync = function(options) {
96
+ try {
97
+ return JSON.parse(this.readTextSync());
98
+ } catch (e) {
99
+ if (e.code === "ENOENT" && options && "fallback" in options) {
100
+ return options.fallback;
101
+ }
102
+ throw e;
103
+ }
97
104
  };
98
105
  Path.prototype.writeSync = function(data, options) {
99
106
  this.parent.mkdirSync();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/sync/index.ts", "../../../../src/sync/static.ts"],
4
- "sourcesContent": ["import {\n cpSync,\n lstatSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n renameSync,\n rmSync,\n statSync,\n symlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { mustNotHaveTrailingSlash, Path } from \"../Path\";\nimport \"./static\";\nimport type { readDirSyncType, readFileSyncType } from \"./modifiedNodeTypes\";\n\n// Note that (non-static) functions in this file are defined using `function(\u2026)\n// { \u2026 }` rather than arrow functions, specifically because we want `this` to\n// operate on the `Path` instance.\n\ndeclare module \"../Path\" {\n interface Path {\n existsSync(constraints?: { mustBe: \"file\" | \"directory\" }): boolean;\n existsAsFileSync(): boolean;\n existsAsDirSync(): boolean;\n\n mkdirSync(options?: Parameters<typeof mkdirSync>[1]): Path;\n cpSync(\n destination: string | URL | Path,\n options?: Parameters<typeof cpSync>[2],\n ): Path;\n renameSync(destination: string | URL | Path): void;\n\n rmSync(options?: Parameters<typeof rmSync>[1]): void;\n rm_rfSync(options?: Parameters<typeof rmSync>[1]): void;\n\n readSync: typeof readFileSyncType;\n readTextSync(): string;\n readJSONSync<T>(): T;\n\n writeSync(\n data: Parameters<typeof writeFileSync>[1],\n options?: Parameters<typeof writeFileSync>[2] | undefined,\n ): Path;\n writeJSONSync<T>(\n data: T,\n replacer?: Parameters<typeof JSON.stringify>[1],\n space?: Parameters<typeof JSON.stringify>[2],\n ): Path;\n\n readDirSync: typeof readDirSyncType;\n\n /** Returns the destination path. */\n symlinkSync(\n target: string | URL | Path,\n type?: Parameters<typeof symlinkSync>[2],\n ): Path;\n\n statSync(\n options?: Parameters<typeof statSync>[1],\n ): ReturnType<typeof statSync>;\n\n // I don't think `lstat` is a great name, but it does match the\n // well-established canonical system call. So in this case we keep the\n // awkward abbreviation.\n lstatSync(\n options?: Parameters<typeof lstatSync>[1],\n ): ReturnType<typeof lstatSync>;\n }\n}\n\n// TODO: find a neat way to dedup with the async version?\nPath.prototype.existsSync = function (constraints?: {\n mustBe: \"file\" | \"directory\";\n}): boolean {\n let stats: ReturnType<typeof statSync>;\n try {\n stats = statSync(this.path);\n // biome-ignore lint/suspicious/noExplicitAny: TypeScript limitation\n } catch (e: any) {\n if (e.code === \"ENOENT\") {\n return false;\n }\n throw e;\n }\n if (!constraints?.mustBe) {\n return true;\n }\n switch (constraints?.mustBe) {\n case \"file\": {\n mustNotHaveTrailingSlash(this);\n if (stats.isFile()) {\n return true;\n }\n throw new Error(`Path exists but is not a file: ${this.path}`);\n }\n case \"directory\": {\n if (stats.isDirectory()) {\n return true;\n }\n throw new Error(`Path exists but is not a directory: ${this.path}`);\n }\n default: {\n throw new Error(\"Invalid path type constraint\");\n }\n }\n};\n\nPath.prototype.existsAsFileSync = function (): boolean {\n return this.existsSync({ mustBe: \"file\" });\n};\n\nPath.prototype.existsAsDirSync = function (): boolean {\n return this.existsSync({ mustBe: \"directory\" });\n};\n\nPath.prototype.mkdirSync = function (\n options?: Parameters<typeof mkdirSync>[1],\n): Path {\n const optionsObject = (() => {\n if (typeof options === \"string\" || typeof options === \"number\") {\n return { mode: options };\n }\n return options ?? {};\n })();\n mkdirSync(this.path, { recursive: true, ...optionsObject });\n return this;\n};\n\nPath.prototype.cpSync = function (\n destination: string | URL | Path,\n options?: Parameters<typeof cpSync>[2],\n): Path {\n cpSync(this.path, new Path(destination).path, options);\n return new Path(destination);\n};\n\nPath.prototype.renameSync = function (destination: string | URL | Path): void {\n renameSync(this.path, new Path(destination).path);\n};\n\nPath.prototype.rmSync = function (\n options?: Parameters<typeof rmSync>[1],\n): void {\n rmSync(this.path, options);\n};\n\nPath.prototype.rm_rfSync = function (\n options?: Parameters<typeof rmSync>[1],\n): void {\n this.rmSync({ recursive: true, force: true, ...(options ?? {}) });\n};\n\nPath.prototype.readSync = function () {\n /** @ts-expect-error ts(2683) */\n return readFileSync(this.path);\n} as typeof readFileSyncType;\n\nPath.prototype.readTextSync = function (): string {\n return readFileSync(this.path, \"utf-8\");\n};\n\nPath.prototype.readJSONSync = function <T>(): T {\n return JSON.parse(this.readTextSync());\n};\n\nPath.prototype.writeSync = function (\n data: Parameters<typeof writeFileSync>[1],\n options?: Parameters<typeof writeFileSync>[2],\n): Path {\n this.parent.mkdirSync();\n writeFileSync(this.path, data, options);\n return this;\n};\n\nPath.prototype.writeJSONSync = function <T>(\n data: T,\n replacer: Parameters<typeof JSON.stringify>[1] = null,\n space: Parameters<typeof JSON.stringify>[2] = \" \",\n): Path {\n this.parent.mkdirSync();\n this.writeSync(JSON.stringify(data, replacer, space));\n return this;\n};\n\n/** @ts-expect-error ts(2322): Wrangle types */\nPath.prototype.readDirSync = function (options) {\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n return readdirSync(this.path, options as any);\n};\n\nPath.prototype.symlinkSync = function symlink(\n target: string | URL | Path,\n type?: Parameters<typeof symlinkSync>[2],\n): Path {\n const targetPath = new Path(target);\n symlinkSync(\n this.path,\n targetPath.path,\n type as Exclude<Parameters<typeof symlinkSync>[2], undefined>, // \uD83E\uDD37\n );\n return targetPath;\n};\n\nPath.prototype.statSync = function (\n options?: Parameters<typeof statSync>[1],\n): ReturnType<typeof statSync> {\n return statSync(this.path, options);\n};\n\nPath.prototype.lstatSync = function (\n options?: Parameters<typeof lstatSync>[1],\n): ReturnType<typeof lstatSync> {\n return lstatSync(this.path, options);\n};\n", "import { mkdtempSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { Path } from \"../Path\";\n\ndeclare module \"../Path\" {\n namespace Path {\n export function makeTempDirSync(prefix?: string): Path;\n }\n}\n\nPath.makeTempDirSync = (prefix?: string): Path =>\n new Path(\n mkdtempSync(new Path(tmpdir()).join(prefix ?? \"js-temp-\").toString()),\n );\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n cpSync,\n lstatSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n renameSync,\n rmSync,\n statSync,\n symlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { mustNotHaveTrailingSlash, Path } from \"../Path\";\nimport \"./static\";\nimport type {\n lstatSyncType,\n readDirSyncType,\n readFileSyncType,\n statSyncType,\n} from \"./modifiedNodeTypes\";\n\n// Note that (non-static) functions in this file are defined using `function(\u2026)\n// { \u2026 }` rather than arrow functions, specifically because we want `this` to\n// operate on the `Path` instance.\n\ndeclare module \"../Path\" {\n interface Path {\n existsSync(constraints?: { mustBe: \"file\" | \"directory\" }): boolean;\n existsAsFileSync(): boolean;\n existsAsDirSync(): boolean;\n\n mkdirSync(options?: Parameters<typeof mkdirSync>[1]): Path;\n cpSync(\n destination: string | URL | Path,\n options?: Parameters<typeof cpSync>[2],\n ): Path;\n renameSync(destination: string | URL | Path): void;\n\n rmSync(options?: Parameters<typeof rmSync>[1]): void;\n rm_rfSync(options?: Parameters<typeof rmSync>[1]): void;\n\n readSync: typeof readFileSyncType;\n readTextSync(): string;\n readJSONSync<T>(options?: { fallback?: T }): T;\n\n writeSync(\n data: Parameters<typeof writeFileSync>[1],\n options?: Parameters<typeof writeFileSync>[2] | undefined,\n ): Path;\n writeJSONSync<T>(\n data: T,\n replacer?: Parameters<typeof JSON.stringify>[1],\n space?: Parameters<typeof JSON.stringify>[2],\n ): Path;\n\n readDirSync: typeof readDirSyncType;\n\n /** Returns the destination path. */\n symlinkSync(\n target: string | URL | Path,\n type?: Parameters<typeof symlinkSync>[2],\n ): Path;\n\n statSync: typeof statSyncType;\n lstatSync: typeof lstatSyncType;\n }\n}\n\n// TODO: find a neat way to dedup with the async version?\nPath.prototype.existsSync = function (constraints?: {\n mustBe: \"file\" | \"directory\";\n}): boolean {\n let stats: ReturnType<typeof statSync>;\n try {\n stats = statSync(this.path);\n // biome-ignore lint/suspicious/noExplicitAny: TypeScript limitation\n } catch (e: any) {\n if (e.code === \"ENOENT\") {\n return false;\n }\n throw e;\n }\n if (!constraints?.mustBe) {\n return true;\n }\n switch (constraints?.mustBe) {\n case \"file\": {\n mustNotHaveTrailingSlash(this);\n if (stats.isFile()) {\n return true;\n }\n throw new Error(`Path exists but is not a file: ${this.path}`);\n }\n case \"directory\": {\n if (stats.isDirectory()) {\n return true;\n }\n throw new Error(`Path exists but is not a directory: ${this.path}`);\n }\n default: {\n throw new Error(\"Invalid path type constraint\");\n }\n }\n};\n\nPath.prototype.existsAsFileSync = function (): boolean {\n return this.existsSync({ mustBe: \"file\" });\n};\n\nPath.prototype.existsAsDirSync = function (): boolean {\n return this.existsSync({ mustBe: \"directory\" });\n};\n\nPath.prototype.mkdirSync = function (\n options?: Parameters<typeof mkdirSync>[1],\n): Path {\n const optionsObject = (() => {\n if (typeof options === \"string\" || typeof options === \"number\") {\n return { mode: options };\n }\n return options ?? {};\n })();\n mkdirSync(this.path, { recursive: true, ...optionsObject });\n return this;\n};\n\nPath.prototype.cpSync = function (\n destination: string | URL | Path,\n options?: Parameters<typeof cpSync>[2],\n): Path {\n cpSync(this.path, new Path(destination).path, options);\n return new Path(destination);\n};\n\nPath.prototype.renameSync = function (destination: string | URL | Path): void {\n renameSync(this.path, new Path(destination).path);\n};\n\nPath.prototype.rmSync = function (\n options?: Parameters<typeof rmSync>[1],\n): void {\n rmSync(this.path, options);\n};\n\nPath.prototype.rm_rfSync = function (\n options?: Parameters<typeof rmSync>[1],\n): void {\n this.rmSync({ recursive: true, force: true, ...(options ?? {}) });\n};\n\nPath.prototype.readSync = function () {\n /** @ts-expect-error ts(2683) */\n return readFileSync(this.path);\n} as typeof readFileSyncType;\n\nPath.prototype.readTextSync = function (): string {\n return readFileSync(this.path, \"utf-8\");\n};\n\nPath.prototype.readJSONSync = function <T>(options?: { fallback?: T }): T {\n try {\n return JSON.parse(this.readTextSync());\n } catch (e) {\n if (\n (e as { code?: string }).code === \"ENOENT\" &&\n options &&\n \"fallback\" in options\n ) {\n return options.fallback as T;\n }\n throw e;\n }\n};\n\nPath.prototype.writeSync = function (\n data: Parameters<typeof writeFileSync>[1],\n options?: Parameters<typeof writeFileSync>[2],\n): Path {\n this.parent.mkdirSync();\n writeFileSync(this.path, data, options);\n return this;\n};\n\nPath.prototype.writeJSONSync = function <T>(\n data: T,\n replacer: Parameters<typeof JSON.stringify>[1] = null,\n space: Parameters<typeof JSON.stringify>[2] = \" \",\n): Path {\n this.parent.mkdirSync();\n this.writeSync(JSON.stringify(data, replacer, space));\n return this;\n};\n\n/** @ts-expect-error ts(2322): Wrangle types */\nPath.prototype.readDirSync = function (options) {\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n return readdirSync(this.path, options as any);\n};\n\nPath.prototype.symlinkSync = function symlink(\n target: string | URL | Path,\n type?: Parameters<typeof symlinkSync>[2],\n): Path {\n const targetPath = new Path(target);\n symlinkSync(\n this.path,\n targetPath.path,\n type as Exclude<Parameters<typeof symlinkSync>[2], undefined>, // \uD83E\uDD37\n );\n return targetPath;\n};\n\n/** @ts-expect-error ts(2322): Wrangle types */\nPath.prototype.statSync = function (\n options?: Parameters<typeof statSync>[1],\n): ReturnType<typeof statSync> {\n return statSync(this.path, options);\n};\n\n/** @ts-expect-error ts(2322): Wrangle types */\nPath.prototype.lstatSync = function (\n options?: Parameters<typeof lstatSync>[1],\n): ReturnType<typeof lstatSync> {\n return lstatSync(this.path, options);\n};\n", "import { mkdtempSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { Path } from \"../Path\";\n\ndeclare module \"../Path\" {\n namespace Path {\n export function makeTempDirSync(prefix?: string): Path;\n }\n}\n\nPath.makeTempDirSync = (prefix?: string): Path =>\n new Path(\n mkdtempSync(new Path(tmpdir()).join(prefix ?? \"js-temp-\").toString()),\n );\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,4 @@
1
- import type { Dirent, ObjectEncodingOptions } from "node:fs";
1
+ import type { BigIntStats, Dirent, ObjectEncodingOptions, StatSyncOptions, Stats } from "node:fs";
2
2
  export declare function readFileSyncType(options?: {
3
3
  encoding?: null | undefined;
4
4
  flag?: string | undefined;
@@ -33,3 +33,24 @@ export declare function readDirSyncType(options: {
33
33
  withFileTypes: true;
34
34
  recursive?: boolean | undefined;
35
35
  }): Dirent<Buffer>[];
36
+ export declare function statSyncType(options?: undefined): Stats;
37
+ export declare function statSyncType(options?: StatSyncOptions & {
38
+ bigint?: false | undefined;
39
+ throwIfNoEntry: false;
40
+ }): Stats | undefined;
41
+ export declare function statSyncType(options: StatSyncOptions & {
42
+ bigint: true;
43
+ throwIfNoEntry: false;
44
+ }): BigIntStats | undefined;
45
+ export declare function statSyncType(options?: StatSyncOptions & {
46
+ bigint?: false | undefined;
47
+ }): Stats;
48
+ export declare function statSyncType(options: StatSyncOptions & {
49
+ bigint: true;
50
+ }): BigIntStats;
51
+ export declare function statSyncType(options: StatSyncOptions & {
52
+ bigint: boolean;
53
+ throwIfNoEntry?: false | undefined;
54
+ }): Stats | BigIntStats;
55
+ export declare function statSyncType(options?: StatSyncOptions): Stats | BigIntStats | undefined;
56
+ export declare const lstatSyncType: typeof statSyncType;
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "path-class",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "author": "Lucas Garron <code@garron.net>",
5
5
  "type": "module",
6
6
  "main": "./dist/lib/path-class/index.js",
7
7
  "types": "./dist/lib/path-class/index.d.ts",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/lgarron/path-class.git"
11
+ },
8
12
  "exports": {
9
13
  ".": {
10
14
  "types": "./dist/lib/path-class/index.d.ts",
@@ -20,10 +24,12 @@
20
24
  "@cubing/dev-config": "^0.3.6",
21
25
  "@types/bun": "^1.3.0",
22
26
  "esbuild": "^0.25.10",
27
+ "printable-shell-command": "^2.6.3",
23
28
  "typescript": "^5.9.3"
24
29
  },
25
30
  "dependencies": {
26
31
  "@types/node": "^24.7.1",
32
+ "readme-cli-help": "^0.1.10",
27
33
  "xdg-basedir": "^5.1.0"
28
34
  },
29
35
  "files": [
package/src/Path.test.ts CHANGED
@@ -279,6 +279,24 @@ test(".readJSON()", async () => {
279
279
  >({ foo: "bar" });
280
280
  });
281
281
 
282
+ test(".readJSON(…) with fallback", async () => {
283
+ const tempDir = await Path.makeTempDir();
284
+ const file = tempDir.join("file.json");
285
+ const json: { foo?: number } = await file.readJSON({ fallback: { foo: 4 } });
286
+ expect(json).toEqual({ foo: 4 });
287
+
288
+ const file2 = tempDir.join("file2.json");
289
+ await file2.writeJSON({ foo: 6 });
290
+ const json2: { foo?: number } = await file2.readJSON({
291
+ fallback: { foo: 4 },
292
+ });
293
+ expect(json2).toEqual({ foo: 6 });
294
+
295
+ expect(() => tempDir.readJSON({ fallback: { foo: 4 } })).toThrowError(
296
+ /^EISDIR/,
297
+ );
298
+ });
299
+
282
300
  test(".write(…)", async () => {
283
301
  const tempDir = await Path.makeTempDir();
284
302
  const file = tempDir.join("file.json");
package/src/Path.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  } from "node:fs/promises";
14
14
  import { homedir, tmpdir } from "node:os";
15
15
  import { basename, dirname, extname, join } from "node:path";
16
+ import { Readable } from "node:stream";
16
17
  import { fileURLToPath, pathToFileURL } from "node:url";
17
18
  import {
18
19
  xdgCache,
@@ -21,7 +22,30 @@ import {
21
22
  xdgRuntime,
22
23
  xdgState,
23
24
  } from "xdg-basedir";
24
- import type { readDirType, readFileType } from "./modifiedNodeTypes";
25
+ import type {
26
+ lstatType,
27
+ readDirType,
28
+ readFileType,
29
+ statType,
30
+ } from "./modifiedNodeTypes";
31
+
32
+ // Note that (non-static) functions in this file are defined using `function(…)
33
+ // { … }` rather than arrow functions, specifically because we want `this` to
34
+ // operate on the `Path` instance.
35
+
36
+ type WritableData = Parameters<typeof writeFile>[1] | ReadableStream | Response;
37
+ async function wrangleWritableData(
38
+ data: WritableData | Promise<WritableData>,
39
+ ): Promise<Parameters<typeof writeFile>[1]> {
40
+ data = await data;
41
+ if (data instanceof Response) {
42
+ data = data.body ? Readable.fromWeb(data.body) : new Uint8Array(0);
43
+ }
44
+ if (data instanceof ReadableStream) {
45
+ data = Readable.fromWeb(data);
46
+ }
47
+ return data;
48
+ }
25
49
 
26
50
  export class Path {
27
51
  // @ts-expect-error ts(2564): False positive. https://github.com/microsoft/TypeScript/issues/32194
@@ -266,8 +290,29 @@ export class Path {
266
290
  return readFile(this.#path, "utf-8");
267
291
  }
268
292
 
269
- async readJSON<T>(): Promise<T> {
270
- return JSON.parse(await this.readText());
293
+ /**
294
+ * Reads JSON from the given file and parses it. No validation is performed
295
+ * (beyond JSON parsing).
296
+ *
297
+ * An optional `fallback` value can be specified. It will be used if (and only
298
+ * if) the file does not exist.
299
+ *
300
+ */
301
+
302
+ // biome-ignore lint/suspicious/noExplicitAny: Allow a default of `any` to match `JSON.parse(…)`.
303
+ async readJSON<T = any>(options?: { fallback?: T }): Promise<T> {
304
+ try {
305
+ return JSON.parse(await this.readText());
306
+ } catch (e) {
307
+ if (
308
+ (e as { code?: string }).code === "ENOENT" &&
309
+ options &&
310
+ "fallback" in options
311
+ ) {
312
+ return options.fallback as T;
313
+ }
314
+ throw e;
315
+ }
271
316
  }
272
317
 
273
318
  /** Creates intermediate directories if they do not exist.
@@ -275,11 +320,11 @@ export class Path {
275
320
  * Returns the original `Path` (for chaining).
276
321
  */
277
322
  async write(
278
- data: Parameters<typeof writeFile>[1],
323
+ data: WritableData | Promise<WritableData>,
279
324
  options?: Parameters<typeof writeFile>[2],
280
325
  ): Promise<Path> {
281
326
  await this.parent.mkdir();
282
- await writeFile(this.#path, data, options);
327
+ await writeFile(this.#path, await wrangleWritableData(data), options);
283
328
  return this;
284
329
  }
285
330
 
@@ -325,16 +370,15 @@ export class Path {
325
370
  return targetPath;
326
371
  }
327
372
 
328
- stat(options?: Parameters<typeof stat>[1]): ReturnType<typeof stat> {
329
- return stat(this.path, options);
330
- }
373
+ // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.
374
+ stat: typeof statType = (...options) => stat(this.#path, ...options) as any;
331
375
 
332
376
  // I don't think `lstat` is a great name, but it does match the
333
377
  // well-established canonical system call. So in this case we keep the
334
378
  // awkward abbreviation.
335
- lstat(options?: Parameters<typeof lstat>[1]): ReturnType<typeof lstat> {
336
- return lstat(this.path, options);
337
- }
379
+ lstat: typeof lstatType = (...options) =>
380
+ // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.
381
+ lstat(this.#path, ...options) as any;
338
382
 
339
383
  static get homedir(): Path {
340
384
  return new Path(homedir());
@@ -1,7 +1,14 @@
1
1
  // Note: this file is `.ts` rather than `.d.ts` to ensure it ends up in the `tsc` output.
2
2
 
3
3
  import type { Abortable } from "node:events";
4
- import type { Dirent, ObjectEncodingOptions, OpenMode } from "node:fs";
4
+ import type {
5
+ BigIntStats,
6
+ Dirent,
7
+ ObjectEncodingOptions,
8
+ OpenMode,
9
+ StatOptions,
10
+ Stats,
11
+ } from "node:fs";
5
12
 
6
13
  // Modifying the type of `readdir(…)` from `node:fs/promises` to remove the
7
14
  // first parameter is difficult, if not impossible. So we give up and duplicate
@@ -76,3 +83,34 @@ export declare function readFileType(
76
83
  | BufferEncoding
77
84
  | null,
78
85
  ): Promise<string | Buffer>;
86
+
87
+ export declare function lstatType(
88
+ opts?: StatOptions & {
89
+ bigint?: false | undefined;
90
+ },
91
+ ): Promise<Stats>;
92
+ export declare function lstatType(
93
+ opts: StatOptions & {
94
+ bigint: true;
95
+ },
96
+ ): Promise<BigIntStats>;
97
+ export declare function lstatType(
98
+ opts?: StatOptions,
99
+ ): Promise<Stats | BigIntStats>;
100
+ /**
101
+ * @since v10.0.0
102
+ * @return Fulfills with the {fs.Stats} object for the given `path`.
103
+ */
104
+ export declare function statType(
105
+ opts?: StatOptions & {
106
+ bigint?: false | undefined;
107
+ },
108
+ ): Promise<Stats>;
109
+ export declare function statType(
110
+ opts: StatOptions & {
111
+ bigint: true;
112
+ },
113
+ ): Promise<BigIntStats>;
114
+ export declare function statType(
115
+ opts?: StatOptions,
116
+ ): Promise<Stats | BigIntStats>;
package/src/sync/index.ts CHANGED
@@ -12,7 +12,12 @@ import {
12
12
  } from "node:fs";
13
13
  import { mustNotHaveTrailingSlash, Path } from "../Path";
14
14
  import "./static";
15
- import type { readDirSyncType, readFileSyncType } from "./modifiedNodeTypes";
15
+ import type {
16
+ lstatSyncType,
17
+ readDirSyncType,
18
+ readFileSyncType,
19
+ statSyncType,
20
+ } from "./modifiedNodeTypes";
16
21
 
17
22
  // Note that (non-static) functions in this file are defined using `function(…)
18
23
  // { … }` rather than arrow functions, specifically because we want `this` to
@@ -36,7 +41,7 @@ declare module "../Path" {
36
41
 
37
42
  readSync: typeof readFileSyncType;
38
43
  readTextSync(): string;
39
- readJSONSync<T>(): T;
44
+ readJSONSync<T>(options?: { fallback?: T }): T;
40
45
 
41
46
  writeSync(
42
47
  data: Parameters<typeof writeFileSync>[1],
@@ -56,16 +61,8 @@ declare module "../Path" {
56
61
  type?: Parameters<typeof symlinkSync>[2],
57
62
  ): Path;
58
63
 
59
- statSync(
60
- options?: Parameters<typeof statSync>[1],
61
- ): ReturnType<typeof statSync>;
62
-
63
- // I don't think `lstat` is a great name, but it does match the
64
- // well-established canonical system call. So in this case we keep the
65
- // awkward abbreviation.
66
- lstatSync(
67
- options?: Parameters<typeof lstatSync>[1],
68
- ): ReturnType<typeof lstatSync>;
64
+ statSync: typeof statSyncType;
65
+ lstatSync: typeof lstatSyncType;
69
66
  }
70
67
  }
71
68
 
@@ -160,8 +157,19 @@ Path.prototype.readTextSync = function (): string {
160
157
  return readFileSync(this.path, "utf-8");
161
158
  };
162
159
 
163
- Path.prototype.readJSONSync = function <T>(): T {
164
- return JSON.parse(this.readTextSync());
160
+ Path.prototype.readJSONSync = function <T>(options?: { fallback?: T }): T {
161
+ try {
162
+ return JSON.parse(this.readTextSync());
163
+ } catch (e) {
164
+ if (
165
+ (e as { code?: string }).code === "ENOENT" &&
166
+ options &&
167
+ "fallback" in options
168
+ ) {
169
+ return options.fallback as T;
170
+ }
171
+ throw e;
172
+ }
165
173
  };
166
174
 
167
175
  Path.prototype.writeSync = function (
@@ -202,12 +210,14 @@ Path.prototype.symlinkSync = function symlink(
202
210
  return targetPath;
203
211
  };
204
212
 
213
+ /** @ts-expect-error ts(2322): Wrangle types */
205
214
  Path.prototype.statSync = function (
206
215
  options?: Parameters<typeof statSync>[1],
207
216
  ): ReturnType<typeof statSync> {
208
217
  return statSync(this.path, options);
209
218
  };
210
219
 
220
+ /** @ts-expect-error ts(2322): Wrangle types */
211
221
  Path.prototype.lstatSync = function (
212
222
  options?: Parameters<typeof lstatSync>[1],
213
223
  ): ReturnType<typeof lstatSync> {
@@ -1,6 +1,12 @@
1
1
  // Note: this file is `.ts` rather than `.d.ts` to ensure it ends up in the `tsc` output.
2
2
 
3
- import type { Dirent, ObjectEncodingOptions } from "node:fs";
3
+ import type {
4
+ BigIntStats,
5
+ Dirent,
6
+ ObjectEncodingOptions,
7
+ StatSyncOptions,
8
+ Stats,
9
+ } from "node:fs";
4
10
 
5
11
  export declare function readFileSyncType(
6
12
  options?: {
@@ -64,3 +70,38 @@ export declare function readDirSyncType(options: {
64
70
  withFileTypes: true;
65
71
  recursive?: boolean | undefined;
66
72
  }): Dirent<Buffer>[];
73
+
74
+ export declare function statSyncType(options?: undefined): Stats;
75
+ export declare function statSyncType(
76
+ options?: StatSyncOptions & {
77
+ bigint?: false | undefined;
78
+ throwIfNoEntry: false;
79
+ },
80
+ ): Stats | undefined;
81
+ export declare function statSyncType(
82
+ options: StatSyncOptions & {
83
+ bigint: true;
84
+ throwIfNoEntry: false;
85
+ },
86
+ ): BigIntStats | undefined;
87
+ export declare function statSyncType(
88
+ options?: StatSyncOptions & {
89
+ bigint?: false | undefined;
90
+ },
91
+ ): Stats;
92
+ export declare function statSyncType(
93
+ options: StatSyncOptions & {
94
+ bigint: true;
95
+ },
96
+ ): BigIntStats;
97
+ export declare function statSyncType(
98
+ options: StatSyncOptions & {
99
+ bigint: boolean;
100
+ throwIfNoEntry?: false | undefined;
101
+ },
102
+ ): Stats | BigIntStats;
103
+ export declare function statSyncType(
104
+ options?: StatSyncOptions,
105
+ ): Stats | BigIntStats | undefined;
106
+
107
+ export declare const lstatSyncType: typeof statSyncType;
@@ -141,7 +141,7 @@ test(".readTextSync()", () => {
141
141
  expect(readFileSync(file.path, "utf-8")).toBe("bye");
142
142
  });
143
143
 
144
- test(".readJSONWync()", () => {
144
+ test(".readJSONSync()", () => {
145
145
  const file = Path.makeTempDirSync().join("file.json");
146
146
  file.writeSync(JSON.stringify({ foo: "bar" }));
147
147
 
@@ -152,6 +152,24 @@ test(".readJSONWync()", () => {
152
152
  >({ foo: "bar" });
153
153
  });
154
154
 
155
+ test(".readJSONSync(…) with fallback", () => {
156
+ const tempDir = Path.makeTempDirSync();
157
+ const file = tempDir.join("file.json");
158
+ const json: { foo?: number } = file.readJSONSync({ fallback: { foo: 4 } });
159
+ expect(json).toEqual({ foo: 4 });
160
+
161
+ const file2 = tempDir.join("file2.json");
162
+ file2.writeJSONSync({ foo: 6 });
163
+ const json2: { foo?: number } = file2.readJSONSync({
164
+ fallback: { foo: 4 },
165
+ });
166
+ expect(json2).toEqual({ foo: 6 });
167
+
168
+ expect(() => tempDir.readJSONSync({ fallback: { foo: 4 } })).toThrowError(
169
+ /^EISDIR/,
170
+ );
171
+ });
172
+
155
173
  test(".writeSync(…)", () => {
156
174
  const tempDir = Path.makeTempDirSync();
157
175
  const file = tempDir.join("file.json");
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../../src/Path.ts"],
4
- "sourcesContent": ["import {\n cp,\n lstat,\n mkdir,\n mkdtemp,\n readdir,\n readFile,\n rename,\n rm,\n stat,\n symlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { basename, dirname, extname, join } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport {\n xdgCache,\n xdgConfig,\n xdgData,\n xdgRuntime,\n xdgState,\n} from \"xdg-basedir\";\nimport type { readDirType, readFileType } from \"./modifiedNodeTypes\";\n\nexport class Path {\n // @ts-expect-error ts(2564): False positive. https://github.com/microsoft/TypeScript/issues/32194\n #path: string;\n /**\n * If `path` is a string starting with `file:///`, it will be parsed as a file URL.\n */\n constructor(path: string | URL | Path) {\n this.#setNormalizedPath(Path.#pathlikeToString(path));\n }\n\n /**\n * Similar to `new URL(path, base)`, but accepting and returning `Path` objects.\n * Note that `base` must be one of:\n *\n * - a valid second argument to `new URL(\u2026)`.\n * - a `Path` representing an absolute path.\n *\n */\n static resolve(path: string | URL | Path, base: string | URL | Path): Path {\n const baseURL = (() => {\n if (!(base instanceof Path)) {\n return base;\n }\n if (!base.isAbsolutePath()) {\n throw new Error(\n \"The `base` arg to `Path.resolve(\u2026)` must be an absolute path.\",\n );\n }\n return pathToFileURL(base.#path);\n })();\n return new Path(new URL(Path.#pathlikeToString(path), baseURL));\n }\n\n static #pathlikeToString(path: string | URL | Path): string {\n if (path instanceof Path) {\n return path.#path;\n }\n if (path instanceof URL) {\n return fileURLToPath(path);\n }\n if (typeof path === \"string\") {\n // TODO: allow turning off this heuristic?\n if (path.startsWith(\"file:///\")) {\n return fileURLToPath(path);\n }\n return path;\n }\n throw new Error(\"Invalid path\");\n }\n\n #setNormalizedPath(path: string): void {\n this.#path = join(path);\n }\n\n isAbsolutePath(): boolean {\n return this.#path.startsWith(\"/\");\n }\n\n toFileURL(): URL {\n if (!this.isAbsolutePath()) {\n throw new Error(\n \"Tried to convert to file URL when the path is not absolute.\",\n );\n }\n return pathToFileURL(this.#path);\n }\n\n /**\n * The `Path` can have a trailing slash, indicating that it represents a\n * directory. (If there is no trailing slash, it can represent either a file\n * or a directory.)\n *\n * Some operations will refuse to treat a directory path as a file path. This\n * function identifies such paths.\n */\n hasTrailingSlash(): boolean {\n // TODO: handle Windows semantically\n return this.#path.endsWith(\"/\");\n }\n\n /**\n * Same as `.toString()`, but more concise.\n */\n get path() {\n return this.#path;\n }\n\n toString(): string {\n return this.#path;\n }\n\n /** Constructs a new path by appending the given path segments.\n * This follows `node` semantics for absolute paths: leading slashes in the given descendant segments are ignored.\n */\n join(...segments: (string | Path)[]): Path {\n const segmentStrings = segments.map((segment) =>\n segment instanceof Path ? segment.path : segment,\n );\n return new Path(join(this.#path, ...segmentStrings));\n }\n\n extendBasename(suffix: string): Path {\n const joinedSuffix = join(suffix);\n if (joinedSuffix !== basename(joinedSuffix)) {\n throw new Error(\"Invalid suffix to extend file name.\");\n }\n // TODO: join basename and dirname instead?\n return new Path(this.#path + joinedSuffix);\n }\n\n get parent(): Path {\n return new Path(dirname(this.#path));\n }\n\n // Normally I'd stick with `node`'s name, but I think `.dirname` is a\n // particularly poor name. So we support `.dirname` for discovery but mark it\n // as deprecated, even if it will never be removed.\n /** @deprecated Alias for `.parent`. */\n get dirname(): Path {\n return this.parent;\n }\n\n get basename(): Path {\n return new Path(basename(this.#path));\n }\n\n get extension(): string {\n mustNotHaveTrailingSlash(this);\n return extname(this.#path);\n }\n\n // Normally I'd stick with `node`'s name, but I think `.extname` is a\n // particularly poor name. So we support `.extname` for discovery but mark it\n // as deprecated, even if it will never be removed.\n /** @deprecated Alias for `.extension`. */\n get extname(): string {\n return this.extension;\n }\n\n // TODO: find a neat way to dedup with the sync version?\n async exists(constraints?: {\n mustBe: \"file\" | \"directory\";\n }): Promise<boolean> {\n let stats: Awaited<ReturnType<typeof stat>>;\n try {\n stats = await stat(this.#path);\n // biome-ignore lint/suspicious/noExplicitAny: TypeScript limitation\n } catch (e: any) {\n if (e.code === \"ENOENT\") {\n return false;\n }\n throw e;\n }\n if (!constraints?.mustBe) {\n return true;\n }\n switch (constraints?.mustBe) {\n case \"file\": {\n mustNotHaveTrailingSlash(this);\n if (stats.isFile()) {\n return true;\n }\n throw new Error(`Path exists but is not a file: ${this.#path}`);\n }\n case \"directory\": {\n if (stats.isDirectory()) {\n return true;\n }\n throw new Error(`Path exists but is not a directory: ${this.#path}`);\n }\n default: {\n throw new Error(\"Invalid path type constraint\");\n }\n }\n }\n\n async existsAsFile(): Promise<boolean> {\n return this.exists({ mustBe: \"file\" });\n }\n\n async existsAsDir(): Promise<boolean> {\n return this.exists({ mustBe: \"directory\" });\n }\n\n // I don't think `mkdir` is a great name, but it does match the\n // well-established canonical commandline name. So in this case we keep the\n // awkward abbreviation.\n /** Defaults to `recursive: true`. */\n async mkdir(options?: Parameters<typeof mkdir>[1]): Promise<Path> {\n const optionsObject = (() => {\n if (typeof options === \"string\" || typeof options === \"number\") {\n return { mode: options };\n }\n return options ?? {};\n })();\n await mkdir(this.#path, { recursive: true, ...optionsObject });\n return this;\n }\n\n // TODO: check idempotency semantics when the destination exists and is a folder.\n /** Returns the destination path. */\n async cp(\n destination: string | URL | Path,\n options?: Parameters<typeof cp>[2],\n ): Promise<Path> {\n await cp(this.#path, new Path(destination).#path, options);\n return new Path(destination);\n }\n\n // TODO: check idempotency semantics when the destination exists and is a folder.\n async rename(destination: string | URL | Path): Promise<void> {\n await rename(this.#path, new Path(destination).#path);\n }\n\n /** Create a temporary dir inside the global temp dir for the current user. */\n static async makeTempDir(prefix?: string): Promise<Path> {\n return new Path(\n await mkdtemp(new Path(tmpdir()).join(prefix ?? \"js-temp-\").toString()),\n );\n }\n\n async rm(options?: Parameters<typeof rm>[1]): Promise<void> {\n await rm(this.#path, options);\n }\n\n /**\n * Equivalent to:\n *\n * .rm({ recursive: true, force: true, ...(options ?? {}) })\n *\n */\n async rm_rf(options?: Parameters<typeof rm>[1]): Promise<void> {\n await this.rm({ recursive: true, force: true, ...(options ?? {}) });\n }\n\n read: typeof readFileType = (options) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n readFile(this.#path, options as any) as any;\n\n async readText(): Promise<string> {\n return readFile(this.#path, \"utf-8\");\n }\n\n async readJSON<T>(): Promise<T> {\n return JSON.parse(await this.readText());\n }\n\n /** Creates intermediate directories if they do not exist.\n *\n * Returns the original `Path` (for chaining).\n */\n async write(\n data: Parameters<typeof writeFile>[1],\n options?: Parameters<typeof writeFile>[2],\n ): Promise<Path> {\n await this.parent.mkdir();\n await writeFile(this.#path, data, options);\n return this;\n }\n\n /**\n * If only `data` is provided, this is equivalent to:\n *\n * .write(JSON.stringify(data, null, \" \"));\n *\n * `replacer` and `space` can also be specified, making this equivalent to:\n *\n * .write(JSON.stringify(data, replacer, space));\n *\n * Returns the original `Path` (for chaining).\n */\n async writeJSON<T>(\n data: T,\n replacer: Parameters<typeof JSON.stringify>[1] = null,\n space: Parameters<typeof JSON.stringify>[2] = \" \",\n ): Promise<Path> {\n await this.write(JSON.stringify(data, replacer, space));\n return this;\n }\n\n // Normally we'd add a `@deprecated` alias named `.readdir`, but that would\n // differ only by capitalization of a single non-leading character. This can\n // be a bit confusing, especially when autocompleting. So for this function in\n // particular we don't include an alias.\n readDir: typeof readDirType = (options) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n readdir(this.#path, options as any) as any;\n\n /** Returns the destination path. */\n async symlink(\n target: string | URL | Path,\n type?: Parameters<typeof symlink>[2],\n ): Promise<Path> {\n const targetPath = new Path(target);\n await symlink(\n this.path,\n targetPath.path,\n type as Exclude<Parameters<typeof symlink>[2], undefined>, // \uD83E\uDD37\n );\n return targetPath;\n }\n\n stat(options?: Parameters<typeof stat>[1]): ReturnType<typeof stat> {\n return stat(this.path, options);\n }\n\n // I don't think `lstat` is a great name, but it does match the\n // well-established canonical system call. So in this case we keep the\n // awkward abbreviation.\n lstat(options?: Parameters<typeof lstat>[1]): ReturnType<typeof lstat> {\n return lstat(this.path, options);\n }\n\n static get homedir(): Path {\n return new Path(homedir());\n }\n\n static xdg = {\n cache: new Path(xdgCache ?? Path.homedir.join(\".cache\")),\n config: new Path(xdgConfig ?? Path.homedir.join(\".config\")),\n data: new Path(xdgData ?? Path.homedir.join(\".local/share\")),\n state: new Path(xdgState ?? Path.homedir.join(\".local/state\")),\n /**\n * {@link Path.xdg.runtime} does not have a default value. Consider\n * {@link Path.xdg.runtimeWithStateFallback} if you need a fallback but do not have a particular fallback in mind.\n */\n runtime: xdgRuntime ? new Path(xdgRuntime) : undefined,\n runtimeWithStateFallback: xdgRuntime\n ? new Path(xdgRuntime)\n : new Path(xdgState ?? Path.homedir.join(\".local/state\")),\n };\n\n /** Chainable function to print the path. Prints the same as:\n *\n * if (args.length > 0) {\n * console.log(...args);\n * }\n * console.log(this.path);\n *\n */\n // biome-ignore lint/suspicious/noExplicitAny: This is the correct type, based on `console.log(\u2026)`.\n debugPrint(...args: any[]): Path {\n if (args.length > 0) {\n console.log(...args);\n }\n console.log(this.#path);\n return this;\n }\n}\n\n/**\n * This function is useful to serialize any `Path`s in a structure to pass on to\n * functions that do not know about the `Path` class, e.g.\n *\n * function process(args: (string | Path)[]) {\n * const argsAsStrings = args.map(stringifyIfPath);\n * }\n *\n */\nexport function stringifyIfPath<T>(value: T | Path): T | string {\n if (value instanceof Path) {\n return value.toString();\n }\n return value;\n}\n\nexport function mustNotHaveTrailingSlash(path: Path): void {\n if (path.hasTrailingSlash()) {\n throw new Error(\n \"Path ends with a slash, which cannot be treated as a file.\",\n );\n }\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }