UNPKG

@thingts/path

Version:

Type-safe, ergonomic package for working with paths in any javascript environment

376 lines (370 loc) 14 kB
declare abstract class FilenameBase { /** @private */ /** @hidden Implemented by subclasses to hold the normalized path string. */ protected abstract filename_: string; /** @hidden Implemented by subclasses to create a new instance with the given filename. */ protected abstract withFilename(filename: string): this; /** Returns the filename as a string. */ abstract toString(): string; /** Returns true if this filename equals the other filename or string. */ abstract equals(other: string | FilenameBase): boolean; /** * Returns the extension of the filename including the leading dot, as a * string. If the filename has no extension, returns an empty string. * * Note that if the filename starts with a dot (e.g. `.gitignore`), * that dot is considered part of the stem. So `.gitignore` has * extension `''` and `.gitignore.bak` has extension `.bak` */ get extension(): string; /** * Returns the stem of the filename, i.e. the part before the extension. * If the filename has no extension, returns the entire filename * * Note that if the filename starts with a dot (e.g. `.gitignore`), * that dot is considered part of the stem. So `.gitignore` and * `.gitignore.bak` both have stem `.gitignore` */ get stem(): string; /** * Replace the filename stem, keeping the extension the same * * @returns A new {@link Filename} instance * * @example * ```ts * new Filename('index.ts').replaceStem('main') // 'main.ts' (Filename) * ``` */ replaceStem(newStem: string): this; /** * Replace the filename extensions, keeping the stem the same * * @returns A new {@link Filename} instance * * @example * ```ts * new Filename('index.ts').replaceExtension('.js') // 'index.js' (Filename) * ``` */ replaceExtension(newExt: string): this; } /** * Represents a filename, without any path components, an provides methods * to query and manipulate it. * * The class is immutable; all methods that modify the filename return a * new instance. * * The constructor ensures that the filename does not contain any directory * separators. If it does, an error is thrown. * * @example * ```ts * const file = new Filename('example.txt') * console.log(file.stem) // 'example' * ``` */ declare class Filename extends FilenameBase { protected filename_: string; /** * Create a {@link Filename} instance from a string or another {@link Filename} * * Throws an error if the provided name contains path separators * * @example * ```ts * new Filename('index.ts') // OK * new Filename('demo/index.ts') // Throws Error * ``` */ constructor(filename: string | Filename); /** * Creates a new {@link Filename} by calling a callback function with the * old filename as a string, and using the returned string as the new * filename * * @returns A new {@link Filename} instance */ transform(fn: (filename: string) => string): this; /** * Returns true if the provided string is a valid filename (i.e. does not * contain any path separators) */ static isFilenameString(filename: string): boolean; toString(): string; equals(other: string | Filename): boolean; protected withFilename(filename: string): this; } declare abstract class PathBase extends FilenameBase { /** @hidden Implemented by subclasses to hold the normalized path string. */ protected abstract path_: string; /** * Protected factory to construct a new instance of the current class, with * the given path. * * Used by all mutation-like methods to return a new instance of the same * class, allowing derived classes that inherit those methods to return new * instances of themselves without needing to override them. * * The default implementation assumes the derived class's constructor takes * a single string argument (the path). Derived classes with different * constructor siguatures should override {@link newSelf}. */ protected newSelf(path: string | FilenameBase): this; /** * The filename component (last path segment) as a {@link Filename}. * * @example * ```ts * new AbsolutePath('/a/b/c.txt').filename // 'c.txt' (Filename) * new RelativePath('a/b/c.txt').filename // 'c.txt' (Filename) * ``` */ get filename(): Filename; /** * The parent directory of this path. * * @returns A new path instance pointing to the parent. * @example * ```ts * new AbsolutePath('/a/b/c.txt').parent // '/a/b' (AbsolutePath) * new RelativePath('a/b/c.txt').parent // 'a/b' (RelativePath) * ``` */ get parent(): this; /** * Join additional path segments to this path. * * Accepts strings or path objects; `null` and `undefined` are ignored. * The resulting path is normalized. * * @returns A new path instance with the segments appended * * @example * ```ts * const a1 = new AbsolutePath('/project/demo') * const a2 = a1.join('demo1/src', 'index.js') // '/project/demo/demo1/src/index.js' * a2 instanceof AbsolutePath // true * * const r1 = new RelativePath('demo') * const r2 = r1.join('demo1/src', 'index.js') // 'demo/demo1/src/index.js' * r2 instanceof RelativePath // true * ``` */ join(...segments: readonly (string | null | undefined | FilenameBase)[]): this; /** * Replace the filename (last segment). * * @returns A new path with the filename replaced. */ replaceFilename(newFilename: string | Filename): this; /** * Replace the filename stem, keeping the extension the same * * @param newStem - New stem to use (extension is preserved). * @returns A new path with the stem replaced. * @example * ```ts * new AbsolutePath('/a/b/c.txt').replaceStem('d') // '/a/b/d.txt' (AbsolutePath) * new RelativePath('a/b/c.txt').replaceStem('d') // 'a/b/d.txt' (RelativePath) * ``` */ replaceStem(newStem: string): this; /** * Replace the filename extension, keeping the stem the same. The passed * can include or omit the leading dot; if omitted, it will be added. * * @param newExt - New extension, e.g. `json` or `.json` * @returns A new path with the extension replaced. * @example * ```ts * new AbsolutePath('/a/b/c.txt').replaceExtension('json') // '/a/b/c.json' (AbsolutePath) * new RelativePath('a/b/c.txt').replaceExtension('.json') // '/a/b/c.json' (RelativePath) * ``` */ replaceExtension(newExt: string): this; /** * Transform the filename via a callback. * * @param fn - Receives the current {@link Filename}, returns a new filename * (string or {@link Filename}). * @returns A new path with the transformed filename. * @example * ```ts * p.transformFilename(f => f.replaceStem(f.stem + '.bak')) * ``` */ transformFilename(fn: (filename: Filename) => string | Filename): this; /** * Replace the parent directory while keeping the current filename. * * @param newParent - Parent directory as string or another `PathBase`. * @returns A new path rooted at `newParent` with the same filename. * @example * ```ts * new AbsolutePath('/old/file.txt').replaceParent('/new/dir') // '/new/dir/file.txt' (AbsolutePath) * new RelativePath('old/file.txt').replaceParent('new/dir') // 'new/dir/file.txt' (RelativePath) * ``` */ replaceParent(newParent: string | PathBase): this; /** Returns the path as string. */ toString(): string; /** Returns true if this path equals the other path or string */ equals(other: string | PathBase): boolean; protected get filename_(): string; protected withFilename(filename: string | Filename): this; } /** * Represents an relative filesystem path (i.e. a path that doesn't start at * the root, i.e. doesn't have a leading separator) and is not, and provides * methods for path manipulation and queries. * * {@link RelativePath} instances are normalized and immutable. * * {@link RelativePath} is similar to {@link AbsolutePath} but * lacks methods that are only valid for absolute paths. * * @example * ```ts * const p1 = new Relative('demos') * const p2 = p1.join('demo1/src', 'index.ts') // 'demos/demo1/src/index.ts' * ``` * */ declare class RelativePath extends PathBase { protected path_: string; /** * Create a new {@link RelativePath} from a string or another {@link RelativePath}. * * The path is normalized and guaranteed to be relative. Any trailing * separator is removed. * * Throws an error if the provided path is absolute * * @example * ```ts * new AbsolutePath('project/demos') // OK * new AbsolutePath('/project/demos') // Throws Error * new AbsolutePath('project//src/../demos/') // normalized => project/demos * ``` */ constructor(relpath: string | RelativePath); /** * Checks whether a string is a relative path. (I.e., if it would be * acceptable to the {@link RelativePath} constructor.) * * @param filepath - The string to check. * @returns True if the string is an absolute path, otherwise false. */ static isRelativePathString(filepath: string): boolean; } /** * Represents an absolute filesystem path (i.e. a path starting at the root, i.e. * has a leading separator), and provides methods for path resolution, * manipulation and queries. * * {@link AbsolutePath} instances are normalized and immutable. * * {@link AbsolutePath} has the same functionality as {@link RelativePath} but * with additional methods that are only valid for absolute paths: {@link * resolve}, {@link relativeTo}, and {@link descendsFrom}. * * Note that {@link AbsolutePath} provides pure path manipulation, it does * not access the filesystem in any way. (If you want to work with the * filesystem, you can use the * [`@thingts/fs-path`](https://npm.com/package/@thingts/fs-path) library which * extends {@link AbsolutePath} with filestem operations.) * * @example * ```ts * const p1 = new AbsolutePath('/project/demos') * const p2 = p1.join('demo1/src', 'index.ts') // '/project/demos/demo1/src/index.ts' * console.log(p2.descendsFrom(p1)) // true * console.log(p2.relativeTo(p1)) // 'demo1/src/index.ts' (RelativePath) * ``` * */ declare class AbsolutePath extends PathBase { #private; protected readonly path_: string; /** * Create a new {@link AbsolutePath} from a string or another {@link AbsolutePath}. * * The path is normalized and guaranteed to be absolute. Any trailing * separator is removed. * * Throws an error if the provided path is not absolute. * * @example * ```ts * new AbsolutePath('/project/demos') // OK * new AbsolutePath('project/demos') // Throws Error * new AbsolutePath('/project//src/../demos/') // normalized => /project/demos * ``` */ constructor(path: string | AbsolutePath); /** * Resolve additional path segments against this absolute path. * * Accepts strings, {@link Filename}, {@link RelativePath}, or {@link AbsolutePath} objects. * Null and undefined segments are ignored. * * Similar to join, except that if any segment is an {@link AbsolutePath} or string * starting with a path separator, the current path is discarded and * resolution starts from that segment. * * @returns A new {@link AbsolutePath} with the resolved path. * * @example * ```ts * const p1 = new AbsolutePath('/project/demos') * const p2 = p1.resolve('demo1/src', 'index.ts') // '/project/demos/demo1/src/index.ts' * const p3 = p1.resolve('/etc/config') // '/etc/config' (resets to absolute path) * ``` */ resolve(...segments: readonly (string | Filename | RelativePath | AbsolutePath | null | undefined)[]): this; /** * Compute the relative path from the given base path to this path. * * @param base - The base absolute path. * @returns A {@link RelativePath} that goes from `base` to `this`. * * @example * ```ts * const p1 = new AbsolutePath('/project/demo') * const p2 = new AbsolutePath('/project/demo/src/index.ts') * const rel = p2.relativeTo(p1) // 'src/index.ts' (RelativePath) * p1.join(rel).equals(p2) // true * ``` */ relativeTo(base: AbsolutePath): RelativePath; /** * Test whether this path is a descendant of the given ancestor path. * * @param ancestor - An `AbsolutePath` or string to check against. * @param opts.includeSelf - If true, return true when the paths are identical. * @returns True if this path descends from the ancestor, otherwise false. * * @example * ```ts * const p1 = new AbsolutePath('/project/demo') * const p2 = new AbsolutePath('/project/demo/src/index.ts') * console.log(p2.descendsFrom(p1)) // true * console.log(p1.descendsFrom(p1)) // false * console.log(p1.descendsFrom(p1, { includeSelf: true })) // true * ``` */ descendsFrom(ancestor: AbsolutePath | string, opts?: { includeSelf?: boolean; }): boolean; /** * Checks whether a string is an absolute path. (I.e., if it would be * acceptable to the {@link AbsolutePath} constructor.) * * @param filepath - The string to check. * @returns True if the string is an absolute path, otherwise false. */ static isAbsolutePathString(filepath: string): boolean; } export { AbsolutePath, Filename, RelativePath };