@alextheman/utility 5.0.0 → 5.1.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.
@@ -1,16 +1,51 @@
1
+ import { ExecaMethod } from "execa";
1
2
  import z from "zod";
2
3
 
3
- //#region src/internal/getExpectedTgzName.d.ts
4
- declare function getExpectedTgzName(packagePath: string, packageManager: string): Promise<string>;
5
- //#endregion
6
- //#region src/internal/getPackageJsonContents.d.ts
7
- declare function getPackageJsonContents(directory: string): Promise<Record<string, any> | null>;
4
+ //#region src/root/types/RecordKey.d.ts
5
+ /**
6
+ * Represents the native Record's possible key type.
7
+ *
8
+ * @category Types
9
+ */
10
+ type RecordKey = string | number | symbol;
8
11
  //#endregion
9
- //#region src/internal/getPackageJsonPath.d.ts
10
- declare function getPackageJsonPath(directory: string): string;
12
+ //#region src/root/types/DataError.d.ts
13
+ /**
14
+ * Represents errors you may get that may've been caused by a specific piece of data.
15
+ *
16
+ * @category Types
17
+ *
18
+ * @template DataType - The type of the data that caused the error.
19
+ */
20
+ declare class DataError<DataType extends Record<RecordKey, unknown> = Record<RecordKey, unknown>> extends Error {
21
+ code: string;
22
+ data: DataType;
23
+ /**
24
+ * @param data - The data that caused the error.
25
+ * @param code - A standardised code (e.g. UNEXPECTED_DATA).
26
+ * @param message - A human-readable error message (e.g. The data provided is invalid).
27
+ * @param options - Extra options to pass to super Error constructor.
28
+ */
29
+ constructor(data: DataType, code?: string, message?: string, options?: ErrorOptions);
30
+ /**
31
+ * Checks whether the given input may have been caused by a DataError.
32
+ *
33
+ * @param input - The input to check.
34
+ *
35
+ * @returns `true` if the input is a DataError, and `false` otherwise. The type of the input will also be narrowed down to DataError if `true`.
36
+ */
37
+ static check<DataType extends Record<RecordKey, unknown> = Record<RecordKey, unknown>>(input: unknown): input is DataError<DataType>;
38
+ }
11
39
  //#endregion
12
- //#region src/internal/parseJsonFromStdout.d.ts
13
- declare function parseJsonFromStdout(stdout: string): Record<string, unknown>;
40
+ //#region src/root/types/CreateEnumType.d.ts
41
+ /**
42
+ * Get the value types from a const object so the object can behave similarly to an enum.
43
+ *
44
+ * @category Types
45
+ *
46
+ * @template ObjectType - The type of the object to get the value types for.
47
+ */
48
+ type CreateEnumType<ObjectType extends Record<RecordKey, unknown>> = ObjectType[keyof ObjectType];
14
49
  //#endregion
15
50
  //#region src/root/types/IsTypeArgumentString.d.ts
16
51
  type IsTypeArgumentString<Argument extends string> = Argument;
@@ -25,4 +60,49 @@ type IsTypeArgumentString<Argument extends string> = Argument;
25
60
  */
26
61
  declare function sayHello(): string;
27
62
  //#endregion
