path-class 0.12.2 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/path-class/Path.d.ts +66 -10
- package/dist/lib/path-class/chunks/{chunk-7GEFMWZY.js → chunk-IZ3HOMWW.js} +144 -32
- package/dist/lib/path-class/chunks/chunk-IZ3HOMWW.js.map +7 -0
- package/dist/lib/path-class/index.d.ts +2 -1
- package/dist/lib/path-class/index.js +1 -1
- package/dist/lib/path-class/stringifyfIfPath.d.ts +11 -0
- package/dist/lib/path-class/sync/PathSync.d.ts +35 -1
- package/dist/lib/path-class/sync/index.js +59 -3
- package/dist/lib/path-class/sync/index.js.map +2 -2
- package/package.json +2 -1
- package/src/Path.test.ts +165 -98
- package/src/Path.ts +172 -43
- package/src/index.ts +2 -1
- package/src/stringifyIfPath.test.ts +9 -0
- package/src/stringifyfIfPath.ts +17 -0
- package/src/sync/PathSync.test.ts +111 -64
- package/src/sync/PathSync.ts +73 -3
- package/src/test.preload.ts +41 -2
- package/dist/lib/path-class/chunks/chunk-7GEFMWZY.js.map +0 -7
|
@@ -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 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": ";;;;;;AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB;AAC1B,SAAS,cAAc;
|
|
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\nconst DEFAULT_TEMP_PREFIX = \"js-temp-sync-\";\nconst DEFAULT_TEMP_FILE_NAME = \"file\";\n\nexport class PathSync extends Path {\n static override fromString(s: string): PathSync {\n return new PathSync(s);\n }\n\n static override resolve(...args: Parameters<typeof Path.resolve>): PathSync {\n return new PathSync(Path.resolve(...args));\n }\n\n override resolve(...args: Parameters<Path[\"resolve\"]>): PathSync {\n return new PathSync(super.resolve(...args));\n }\n\n override descendantRelativePath(\n ...args: Parameters<Path[\"descendantRelativePath\"]>\n ): PathSync | null {\n const v = super.descendantRelativePath(...args);\n if (v === null) {\n return null;\n }\n return new PathSync(v);\n }\n\n override toggleTrailingSlash(\n ...args: Parameters<Path[\"toggleTrailingSlash\"]>\n ): PathSync {\n return new PathSync(super.toggleTrailingSlash(...args));\n }\n\n override join(...args: Parameters<Path[\"join\"]>): PathSync {\n return new PathSync(super.join(...args));\n }\n\n override asRelative(...args: Parameters<Path[\"asRelative\"]>): PathSync {\n return new PathSync(super.asRelative(...args));\n }\n\n override asAbsolute(...args: Parameters<Path[\"asAbsolute\"]>): PathSync {\n return new PathSync(super.asAbsolute(...args));\n }\n\n override asBare(...args: Parameters<Path[\"asBare\"]>): PathSync {\n return new PathSync(super.asBare(...args));\n }\n\n override extendBasename(\n ...args: Parameters<Path[\"extendBasename\"]>\n ): PathSync {\n return new PathSync(super.extendBasename(...args));\n }\n\n override get parent(): PathSync {\n return new PathSync(super.parent);\n }\n\n override get dirname(): PathSync {\n return new PathSync(super.dirname);\n }\n\n override get basename(): PathSync {\n return new PathSync(super.basename);\n }\n\n static override get homedir(): PathSync {\n return new PathSync(Path.homedir);\n }\n\n static override get cwd(): PathSync {\n return new PathSync(Path.cwd);\n }\n\n override debugPrint(...args: Parameters<Path[\"debugPrint\"]>): PathSync {\n return new PathSync(super.debugPrint(...args));\n }\n\n // TODO: find a neat way to dedup with the async version? // lint-sync-code-expect-error\n existsSync(constraints?: { mustBe: \"file\" | \"directory\" }): boolean {\n if (constraints?.mustBe === \"file\") {\n mustNotHaveTrailingSlash(this);\n }\n let stats: ReturnType<typeof statSync>;\n try {\n stats = statSync(this.path);\n // biome-ignore lint/suspicious/noExplicitAny: TypeScript limitation\n } catch (e: any) {\n if (e.code === \"ENOENT\") {\n return false;\n }\n throw e;\n }\n if (!constraints?.mustBe) {\n return true;\n }\n switch (constraints?.mustBe) {\n case \"file\": {\n if (stats.isFile()) {\n return true;\n }\n throw new Error(`PathSync exists but is not a file: ${this.path}`);\n }\n case \"directory\": {\n if (stats.isDirectory()) {\n return true;\n }\n throw new Error(`PathSync exists but is not a directory: ${this.path}`);\n }\n default: {\n throw new Error(\"Invalid path type constraint\");\n }\n }\n }\n\n existsAsFileSync(): boolean {\n return this.existsSync({ mustBe: \"file\" });\n }\n\n existsAsDirSync(): boolean {\n return this.existsSync({ mustBe: \"directory\" });\n }\n\n mkdirSync(options?: Parameters<typeof mkdirSync>[1]): PathSync {\n const optionsObject = (() => {\n if (typeof options === \"string\" || typeof options === \"number\") {\n return { mode: options };\n }\n return options ?? {};\n })();\n mkdirSync(this.path, { recursive: true, ...optionsObject });\n return this;\n }\n\n cpSync(\n destination: string | URL | Path,\n options?: Parameters<typeof cpSync>[2] & {\n createIntermediateDirs?: boolean;\n },\n ): PathSync {\n const { createIntermediateDirs, ...cpOptions } = options ?? {};\n const destinationPath = new PathSync(destination);\n if (createIntermediateDirs ?? true) {\n destinationPath.parent.mkdirSync();\n }\n cpSync(this.path, destinationPath.path, cpOptions);\n return destinationPath;\n }\n\n renameSync(\n destination: string | URL | Path,\n options?: { createIntermediateDirs?: boolean },\n ): PathSync {\n const destinationPath = new PathSync(destination);\n if (options?.createIntermediateDirs ?? true) {\n destinationPath.parent.mkdirSync();\n }\n renameSync(this.path, destinationPath.path);\n return destinationPath;\n }\n\n static makeTempDirSync(prefix?: string): DisposablePathSync {\n return new DisposablePathSync(\n mkdtempSync(\n new Path(tmpdir()).join(prefix ?? DEFAULT_TEMP_PREFIX).toString(),\n ),\n );\n }\n\n /**\n * Return a path:\n *\n * - whose parent dir is a temp dir that *has* been created, but\n * - which has itself not yet been created.\n *\n * Note that this path can actually also be used to create dir, but it is most\n * convenient to get a path for a temporary file that can be written to, while\n * having a disposal implementation that cleans everything up:\n *\n * using tempFile = PathSync.tempFilePathSync({ basename: \"foo.txt\" });\n * tempFile.writeSync(\"hello world!\");\n * // \u2026\n *\n * Note that that the following are equivalent when *not* using `using`:\n *\n * PathSync.tempFilePathSync({ basename: \"foo.txt\" });\n * PathSync.makeTempDirSync().join(\"file.txt\");\n *\n * However, it is recommended to use `using` to ensure cleanup.\n */\n static tempFilePathSync(options: {\n tempDirPrefix?: string;\n basename?: string | Path;\n }): DisposablePathSync {\n const tempDir = PathSync.makeTempDirSync(options?.tempDirPrefix);\n return new DisposablePathSync(\n tempDir.join(options?.basename ?? DEFAULT_TEMP_FILE_NAME),\n { disposePathInstead: tempDir },\n );\n }\n\n rmSync(options?: Parameters<typeof rmSync>[1]): void {\n rmSync(this.path, options);\n }\n\n rmDirSync(): void {\n rmdirSync(this.path);\n }\n\n rm_rfSync(options?: Parameters<typeof rmSync>[1]): void {\n this.rmSync({ recursive: true, force: true, ...(options ?? {}) });\n }\n\n readSync: typeof readFileSyncType = (options) =>\n // biome-ignore lint/suspicious/noExplicitAny: Needed to wrangle the types.\n readFileSync(this.path, options) as any;\n\n readTextSync(): string {\n return readFileSync(this.path, \"utf-8\");\n }\n\n readJSONSync<T>(options?: { fallback?: T }): T {\n try {\n return JSON.parse(this.readTextSync());\n } catch (e) {\n if (\n (e as { code?: string }).code === \"ENOENT\" &&\n options &&\n \"fallback\" in options\n ) {\n return options.fallback as T;\n }\n throw e;\n }\n }\n\n appendFileSync(\n data: Parameters<typeof appendFileSync>[1],\n options?: Parameters<typeof appendFileSync>[2],\n ): PathSync {\n appendFileSync(this.path, data, options);\n return this;\n }\n\n writeSync(\n data: Parameters<typeof writeFileSync>[1],\n options?: Parameters<typeof writeFileSync>[2],\n ): PathSync {\n this.parent.mkdirSync();\n writeFileSync(this.path, data, options);\n return this;\n }\n\n writeJSONSync<T>(\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\nexport class DisposablePathSync extends PathSync {\n #options?: { disposePathInstead: PathSync };\n constructor(\n path: ConstructorParameters<typeof Path>[0],\n options?: { disposePathInstead: Path | string },\n ) {\n super(path);\n if (options) {\n this.#options = {\n disposePathInstead: new PathSync(options.disposePathInstead),\n };\n }\n }\n\n [Symbol.dispose]() {\n (this.#options?.disposePathInstead ?? this).rm_rfSync();\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AASvB,IAAM,sBAAsB;AAC5B,IAAM,yBAAyB;AAExB,IAAM,WAAN,MAAM,kBAAiB,KAAK;AAAA,EACjC,OAAgB,WAAW,GAAqB;AAC9C,WAAO,IAAI,UAAS,CAAC;AAAA,EACvB;AAAA,EAEA,OAAgB,WAAW,MAAiD;AAC1E,WAAO,IAAI,UAAS,KAAK,QAAQ,GAAG,IAAI,CAAC;AAAA,EAC3C;AAAA,EAES,WAAW,MAA6C;AAC/D,WAAO,IAAI,UAAS,MAAM,QAAQ,GAAG,IAAI,CAAC;AAAA,EAC5C;AAAA,EAES,0BACJ,MACc;AACjB,UAAM,IAAI,MAAM,uBAAuB,GAAG,IAAI;AAC9C,QAAI,MAAM,MAAM;AACd,aAAO;AAAA,IACT;AACA,WAAO,IAAI,UAAS,CAAC;AAAA,EACvB;AAAA,EAES,uBACJ,MACO;AACV,WAAO,IAAI,UAAS,MAAM,oBAAoB,GAAG,IAAI,CAAC;AAAA,EACxD;AAAA,EAES,QAAQ,MAA0C;AACzD,WAAO,IAAI,UAAS,MAAM,KAAK,GAAG,IAAI,CAAC;AAAA,EACzC;AAAA,EAES,cAAc,MAAgD;AACrE,WAAO,IAAI,UAAS,MAAM,WAAW,GAAG,IAAI,CAAC;AAAA,EAC/C;AAAA,EAES,cAAc,MAAgD;AACrE,WAAO,IAAI,UAAS,MAAM,WAAW,GAAG,IAAI,CAAC;AAAA,EAC/C;AAAA,EAES,UAAU,MAA4C;AAC7D,WAAO,IAAI,UAAS,MAAM,OAAO,GAAG,IAAI,CAAC;AAAA,EAC3C;AAAA,EAES,kBACJ,MACO;AACV,WAAO,IAAI,UAAS,MAAM,eAAe,GAAG,IAAI,CAAC;AAAA,EACnD;AAAA,EAEA,IAAa,SAAmB;AAC9B,WAAO,IAAI,UAAS,MAAM,MAAM;AAAA,EAClC;AAAA,EAEA,IAAa,UAAoB;AAC/B,WAAO,IAAI,UAAS,MAAM,OAAO;AAAA,EACnC;AAAA,EAEA,IAAa,WAAqB;AAChC,WAAO,IAAI,UAAS,MAAM,QAAQ;AAAA,EACpC;AAAA,EAEA,WAAoB,UAAoB;AACtC,WAAO,IAAI,UAAS,KAAK,OAAO;AAAA,EAClC;AAAA,EAEA,WAAoB,MAAgB;AAClC,WAAO,IAAI,UAAS,KAAK,GAAG;AAAA,EAC9B;AAAA,EAES,cAAc,MAAgD;AACrE,WAAO,IAAI,UAAS,MAAM,WAAW,GAAG,IAAI,CAAC;AAAA,EAC/C;AAAA;AAAA,EAGA,WAAW,aAAyD;AAClE,QAAI,aAAa,WAAW,QAAQ;AAClC,+BAAyB,IAAI;AAAA,IAC/B;AACA,QAAI;AACJ,QAAI;AACF,cAAQ,SAAS,KAAK,IAAI;AAAA,IAE5B,SAAS,GAAQ;AACf,UAAI,EAAE,SAAS,UAAU;AACvB,eAAO;AAAA,MACT;AACA,YAAM;AAAA,IACR;AACA,QAAI,CAAC,aAAa,QAAQ;AACxB,aAAO;AAAA,IACT;AACA,YAAQ,aAAa,QAAQ;AAAA,MAC3B,KAAK,QAAQ;AACX,YAAI,MAAM,OAAO,GAAG;AAClB,iBAAO;AAAA,QACT;AACA,cAAM,IAAI,MAAM,sCAAsC,KAAK,IAAI,EAAE;AAAA,MACnE;AAAA,MACA,KAAK,aAAa;AAChB,YAAI,MAAM,YAAY,GAAG;AACvB,iBAAO;AAAA,QACT;AACA,cAAM,IAAI,MAAM,2CAA2C,KAAK,IAAI,EAAE;AAAA,MACxE;AAAA,MACA,SAAS;AACP,cAAM,IAAI,MAAM,8BAA8B;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,mBAA4B;AAC1B,WAAO,KAAK,WAAW,EAAE,QAAQ,OAAO,CAAC;AAAA,EAC3C;AAAA,EAEA,kBAA2B;AACzB,WAAO,KAAK,WAAW,EAAE,QAAQ,YAAY,CAAC;AAAA,EAChD;AAAA,EAEA,UAAU,SAAqD;AAC7D,UAAM,iBAAiB,MAAM;AAC3B,UAAI,OAAO,YAAY,YAAY,OAAO,YAAY,UAAU;AAC9D,eAAO,EAAE,MAAM,QAAQ;AAAA,MACzB;AACA,aAAO,WAAW,CAAC;AAAA,IACrB,GAAG;AACH,cAAU,KAAK,MAAM,EAAE,WAAW,MAAM,GAAG,cAAc,CAAC;AAC1D,WAAO;AAAA,EACT;AAAA,EAEA,OACE,aACA,SAGU;AACV,UAAM,EAAE,wBAAwB,GAAG,UAAU,IAAI,WAAW,CAAC;AAC7D,UAAM,kBAAkB,IAAI,UAAS,WAAW;AAChD,QAAI,0BAA0B,MAAM;AAClC,sBAAgB,OAAO,UAAU;AAAA,IACnC;AACA,WAAO,KAAK,MAAM,gBAAgB,MAAM,SAAS;AACjD,WAAO;AAAA,EACT;AAAA,EAEA,WACE,aACA,SACU;AACV,UAAM,kBAAkB,IAAI,UAAS,WAAW;AAChD,QAAI,SAAS,0BAA0B,MAAM;AAC3C,sBAAgB,OAAO,UAAU;AAAA,IACnC;AACA,eAAW,KAAK,MAAM,gBAAgB,IAAI;AAC1C,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,gBAAgB,QAAqC;AAC1D,WAAO,IAAI;AAAA,MACT;AAAA,QACE,IAAI,KAAK,OAAO,CAAC,EAAE,KAAK,UAAU,mBAAmB,EAAE,SAAS;AAAA,MAClE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,OAAO,iBAAiB,SAGD;AACrB,UAAM,UAAU,UAAS,gBAAgB,SAAS,aAAa;AAC/D,WAAO,IAAI;AAAA,MACT,QAAQ,KAAK,SAAS,YAAY,sBAAsB;AAAA,MACxD,EAAE,oBAAoB,QAAQ;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,OAAO,SAA8C;AACnD,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B;AAAA,EAEA,YAAkB;AAChB,cAAU,KAAK,IAAI;AAAA,EACrB;AAAA,EAEA,UAAU,SAA8C;AACtD,SAAK,OAAO,EAAE,WAAW,MAAM,OAAO,MAAM,GAAI,WAAW,CAAC,EAAG,CAAC;AAAA,EAClE;AAAA,EAEA,WAAoC,CAAC;AAAA;AAAA,IAEnC,aAAa,KAAK,MAAM,OAAO;AAAA;AAAA,EAEjC,eAAuB;AACrB,WAAO,aAAa,KAAK,MAAM,OAAO;AAAA,EACxC;AAAA,EAEA,aAAgB,SAA+B;AAC7C,QAAI;AACF,aAAO,KAAK,MAAM,KAAK,aAAa,CAAC;AAAA,IACvC,SAAS,GAAG;AACV,UACG,EAAwB,SAAS,YAClC,WACA,cAAc,SACd;AACA,eAAO,QAAQ;AAAA,MACjB;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,eACE,MACA,SACU;AACV,mBAAe,KAAK,MAAM,MAAM,OAAO;AACvC,WAAO;AAAA,EACT;AAAA,EAEA,UACE,MACA,SACU;AACV,SAAK,OAAO,UAAU;AACtB,kBAAc,KAAK,MAAM,MAAM,OAAO;AACtC,WAAO;AAAA,EACT;AAAA,EAEA,cACE,MACA,WAAiD,MACjD,QAA8C,MACpC;AACV,SAAK,OAAO,UAAU;AACtB,SAAK,UAAU,KAAK,UAAU,MAAM,UAAU,KAAK,CAAC;AACpD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,cAAsC,CAAC;AAAA;AAAA,IAErC,YAAY,KAAK,MAAM,OAAO;AAAA;AAAA,EAEhC,YACE,QACA,MACU;AACV,UAAM,aAAa,IAAI,UAAS,MAAM;AACtC;AAAA,MACE,KAAK;AAAA,MACL,WAAW;AAAA,MACX;AAAA;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,eAAyB;AACvB,WAAO,IAAI,UAAS,aAAa,KAAK,IAAI,CAAC;AAAA,EAC7C;AAAA,EAEA,WAAgC,CAAC;AAAA;AAAA,IAE/B,SAAS,KAAK,MAAM,OAAO;AAAA;AAAA,EAE7B,YAAkC,CAAC;AAAA;AAAA,IAEjC,UAAU,KAAK,MAAM,OAAO;AAAA;AAAA,EAE9B,UAAU,MAAiD;AACzD,cAAU,KAAK,MAAM,IAAI;AACzB,WAAO;AAAA,EACT;AAAA,EAEA,aAAuB;AACrB,UAAM,EAAE,KAAK,IAAI,KAAK,SAAS;AAC/B,SAAK;AAAA,MACH,OAAO,UAAU,UAAU,UAAU,UAAU,UAAU;AAAA,IAC3D;AACA,WAAO;AAAA,EACT;AACF;AAEO,IAAM,qBAAN,cAAiC,SAAS;AAAA,EAC/C;AAAA,EACA,YACE,MACA,SACA;AACA,UAAM,IAAI;AACV,QAAI,SAAS;AACX,WAAK,WAAW;AAAA,QACd,oBAAoB,IAAI,SAAS,QAAQ,kBAAkB;AAAA,MAC7D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,CAAC,OAAO,OAAO,IAAI;AACjB,KAAC,KAAK,UAAU,sBAAsB,MAAM,UAAU;AAAA,EACxD;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "path-class",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "A semantic `Path` class for `node` and `bun`. Inspired by `bun`'s [`file(…)`](https://bun.com/docs/runtime/file-io) API, but `node`-compatible.",
|
|
5
5
|
"author": "Lucas Garron <code@garron.net>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"@cubing/dev-config": ">=0.8.1",
|
|
31
31
|
"@types/bun": "^1.3.0",
|
|
32
32
|
"bun-dx": ">=0.1.3",
|
|
33
|
+
"ergonomic-date": "^0.1.3",
|
|
33
34
|
"esbuild": "^0.25.10",
|
|
34
35
|
"printable-shell-command": "^4.0.4",
|
|
35
36
|
"readme-cli-help": ">=0.4.9",
|
package/src/Path.test.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { expect, spyOn, test } from "bun:test";
|
|
1
|
+
import { expect, jest, spyOn, test } from "bun:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
2
4
|
import { constants, readFile, realpath } from "node:fs/promises";
|
|
3
5
|
import { join } from "node:path";
|
|
4
6
|
import { chdir } from "node:process";
|
|
5
|
-
import {
|
|
6
|
-
import { Path, ResolutionPrefix, stringifyIfPath } from "./Path";
|
|
7
|
+
import { Path, ResolutionPrefix } from "./Path";
|
|
7
8
|
|
|
8
9
|
test.concurrent("constructor", async () => {
|
|
9
10
|
expect(new Path("bare").path).toEqual("bare");
|
|
@@ -25,6 +26,8 @@ test.concurrent("constructor", async () => {
|
|
|
25
26
|
expect(new Path("../").path).toEqual("../");
|
|
26
27
|
expect(new Path(new URL("file:///root/")).path).toEqual("/root/");
|
|
27
28
|
expect(new Path(new Path("foo")).path).toEqual("foo");
|
|
29
|
+
|
|
30
|
+
expect(new Path("./.foo").path).toEqual("./.foo");
|
|
28
31
|
});
|
|
29
32
|
|
|
30
33
|
test.concurrent(".fromString(…)", async () => {
|
|
@@ -83,6 +86,9 @@ test.concurrent("Path.resolve(…)", async () => {
|
|
|
83
86
|
expect(Path.resolve("foo/lish", new Path("/bar/baz")).path).toEqual(
|
|
84
87
|
"/bar/foo/lish",
|
|
85
88
|
);
|
|
89
|
+
expect(Path.resolve("foo/lish", new Path("/bar/baz/")).path).toEqual(
|
|
90
|
+
"/bar/baz/foo/lish",
|
|
91
|
+
);
|
|
86
92
|
expect(() => Path.resolve("foo/lish", new Path("bar/baz")).path).toThrow(
|
|
87
93
|
/must be an absolute path/,
|
|
88
94
|
);
|
|
@@ -95,6 +101,78 @@ test.concurrent("Path.resolve(…)", async () => {
|
|
|
95
101
|
);
|
|
96
102
|
});
|
|
97
103
|
|
|
104
|
+
test.concurrent(".resolve(…)", async () => {
|
|
105
|
+
expect(new Path("/bar/baz").resolve("foo/lish").path).toEqual(
|
|
106
|
+
"/bar/foo/lish",
|
|
107
|
+
);
|
|
108
|
+
expect(new Path("/bar/baz/").resolve("foo/lish").path).toEqual(
|
|
109
|
+
"/bar/baz/foo/lish",
|
|
110
|
+
);
|
|
111
|
+
expect(() => new Path("bar/baz").resolve("foo/lish").path).toThrow(
|
|
112
|
+
/must be an absolute path/,
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
function mustNotBeNull<T>(t: T): Exclude<T, null> {
|
|
117
|
+
assert.notEqual(t, null);
|
|
118
|
+
// @ts-expect-error Cast
|
|
119
|
+
return t;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
test.concurrent(".descendantRelativePath(…)", async () => {
|
|
123
|
+
expect(
|
|
124
|
+
mustNotBeNull(
|
|
125
|
+
new Path("/Users/lgarron/").descendantRelativePath(
|
|
126
|
+
"/Users/lgarron/Library",
|
|
127
|
+
),
|
|
128
|
+
).path,
|
|
129
|
+
).toEqual("./Library");
|
|
130
|
+
expect(
|
|
131
|
+
mustNotBeNull(
|
|
132
|
+
new Path("/Users/lgarron/").descendantRelativePath(
|
|
133
|
+
"/Users/lgarron/Library/",
|
|
134
|
+
),
|
|
135
|
+
).path,
|
|
136
|
+
).toEqual("./Library/");
|
|
137
|
+
expect(
|
|
138
|
+
() =>
|
|
139
|
+
mustNotBeNull(
|
|
140
|
+
new Path("/Users/lgarron").descendantRelativePath(
|
|
141
|
+
"/Users/lgarron/Library/",
|
|
142
|
+
),
|
|
143
|
+
).path,
|
|
144
|
+
).toThrow("Ancestor must have a trailing slash.");
|
|
145
|
+
expect(
|
|
146
|
+
mustNotBeNull(
|
|
147
|
+
new Path("/Users/lgarron").descendantRelativePath(
|
|
148
|
+
"/Users/lgarron/Library/",
|
|
149
|
+
{ requireTrailingSlashForAncestor: false },
|
|
150
|
+
),
|
|
151
|
+
).path,
|
|
152
|
+
).toEqual("./Library/");
|
|
153
|
+
expect(
|
|
154
|
+
mustNotBeNull(
|
|
155
|
+
new Path("/Users/lgarron").descendantRelativePath(
|
|
156
|
+
"/Users/lgarron/Downloads/test.png",
|
|
157
|
+
{ requireTrailingSlashForAncestor: false },
|
|
158
|
+
),
|
|
159
|
+
).path,
|
|
160
|
+
).toEqual("./Downloads/test.png");
|
|
161
|
+
expect(
|
|
162
|
+
new Path("/Users/lgarron/").descendantRelativePath("/etc/hosts/"),
|
|
163
|
+
).toBe(null);
|
|
164
|
+
expect(
|
|
165
|
+
new Path("/Users/lgarron/").descendantRelativePath("/Users/shared/"),
|
|
166
|
+
).toBe(null);
|
|
167
|
+
expect(
|
|
168
|
+
mustNotBeNull(
|
|
169
|
+
new Path("/Users/lgarron/").descendantRelativePath(
|
|
170
|
+
"/Users/shared/../lgarron",
|
|
171
|
+
),
|
|
172
|
+
).path,
|
|
173
|
+
).toBe(".");
|
|
174
|
+
});
|
|
175
|
+
|
|
98
176
|
test.concurrent(".isAbsolutePath()", async () => {
|
|
99
177
|
expect(new Path("/foo/bar").isAbsolutePath()).toBe(true);
|
|
100
178
|
expect(new Path("foo/bar").isAbsolutePath()).toBe(false);
|
|
@@ -132,6 +210,7 @@ test.concurrent(".toggleTrailingSlash(…)", async () => {
|
|
|
132
210
|
expect(new Path(".").toggleTrailingSlash().path).toBe("./");
|
|
133
211
|
expect(new Path("../").toggleTrailingSlash().path).toBe("..");
|
|
134
212
|
expect(new Path("..").toggleTrailingSlash().path).toBe("../");
|
|
213
|
+
expect(new Path("./foo").toggleTrailingSlash().path).toEqual("./foo/");
|
|
135
214
|
});
|
|
136
215
|
|
|
137
216
|
test.concurrent(".blue", async () => {
|
|
@@ -151,6 +230,7 @@ test.concurrent("normalize", async () => {
|
|
|
151
230
|
|
|
152
231
|
test.concurrent(".join(…)", async () => {
|
|
153
232
|
expect(new Path("foo").join("bar").path).toEqual("foo/bar");
|
|
233
|
+
expect(new Path("./foo").join("bar").path).toEqual("./foo/bar");
|
|
154
234
|
expect(new Path("foo/bar").join("bath", "kitchen/sink").path).toEqual(
|
|
155
235
|
"foo/bar/bath/kitchen/sink",
|
|
156
236
|
);
|
|
@@ -325,47 +405,49 @@ test.concurrent(".extname", async () => {
|
|
|
325
405
|
});
|
|
326
406
|
|
|
327
407
|
test.concurrent(".existsAsFile()", async () => {
|
|
328
|
-
|
|
329
|
-
expect(await
|
|
330
|
-
expect(await
|
|
331
|
-
expect(await
|
|
332
|
-
expect(await
|
|
333
|
-
expect(() =>
|
|
408
|
+
await using file = await Path.tempFilePath({ basename: "file.txt" });
|
|
409
|
+
expect(await file.exists()).toBe(false);
|
|
410
|
+
expect(await file.exists({ mustBe: "file" })).toBe(false);
|
|
411
|
+
expect(await file.exists({ mustBe: "directory" })).toBe(false);
|
|
412
|
+
expect(await file.existsAsFile()).toBe(false);
|
|
413
|
+
expect(() => file.join("./").existsAsFile()).toThrow(
|
|
334
414
|
"Path ends with a slash, which cannot be treated as a file.",
|
|
335
415
|
);
|
|
336
|
-
await
|
|
337
|
-
expect(await
|
|
338
|
-
expect(await
|
|
339
|
-
expect(() =>
|
|
416
|
+
await file.write("test");
|
|
417
|
+
expect(await file.exists()).toBe(true);
|
|
418
|
+
expect(await file.exists({ mustBe: "file" })).toBe(true);
|
|
419
|
+
expect(() => file.exists({ mustBe: "directory" })).toThrow(
|
|
340
420
|
/Path exists but is not a directory/,
|
|
341
421
|
);
|
|
342
|
-
expect(await
|
|
422
|
+
expect(await file.existsAsFile()).toBe(true);
|
|
343
423
|
});
|
|
344
424
|
|
|
345
425
|
test.concurrent(".existsAsDir()", async () => {
|
|
346
|
-
|
|
347
|
-
expect(await
|
|
348
|
-
expect(() =>
|
|
426
|
+
await using tempDir = await Path.makeTempDir();
|
|
427
|
+
expect(await tempDir.exists()).toBe(true);
|
|
428
|
+
expect(() => tempDir.exists({ mustBe: "file" })).toThrow(
|
|
349
429
|
/Path exists but is not a file/,
|
|
350
430
|
);
|
|
351
|
-
expect(await
|
|
352
|
-
expect(await
|
|
353
|
-
await
|
|
354
|
-
expect(await
|
|
355
|
-
expect(await
|
|
356
|
-
expect(await
|
|
357
|
-
expect(await
|
|
431
|
+
expect(await tempDir.exists({ mustBe: "directory" })).toBe(true);
|
|
432
|
+
expect(await tempDir.existsAsDir()).toBe(true);
|
|
433
|
+
await tempDir.rm_rf();
|
|
434
|
+
expect(await tempDir.exists()).toBe(false);
|
|
435
|
+
expect(await tempDir.exists({ mustBe: "file" })).toBe(false);
|
|
436
|
+
expect(await tempDir.exists({ mustBe: "directory" })).toBe(false);
|
|
437
|
+
expect(await tempDir.existsAsDir()).toBe(false);
|
|
358
438
|
});
|
|
359
439
|
|
|
360
440
|
test.concurrent(".mkdir(…) (un-nested)", async () => {
|
|
361
|
-
|
|
441
|
+
await using tempDir = await Path.makeTempDir();
|
|
442
|
+
const dir = tempDir.join("mkdir-test");
|
|
362
443
|
expect(await dir.exists()).toBe(false);
|
|
363
444
|
await dir.mkdir();
|
|
364
445
|
expect(await dir.exists()).toBe(true);
|
|
365
446
|
});
|
|
366
447
|
|
|
367
448
|
test.concurrent(".mkdir(…) (nested)", async () => {
|
|
368
|
-
|
|
449
|
+
await using tempDir = await Path.makeTempDir();
|
|
450
|
+
const dir = tempDir.join("mkdir-test/nested");
|
|
369
451
|
expect(await dir.exists()).toBe(false);
|
|
370
452
|
expect(() => dir.mkdir({ recursive: false })).toThrow("no such file");
|
|
371
453
|
await dir.mkdir();
|
|
@@ -373,7 +455,7 @@ test.concurrent(".mkdir(…) (nested)", async () => {
|
|
|
373
455
|
});
|
|
374
456
|
|
|
375
457
|
test.concurrent(".cp(…)", async () => {
|
|
376
|
-
|
|
458
|
+
await using parentDir = await Path.makeTempDir();
|
|
377
459
|
const file1 = parentDir.join("file1.txt");
|
|
378
460
|
const file2 = parentDir.join("file2.txt");
|
|
379
461
|
const file3 = parentDir.join("nonexistent/dirs/file3.txt");
|
|
@@ -398,7 +480,7 @@ test.concurrent(".cp(…)", async () => {
|
|
|
398
480
|
});
|
|
399
481
|
|
|
400
482
|
test.concurrent(".rename(…)", async () => {
|
|
401
|
-
|
|
483
|
+
await using parentDir = await Path.makeTempDir();
|
|
402
484
|
const file1 = parentDir.join("file1.txt");
|
|
403
485
|
const file2 = parentDir.join("file2.txt");
|
|
404
486
|
const file3 = parentDir.join("nonexistent/dirs/file3.txt");
|
|
@@ -444,41 +526,41 @@ test.concurrent(".makeTempDir(…)", async () => {
|
|
|
444
526
|
});
|
|
445
527
|
|
|
446
528
|
test.concurrent(".rm(…) (file)", async () => {
|
|
447
|
-
|
|
448
|
-
await
|
|
449
|
-
expect(await
|
|
450
|
-
await
|
|
451
|
-
expect(await
|
|
452
|
-
expect(await
|
|
453
|
-
expect(async () =>
|
|
529
|
+
await using filePath = await Path.tempFilePath({ basename: "file.txt" });
|
|
530
|
+
await filePath.write("");
|
|
531
|
+
expect(await filePath.existsAsFile()).toBe(true);
|
|
532
|
+
await filePath.rm();
|
|
533
|
+
expect(await filePath.existsAsFile()).toBe(false);
|
|
534
|
+
expect(await filePath.parent.existsAsDir()).toBe(true);
|
|
535
|
+
expect(async () => filePath.rm()).toThrow(/^ENOENT/);
|
|
454
536
|
});
|
|
455
537
|
|
|
456
538
|
test.concurrent(".rm(…) (folder)", async () => {
|
|
457
|
-
|
|
539
|
+
await using tempDir = await Path.makeTempDir();
|
|
458
540
|
const file = tempDir.join("file.txt");
|
|
459
541
|
await file.write("");
|
|
460
542
|
expect(await tempDir.existsAsDir()).toBe(true);
|
|
461
|
-
expect(async () => tempDir.rm()).
|
|
543
|
+
expect(async () => tempDir.rm()).toThrow(/^(EACCES|EFAULT)/);
|
|
462
544
|
await file.rm();
|
|
463
545
|
await tempDir.rm({ recursive: true });
|
|
464
546
|
expect(await tempDir.existsAsDir()).toBe(false);
|
|
465
|
-
expect(async () => tempDir.rm()).
|
|
547
|
+
expect(async () => tempDir.rm()).toThrow(/^ENOENT/);
|
|
466
548
|
});
|
|
467
549
|
|
|
468
550
|
test.concurrent(".rmDir(…)", async () => {
|
|
469
|
-
|
|
551
|
+
await using tempDir = await Path.makeTempDir();
|
|
470
552
|
const file = tempDir.join("file.txt");
|
|
471
553
|
await file.write("");
|
|
472
554
|
expect(await tempDir.existsAsDir()).toBe(true);
|
|
473
|
-
expect(async () => tempDir.rmDir()).
|
|
555
|
+
expect(async () => tempDir.rmDir()).toThrow(/^ENOTEMPTY/);
|
|
474
556
|
await file.rm();
|
|
475
557
|
await tempDir.rmDir();
|
|
476
558
|
expect(await tempDir.existsAsDir()).toBe(false);
|
|
477
|
-
expect(async () => tempDir.rmDir()).
|
|
559
|
+
expect(async () => tempDir.rmDir()).toThrow(/^ENOENT/);
|
|
478
560
|
});
|
|
479
561
|
|
|
480
562
|
test.concurrent(".rm_rf(…) (file)", async () => {
|
|
481
|
-
|
|
563
|
+
await using file = await Path.tempFilePath({ basename: "file.txt" });
|
|
482
564
|
await file.write("");
|
|
483
565
|
expect(await file.existsAsFile()).toBe(true);
|
|
484
566
|
await file.rm_rf();
|
|
@@ -489,7 +571,7 @@ test.concurrent(".rm_rf(…) (file)", async () => {
|
|
|
489
571
|
});
|
|
490
572
|
|
|
491
573
|
test.concurrent(".rm_rf(…) (folder)", async () => {
|
|
492
|
-
|
|
574
|
+
await using tempDir = await Path.makeTempDir();
|
|
493
575
|
await tempDir.join("file.txt").write("");
|
|
494
576
|
expect(tempDir.path).toContain("/js-temp-");
|
|
495
577
|
expect(await tempDir.exists()).toBe(true);
|
|
@@ -500,7 +582,7 @@ test.concurrent(".rm_rf(…) (folder)", async () => {
|
|
|
500
582
|
});
|
|
501
583
|
|
|
502
584
|
test.concurrent(".readText()", async () => {
|
|
503
|
-
|
|
585
|
+
await using file = await Path.tempFilePath({ basename: "file.txt" });
|
|
504
586
|
await file.write("hi");
|
|
505
587
|
await file.write("bye");
|
|
506
588
|
|
|
@@ -509,14 +591,14 @@ test.concurrent(".readText()", async () => {
|
|
|
509
591
|
});
|
|
510
592
|
|
|
511
593
|
test.concurrent(".readLines()", async () => {
|
|
512
|
-
|
|
594
|
+
await using file = await Path.tempFilePath({ basename: "file.txt" });
|
|
513
595
|
await file.write("hi\nbye\n");
|
|
514
596
|
|
|
515
597
|
expect(await Array.fromAsync(file.readLines())).toEqual(["hi", "bye"]);
|
|
516
598
|
});
|
|
517
599
|
|
|
518
600
|
test.concurrent(".readJSON()", async () => {
|
|
519
|
-
|
|
601
|
+
await using file = await Path.tempFilePath({ basename: "file.json" });
|
|
520
602
|
await file.write(JSON.stringify({ foo: "bar" }));
|
|
521
603
|
|
|
522
604
|
expect(await file.readJSON()).toEqual<Record<string, string>>({ foo: "bar" });
|
|
@@ -527,7 +609,7 @@ test.concurrent(".readJSON()", async () => {
|
|
|
527
609
|
});
|
|
528
610
|
|
|
529
611
|
test.concurrent(".readJSON(…) with fallback", async () => {
|
|
530
|
-
|
|
612
|
+
await using tempDir = await Path.makeTempDir();
|
|
531
613
|
const file = tempDir.join("file.json");
|
|
532
614
|
const json: { foo?: number } = await file.readJSON({ fallback: { foo: 4 } });
|
|
533
615
|
expect(json).toEqual({ foo: 4 });
|
|
@@ -539,13 +621,11 @@ test.concurrent(".readJSON(…) with fallback", async () => {
|
|
|
539
621
|
});
|
|
540
622
|
expect(json2).toEqual({ foo: 6 });
|
|
541
623
|
|
|
542
|
-
expect(() => tempDir.readJSON({ fallback: { foo: 4 } })).
|
|
543
|
-
/^EISDIR/,
|
|
544
|
-
);
|
|
624
|
+
expect(() => tempDir.readJSON({ fallback: { foo: 4 } })).toThrow(/^EISDIR/);
|
|
545
625
|
});
|
|
546
626
|
|
|
547
627
|
test.concurrent(".write(…)", async () => {
|
|
548
|
-
|
|
628
|
+
await using tempDir = await Path.makeTempDir();
|
|
549
629
|
const file = tempDir.join("file.json");
|
|
550
630
|
expect(await file.write("foo")).toBe(file);
|
|
551
631
|
|
|
@@ -561,14 +641,14 @@ test.concurrent(".write(…)", async () => {
|
|
|
561
641
|
});
|
|
562
642
|
|
|
563
643
|
test.concurrent(".writeJSON(…)", async () => {
|
|
564
|
-
|
|
644
|
+
await using file = await Path.tempFilePath({ basename: "file.json" });
|
|
565
645
|
expect(await file.writeJSON({ foo: "bar" })).toBe(file);
|
|
566
646
|
|
|
567
647
|
expect(await file.readJSON()).toEqual<Record<string, string>>({ foo: "bar" });
|
|
568
648
|
});
|
|
569
649
|
|
|
570
650
|
test.concurrent(".appendFile(…)", async () => {
|
|
571
|
-
|
|
651
|
+
await using file = await Path.tempFilePath({ basename: "file.txt" });
|
|
572
652
|
await file.appendFile("test\n");
|
|
573
653
|
expect(await file.readText()).toEqual("test\n");
|
|
574
654
|
await file.appendFile("more\n");
|
|
@@ -576,7 +656,7 @@ test.concurrent(".appendFile(…)", async () => {
|
|
|
576
656
|
});
|
|
577
657
|
|
|
578
658
|
test.concurrent(".readDir(…)", async () => {
|
|
579
|
-
|
|
659
|
+
await using dir = await Path.makeTempDir();
|
|
580
660
|
await dir.join("file.txt").write("hello");
|
|
581
661
|
await dir.join("dir/file.json").write("hello");
|
|
582
662
|
|
|
@@ -590,19 +670,19 @@ test.concurrent(".readDir(…)", async () => {
|
|
|
590
670
|
});
|
|
591
671
|
|
|
592
672
|
test.concurrent(".symlink(…)", async () => {
|
|
593
|
-
|
|
673
|
+
await using tempDir = await Path.makeTempDir();
|
|
594
674
|
const source = tempDir.join("foo.txt");
|
|
595
675
|
const target = tempDir.join("bar.txt");
|
|
596
676
|
await source.symlink(target);
|
|
597
677
|
expect(await target.existsAsFile()).toBe(false);
|
|
598
|
-
expect(() => target.readText()).toThrow(
|
|
678
|
+
expect(() => target.readText()).toThrow(/^ENOENT/);
|
|
599
679
|
await source.write("hello");
|
|
600
680
|
expect(await target.existsAsFile()).toBe(true);
|
|
601
681
|
expect(await target.readText()).toEqual("hello");
|
|
602
682
|
});
|
|
603
683
|
|
|
604
684
|
test.concurrent(".realpath(…)", async () => {
|
|
605
|
-
|
|
685
|
+
await using tempDir = await Path.makeTempDir();
|
|
606
686
|
const source = tempDir.join("foo.txt");
|
|
607
687
|
await source.write("hello world!");
|
|
608
688
|
const target = tempDir.join("bar.txt");
|
|
@@ -613,7 +693,7 @@ test.concurrent(".realpath(…)", async () => {
|
|
|
613
693
|
});
|
|
614
694
|
|
|
615
695
|
test.concurrent(".stat(…)", async () => {
|
|
616
|
-
|
|
696
|
+
await using file = await Path.tempFilePath({ basename: "foo.txt" });
|
|
617
697
|
await file.write("hello");
|
|
618
698
|
|
|
619
699
|
expect((await file.stat()).size).toEqual(5);
|
|
@@ -622,7 +702,7 @@ test.concurrent(".stat(…)", async () => {
|
|
|
622
702
|
});
|
|
623
703
|
|
|
624
704
|
test.concurrent(".lstat(…)", async () => {
|
|
625
|
-
|
|
705
|
+
await using tempDir = await Path.makeTempDir();
|
|
626
706
|
const source = tempDir.join("foo.txt");
|
|
627
707
|
const target = tempDir.join("bar.txt");
|
|
628
708
|
await source.symlink(target);
|
|
@@ -635,45 +715,37 @@ test.concurrent(".lstat(…)", async () => {
|
|
|
635
715
|
});
|
|
636
716
|
|
|
637
717
|
test.concurrent(".chmod(…)", async () => {
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
718
|
+
await using binPath = await Path.tempFilePath({
|
|
719
|
+
basename: "bin.bash",
|
|
720
|
+
});
|
|
721
|
+
expect(() => execSync(binPath.path)).toThrow(
|
|
722
|
+
/No such file or directory|not found/,
|
|
641
723
|
);
|
|
642
|
-
await binPath.write(`#!/usr/bin/env
|
|
724
|
+
await binPath.write(`#!/usr/bin/env -S bun run --
|
|
643
725
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
// await binPath.write(`#!/usr/bin/env -S bun run --
|
|
647
|
-
|
|
648
|
-
// console.log("hi");`);
|
|
649
|
-
expect(() => new PrintableShellCommand(binPath, []).text()).toThrow(
|
|
650
|
-
/EACCES|Premature close/,
|
|
651
|
-
);
|
|
726
|
+
console.log("hi");`);
|
|
727
|
+
expect(() => execSync(binPath.path)).toThrow(/Permission denied/);
|
|
652
728
|
await binPath.chmod(0o755);
|
|
653
|
-
expect(
|
|
729
|
+
expect(execSync(binPath.path, { encoding: "utf-8" })).toEqual("hi\n");
|
|
654
730
|
});
|
|
655
731
|
|
|
656
732
|
test.concurrent(".chmodX(…)", async () => {
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
733
|
+
await using binPath = await Path.tempFilePath({
|
|
734
|
+
basename: "bin.bash",
|
|
735
|
+
});
|
|
736
|
+
expect(() => execSync(binPath.path)).toThrow(
|
|
737
|
+
/No such file or directory|not found/,
|
|
660
738
|
);
|
|
661
|
-
await binPath.write(`#!/usr/bin/env
|
|
739
|
+
await binPath.write(`#!/usr/bin/env -S bun run --
|
|
662
740
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
// await binPath.write(`#!/usr/bin/env -S bun run --
|
|
666
|
-
|
|
667
|
-
// console.log("hi");`);
|
|
668
|
-
expect(() => new PrintableShellCommand(binPath, []).text()).toThrow(
|
|
669
|
-
/EACCES|Premature close/,
|
|
670
|
-
);
|
|
741
|
+
console.log("hi");`);
|
|
742
|
+
expect(() => execSync(binPath.path)).toThrow(/Permission denied/);
|
|
671
743
|
expect((await binPath.stat()).mode & constants.S_IWUSR).toBeTruthy();
|
|
672
744
|
await binPath.chmod(0o444);
|
|
673
745
|
expect((await binPath.stat()).mode & constants.S_IWUSR).toBeFalsy();
|
|
674
746
|
expect((await binPath.stat()).mode & constants.S_IXUSR).toBeFalsy();
|
|
675
747
|
await binPath.chmodX();
|
|
676
|
-
expect(
|
|
748
|
+
expect(execSync(binPath.path, { encoding: "utf-8" })).toEqual("hi\n");
|
|
677
749
|
expect((await binPath.stat()).mode & constants.S_IWUSR).toBeFalsy();
|
|
678
750
|
expect((await binPath.stat()).mode & constants.S_IXUSR).toBeTruthy();
|
|
679
751
|
});
|
|
@@ -682,11 +754,14 @@ test.concurrent(".homedir", async () => {
|
|
|
682
754
|
expect(Path.homedir.path).toEqual("/mock/home/dir");
|
|
683
755
|
});
|
|
684
756
|
|
|
685
|
-
|
|
757
|
+
// This is serial because it can break binary execution tests elsewhere.
|
|
758
|
+
test.serial(".cwd", async () => {
|
|
759
|
+
const originalCwd = Path.cwd;
|
|
686
760
|
expect(Path.cwd.basename.path).toEqual("path-class");
|
|
687
|
-
|
|
761
|
+
await using tempDir = await Path.makeTempDir();
|
|
688
762
|
chdir(tempDir.path);
|
|
689
763
|
expect(await realpath(Path.cwd.path)).toEqual(await realpath(tempDir.path));
|
|
764
|
+
chdir(originalCwd.path);
|
|
690
765
|
});
|
|
691
766
|
|
|
692
767
|
test.concurrent(".xdg", async () => {
|
|
@@ -700,20 +775,12 @@ test.concurrent(".xdg", async () => {
|
|
|
700
775
|
);
|
|
701
776
|
});
|
|
702
777
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
test(".debugPrint(…)", async () => {
|
|
706
|
-
spy.mockReset();
|
|
778
|
+
test.concurrent(".debugPrint(…)", async () => {
|
|
779
|
+
const spy = spyOn(console, "log");
|
|
707
780
|
Path.homedir.debugPrint("Here is a test log of the mock home directory:");
|
|
708
781
|
expect(spy.mock.calls).toEqual([
|
|
709
782
|
["Here is a test log of the mock home directory:"],
|
|
710
783
|
["/mock/home/dir"],
|
|
711
784
|
]);
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
test(".stringifyIfPath(…)", async () => {
|
|
715
|
-
spy.mockReset();
|
|
716
|
-
expect(stringifyIfPath(Path.homedir)).toBe("/mock/home/dir");
|
|
717
|
-
expect(stringifyIfPath("/mock/home/dir")).toBe("/mock/home/dir");
|
|
718
|
-
expect(stringifyIfPath(4)).toBe(4);
|
|
785
|
+
jest.restoreAllMocks();
|
|
719
786
|
});
|