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/dist/index.js CHANGED
@@ -1,477 +1,372 @@
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";
51
+ }
52
+ return fallback;
120
53
  }
121
- else if (segments[0] === "." || segments[0] === "..") {
122
- return "relative";
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();
123
60
  }
124
- else {
125
- return "unqualified";
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
+ }
126
72
  }
127
- }
128
- function detectRootKind(pathString) {
129
- var sep = getSeparator(pathString);
130
- if (pathString.match(/^[A-Z]:/i)) {
131
- return "letter-drive";
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, "/");
132
81
  }
133
- else if (pathString.startsWith("\\\\")) {
134
- return "unc";
82
+ /**
83
+ * Create a new Path object using the provided segments and separator.
84
+ */
85
+ static fromRaw(segments, separator) {
86
+ const path = new Path();
87
+ path.segments = validateSegments(segments, separator);
88
+ path.separator = separator;
89
+ return path;
135
90
  }
136
- else if (pathString.startsWith(sep)) {
137
- return "leading-slash";
91
+ /**
92
+ * Resolve all non-leading `.` and `..` segments in this path.
93
+ */
94
+ normalize() {
95
+ // we clone this cause we're gonna mutate it
96
+ const segments = [...this.segments];
97
+ const newSegments = [];
98
+ let currentSegment;
99
+ while (segments.length > 0) {
100
+ currentSegment = segments.shift();
101
+ switch (currentSegment) {
102
+ case ".": {
103
+ if (newSegments.length === 0) {
104
+ newSegments.push(currentSegment);
105
+ }
106
+ break;
107
+ }
108
+ case "..": {
109
+ if (newSegments.length === 0) {
110
+ newSegments.push(currentSegment);
111
+ }
112
+ else {
113
+ newSegments.pop();
114
+ }
115
+ break;
116
+ }
117
+ default: {
118
+ if (currentSegment != null) {
119
+ newSegments.push(currentSegment);
120
+ }
121
+ break;
122
+ }
123
+ }
124
+ }
125
+ return Path.fromRaw(newSegments, this.separator);
138
126
  }
139
- else {
140
- throw new Error("Unable to detect root kind from path string: '" + pathString + "'. Is it an absolute path?");
127
+ /**
128
+ * Create a new Path by appending additional path segments onto the end of
129
+ * this Path's segments.
130
+ *
131
+ * The returned path will use this path's separator.
132
+ */
133
+ concat(...others) {
134
+ const otherSegments = new Path(others.flat(1)).segments;
135
+ return Path.fromRaw(this.segments.concat(otherSegments), this.separator);
141
136
  }
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;
156
- }
137
+ /**
138
+ * Return whether this path is absolute; that is, whether it starts with
139
+ * either `/`, `\`, or a drive letter (ie `C:`).
140
+ */
141
+ isAbsolute() {
142
+ const firstPart = this.segments[0];
143
+ // empty first component indicates that path starts with leading slash.
144
+ // could be unix fs root, or windows unc path
145
+ if (firstPart === "")
146
+ return true;
147
+ // windows drive
148
+ if (/^[A-Za-z]:/.test(firstPart))
149
+ return true;
150
+ return false;
157
151
  }
158
- return prefix + suffix;
159
- }
160
- var Path = /** @class */ (function () {
161
- function Path(raw, kind) {
162
- this.raw = raw;
163
- this.kind = kind;
152
+ /**
153
+ * Make a second Path object containing the same segments and separator as
154
+ * this one.
155
+ */
156
+ clone() {
157
+ const theClone = this.constructor.fromRaw(this.segments, this.separator);
158
+ return theClone;
164
159
  }
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));
160
+ /**
161
+ * Express this path relative to `dir`.
162
+ *
163
+ * @param dir - The directory to create a new path relative to.
164
+ * @param options - Options that affect the resulting path.
165
+ */
166
+ relativeTo(dir, options = {}) {
167
+ if (!(dir instanceof Path)) {
168
+ dir = new Path(dir);
179
169
  }
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));
170
+ const ownSegments = [...this.segments];
171
+ const dirSegments = [...dir.segments];
172
+ while (ownSegments[0] === dirSegments[0]) {
173
+ ownSegments.shift();
174
+ dirSegments.shift();
186
175
  }
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");
176
+ if (dirSegments.length === 0) {
177
+ if (options.noLeadingDot) {
178
+ return Path.fromRaw(ownSegments, this.separator);
179
+ }
180
+ else {
181
+ return Path.fromRaw([".", ...ownSegments], this.separator);
200
182
  }
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
183
  }
220
184
  else {
221
- return new Path(this.raw.replace(/(?:[/\\])+$/, ""), this.kind);
185
+ const dotDots = dirSegments.map((_) => "..");
186
+ return Path.fromRaw([...dotDots, ...ownSegments], this.separator);
222
187
  }
223
- };
224
- Path.prototype.append = function () {
225
- var segments = [];
226
- for (var _i = 0; _i < arguments.length; _i++) {
227
- segments[_i] = arguments[_i];
188
+ }
189
+ /**
190
+ * Turn this path into a string by joining its segments using its separator.
191
+ */
192
+ toString() {
193
+ let result = this.segments.join(this.separator);
194
+ if (result == "") {
195
+ return "/";
228
196
  }
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();
197
+ else {
198
+ if (isWin32DriveLetter(result)) {
199
+ return result + this.separator;
242
200
  }
243
201
  else {
244
- nextSegments.push(currentSegment);
202
+ return result;
245
203
  }
246
204
  }
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
205
  }
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));
206
+ /**
207
+ * Return the final path segment of this path. If this path has no path
208
+ * segments, the empty string is returned.
209
+ */
210
+ basename() {
211
+ const last = this.segments[this.segments.length - 1];
212
+ return last || "";
213
+ }
214
+ /**
215
+ * Return the trailing extension of this path. Set option `full` to `true` to
216
+ * get a compound extension like ".d.ts" instead of ".ts".
217
+ */
218
+ extname(options = {}) {
219
+ const filename = this.basename();
220
+ const parts = filename.split(".");
221
+ if (parts.length === 1) {
222
+ return "";
270
223
  }
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));
224
+ if (options.full) {
225
+ return "." + parts.slice(1).join(".");
279
226
  }
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));
227
+ else {
228
+ return "." + parts[parts.length - 1];
289
229
  }
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;
230
+ }
231
+ /**
232
+ * Return a new Path containing all of the path segments in this one except
233
+ * for the last one; ie. the path to the directory that contains this path.
234
+ */
235
+ dirname() {
236
+ return this.replaceLast([]);
237
+ }
238
+ /**
239
+ * Return whether this path starts with the provided value, by comparing one
240
+ * path segment at a time.
241
+ *
242
+ * The starting segments of this path must *exactly* match the segments in the
243
+ * provided value.
244
+ *
245
+ * This means that, given two Paths A and B:
246
+ *
247
+ * ```
248
+ * A: Path { /home/user/.config }
249
+ * B: Path { /home/user/.config2 }
250
+ * ```
251
+ *
252
+ * Path B does *not* start with Path A, because `".config" !== ".config2"`.
253
+ */
254
+ startsWith(value) {
255
+ value = new Path(value);
256
+ return value.segments.every((segment, index) => this.segments[index] === segment);
257
+ }
258
+ /**
259
+ * Return whether this path ends with the provided value, by comparing one
260
+ * path segment at a time.
261
+ *
262
+ * The ending segments of this path must *exactly* match the segments in the
263
+ * provided value.
264
+ *
265
+ * This means that, given two Paths A and B:
266
+ *
267
+ * ```
268
+ * A: Path { /home/1user/.config }
269
+ * B: Path { user/.config }
270
+ * ```
271
+ *
272
+ * Path A does *not* end with Path B, because `"1user" !== "user"`.
273
+ */
274
+ endsWith(value) {
275
+ value = new Path(value);
276
+ const valueSegmentsReversed = [...value.segments].reverse();
277
+ const ownSegmentsReversed = [...this.segments].reverse();
278
+ return valueSegmentsReversed.every((segment, index) => ownSegmentsReversed[index] === segment);
279
+ }
280
+ /**
281
+ * Return the path segment index at which `value` appears in this path, or
282
+ * `-1` if it doesn't appear in this path.
283
+ *
284
+ * @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.
285
+ * @param fromIndex - The index into this path's segments to begin searching at. Defaults to `0`.
286
+ */
287
+ indexOf(value, fromIndex = 0) {
288
+ value = new Path(value);
289
+ const ownSegmentsLength = this.segments.length;
290
+ for (let i = fromIndex; i < ownSegmentsLength; i++) {
291
+ if (value.segments.every((valueSegment, valueIndex) => {
292
+ return this.segments[i + valueIndex] === valueSegment;
293
+ })) {
294
+ return i;
300
295
  }
301
296
  }
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);
297
+ return -1;
298
+ }
299
+ /**
300
+ * Return whether `value` appears in this path.
301
+ *
302
+ * @param value - The value to search for.
303
+ * @param fromIndex - The index into this path's segments to begin searching at. Defaults to `0`.
304
+ */
305
+ includes(value, fromIndex = 0) {
306
+ return this.indexOf(value, fromIndex) !== -1;
307
+ }
308
+ /**
309
+ * Return a new Path wherein the segments in `value` have been replaced with
310
+ * the segments in `replacement`. If the segments in `value` are not present
311
+ * in this path, a clone of this path is returned.
312
+ *
313
+ * Note that only the first match is replaced.
314
+ *
315
+ * @param value - What should be replaced
316
+ * @param replacement - What it should be replaced with
317
+ */
318
+ replace(value, replacement) {
319
+ value = new Path(value);
320
+ replacement = new Path(replacement);
321
+ const matchIndex = this.indexOf(value);
322
+ if (matchIndex === -1) {
323
+ return this.clone();
307
324
  }
308
325
  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));