28
- export { type IsTypeArgumentString, getExpectedTgzName, getPackageJsonContents, getPackageJsonPath, parseJsonFromStdout, sayHello };
63
+ //#region src/internal/DependencyGroup.d.ts
64
+ declare const DependencyGroup: {
65
+ readonly DEPENDENCIES: "dependencies";
66
+ readonly DEV_DEPENDENCIES: "devDependencies";
67
+ };
68
+ type DependencyGroup = CreateEnumType<typeof DependencyGroup>;
69
+ //#endregion
70
+ //#region src/internal/getExpectedTgzName.d.ts
71
+ declare function getExpectedTgzName(packagePath: string, packageManager: string): Promise<string>;
72
+ //#endregion
73
+ //#region src/internal/getPackageJsonContents.d.ts
74
+ declare function getPackageJsonContents(directory: string): Promise<Record<string, any> | null>;
75
+ //#endregion
76
+ //#region src/internal/getPackageJsonPath.d.ts
77
+ declare function getPackageJsonPath(directory: string): string;
78
+ //#endregion
79
+ //#region src/internal/ModuleType.d.ts
80
+ declare const ModuleType: {
81
+ readonly COMMON_JS: "commonjs";
82
+ readonly ES_MODULES: "module";
83
+ readonly TYPESCRIPT: "typescript";
84
+ };
85
+ type ModuleType = CreateEnumType<typeof ModuleType>;
86
+ //#endregion
87
+ //#region src/internal/packageJsonNotFoundError.d.ts
88
+ declare function packageJsonNotFoundError(packagePath: string): DataError;
89
+ //#endregion
90
+ //#region src/internal/PackageManager.d.ts
91
+ declare const PackageManager: {
92
+ readonly NPM: "npm";
93
+ readonly PNPM: "pnpm";
94
+ };
95
+ type PackageManager = CreateEnumType<typeof PackageManager>;
96
+ //#endregion
97
+ //#region src/internal/parseJsonFromStdout.d.ts
98
+ declare function parseJsonFromStdout(stdout: string): Record<string, unknown>;
99
+ //#endregion
100
+ //#region src/internal/setupPackageEndToEnd.d.ts
101
+ interface SetupPackageEndToEndOptions {
102
+ dependencyGroup?: DependencyGroup;
103
+ }
104
+ declare function setupPackageEndToEnd(temporaryPath: string, packageManager: PackageManager, moduleType: ModuleType, options?: SetupPackageEndToEndOptions): Promise<ExecaMethod<{
105
+ cwd: string;
106
+ }>>;
107
+ //#endregion
108
+ export { DependencyGroup, type IsTypeArgumentString, ModuleType, PackageManager, getExpectedTgzName, getPackageJsonContents, getPackageJsonPath, packageJsonNotFoundError, parseJsonFromStdout, sayHello, setupPackageEndToEnd };
@@ -1,9 +1,16 @@
1
1
  import { execa } from "execa";
2
2
  import z from "zod";
3
- import path from "node:path";
4
3
  import "libsodium-wrappers";
5
- import { readFile } from "node:fs/promises";
4
+ import { readFile, writeFile } from "node:fs/promises";
5
+ import path from "node:path";
6
6
 
7
+ //#region src/internal/DependencyGroup.ts
8
+ const DependencyGroup = {
9
+ DEPENDENCIES: "dependencies",
10
+ DEV_DEPENDENCIES: "devDependencies"
11
+ };
12
+
13
+ //#endregion
7
14
  //#region src/root/functions/arrayHelpers/fillArray.ts
