nice-path 0.1.1 → 2.0.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/.node-version ADDED
@@ -0,0 +1 @@
1
+ v18.18.2
package/LICENSE CHANGED
@@ -1,5 +1,5 @@
1
1
  The MIT License (MIT)
2
- Copyright (c) 2019 Lily Scott
2
+ Copyright (c) 2019-2024 Lily Skye
3
3
 
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
package/README.md CHANGED
@@ -1,47 +1,313 @@
1
1
  # nice-path
2
2
 
3
- `nice-path` is a replacement for the [Node.js](https://nodejs.org/) `path` builtin module.
3
+ `nice-path` provides a class that represents a filesystem path (POSIX-style or Win32-style), which has various nice methods on it that make it easy to work with. It can be used as a replacement for the Node.js `path` builtin module, where you pass around Path objects and stringify them before use, rather than passing around strings.
4
4
 
5
- ## The Problem
5
+ ## Example
6
6
 
7
- When using `path` in Node, paths are represented as strings. Because of this, it's easy to mix up absolute paths and relative paths, and when using Flow or TypeScript, it's hard to define and enforce whether a function you're writing needs absolute or relative paths. These problems mean there are a lot of things you need to remember to handle when working with paths.
7
+ ```ts
8
+ import { Path } from "nice-path";
8
9
 
9
- Additionally, Node's `path` module cannot handle paths for OSes other than the one where it is running; if you use `path.join` on a Windows-style path on a Unix system, then it behaves incorrectly. This can be problematic when writing cross-platform applications, or when paths from one computer are used on another.
10
+ const here = new Path(__dirname);
11
+ console.log(here);
12
+ // Path {
13
+ // segments: ["", "home", "suchipi", "Code", "nice-path"],
14
+ // separator: "/"
15
+ // }
10
16
 
11
- ## This Solution
17
+ here.toString(); // "/home/suchipi/Code/nice-path"
18
+ here.isAbsolute(); // true
12
19
 
13
- This package treats paths as first-class objects, and distinguishes between absolute paths, relative paths, and "unqualified" paths (relative paths without any leading `./`). It can handle paths from Windows on Unix systems and vice versa (including UNC paths).
20
+ const readme = here.concat("README.md");
21
+ console.log(readme);
22
+ // Path {
23
+ // segments: [
24
+ // "",
25
+ // "home",
26
+ // "suchipi",
27
+ // "Code",
28
+ // "nice-path",
29
+ // "README.md"
30
+ // ],
31
+ // separator: "/"
32
+ // }
33
+ readme.basename(); // "README.md"
34
+ readme.extname(); // ".md"
35
+ readme.dirname(); // Path object with same contents as 'here'
14
36
 
15
- ## Usage Example
37
+ // normalize resolves . and .. components
38
+ const homeDir = here.concat("../../.").normalize();
39
+ console.log(homeDir);
40
+ // Path {
41
+ // segments: [
42
+ // "",
43
+ // "home",
44
+ // "suchipi"
45
+ // ],
46
+ // separator: "/"
47
+ // }
16
48
 
17
- ```ts
18
- import { Path, AbsolutePath } from "nice-path";
19
- import fs from "fs";
49
+ here.relativeTo(homeDir).toString(); // "./Code/nice-path"
50
+ readme.relativeTo(homeDir).toString(); // "./Code/nice-path/README.txt"
51
+
52
+ // There are also several other methods which aren't in this example.
53
+ ```
54
+
55
+ ## API Documentation
56
+
57
+ ### Overview
58
+
59
+ This package has one named export: "Path", which is a class.
20
60
 
21
- const absolutePath = Path.fromAbsolutePathString("/tmp/some-folder/");
22
- absolutePath.raw; // "/tmp/some-folder/"
61
+ The "Path" class has the following instance properties:
23
62
 
24
- absolutePath.hasTrailingSlash(); // true
63
+ - segments (Array of string)
64
+ - separator (string)
25
65
 
26
- const newPath = absolutePath.removeTrailingSlash(); // AbsolutePath
66
+ and the following instance methods:
27
67
 
28
- const tmpDir = absolutePath.parentDirectory(); // AbsolutePath of "/tmp"
68
+ - normalize
69
+ - concat
70
+ - isAbsolute
71
+ - clone
72
+ - relativeTo
73
+ - toString
74
+ - basename
75
+ - extname
76
+ - dirname
77
+ - startsWith
78
+ - endsWith
79
+ - indexOf
80
+ - includes
81
+ - replace
82
+ - replaceAll
83
+ - replaceLast
29
84
 
30
- const someOtherFolder = tmpDir.append("some", "other-folder"); // AbsolutePath of "/tmp/some/other-folder"
85
+ and the following static methods:
31
86
 
32
- function onlyWorksWithAbsolutePaths(path: AbsolutePath) {
33
- fs.writeFileSync(path.raw, "hi");
87
+ - splitToSegments
88
+ - detectSeparator
89
+ - normalize
90
+ - isAbsolute
91
+ - fromRaw
92
+
93
+ ### Details
94
+
95
+ #### Path (class)
96
+
97
+ An object that represents a filesystem path. It has the following constructor signature:
98
+
99
+ ```ts
100
+ class Path {
101
+ constructor(...inputs: Array<string | Path | Array<string | Path>>);
34
102
  }
103
+ ```
104
+
105
+ You can pass in zero or more arguments to the constructor, where each argument can be either a string, a Path object, or an array of strings and Path objects.
106
+
107
+ When multiple strings/Paths are provided to the constructor, they will be concatenated together in order, left-to-right.
108
+
109
+ The resulting object has two properties: `segments`, which is an array of strings containing all the non-slash portions of the Path, and `separator`, which is the slash string that those portions would have between them if this path were represented as a string.
110
+
111
+ #### `segments: Array<string>` (instance property of Path)
112
+
113
+ Each `Path` object has a `segments` property, which is an array of strings containing all the non-slash portions of the Path.
114
+
115
+ For example, given a path object `myPath = new Path("a/b/c/d")`, `myPath.segments` is an array of strings `["a", "b", "c", "d"]`.
116
+
117
+ You may freely mutate this array in order to add or remove segments, but I recommend you instead use instance methods on the Path object, which take a "separator-aware" approach to looking at path segments.
118
+
119
+ POSIX-style absolute paths start with a leading slash character, like "/a/b/c". A Path object representing that path, (ie `new Path("/a/b/c")`) would have the following path segments:
120
+
121
+ ```json
122
+ ["", "a", "b", "c"]
123
+ ```
124
+
125
+ That empty string in the first position represents the "left side" of the first slash. When you use `.toString()` on that Path object, it will become "/a/b/c" again, as expected.
126
+
127
+ Windows [UNC paths](https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#unc-paths) have two empty strings at the beginning of the Array.
128
+
129
+ #### `separator: string` (instance property of Path)
130
+
131
+ Each `Path` object has a `separator` property, which is the slash string that those portions would have between them if this path were represented as a string. It's always either `/` (forward slash) or `\` (backward slash). If you change this property, the result of calling `.toString()` on the Path object will change:
132
+
133
+ ```ts
134
+ // The initial value of separator is inferred from the input path:
135
+ const myPath = new Path("hi/there/friend");
136
+ console.log(myPath.separator); // prints /
137
+
138
+ // Using toString() joins the path segments using the separator:
139
+ console.log(myPath.toString()); // prints hi/there/friend
140
+
141
+ // If you change the separator... (note: this is a single backslash character. It has to be "escaped" with another one, which is why there are two.)
142
+ myPath.separator = "\\";
143
+
144
+ // Then toString() uses the new separator instead:
145
+ console.log(myPath.toString()); // prints hi\there\friend
146
+ ```
147
+
148
+ The initial value of the `separator` property is inferred by searching the input string(s) the Path was constructed with for a slash character and using the first one found. If none is found, a forward slash (`/`) is used.
149
+
150
+ #### `normalize(): Path` (instance method of Path)
151
+
152
+ The `normalize` method returns a new Path with all non-leading `.` and `..` segments resolved.
153
+
154
+ ```ts
155
+ const myPath = new Path("/some/where/../why/over/./here");
156
+ const resolved = myPath.normalize();
157
+ console.log(resolved.toString()); // /some/why/over/here
158
+ ```
159
+
160
+ If you want to evaluate a relative path (a path with leading `.` or `..` segments) using a base directory, you can concatenate and then normalize them:
161
+
162
+ ```ts
163
+ const baseDir = new Path("/home/me");
164
+ const relativeDir = new Path("./somewhere/something/../blue");
165
+ const resolved = baseDir.concat(relativeDir).normalize();
166
+ console.log(resolved.toString()); // /home/me/something/blue
167
+ ```
168
+
169
+ #### `concat(...others): Path` (instance method of Path)
170
+
171
+ The `concat` method creates a new Path by appending additional path segments onto the end of the target Path's segments. The additional path segments are passed to the concat method as either strings, Paths, or Arrays of strings and Paths.
172
+
173
+ The new Path will use the separator from the target Path.
174
+
175
+ ```ts
176
+ const pathOne = new Path("a/one");
177
+ const withStuffAdded = pathOne.concat(
178
+ "two",
179
+ "three/four",
180
+ new Path("yeah\\yes"),
181
+ );
182
+
183
+ console.log(withStuffAdded.toString());
184
+ // "a/one/two/three/four/yeah/yes"
185
+ ```
186
+
187
+ #### `isAbsolute(): boolean` (instance method of Path)
188
+
189
+ The `isAbsolute` method returns whether the target Path is an absolute path; that is, whether it starts with either `/`, `\`, or a drive letter (ie `C:`).
190
+
191
+ #### `clone(): Path` (instance method of Path)
192
+
193
+ The `clone` method makes a second Path object containing the same segments and separator as the target.
35
194
 
36
- const relativePath = Path.fromRelativePathString("./data");
195
+ #### `relativeTo(dir, options?): Path` (instance method of Path)
37
196
 
38
- onlyWorksWithAbsolutePaths(relativePath); // TypeScript error
197
+ The `relativeTo` method expresses the target path as a relative path, relative to the `dir` argument.
39
198
 
40
- const absoluteData = relativePath.toAbsolute(absolutePath); // AbsolutePath of "/tmp/some-folder/data"
199
+ The `options` argument, if present, should be an object with the property "noLeadingDot", which is a boolean. The noLeadingDot option controls whether the resulting relative path has a leading `.` segment or not. If this option isn't provided, the leading dot will be present. Note that if the resulting path starts with "..", that will always be present, regardless of the option.
41
200
 
42
- onlyWorksWithAbsolutePaths(absoluteData); // No error
201
+ #### `toString(): string` (instance method of Path)
202
+
203
+ The `toString` method returns a string representation of the target Path by joining its segments using its separator.
204
+
205
+ #### `basename(): string` (instance method of Path)
206
+
207
+ The `basename` method returns the final segment string of the target Path. If that Path has no segments, the empty string is returned.
208
+
209
+ #### `extname(options?): string` (instance method of Path)
210
+
211
+ The `extname` method returns the trailing extension of the target Path. Pass `{ full: true }` as the "options" argument to get a compound extension like ".d.ts", rather than the final extension (like ".ts").
212
+
213
+ If the Path doesn't have a trailing extension, the empty string (`""`) is returned.
214
+
215
+ #### `dirname(): Path` (instance method of Path)
216
+
217
+ The `dirname` method returns a new Path containing all of the segments in the target Path except for the last one; ie. the path to the directory that contains the target path.
218
+
219
+ #### `startsWith(value): boolean` (instance method of Path)
220
+
221
+ The `startsWith` method returns whether the target Path starts with the provided value (string, Path, or Array of string/Path), by comparing one path segment at a time, left-to-right.
222
+
223
+ The starting segment(s) of the target Path must _exactly_ match the segment(s) in the provided value.
224
+
225
+ This means that, given two Paths A and B:
226
+
227
+ ```
228
+ A: Path { /home/user/.config }
229
+ B: Path { /home/user/.config2 }
43
230
  ```
44
231
 
232
+ Path B does _not_ start with Path A, because `".config" !== ".config2"`.
233
+
234
+ #### `endsWith(value): boolean` (instance method of Path)
235
+
236
+ The `endsWith` method returns whether the target Path ends with the provided value (string, Path, or Array of string/Path), by comparing one path segment at a time, right-to-left.
237
+
238
+ The ending segment(s) of the target Path must _exactly_ match the segment(s) in the provided value.
239
+
240
+ This means that, given two Paths A and B:
241
+
242
+ ```
243
+ A: Path { /home/1user/.config }
244
+ B: Path { user/.config }
245
+ ```
246
+
247
+ Path A does _not_ end with Path B, because `"1user" !== "user"`.
248
+
249
+ #### `indexOf(value, fromIndex?): number;` (instance method of Path)
250
+
251
+ The `indexOf` method returns the path segment index (number) at which `value` (string, Path, or Array of string/Path) appears in the target Path, or `-1` if it doesn't appear.
252
+
253
+ If the provided value argument contains more than one path segment, the returned index will refer to the location of the value's first path segment.
254
+
255
+ The optional argument `fromIndex` can be provided to specify which index into the target Path's segments to begin the search at. If not provided, the search starts at the beginning (index 0).
256
+
257
+ #### `includes(value, fromIndex?): boolean;` (instance method of Path)
258
+
259
+ The `includes` method returns a boolean indicating whether `value` (string, Path, or Array of string/Path) appears in the target Path.
260
+
261
+ The optional argument `fromIndex` can be provided to specify which index into the target Path's segments to begin the search at. If not provided, the search starts at the beginning (index 0).
262
+
263
+ #### `replace(value, replacement): Path;` (instance method of Path)
264
+
265
+ The `replace` method returns a new `Path` object wherein the first occurrence of `value` (string, Path, or Array of string/Path) in the target Path has been replaced with `replacement` (string, Path, or Array of string/Path).
266
+
267
+ Note that only the first match is replaced; to replace multiple occurrences, use `replaceAll`.
268
+
269
+ If `value` is not present in the target Path, a clone of said Path is returned.
270
+
271
+ > Tip: To "replace with nothing", pass an empty array as the replacement.
272
+
273
+ #### `replaceAll(value, replacement): Path;` (instance method of Path)
274
+
275
+ The `replaceAll` method returns a new `Path` object wherein all occurrences of `value` (string, Path, or Array of string/Path) in the target Path have been replaced with `replacement` (string, Path, or Array of string/Path).
276
+
277
+ If you want to only replace the first occurrence, use `replace` instead.
278
+
279
+ If `value` is not present in the target Path, a clone of said Path is returned.
280
+
281
+ > Tip: To "replace with nothing", pass an empty array as the replacement.
282
+
283
+ #### `replaceLast(replacement): Path;` (instance method of Path)
284
+
285
+ The `replaceLast` method returns a copy of the target Path, but with its final segment replaced with `replacement` (string, Path, or Array of string/Path).
286
+
287
+ This method is most commonly used to modify the final (filename) part of a path.
288
+
289
+ If the target Path has no segments, the returned Path will contain exactly the segments from `replacement`.
290
+
291
+ #### `Path.splitToSegments(inputParts): Array<string>` (static method on Path)
292
+
293
+ Splits one or more path strings into an array of path segments. Used internally by the Path constructor.
294
+
295
+ #### `Path.detectSeparator(input, fallback)` (static method on Path)
296
+
297
+ Searches input (a string or Array of strings) for a path separator character (either forward slash or backslash), and returns it. If none is found, returns `fallback`.
298
+
299
+ #### `Path.normalize(...inputs): Path` (static method on Path)
300
+
301
+ Concatenates the input path(s) and then resolves all non-leading `.` and `..` segments. Shortcut for `new Path(...inputs).normalize()`.
302
+
303
+ #### `Path.isAbsolute(path)`: boolean (static method on Path)
304
+
305
+ Return whether the `path` argument (string or Path) is an absolute path; that is, whether it starts with either `/`, `\`, or a drive letter (ie `C:`).
306
+
307
+ #### `Path.fromRaw(segments, separator): Path` (static method on Path)
308
+
309
+ Creates a new Path object using the provided segments and separator.
310
+
45
311
  ## License
46
312
 
47
313
  MIT
@@ -0,0 +1,167 @@
1
+ /** An object that represents a filesystem path. */
2
+ export declare class Path {
3
+ /** Split one or more path strings into an array of path segments. */
4
+ static splitToSegments(inputParts: Array<string> | string): Array<string>;
5
+ /**
6
+ * Search the provided path string or strings for a path separator character
7
+ * (either forward slash or backslash), and return it. If none is found,
8
+ * return `fallback`.
9
+ */
10
+ static detectSeparator<Fallback extends string | null = string>(input: Array<string> | string, fallback: Fallback): string | Fallback;
11
+ /**
12
+ * Concatenates the input path(s) and then resolves all non-leading `.` and
13
+ * `..` segments.
14
+ */
15
+ static normalize(...inputs: Array<string | Path | Array<string | Path>>): Path;
16
+ /**
17
+ * Return whether the provided path is absolute; that is, whether it
18
+ * starts with either `/`, `\`, or a drive letter (ie `C:`).
19
+ */
20
+ static isAbsolute(path: string | Path): boolean;
21
+ /**
22
+ * An array of the path segments that make up this path.
23
+ *
24
+ * For `/tmp/foo.txt`, it'd be `["", "tmp", "foo.txt"]`.
25
+ *
26
+ * For `C:\something\somewhere.txt`, it'd be `["C:", "something", "somewhere.txt"]`.
27
+ */
28
+ segments: Array<string>;
29
+ /**
30
+ * The path separator that should be used to turn this path into a string.
31
+ *
32
+ * Will be either `/` or `\`.
33
+ */
34
+ separator: string;
35
+ /** Create a new Path object using the provided input(s). */
36
+ constructor(...inputs: Array<string | Path | Array<string | Path>>);
37
+ /**
38
+ * Create a new Path object using the provided segments and separator.
39
+ */
40
+ static fromRaw(segments: Array<string>, separator: string): Path;
41
+ /**
42
+ * Resolve all non-leading `.` and `..` segments in this path.
43
+ */
44
+ normalize(): Path;
45
+ /**
46
+ * Create a new Path by appending additional path segments onto the end of
47
+ * this Path's segments.
48
+ *
49
+ * The returned path will use this path's separator.
50
+ */
51
+ concat(...others: Array<string | Path | Array<string | Path>>): Path;
52
+ /**
53
+ * Return whether this path is absolute; that is, whether it starts with
54
+ * either `/`, `\`, or a drive letter (ie `C:`).
55
+ */
56
+ isAbsolute(): boolean;
57
+ /**
58
+ * Make a second Path object containing the same segments and separator as
59
+ * this one.
60
+ */
61
+ clone(): this;
62
+ /**
63
+ * Express this path relative to `dir`.
64
+ *
65
+ * @param dir - The directory to create a new path relative to.
66
+ * @param options - Options that affect the resulting path.
67
+ */
68
+ relativeTo(dir: Path | string, options?: {
69
+ noLeadingDot?: boolean;
70
+ }): Path;
71
+ /**
72
+ * Turn this path into a string by joining its segments using its separator.
73
+ */
74
+ toString(): string;
75
+ /**
76
+ * Return the final path segment of this path. If this path has no path
77
+ * segments, the empty string is returned.
78
+ */
79
+ basename(): string;
80
+ /**
81
+ * Return the trailing extension of this path. Set option `full` to `true` to
82
+ * get a compound extension like ".d.ts" instead of ".ts".
83
+ */
84
+ extname(options?: {
85
+ full?: boolean;
86
+ }): string;
87
+ /**
88
+ * Return a new Path containing all of the path segments in this one except
89
+ * for the last one; ie. the path to the directory that contains this path.
90
+ */
91
+ dirname(): Path;
92
+ /**
93
+ * Return whether this path starts with the provided value, by comparing one
94
+ * path segment at a time.
95
+ *
96
+ * The starting segments of this path must *exactly* match the segments in the
97
+ * provided value.
98
+ *
99
+ * This means that, given two Paths A and B:
100
+ *
101
+ * ```
102
+ * A: Path { /home/user/.config }
103
+ * B: Path { /home/user/.config2 }
104
+ * ```
105
+ *
106
+ * Path B does *not* start with Path A, because `".config" !== ".config2"`.
107
+ */
108
+ startsWith(value: string | Path | Array<string | Path>): boolean;
109
+ /**
110
+ * Return whether this path ends with the provided value, by comparing one
111
+ * path segment at a time.
112
+ *
113
+ * The ending segments of this path must *exactly* match the segments in the
114
+ * provided value.
115
+ *
116
+ * This means that, given two Paths A and B:
117
+ *
118
+ * ```
119
+ * A: Path { /home/1user/.config }
120
+ * B: Path { user/.config }
121
+ * ```
122
+ *
123
+ * Path A does *not* end with Path B, because `"1user" !== "user"`.
124
+ */
125
+ endsWith(value: string | Path | Array<string | Path>): boolean;
126
+ /**
127
+ * Return the path segment index at which `value` appears in this path, or
128
+ * `-1` if it doesn't appear in this path.
129
+ *
130
+ * @param value - The value to search for. If the value contains more than one path segment, the returned index will refer to the location of the value's first path segment.
131
+ * @param fromIndex - The index into this path's segments to begin searching at. Defaults to `0`.
132
+ */
133
+ indexOf(value: string | Path | Array<string | Path>, fromIndex?: number): number;
134
+ /**
135
+ * Return whether `value` appears in this path.
136
+ *
137
+ * @param value - The value to search for.
138
+ * @param fromIndex - The index into this path's segments to begin searching at. Defaults to `0`.
139
+ */
140
+ includes(value: string | Path | Array<string | Path>, fromIndex?: number): boolean;
141
+ /**
142
+ * Return a new Path wherein the segments in `value` have been replaced with
143
+ * the segments in `replacement`. If the segments in `value` are not present
144
+ * in this path, a clone of this path is returned.
145
+ *
146
+ * Note that only the first match is replaced.
147
+ *
148
+ * @param value - What should be replaced
149
+ * @param replacement - What it should be replaced with
150
+ */
151
+ replace(value: string | Path | Array<string | Path>, replacement: string | Path | Array<string | Path>): Path;
152
+ /**
153
+ * Return a new Path wherein all occurrences of the segments in `value` have
154
+ * been replaced with the segments in `replacement`. If the segments in
155
+ * `value` are not present in this path, a clone of this path is returned.
156
+ *
157
+ * @param value - What should be replaced
158
+ * @param replacement - What it should be replaced with
159
+ */
160
+ replaceAll(value: string | Path | Array<string | Path>, replacement: string | Path | Array<string | Path>): Path;
161
+ /**
162
+ * Return a copy of this path but with the final segment replaced with `replacement`
163
+ *
164
+ * @param replacement - The new final segment(s) for the returned Path
165
+ */
166
+ replaceLast(replacement: string | Path | Array<string | Path>): Path;
167
+ }