nice-path 0.1.1 → 3.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/dist/index.js CHANGED
@@ -1,477 +1,393 @@
1
1
  "use strict";
2
- var __extends = (this && this.__extends) || (function () {
3
- var extendStatics = function (d, b) {
4
- extendStatics = Object.setPrototypeOf ||
5
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
- function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
7
- return extendStatics(d, b);
8
- };
9
- return function (d, b) {
10
- extendStatics(d, b);
11
- function __() { this.constructor = d; }
12
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
13
- };
14
- })();
15
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
16
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
17
- return new (P || (P = Promise))(function (resolve, reject) {
18
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
19
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
20
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
21
- step((generator = generator.apply(thisArg, _arguments || [])).next());
22
- });
23
- };
24
- var __generator = (this && this.__generator) || function (thisArg, body) {
25
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
26
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
27
- function verb(n) { return function (v) { return step([n, v]); }; }
28
- function step(op) {
29
- if (f) throw new TypeError("Generator is already executing.");
30
- while (_) try {
31
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
32
- if (y = 0, t) op = [op[0] & 2, t.value];
33
- switch (op[0]) {
34
- case 0: case 1: t = op; break;
35
- case 4: _.label++; return { value: op[1], done: false };
36
- case 5: _.label++; y = op[1]; op = [0]; continue;
37
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
38
- default:
39
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
40
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
41
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
42
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
43
- if (t[2]) _.ops.pop();
44
- _.trys.pop(); continue;
45
- }
46
- op = body.call(thisArg, _);
47
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
48
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
49
- }
50
- };
51
- var __spreadArrays = (this && this.__spreadArrays) || function () {
52
- for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
53
- for (var r = Array(s), k = 0, i = 0; i < il; i++)
54
- for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
55
- r[k] = a[j];
56
- return r;
57
- };
58
- var __importDefault = (this && this.__importDefault) || function (mod) {
59
- return (mod && mod.__esModule) ? mod : { "default": mod };
60
- };
61
2
  Object.defineProperty(exports, "__esModule", { value: true });
62
- var fs_1 = __importDefault(require("fs"));
63
- var path_1 = __importDefault(require("path"));
64
- var util_1 = __importDefault(require("util"));
65
- var readdirP = util_1.default.promisify(fs_1.default.readdir);
66
- function aOrAn(nextWord) {
67
- return /^[aeiou]/i.test(nextWord) ? "an" : "a";
68
- }
69
- function pathKindErrorMessage(expected, actual) {
70
- return "Expected to receive " + aOrAn(expected) + " " + expected + " path, but received " + aOrAn(actual) + " " + actual + " path instead";
3
+ exports.Path = void 0;
4
+ const WIN32_DRIVE_LETTER_REGEXP = /^[A-Za-z]:$/;
5
+ function isWin32DriveLetter(pathString) {
6
+ return WIN32_DRIVE_LETTER_REGEXP.test(pathString);
71
7
  }
72
- function comparisonRootKindMismatchErrorMessage(firstPath, secondPath) {
73
- var firstRootKind = detectRootKind(firstPath);
74
- var secondRootKind = detectRootKind(secondPath);
75
- return ("The two paths being compared are not in the same namespace; " +
76
- ("the first path (" + JSON.stringify(firstPath) + ") has root kind " + JSON.stringify(firstRootKind) + " ") +
77
- ("but the second path (" + JSON.stringify(secondPath) + ") has root kind ") +
78
- ("'" + JSON.stringify(secondRootKind) + "'."));
8
+ function validateSegments(segments, separator) {
9
+ return segments.filter((part, index) => {
10
+ // first part can be "" to represent left side of root "/"
11
+ // second part can be "" to support windows UNC paths
12
+ if (part === "" && index === 0) {
13
+ return true;
14
+ }
15
+ else if (part === "" &&
16
+ index === 1 &&
17
+ separator === "\\" &&
18
+ segments[0] === "") {
19
+ return true;
20
+ }
21
+ return Boolean(part);
22
+ });
79
23
  }
80
- function getSeparator(pathString) {
81
- var sep = "/";
82
- if (!pathString.includes(sep)) {
83
- sep = "\\";
84
- if (!pathString.includes(sep)) {
85
- sep = path_1.default.sep;
24
+ /** An object that represents a filesystem path. */
25
+ class Path {
26
+ /** Split one or more path strings into an array of path segments. */
27
+ static splitToSegments(inputParts) {
28
+ if (!Array.isArray(inputParts)) {
29
+ inputParts = [inputParts];
86
30
  }
31
+ const separator = Path.detectSeparator(inputParts, "/");
32
+ return validateSegments(inputParts.map((part) => part.split(/(?:\/|\\)/g)).flat(1), separator);
87
33
  }
88
- return sep;
89
- }
90
- function getSegments(pathString) {
91
- var sep = getSeparator(pathString);
92
- var segments = pathString.split(sep).filter(Boolean);
93
- return segments;
94
- }
95
- // Normal path.join always uses path.sep and removes leading . or .. segments,
96
- // so this is a safer alternative.
97
- function safeJoin(pathsOrSegments, sep) {
98
- var trimmedSegments = pathsOrSegments.map(function (pathOrSegment, pathOrSegmentIndex) {
99
- return pathOrSegment
100
- .split(sep)
101
- .filter(function (segment) {
102
- if (pathOrSegmentIndex === 0) {
103
- return true;
34
+ /**
35
+ * Search the provided path string or strings for a path separator character
36
+ * (either forward slash or backslash), and return it. If none is found,
37
+ * return `fallback`.
38
+ */
39
+ static detectSeparator(input, fallback) {
40
+ let testStr = input;
41
+ if (Array.isArray(input)) {
42
+ testStr = input.join("|");
43
+ }
44
+ for (const char of testStr) {
45
+ if (char === "/") {
46
+ return "/";
104
47
  }
105
- else {
106
- return Boolean(segment);
48
+ else if (char === "\\") {
49
+ return "\\";
107
50
  }
108
- })
109
- .join(sep);
110
- });
111
- return trimmedSegments.join(sep);
112
- }
113
- function detectPathKind(pathString) {
114
- var sep = getSeparator(pathString);
115
- var segments = getSegments(pathString);
116
- if (pathString.startsWith(sep) ||
117
- pathString.startsWith("\\\\") ||
118
- pathString.match(/^[A-Z]:/i)) {
119
- return "absolute";
120
- }
121
- else if (segments[0] === "." || segments[0] === "..") {
122
- return "relative";
51
+ }
52
+ return fallback;
123
53
  }
124
- else {
125
- return "unqualified";
54
+ /**
55
+ * Concatenates the input path(s) and then resolves all non-leading `.` and
56
+ * `..` segments.
57
+ */
58
+ static normalize(...inputs) {
59
+ return new Path(...inputs).normalize();
126
60
  }
127
- }
128
- function detectRootKind(pathString) {
129
- var sep = getSeparator(pathString);
130
- if (pathString.match(/^[A-Z]:/i)) {
131
- return "letter-drive";
61
+ /**
62
+ * Return whether the provided path is absolute; that is, whether it
63
+ * starts with either `/`, `\`, or a drive letter (ie `C:`).
64
+ */
65
+ static isAbsolute(path) {
66
+ if (path instanceof Path) {
67
+ return path.isAbsolute();
68
+ }
69
+ else {
70
+ return new Path(path).isAbsolute();
71
+ }
132
72
  }
133
- else if (pathString.startsWith("\\\\")) {
134
- return "unc";
73
+ /** Create a new Path object using the provided input(s). */
74
+ constructor(...inputs) {
75
+ const parts = inputs
76
+ .flat(1)
77
+ .map((part) => (typeof part === "string" ? part : part.segments))
78
+ .flat(1);
79
+ this.segments = Path.splitToSegments(parts);
80
+ this.separator = Path.detectSeparator(parts, "/");
135
81
  }
136
- else if (pathString.startsWith(sep)) {
137
- return "leading-slash";
82
+ /**
83
+ * Create a new Path object using the provided segments and, optionally,
84
+ * separator.
85
+ *
86
+ * NOTE: this doesn't set the `segments` directly; it passes them through a
87
+ * filtering step first, to remove any double-slashes or etc. To set the
88
+ * `.segments` directly, use {@link fromRaw}.
89
+ */
90
+ static from(segments, separator) {
91
+ const separatorToUse = separator || Path.detectSeparator(segments, "/");
92
+ const path = new Path();
93
+ path.segments = validateSegments(segments, separatorToUse);
94
+ path.separator = separatorToUse;
95
+ return path;
138
96
  }
139
- else {
140
- throw new Error("Unable to detect root kind from path string: '" + pathString + "'. Is it an absolute path?");
97
+ /**
98
+ * Create a new Path object using the provided segments and separator.
99
+ *
100
+ * NOTE: this method doesn't do any sort of validation on `segments`; as such,
101
+ * it can be used to construct an invalid Path object. Consider using
102
+ * {@link from} instead.
103
+ */
104
+ static fromRaw(segments, separator) {
105
+ const path = new Path();
106
+ path.segments = segments;
107
+ path.separator = separator;
108
+ return path;
141
109
  }
142
- }
143
- function reconsistuteAbsolutePath(originalRaw, newSegments) {
144
- var sep = getSeparator(originalRaw);
145
- var rootKind = detectRootKind(originalRaw);
146
- var suffix = safeJoin(newSegments, sep);
147
- var prefix = "";
148
- switch (rootKind) {
149
- case "leading-slash": {
150
- prefix = sep;
151
- break;
152
- }
153
- case "unc": {
154
- prefix = sep.repeat(2);
155
- break;
110
+ /**
111
+ * Resolve all non-leading `.` and `..` segments in this path.
112
+ */
113
+ normalize() {
114
+ // we clone this cause we're gonna mutate it
115
+ const segments = [...this.segments];
116
+ const newSegments = [];
117
+ let currentSegment;
118
+ while (segments.length > 0) {
119
+ currentSegment = segments.shift();
120
+ switch (currentSegment) {
121
+ case ".": {
122
+ if (newSegments.length === 0) {
123
+ newSegments.push(currentSegment);
124
+ }
125
+ break;
126
+ }
127
+ case "..": {
128
+ if (newSegments.length === 0) {
129
+ newSegments.push(currentSegment);
130
+ }
131
+ else {
132
+ newSegments.pop();
133
+ }
134
+ break;
135
+ }
136
+ default: {
137
+ if (currentSegment != null) {
138
+ newSegments.push(currentSegment);
139
+ }
140
+ break;
141
+ }
142
+ }
156
143
  }
144
+ return Path.fromRaw(newSegments, this.separator);
157
145
  }
158
- return prefix + suffix;
159
- }
160
- var Path = /** @class */ (function () {
161
- function Path(raw, kind) {
162
- this.raw = raw;
163
- this.kind = kind;
146
+ /**
147
+ * Create a new Path by appending additional path segments onto the end of
148
+ * this Path's segments.
149
+ *
150
+ * The returned path will use this path's separator.
151
+ */
152
+ concat(...others) {
153
+ const otherSegments = new Path(others.flat(1)).segments;
154
+ return Path.from(this.segments.concat(otherSegments), this.separator);
164
155
  }
165
- Path.prototype.toString = function () {
166
- return this.raw;
167
- };
168
- Path.fromAbsolutePathString = function (absolutePath) {
169
- var kind = detectPathKind(absolutePath);
170
- if (kind !== "absolute") {
171
- throw new Error(pathKindErrorMessage("absolute", kind));
172
- }
173
- return new AbsolutePath(absolutePath);
174
- };
175
- Path.fromRelativePathString = function (relativePath) {
176
- var kind = detectPathKind(relativePath);
177
- if (kind !== "relative") {
178
- throw new Error(pathKindErrorMessage("relative", kind));
156
+ /**
157
+ * Return whether this path is absolute; that is, whether it starts with
158
+ * either `/`, `\`, or a drive letter (ie `C:`).
159
+ */
160
+ isAbsolute() {
161
+ const firstPart = this.segments[0];
162
+ // empty first component indicates that path starts with leading slash.
163
+ // could be unix fs root, or windows unc path
164
+ if (firstPart === "")
165
+ return true;
166
+ // windows drive
167
+ if (/^[A-Za-z]:/.test(firstPart))
168
+ return true;
169
+ return false;
170
+ }
171
+ /**
172
+ * Make a second Path object containing the same segments and separator as
173
+ * this one.
174
+ */
175
+ clone() {
176
+ const theClone = this.constructor.fromRaw([...this.segments], this.separator);
177
+ return theClone;
178
+ }
179
+ /**
180
+ * Express this path relative to `dir`.
181
+ *
182
+ * @param dir - The directory to create a new path relative to.
183
+ * @param options - Options that affect the resulting path.
184
+ */
185
+ relativeTo(dir, options = {}) {
186
+ if (!(dir instanceof Path)) {
187
+ dir = new Path(dir);
179
188
  }
180
- return new RelativePath(relativePath);
181
- };
182
- Path.fromUnqualifiedPathString = function (unqualifiedPath) {
183
- var kind = detectPathKind(unqualifiedPath);
184
- if (kind !== "unqualified") {
185
- throw new Error(pathKindErrorMessage("unqualified", kind));
189
+ const ownSegments = [...this.segments];
190
+ const dirSegments = [...dir.segments];
191
+ while (ownSegments[0] === dirSegments[0]) {
192
+ ownSegments.shift();
193
+ dirSegments.shift();
186
194
  }
187
- return new UnqualifiedPath(unqualifiedPath);
188
- };
189
- Path.from = function (raw) {
190
- var kind = detectPathKind(raw);
191
- switch (kind) {
192
- case "absolute":
193
- return new AbsolutePath(raw);
194
- case "relative":
195
- return new RelativePath(raw);
196
- case "unqualified":
197
- return new UnqualifiedPath(raw);
198
- default: {
199
- throw new Error("Unable to detect path type from input string");
195
+ if (dirSegments.length === 0) {
196
+ if (options.noLeadingDot) {
197
+ return Path.from(ownSegments, this.separator);
198
+ }
199
+ else {
200
+ return Path.from([".", ...ownSegments], this.separator);
200
201
  }
201
- }
202
- };
203
- Path.prototype.isAbsolute = function () {
204
- return this.kind === "absolute";
205
- };
206
- Path.prototype.isRelative = function () {
207
- return this.kind === "relative";
208
- };
209
- Path.prototype.isUnqualified = function () {
210
- return this.kind === "unqualified";
211
- };
212
- Path.prototype.hasTrailingSlash = function () {
213
- var sep = getSeparator(this.raw);
214
- return this.raw.endsWith(sep);
215
- };
216
- Path.prototype.removeTrailingSlash = function () {
217
- if (!this.hasTrailingSlash()) {
218
- return this;
219
202
  }
220
203
  else {
221
- return new Path(this.raw.replace(/(?:[/\\])+$/, ""), this.kind);
204
+ const dotDots = dirSegments.map((_) => "..");
205
+ return Path.from([...dotDots, ...ownSegments], this.separator);
222
206
  }
223
- };
224
- Path.prototype.append = function () {
225
- var segments = [];
226
- for (var _i = 0; _i < arguments.length; _i++) {
227
- segments[_i] = arguments[_i];
207
+ }
208
+ /**
209
+ * Turn this path into a string by joining its segments using its separator.
210
+ */
211
+ toString() {
212
+ let result = this.segments.join(this.separator);
213
+ if (result == "") {
214
+ return "/";
228
215
  }
229
- var sep = getSeparator(this.raw);
230
- return new Path(safeJoin(__spreadArrays([this.raw], segments), sep), this.kind);
231
- };
232
- Path.prototype.resolve = function () {
233
- var segments = getSegments(this.raw);
234
- var nextSegments = [];
235
- for (var i = 0; i < segments.length; i++) {
236
- var currentSegment = segments[i];
237
- if (currentSegment === "." && i !== 0) {
238
- continue;
239
- }
240
- else if (currentSegment === ".." && nextSegments.length > 0) {
241
- nextSegments.pop();
216
+ else {
217
+ if (isWin32DriveLetter(result)) {
218
+ return result + this.separator;
242
219
  }
243
220
  else {
244
- nextSegments.push(currentSegment);
221
+ return result;
245
222
  }
246
223
  }
247
- var nextRaw = reconsistuteAbsolutePath(this.raw, nextSegments);
248
- return new Path(nextRaw, this.kind);
249
- };
250
- return Path;
251
- }());
252
- exports.Path = Path;
253
- var AbsolutePath = /** @class */ (function (_super) {
254
- __extends(AbsolutePath, _super);
255
- function AbsolutePath(raw) {
256
- return _super.call(this, raw, "absolute") || this;
257
224
  }
258
- AbsolutePath.prototype.relativeTo = function (relativeTo) {
259
- if (typeof relativeTo === "string") {
260
- var pathKind = detectPathKind(relativeTo);
261
- if (pathKind !== "absolute") {
262
- throw new Error(pathKindErrorMessage("absolute", pathKind));
263
- }
264
- }
265
- var relativeToString = typeof relativeTo === "string" ? relativeTo : relativeTo.raw;
266
- var thisRootKind = detectRootKind(this.raw);
267
- var relativeToRootKind = detectRootKind(relativeToString);
268
- if (thisRootKind !== relativeToRootKind) {
269
- throw new Error(comparisonRootKindMismatchErrorMessage(this.raw, relativeToString));
225
+ /**
226
+ * Return the final path segment of this path. If this path has no path
227
+ * segments, the empty string is returned.
228
+ */
229
+ basename() {
230
+ const last = this.segments[this.segments.length - 1];
231
+ return last || "";
232
+ }
233
+ /**
234
+ * Return the trailing extension of this path. Set option `full` to `true` to
235
+ * get a compound extension like ".d.ts" instead of ".ts".
236
+ */
237
+ extname(options = {}) {
238
+ const filename = this.basename();
239
+ const parts = filename.split(".");
240
+ if (parts.length === 1) {
241
+ return "";
270
242
  }
271
- if (thisRootKind === "letter-drive" &&
272
- relativeToRootKind === "letter-drive" &&
273
- this.raw.slice(0, 1).toUpperCase() !==
274
- relativeToString.slice(0, 1).toUpperCase()) {
275
- throw new Error("The two paths being compared are not on the same drive; comparing " +
276
- JSON.stringify(this.raw) +
277
- " and " +
278
- JSON.stringify(relativeToString));
243
+ if (options.full) {
244
+ return "." + parts.slice(1).join(".");
279
245
  }
280
- var thisSegments = getSegments(this.raw);
281
- var relativeToSegments = getSegments(relativeToString);
282
- if (thisRootKind === "unc" &&
283
- relativeToRootKind === "unc" &&
284
- thisSegments[0] !== relativeToSegments[0]) {
285
- throw new Error("The two paths being compared are not on the same UNC host; comparing " +
286
- JSON.stringify(this.raw) +
287
- " and " +
288
- JSON.stringify(relativeToString));
246
+ else {
247
+ return "." + parts[parts.length - 1];
289
248
  }
290
- var longerLength = Math.max(thisSegments.length, relativeToSegments.length);
291
- var lastSharedSegmentIndex = -1;
292
- for (var i = 0; i < longerLength; i++) {
293
- var thisSegment = thisSegments[i];
294
- var relativeToSegment = relativeToSegments[i];
295
- if (thisSegment === relativeToSegment) {
296
- lastSharedSegmentIndex = i;
297
- }
298
- else {
299
- break;
249
+ }
250
+ /**
251
+ * Return a new Path containing all of the path segments in this one except
252
+ * for the last one; ie. the path to the directory that contains this path.
253
+ */
254
+ dirname() {
255
+ return this.replaceLast([]);
256
+ }
257
+ /**
258
+ * Return whether this path starts with the provided value, by comparing one
259
+ * path segment at a time.
260
+ *
261
+ * The starting segments of this path must *exactly* match the segments in the
262
+ * provided value.
263
+ *
264
+ * This means that, given two Paths A and B:
265
+ *
266
+ * ```
267
+ * A: Path { /home/user/.config }
268
+ * B: Path { /home/user/.config2 }
269
+ * ```
270
+ *
271
+ * Path B does *not* start with Path A, because `".config" !== ".config2"`.
272
+ */
273
+ startsWith(value) {
274
+ value = new Path(value);
275
+ return value.segments.every((segment, index) => this.segments[index] === segment);
276
+ }
277
+ /**
278
+ * Return whether this path ends with the provided value, by comparing one
279
+ * path segment at a time.
280
+ *
281
+ * The ending segments of this path must *exactly* match the segments in the
282
+ * provided value.
283
+ *
284
+ * This means that, given two Paths A and B:
285
+ *
286
+ * ```
287
+ * A: Path { /home/1user/.config }
288
+ * B: Path { user/.config }
289
+ * ```
290
+ *
291
+ * Path A does *not* end with Path B, because `"1user" !== "user"`.
292
+ */
293
+ endsWith(value) {
294
+ value = new Path(value);
295
+ const valueSegmentsReversed = [...value.segments].reverse();
296
+ const ownSegmentsReversed = [...this.segments].reverse();
297
+ return valueSegmentsReversed.every((segment, index) => ownSegmentsReversed[index] === segment);
298
+ }
299
+ /**
300
+ * Return the path segment index at which `value` appears in this path, or
301
+ * `-1` if it doesn't appear in this path.
302
+ *
303
+ * @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.
304
+ * @param fromIndex - The index into this path's segments to begin searching at. Defaults to `0`.
305
+ */
306
+ indexOf(value, fromIndex = 0) {
307
+ value = new Path(value);
308
+ const ownSegmentsLength = this.segments.length;
309
+ for (let i = fromIndex; i < ownSegmentsLength; i++) {
310
+ if (value.segments.every((valueSegment, valueIndex) => {
311
+ return this.segments[i + valueIndex] === valueSegment;
312
+ })) {
313
+ return i;
300
314
  }
301
315
  }
302
- var nextSegments = [];
303
- if (lastSharedSegmentIndex === -1) {
304
- // No shared commonality between the paths
305
- nextSegments = relativeToSegments.map(function () { return ".."; });
306
- nextSegments.push.apply(nextSegments, thisSegments);
316
+ return -1;
317
+ }
318
+ /**
319
+ * Return whether `value` appears in this path.
320
+ *
321
+ * @param value - The value to search for.
322
+ * @param fromIndex - The index into this path's segments to begin searching at. Defaults to `0`.
323
+ */
324
+ includes(value, fromIndex = 0) {
325
+ return this.indexOf(value, fromIndex) !== -1;
326
+ }
327
+ /**
328
+ * Return a new Path wherein the segments in `value` have been replaced with
329
+ * the segments in `replacement`. If the segments in `value` are not present
330
+ * in this path, a clone of this path is returned.
331
+ *
332
+ * Note that only the first match is replaced.
333
+ *
334
+ * @param value - What should be replaced
335
+ * @param replacement - What it should be replaced with
336
+ *
337
+ * NOTE: to remove segments, use an empty Array for `replacement`.
338
+ */
339
+ replace(value, replacement) {
340
+ value = new Path(value);
341
+ replacement = new Path(replacement);
342
+ const matchIndex = this.indexOf(value);
343
+ if (matchIndex === -1) {
344
+ return this.clone();
307
345
  }
308
346
  else {
309
- var remainingThisSegments = [];
310
- for (var i = 0; i < thisSegments.length; i++) {
311
- if (i > lastSharedSegmentIndex) {
312
- remainingThisSegments.push(thisSegments[i]);
313
- }
314
- }
315
- var remainingRelativeToSegments = [];
316
- for (var i = 0; i < relativeToSegments.length; i++) {
317
- if (i > lastSharedSegmentIndex) {
318
- remainingRelativeToSegments.push(thisSegments[i]);
319
- }
320
- }
321
- nextSegments.push.apply(nextSegments, __spreadArrays(remainingRelativeToSegments.map(function () { return ".."; }), remainingThisSegments));
322
- }
323
- if (nextSegments[0] !== "..") {
324
- nextSegments = __spreadArrays(["."], nextSegments);
347
+ const newSegments = [
348
+ ...this.segments.slice(0, matchIndex),
349
+ ...replacement.segments,
350
+ ...this.segments.slice(matchIndex + value.segments.length),
351
+ ];
352
+ return Path.from(newSegments, this.separator);
325
353
  }
326
- var sep = getSeparator(this.raw);
327
- var raw = safeJoin(nextSegments, sep);
328
- return new RelativePath(raw);
329
- };
330
- AbsolutePath.prototype.append = function () {
331
- var segments = [];
332
- for (var _i = 0; _i < arguments.length; _i++) {
333
- segments[_i] = arguments[_i];
334
- }
335
- var normalPath = _super.prototype.append.apply(this, segments);
336
- return new AbsolutePath(normalPath.raw);
337
- };
338
- AbsolutePath.prototype.removeTrailingSlash = function () {
339
- var normalPath = _super.prototype.removeTrailingSlash.call(this);
340
- return new AbsolutePath(normalPath.raw);
341
- };
342
- AbsolutePath.prototype.resolve = function () {
343
- var normalPath = _super.prototype.resolve.call(this);
344
- return new AbsolutePath(normalPath.raw);
345
- };
346
- AbsolutePath.prototype.readdir = function (options) {
347
- return __awaiter(this, void 0, void 0, function () {
348
- var dirs;
349
- var _this = this;
350
- return __generator(this, function (_a) {
351
- switch (_a.label) {
352
- case 0: return [4 /*yield*/, readdirP(this.raw, options)];
353
- case 1:
354
- dirs = _a.sent();
355
- // @ts-ignore
356
- return [2 /*return*/, dirs.map(function (dir) {
357
- return new AbsolutePath(path_1.default.join(_this.raw, dir.toString("utf-8")));
358
- })];
359
- }
360
- });
361
- });
362
- };
363
- AbsolutePath.prototype.readdirSync = function (options) {
364
- var _this = this;
365
- var dirs = fs_1.default.readdirSync(this.raw, options);
366
- // @ts-ignore
367
- return dirs.map(function (dir) {
368
- return new AbsolutePath(path_1.default.join(_this.raw, dir.toString("utf-8")));
369
- });
370
- };
371
- AbsolutePath.prototype.parentDirectory = function () {
372
- var segments = getSegments(this.raw);
373
- var nextSegments = segments.slice(0, -1);
374
- var nextRaw = reconsistuteAbsolutePath(this.raw, nextSegments);
375
- return new AbsolutePath(nextRaw);
376
- };
377
- return AbsolutePath;
378
- }(Path));
379
- exports.AbsolutePath = AbsolutePath;
380
- var RelativePath = /** @class */ (function (_super) {
381
- __extends(RelativePath, _super);
382
- function RelativePath(raw) {
383
- return _super.call(this, raw, "relative") || this;
384
354
  }
385
- RelativePath.prototype.toAbsolute = function (contextDir) {
386
- var contextDirPath = typeof contextDir === "string"
387
- ? Path.fromAbsolutePathString(contextDir)
388
- : contextDir;
389
- var thisSegments = getSegments(this.raw);
390
- var nextSegments = __spreadArrays([
391
- contextDirPath.removeTrailingSlash().raw
392
- ], thisSegments);
393
- return new AbsolutePath(safeJoin(nextSegments, getSeparator(contextDirPath.raw))).resolve();
394
- };
395
- RelativePath.prototype.toUnqualified = function () {
396
- var segments = getSegments(this.raw);
397
- var sep = getSeparator(this.raw);
398
- var firstNonRelativeSegmentIndex = 0;
399
- for (var _i = 0, segments_1 = segments; _i < segments_1.length; _i++) {
400
- var segment = segments_1[_i];
401
- if (segment === "." || segment === "..") {
402
- firstNonRelativeSegmentIndex++;
355
+ /**
356
+ * Return a new Path wherein all occurrences of the segments in `value` have
357
+ * been replaced with the segments in `replacement`. If the segments in
358
+ * `value` are not present in this path, a clone of this path is returned.
359
+ *
360
+ * @param value - What should be replaced
361
+ * @param replacement - What it should be replaced with
362
+ */
363
+ replaceAll(value, replacement) {
364
+ replacement = new Path(replacement);
365
+ let searchIndex = 0;
366
+ let currentPath = this;
367
+ const ownLength = this.segments.length;
368
+ while (searchIndex < ownLength) {
369
+ const matchingIndex = this.indexOf(value, searchIndex);
370
+ if (matchingIndex === -1) {
371
+ break;
403
372
  }
404
373
  else {
405
- break;
374
+ currentPath = currentPath.replace(value, replacement);
375
+ searchIndex = matchingIndex + replacement.segments.length;
406
376
  }
407
377
  }
408
- var unqualifiedSegments = segments.slice(firstNonRelativeSegmentIndex);
409
- var newRaw = safeJoin(unqualifiedSegments, sep);
410
- return new UnqualifiedPath(newRaw);
411
- };
412
- RelativePath.prototype.append = function () {
413
- var segments = [];
414
- for (var _i = 0; _i < arguments.length; _i++) {
415
- segments[_i] = arguments[_i];
416
- }
417
- var normalPath = _super.prototype.append.apply(this, segments);
418
- return new RelativePath(normalPath.raw);
419
- };
420
- RelativePath.prototype.removeTrailingSlash = function () {
421
- var normalPath = _super.prototype.removeTrailingSlash.call(this);
422
- return new RelativePath(normalPath.raw);
423
- };
424
- RelativePath.prototype.resolve = function () {
425
- var normalPath = _super.prototype.resolve.call(this);
426
- return new RelativePath(normalPath.raw);
427
- };
428
- return RelativePath;
429
- }(Path));
430
- exports.RelativePath = RelativePath;
431
- var UnqualifiedPath = /** @class */ (function (_super) {
432
- __extends(UnqualifiedPath, _super);
433
- function UnqualifiedPath(raw) {
434
- return _super.call(this, raw, "unqualified") || this;
378
+ return currentPath;
435
379
  }
436
- UnqualifiedPath.prototype.toAbsolute = function (contextDir) {
437
- var contextDirPath = typeof contextDir === "string"
438
- ? Path.fromAbsolutePathString(contextDir)
439
- : contextDir;
440
- var thisSegments = getSegments(this.raw);
441
- var nextSegments = __spreadArrays([
442
- contextDirPath.removeTrailingSlash().raw
443
- ], thisSegments);
444
- return new AbsolutePath(safeJoin(nextSegments, getSeparator(contextDirPath.raw))).resolve();
445
- };
446
- UnqualifiedPath.prototype.append = function () {
447
- var segments = [];
448
- for (var _i = 0; _i < arguments.length; _i++) {
449
- segments[_i] = arguments[_i];
450
- }
451
- var normalPath = _super.prototype.append.apply(this, segments);
452
- return new UnqualifiedPath(normalPath.raw);
453
- };
454
- UnqualifiedPath.prototype.prepend = function () {
455
- var segments = [];
456
- for (var _i = 0; _i < arguments.length; _i++) {
457
- segments[_i] = arguments[_i];
458
- }
459
- var sep = getSeparator(this.raw);
460
- return new UnqualifiedPath(safeJoin(__spreadArrays(segments, [this.raw]), sep));
461
- };
462
- UnqualifiedPath.prototype.removeTrailingSlash = function () {
463
- var normalPath = _super.prototype.removeTrailingSlash.call(this);
464
- return new UnqualifiedPath(normalPath.raw);
465
- };
466
- UnqualifiedPath.prototype.resolve = function () {
467
- var normalPath = _super.prototype.resolve.call(this);
468
- return new UnqualifiedPath(normalPath.raw);
469
- };
470
- return UnqualifiedPath;
471
- }(Path));
472
- exports.UnqualifiedPath = UnqualifiedPath;
473
- module.exports = Path;
474
- module.exports.Path = Path;
475
- module.exports.AbsolutePath = AbsolutePath;
476
- module.exports.RelativePath = RelativePath;
477
- module.exports.UnqualifiedPath = UnqualifiedPath;
380
+ /**
381
+ * Return a copy of this path but with the final segment replaced with `replacement`
382
+ *
383
+ * @param replacement - The new final segment(s) for the returned Path
384
+ */
385
+ replaceLast(replacement) {
386
+ replacement = new Path(replacement);
387
+ const segments = [...this.segments];
388
+ segments.pop();
389
+ segments.push(...replacement.segments);
390
+ return Path.from(segments, this.separator);
391
+ }
392
+ }
393
+ exports.Path = Path;