8
15
  /**
9
16
  * Creates a new array where each element is the result of the provided callback.
@@ -596,6 +603,27 @@ async function getPackageJsonContents(directory) {
596
603
  }
597
604
  }
598
605
 
606
+ //#endregion
607
+ //#region src/internal/ModuleType.ts
608
+ const ModuleType = {
609
+ COMMON_JS: "commonjs",
610
+ ES_MODULES: "module",
611
+ TYPESCRIPT: "typescript"
612
+ };
613
+
614
+ //#endregion
615
+ //#region src/internal/packageJsonNotFoundError.ts
616
+ function packageJsonNotFoundError(packagePath) {
617
+ return new DataError({ packagePath: getPackageJsonPath(packagePath) }, "PACKAGE_JSON_NOT_FOUND", "Could not find package.json in directory.");
618
+ }
619
+
620
+ //#endregion
621
+ //#region src/internal/PackageManager.ts
622
+ const PackageManager = {
623
+ NPM: "npm",
624
+ PNPM: "pnpm"
625
+ };
626
+
599
627
  //#endregion
600
628
  //#region src/internal/parseJsonFromStdout.ts
601
629
  function parseJsonFromStdout(stdout) {
@@ -613,4 +641,21 @@ function parseJsonFromStdout(stdout) {
613
641
  var sayHello_default = sayHello;
614
642
 
615
643
  //#endregion
616
- export { getExpectedTgzName, getPackageJsonContents, getPackageJsonPath, parseJsonFromStdout, sayHello_default as sayHello };
644
+ //#region src/internal/setupPackageEndToEnd.ts
645
+ async function setupPackageEndToEnd(temporaryPath, packageManager, moduleType, options) {
646
+ const { dependencyGroup = "dependencies" } = options ?? {};
647
+ await execa({ cwd: process.cwd() })`${packageManager} pack --pack-destination ${temporaryPath}`;
648
+ const tgzFileName = await getExpectedTgzName(process.cwd(), packageManager);
649
+ const runCommandInTempDirectory = execa({ cwd: temporaryPath });
650
+ if (packageManager === PackageManager.NPM) await runCommandInTempDirectory`npm init -y`;
651
+ else await runCommandInTempDirectory`pnpm init`;
652
+ const packageInfo = await getPackageJsonContents(temporaryPath);
653
+ if (packageInfo === null) throw packageJsonNotFoundError(temporaryPath);
654
+ packageInfo.type = moduleType === ModuleType.TYPESCRIPT ? ModuleType.ES_MODULES : moduleType;
655
+ await writeFile(path.join(temporaryPath, "package.json"), JSON.stringify(packageInfo, null, 2));
656
+ await runCommandInTempDirectory`${packageManager} install ${dependencyGroup === "devDependencies" ? "--save-dev" : "--save-prod"} ${path.join(temporaryPath, tgzFileName)}`;
657
+ return runCommandInTempDirectory;
658
+ }
659
+
660
+ //#endregion
661
+ export { DependencyGroup, ModuleType, PackageManager, getExpectedTgzName, getPackageJsonContents, getPackageJsonPath, packageJsonNotFoundError, parseJsonFromStdout, sayHello_default as sayHello, setupPackageEndToEnd };
@@ -76,6 +76,47 @@ const FILE_PATH_REGEX = String.raw`^(?<directory>.+)[\/\\](?<base>[^\/\\]+)$`;
76
76
  //#region src/root/constants/VERSION_NUMBER_REGEX.ts
77
77
  const VERSION_NUMBER_REGEX = "^(?:v)?(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$";
78
78
 
79
+ //#endregion
80
+ //#region src/root/types/DataError.ts
81
+ /**
82
+ * Represents errors you may get that may've been caused by a specific piece of data.
83
+ *
84
+ * @category Types
85
+ *
86
+ * @template DataType - The type of the data that caused the error.
87
+ */
88
+ var DataError = class DataError extends Error {
89
+ code;
90
+ data;
91
+ /**
92
+ * @param data - The data that caused the error.
93
+ * @param code - A standardised code (e.g. UNEXPECTED_DATA).
94
+ * @param message - A human-readable error message (e.g. The data provided is invalid).
95
+ * @param options - Extra options to pass to super Error constructor.
96
+ */
97
+ constructor(data, code = "INVALID_DATA", message = "The data provided is invalid", options) {
98
+ super(message, options);
99
+ if (Error.captureStackTrace) Error.captureStackTrace(this, new.target);
100
+ this.name = new.target.name;
101
+ this.code = code;
102
+ this.data = data;
103
+ Object.defineProperty(this, "message", { enumerable: true });
104
+ Object.setPrototypeOf(this, new.target.prototype);
105
+ }
106
+ /**
107
+ * Checks whether the given input may have been caused by a DataError.
108
+ *
109
+ * @param input - The input to check.
110
+ *
111
+ * @returns `true` if the input is a DataError, and `false` otherwise. The type of the input will also be narrowed down to DataError if `true`.
112
+ */
113
+ static check(input) {
114
+ if (input instanceof DataError) return true;
115
+ const data = input;
116
+ return typeof data === "object" && data !== null && typeof data.message === "string" && typeof data.code === "string" && "data" in data;
117
+ }
118
+ };
119
+
79
120
  //#endregion
80
121
  //#region src/root/functions/arrayHelpers/fillArray.ts