326
+ const newSegments = [
327
+ ...this.segments.slice(0, matchIndex),
328
+ ...replacement.segments,
329
+ ...this.segments.slice(matchIndex + value.segments.length),
330
+ ];
331
+ return Path.fromRaw(newSegments, this.separator);
322
332
  }
323
- if (nextSegments[0] !== "..") {
324
- nextSegments = __spreadArrays(["."], nextSegments);
325
- }
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
333
  }
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++;
334
+ /**
335
+ * Return a new Path wherein all occurrences of the segments in `value` have
336
+ * been replaced with the segments in `replacement`. If the segments in
337
+ * `value` are not present in this path, a clone of this path is returned.
338
+ *
339
+ * @param value - What should be replaced
340
+ * @param replacement - What it should be replaced with
341
+ */
342
+ replaceAll(value, replacement) {
343
+ replacement = new Path(replacement);
344
+ let searchIndex = 0;
345
+ let currentPath = this;
346
+ const ownLength = this.segments.length;
347
+ while (searchIndex < ownLength) {
348
+ const matchingIndex = this.indexOf(value, searchIndex);
349
+ if (matchingIndex === -1) {
350
+ break;
403
351
  }
404
352
  else {
405
- break;
353
+ currentPath = currentPath.replace(value, replacement);
354
+ searchIndex = matchingIndex + replacement.segments.length;
406
355
  }
407
356
  }
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;
357
+ return currentPath;
435
358
  }
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;
359
+ /**
360
+ * Return a copy of this path but with the final segment replaced with `replacement`
361
+ *
362
+ * @param replacement - The new final segment(s) for the returned Path
363
+ */
364
+ replaceLast(replacement) {
365
+ replacement = new Path(replacement);
366
+ const segments = [...this.segments];
367
+ segments.pop();
368
+ segments.push(...replacement.segments);
369
+ return Path.fromRaw(segments, this.separator);
370
+ }
371
+ }
372
+ exports.Path = Path;