path-class 0.11.1 → 0.12.1

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.
@@ -135,8 +135,12 @@ export declare class Path {
135
135
  /** Defaults to `recursive: true`. */
136
136
  mkdir(options?: Parameters<typeof mkdir>[1]): Promise<Path>;
137
137
  /** Returns the destination path. */
138
- cp(destination: string | URL | Path, options?: Parameters<typeof cp>[2]): Promise<Path>;
139
- rename(destination: string | URL | Path): Promise<void>;
138
+ cp(destination: string | URL | Path, options?: Parameters<typeof cp>[2] & {
139
+ createIntermediateDirs?: boolean;
140
+ }): Promise<Path>;
141
+ rename(destination: string | URL | Path, options?: {
142
+ createIntermediateDirs?: boolean;
143
+ }): Promise<Path>;
140
144
  /**
141
145
  * Create a temporary dir inside the global temp dir for the current user.
142
146
  *
@@ -390,12 +390,22 @@ var Path = class _Path {
390
390
  // TODO: check idempotency semantics when the destination exists and is a folder.
391
391
  /** Returns the destination path. */
392
392
  async cp(destination, options) {
393
- await cp(this.#path, new _Path(destination).#path, options);
394
- return new _Path(destination);
393
+ const { createIntermediateDirs, ...cpOptions } = options ?? {};
394
+ const destinationPath = new _Path(destination);
395
+ if (createIntermediateDirs ?? true) {
396
+ await destinationPath.parent.mkdir();
397
+ }
398
+ await cp(this.#path, destinationPath.#path, cpOptions);
399
+ return destinationPath;
395
400
  }
396
401
  // TODO: check idempotency semantics when the destination exists and is a folder.
397
- async rename(destination) {
398
- await rename(this.#path, new _Path(destination).#path);
402
+ async rename(destination, options) {
403
+ const destinationPath = new _Path(destination);
404
+ if (options?.createIntermediateDirs ?? true) {
405
+ await destinationPath.parent.mkdir();
406
+ }
407
+ await rename(this.#path, destinationPath.#path);
408
+ return destinationPath;
399
409
  }
400
410
  /**
401
411
  * Create a temporary dir inside the global temp dir for the current user.
@@ -539,7 +549,7 @@ var Path = class _Path {
539
549
  async chmodX() {
540
550
  const { mode } = await this.stat();
541
551
  await this.chmod(
542
- mode | constants.S_IRWXU | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH
552
+ mode | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH
543
553
  );
544
554
  return this;
545
555
  }
@@ -600,6 +610,10 @@ function mustNotHaveTrailingSlash(path) {
600
610
  );
601
611
  }
602
612
  }
613
+ var tmp = await Path.makeTempDir();
614
+ (await tmp.join("foo.json").write("foo")).rename(
615
+ tmp.join("sdfsD", "sdfsdfsdf", "sdfsdf.json")
616
+ );
603
617
 
604
618
  export {
605
619
  ResolutionPrefix,
@@ -608,4 +622,4 @@ export {
608
622
  stringifyIfPath,
609
623
  mustNotHaveTrailingSlash
610
624
  };
611
- //# sourceMappingURL=chunk-55PEPNLJ.js.map
625
+ //# sourceMappingURL=chunk-K2SCBFED.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/Path.ts"],
4
- "sourcesContent": ["import {\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 { Readable } from \"node:stream\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport {\n xdgCache,\n xdgConfig,\n xdgData,\n xdgRuntime,\n xdgState,\n} from \"xdg-basedir\";\nimport type {\n lstatType,\n readDirType,\n readFileType,\n statType,\n} from \"./modifiedNodeTypes\";\n\n// Note that (non-static) functions in this file are defined using `function(\u2026)\n// { \u2026 }` rather than arrow functions, specifically because we want `this` to\n// operate on the `Path` instance.\n\ntype WritableData = Parameters<typeof writeFile>[1] | ReadableStream | Response;\nasync function wrangleWritableData(\n data: WritableData | Promise<WritableData>,\n): Promise<Parameters<typeof writeFile>[1]> {\n data = await data;\n if (data instanceof Response) {\n data = data.body ? Readable.fromWeb(data.body) : new Uint8Array(0);\n }\n if (data instanceof ReadableStream) {\n data = Readable.fromWeb(data);\n }\n return data;\n}\n\nexport 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\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 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 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 const prefix = resolutionPrefix(path);\n this.#path = join(path);\n if (prefix === ResolutionPrefix.Relative && !this.#path.startsWith(\".\")) {\n // We don't have to handle the case of `\".\"`, as it already starts with `\".\"`\n this.#path = `./${this.#path}`;\n }\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(join(this.#path, ...segmentStrings));\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],\n ): Promise<Path> {\n await cp(this.#path, new Path(destination).#path, options);\n return new Path(destination);\n }\n\n // TODO: check idempotency semantics when the destination exists and is a folder.\n async rename(destination: string | URL | Path): Promise<void> {\n await rename(this.#path, new Path(destination).#path);\n }\n\n /**\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(new Path(tmpdir()).join(prefix ?? \"js-temp-\").toString()),\n );\n }\n\n async rm(options?: Parameters<typeof rm>[1]): Promise<void> {\n await rm(this.#path, options);\n }\n\n 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 * 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>(\n data: T,\n replacer: Parameters<typeof JSON.stringify>[1] = null,\n space: Parameters<typeof JSON.stringify>[2] = \" \",\n ): Promise<Path> {\n await this.write(JSON.stringify(data, replacer, space));\n return this;\n }\n\n // Normally we'd add a `@deprecated` alias named `.readdir`, but that would\n // differ only by capitalization of a single non-leading character. This can\n // be a bit confusing, especially when autocompleting. So for this function in\n // particular we don't include an alias.\n readDir: typeof readDirType = (options) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n readdir(this.#path, options as any) as any;\n\n /** Returns the destination path. */\n async symlink(\n target: string | URL | Path,\n type?: Parameters<typeof symlink>[2],\n ): Promise<Path> {\n const targetPath = new Path(target);\n await symlink(\n this.path,\n targetPath.path,\n type as Exclude<Parameters<typeof symlink>[2], undefined>, // \uD83E\uDD37\n );\n return targetPath;\n }\n\n // 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 |\n constants.S_IRWXU |\n constants.S_IXUSR |\n constants.S_IXGRP |\n 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 = {\n cache: new Path(xdgCache ?? Path.homedir.join(\".cache\")),\n config: new Path(xdgConfig ?? Path.homedir.join(\".config\")),\n data: new Path(xdgData ?? Path.homedir.join(\".local/share\")),\n state: new Path(xdgState ?? Path.homedir.join(\".local/state\")),\n /**\n * {@link Path.xdg.runtime} does not have a default value. Consider\n * {@link Path.xdg.runtimeWithStateFallback} if you need a fallback but do not have a particular fallback in mind.\n */\n runtime: xdgRuntime ? new Path(xdgRuntime) : undefined,\n runtimeWithStateFallback: xdgRuntime\n ? new Path(xdgRuntime)\n : new Path(xdgState ?? Path.homedir.join(\".local/state\")),\n };\n\n /** Chainable function to print the path. Prints the same as:\n *\n * if (args.length > 0) {\n * console.log(...args);\n * }\n * console.log(this.path);\n *\n */\n // biome-ignore lint/suspicious/noExplicitAny: This is the correct type, based on `console.log(\u2026)`.\n debugPrint(...args: any[]): Path {\n if (args.length > 0) {\n console.log(...args);\n }\n console.log(this.#path);\n return this;\n }\n}\n\nexport class AsyncDisposablePath extends Path {\n async [Symbol.asyncDispose]() {\n await this.rm_rf();\n }\n}\n\n/**\n * This function is useful to serialize any `Path`s in a structure to pass on to\n * functions that do not know about the `Path` class, e.g.\n *\n * function process(args: (string | Path)[]) {\n * const argsAsStrings = args.map(stringifyIfPath);\n * }\n *\n */\nexport function stringifyIfPath<T>(value: T | Path): T | string {\n if (value instanceof Path) {\n return value.toString();\n }\n return value;\n}\n\nexport function mustNotHaveTrailingSlash(path: Path): void {\n if (path.hasTrailingSlash()) {\n throw new Error(\n \"Path ends with a slash, which cannot be treated as a file.\",\n );\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\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 { Readable } from \"node:stream\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport {\n xdgCache,\n xdgConfig,\n xdgData,\n xdgRuntime,\n xdgState,\n} from \"xdg-basedir\";\nimport type {\n lstatType,\n readDirType,\n readFileType,\n statType,\n} from \"./modifiedNodeTypes\";\n\n// Note that (non-static) functions in this file are defined using `function(\u2026)\n// { \u2026 }` rather than arrow functions, specifically because we want `this` to\n// operate on the `Path` instance.\n\ntype WritableData = Parameters<typeof writeFile>[1] | ReadableStream | Response;\nasync function wrangleWritableData(\n data: WritableData | Promise<WritableData>,\n): Promise<Parameters<typeof writeFile>[1]> {\n data = await data;\n if (data instanceof Response) {\n data = data.body ? Readable.fromWeb(data.body) : new Uint8Array(0);\n }\n if (data instanceof ReadableStream) {\n data = Readable.fromWeb(data);\n }\n return data;\n}\n\nexport 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\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 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 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 const prefix = resolutionPrefix(path);\n this.#path = join(path);\n if (prefix === ResolutionPrefix.Relative && !this.#path.startsWith(\".\")) {\n // We don't have to handle the case of `\".\"`, as it already starts with `\".\"`\n this.#path = `./${this.#path}`;\n }\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(join(this.#path, ...segmentStrings));\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(new Path(tmpdir()).join(prefix ?? \"js-temp-\").toString()),\n );\n }\n\n async rm(options?: Parameters<typeof rm>[1]): Promise<void> {\n await rm(this.#path, options);\n }\n\n 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 * 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>(\n data: T,\n replacer: Parameters<typeof JSON.stringify>[1] = null,\n space: Parameters<typeof JSON.stringify>[2] = \" \",\n ): Promise<Path> {\n await this.write(JSON.stringify(data, replacer, space));\n return this;\n }\n\n // Normally we'd add a `@deprecated` alias named `.readdir`, but that would\n // differ only by capitalization of a single non-leading character. This can\n // be a bit confusing, especially when autocompleting. So for this function in\n // particular we don't include an alias.\n readDir: typeof readDirType = (options) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n readdir(this.#path, options as any) as any;\n\n /** Returns the destination path. */\n async symlink(\n target: string | URL | Path,\n type?: Parameters<typeof symlink>[2],\n ): Promise<Path> {\n const targetPath = new Path(target);\n await symlink(\n this.path,\n targetPath.path,\n type as Exclude<Parameters<typeof symlink>[2], undefined>, // \uD83E\uDD37\n );\n return targetPath;\n }\n\n // 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 = {\n cache: new Path(xdgCache ?? Path.homedir.join(\".cache\")),\n config: new Path(xdgConfig ?? Path.homedir.join(\".config\")),\n data: new Path(xdgData ?? Path.homedir.join(\".local/share\")),\n state: new Path(xdgState ?? Path.homedir.join(\".local/state\")),\n /**\n * {@link Path.xdg.runtime} does not have a default value. Consider\n * {@link Path.xdg.runtimeWithStateFallback} if you need a fallback but do not have a particular fallback in mind.\n */\n runtime: xdgRuntime ? new Path(xdgRuntime) : undefined,\n runtimeWithStateFallback: xdgRuntime\n ? new Path(xdgRuntime)\n : new Path(xdgState ?? Path.homedir.join(\".local/state\")),\n };\n\n /** Chainable function to print the path. Prints the same as:\n *\n * if (args.length > 0) {\n * console.log(...args);\n * }\n * console.log(this.path);\n *\n */\n // biome-ignore lint/suspicious/noExplicitAny: This is the correct type, based on `console.log(\u2026)`.\n debugPrint(...args: any[]): Path {\n if (args.length > 0) {\n console.log(...args);\n }\n console.log(this.#path);\n return this;\n }\n}\n\nexport class AsyncDisposablePath extends Path {\n async [Symbol.asyncDispose]() {\n await this.rm_rf();\n }\n}\n\n/**\n * This function is useful to serialize any `Path`s in a structure to pass on to\n * functions that do not know about the `Path` class, e.g.\n *\n * function process(args: (string | Path)[]) {\n * const argsAsStrings = args.map(stringifyIfPath);\n * }\n *\n */\nexport function stringifyIfPath<T>(value: T | Path): T | string {\n if (value instanceof Path) {\n return value.toString();\n }\n return value;\n}\n\nexport function mustNotHaveTrailingSlash(path: Path): void {\n if (path.hasTrailingSlash()) {\n throw new Error(\n \"Path ends with a slash, which cannot be treated as a file.\",\n );\n }\n}\n\nconst tmp = await Path.makeTempDir();\n(await tmp.join(\"foo.json\").write(\"foo\")).rename(\n tmp.join(\"sdfsD\", \"sdfsdfsdf\", \"sdfsdf.json\"),\n);\n"],
5
+ "mappings": "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",
6
6
  "names": ["ResolutionPrefix"]
7
7
  }
@@ -3,7 +3,7 @@ import {
3
3
  Path,
4
4
  ResolutionPrefix,
5
5
  stringifyIfPath
6
- } from "./chunks/chunk-55PEPNLJ.js";
6
+ } from "./chunks/chunk-K2SCBFED.js";
7
7
  export {
8
8
  AsyncDisposablePath,
9
9
  Path,
@@ -22,8 +22,12 @@ export declare class PathSync extends Path {
22
22
  existsAsFileSync(): boolean;
23
23
  existsAsDirSync(): boolean;
24
24
  mkdirSync(options?: Parameters<typeof mkdirSync>[1]): PathSync;
25
- cpSync(destination: string | URL | Path, options?: Parameters<typeof cpSync>[2]): PathSync;
26
- renameSync(destination: string | URL | Path): void;
25
+ cpSync(destination: string | URL | Path, options?: Parameters<typeof cpSync>[2] & {
26
+ createIntermediateDirs?: boolean;
27
+ }): PathSync;
28
+ renameSync(destination: string | URL | Path, options?: {
29
+ createIntermediateDirs?: boolean;
30
+ }): PathSync;
27
31
  static makeTempDirSync(prefix?: string): PathSync;
28
32
  rmSync(options?: Parameters<typeof rmSync>[1]): void;
29
33
  rmDirSync(): void;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Path,
3
3
  mustNotHaveTrailingSlash
4
- } from "../chunks/chunk-55PEPNLJ.js";
4
+ } from "../chunks/chunk-K2SCBFED.js";
5
5
 
6
6
  // src/sync/PathSync.ts
7
7
  import {
@@ -118,11 +118,21 @@ var PathSync = class _PathSync extends Path {
118
118
  return this;
119
119
  }
120
120
  cpSync(destination, options) {
121
- cpSync(this.path, new Path(destination).path, options);
122
- return new _PathSync(destination);
121
+ const { createIntermediateDirs, ...cpOptions } = options ?? {};
122
+ const destinationPath = new _PathSync(destination);
123
+ if (createIntermediateDirs ?? true) {
124
+ destinationPath.parent.mkdirSync();
125
+ }
126
+ cpSync(this.path, destinationPath.path, cpOptions);
127
+ return destinationPath;
123
128
  }
124
- renameSync(destination) {
125
- renameSync(this.path, new Path(destination).path);
129
+ renameSync(destination, options) {
130
+ const destinationPath = new _PathSync(destination);
131
+ if (options?.createIntermediateDirs ?? true) {
132
+ destinationPath.parent.mkdirSync();
133
+ }
134
+ renameSync(this.path, destinationPath.path);
135
+ return destinationPath;
126
136
  }
127
137
  static makeTempDirSync(prefix) {
128
138
  return new _PathSync(
@@ -202,7 +212,7 @@ var PathSync = class _PathSync extends Path {
202
212
  chmodXSync() {
203
213
  const { mode } = this.statSync();
204
214
  this.chmodSync(
205
- mode | constants.S_IRWXU | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH
215
+ mode | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH
206
216
  );
207
217
  return this;
208
218
  }
@@ -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 { mustNotHaveTrailingSlash, Path } from \"../Path\";\nimport type {\n lstatSyncType,\n readDirSyncType,\n readFileSyncType,\n statSyncType,\n} from \"./modifiedNodeTypes\";\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 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 ): PathSync {\n cpSync(this.path, new Path(destination).path, options);\n return new PathSync(destination);\n }\n\n renameSync(destination: string | URL | Path): void {\n renameSync(this.path, new Path(destination).path);\n }\n\n static makeTempDirSync(prefix?: string): PathSync {\n return new PathSync(\n mkdtempSync(new Path(tmpdir()).join(prefix ?? \"js-temp-\").toString()),\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>(\n data: T,\n replacer: Parameters<typeof JSON.stringify>[1] = null,\n space: Parameters<typeof JSON.stringify>[2] = \" \",\n ): PathSync {\n this.parent.mkdirSync();\n this.writeSync(JSON.stringify(data, replacer, space));\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 |\n constants.S_IRWXU |\n constants.S_IXUSR |\n constants.S_IXGRP |\n constants.S_IXOTH,\n );\n return this;\n }\n}\n"],
5
- "mappings": "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",
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 { mustNotHaveTrailingSlash, Path } from \"../Path\";\nimport type {\n lstatSyncType,\n readDirSyncType,\n readFileSyncType,\n statSyncType,\n} from \"./modifiedNodeTypes\";\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 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): PathSync {\n return new PathSync(\n mkdtempSync(new Path(tmpdir()).join(prefix ?? \"js-temp-\").toString()),\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>(\n data: T,\n replacer: Parameters<typeof JSON.stringify>[1] = null,\n space: Parameters<typeof JSON.stringify>[2] = \" \",\n ): PathSync {\n this.parent.mkdirSync();\n this.writeSync(JSON.stringify(data, replacer, space));\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"],
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.11.1",
3
+ "version": "0.12.1",
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",
package/src/Path.test.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { expect, spyOn, test } from "bun:test";
2
- import { readFile, realpath } from "node:fs/promises";
2
+ import { constants, readFile, realpath } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
  import { chdir } from "node:process";
5
5
  import { PrintableShellCommand } from "printable-shell-command";
6
6
  import { Path, ResolutionPrefix, stringifyIfPath } from "./Path";
7
7
 
8
- test("constructor", async () => {
8
+ test.concurrent("constructor", async () => {
9
9
  expect(new Path("bare").path).toEqual("bare");
10
10
  expect(new Path("bare/").path).toEqual("bare/");
11
11
  expect(new Path("bare/path").path).toEqual("bare/path");
@@ -27,7 +27,7 @@ test("constructor", async () => {
27
27
  expect(new Path(new Path("foo")).path).toEqual("foo");
28
28
  });
29
29
 
30
- test(".fromString(…)", async () => {
30
+ test.concurrent(".fromString(…)", async () => {
31
31
  expect(Path.fromString("bare").path).toEqual("bare");
32
32
  // biome-ignore lint/suspicious/noExplicitAny: We're purposely passing an invalid type.
33
33
  expect(() => Path.fromString(new URL("file:///test") as any)).toThrow(
@@ -35,7 +35,7 @@ test(".fromString(…)", async () => {
35
35
  );
36
36
  });
37
37
 
38
- test(".resolutionPrefix", async () => {
38
+ test.concurrent(".resolutionPrefix", async () => {
39
39
  expect(new Path("bare").resolutionPrefix).toEqual(ResolutionPrefix.Bare);
40
40
  expect(new Path("bare/").resolutionPrefix).toEqual(ResolutionPrefix.Bare);
41
41
  expect(new Path("bare/path").resolutionPrefix).toEqual(ResolutionPrefix.Bare);
@@ -79,7 +79,7 @@ test(".resolutionPrefix", async () => {
79
79
  expect(new Path("../").resolutionPrefix).toEqual(ResolutionPrefix.Relative);
80
80
  });
81
81
 
82
- test("Path.resolve(…)", async () => {
82
+ test.concurrent("Path.resolve(…)", async () => {
83
83
  expect(Path.resolve("foo/lish", new Path("/bar/baz")).path).toEqual(
84
84
  "/bar/foo/lish",
85
85
  );
@@ -95,13 +95,13 @@ test("Path.resolve(…)", async () => {
95
95
  );
96
96
  });
97
97
 
98
- test(".isAbsolutePath()", async () => {
98
+ test.concurrent(".isAbsolutePath()", async () => {
99
99
  expect(new Path("/foo/bar").isAbsolutePath()).toBe(true);
100
100
  expect(new Path("foo/bar").isAbsolutePath()).toBe(false);
101
101
  expect(new Path(import.meta.url).isAbsolutePath()).toBe(true);
102
102
  });
103
103
 
104
- test(".toFileURL()", async () => {
104
+ test.concurrent(".toFileURL()", async () => {
105
105
  expect(new Path("/foo/bar").toFileURL().toString()).toEqual(
106
106
  "file:///foo/bar",
107
107
  );
@@ -111,7 +111,7 @@ test(".toFileURL()", async () => {
111
111
  );
112
112
  });
113
113
 
114
- test(".hasTrailingSlash()", async () => {
114
+ test.concurrent(".hasTrailingSlash()", async () => {
115
115
  expect(new Path("/foo/bar").hasTrailingSlash()).toBe(false);
116
116
  expect(new Path("/foo/bar/").hasTrailingSlash()).toBe(true);
117
117
  expect(new Path("foo/bar").hasTrailingSlash()).toBe(false);
@@ -124,7 +124,7 @@ test(".hasTrailingSlash()", async () => {
124
124
  expect(new Path(import.meta.url).join(".").hasTrailingSlash()).toBe(false);
125
125
  });
126
126
 
127
- test(".toggleTrailingSlash(…)", async () => {
127
+ test.concurrent(".toggleTrailingSlash(…)", async () => {
128
128
  expect(new Path("/foo/bar").toggleTrailingSlash().path).toBe("/foo/bar/");
129
129
  expect(new Path("/foo/bar/").toggleTrailingSlash().path).toBe("/foo/bar");
130
130
  expect(new Path("/").toggleTrailingSlash().path).toBe("/");
@@ -134,14 +134,14 @@ test(".toggleTrailingSlash(…)", async () => {
134
134
  expect(new Path("..").toggleTrailingSlash().path).toBe("../");
135
135
  });
136
136
 
137
- test(".blue", async () => {
137
+ test.concurrent(".blue", async () => {
138
138
  expect(Path.fromString("bare").path).toEqual("bare");
139
139
  expect(`Home dir: ${Path.homedir.blue}`).toEqual(
140
140
  "Home dir: \u001b[1m\u001b[34m/mock/home/dir\u001b[39m\u001b[22m",
141
141
  );
142
142
  });
143
143
 
144
- test("normalize", async () => {
144
+ test.concurrent("normalize", async () => {
145
145
  expect(new Path("foo//bar").path).toEqual("foo/bar");
146
146
  expect(new Path("foo////bar").path).toEqual("foo/bar");
147
147
  expect(new Path("foo/bar/").path).toEqual("foo/bar/");
@@ -149,7 +149,7 @@ test("normalize", async () => {
149
149
  expect(new Path("//absolute////bar").path).toEqual("/absolute/bar");
150
150
  });
151
151
 
152
- test(".join(…)", async () => {
152
+ test.concurrent(".join(…)", async () => {
153
153
  expect(new Path("foo").join("bar").path).toEqual("foo/bar");
154
154
  expect(new Path("foo/bar").join("bath", "kitchen/sink").path).toEqual(
155
155
  "foo/bar/bath/kitchen/sink",
@@ -163,7 +163,7 @@ test(".join(…)", async () => {
163
163
  );
164
164
  });
165
165
 
166
- test("asRelative()", async () => {
166
+ test.concurrent("asRelative()", async () => {
167
167
  // From doc comment
168
168
  expect(new Path("bare").asRelative().path).toEqual("./bare");
169
169
  expect(new Path("./relative").asRelative().path).toEqual("./relative");
@@ -181,7 +181,7 @@ test("asRelative()", async () => {
181
181
  expect(new Path("../up/").asRelative().path).toEqual("../up/");
182
182
  });
183
183
 
184
- test("asAbsolute()", async () => {
184
+ test.concurrent("asAbsolute()", async () => {
185
185
  // From doc comment
186
186
  expect(new Path("bare").asAbsolute().path).toEqual("/bare");
187
187
  expect(new Path("./relative").asAbsolute().path).toEqual("/relative");
@@ -193,7 +193,7 @@ test("asAbsolute()", async () => {
193
193
  expect(new Path("../up/").asAbsolute().path).toEqual("/up/");
194
194
  });
195
195
 
196
- test("asBare(…)", async () => {
196
+ test.concurrent("asBare(…)", async () => {
197
197
  const ERROR_1 =
198
198
  'Converting path to a bare path resulted in a `..` traversal prefix. Pass `"strip"` or `"keep"` as the `parentTraversalHandling` option to avoid an error.';
199
199
  const ERROR_2 = "Output does not start with a named component.";
@@ -268,7 +268,7 @@ test("asBare(…)", async () => {
268
268
  ).toThrow(ERROR_2);
269
269
  });
270
270
 
271
- test("traverse", async () => {
271
+ test.concurrent("traverse", async () => {
272
272
  expect(new Path("foo/bar").join("..").path).toEqual("foo");
273
273
  expect(new Path("foo/bar").join(".").path).toEqual("foo/bar");
274
274
  expect(new Path("foo/bar").join("../baz").path).toEqual("foo/baz");
@@ -277,7 +277,7 @@ test("traverse", async () => {
277
277
  expect(new Path("/").join("..").path).toEqual("/");
278
278
  });
279
279
 
280
- test(".extendBasename(…)", async () => {
280
+ test.concurrent(".extendBasename(…)", async () => {
281
281
  expect(new Path("file.mp4").extendBasename(".hevc.qv65.mov").path).toEqual(
282
282
  "file.mp4.hevc.qv65.mov",
283
283
  );
@@ -287,19 +287,19 @@ test(".extendBasename(…)", async () => {
287
287
  );
288
288
  });
289
289
 
290
- test(".parent", async () => {
290
+ test.concurrent(".parent", async () => {
291
291
  expect(new Path("/").parent.path).toEqual("/");
292
292
  expect(new Path("dir").parent.path).toEqual(".");
293
293
  expect(new Path("dir/").parent.path).toEqual(".");
294
294
  });
295
295
 
296
- test(".dirname", async () => {
296
+ test.concurrent(".dirname", async () => {
297
297
  expect(new Path("/").dirname.path).toEqual("/");
298
298
  expect(new Path("dir").dirname.path).toEqual(".");
299
299
  expect(new Path("dir/").dirname.path).toEqual(".");
300
300
  });
301
301
 
302
- test(".basename", async () => {
302
+ test.concurrent(".basename", async () => {
303
303
  expect(new Path("/").basename.path).toEqual("."); // TODO?
304
304
  expect(new Path("dir").basename.path).toEqual("dir");
305
305
  expect(new Path("dir/").basename.path).toEqual("dir");
@@ -308,7 +308,7 @@ test(".basename", async () => {
308
308
  );
309
309
  });
310
310
 
311
- test(".extension", async () => {
311
+ test.concurrent(".extension", async () => {
312
312
  expect(new Path("foo.txt").extension).toEqual(".txt");
313
313
  expect(new Path("foo.").extension).toEqual(".");
314
314
  expect(new Path("foo").extension).toEqual("");
@@ -316,7 +316,7 @@ test(".extension", async () => {
316
316
  expect(() => new Path("/").extension).toThrow();
317
317
  });
318
318
 
319
- test(".extname", async () => {
319
+ test.concurrent(".extname", async () => {
320
320
  expect(new Path("foo.txt").extname).toEqual(".txt");
321
321
  expect(new Path("foo.").extname).toEqual(".");
322
322
  expect(new Path("foo").extname).toEqual("");
@@ -324,7 +324,7 @@ test(".extname", async () => {
324
324
  expect(() => new Path("/").extname).toThrow();
325
325
  });
326
326
 
327
- test(".existsAsFile()", async () => {
327
+ test.concurrent(".existsAsFile()", async () => {
328
328
  const filePath = (await Path.makeTempDir()).join("file.txt");
329
329
  expect(await filePath.exists()).toBe(false);
330
330
  expect(await filePath.exists({ mustBe: "file" })).toBe(false);
@@ -342,7 +342,7 @@ test(".existsAsFile()", async () => {
342
342
  expect(await filePath.existsAsFile()).toBe(true);
343
343
  });
344
344
 
345
- test(".existsAsDir()", async () => {
345
+ test.concurrent(".existsAsDir()", async () => {
346
346
  const filePath = await Path.makeTempDir();
347
347
  expect(await filePath.exists()).toBe(true);
348
348
  expect(() => filePath.exists({ mustBe: "file" })).toThrow(
@@ -357,14 +357,14 @@ test(".existsAsDir()", async () => {
357
357
  expect(await filePath.existsAsDir()).toBe(false);
358
358
  });
359
359
 
360
- test(".mkdir(…) (un-nested)", async () => {
360
+ test.concurrent(".mkdir(…) (un-nested)", async () => {
361
361
  const dir = (await Path.makeTempDir()).join("mkdir-test");
362
362
  expect(await dir.exists()).toBe(false);
363
363
  await dir.mkdir();
364
364
  expect(await dir.exists()).toBe(true);
365
365
  });
366
366
 
367
- test(".mkdir(…) (nested)", async () => {
367
+ test.concurrent(".mkdir(…) (nested)", async () => {
368
368
  const dir = (await Path.makeTempDir()).join("mkdir-test/nested");
369
369
  expect(await dir.exists()).toBe(false);
370
370
  expect(() => dir.mkdir({ recursive: false })).toThrow("no such file");
@@ -372,10 +372,11 @@ test(".mkdir(…) (nested)", async () => {
372
372
  expect(await dir.exists()).toBe(true);
373
373
  });
374
374
 
375
- test(".cp(…)", async () => {
375
+ test.concurrent(".cp(…)", async () => {
376
376
  const parentDir = await Path.makeTempDir();
377
377
  const file1 = parentDir.join("file1.txt");
378
378
  const file2 = parentDir.join("file2.txt");
379
+ const file3 = parentDir.join("nonexistent/dirs/file3.txt");
379
380
 
380
381
  await file1.write("hello world");
381
382
  expect(await file1.exists()).toBe(true);
@@ -384,12 +385,23 @@ test(".cp(…)", async () => {
384
385
  await file1.cp(file2);
385
386
  expect(await file1.exists()).toBe(true);
386
387
  expect(await file2.exists()).toBe(true);
388
+
389
+ expect(() => file2.rename(file3, { createIntermediateDirs: false })).toThrow(
390
+ /^ENOENT/,
391
+ );
392
+ expect(await file2.exists()).toBe(true);
393
+ expect(await file3.exists()).toBe(false);
394
+
395
+ expect((await file2.cp(file3)).path).toEqual(file3.path);
396
+ expect(await file2.exists()).toBe(true);
397
+ expect(await file3.exists()).toBe(true);
387
398
  });
388
399
 
389
- test(".rename(…)", async () => {
400
+ test.concurrent(".rename(…)", async () => {
390
401
  const parentDir = await Path.makeTempDir();
391
402
  const file1 = parentDir.join("file1.txt");
392
403
  const file2 = parentDir.join("file2.txt");
404
+ const file3 = parentDir.join("nonexistent/dirs/file3.txt");
393
405
 
394
406
  await file1.write("hello world");
395
407
  expect(await file1.exists()).toBe(true);
@@ -398,9 +410,19 @@ test(".rename(…)", async () => {
398
410
  await file1.rename(file2);
399
411
  expect(await file1.exists()).toBe(false);
400
412
  expect(await file2.exists()).toBe(true);
413
+
414
+ expect(() => file2.rename(file3, { createIntermediateDirs: false })).toThrow(
415
+ /^ENOENT/,
416
+ );
417
+ expect(await file2.exists()).toBe(true);
418
+ expect(await file3.exists()).toBe(false);
419
+
420
+ expect((await file2.rename(file3)).path).toEqual(file3.path);
421
+ expect(await file2.exists()).toBe(false);
422
+ expect(await file3.exists()).toBe(true);
401
423
  });
402
424
 
403
- test(".makeTempDir(…)", async () => {
425
+ test.concurrent(".makeTempDir(…)", async () => {
404
426
  let asyncDisposablePathString: string;
405
427
  {
406
428
  await using tempDir = await Path.makeTempDir();
@@ -421,7 +443,7 @@ test(".makeTempDir(…)", async () => {
421
443
  expect(await new Path(asyncDisposablePathString2).existsAsDir()).toBe(false);
422
444
  });
423
445
 
424
- test(".rm(…) (file)", async () => {
446
+ test.concurrent(".rm(…) (file)", async () => {
425
447
  const file = (await Path.makeTempDir()).join("file.txt");
426
448
  await file.write("");
427
449
  expect(await file.existsAsFile()).toBe(true);
@@ -431,7 +453,7 @@ test(".rm(…) (file)", async () => {
431
453
  expect(async () => file.rm()).toThrowError(/ENOENT/);
432
454
  });
433
455
 
434
- test(".rm(…) (folder)", async () => {
456
+ test.concurrent(".rm(…) (folder)", async () => {
435
457
  const tempDir = await Path.makeTempDir();
436
458
  const file = tempDir.join("file.txt");
437
459
  await file.write("");
@@ -443,7 +465,7 @@ test(".rm(…) (folder)", async () => {
443
465
  expect(async () => tempDir.rm()).toThrowError(/ENOENT/);
444
466
  });
445
467
 
446
- test(".rmDir(…)", async () => {
468
+ test.concurrent(".rmDir(…)", async () => {
447
469
  const tempDir = await Path.makeTempDir();
448
470
  const file = tempDir.join("file.txt");
449
471
  await file.write("");
@@ -455,7 +477,7 @@ test(".rmDir(…)", async () => {
455
477
  expect(async () => tempDir.rmDir()).toThrowError(/ENOENT/);
456
478
  });
457
479
 
458
- test(".rm_rf(…) (file)", async () => {
480
+ test.concurrent(".rm_rf(…) (file)", async () => {
459
481
  const file = (await Path.makeTempDir()).join("file.txt");
460
482
  await file.write("");
461
483
  expect(await file.existsAsFile()).toBe(true);
@@ -466,7 +488,7 @@ test(".rm_rf(…) (file)", async () => {
466
488
  expect(await file.existsAsFile()).toBe(false);
467
489
  });
468
490
 
469
- test(".rm_rf(…) (folder)", async () => {
491
+ test.concurrent(".rm_rf(…) (folder)", async () => {
470
492
  const tempDir = await Path.makeTempDir();
471
493
  await tempDir.join("file.txt").write("");
472
494
  expect(tempDir.path).toContain("/js-temp-");
@@ -477,7 +499,7 @@ test(".rm_rf(…) (folder)", async () => {
477
499
  expect(await tempDir.exists()).toBe(false);
478
500
  });
479
501
 
480
- test(".readText()", async () => {
502
+ test.concurrent(".readText()", async () => {
481
503
  const file = (await Path.makeTempDir()).join("file.txt");
482
504
  await file.write("hi");
483
505
  await file.write("bye");
@@ -486,7 +508,7 @@ test(".readText()", async () => {
486
508
  expect(await readFile(file.path, "utf-8")).toBe("bye");
487
509
  });
488
510
 
489
- test(".readJSON()", async () => {
511
+ test.concurrent(".readJSON()", async () => {
490
512
  const file = (await Path.makeTempDir()).join("file.json");
491
513
  await file.write(JSON.stringify({ foo: "bar" }));
492
514
 
@@ -497,7 +519,7 @@ test(".readJSON()", async () => {
497
519
  >({ foo: "bar" });
498
520
  });
499
521
 
500
- test(".readJSON(…) with fallback", async () => {
522
+ test.concurrent(".readJSON(…) with fallback", async () => {
501
523
  const tempDir = await Path.makeTempDir();
502
524
  const file = tempDir.join("file.json");
503
525
  const json: { foo?: number } = await file.readJSON({ fallback: { foo: 4 } });
@@ -515,7 +537,7 @@ test(".readJSON(…) with fallback", async () => {
515
537
  );
516
538
  });
517
539
 
518
- test(".write(…)", async () => {
540
+ test.concurrent(".write(…)", async () => {
519
541
  const tempDir = await Path.makeTempDir();
520
542
  const file = tempDir.join("file.json");
521
543
  expect(await file.write("foo")).toBe(file);
@@ -531,14 +553,14 @@ test(".write(…)", async () => {
531
553
  ).toEqual("bar");
532
554
  });
533
555
 
534
- test(".writeJSON(…)", async () => {
556
+ test.concurrent(".writeJSON(…)", async () => {
535
557
  const file = (await Path.makeTempDir()).join("file.json");
536
558
  expect(await file.writeJSON({ foo: "bar" })).toBe(file);
537
559
 
538
560
  expect(await file.readJSON()).toEqual<Record<string, string>>({ foo: "bar" });
539
561
  });
540
562
 
541
- test(".appendFile(…)", async () => {
563
+ test.concurrent(".appendFile(…)", async () => {
542
564
  const file = (await Path.makeTempDir()).join("file.txt");
543
565
  await file.appendFile("test\n");
544
566
  expect(await file.readText()).toEqual("test\n");
@@ -546,7 +568,7 @@ test(".appendFile(…)", async () => {
546
568
  expect(await file.readText()).toEqual("test\nmore\n");
547
569
  });
548
570
 
549
- test(".readDir(…)", async () => {
571
+ test.concurrent(".readDir(…)", async () => {
550
572
  const dir = await Path.makeTempDir();
551
573
  await dir.join("file.txt").write("hello");
552
574
  await dir.join("dir/file.json").write("hello");
@@ -560,7 +582,7 @@ test(".readDir(…)", async () => {
560
582
  );
561
583
  });
562
584
 
563
- test(".symlink(…)", async () => {
585
+ test.concurrent(".symlink(…)", async () => {
564
586
  const tempDir = await Path.makeTempDir();
565
587
  const source = tempDir.join("foo.txt");
566
588
  const target = tempDir.join("bar.txt");
@@ -572,7 +594,7 @@ test(".symlink(…)", async () => {
572
594
  expect(await target.readText()).toEqual("hello");
573
595
  });
574
596
 
575
- test(".realpath(…)", async () => {
597
+ test.concurrent(".realpath(…)", async () => {
576
598
  const tempDir = await Path.makeTempDir();
577
599
  const source = tempDir.join("foo.txt");
578
600
  await source.write("hello world!");
@@ -583,7 +605,7 @@ test(".realpath(…)", async () => {
583
605
  );
584
606
  });
585
607
 
586
- test(".stat(…)", async () => {
608
+ test.concurrent(".stat(…)", async () => {
587
609
  const file = (await Path.makeTempDir()).join("foo.txt");
588
610
  await file.write("hello");
589
611
 
@@ -592,7 +614,7 @@ test(".stat(…)", async () => {
592
614
  expect((await file.stat({ bigint: true })).size).toBeTypeOf("bigint");
593
615
  });
594
616
 
595
- test(".lstat(…)", async () => {
617
+ test.concurrent(".lstat(…)", async () => {
596
618
  const tempDir = await Path.makeTempDir();
597
619
  const source = tempDir.join("foo.txt");
598
620
  const target = tempDir.join("bar.txt");
@@ -605,7 +627,7 @@ test(".lstat(…)", async () => {
605
627
  expect(await target.readText()).toEqual("hello");
606
628
  });
607
629
 
608
- test(".chmod(…)", async () => {
630
+ test.concurrent(".chmod(…)", async () => {
609
631
  const binPath = (await Path.makeTempDir()).join("nonexistent.bin");
610
632
  expect(() => new PrintableShellCommand(binPath, []).text()).toThrow(
611
633
  /ENOENT|Premature close/,
@@ -624,7 +646,7 @@ echo hi`);
624
646
  expect(await new PrintableShellCommand(binPath, []).text()).toEqual("hi\n");
625
647
  });
626
648
 
627
- test(".chmodX(…)", async () => {
649
+ test.concurrent(".chmodX(…)", async () => {
628
650
  const binPath = (await Path.makeTempDir()).join("nonexistent.bin");
629
651
  expect(() => new PrintableShellCommand(binPath, []).text()).toThrow(
630
652
  /ENOENT|Premature close/,
@@ -639,22 +661,28 @@ echo hi`);
639
661
  expect(() => new PrintableShellCommand(binPath, []).text()).toThrow(
640
662
  /EACCES|Premature close/,
641
663
  );
664
+ expect((await binPath.stat()).mode & constants.S_IWUSR).toBeTruthy();
665
+ await binPath.chmod(0o444);
666
+ expect((await binPath.stat()).mode & constants.S_IWUSR).toBeFalsy();
667
+ expect((await binPath.stat()).mode & constants.S_IXUSR).toBeFalsy();
642
668
  await binPath.chmodX();
643
669
  expect(await new PrintableShellCommand(binPath, []).text()).toEqual("hi\n");
670
+ expect((await binPath.stat()).mode & constants.S_IWUSR).toBeFalsy();
671
+ expect((await binPath.stat()).mode & constants.S_IXUSR).toBeTruthy();
644
672
  });
645
673
 
646
- test(".homedir", async () => {
674
+ test.concurrent(".homedir", async () => {
647
675
  expect(Path.homedir.path).toEqual("/mock/home/dir");
648
676
  });
649
677
 
650
- test(".cwd", async () => {
678
+ test.concurrent(".cwd", async () => {
651
679
  expect(Path.cwd.basename.path).toEqual("path-class");
652
680
  const tempDir = await Path.makeTempDir();
653
681
  chdir(tempDir.path);
654
682
  expect(await realpath(Path.cwd.path)).toEqual(await realpath(tempDir.path));
655
683
  });
656
684
 
657
- test(".xdg", async () => {
685
+ test.concurrent(".xdg", async () => {
658
686
  expect(Path.xdg.cache.path).toEqual("/mock/home/dir/.cache");
659
687
  expect(Path.xdg.config.path).toEqual("/xdg/config");
660
688
  expect(Path.xdg.data.path).toEqual("/mock/home/dir/.local/share");
@@ -668,6 +696,7 @@ test(".xdg", async () => {
668
696
  const spy = spyOn(console, "log");
669
697
 
670
698
  test(".debugPrint(…)", async () => {
699
+ spy.mockReset();
671
700
  Path.homedir.debugPrint("Here is a test log of the mock home directory:");
672
701
  expect(spy.mock.calls).toEqual([
673
702
  ["Here is a test log of the mock home directory:"],
@@ -676,6 +705,7 @@ test(".debugPrint(…)", async () => {
676
705
  });
677
706
 
678
707
  test(".stringifyIfPath(…)", async () => {
708
+ spy.mockReset();
679
709
  expect(stringifyIfPath(Path.homedir)).toBe("/mock/home/dir");
680
710
  expect(stringifyIfPath("/mock/home/dir")).toBe("/mock/home/dir");
681
711
  expect(stringifyIfPath(4)).toBe(4);
package/src/Path.ts CHANGED
@@ -450,15 +450,28 @@ export class Path {
450
450
  /** Returns the destination path. */
451
451
  async cp(
452
452
  destination: string | URL | Path,
453
- options?: Parameters<typeof cp>[2],
453
+ options?: Parameters<typeof cp>[2] & { createIntermediateDirs?: boolean },
454
454
  ): Promise<Path> {
455
- await cp(this.#path, new Path(destination).#path, options);
456
- return new Path(destination);
455
+ const { createIntermediateDirs, ...cpOptions } = options ?? {};
456
+ const destinationPath = new Path(destination);
457
+ if (createIntermediateDirs ?? true) {
458
+ await destinationPath.parent.mkdir();
459
+ }
460
+ await cp(this.#path, destinationPath.#path, cpOptions);
461
+ return destinationPath;
457
462
  }
458
463
 
459
464
  // TODO: check idempotency semantics when the destination exists and is a folder.
460
- async rename(destination: string | URL | Path): Promise<void> {
461
- await rename(this.#path, new Path(destination).#path);
465
+ async rename(
466
+ destination: string | URL | Path,
467
+ options?: { createIntermediateDirs?: boolean },
468
+ ): Promise<Path> {
469
+ const destinationPath = new Path(destination);
470
+ if (options?.createIntermediateDirs ?? true) {
471
+ await destinationPath.parent.mkdir();
472
+ }
473
+ await rename(this.#path, destinationPath.#path);
474
+ return destinationPath;
462
475
  }
463
476
 
464
477
  /**
@@ -633,11 +646,7 @@ export class Path {
633
646
  async chmodX(): Promise<Path> {
634
647
  const { mode } = await this.stat();
635
648
  await this.chmod(
636
- mode |
637
- constants.S_IRWXU |
638
- constants.S_IXUSR |
639
- constants.S_IXGRP |
640
- constants.S_IXOTH,
649
+ mode | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH,
641
650
  );
642
651
  return this;
643
652
  }
@@ -716,3 +725,8 @@ export function mustNotHaveTrailingSlash(path: Path): void {
716
725
  );
717
726
  }
718
727
  }
728
+
729
+ const tmp = await Path.makeTempDir();
730
+ (await tmp.join("foo.json").write("foo")).rename(
731
+ tmp.join("sdfsD", "sdfsdfsdf", "sdfsdf.json"),
732
+ );
@@ -2,11 +2,11 @@ import { expect, test } from "bun:test";
2
2
  import { readFileSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import "./PathSync";
5
+ import { constants } from "node:fs/promises";
5
6
  import { PrintableShellCommand } from "printable-shell-command";
6
-
7
7
  import { PathSync } from "./PathSync";
8
8
 
9
- test(".existsAsFileSync()", () => {
9
+ test.concurrent(".existsAsFileSync()", () => {
10
10
  const filePath = PathSync.makeTempDirSync().join("file.txt");
11
11
  expect(filePath.existsSync()).toBe(false);
12
12
  expect(filePath.existsSync({ mustBe: "file" })).toBe(false);
@@ -24,7 +24,7 @@ test(".existsAsFileSync()", () => {
24
24
  expect(filePath.existsAsFileSync()).toBe(true);
25
25
  });
26
26
 
27
- test(".existsAsDir()", () => {
27
+ test.concurrent(".existsAsDir()", () => {
28
28
  const filePath = PathSync.makeTempDirSync();
29
29
  expect(filePath.existsSync()).toBe(true);
30
30
  expect(() => filePath.existsSync({ mustBe: "file" })).toThrow(
@@ -39,14 +39,14 @@ test(".existsAsDir()", () => {
39
39
  expect(filePath.existsAsDirSync()).toBe(false);
40
40
  });
41
41
 
42
- test(".mkdirSync(…) (un-nested)", () => {
42
+ test.concurrent(".mkdirSync(…) (un-nested)", () => {
43
43
  const dir = PathSync.makeTempDirSync().join("mkdir-test");
44
44
  expect(dir.existsSync()).toBe(false);
45
45
  dir.mkdirSync();
46
46
  expect(dir.existsSync()).toBe(true);
47
47
  });
48
48
 
49
- test(".mkdirSync(…) (nested)", () => {
49
+ test.concurrent(".mkdirSync(…) (nested)", () => {
50
50
  const dir = PathSync.makeTempDirSync().join("mkdir-test/nested");
51
51
  expect(dir.existsSync()).toBe(false);
52
52
  expect(() => dir.mkdirSync({ recursive: false })).toThrow("no such file");
@@ -54,10 +54,11 @@ test(".mkdirSync(…) (nested)", () => {
54
54
  expect(dir.existsSync()).toBe(true);
55
55
  });
56
56
 
57
- test(".cpSync(…)", () => {
57
+ test.concurrent(".cpSync(…)", () => {
58
58
  const parentDir = PathSync.makeTempDirSync();
59
59
  const file1 = parentDir.join("file1.txt");
60
60
  const file2 = parentDir.join("file2.txt");
61
+ const file3 = parentDir.join("nonexistent/dirs/file3.txt");
61
62
 
62
63
  file1.writeSync("hello world");
63
64
  expect(file1.existsSync()).toBe(true);
@@ -66,12 +67,23 @@ test(".cpSync(…)", () => {
66
67
  file1.cpSync(file2);
67
68
  expect(file1.existsSync()).toBe(true);
68
69
  expect(file2.existsSync()).toBe(true);
70
+
71
+ expect(() =>
72
+ file2.renameSync(file3, { createIntermediateDirs: false }),
73
+ ).toThrow(/^ENOENT/);
74
+ expect(file2.existsSync()).toBe(true);
75
+ expect(file3.existsSync()).toBe(false);
76
+
77
+ expect(file2.cpSync(file3).path).toEqual(file3.path);
78
+ expect(file2.existsSync()).toBe(true);
79
+ expect(file3.existsSync()).toBe(true);
69
80
  });
70
81
 
71
- test(".renameSync(…)", () => {
82
+ test.concurrent(".renameSync(…)", () => {
72
83
  const parentDir = PathSync.makeTempDirSync();
73
84
  const file1 = parentDir.join("file1.txt");
74
85
  const file2 = parentDir.join("file2.txt");
86
+ const file3 = parentDir.join("nonexistent/dirs/file3.txt");
75
87
 
76
88
  file1.writeSync("hello world");
77
89
  expect(file1.existsSync()).toBe(true);
@@ -80,9 +92,19 @@ test(".renameSync(…)", () => {
80
92
  file1.renameSync(file2);
81
93
  expect(file1.existsSync()).toBe(false);
82
94
  expect(file2.existsSync()).toBe(true);
95
+
96
+ expect(() =>
97
+ file2.renameSync(file3, { createIntermediateDirs: false }),
98
+ ).toThrow(/^ENOENT/);
99
+ expect(file2.existsSync()).toBe(true);
100
+ expect(file3.existsSync()).toBe(false);
101
+
102
+ expect(file2.renameSync(file3).path).toEqual(file3.path);
103
+ expect(file2.existsSync()).toBe(false);
104
+ expect(file3.existsSync()).toBe(true);
83
105
  });
84
106
 
85
- test(".makeTempDirSync(…)", () => {
107
+ test.concurrent(".makeTempDirSync(…)", () => {
86
108
  const tempDir = PathSync.makeTempDirSync();
87
109
  expect(tempDir.path).toContain("/js-temp-");
88
110
  expect(tempDir.basename.path).toStartWith("js-temp-");
@@ -93,7 +115,7 @@ test(".makeTempDirSync(…)", () => {
93
115
  expect(tempDir2.basename.path).toStartWith("foo");
94
116
  });
95
117
 
96
- test(".rmSync(…) (file)", () => {
118
+ test.concurrent(".rmSync(…) (file)", () => {
97
119
  const file = PathSync.makeTempDirSync().join("file.txt");
98
120
  file.writeSync("");
99
121
  expect(file.existsAsFileSync()).toBe(true);
@@ -103,7 +125,7 @@ test(".rmSync(…) (file)", () => {
103
125
  expect(() => file.rmSync()).toThrowError(/ENOENT/);
104
126
  });
105
127
 
106
- test(".rmSync(…) (folder)", () => {
128
+ test.concurrent(".rmSync(…) (folder)", () => {
107
129
  const tempDir = PathSync.makeTempDirSync();
108
130
  const file = tempDir.join("file.txt");
109
131
  file.writeSync("");
@@ -115,7 +137,7 @@ test(".rmSync(…) (folder)", () => {
115
137
  expect(() => tempDir.rmSync()).toThrowError(/ENOENT/);
116
138
  });
117
139
 
118
- test(".rmDirSync(…) (folder)", () => {
140
+ test.concurrent(".rmDirSync(…) (folder)", () => {
119
141
  const tempDir = PathSync.makeTempDirSync();
120
142
  const file = tempDir.join("file.txt");
121
143
  file.writeSync("");
@@ -127,7 +149,7 @@ test(".rmDirSync(…) (folder)", () => {
127
149
  expect(() => tempDir.rmDirSync()).toThrowError(/ENOENT/);
128
150
  });
129
151
 
130
- test(".rm_rfSync(…) (file)", () => {
152
+ test.concurrent(".rm_rfSync(…) (file)", () => {
131
153
  const file = PathSync.makeTempDirSync().join("file.txt");
132
154
  file.writeSync("");
133
155
  expect(file.existsAsFileSync()).toBe(true);
@@ -138,7 +160,7 @@ test(".rm_rfSync(…) (file)", () => {
138
160
  expect(file.existsAsFileSync()).toBe(false);
139
161
  });
140
162
 
141
- test(".rm_rfSync(…) (folder)", () => {
163
+ test.concurrent(".rm_rfSync(…) (folder)", () => {
142
164
  const tempDir = PathSync.makeTempDirSync();
143
165
  tempDir.join("file.txt").writeSync("");
144
166
  expect(tempDir.path).toContain("/js-temp-");
@@ -149,7 +171,7 @@ test(".rm_rfSync(…) (folder)", () => {
149
171
  expect(tempDir.existsSync()).toBe(false);
150
172
  });
151
173
 
152
- test(".readTextSync()", () => {
174
+ test.concurrent(".readTextSync()", () => {
153
175
  const file = PathSync.makeTempDirSync().join("file.txt");
154
176
  file.writeSync("hi");
155
177
  file.writeSync("bye");
@@ -158,7 +180,7 @@ test(".readTextSync()", () => {
158
180
  expect(readFileSync(file.path, "utf-8")).toBe("bye");
159
181
  });
160
182
 
161
- test(".readJSONSync()", () => {
183
+ test.concurrent(".readJSONSync()", () => {
162
184
  const file = PathSync.makeTempDirSync().join("file.json");
163
185
  file.writeSync(JSON.stringify({ foo: "bar" }));
164
186
 
@@ -169,7 +191,7 @@ test(".readJSONSync()", () => {
169
191
  >({ foo: "bar" });
170
192
  });
171
193
 
172
- test(".readJSONSync(…) with fallback", () => {
194
+ test.concurrent(".readJSONSync(…) with fallback", () => {
173
195
  const tempDir = PathSync.makeTempDirSync();
174
196
  const file = tempDir.join("file.json");
175
197
  const json: { foo?: number } = file.readJSONSync({ fallback: { foo: 4 } });
@@ -187,7 +209,7 @@ test(".readJSONSync(…) with fallback", () => {
187
209
  );
188
210
  });
189
211
 
190
- test(".writeSync(…)", () => {
212
+ test.concurrent(".writeSync(…)", () => {
191
213
  const tempDir = PathSync.makeTempDirSync();
192
214
  const file = tempDir.join("file.json");
193
215
  expect(file.writeSync("foo")).toBe(file);
@@ -203,14 +225,14 @@ test(".writeSync(…)", () => {
203
225
  ).toEqual("bar");
204
226
  });
205
227
 
206
- test(".writeJSONSync(…)", () => {
228
+ test.concurrent(".writeJSONSync(…)", () => {
207
229
  const file = PathSync.makeTempDirSync().join("file.json");
208
230
  expect(file.writeJSONSync({ foo: "bar" })).toBe(file);
209
231
 
210
232
  expect(file.readJSONSync()).toEqual<Record<string, string>>({ foo: "bar" });
211
233
  });
212
234
 
213
- test(".appendFileSync(…)", () => {
235
+ test.concurrent(".appendFileSync(…)", () => {
214
236
  const file = PathSync.makeTempDirSync().join("file.txt");
215
237
  file.appendFileSync("test\n");
216
238
  expect(file.readTextSync()).toEqual("test\n");
@@ -218,7 +240,7 @@ test(".appendFileSync(…)", () => {
218
240
  expect(file.readTextSync()).toEqual("test\nmore\n");
219
241
  });
220
242
 
221
- test(".readDirSync(…)", () => {
243
+ test.concurrent(".readDirSync(…)", () => {
222
244
  const dir = PathSync.makeTempDirSync();
223
245
  dir.join("file.txt").writeSync("hello");
224
246
  dir.join("dir/file.json").writeSync("hello");
@@ -232,7 +254,7 @@ test(".readDirSync(…)", () => {
232
254
  );
233
255
  });
234
256
 
235
- test(".symlinkSync(…)", () => {
257
+ test.concurrent(".symlinkSync(…)", () => {
236
258
  const tempDir = PathSync.makeTempDirSync();
237
259
  const source = tempDir.join("foo.txt");
238
260
  const target = tempDir.join("bar.txt");
@@ -244,7 +266,7 @@ test(".symlinkSync(…)", () => {
244
266
  expect(target.readTextSync()).toEqual("hello");
245
267
  });
246
268
 
247
- test(".realpathSync(…)", () => {
269
+ test.concurrent(".realpathSync(…)", () => {
248
270
  const tempDir = PathSync.makeTempDirSync();
249
271
  const source = tempDir.join("foo.txt");
250
272
  source.writeSync("hello world!");
@@ -253,7 +275,7 @@ test(".realpathSync(…)", () => {
253
275
  expect(source.realpathSync().path).toEqual(target.realpathSync().path);
254
276
  });
255
277
 
256
- test(".statSync(…)", () => {
278
+ test.concurrent(".statSync(…)", () => {
257
279
  const file = PathSync.makeTempDirSync().join("foo.txt");
258
280
  file.writeSync("hello");
259
281
 
@@ -262,7 +284,7 @@ test(".statSync(…)", () => {
262
284
  expect(file.statSync({ bigint: true })?.size).toBeTypeOf("bigint");
263
285
  });
264
286
 
265
- test(".lstatSync(…)", () => {
287
+ test.concurrent(".lstatSync(…)", () => {
266
288
  const tempDir = PathSync.makeTempDirSync();
267
289
  const source = tempDir.join("foo.txt");
268
290
  const target = tempDir.join("bar.txt");
@@ -275,7 +297,7 @@ test(".lstatSync(…)", () => {
275
297
  expect(target.readTextSync()).toEqual("hello");
276
298
  });
277
299
 
278
- test(".chmodSync(…)", () => {
300
+ test.concurrent(".chmodSync(…)", () => {
279
301
  const binPath = PathSync.makeTempDirSync().join("nonexistent.bin");
280
302
  expect(() => new PrintableShellCommand(binPath, []).text()).toThrow(
281
303
  /ENOENT|Premature close/,
@@ -294,7 +316,7 @@ echo hi`);
294
316
  expect(() => new PrintableShellCommand(binPath, []).text()).not.toThrow();
295
317
  });
296
318
 
297
- test(".chmodXSync(…)", () => {
319
+ test.concurrent(".chmodXSync(…)", () => {
298
320
  const binPath = PathSync.makeTempDirSync().join("nonexistent.bin");
299
321
  expect(() => new PrintableShellCommand(binPath, []).text()).toThrow(
300
322
  /ENOENT|Premature close/,
@@ -309,6 +331,12 @@ echo hi`);
309
331
  expect(() => new PrintableShellCommand(binPath, []).text()).toThrow(
310
332
  /EACCES|Premature close/,
311
333
  );
334
+ expect(binPath.statSync().mode & constants.S_IWUSR).toBeTruthy();
335
+ binPath.chmodSync(0o444);
336
+ expect(binPath.statSync().mode & constants.S_IWUSR).toBeFalsy();
337
+ expect(binPath.statSync().mode & constants.S_IXUSR).toBeFalsy();
312
338
  binPath.chmodXSync();
313
339
  expect(() => new PrintableShellCommand(binPath, []).text()).not.toThrow();
340
+ expect(binPath.statSync().mode & constants.S_IWUSR).toBeFalsy();
341
+ expect(binPath.statSync().mode & constants.S_IXUSR).toBeTruthy();
314
342
  });
@@ -144,14 +144,29 @@ export class PathSync extends Path {
144
144
 
145
145
  cpSync(
146
146
  destination: string | URL | Path,
147
- options?: Parameters<typeof cpSync>[2],
147
+ options?: Parameters<typeof cpSync>[2] & {
148
+ createIntermediateDirs?: boolean;
149
+ },
148
150
  ): PathSync {
149
- cpSync(this.path, new Path(destination).path, options);
150
- return new PathSync(destination);
151
+ const { createIntermediateDirs, ...cpOptions } = options ?? {};
152
+ const destinationPath = new PathSync(destination);
153
+ if (createIntermediateDirs ?? true) {
154
+ destinationPath.parent.mkdirSync();
155
+ }
156
+ cpSync(this.path, destinationPath.path, cpOptions);
157
+ return destinationPath;
151
158
  }
152
159
 
153
- renameSync(destination: string | URL | Path): void {
154
- renameSync(this.path, new Path(destination).path);
160
+ renameSync(
161
+ destination: string | URL | Path,
162
+ options?: { createIntermediateDirs?: boolean },
163
+ ): PathSync {
164
+ const destinationPath = new PathSync(destination);
165
+ if (options?.createIntermediateDirs ?? true) {
166
+ destinationPath.parent.mkdirSync();
167
+ }
168
+ renameSync(this.path, destinationPath.path);
169
+ return destinationPath;
155
170
  }
156
171
 
157
172
  static makeTempDirSync(prefix?: string): PathSync {
@@ -260,11 +275,7 @@ export class PathSync extends Path {
260
275
  chmodXSync(): PathSync {
261
276
  const { mode } = this.statSync();
262
277
  this.chmodSync(
263
- mode |
264
- constants.S_IRWXU |
265
- constants.S_IXUSR |
266
- constants.S_IXGRP |
267
- constants.S_IXOTH,
278
+ mode | constants.S_IXUSR | constants.S_IXGRP | constants.S_IXOTH,
268
279
  );
269
280
  return this;
270
281
  }