81
122
  /**
@@ -127,212 +168,6 @@ function paralleliseArrays(firstArray, secondArray) {
127
168
  return outputArray;
128
169
  }
129
170
 
130
- //#endregion
131
- //#region src/root/types/DataError.ts
132
- /**
133
- * Represents errors you may get that may've been caused by a specific piece of data.
134
- *
135
- * @category Types
136
- *
137
- * @template DataType - The type of the data that caused the error.
138
- */
139
- var DataError = class DataError extends Error {
140
- code;
141
- data;
142
- /**
143
- * @param data - The data that caused the error.
144
- * @param code - A standardised code (e.g. UNEXPECTED_DATA).
145
- * @param message - A human-readable error message (e.g. The data provided is invalid).
146
- * @param options - Extra options to pass to super Error constructor.
147
- */
148
- constructor(data, code = "INVALID_DATA", message = "The data provided is invalid", options) {
149
- super(message, options);
150
- if (Error.captureStackTrace) Error.captureStackTrace(this, new.target);
151
- this.name = new.target.name;
152
- this.code = code;
153
- this.data = data;
154
- Object.defineProperty(this, "message", { enumerable: true });
155
- Object.setPrototypeOf(this, new.target.prototype);
156
- }
157
- /**
158
- * Checks whether the given input may have been caused by a DataError.
159
- *
160
- * @param input - The input to check.
161
- *
162
- * @returns `true` if the input is a DataError, and `false` otherwise. The type of the input will also be narrowed down to DataError if `true`.
163
- */
164
- static check(input) {
165
- if (input instanceof DataError) return true;
166
- const data = input;
167
- return typeof data === "object" && data !== null && typeof data.message === "string" && typeof data.code === "string" && "data" in data;
168
- }
169
- };
170
-
171
- //#endregion
172
- //#region src/root/types/VersionNumber.ts
173
- /**
174
- * Represents a software version number, considered to be made up of a major, minor, and patch part.
175
- *
176
- * @category Types
177
- */
178
- var VersionNumber = class VersionNumber {
179
- static NON_NEGATIVE_TUPLE_ERROR = "Input array must be a tuple of three non-negative integers.";
180
- /** The major number. Increments when a feature is removed or changed in a way that is not backwards-compatible with the previous release. */
181
- major = 0;
182
- /** The minor number. Increments when a new feature is added/deprecated and is expected to be backwards-compatible with the previous release. */
183
- minor = 0;
184
- /** The patch number. Increments when the next release is fixing a bug or doing a small refactor that should not be noticeable in practice. */
185
- patch = 0;
186
- /**
187
- * @param input - The input to create a new instance of `VersionNumber` from.
188
- */
189
- constructor(input) {
190
- if (input instanceof VersionNumber) {
191
- this.major = input.major;
192
- this.minor = input.minor;
193
- this.patch = input.patch;
194
- } else if (typeof input === "string") {
195
- if (!RegExp(VERSION_NUMBER_REGEX).test(input)) throw new DataError({ input }, "INVALID_VERSION", `"${input}" is not a valid version number. Version numbers must be of the format "X.Y.Z" or "vX.Y.Z", where X, Y, and Z are non-negative integers.`);
196
- const [major, minor, patch] = VersionNumber.formatString(input, { omitPrefix: true }).split(".").map((number) => {
197
- return parseIntStrict(number);
198
- });
199
- this.major = major;
200
- this.minor = minor;
201
- this.patch = patch;
202
- } else if (Array.isArray(input)) {
203
- if (input.length !== 3) throw new DataError({ input }, "INVALID_LENGTH", VersionNumber.NON_NEGATIVE_TUPLE_ERROR);
204
- const [major, minor, patch] = input.map((number) => {
205
- const parsedInteger = parseIntStrict(number?.toString());
206
- if (parsedInteger < 0) throw new DataError({ input }, "NEGATIVE_INPUTS", VersionNumber.NON_NEGATIVE_TUPLE_ERROR);
207
- return parsedInteger;
208
- });
209
- this.major = major;
210
- this.minor = minor;
211
- this.patch = patch;
212
- }
213
- }
214
- /**
215
- * Gets the current version type of the current instance of `VersionNumber`.
216
- *
217
- * @returns Either `"major"`, `"minor"`, or `"patch"`, depending on the version type.
218
- */
219
- get type() {
220
- if (this.minor === 0 && this.patch === 0) return VersionType.MAJOR;
221
- if (this.patch === 0) return VersionType.MINOR;
222
- return VersionType.PATCH;
223
- }
224
- static formatString(input, options) {
225
- if (options?.omitPrefix) return input.startsWith("v") ? input.slice(1) : input;
226
- return input.startsWith("v") ? input : `v${input}`;
227
- }
228
- /**
229
- * Checks if the provided version numbers have the exact same major, minor, and patch numbers.
230
- *
231
- * @param firstVersion - The first version number to compare.
232
- * @param secondVersion - The second version number to compare.
233
- *
234
- * @returns `true` if the provided version numbers have exactly the same major, minor, and patch numbers, and returns `false` otherwise.
235
- */
236
- static isEqual(firstVersion, secondVersion) {
237
- return firstVersion.major === secondVersion.major && firstVersion.minor === secondVersion.minor && firstVersion.patch === secondVersion.patch;
238
- }
239
- /**
240
- * Get a formatted string representation of the current version number
241
- *
242
- * @param options - Options to apply to the string formatting.
243
- *
244
- * @returns A formatted string representation of the current version number with the options applied.
245
- */
246
- format(options) {
247
- let baseOutput = `${this.major}`;
248
- if (!options?.omitMinor) {
249
- baseOutput += `.${this.minor}`;
250
- if (!options?.omitPatch) baseOutput += `.${this.patch}`;
251
- }
252
- return VersionNumber.formatString(baseOutput, { omitPrefix: options?.omitPrefix });
253
- }
254
- /**
255
- * Increments the current version number by the given increment type, returning the result as a new reference in memory.
256
- *
257
- * @param incrementType - The type of increment. Can be one of the following:
258
- * - `"major"`: Change the major version `v1.2.3` → `v2.0.0`
259
- * - `"minor"`: Change the minor version `v1.2.3` → `v1.3.0`
260
- * - `"patch"`: Change the patch version `v1.2.3` → `v1.2.4`
261
- * @param incrementAmount - The amount to increment by (defaults to 1).
262
- *
263
- * @returns A new instance of `VersionNumber` with the increment applied.
264
- */
265
- increment(incrementType, incrementAmount = 1) {
266
- const incrementBy = parseIntStrict(String(incrementAmount));
267
- const calculatedRawVersion = {
268
- major: [
269
- this.major + incrementBy,
270
- 0,
271
- 0
272
- ],
273
- minor: [
274
- this.major,
275
- this.minor + incrementBy,
276
- 0
277
- ],
278
- patch: [
279
- this.major,
280
- this.minor,
281
- this.patch + incrementBy
282
- ]
283
- }[incrementType];
284
- try {
285
- return new VersionNumber(calculatedRawVersion);
286
- } catch (error) {
287
- if (DataError.check(error) && error.code === "NEGATIVE_INPUTS") throw new DataError({
288
- currentVersion: this.toString(),
289
- calculatedRawVersion: `v${calculatedRawVersion.join(".")}`,
290
- incrementAmount
291
- }, "NEGATIVE_VERSION", "Cannot apply this increment amount as it would lead to a negative version number.");
292
- else throw error;
293
- }
294
- }
295
- /**
296
- * Ensures that the VersionNumber behaves correctly when attempted to be coerced to a string.
297
- *
298
- * @param hint - Not used as of now, but generally used to help with numeric coercion, I think (which we most likely do not need for version numbers).
299
- *
300
- * @returns A stringified representation of the current version number, prefixed with `v`.
301
- */
302
- [Symbol.toPrimitive](hint) {
303
- if (hint === "number") throw new DataError({ thisVersion: this.toString() }, "INVALID_COERCION", "VersionNumber cannot be coerced to a number type.");
304
- return this.toString();
305
- }
306
- /**
307
- * Ensures that the VersionNumber behaves correctly when attempted to be converted to JSON.
308
- *
309
- * @returns A stringified representation of the current version number, prefixed with `v`.
310
- */
311
- toJSON() {
312
- return this.toString();
313
- }
314
- /**
315
- * Get a string representation of the current version number.
316
- *
317
- * @returns A stringified representation of the current version number with the prefix.
318
- */
319
- toString() {
320
- const rawString = `${this.major}.${this.minor}.${this.patch}`;
321
- return VersionNumber.formatString(rawString, { omitPrefix: false });
322
- }
323
- };
324
- const zodVersionNumber = zod.default.union([
325
- zod.default.string(),
326
- zod.default.tuple([
327
- zod.default.number(),
328
- zod.default.number(),
329
- zod.default.number()
330
- ]),
331
- zod.default.instanceof(VersionNumber)
332
- ]).transform((rawVersionNumber) => {
333
- return new VersionNumber(rawVersionNumber);
334
- });
335
-
336
171
  //#endregion
