path-class 0.14.0 → 0.15.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/dist/lib/path-class/Path.d.ts +47 -0
- package/dist/lib/path-class/chunks/{chunk-THKWY3VT.js → chunk-DIQ7KWIN.js} +106 -1
- package/dist/lib/path-class/chunks/chunk-DIQ7KWIN.js.map +7 -0
- package/dist/lib/path-class/emptyish.d.ts +14 -0
- package/dist/lib/path-class/index.js +1 -1
- package/dist/lib/path-class/sync/PathSync.d.ts +50 -0
- package/dist/lib/path-class/sync/index.js +74 -1
- package/dist/lib/path-class/sync/index.js.map +2 -2
- package/package.json +3 -6
- package/src/Path.test.ts +22 -0
- package/src/Path.ts +78 -0
- package/src/emptyish.ts +79 -0
- package/src/sync/PathSync.test.ts +22 -0
- package/src/sync/PathSync.ts +79 -0
- package/dist/lib/path-class/chunks/chunk-THKWY3VT.js.map +0 -7
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { appendFile, chmod, cp, mkdir, rm, symlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { type IgnoreAutoGeneratedFilesOptions } from "./emptyish";
|
|
2
3
|
import { type WriteJSONOptions } from "./json";
|
|
3
4
|
import type { lstatType, readDirType, readFileType, statType } from "./modifiedNodeTypes";
|
|
4
5
|
type WritableData = Parameters<typeof writeFile>[1] | ReadableStream | Response;
|
|
@@ -275,6 +276,52 @@ export declare class Path {
|
|
|
275
276
|
*/
|
|
276
277
|
writeJSON<T>(data: T, options?: WriteJSONOptions): Promise<Path>;
|
|
277
278
|
readDir: typeof readDirType;
|
|
279
|
+
/**
|
|
280
|
+
* - Returns false if the path does not exists.
|
|
281
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
282
|
+
*/
|
|
283
|
+
isEmptyDir(options?: IgnoreAutoGeneratedFilesOptions): Promise<boolean>;
|
|
284
|
+
/**
|
|
285
|
+
* Equivalent to:
|
|
286
|
+
*
|
|
287
|
+
* ```
|
|
288
|
+
* .isEmptyDir({
|
|
289
|
+
* ignoreOSAutogeneratedFiles: true,
|
|
290
|
+
* })
|
|
291
|
+
* ```
|
|
292
|
+
*
|
|
293
|
+
* Note that this ignores common files automatically created by the operating
|
|
294
|
+
* system. These are *often* vestigial and unnecessary, but may occasionally
|
|
295
|
+
* be important to the user (e.g. an icon for an empty folder, or folder
|
|
296
|
+
* layout information that will be used once files are added atain).
|
|
297
|
+
*
|
|
298
|
+
* There is no general way to tell whether a given folder is "safe" to delete,
|
|
299
|
+
* but this can be a useful heuristic to tell whether a folder is "actually"
|
|
300
|
+
* empty when it is fully managed by the calling program and otherwise empty.
|
|
301
|
+
*
|
|
302
|
+
* - Returns `false` if the path does not exists.
|
|
303
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
304
|
+
*/
|
|
305
|
+
isEmptyishDir(): Promise<boolean>;
|
|
306
|
+
/**
|
|
307
|
+
* A wrapper around `.rmDir()` that ignores files according to
|
|
308
|
+
* `.isEmptyishDir()`.
|
|
309
|
+
*
|
|
310
|
+
* Note that this ignores common files automatically created by the operating
|
|
311
|
+
* system. These are *often* vestigial and unnecessary, but may occasionally
|
|
312
|
+
* be important to the user (e.g. an icon for an empty folder, or folder
|
|
313
|
+
* layout information that will be used once files are added atain).
|
|
314
|
+
*
|
|
315
|
+
* There is no general way to tell whether a given folder is "safe" to delete,
|
|
316
|
+
* but this can be a useful heuristic to tell whether a folder is "actually"
|
|
317
|
+
* empty when it is fully managed by the calling program and otherwise empty.
|
|
318
|
+
*
|
|
319
|
+
* - Returns `true` if the directory was removed.
|
|
320
|
+
* - Returns `false` if the directory is not emptyish.
|
|
321
|
+
* - Returns `false` if the path does not exists.
|
|
322
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
323
|
+
*/
|
|
324
|
+
rmDirIfEmptyish(): Promise<boolean>;
|
|
278
325
|
/** Returns the destination path. */
|
|
279
326
|
symlink(target: string | URL | Path, type?: Parameters<typeof symlink>[2]): Promise<Path>;
|
|
280
327
|
realpath(): Promise<Path>;
|
|
@@ -25,6 +25,42 @@ import { createInterface } from "node:readline/promises";
|
|
|
25
25
|
import { Readable } from "node:stream";
|
|
26
26
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
27
27
|
|
|
28
|
+
// src/emptyish.ts
|
|
29
|
+
var MACOS_DOT_DS_STORE = ".DS_Store";
|
|
30
|
+
var MACOS_ICON_PATH = "Icon\r";
|
|
31
|
+
var WINDOWS_THUMBS_DOT_DB = "Thumbs.db";
|
|
32
|
+
var WINDOWS_DESKTOP_INI = "Desktop.ini";
|
|
33
|
+
var ALL_KNOWN_AUTOGENERATED_FILES = [
|
|
34
|
+
MACOS_DOT_DS_STORE,
|
|
35
|
+
MACOS_ICON_PATH,
|
|
36
|
+
WINDOWS_THUMBS_DOT_DB,
|
|
37
|
+
WINDOWS_DESKTOP_INI
|
|
38
|
+
];
|
|
39
|
+
function removeAutogeneratedFileEntries(entries, options) {
|
|
40
|
+
const ignore = options?.ignoreOSAutogeneratedFiles ?? {};
|
|
41
|
+
{
|
|
42
|
+
if (ignore === true || ignore?.macOS === true || ignore.macOS?.dotDSStore) {
|
|
43
|
+
entries.delete(MACOS_DOT_DS_STORE);
|
|
44
|
+
}
|
|
45
|
+
if (ignore === true || ignore?.macOS === true || ignore.macOS?.iconPath) {
|
|
46
|
+
entries.delete(MACOS_ICON_PATH);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
{
|
|
50
|
+
if (ignore === true || ignore?.windows === true || ignore.windows?.ThumbsDotDB) {
|
|
51
|
+
entries.delete(WINDOWS_THUMBS_DOT_DB);
|
|
52
|
+
}
|
|
53
|
+
if (ignore === true || ignore?.windows === true || ignore.windows?.DesktopDotIni) {
|
|
54
|
+
entries.delete(WINDOWS_DESKTOP_INI);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function isEmptyExceptForAutoGeneratedFiles(entries, options) {
|
|
59
|
+
const entriesSet = new Set(entries);
|
|
60
|
+
removeAutogeneratedFileEntries(entriesSet, options);
|
|
61
|
+
return entriesSet.size === 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
28
64
|
// src/json.ts
|
|
29
65
|
var JSON_WRITE_DEFAULTS = {
|
|
30
66
|
replacer: null,
|
|
@@ -674,6 +710,73 @@ var Path = class _Path {
|
|
|
674
710
|
// biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.
|
|
675
711
|
readdir(this.#path, options)
|
|
676
712
|
);
|
|
713
|
+
/**
|
|
714
|
+
* - Returns false if the path does not exists.
|
|
715
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
716
|
+
*/
|
|
717
|
+
// TODO: return `false` when the path is a file?
|
|
718
|
+
async isEmptyDir(options) {
|
|
719
|
+
if (!this.existsAsDir()) {
|
|
720
|
+
return false;
|
|
721
|
+
}
|
|
722
|
+
return isEmptyExceptForAutoGeneratedFiles(await this.readDir(), options);
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Equivalent to:
|
|
726
|
+
*
|
|
727
|
+
* ```
|
|
728
|
+
* .isEmptyDir({
|
|
729
|
+
* ignoreOSAutogeneratedFiles: true,
|
|
730
|
+
* })
|
|
731
|
+
* ```
|
|
732
|
+
*
|
|
733
|
+
* Note that this ignores common files automatically created by the operating
|
|
734
|
+
* system. These are *often* vestigial and unnecessary, but may occasionally
|
|
735
|
+
* be important to the user (e.g. an icon for an empty folder, or folder
|
|
736
|
+
* layout information that will be used once files are added atain).
|
|
737
|
+
*
|
|
738
|
+
* There is no general way to tell whether a given folder is "safe" to delete,
|
|
739
|
+
* but this can be a useful heuristic to tell whether a folder is "actually"
|
|
740
|
+
* empty when it is fully managed by the calling program and otherwise empty.
|
|
741
|
+
*
|
|
742
|
+
* - Returns `false` if the path does not exists.
|
|
743
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
744
|
+
*/
|
|
745
|
+
async isEmptyishDir() {
|
|
746
|
+
return this.isEmptyDir({
|
|
747
|
+
ignoreOSAutogeneratedFiles: true
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* A wrapper around `.rmDir()` that ignores files according to
|
|
752
|
+
* `.isEmptyishDir()`.
|
|
753
|
+
*
|
|
754
|
+
* Note that this ignores common files automatically created by the operating
|
|
755
|
+
* system. These are *often* vestigial and unnecessary, but may occasionally
|
|
756
|
+
* be important to the user (e.g. an icon for an empty folder, or folder
|
|
757
|
+
* layout information that will be used once files are added atain).
|
|
758
|
+
*
|
|
759
|
+
* There is no general way to tell whether a given folder is "safe" to delete,
|
|
760
|
+
* but this can be a useful heuristic to tell whether a folder is "actually"
|
|
761
|
+
* empty when it is fully managed by the calling program and otherwise empty.
|
|
762
|
+
*
|
|
763
|
+
* - Returns `true` if the directory was removed.
|
|
764
|
+
* - Returns `false` if the directory is not emptyish.
|
|
765
|
+
* - Returns `false` if the path does not exists.
|
|
766
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
767
|
+
*/
|
|
768
|
+
async rmDirIfEmptyish() {
|
|
769
|
+
if (await this.isEmptyishDir()) {
|
|
770
|
+
await Promise.all([
|
|
771
|
+
ALL_KNOWN_AUTOGENERATED_FILES.map(
|
|
772
|
+
(path) => this.join(path).rm({ force: true })
|
|
773
|
+
)
|
|
774
|
+
]);
|
|
775
|
+
await this.rmDir();
|
|
776
|
+
return true;
|
|
777
|
+
}
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
677
780
|
/** Returns the destination path. */
|
|
678
781
|
async symlink(target, type) {
|
|
679
782
|
const targetPath = new _Path(target);
|
|
@@ -776,6 +879,8 @@ function stringifyIfPath(value) {
|
|
|
776
879
|
}
|
|
777
880
|
|
|
778
881
|
export {
|
|
882
|
+
ALL_KNOWN_AUTOGENERATED_FILES,
|
|
883
|
+
isEmptyExceptForAutoGeneratedFiles,
|
|
779
884
|
jsonChunks,
|
|
780
885
|
stringifyIfPath,
|
|
781
886
|
ResolutionPrefix,
|
|
@@ -783,4 +888,4 @@ export {
|
|
|
783
888
|
AsyncDisposablePath,
|
|
784
889
|
mustNotHaveTrailingSlash
|
|
785
890
|
};
|
|
786
|
-
//# sourceMappingURL=chunk-
|
|
891
|
+
//# sourceMappingURL=chunk-DIQ7KWIN.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/Path.ts", "../../../../src/emptyish.ts", "../../../../src/json.ts", "../../../../src/xdg.ts", "../../../../src/stringifyfIfPath.ts"],
|
|
4
|
+
"sourcesContent": ["import { createReadStream } from \"node:fs\";\nimport {\n appendFile,\n chmod,\n constants,\n cp,\n lstat,\n mkdir,\n mkdtemp,\n readdir,\n readFile,\n realpath,\n rename,\n rm,\n rmdir,\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 { cwd } from \"node:process\";\nimport { createInterface } from \"node:readline/promises\";\nimport { Readable } from \"node:stream\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport {\n ALL_KNOWN_AUTOGENERATED_FILES,\n type IgnoreAutoGeneratedFilesOptions,\n isEmptyExceptForAutoGeneratedFiles,\n} from \"./emptyish\";\nimport { jsonChunks, type WriteJSONOptions } from \"./json\";\nimport type {\n lstatType,\n readDirType,\n readFileType,\n statType,\n} from \"./modifiedNodeTypes\";\nimport { stringifyIfPath } from \"./stringifyfIfPath\";\nimport { xdg } from \"./xdg\";\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 enum ResolutionPrefix {\n Absolute = \"absolute\",\n Relative = \"relative\",\n Bare = \"bare\",\n}\n\nexport function resolutionPrefix(pathString: string): ResolutionPrefix {\n if (pathString.startsWith(\"/\")) {\n return ResolutionPrefix.Absolute;\n } else if (pathString.startsWith(\"./\")) {\n return ResolutionPrefix.Relative;\n } else if (pathString.startsWith(\"../\")) {\n return ResolutionPrefix.Relative;\n } else if (pathString === \".\") {\n return ResolutionPrefix.Relative;\n } else if (pathString === \"..\") {\n return ResolutionPrefix.Relative;\n }\n return ResolutionPrefix.Bare;\n}\n\nconst DEFAULT_TEMP_PREFIX = \"js-temp-\";\nconst DEFAULT_TEMP_FILE_NAME = \"file\";\n\nfunction preserveRelativeResolutionPrefix(\n pathString: string,\n from: string,\n): string {\n if (\n resolutionPrefix(from) === ResolutionPrefix.Relative &&\n resolutionPrefix(pathString) !== ResolutionPrefix.Relative\n ) {\n // We don't have to handle the case of `\".\"`, as it already starts with `\".\"`\n return `./${pathString}`;\n }\n return pathString;\n}\n\nfunction joinPreservingRelativeResolutionPrefix(\n base: string,\n relative: string[],\n): string {\n return preserveRelativeResolutionPrefix(join(base, ...relative), base);\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 const s = Path.#pathlikeToString(path);\n this.#setNormalizedPath(s);\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 // Preserves the `ResolutionPrefix` status when possible.\n #setNormalizedPath(path: string): void {\n this.#path = joinPreservingRelativeResolutionPrefix(path, []);\n }\n static fromString(s: string): Path {\n if (typeof s !== \"string\") {\n throw new Error(\n \"Invalid argument to `Path.fromString(\u2026)` \u2014 expected a string.\",\n );\n }\n return new Path(s);\n }\n\n get resolutionPrefix(): ResolutionPrefix {\n return resolutionPrefix(this.#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 if (typeof base === \"string\" && !base.startsWith(\"file://\")) {\n return pathToFileURL(base);\n }\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 /**\n * Convenience function. The following are equivalent:\n *\n * B.resolve(A);\n * Path.resolve(A, B);\n *\n */\n resolve(path: string | URL | Path): Path {\n return Path.resolve(path, this);\n }\n\n /**\n * Computes the relative path from an ancestor (this) to another path.\n *\n * - If the path is a descendant *or is the same path*, this returns a\n * relative path. Call {@link Path.asBare `.asBare()`} on the output if\n * needed.\n * - The output is `null` unless the input paths are:\n * - Both absolute paths.\n * - Both relative paths. Resolve the paths before passing to this function\n * if needed.\n * - Trailing slashes:\n * - By default, the ancestor path must have a trailing slash for the\n * function to be called. Pass `{ requireTrailingSlashForAncestor: false\n * }` if needed. Note that recombining the ancestor path with the output\n * using {@link Path.prototype.resolve `.resolve(\u2026)` } does not result in\n * the original input path if the ancestor path did not have a trailing\n * slash.\n * - For the descendant/same path check, trailing slashes are ignored. In\n * particular, if the ancestor path has a trailing slash and the\n * descendant path is the same path without a trailing slash, this is\n * still considered to be the same path.\n * - The output has trailing slash if and only if:\n * - the input descendant does, or\n * - the output is the absolute path `/`.\n */\n descendantRelativePath(\n potentialDescendant: string | URL | Path,\n options?: { requireTrailingSlashForAncestor: boolean },\n ): Path | null {\n const requireTrailingSlashForAncestor =\n options?.requireTrailingSlashForAncestor ?? true;\n if (requireTrailingSlashForAncestor && !this.hasTrailingSlash()) {\n throw new Error(\n \"Ancestor must have a trailing slash. Pass `{ requireTrailingSlashForAncestor: false }` if needed.\",\n );\n }\n\n const other = new Path(potentialDescendant);\n if (this.isAbsolutePath() !== other.isAbsolutePath()) {\n return null;\n }\n\n // Leading slashes are okay, as they will result in a `\"\"` component for\n // absolute paths (and we don't compare absolute paths to relative paths.)\n const thisParts = this.toggleTrailingSlash(false).#path.split(\"/\");\n const otherParts = other.toggleTrailingSlash(false).#path.split(\"/\");\n\n if (otherParts.length < thisParts.length) {\n return null;\n }\n for (let i = 0; i < thisParts.length; i++) {\n if (thisParts[i] !== otherParts[i]) {\n return null;\n }\n }\n return new Path(\"./\")\n .join(...otherParts.slice(thisParts.length))\n .toggleTrailingSlash(other.hasTrailingSlash());\n }\n\n unresolve(path: string | URL | Path): Path {\n return Path.resolve(path, this);\n }\n\n isAbsolutePath(): boolean {\n return this.resolutionPrefix === ResolutionPrefix.Absolute;\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 * Toggles or sets a trailing slash as specified.\n * If the path is `/`, it will always be left as-is.\n */\n // Similar convention to:\n // https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/toggle Most\n // use cases will probably use the `force` parameters, but supporting the\n // \"toggle\" use case is easy to implement and hopefully will make the name\n // and API more familiar to web devs.\n toggleTrailingSlash(force?: boolean): Path {\n if (this.#path === \"/\") {\n return this;\n }\n const wantTrailingSlash = force ?? !this.hasTrailingSlash();\n if (wantTrailingSlash) {\n return this.hasTrailingSlash() ? this : this.join(\"./\");\n } else {\n return this.hasTrailingSlash() ? new Path(this.#path.slice(0, -1)) : this;\n }\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 /**\n * Format this with the escape codes for printing to the shell in bold blue.\n *\n * Example usage:\n *\n * console.log(`Processing: ${path.blue}`)\n *\n */\n get blue() {\n const { styleText } = globalThis.process.getBuiltinModule(\"node:util\");\n return styleText([\"bold\", \"blue\"], 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 const s = stringifyIfPath(segment);\n if (resolutionPrefix(s) === ResolutionPrefix.Absolute) {\n throw new Error(\n \"Arguments to `.join(\u2026)` cannot be absolute. Use `.asRelative()` to convert them first if needed.\",\n );\n }\n return s;\n });\n return new Path(\n joinPreservingRelativeResolutionPrefix(this.#path, segmentStrings),\n );\n }\n\n /**\n * Adjust the prefix to construct a relative path.\n *\n * | Example input | Output |\n * |-----------------|-----------------|\n * | `\"bare\"` | `\"./bare\"` |\n * | `\"./relative\"` | `\"./relative\"` |\n * | `\"../up-first\"` | `\"../up-first\"` |\n * | `\"/absolute\"` | `\"./absolute\"` |\n *\n */\n asRelative(): Path {\n return new Path(`./${this.#path}`);\n }\n\n /**\n * Adjust the prefix to construct an absolute path.\n *\n * | Example input | Output |\n * |-----------------|---------------|\n * | `\"bare\"` | `\"/bare\"` |\n * | `\"./relative\"` | `\"/relative\"` |\n * | `\"../up-first\"` | `\"/up-first\"` |\n * | `\"/absolute\"` | `\"/absolute\"` |\n *\n */\n asAbsolute(): Path {\n return new Path(join(\"/\", this.#path));\n }\n\n /**\n * Adjust the prefix to construct a bare path. Note that this returns `\".\"` if\n * there are no named paths left.\n *\n * | Example input | Output |\n * |-------------------|--------------|\n * | `\"bare\"` | `\"bare\"` |\n * | `\"./relative\" ` | `\"relative\"` |\n * | `\"/absolute\"` | `\"absolute\"` |\n * | `\".\"` | `\".\"` |\n * | `\"down-first/..\"` | `\".\"` |\n * | `\"../up-first\"` | (error) |\n * | `\"..\"` | (error) |\n *\n * Specify `parentTraversalPrefixHandling` in the `options` if you would like\n * to strip or keep resolution prefixes like `../` rather than erroring.\n *\n * | Example input | Output with `{ parentTraversalPrefixHandling: \"strip\" }` |\n * |----------------------|----------------------------------------------------------|\n * | `\"../up-first\"` | `\"up-first\"` |\n * | `\"..\"` | `\".\"` |\n *\n * | Example input | Output with `{ parentTraversalPrefixHandling: \"keep\" }` |\n * |----------------------|---------------------------------------------------------|\n * | `\"../up-first\"` | `\"../up-first\"` |\n * | `\"..\"` | `\"..\"` |\n *\n * If you need the output to start with a named component and return values\n * like `.`, `..`, `../`, or `../\u2026` are not okay, pass\n * `requireNamedComponentPrefix: true`. This is useful if the path represents\n * an `npm`-style package name (e.g. `\"typescript\"`, `\"@biomejs/biome\"`).\n *\n */\n asBare(options?: {\n parentTraversalPrefixHandling?: \"error\" | \"strip\" | \"keep\";\n requireNamedComponentPrefix?: boolean;\n }): Path {\n const path = new Path(join(\".\", this.#path));\n if (!path.#path.startsWith(\"../\") && path.#path !== \"..\") {\n if (\n options?.requireNamedComponentPrefix &&\n path.resolutionPrefix === ResolutionPrefix.Relative\n ) {\n throw new Error(\"Output does not start with a named component.\");\n }\n return path;\n }\n const parentTraversalHandling =\n options?.parentTraversalPrefixHandling ?? \"error\";\n switch (parentTraversalHandling) {\n case \"error\": {\n throw new Error(\n 'Converting path to a bare path resulted in a `..` traversal prefix. Pass `\"strip\"` or `\"keep\"` as the `parentTraversalHandling` option to avoid an error.',\n );\n }\n case \"strip\": {\n let newPath = path.#path.replace(/^(\\.\\.\\/)+/, \"\");\n if ([\"\", \"..\"].includes(newPath)) {\n newPath = \".\";\n }\n const output = new Path(newPath);\n if (\n options?.requireNamedComponentPrefix &&\n output.resolutionPrefix === ResolutionPrefix.Relative\n ) {\n throw new Error(\"Output does not start with a named component.\");\n }\n return new Path(newPath);\n }\n case \"keep\": {\n if (options?.requireNamedComponentPrefix) {\n throw new Error(\"Output does not start with a named component.\");\n }\n return path;\n }\n }\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 if (constraints?.mustBe === \"file\") {\n mustNotHaveTrailingSlash(this);\n }\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 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] & { createIntermediateDirs?: boolean },\n ): Promise<Path> {\n const { createIntermediateDirs, ...cpOptions } = options ?? {};\n const destinationPath = new Path(destination);\n if (createIntermediateDirs ?? true) {\n await destinationPath.parent.mkdir();\n }\n await cp(this.#path, destinationPath.#path, cpOptions);\n return destinationPath;\n }\n\n // TODO: check idempotency semantics when the destination exists and is a folder.\n async rename(\n destination: string | URL | Path,\n options?: { createIntermediateDirs?: boolean },\n ): Promise<Path> {\n const destinationPath = new Path(destination);\n if (options?.createIntermediateDirs ?? true) {\n await destinationPath.parent.mkdir();\n }\n await rename(this.#path, destinationPath.#path);\n return destinationPath;\n }\n\n /**\n * Create a temporary dir inside the global temp dir for the current user.\n *\n * This can be used with [`await\n * using`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/await_using)\n * to automatically delete the dir when it goes out of scope.\n *\n * {\n * await using tempDir = await Path.makeTempDir();\n * // Temporary dir exists while we're inside this block.\n * }\n * // Temporary dir has now been deleted.\n *\n * Note that (due to the semantics of JS runtime implementations) this does\n * not delete the temp dir if the process calls `exit(\u2026)` before the `using`\n * goes out of scope.\n *\n * */\n static async makeTempDir(prefix?: string): Promise<AsyncDisposablePath> {\n return new AsyncDisposablePath(\n await mkdtemp(\n new Path(tmpdir()).join(prefix ?? DEFAULT_TEMP_PREFIX).toString(),\n ),\n );\n }\n\n /**\n * Return a path:\n *\n * - whose parent dir is a temp dir that *has* been created, but\n * - which has itself not yet been created.\n *\n * Note that this path can actually also be used to create dir, but it is most\n * convenient to get a path for a temporary file that can be written to, while\n * having a disposal implementation that cleans everything up:\n *\n * await using tempFile = await Path.tempFilePath({ basename: \"foo.txt\" });\n * await tempFile.write(\"hello world!\");\n * // \u2026\n *\n * Note that that the following are equivalent when *not* using `await using`:\n *\n * await Path.tempFilePath({ basename: \"foo.txt\" });\n * (await Path.makeTempDir()).join(\"file.txt\");\n *\n * However, it is recommended to use `await using` to ensure cleanup.\n */\n static async tempFilePath(options: {\n tempDirPrefix?: string;\n basename?: string | Path;\n }): Promise<AsyncDisposablePath> {\n const tempDir = new Path(\n await mkdtemp(\n new Path(tmpdir())\n .join(options?.tempDirPrefix ?? DEFAULT_TEMP_PREFIX)\n .toString(),\n ),\n );\n return new AsyncDisposablePath(\n tempDir.join(options?.basename ?? DEFAULT_TEMP_FILE_NAME),\n { disposePathInstead: tempDir },\n );\n }\n\n async rm(options?: Parameters<typeof rm>[1]): Promise<void> {\n await rm(this.#path, options);\n }\n\n async rmDir(): Promise<void> {\n await rmdir(this.#path);\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 * Yields one line from the text of the line at a time.\n *\n * This uses streams, so it can be considerably more efficient than calling e.g. `.split(\"\\n\")` on the output of {@link readText `.readText()`}.\n *\n * Note that this function does not have a `.readLinesSync()` counterpart.\n */\n async *readLines(): AsyncIterable<string> {\n const stream = createReadStream(this.#path, \"utf-8\");\n yield* createInterface({ input: stream, terminal: false });\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 /**\n * Returns the original `Path` (for chaining).\n */\n async appendFile(\n data: Parameters<typeof appendFile>[1],\n options?: Parameters<typeof appendFile>[2],\n ): Promise<Path> {\n await appendFile(this.#path, data, options);\n return this;\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>(data: T, options?: WriteJSONOptions): Promise<Path> {\n // TODO: file handle mechanism to avoid JSON serialization in memory.\n await this.write(jsonChunks(data, options));\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 /**\n * - Returns false if the path does not exists.\n * - Throws an error if the path exists as a file rather than a dir.\n */\n // TODO: return `false` when the path is a file?\n async isEmptyDir(\n options?: IgnoreAutoGeneratedFilesOptions,\n ): Promise<boolean> {\n if (!this.existsAsDir()) {\n return false;\n }\n return isEmptyExceptForAutoGeneratedFiles(await this.readDir(), options);\n }\n\n /**\n * Equivalent to:\n *\n * ```\n * .isEmptyDir({\n * ignoreOSAutogeneratedFiles: true,\n * })\n * ```\n *\n * Note that this ignores common files automatically created by the operating\n * system. These are *often* vestigial and unnecessary, but may occasionally\n * be important to the user (e.g. an icon for an empty folder, or folder\n * layout information that will be used once files are added atain).\n *\n * There is no general way to tell whether a given folder is \"safe\" to delete,\n * but this can be a useful heuristic to tell whether a folder is \"actually\"\n * empty when it is fully managed by the calling program and otherwise empty.\n *\n * - Returns `false` if the path does not exists.\n * - Throws an error if the path exists as a file rather than a dir.\n */\n async isEmptyishDir(): Promise<boolean> {\n return this.isEmptyDir({\n ignoreOSAutogeneratedFiles: true,\n });\n }\n\n /**\n * A wrapper around `.rmDir()` that ignores files according to\n * `.isEmptyishDir()`.\n *\n * Note that this ignores common files automatically created by the operating\n * system. These are *often* vestigial and unnecessary, but may occasionally\n * be important to the user (e.g. an icon for an empty folder, or folder\n * layout information that will be used once files are added atain).\n *\n * There is no general way to tell whether a given folder is \"safe\" to delete,\n * but this can be a useful heuristic to tell whether a folder is \"actually\"\n * empty when it is fully managed by the calling program and otherwise empty.\n *\n * - Returns `true` if the directory was removed.\n * - Returns `false` if the directory is not emptyish.\n * - Returns `false` if the path does not exists.\n * - Throws an error if the path exists as a file rather than a dir.\n */\n async rmDirIfEmptyish(): Promise<boolean> {\n if (await this.isEmptyishDir()) {\n // TODO: doos any OS cause a race condition here, in practice?\n await Promise.all([\n ALL_KNOWN_AUTOGENERATED_FILES.map((path) =>\n this.join(path).rm({ force: true }),\n ),\n ]);\n await this.rmDir();\n return true;\n }\n return false;\n }\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 // I don't think `realpath` is a great name, but it does match the\n // well-established canonical commandline name. So in this case we keep the\n // name instead of using `realPath`.\n //\n // Note: There are no options in our API, because the only option is an\n // encoding. We set the encoding to construct the returned `Path`.\n async realpath(): Promise<Path> {\n return new Path(await realpath(this.#path, \"utf-8\"));\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 async chmod(mode: Parameters<typeof chmod>[1]): Promise<Path> {\n await chmod(this.#path, mode);\n return this;\n }\n\n /**\n * Add the executable bit (for everyone) to the given path without modifying other bits (`chmod +x`).\n */\n async chmodX(): Promise<Path> {\n const { mode } = await this.stat();\n await this.chmod(\n mode | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH,\n );\n return this;\n }\n\n static get homedir(): Path {\n return new Path(homedir());\n }\n\n /**\n * Get the current working directory as a path. Always includes a trailing slash.\n * Note that this computes the `cwd` from fresh every time, in case it has changed for the current process.\n */\n static get cwd(): Path {\n return new Path(cwd()).toggleTrailingSlash(true);\n }\n\n static xdg = xdg;\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\nexport class AsyncDisposablePath extends Path {\n #options?: { disposePathInstead: Path };\n constructor(\n path: ConstructorParameters<typeof Path>[0],\n options?: { disposePathInstead: Path | string },\n ) {\n super(path);\n if (options) {\n this.#options = {\n disposePathInstead: new Path(options.disposePathInstead),\n };\n }\n }\n\n async [Symbol.asyncDispose]() {\n await (this.#options?.disposePathInstead ?? this).rm_rf();\n }\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", "const MACOS_DOT_DS_STORE = \".DS_Store\";\nconst MACOS_ICON_PATH = \"Icon\\r\";\nconst WINDOWS_THUMBS_DOT_DB = \"Thumbs.db\";\nconst WINDOWS_DESKTOP_INI = \"Desktop.ini\";\n\nexport const ALL_KNOWN_AUTOGENERATED_FILES = [\n MACOS_DOT_DS_STORE,\n MACOS_ICON_PATH,\n WINDOWS_THUMBS_DOT_DB,\n WINDOWS_DESKTOP_INI,\n];\n\nexport interface IgnoreAutoGeneratedFilesOptions {\n ignoreOSAutogeneratedFiles?:\n | true\n | {\n macOS?:\n | true\n | {\n dotDSStore?: boolean;\n iconPath?: boolean;\n };\n windows?:\n | true\n | {\n // TODO: this can have alternative names?\n ThumbsDotDB?: boolean;\n DesktopDotIni?: boolean;\n };\n // TODO: anything common on Linux?\n };\n}\n\n/**\n * Modifies the input `entries`.\n */\nfunction removeAutogeneratedFileEntries(\n entries: Set<string>,\n options?: IgnoreAutoGeneratedFilesOptions,\n) {\n const ignore = options?.ignoreOSAutogeneratedFiles ?? {};\n\n // biome-ignore lint/complexity/noUselessLoneBlockStatements: Grouping for legibility.\n {\n if (ignore === true || ignore?.macOS === true || ignore.macOS?.dotDSStore) {\n entries.delete(MACOS_DOT_DS_STORE);\n }\n if (ignore === true || ignore?.macOS === true || ignore.macOS?.iconPath) {\n entries.delete(MACOS_ICON_PATH);\n }\n }\n\n // biome-ignore lint/complexity/noUselessLoneBlockStatements: Grouping for legibility.\n {\n if (\n ignore === true ||\n ignore?.windows === true ||\n ignore.windows?.ThumbsDotDB\n ) {\n entries.delete(WINDOWS_THUMBS_DOT_DB);\n }\n if (\n ignore === true ||\n ignore?.windows === true ||\n ignore.windows?.DesktopDotIni\n ) {\n entries.delete(WINDOWS_DESKTOP_INI);\n }\n }\n}\n\nexport function isEmptyExceptForAutoGeneratedFiles(\n entries: Iterable<string>,\n options?: IgnoreAutoGeneratedFilesOptions,\n) {\n const entriesSet = new Set(entries);\n removeAutogeneratedFileEntries(entriesSet, options);\n return entriesSet.size === 0;\n}\n", "export interface WriteJSONOptions {\n replacer?: Parameters<typeof JSON.stringify>[1];\n space?: Parameters<typeof JSON.stringify>[2];\n trailingNewline?: boolean;\n}\n\nconst JSON_WRITE_DEFAULTS: WriteJSONOptions = {\n replacer: null,\n space: \" \",\n trailingNewline: true,\n};\n\n/**\n * Returns an array of chunks, to ensure we don't perforn unnecessary in-memory concatenation before writing.\n */\nexport function jsonChunks<T>(data: T, options?: WriteJSONOptions): string[] {\n const { replacer, space, trailingNewline } = {\n ...JSON_WRITE_DEFAULTS,\n ...options,\n };\n return [JSON.stringify(data, replacer, space), trailingNewline ? \"\\n\" : \"\"];\n}\n", "/** biome-ignore-all lint/complexity/useLiteralKeys: TODO: https://github.com/biomejs/biome/discussions/7572 */\n\nimport { Path } from \"./Path\";\n\nfunction env(): typeof import(\"node:process\").env {\n const { env } = globalThis.process.getBuiltinModule(\"node:process\");\n return env;\n}\n\nfunction varWithoutFallback(envVar: `XDG_${string}`): Path | undefined {\n const value = env()[envVar];\n return value ? Path.fromString(value) : undefined;\n}\n\nfunction varWithFallback(\n envVar: `XDG_${string}`,\n fallbackInHomeDir: string,\n): Path {\n return varWithoutFallback(envVar) ?? Path.homedir.join(fallbackInHomeDir);\n}\n\n// This is a level of indirection to avoid accessing env vars or `homedir()`\n// until needed, to avoid permissions prompts / flags for `deno` when these are\n// not needed for the calling code.\n//\n// TODO: cache values?\nclass XDG {\n get cache() {\n return varWithFallback(\"XDG_CACHE_HOME\", \".cache\");\n }\n get config() {\n return varWithFallback(\"XDG_CONFIG_HOME\", \".config\");\n }\n get data() {\n return varWithFallback(\"XDG_DATA_HOME\", \".local/share\");\n }\n get state() {\n return varWithFallback(\"XDG_STATE_HOME\", \".local/state\");\n }\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 get runtime() {\n return varWithoutFallback(\"XDG_RUNTIME_DIR\");\n }\n get runtimeWithStateFallback() {\n return varWithoutFallback(\"XDG_RUNTIME_DIR\") ?? this.state;\n }\n}\n\nexport const xdg = new XDG();\n", "import { Path } from \"./Path\";\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: Exclude<T, Path> | Path): T | string {\n if (value instanceof Path) {\n return value.toString();\n }\n return value;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["env", "ResolutionPrefix"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const ALL_KNOWN_AUTOGENERATED_FILES: string[];
|
|
2
|
+
export interface IgnoreAutoGeneratedFilesOptions {
|
|
3
|
+
ignoreOSAutogeneratedFiles?: true | {
|
|
4
|
+
macOS?: true | {
|
|
5
|
+
dotDSStore?: boolean;
|
|
6
|
+
iconPath?: boolean;
|
|
7
|
+
};
|
|
8
|
+
windows?: true | {
|
|
9
|
+
ThumbsDotDB?: boolean;
|
|
10
|
+
DesktopDotIni?: boolean;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export declare function isEmptyExceptForAutoGeneratedFiles(entries: Iterable<string>, options?: IgnoreAutoGeneratedFilesOptions): boolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { appendFileSync, chmodSync, cpSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { type IgnoreAutoGeneratedFilesOptions } from "../emptyish";
|
|
2
3
|
import { type WriteJSONOptions } from "../json";
|
|
3
4
|
import { Path } from "../Path";
|
|
4
5
|
import type { lstatSyncType, readDirSyncType, readFileSyncType, statSyncType } from "./modifiedNodeTypes";
|
|
@@ -69,6 +70,55 @@ export declare class PathSync extends Path {
|
|
|
69
70
|
writeSync(data: Parameters<typeof writeFileSync>[1], options?: Parameters<typeof writeFileSync>[2]): PathSync;
|
|
70
71
|
writeJSONSync<T>(data: T, options?: WriteJSONOptions): PathSync;
|
|
71
72
|
readDirSync: typeof readDirSyncType;
|
|
73
|
+
/**
|
|
74
|
+
* - Returns false if the path does not exists.
|
|
75
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
76
|
+
*/
|
|
77
|
+
isEmptyDirSync(options?: IgnoreAutoGeneratedFilesOptions): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Equivalent to:
|
|
80
|
+
*
|
|
81
|
+
* ```
|
|
82
|
+
* .isEmptyDir({
|
|
83
|
+
* ignoreOSAutogeneratedFiles: {
|
|
84
|
+
* macOS: true,
|
|
85
|
+
* windows: true,
|
|
86
|
+
* },
|
|
87
|
+
* })
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* Note that this ignores common files automatically created by the operating
|
|
91
|
+
* system. These are *often* vestigial and unnecessary, but may occasionally
|
|
92
|
+
* be important to the user (e.g. an icon for an empty folder, or folder
|
|
93
|
+
* layout information that will be used once files are added atain).
|
|
94
|
+
*
|
|
95
|
+
* There is no general way to tell whether a given folder is "safe" to delete,
|
|
96
|
+
* but this can be a useful heuristic to tell whether a folder is "actually"
|
|
97
|
+
* empty when it is fully managed by the calling program and otherwise empty.
|
|
98
|
+
*
|
|
99
|
+
* - Returns `false` if the path does not exists.
|
|
100
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
101
|
+
*/
|
|
102
|
+
isEmptyishDirSync(): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* A wrapper around `.rmDir()` that ignores files according to
|
|
105
|
+
* `.isEmptyishDir()`.
|
|
106
|
+
*
|
|
107
|
+
* Note that this ignores common files automatically created by the operating
|
|
108
|
+
* system. These are *often* vestigial and unnecessary, but may occasionally
|
|
109
|
+
* be important to the user (e.g. an icon for an empty folder, or folder
|
|
110
|
+
* layout information that will be used once files are added atain).
|
|
111
|
+
*
|
|
112
|
+
* There is no general way to tell whether a given folder is "safe" to delete,
|
|
113
|
+
* but this can be a useful heuristic to tell whether a folder is "actually"
|
|
114
|
+
* empty when it is fully managed by the calling program and otherwise empty.
|
|
115
|
+
*
|
|
116
|
+
* - Returns `true` if the directory was removed.
|
|
117
|
+
* - Returns `false` if the directory is not emptyish.
|
|
118
|
+
* - Returns `false` if the path does not exists.
|
|
119
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
120
|
+
*/
|
|
121
|
+
rmDirIfEmptyishSync(): boolean;
|
|
72
122
|
symlinkSync(target: string | URL | Path, type?: Parameters<typeof symlinkSync>[2]): PathSync;
|
|
73
123
|
realpathSync(): PathSync;
|
|
74
124
|
statSync: typeof statSyncType;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ALL_KNOWN_AUTOGENERATED_FILES,
|
|
2
3
|
Path,
|
|
4
|
+
isEmptyExceptForAutoGeneratedFiles,
|
|
3
5
|
jsonChunks,
|
|
4
6
|
mustNotHaveTrailingSlash
|
|
5
|
-
} from "../chunks/chunk-
|
|
7
|
+
} from "../chunks/chunk-DIQ7KWIN.js";
|
|
6
8
|
|
|
7
9
|
// src/sync/PathSync.ts
|
|
8
10
|
import {
|
|
@@ -227,6 +229,77 @@ var PathSync = class _PathSync extends Path {
|
|
|
227
229
|
// biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.
|
|
228
230
|
readdirSync(this.path, options)
|
|
229
231
|
);
|
|
232
|
+
/**
|
|
233
|
+
* - Returns false if the path does not exists.
|
|
234
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
235
|
+
*/
|
|
236
|
+
// TODO: return `false` when the path is a file?
|
|
237
|
+
isEmptyDirSync(options) {
|
|
238
|
+
if (!this.existsAsDirSync()) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
return isEmptyExceptForAutoGeneratedFiles(this.readDirSync(), options);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Equivalent to:
|
|
245
|
+
*
|
|
246
|
+
* ```
|
|
247
|
+
* .isEmptyDir({
|
|
248
|
+
* ignoreOSAutogeneratedFiles: {
|
|
249
|
+
* macOS: true,
|
|
250
|
+
* windows: true,
|
|
251
|
+
* },
|
|
252
|
+
* })
|
|
253
|
+
* ```
|
|
254
|
+
*
|
|
255
|
+
* Note that this ignores common files automatically created by the operating
|
|
256
|
+
* system. These are *often* vestigial and unnecessary, but may occasionally
|
|
257
|
+
* be important to the user (e.g. an icon for an empty folder, or folder
|
|
258
|
+
* layout information that will be used once files are added atain).
|
|
259
|
+
*
|
|
260
|
+
* There is no general way to tell whether a given folder is "safe" to delete,
|
|
261
|
+
* but this can be a useful heuristic to tell whether a folder is "actually"
|
|
262
|
+
* empty when it is fully managed by the calling program and otherwise empty.
|
|
263
|
+
*
|
|
264
|
+
* - Returns `false` if the path does not exists.
|
|
265
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
266
|
+
*/
|
|
267
|
+
isEmptyishDirSync() {
|
|
268
|
+
return this.isEmptyDirSync({
|
|
269
|
+
ignoreOSAutogeneratedFiles: {
|
|
270
|
+
macOS: true,
|
|
271
|
+
windows: true
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* A wrapper around `.rmDir()` that ignores files according to
|
|
277
|
+
* `.isEmptyishDir()`.
|
|
278
|
+
*
|
|
279
|
+
* Note that this ignores common files automatically created by the operating
|
|
280
|
+
* system. These are *often* vestigial and unnecessary, but may occasionally
|
|
281
|
+
* be important to the user (e.g. an icon for an empty folder, or folder
|
|
282
|
+
* layout information that will be used once files are added atain).
|
|
283
|
+
*
|
|
284
|
+
* There is no general way to tell whether a given folder is "safe" to delete,
|
|
285
|
+
* but this can be a useful heuristic to tell whether a folder is "actually"
|
|
286
|
+
* empty when it is fully managed by the calling program and otherwise empty.
|
|
287
|
+
*
|
|
288
|
+
* - Returns `true` if the directory was removed.
|
|
289
|
+
* - Returns `false` if the directory is not emptyish.
|
|
290
|
+
* - Returns `false` if the path does not exists.
|
|
291
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
292
|
+
*/
|
|
293
|
+
rmDirIfEmptyishSync() {
|
|
294
|
+
if (this.isEmptyishDirSync()) {
|
|
295
|
+
ALL_KNOWN_AUTOGENERATED_FILES.map(
|
|
296
|
+
(path) => this.join(path).rmSync({ force: true })
|
|
297
|
+
);
|
|
298
|
+
this.rmDirSync();
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
230
303
|
symlinkSync(target, type) {
|
|
231
304
|
const targetPath = new _PathSync(target);
|
|
232
305
|
symlinkSync(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/sync/PathSync.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n appendFileSync,\n chmodSync,\n cpSync,\n lstatSync,\n mkdirSync,\n mkdtempSync,\n readdirSync,\n readFileSync,\n realpathSync,\n renameSync,\n rmdirSync,\n rmSync,\n statSync,\n symlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { constants } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { jsonChunks, type WriteJSONOptions } from \"../json\";\nimport { mustNotHaveTrailingSlash, Path } from \"../Path\";\nimport type {\n lstatSyncType,\n readDirSyncType,\n readFileSyncType,\n statSyncType,\n} from \"./modifiedNodeTypes\";\n\nconst DEFAULT_TEMP_PREFIX = \"js-temp-sync-\";\nconst DEFAULT_TEMP_FILE_NAME = \"file\";\n\nexport class PathSync extends Path {\n static override fromString(s: string): PathSync {\n return new PathSync(s);\n }\n\n static override resolve(...args: Parameters<typeof Path.resolve>): PathSync {\n return new PathSync(Path.resolve(...args));\n }\n\n override resolve(...args: Parameters<Path[\"resolve\"]>): PathSync {\n return new PathSync(super.resolve(...args));\n }\n\n override descendantRelativePath(\n ...args: Parameters<Path[\"descendantRelativePath\"]>\n ): PathSync | null {\n const v = super.descendantRelativePath(...args);\n if (v === null) {\n return null;\n }\n return new PathSync(v);\n }\n\n override toggleTrailingSlash(\n ...args: Parameters<Path[\"toggleTrailingSlash\"]>\n ): PathSync {\n return new PathSync(super.toggleTrailingSlash(...args));\n }\n\n override join(...args: Parameters<Path[\"join\"]>): PathSync {\n return new PathSync(super.join(...args));\n }\n\n override asRelative(...args: Parameters<Path[\"asRelative\"]>): PathSync {\n return new PathSync(super.asRelative(...args));\n }\n\n override asAbsolute(...args: Parameters<Path[\"asAbsolute\"]>): PathSync {\n return new PathSync(super.asAbsolute(...args));\n }\n\n override asBare(...args: Parameters<Path[\"asBare\"]>): PathSync {\n return new PathSync(super.asBare(...args));\n }\n\n override extendBasename(\n ...args: Parameters<Path[\"extendBasename\"]>\n ): PathSync {\n return new PathSync(super.extendBasename(...args));\n }\n\n override get parent(): PathSync {\n return new PathSync(super.parent);\n }\n\n override get dirname(): PathSync {\n return new PathSync(super.dirname);\n }\n\n override get basename(): PathSync {\n return new PathSync(super.basename);\n }\n\n static override get homedir(): PathSync {\n return new PathSync(Path.homedir);\n }\n\n static override get cwd(): PathSync {\n return new PathSync(Path.cwd);\n }\n\n override debugPrint(...args: Parameters<Path[\"debugPrint\"]>): PathSync {\n return new PathSync(super.debugPrint(...args));\n }\n\n // TODO: find a neat way to dedup with the async version? // lint-sync-code-expect-error\n existsSync(constraints?: { mustBe: \"file\" | \"directory\" }): boolean {\n if (constraints?.mustBe === \"file\") {\n mustNotHaveTrailingSlash(this);\n }\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 if (stats.isFile()) {\n return true;\n }\n throw new Error(`PathSync exists but is not a file: ${this.path}`);\n }\n case \"directory\": {\n if (stats.isDirectory()) {\n return true;\n }\n throw new Error(`PathSync exists but is not a directory: ${this.path}`);\n }\n default: {\n throw new Error(\"Invalid path type constraint\");\n }\n }\n }\n\n existsAsFileSync(): boolean {\n return this.existsSync({ mustBe: \"file\" });\n }\n\n existsAsDirSync(): boolean {\n return this.existsSync({ mustBe: \"directory\" });\n }\n\n mkdirSync(options?: Parameters<typeof mkdirSync>[1]): PathSync {\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\n cpSync(\n destination: string | URL | Path,\n options?: Parameters<typeof cpSync>[2] & {\n createIntermediateDirs?: boolean;\n },\n ): PathSync {\n const { createIntermediateDirs, ...cpOptions } = options ?? {};\n const destinationPath = new PathSync(destination);\n if (createIntermediateDirs ?? true) {\n destinationPath.parent.mkdirSync();\n }\n cpSync(this.path, destinationPath.path, cpOptions);\n return destinationPath;\n }\n\n renameSync(\n destination: string | URL | Path,\n options?: { createIntermediateDirs?: boolean },\n ): PathSync {\n const destinationPath = new PathSync(destination);\n if (options?.createIntermediateDirs ?? true) {\n destinationPath.parent.mkdirSync();\n }\n renameSync(this.path, destinationPath.path);\n return destinationPath;\n }\n\n static makeTempDirSync(prefix?: string): DisposablePathSync {\n return new DisposablePathSync(\n mkdtempSync(\n new Path(tmpdir()).join(prefix ?? DEFAULT_TEMP_PREFIX).toString(),\n ),\n );\n }\n\n /**\n * Return a path:\n *\n * - whose parent dir is a temp dir that *has* been created, but\n * - which has itself not yet been created.\n *\n * Note that this path can actually also be used to create dir, but it is most\n * convenient to get a path for a temporary file that can be written to, while\n * having a disposal implementation that cleans everything up:\n *\n * using tempFile = PathSync.tempFilePathSync({ basename: \"foo.txt\" });\n * tempFile.writeSync(\"hello world!\");\n * // \u2026\n *\n * Note that that the following are equivalent when *not* using `using`:\n *\n * PathSync.tempFilePathSync({ basename: \"foo.txt\" });\n * PathSync.makeTempDirSync().join(\"file.txt\");\n *\n * However, it is recommended to use `using` to ensure cleanup.\n */\n static tempFilePathSync(options: {\n tempDirPrefix?: string;\n basename?: string | Path;\n }): DisposablePathSync {\n const tempDir = PathSync.makeTempDirSync(options?.tempDirPrefix);\n return new DisposablePathSync(\n tempDir.join(options?.basename ?? DEFAULT_TEMP_FILE_NAME),\n { disposePathInstead: tempDir },\n );\n }\n\n rmSync(options?: Parameters<typeof rmSync>[1]): void {\n rmSync(this.path, options);\n }\n\n rmDirSync(): void {\n rmdirSync(this.path);\n }\n\n rm_rfSync(options?: Parameters<typeof rmSync>[1]): void {\n this.rmSync({ recursive: true, force: true, ...(options ?? {}) });\n }\n\n readSync: typeof readFileSyncType = (options) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n readFileSync(this.path, options) as any;\n\n readTextSync(): string {\n return readFileSync(this.path, \"utf-8\");\n }\n\n readJSONSync<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\n appendFileSync(\n data: Parameters<typeof appendFileSync>[1],\n options?: Parameters<typeof appendFileSync>[2],\n ): PathSync {\n appendFileSync(this.path, data, options);\n return this;\n }\n\n writeSync(\n data: Parameters<typeof writeFileSync>[1],\n options?: Parameters<typeof writeFileSync>[2],\n ): PathSync {\n this.parent.mkdirSync();\n writeFileSync(this.path, data, options);\n return this;\n }\n\n writeJSONSync<T>(data: T, options?: WriteJSONOptions): PathSync {\n this.parent.mkdirSync();\n // TODO: use a file handle to avoid concatenation.\n this.writeSync(jsonChunks(data, options).join(\"\"));\n return this;\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Type wrangling.\n readDirSync: typeof readDirSyncType = (options: any) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n readdirSync(this.path, options) as any;\n\n symlinkSync(\n target: string | URL | Path,\n type?: Parameters<typeof symlinkSync>[2],\n ): PathSync {\n const targetPath = new PathSync(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 realpathSync(): PathSync {\n return new PathSync(realpathSync(this.path));\n }\n\n statSync: typeof statSyncType = (options) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n statSync(this.path, options) as any;\n\n lstatSync: typeof lstatSyncType = (options) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n lstatSync(this.path, options) as any;\n\n chmodSync(mode: Parameters<typeof chmodSync>[1]): PathSync {\n chmodSync(this.path, mode);\n return this;\n }\n\n chmodXSync(): PathSync {\n const { mode } = this.statSync();\n this.chmodSync(\n mode | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH,\n );\n return this;\n }\n}\n\nexport class DisposablePathSync extends PathSync {\n #options?: { disposePathInstead: PathSync };\n constructor(\n path: ConstructorParameters<typeof Path>[0],\n options?: { disposePathInstead: Path | string },\n ) {\n super(path);\n if (options) {\n this.#options = {\n disposePathInstead: new PathSync(options.disposePathInstead),\n };\n }\n }\n\n [Symbol.dispose]() {\n (this.#options?.disposePathInstead ?? this).rm_rfSync();\n }\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import {\n appendFileSync,\n chmodSync,\n cpSync,\n lstatSync,\n mkdirSync,\n mkdtempSync,\n readdirSync,\n readFileSync,\n realpathSync,\n renameSync,\n rmdirSync,\n rmSync,\n statSync,\n symlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { constants } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport {\n ALL_KNOWN_AUTOGENERATED_FILES,\n type IgnoreAutoGeneratedFilesOptions,\n isEmptyExceptForAutoGeneratedFiles,\n} from \"../emptyish\";\nimport { jsonChunks, type WriteJSONOptions } from \"../json\";\nimport { mustNotHaveTrailingSlash, Path } from \"../Path\";\nimport type {\n lstatSyncType,\n readDirSyncType,\n readFileSyncType,\n statSyncType,\n} from \"./modifiedNodeTypes\";\n\nconst DEFAULT_TEMP_PREFIX = \"js-temp-sync-\";\nconst DEFAULT_TEMP_FILE_NAME = \"file\";\n\nexport class PathSync extends Path {\n static override fromString(s: string): PathSync {\n return new PathSync(s);\n }\n\n static override resolve(...args: Parameters<typeof Path.resolve>): PathSync {\n return new PathSync(Path.resolve(...args));\n }\n\n override resolve(...args: Parameters<Path[\"resolve\"]>): PathSync {\n return new PathSync(super.resolve(...args));\n }\n\n override descendantRelativePath(\n ...args: Parameters<Path[\"descendantRelativePath\"]>\n ): PathSync | null {\n const v = super.descendantRelativePath(...args);\n if (v === null) {\n return null;\n }\n return new PathSync(v);\n }\n\n override toggleTrailingSlash(\n ...args: Parameters<Path[\"toggleTrailingSlash\"]>\n ): PathSync {\n return new PathSync(super.toggleTrailingSlash(...args));\n }\n\n override join(...args: Parameters<Path[\"join\"]>): PathSync {\n return new PathSync(super.join(...args));\n }\n\n override asRelative(...args: Parameters<Path[\"asRelative\"]>): PathSync {\n return new PathSync(super.asRelative(...args));\n }\n\n override asAbsolute(...args: Parameters<Path[\"asAbsolute\"]>): PathSync {\n return new PathSync(super.asAbsolute(...args));\n }\n\n override asBare(...args: Parameters<Path[\"asBare\"]>): PathSync {\n return new PathSync(super.asBare(...args));\n }\n\n override extendBasename(\n ...args: Parameters<Path[\"extendBasename\"]>\n ): PathSync {\n return new PathSync(super.extendBasename(...args));\n }\n\n override get parent(): PathSync {\n return new PathSync(super.parent);\n }\n\n override get dirname(): PathSync {\n return new PathSync(super.dirname);\n }\n\n override get basename(): PathSync {\n return new PathSync(super.basename);\n }\n\n static override get homedir(): PathSync {\n return new PathSync(Path.homedir);\n }\n\n static override get cwd(): PathSync {\n return new PathSync(Path.cwd);\n }\n\n override debugPrint(...args: Parameters<Path[\"debugPrint\"]>): PathSync {\n return new PathSync(super.debugPrint(...args));\n }\n\n // TODO: find a neat way to dedup with the async version? // lint-sync-code-expect-error\n existsSync(constraints?: { mustBe: \"file\" | \"directory\" }): boolean {\n if (constraints?.mustBe === \"file\") {\n mustNotHaveTrailingSlash(this);\n }\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 if (stats.isFile()) {\n return true;\n }\n throw new Error(`PathSync exists but is not a file: ${this.path}`);\n }\n case \"directory\": {\n if (stats.isDirectory()) {\n return true;\n }\n throw new Error(`PathSync exists but is not a directory: ${this.path}`);\n }\n default: {\n throw new Error(\"Invalid path type constraint\");\n }\n }\n }\n\n existsAsFileSync(): boolean {\n return this.existsSync({ mustBe: \"file\" });\n }\n\n existsAsDirSync(): boolean {\n return this.existsSync({ mustBe: \"directory\" });\n }\n\n mkdirSync(options?: Parameters<typeof mkdirSync>[1]): PathSync {\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\n cpSync(\n destination: string | URL | Path,\n options?: Parameters<typeof cpSync>[2] & {\n createIntermediateDirs?: boolean;\n },\n ): PathSync {\n const { createIntermediateDirs, ...cpOptions } = options ?? {};\n const destinationPath = new PathSync(destination);\n if (createIntermediateDirs ?? true) {\n destinationPath.parent.mkdirSync();\n }\n cpSync(this.path, destinationPath.path, cpOptions);\n return destinationPath;\n }\n\n renameSync(\n destination: string | URL | Path,\n options?: { createIntermediateDirs?: boolean },\n ): PathSync {\n const destinationPath = new PathSync(destination);\n if (options?.createIntermediateDirs ?? true) {\n destinationPath.parent.mkdirSync();\n }\n renameSync(this.path, destinationPath.path);\n return destinationPath;\n }\n\n static makeTempDirSync(prefix?: string): DisposablePathSync {\n return new DisposablePathSync(\n mkdtempSync(\n new Path(tmpdir()).join(prefix ?? DEFAULT_TEMP_PREFIX).toString(),\n ),\n );\n }\n\n /**\n * Return a path:\n *\n * - whose parent dir is a temp dir that *has* been created, but\n * - which has itself not yet been created.\n *\n * Note that this path can actually also be used to create dir, but it is most\n * convenient to get a path for a temporary file that can be written to, while\n * having a disposal implementation that cleans everything up:\n *\n * using tempFile = PathSync.tempFilePathSync({ basename: \"foo.txt\" });\n * tempFile.writeSync(\"hello world!\");\n * // \u2026\n *\n * Note that that the following are equivalent when *not* using `using`:\n *\n * PathSync.tempFilePathSync({ basename: \"foo.txt\" });\n * PathSync.makeTempDirSync().join(\"file.txt\");\n *\n * However, it is recommended to use `using` to ensure cleanup.\n */\n static tempFilePathSync(options: {\n tempDirPrefix?: string;\n basename?: string | Path;\n }): DisposablePathSync {\n const tempDir = PathSync.makeTempDirSync(options?.tempDirPrefix);\n return new DisposablePathSync(\n tempDir.join(options?.basename ?? DEFAULT_TEMP_FILE_NAME),\n { disposePathInstead: tempDir },\n );\n }\n\n rmSync(options?: Parameters<typeof rmSync>[1]): void {\n rmSync(this.path, options);\n }\n\n rmDirSync(): void {\n rmdirSync(this.path);\n }\n\n rm_rfSync(options?: Parameters<typeof rmSync>[1]): void {\n this.rmSync({ recursive: true, force: true, ...(options ?? {}) });\n }\n\n readSync: typeof readFileSyncType = (options) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n readFileSync(this.path, options) as any;\n\n readTextSync(): string {\n return readFileSync(this.path, \"utf-8\");\n }\n\n readJSONSync<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\n appendFileSync(\n data: Parameters<typeof appendFileSync>[1],\n options?: Parameters<typeof appendFileSync>[2],\n ): PathSync {\n appendFileSync(this.path, data, options);\n return this;\n }\n\n writeSync(\n data: Parameters<typeof writeFileSync>[1],\n options?: Parameters<typeof writeFileSync>[2],\n ): PathSync {\n this.parent.mkdirSync();\n writeFileSync(this.path, data, options);\n return this;\n }\n\n writeJSONSync<T>(data: T, options?: WriteJSONOptions): PathSync {\n this.parent.mkdirSync();\n // TODO: use a file handle to avoid concatenation.\n this.writeSync(jsonChunks(data, options).join(\"\"));\n return this;\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: Type wrangling.\n readDirSync: typeof readDirSyncType = (options: any) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n readdirSync(this.path, options) as any;\n\n /**\n * - Returns false if the path does not exists.\n * - Throws an error if the path exists as a file rather than a dir.\n */\n // TODO: return `false` when the path is a file?\n isEmptyDirSync(options?: IgnoreAutoGeneratedFilesOptions): boolean {\n if (!this.existsAsDirSync()) {\n return false;\n }\n return isEmptyExceptForAutoGeneratedFiles(this.readDirSync(), options);\n }\n\n /**\n * Equivalent to:\n *\n * ```\n * .isEmptyDir({\n * ignoreOSAutogeneratedFiles: {\n * macOS: true,\n * windows: true,\n * },\n * })\n * ```\n *\n * Note that this ignores common files automatically created by the operating\n * system. These are *often* vestigial and unnecessary, but may occasionally\n * be important to the user (e.g. an icon for an empty folder, or folder\n * layout information that will be used once files are added atain).\n *\n * There is no general way to tell whether a given folder is \"safe\" to delete,\n * but this can be a useful heuristic to tell whether a folder is \"actually\"\n * empty when it is fully managed by the calling program and otherwise empty.\n *\n * - Returns `false` if the path does not exists.\n * - Throws an error if the path exists as a file rather than a dir.\n */\n isEmptyishDirSync(): boolean {\n return this.isEmptyDirSync({\n ignoreOSAutogeneratedFiles: {\n macOS: true,\n windows: true,\n },\n });\n }\n\n /**\n * A wrapper around `.rmDir()` that ignores files according to\n * `.isEmptyishDir()`.\n *\n * Note that this ignores common files automatically created by the operating\n * system. These are *often* vestigial and unnecessary, but may occasionally\n * be important to the user (e.g. an icon for an empty folder, or folder\n * layout information that will be used once files are added atain).\n *\n * There is no general way to tell whether a given folder is \"safe\" to delete,\n * but this can be a useful heuristic to tell whether a folder is \"actually\"\n * empty when it is fully managed by the calling program and otherwise empty.\n *\n * - Returns `true` if the directory was removed.\n * - Returns `false` if the directory is not emptyish.\n * - Returns `false` if the path does not exists.\n * - Throws an error if the path exists as a file rather than a dir.\n */\n rmDirIfEmptyishSync(): boolean {\n if (this.isEmptyishDirSync()) {\n ALL_KNOWN_AUTOGENERATED_FILES.map((path) =>\n this.join(path).rmSync({ force: true }),\n );\n this.rmDirSync();\n return true;\n }\n return false;\n }\n\n symlinkSync(\n target: string | URL | Path,\n type?: Parameters<typeof symlinkSync>[2],\n ): PathSync {\n const targetPath = new PathSync(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 realpathSync(): PathSync {\n return new PathSync(realpathSync(this.path));\n }\n\n statSync: typeof statSyncType = (options) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n statSync(this.path, options) as any;\n\n lstatSync: typeof lstatSyncType = (options) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n lstatSync(this.path, options) as any;\n\n chmodSync(mode: Parameters<typeof chmodSync>[1]): PathSync {\n chmodSync(this.path, mode);\n return this;\n }\n\n chmodXSync(): PathSync {\n const { mode } = this.statSync();\n this.chmodSync(\n mode | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH,\n );\n return this;\n }\n}\n\nexport class DisposablePathSync extends PathSync {\n #options?: { disposePathInstead: PathSync };\n constructor(\n path: ConstructorParameters<typeof Path>[0],\n options?: { disposePathInstead: Path | string },\n ) {\n super(path);\n if (options) {\n this.#options = {\n disposePathInstead: new PathSync(options.disposePathInstead),\n };\n }\n }\n\n [Symbol.dispose]() {\n (this.#options?.disposePathInstead ?? this).rm_rfSync();\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "path-class",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "A semantic `Path` class for `node` and `bun`. Inspired by `bun`'s [`file(…)`](https://bun.com/docs/runtime/file-io) API, but `node`-compatible.",
|
|
5
5
|
"author": "Lucas Garron <code@garron.net>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@biomejs/biome": "^2.4.8",
|
|
30
30
|
"@cubing/dev-config": ">=0.9.4",
|
|
31
|
-
"@types/bun": "^1.3.
|
|
32
|
-
"@typescript/native-preview": "^7.0.0-dev.
|
|
31
|
+
"@types/bun": "^1.3.14",
|
|
32
|
+
"@typescript/native-preview": "^7.0.0-dev.20260512.1",
|
|
33
33
|
"bun-dx": ">=0.1.3",
|
|
34
34
|
"ergonomic-date": "^0.1.3",
|
|
35
35
|
"esbuild": "^0.25.10",
|
|
@@ -42,8 +42,5 @@
|
|
|
42
42
|
],
|
|
43
43
|
"scripts": {
|
|
44
44
|
"prepublishOnly": "make prepublishOnly"
|
|
45
|
-
},
|
|
46
|
-
"patchedDependencies": {
|
|
47
|
-
"bun-types@1.3.13": "patches/bun-types@1.3.13.patch"
|
|
48
45
|
}
|
|
49
46
|
}
|
package/src/Path.test.ts
CHANGED
|
@@ -713,6 +713,28 @@ test.concurrent(".readDir(…)", async () => {
|
|
|
713
713
|
);
|
|
714
714
|
});
|
|
715
715
|
|
|
716
|
+
test.concurrent(".isEmptyDir(…), .isEmptyishDir(…), and .rmDirIfEmptyish()", async () => {
|
|
717
|
+
await using dir = await Path.makeTempDir();
|
|
718
|
+
|
|
719
|
+
expect(await dir.isEmptyDir()).toBe(true);
|
|
720
|
+
expect(await dir.isEmptyishDir()).toBe(true);
|
|
721
|
+
|
|
722
|
+
await dir.join(".DS_Store").write("");
|
|
723
|
+
expect(await dir.isEmptyDir()).toBe(false);
|
|
724
|
+
expect(await dir.isEmptyishDir()).toBe(true);
|
|
725
|
+
|
|
726
|
+
await dir.join("foo.txt").write("");
|
|
727
|
+
expect(await dir.isEmptyDir()).toBe(false);
|
|
728
|
+
expect(await dir.isEmptyishDir()).toBe(false);
|
|
729
|
+
|
|
730
|
+
expect(await dir.rmDirIfEmptyish()).toBe(false);
|
|
731
|
+
expect(await dir.exists()).toBe(true);
|
|
732
|
+
|
|
733
|
+
await dir.join("foo.txt").rm();
|
|
734
|
+
expect(await dir.rmDirIfEmptyish()).toBe(true);
|
|
735
|
+
expect(await dir.exists()).toBe(false);
|
|
736
|
+
});
|
|
737
|
+
|
|
716
738
|
test.concurrent(".symlink(…)", async () => {
|
|
717
739
|
await using tempDir = await Path.makeTempDir();
|
|
718
740
|
const source = tempDir.join("foo.txt");
|
package/src/Path.ts
CHANGED
|
@@ -23,6 +23,11 @@ import { cwd } from "node:process";
|
|
|
23
23
|
import { createInterface } from "node:readline/promises";
|
|
24
24
|
import { Readable } from "node:stream";
|
|
25
25
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
26
|
+
import {
|
|
27
|
+
ALL_KNOWN_AUTOGENERATED_FILES,
|
|
28
|
+
type IgnoreAutoGeneratedFilesOptions,
|
|
29
|
+
isEmptyExceptForAutoGeneratedFiles,
|
|
30
|
+
} from "./emptyish";
|
|
26
31
|
import { jsonChunks, type WriteJSONOptions } from "./json";
|
|
27
32
|
import type {
|
|
28
33
|
lstatType,
|
|
@@ -743,6 +748,79 @@ export class Path {
|
|
|
743
748
|
// biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.
|
|
744
749
|
readdir(this.#path, options as any) as any;
|
|
745
750
|
|
|
751
|
+
/**
|
|
752
|
+
* - Returns false if the path does not exists.
|
|
753
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
754
|
+
*/
|
|
755
|
+
// TODO: return `false` when the path is a file?
|
|
756
|
+
async isEmptyDir(
|
|
757
|
+
options?: IgnoreAutoGeneratedFilesOptions,
|
|
758
|
+
): Promise<boolean> {
|
|
759
|
+
if (!this.existsAsDir()) {
|
|
760
|
+
return false;
|
|
761
|
+
}
|
|
762
|
+
return isEmptyExceptForAutoGeneratedFiles(await this.readDir(), options);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* Equivalent to:
|
|
767
|
+
*
|
|
768
|
+
* ```
|
|
769
|
+
* .isEmptyDir({
|
|
770
|
+
* ignoreOSAutogeneratedFiles: true,
|
|
771
|
+
* })
|
|
772
|
+
* ```
|
|
773
|
+
*
|
|
774
|
+
* Note that this ignores common files automatically created by the operating
|
|
775
|
+
* system. These are *often* vestigial and unnecessary, but may occasionally
|
|
776
|
+
* be important to the user (e.g. an icon for an empty folder, or folder
|
|
777
|
+
* layout information that will be used once files are added atain).
|
|
778
|
+
*
|
|
779
|
+
* There is no general way to tell whether a given folder is "safe" to delete,
|
|
780
|
+
* but this can be a useful heuristic to tell whether a folder is "actually"
|
|
781
|
+
* empty when it is fully managed by the calling program and otherwise empty.
|
|
782
|
+
*
|
|
783
|
+
* - Returns `false` if the path does not exists.
|
|
784
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
785
|
+
*/
|
|
786
|
+
async isEmptyishDir(): Promise<boolean> {
|
|
787
|
+
return this.isEmptyDir({
|
|
788
|
+
ignoreOSAutogeneratedFiles: true,
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* A wrapper around `.rmDir()` that ignores files according to
|
|
794
|
+
* `.isEmptyishDir()`.
|
|
795
|
+
*
|
|
796
|
+
* Note that this ignores common files automatically created by the operating
|
|
797
|
+
* system. These are *often* vestigial and unnecessary, but may occasionally
|
|
798
|
+
* be important to the user (e.g. an icon for an empty folder, or folder
|
|
799
|
+
* layout information that will be used once files are added atain).
|
|
800
|
+
*
|
|
801
|
+
* There is no general way to tell whether a given folder is "safe" to delete,
|
|
802
|
+
* but this can be a useful heuristic to tell whether a folder is "actually"
|
|
803
|
+
* empty when it is fully managed by the calling program and otherwise empty.
|
|
804
|
+
*
|
|
805
|
+
* - Returns `true` if the directory was removed.
|
|
806
|
+
* - Returns `false` if the directory is not emptyish.
|
|
807
|
+
* - Returns `false` if the path does not exists.
|
|
808
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
809
|
+
*/
|
|
810
|
+
async rmDirIfEmptyish(): Promise<boolean> {
|
|
811
|
+
if (await this.isEmptyishDir()) {
|
|
812
|
+
// TODO: doos any OS cause a race condition here, in practice?
|
|
813
|
+
await Promise.all([
|
|
814
|
+
ALL_KNOWN_AUTOGENERATED_FILES.map((path) =>
|
|
815
|
+
this.join(path).rm({ force: true }),
|
|
816
|
+
),
|
|
817
|
+
]);
|
|
818
|
+
await this.rmDir();
|
|
819
|
+
return true;
|
|
820
|
+
}
|
|
821
|
+
return false;
|
|
822
|
+
}
|
|
823
|
+
|
|
746
824
|
/** Returns the destination path. */
|
|
747
825
|
async symlink(
|
|
748
826
|
target: string | URL | Path,
|
package/src/emptyish.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const MACOS_DOT_DS_STORE = ".DS_Store";
|
|
2
|
+
const MACOS_ICON_PATH = "Icon\r";
|
|
3
|
+
const WINDOWS_THUMBS_DOT_DB = "Thumbs.db";
|
|
4
|
+
const WINDOWS_DESKTOP_INI = "Desktop.ini";
|
|
5
|
+
|
|
6
|
+
export const ALL_KNOWN_AUTOGENERATED_FILES = [
|
|
7
|
+
MACOS_DOT_DS_STORE,
|
|
8
|
+
MACOS_ICON_PATH,
|
|
9
|
+
WINDOWS_THUMBS_DOT_DB,
|
|
10
|
+
WINDOWS_DESKTOP_INI,
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export interface IgnoreAutoGeneratedFilesOptions {
|
|
14
|
+
ignoreOSAutogeneratedFiles?:
|
|
15
|
+
| true
|
|
16
|
+
| {
|
|
17
|
+
macOS?:
|
|
18
|
+
| true
|
|
19
|
+
| {
|
|
20
|
+
dotDSStore?: boolean;
|
|
21
|
+
iconPath?: boolean;
|
|
22
|
+
};
|
|
23
|
+
windows?:
|
|
24
|
+
| true
|
|
25
|
+
| {
|
|
26
|
+
// TODO: this can have alternative names?
|
|
27
|
+
ThumbsDotDB?: boolean;
|
|
28
|
+
DesktopDotIni?: boolean;
|
|
29
|
+
};
|
|
30
|
+
// TODO: anything common on Linux?
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Modifies the input `entries`.
|
|
36
|
+
*/
|
|
37
|
+
function removeAutogeneratedFileEntries(
|
|
38
|
+
entries: Set<string>,
|
|
39
|
+
options?: IgnoreAutoGeneratedFilesOptions,
|
|
40
|
+
) {
|
|
41
|
+
const ignore = options?.ignoreOSAutogeneratedFiles ?? {};
|
|
42
|
+
|
|
43
|
+
// biome-ignore lint/complexity/noUselessLoneBlockStatements: Grouping for legibility.
|
|
44
|
+
{
|
|
45
|
+
if (ignore === true || ignore?.macOS === true || ignore.macOS?.dotDSStore) {
|
|
46
|
+
entries.delete(MACOS_DOT_DS_STORE);
|
|
47
|
+
}
|
|
48
|
+
if (ignore === true || ignore?.macOS === true || ignore.macOS?.iconPath) {
|
|
49
|
+
entries.delete(MACOS_ICON_PATH);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// biome-ignore lint/complexity/noUselessLoneBlockStatements: Grouping for legibility.
|
|
54
|
+
{
|
|
55
|
+
if (
|
|
56
|
+
ignore === true ||
|
|
57
|
+
ignore?.windows === true ||
|
|
58
|
+
ignore.windows?.ThumbsDotDB
|
|
59
|
+
) {
|
|
60
|
+
entries.delete(WINDOWS_THUMBS_DOT_DB);
|
|
61
|
+
}
|
|
62
|
+
if (
|
|
63
|
+
ignore === true ||
|
|
64
|
+
ignore?.windows === true ||
|
|
65
|
+
ignore.windows?.DesktopDotIni
|
|
66
|
+
) {
|
|
67
|
+
entries.delete(WINDOWS_DESKTOP_INI);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function isEmptyExceptForAutoGeneratedFiles(
|
|
73
|
+
entries: Iterable<string>,
|
|
74
|
+
options?: IgnoreAutoGeneratedFilesOptions,
|
|
75
|
+
) {
|
|
76
|
+
const entriesSet = new Set(entries);
|
|
77
|
+
removeAutogeneratedFileEntries(entriesSet, options);
|
|
78
|
+
return entriesSet.size === 0;
|
|
79
|
+
}
|
|
@@ -348,6 +348,28 @@ test.concurrent(".readDirSync(…)", () => {
|
|
|
348
348
|
);
|
|
349
349
|
});
|
|
350
350
|
|
|
351
|
+
test.concurrent(".isEmptyDir(…), .isEmptyishDir(…), and .rmDirIfEmptyish()", () => {
|
|
352
|
+
using dir = PathSync.makeTempDirSync();
|
|
353
|
+
|
|
354
|
+
expect(dir.isEmptyDirSync()).toBe(true);
|
|
355
|
+
expect(dir.isEmptyishDirSync()).toBe(true);
|
|
356
|
+
|
|
357
|
+
dir.join(".DS_Store").writeSync("");
|
|
358
|
+
expect(dir.isEmptyDirSync()).toBe(false);
|
|
359
|
+
expect(dir.isEmptyishDirSync()).toBe(true);
|
|
360
|
+
|
|
361
|
+
dir.join("foo.txt").writeSync("");
|
|
362
|
+
expect(dir.isEmptyDirSync()).toBe(false);
|
|
363
|
+
expect(dir.isEmptyishDirSync()).toBe(false);
|
|
364
|
+
|
|
365
|
+
expect(dir.rmDirIfEmptyishSync()).toBe(false);
|
|
366
|
+
expect(dir.existsSync()).toBe(true);
|
|
367
|
+
|
|
368
|
+
dir.join("foo.txt").rmSync();
|
|
369
|
+
expect(dir.rmDirIfEmptyishSync()).toBe(true);
|
|
370
|
+
expect(dir.existsSync()).toBe(false);
|
|
371
|
+
});
|
|
372
|
+
|
|
351
373
|
test.concurrent(".symlinkSync(…)", () => {
|
|
352
374
|
using tempDir = PathSync.makeTempDirSync();
|
|
353
375
|
const source = tempDir.join("foo.txt");
|
package/src/sync/PathSync.ts
CHANGED
|
@@ -17,6 +17,11 @@ import {
|
|
|
17
17
|
} from "node:fs";
|
|
18
18
|
import { constants } from "node:fs/promises";
|
|
19
19
|
import { tmpdir } from "node:os";
|
|
20
|
+
import {
|
|
21
|
+
ALL_KNOWN_AUTOGENERATED_FILES,
|
|
22
|
+
type IgnoreAutoGeneratedFilesOptions,
|
|
23
|
+
isEmptyExceptForAutoGeneratedFiles,
|
|
24
|
+
} from "../emptyish";
|
|
20
25
|
import { jsonChunks, type WriteJSONOptions } from "../json";
|
|
21
26
|
import { mustNotHaveTrailingSlash, Path } from "../Path";
|
|
22
27
|
import type {
|
|
@@ -291,6 +296,80 @@ export class PathSync extends Path {
|
|
|
291
296
|
// biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.
|
|
292
297
|
readdirSync(this.path, options) as any;
|
|
293
298
|
|
|
299
|
+
/**
|
|
300
|
+
* - Returns false if the path does not exists.
|
|
301
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
302
|
+
*/
|
|
303
|
+
// TODO: return `false` when the path is a file?
|
|
304
|
+
isEmptyDirSync(options?: IgnoreAutoGeneratedFilesOptions): boolean {
|
|
305
|
+
if (!this.existsAsDirSync()) {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
return isEmptyExceptForAutoGeneratedFiles(this.readDirSync(), options);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Equivalent to:
|
|
313
|
+
*
|
|
314
|
+
* ```
|
|
315
|
+
* .isEmptyDir({
|
|
316
|
+
* ignoreOSAutogeneratedFiles: {
|
|
317
|
+
* macOS: true,
|
|
318
|
+
* windows: true,
|
|
319
|
+
* },
|
|
320
|
+
* })
|
|
321
|
+
* ```
|
|
322
|
+
*
|
|
323
|
+
* Note that this ignores common files automatically created by the operating
|
|
324
|
+
* system. These are *often* vestigial and unnecessary, but may occasionally
|
|
325
|
+
* be important to the user (e.g. an icon for an empty folder, or folder
|
|
326
|
+
* layout information that will be used once files are added atain).
|
|
327
|
+
*
|
|
328
|
+
* There is no general way to tell whether a given folder is "safe" to delete,
|
|
329
|
+
* but this can be a useful heuristic to tell whether a folder is "actually"
|
|
330
|
+
* empty when it is fully managed by the calling program and otherwise empty.
|
|
331
|
+
*
|
|
332
|
+
* - Returns `false` if the path does not exists.
|
|
333
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
334
|
+
*/
|
|
335
|
+
isEmptyishDirSync(): boolean {
|
|
336
|
+
return this.isEmptyDirSync({
|
|
337
|
+
ignoreOSAutogeneratedFiles: {
|
|
338
|
+
macOS: true,
|
|
339
|
+
windows: true,
|
|
340
|
+
},
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* A wrapper around `.rmDir()` that ignores files according to
|
|
346
|
+
* `.isEmptyishDir()`.
|
|
347
|
+
*
|
|
348
|
+
* Note that this ignores common files automatically created by the operating
|
|
349
|
+
* system. These are *often* vestigial and unnecessary, but may occasionally
|
|
350
|
+
* be important to the user (e.g. an icon for an empty folder, or folder
|
|
351
|
+
* layout information that will be used once files are added atain).
|
|
352
|
+
*
|
|
353
|
+
* There is no general way to tell whether a given folder is "safe" to delete,
|
|
354
|
+
* but this can be a useful heuristic to tell whether a folder is "actually"
|
|
355
|
+
* empty when it is fully managed by the calling program and otherwise empty.
|
|
356
|
+
*
|
|
357
|
+
* - Returns `true` if the directory was removed.
|
|
358
|
+
* - Returns `false` if the directory is not emptyish.
|
|
359
|
+
* - Returns `false` if the path does not exists.
|
|
360
|
+
* - Throws an error if the path exists as a file rather than a dir.
|
|
361
|
+
*/
|
|
362
|
+
rmDirIfEmptyishSync(): boolean {
|
|
363
|
+
if (this.isEmptyishDirSync()) {
|
|
364
|
+
ALL_KNOWN_AUTOGENERATED_FILES.map((path) =>
|
|
365
|
+
this.join(path).rmSync({ force: true }),
|
|
366
|
+
);
|
|
367
|
+
this.rmDirSync();
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
|
|
294
373
|
symlinkSync(
|
|
295
374
|
target: string | URL | Path,
|
|
296
375
|
type?: Parameters<typeof symlinkSync>[2],
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../../src/Path.ts", "../../../../src/json.ts", "../../../../src/xdg.ts", "../../../../src/stringifyfIfPath.ts"],
|
|
4
|
-
"sourcesContent": ["import { createReadStream } from \"node:fs\";\nimport {\n appendFile,\n chmod,\n constants,\n cp,\n lstat,\n mkdir,\n mkdtemp,\n readdir,\n readFile,\n realpath,\n rename,\n rm,\n rmdir,\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 { cwd } from \"node:process\";\nimport { createInterface } from \"node:readline/promises\";\nimport { Readable } from \"node:stream\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { jsonChunks, type WriteJSONOptions } from \"./json\";\nimport type {\n lstatType,\n readDirType,\n readFileType,\n statType,\n} from \"./modifiedNodeTypes\";\nimport { stringifyIfPath } from \"./stringifyfIfPath\";\nimport { xdg } from \"./xdg\";\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 enum ResolutionPrefix {\n Absolute = \"absolute\",\n Relative = \"relative\",\n Bare = \"bare\",\n}\n\nexport function resolutionPrefix(pathString: string): ResolutionPrefix {\n if (pathString.startsWith(\"/\")) {\n return ResolutionPrefix.Absolute;\n } else if (pathString.startsWith(\"./\")) {\n return ResolutionPrefix.Relative;\n } else if (pathString.startsWith(\"../\")) {\n return ResolutionPrefix.Relative;\n } else if (pathString === \".\") {\n return ResolutionPrefix.Relative;\n } else if (pathString === \"..\") {\n return ResolutionPrefix.Relative;\n }\n return ResolutionPrefix.Bare;\n}\n\nconst DEFAULT_TEMP_PREFIX = \"js-temp-\";\nconst DEFAULT_TEMP_FILE_NAME = \"file\";\n\nfunction preserveRelativeResolutionPrefix(\n pathString: string,\n from: string,\n): string {\n if (\n resolutionPrefix(from) === ResolutionPrefix.Relative &&\n resolutionPrefix(pathString) !== ResolutionPrefix.Relative\n ) {\n // We don't have to handle the case of `\".\"`, as it already starts with `\".\"`\n return `./${pathString}`;\n }\n return pathString;\n}\n\nfunction joinPreservingRelativeResolutionPrefix(\n base: string,\n relative: string[],\n): string {\n return preserveRelativeResolutionPrefix(join(base, ...relative), base);\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 const s = Path.#pathlikeToString(path);\n this.#setNormalizedPath(s);\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 // Preserves the `ResolutionPrefix` status when possible.\n #setNormalizedPath(path: string): void {\n this.#path = joinPreservingRelativeResolutionPrefix(path, []);\n }\n static fromString(s: string): Path {\n if (typeof s !== \"string\") {\n throw new Error(\n \"Invalid argument to `Path.fromString(\u2026)` \u2014 expected a string.\",\n );\n }\n return new Path(s);\n }\n\n get resolutionPrefix(): ResolutionPrefix {\n return resolutionPrefix(this.#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 if (typeof base === \"string\" && !base.startsWith(\"file://\")) {\n return pathToFileURL(base);\n }\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 /**\n * Convenience function. The following are equivalent:\n *\n * B.resolve(A);\n * Path.resolve(A, B);\n *\n */\n resolve(path: string | URL | Path): Path {\n return Path.resolve(path, this);\n }\n\n /**\n * Computes the relative path from an ancestor (this) to another path.\n *\n * - If the path is a descendant *or is the same path*, this returns a\n * relative path. Call {@link Path.asBare `.asBare()`} on the output if\n * needed.\n * - The output is `null` unless the input paths are:\n * - Both absolute paths.\n * - Both relative paths. Resolve the paths before passing to this function\n * if needed.\n * - Trailing slashes:\n * - By default, the ancestor path must have a trailing slash for the\n * function to be called. Pass `{ requireTrailingSlashForAncestor: false\n * }` if needed. Note that recombining the ancestor path with the output\n * using {@link Path.prototype.resolve `.resolve(\u2026)` } does not result in\n * the original input path if the ancestor path did not have a trailing\n * slash.\n * - For the descendant/same path check, trailing slashes are ignored. In\n * particular, if the ancestor path has a trailing slash and the\n * descendant path is the same path without a trailing slash, this is\n * still considered to be the same path.\n * - The output has trailing slash if and only if:\n * - the input descendant does, or\n * - the output is the absolute path `/`.\n */\n descendantRelativePath(\n potentialDescendant: string | URL | Path,\n options?: { requireTrailingSlashForAncestor: boolean },\n ): Path | null {\n const requireTrailingSlashForAncestor =\n options?.requireTrailingSlashForAncestor ?? true;\n if (requireTrailingSlashForAncestor && !this.hasTrailingSlash()) {\n throw new Error(\n \"Ancestor must have a trailing slash. Pass `{ requireTrailingSlashForAncestor: false }` if needed.\",\n );\n }\n\n const other = new Path(potentialDescendant);\n if (this.isAbsolutePath() !== other.isAbsolutePath()) {\n return null;\n }\n\n // Leading slashes are okay, as they will result in a `\"\"` component for\n // absolute paths (and we don't compare absolute paths to relative paths.)\n const thisParts = this.toggleTrailingSlash(false).#path.split(\"/\");\n const otherParts = other.toggleTrailingSlash(false).#path.split(\"/\");\n\n if (otherParts.length < thisParts.length) {\n return null;\n }\n for (let i = 0; i < thisParts.length; i++) {\n if (thisParts[i] !== otherParts[i]) {\n return null;\n }\n }\n return new Path(\"./\")\n .join(...otherParts.slice(thisParts.length))\n .toggleTrailingSlash(other.hasTrailingSlash());\n }\n\n unresolve(path: string | URL | Path): Path {\n return Path.resolve(path, this);\n }\n\n isAbsolutePath(): boolean {\n return this.resolutionPrefix === ResolutionPrefix.Absolute;\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 * Toggles or sets a trailing slash as specified.\n * If the path is `/`, it will always be left as-is.\n */\n // Similar convention to:\n // https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/toggle Most\n // use cases will probably use the `force` parameters, but supporting the\n // \"toggle\" use case is easy to implement and hopefully will make the name\n // and API more familiar to web devs.\n toggleTrailingSlash(force?: boolean): Path {\n if (this.#path === \"/\") {\n return this;\n }\n const wantTrailingSlash = force ?? !this.hasTrailingSlash();\n if (wantTrailingSlash) {\n return this.hasTrailingSlash() ? this : this.join(\"./\");\n } else {\n return this.hasTrailingSlash() ? new Path(this.#path.slice(0, -1)) : this;\n }\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 /**\n * Format this with the escape codes for printing to the shell in bold blue.\n *\n * Example usage:\n *\n * console.log(`Processing: ${path.blue}`)\n *\n */\n get blue() {\n const { styleText } = globalThis.process.getBuiltinModule(\"node:util\");\n return styleText([\"bold\", \"blue\"], 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 const s = stringifyIfPath(segment);\n if (resolutionPrefix(s) === ResolutionPrefix.Absolute) {\n throw new Error(\n \"Arguments to `.join(\u2026)` cannot be absolute. Use `.asRelative()` to convert them first if needed.\",\n );\n }\n return s;\n });\n return new Path(\n joinPreservingRelativeResolutionPrefix(this.#path, segmentStrings),\n );\n }\n\n /**\n * Adjust the prefix to construct a relative path.\n *\n * | Example input | Output |\n * |-----------------|-----------------|\n * | `\"bare\"` | `\"./bare\"` |\n * | `\"./relative\"` | `\"./relative\"` |\n * | `\"../up-first\"` | `\"../up-first\"` |\n * | `\"/absolute\"` | `\"./absolute\"` |\n *\n */\n asRelative(): Path {\n return new Path(`./${this.#path}`);\n }\n\n /**\n * Adjust the prefix to construct an absolute path.\n *\n * | Example input | Output |\n * |-----------------|---------------|\n * | `\"bare\"` | `\"/bare\"` |\n * | `\"./relative\"` | `\"/relative\"` |\n * | `\"../up-first\"` | `\"/up-first\"` |\n * | `\"/absolute\"` | `\"/absolute\"` |\n *\n */\n asAbsolute(): Path {\n return new Path(join(\"/\", this.#path));\n }\n\n /**\n * Adjust the prefix to construct a bare path. Note that this returns `\".\"` if\n * there are no named paths left.\n *\n * | Example input | Output |\n * |-------------------|--------------|\n * | `\"bare\"` | `\"bare\"` |\n * | `\"./relative\" ` | `\"relative\"` |\n * | `\"/absolute\"` | `\"absolute\"` |\n * | `\".\"` | `\".\"` |\n * | `\"down-first/..\"` | `\".\"` |\n * | `\"../up-first\"` | (error) |\n * | `\"..\"` | (error) |\n *\n * Specify `parentTraversalPrefixHandling` in the `options` if you would like\n * to strip or keep resolution prefixes like `../` rather than erroring.\n *\n * | Example input | Output with `{ parentTraversalPrefixHandling: \"strip\" }` |\n * |----------------------|----------------------------------------------------------|\n * | `\"../up-first\"` | `\"up-first\"` |\n * | `\"..\"` | `\".\"` |\n *\n * | Example input | Output with `{ parentTraversalPrefixHandling: \"keep\" }` |\n * |----------------------|---------------------------------------------------------|\n * | `\"../up-first\"` | `\"../up-first\"` |\n * | `\"..\"` | `\"..\"` |\n *\n * If you need the output to start with a named component and return values\n * like `.`, `..`, `../`, or `../\u2026` are not okay, pass\n * `requireNamedComponentPrefix: true`. This is useful if the path represents\n * an `npm`-style package name (e.g. `\"typescript\"`, `\"@biomejs/biome\"`).\n *\n */\n asBare(options?: {\n parentTraversalPrefixHandling?: \"error\" | \"strip\" | \"keep\";\n requireNamedComponentPrefix?: boolean;\n }): Path {\n const path = new Path(join(\".\", this.#path));\n if (!path.#path.startsWith(\"../\") && path.#path !== \"..\") {\n if (\n options?.requireNamedComponentPrefix &&\n path.resolutionPrefix === ResolutionPrefix.Relative\n ) {\n throw new Error(\"Output does not start with a named component.\");\n }\n return path;\n }\n const parentTraversalHandling =\n options?.parentTraversalPrefixHandling ?? \"error\";\n switch (parentTraversalHandling) {\n case \"error\": {\n throw new Error(\n 'Converting path to a bare path resulted in a `..` traversal prefix. Pass `\"strip\"` or `\"keep\"` as the `parentTraversalHandling` option to avoid an error.',\n );\n }\n case \"strip\": {\n let newPath = path.#path.replace(/^(\\.\\.\\/)+/, \"\");\n if ([\"\", \"..\"].includes(newPath)) {\n newPath = \".\";\n }\n const output = new Path(newPath);\n if (\n options?.requireNamedComponentPrefix &&\n output.resolutionPrefix === ResolutionPrefix.Relative\n ) {\n throw new Error(\"Output does not start with a named component.\");\n }\n return new Path(newPath);\n }\n case \"keep\": {\n if (options?.requireNamedComponentPrefix) {\n throw new Error(\"Output does not start with a named component.\");\n }\n return path;\n }\n }\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 if (constraints?.mustBe === \"file\") {\n mustNotHaveTrailingSlash(this);\n }\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 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] & { createIntermediateDirs?: boolean },\n ): Promise<Path> {\n const { createIntermediateDirs, ...cpOptions } = options ?? {};\n const destinationPath = new Path(destination);\n if (createIntermediateDirs ?? true) {\n await destinationPath.parent.mkdir();\n }\n await cp(this.#path, destinationPath.#path, cpOptions);\n return destinationPath;\n }\n\n // TODO: check idempotency semantics when the destination exists and is a folder.\n async rename(\n destination: string | URL | Path,\n options?: { createIntermediateDirs?: boolean },\n ): Promise<Path> {\n const destinationPath = new Path(destination);\n if (options?.createIntermediateDirs ?? true) {\n await destinationPath.parent.mkdir();\n }\n await rename(this.#path, destinationPath.#path);\n return destinationPath;\n }\n\n /**\n * Create a temporary dir inside the global temp dir for the current user.\n *\n * This can be used with [`await\n * using`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/await_using)\n * to automatically delete the dir when it goes out of scope.\n *\n * {\n * await using tempDir = await Path.makeTempDir();\n * // Temporary dir exists while we're inside this block.\n * }\n * // Temporary dir has now been deleted.\n *\n * Note that (due to the semantics of JS runtime implementations) this does\n * not delete the temp dir if the process calls `exit(\u2026)` before the `using`\n * goes out of scope.\n *\n * */\n static async makeTempDir(prefix?: string): Promise<AsyncDisposablePath> {\n return new AsyncDisposablePath(\n await mkdtemp(\n new Path(tmpdir()).join(prefix ?? DEFAULT_TEMP_PREFIX).toString(),\n ),\n );\n }\n\n /**\n * Return a path:\n *\n * - whose parent dir is a temp dir that *has* been created, but\n * - which has itself not yet been created.\n *\n * Note that this path can actually also be used to create dir, but it is most\n * convenient to get a path for a temporary file that can be written to, while\n * having a disposal implementation that cleans everything up:\n *\n * await using tempFile = await Path.tempFilePath({ basename: \"foo.txt\" });\n * await tempFile.write(\"hello world!\");\n * // \u2026\n *\n * Note that that the following are equivalent when *not* using `await using`:\n *\n * await Path.tempFilePath({ basename: \"foo.txt\" });\n * (await Path.makeTempDir()).join(\"file.txt\");\n *\n * However, it is recommended to use `await using` to ensure cleanup.\n */\n static async tempFilePath(options: {\n tempDirPrefix?: string;\n basename?: string | Path;\n }): Promise<AsyncDisposablePath> {\n const tempDir = new Path(\n await mkdtemp(\n new Path(tmpdir())\n .join(options?.tempDirPrefix ?? DEFAULT_TEMP_PREFIX)\n .toString(),\n ),\n );\n return new AsyncDisposablePath(\n tempDir.join(options?.basename ?? DEFAULT_TEMP_FILE_NAME),\n { disposePathInstead: tempDir },\n );\n }\n\n async rm(options?: Parameters<typeof rm>[1]): Promise<void> {\n await rm(this.#path, options);\n }\n\n async rmDir(): Promise<void> {\n await rmdir(this.#path);\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 * Yields one line from the text of the line at a time.\n *\n * This uses streams, so it can be considerably more efficient than calling e.g. `.split(\"\\n\")` on the output of {@link readText `.readText()`}.\n *\n * Note that this function does not have a `.readLinesSync()` counterpart.\n */\n async *readLines(): AsyncIterable<string> {\n const stream = createReadStream(this.#path, \"utf-8\");\n yield* createInterface({ input: stream, terminal: false });\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 /**\n * Returns the original `Path` (for chaining).\n */\n async appendFile(\n data: Parameters<typeof appendFile>[1],\n options?: Parameters<typeof appendFile>[2],\n ): Promise<Path> {\n await appendFile(this.#path, data, options);\n return this;\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>(data: T, options?: WriteJSONOptions): Promise<Path> {\n // TODO: file handle mechanism to avoid JSON serialization in memory.\n await this.write(jsonChunks(data, options));\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 // I don't think `realpath` is a great name, but it does match the\n // well-established canonical commandline name. So in this case we keep the\n // name instead of using `realPath`.\n //\n // Note: There are no options in our API, because the only option is an\n // encoding. We set the encoding to construct the returned `Path`.\n async realpath(): Promise<Path> {\n return new Path(await realpath(this.#path, \"utf-8\"));\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 async chmod(mode: Parameters<typeof chmod>[1]): Promise<Path> {\n await chmod(this.#path, mode);\n return this;\n }\n\n /**\n * Add the executable bit (for everyone) to the given path without modifying other bits (`chmod +x`).\n */\n async chmodX(): Promise<Path> {\n const { mode } = await this.stat();\n await this.chmod(\n mode | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH,\n );\n return this;\n }\n\n static get homedir(): Path {\n return new Path(homedir());\n }\n\n /**\n * Get the current working directory as a path. Always includes a trailing slash.\n * Note that this computes the `cwd` from fresh every time, in case it has changed for the current process.\n */\n static get cwd(): Path {\n return new Path(cwd()).toggleTrailingSlash(true);\n }\n\n static xdg = xdg;\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\nexport class AsyncDisposablePath extends Path {\n #options?: { disposePathInstead: Path };\n constructor(\n path: ConstructorParameters<typeof Path>[0],\n options?: { disposePathInstead: Path | string },\n ) {\n super(path);\n if (options) {\n this.#options = {\n disposePathInstead: new Path(options.disposePathInstead),\n };\n }\n }\n\n async [Symbol.asyncDispose]() {\n await (this.#options?.disposePathInstead ?? this).rm_rf();\n }\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", "export interface WriteJSONOptions {\n replacer?: Parameters<typeof JSON.stringify>[1];\n space?: Parameters<typeof JSON.stringify>[2];\n trailingNewline?: boolean;\n}\n\nconst JSON_WRITE_DEFAULTS: WriteJSONOptions = {\n replacer: null,\n space: \" \",\n trailingNewline: true,\n};\n\n/**\n * Returns an array of chunks, to ensure we don't perforn unnecessary in-memory concatenation before writing.\n */\nexport function jsonChunks<T>(data: T, options?: WriteJSONOptions): string[] {\n const { replacer, space, trailingNewline } = {\n ...JSON_WRITE_DEFAULTS,\n ...options,\n };\n return [JSON.stringify(data, replacer, space), trailingNewline ? \"\\n\" : \"\"];\n}\n", "/** biome-ignore-all lint/complexity/useLiteralKeys: TODO: https://github.com/biomejs/biome/discussions/7572 */\n\nimport { Path } from \"./Path\";\n\nfunction env(): typeof import(\"node:process\").env {\n const { env } = globalThis.process.getBuiltinModule(\"node:process\");\n return env;\n}\n\nfunction varWithoutFallback(envVar: `XDG_${string}`): Path | undefined {\n const value = env()[envVar];\n return value ? Path.fromString(value) : undefined;\n}\n\nfunction varWithFallback(\n envVar: `XDG_${string}`,\n fallbackInHomeDir: string,\n): Path {\n return varWithoutFallback(envVar) ?? Path.homedir.join(fallbackInHomeDir);\n}\n\n// This is a level of indirection to avoid accessing env vars or `homedir()`\n// until needed, to avoid permissions prompts / flags for `deno` when these are\n// not needed for the calling code.\n//\n// TODO: cache values?\nclass XDG {\n get cache() {\n return varWithFallback(\"XDG_CACHE_HOME\", \".cache\");\n }\n get config() {\n return varWithFallback(\"XDG_CONFIG_HOME\", \".config\");\n }\n get data() {\n return varWithFallback(\"XDG_DATA_HOME\", \".local/share\");\n }\n get state() {\n return varWithFallback(\"XDG_STATE_HOME\", \".local/state\");\n }\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 get runtime() {\n return varWithoutFallback(\"XDG_RUNTIME_DIR\");\n }\n get runtimeWithStateFallback() {\n return varWithoutFallback(\"XDG_RUNTIME_DIR\") ?? this.state;\n }\n}\n\nexport const xdg = new XDG();\n", "import { Path } from \"./Path\";\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: Exclude<T, Path> | Path): T | string {\n if (value instanceof Path) {\n return value.toString();\n }\n return value;\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["env", "ResolutionPrefix"]
|
|
7
|
-
}
|