337
172
  //#region src/root/functions/parsers/parseIntStrict.ts
338
173
  /**
@@ -584,6 +419,202 @@ const VersionType = {
584
419
  PATCH: "patch"
585
420
  };
586
421
 
422
+ //#endregion
423
+ //#region src/root/types/VersionNumber.ts
424
+ /**
425
+ * Represents a software version number, considered to be made up of a major, minor, and patch part.
426
+ *
427
+ * @category Types
428
+ */
429
+ var VersionNumber = class VersionNumber {
430
+ static NON_NEGATIVE_TUPLE_ERROR = "Input array must be a tuple of three non-negative integers.";
431
+ /** The major number. Increments when a feature is removed or changed in a way that is not backwards-compatible with the previous release. */
432
+ major = 0;
433
+ /** The minor number. Increments when a new feature is added/deprecated and is expected to be backwards-compatible with the previous release. */
434
+ minor = 0;
435
+ /** The patch number. Increments when the next release is fixing a bug or doing a small refactor that should not be noticeable in practice. */
436
+ patch = 0;
437
+ /**
438
+ * @param input - The input to create a new instance of `VersionNumber` from.
439
+ */
440
+ constructor(input) {
441
+ if (input instanceof VersionNumber) {
442
+ this.major = input.major;
443
+ this.minor = input.minor;
444
+ this.patch = input.patch;
445
+ } else if (typeof input === "string") {
446
+ if (!RegExp(VERSION_NUMBER_REGEX).test(input)) throw new DataError({ input }, "INVALID_VERSION", `"${input}" is not a valid version number. Version numbers must be of the format "X.Y.Z" or "vX.Y.Z", where X, Y, and Z are non-negative integers.`);
447
+ const [major, minor, patch] = VersionNumber.formatString(input, { omitPrefix: true }).split(".").map((number) => {
448
+ return parseIntStrict(number);
449
+ });
450
+ this.major = major;
451
+ this.minor = minor;
452
+ this.patch = patch;
453
+ } else if (Array.isArray(input)) {
454
+ if (input.length !== 3) throw new DataError({ input }, "INVALID_LENGTH", VersionNumber.NON_NEGATIVE_TUPLE_ERROR);
455
+ const [major, minor, patch] = input.map((number) => {
456
+ const parsedInteger = parseIntStrict(number?.toString());
457
+ if (parsedInteger < 0) throw new DataError({ input }, "NEGATIVE_INPUTS", VersionNumber.NON_NEGATIVE_TUPLE_ERROR);
458
+ return parsedInteger;
459
+ });
460
+ this.major = major;
461
+ this.minor = minor;
462
+ this.patch = patch;
463
+ }
464
+ }
465
+ /**
466
+ * Gets the current version type of the current instance of `VersionNumber`.
467
+ *
468
+ * @returns Either `"major"`, `"minor"`, or `"patch"`, depending on the version type.
469
+ */
470
+ get type() {
471
+ if (this.minor === 0 && this.patch === 0) return VersionType.MAJOR;
472
+ if (this.patch === 0) return VersionType.MINOR;
473
+ return VersionType.PATCH;
474
+ }
475
+ static formatString(input, options) {
476
+ if (options?.omitPrefix) return input.startsWith("v") ? input.slice(1) : input;
477
+ return input.startsWith("v") ? input : `v${input}`;
478
+ }
479
+ /**
480
+ * Checks if the provided version numbers have the exact same major, minor, and patch numbers.
481
+ *
482
+ * @param firstVersion - The first version number to compare.
483
+ * @param secondVersion - The second version number to compare.
484
+ *
485
+ * @returns `true` if the provided version numbers have exactly the same major, minor, and patch numbers, and returns `false` otherwise.
486
+ */
487
+ static isEqual(firstVersion, secondVersion) {
488
+ return firstVersion.major === secondVersion.major && firstVersion.minor === secondVersion.minor && firstVersion.patch === secondVersion.patch;
489
+ }
490
+ /**
491
+ * Get a formatted string representation of the current version number
492
+ *
493
+ * @param options - Options to apply to the string formatting.
494
+ *
495
+ * @returns A formatted string representation of the current version number with the options applied.
496
+ */
497
+ format(options) {
498
+ let baseOutput = `${this.major}`;
499
+ if (!options?.omitMinor) {
500
+ baseOutput += `.${this.minor}`;
501
+ if (!options?.omitPatch) baseOutput += `.${this.patch}`;
502
+ }
503
+ return VersionNumber.formatString(baseOutput, { omitPrefix: options?.omitPrefix });
504
+ }
505
+ /**
506
+ * Increments the current version number by the given increment type, returning the result as a new reference in memory.
507
+ *
508
+ * @param incrementType - The type of increment. Can be one of the following:
509
+ * - `"major"`: Change the major version `v1.2.3` → `v2.0.0`
510
+ * - `"minor"`: Change the minor version `v1.2.3` → `v1.3.0`
511
+ * - `"patch"`: Change the patch version `v1.2.3` → `v1.2.4`
512
+ * @param incrementAmount - The amount to increment by (defaults to 1).
513
+ *
514
+ * @returns A new instance of `VersionNumber` with the increment applied.
515
+ */
516
+ increment(incrementType, incrementAmount = 1) {
517
+ const incrementBy = parseIntStrict(String(incrementAmount));
518
+ const calculatedRawVersion = {
519
+ major: [
520
+ this.major + incrementBy,
521
+ 0,
522
+ 0
523
+ ],
524
+ minor: [
525
+ this.major,
526
+ this.minor + incrementBy,
527
+ 0
528
+ ],
529
+ patch: [
530
+ this.major,
531
+ this.minor,
532
+ this.patch + incrementBy
533
+ ]
534
+ }[incrementType];
535
+ try {
536
+ return new VersionNumber(calculatedRawVersion);
537
+ } catch (error) {
538
+ if (DataError.check(error) && error.code === "NEGATIVE_INPUTS") throw new DataError({
539
+ currentVersion: this.toString(),
540
+ calculatedRawVersion: `v${calculatedRawVersion.join(".")}`,
541
+ incrementAmount
542
+ }, "NEGATIVE_VERSION", "Cannot apply this increment amount as it would lead to a negative version number.");
543
+ else throw error;
544
+ }
545
+ }
546
+ /**
547
+ * Ensures that the VersionNumber behaves correctly when attempted to be coerced to a string.
548
+ *
549
+ * @param hint - Not used as of now, but generally used to help with numeric coercion, I think (which we most likely do not need for version numbers).
550
+ *
551
+ * @returns A stringified representation of the current version number, prefixed with `v`.
552
+ */
553
+ [Symbol.toPrimitive](hint) {
554
+ if (hint === "number") throw new DataError({ thisVersion: this.toString() }, "INVALID_COERCION", "VersionNumber cannot be coerced to a number type.");
555
+ return this.toString();
556
+ }
557
+ /**
558
+ * Ensures that the VersionNumber behaves correctly when attempted to be converted to JSON.
559
+ *
560
+ * @returns A stringified representation of the current version number, prefixed with `v`.
561
+ */
562
+ toJSON() {
563
+ return this.toString();
564
+ }
565
+ /**
566
+ * Get a string representation of the current version number.
567
+ *
568
+ * @returns A stringified representation of the current version number with the prefix.
569
+ */
570
+ toString() {
571
+ const rawString = `${this.major}.${this.minor}.${this.patch}`;
572
+ return VersionNumber.formatString(rawString, { omitPrefix: false });
573
+ }
574
+ };
575
+ const zodVersionNumber = zod.default.union([
576
+ zod.default.string(),
577
+ zod.default.tuple([
578
+ zod.default.number(),
579
+ zod.default.number(),
580
+ zod.default.number()
581
+ ]),
582
+ zod.default.instanceof(VersionNumber)
583
+ ]).transform((rawVersionNumber) => {
584
+ return new VersionNumber(rawVersionNumber);
585
+ });
586
+
587
+ //#endregion
588
+ //#region src/node/functions/parseFilePath.ts
589
+ /**
590
+ * Takes a file path string and parses it into the directory part, the base part, and the full path.
591
+ *
592
+ * @category Parsers
593
+ *
594
+ * @param filePath - The file path to parse.
595
+ *
596
+ * @throws {DataError} If the file path is invalid.
597
+ *
598
+ * @returns An object representing the different ways the file path can be represented.
599
+ */
600
+ function parseFilePath(filePath) {
601
+ const caughtGroups = filePath.match(RegExp(FILE_PATH_REGEX));
602
+ if (!caughtGroups) {
603
+ if (!(filePath.includes("/") || filePath.includes("\\")) && filePath.includes(".")) return {
604
+ directory: "",
605
+ base: filePath,
606
+ fullPath: filePath
607
+ };
608
+ throw new DataError({ filePath }, "INVALID_FILE_PATH", "The file path you provided is not valid.");
609
+ }
610
+ if (!caughtGroups.groups) throw new DataError({ filePath }, "PARSING_ERROR", "An error occurred while trying to parse the data.");
611
+ return {
612
+ directory: caughtGroups.groups.directory,
613
+ base: caughtGroups.groups.base,
614
+ fullPath: node_path.default.join(caughtGroups.groups.directory.replaceAll("\\", "/"), caughtGroups.groups.base)
615
+ };
616
+ }
617
+
587
618
  //#endregion
588
619
  //#region src/node/functions/sayHello.ts
589
620
  var sayHello_default = sayHello;
@@ -591,4 +622,5 @@ var sayHello_default = sayHello;
591
622
  //#endregion
592
623
  exports.normaliseImportPath = normaliseImportPath;
593
624
  exports.normalizeImportPath = normalizeImportPath;
625
+ exports.parseFilePath = parseFilePath;
594
626
  exports.sayHello = sayHello_default;
@@ -30,6 +30,28 @@ declare function normalizeImportPath(importPath: string): string;
30
30
  */
31
31
  declare const normaliseImportPath: typeof normalizeImportPath;
32
32
  //#endregion
33
+ //#region src/node/functions/parseFilePath.d.ts
34
+ interface FilePathData {
35
+ /** The file path without the final part. */
36
+ directory: string;
37
+ /** The final part of the file path. */
38
+ base: string;
39
+ /** The full file path, normalised. */
40
+ fullPath: string;
41
+ }
42
+ /**
43
+ * Takes a file path string and parses it into the directory part, the base part, and the full path.
44
+ *
45
+ * @category Parsers
46
+ *
47
+ * @param filePath - The file path to parse.
48
+ *
49
+ * @throws {DataError} If the file path is invalid.
50
+ *
51
+ * @returns An object representing the different ways the file path can be represented.
52
+ */
53
+ declare function parseFilePath(filePath: string): FilePathData;
54
+ //#endregion
33
55
  //#region src/root/types/IsTypeArgumentString.d.ts
34
56
  type IsTypeArgumentString<Argument extends string> = Argument;
35
57
  //#endregion
@@ -43,4 +65,4 @@ type IsTypeArgumentString<Argument extends string> = Argument;
43
65
  */
44
66
  declare function sayHello(): string;
45
67
  //#endregion
46
- export { IsTypeArgumentString, normaliseImportPath, normalizeImportPath, sayHello };
68
+ export { IsTypeArgumentString, normaliseImportPath, normalizeImportPath, parseFilePath, sayHello };