@serenity-js/core 3.18.1 → 3.20.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/CHANGELOG.md +19 -0
- package/lib/io/FileSystem.d.ts +26 -9
- package/lib/io/FileSystem.d.ts.map +1 -1
- package/lib/io/FileSystem.js +42 -48
- package/lib/io/FileSystem.js.map +1 -1
- package/lib/io/Version.d.ts +24 -0
- package/lib/io/Version.d.ts.map +1 -0
- package/lib/io/Version.js +58 -0
- package/lib/io/Version.js.map +1 -0
- package/lib/io/index.d.ts +1 -0
- package/lib/io/index.d.ts.map +1 -1
- package/lib/io/index.js +1 -0
- package/lib/io/index.js.map +1 -1
- package/lib/io/loader/ModuleLoader.d.ts +8 -3
- package/lib/io/loader/ModuleLoader.d.ts.map +1 -1
- package/lib/io/loader/ModuleLoader.js +16 -5
- package/lib/io/loader/ModuleLoader.js.map +1 -1
- package/lib/io/loader/index.d.ts +0 -1
- package/lib/io/loader/index.d.ts.map +1 -1
- package/lib/io/loader/index.js +0 -1
- package/lib/io/loader/index.js.map +1 -1
- package/lib/screenplay/abilities/Ability.d.ts +1 -1
- package/lib/screenplay/abilities/Ability.js +1 -1
- package/package.json +3 -3
- package/src/io/FileSystem.ts +65 -81
- package/src/io/Version.ts +61 -0
- package/src/io/index.ts +1 -0
- package/src/io/loader/ModuleLoader.ts +19 -5
- package/src/io/loader/index.ts +0 -1
- package/src/screenplay/abilities/Ability.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [3.20.0](https://github.com/serenity-js/serenity-js/compare/v3.19.0...v3.20.0) (2024-03-02)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **core:** simplified reading and writing files in CrewMembers using the FileSystem ([7f0d0cc](https://github.com/serenity-js/serenity-js/commit/7f0d0cc6de675a526a6e9351fe94055501d87e2c)), closes [#2244](https://github.com/serenity-js/serenity-js/issues/2244)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# [3.19.0](https://github.com/serenity-js/serenity-js/compare/v3.18.1...v3.19.0) (2024-03-01)
|
|
18
|
+
|
|
19
|
+
**Note:** Version bump only for package @serenity-js/core
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
6
25
|
## [3.18.1](https://github.com/serenity-js/serenity-js/compare/v3.18.0...v3.18.1) (2024-02-23)
|
|
7
26
|
|
|
8
27
|
**Note:** Version bump only for package @serenity-js/core
|
package/lib/io/FileSystem.d.ts
CHANGED
|
@@ -1,26 +1,43 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
/// <reference types="node" />
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
import * as
|
|
4
|
+
/// <reference types="node" />
|
|
5
|
+
import * as nodeOS from 'node:os';
|
|
6
|
+
import type * as NodeFS from 'fs';
|
|
7
7
|
import { Path } from './Path';
|
|
8
8
|
export declare class FileSystem {
|
|
9
9
|
private readonly root;
|
|
10
10
|
private readonly fs;
|
|
11
11
|
private readonly os;
|
|
12
12
|
private readonly directoryMode;
|
|
13
|
-
constructor(root: Path, fs?: typeof
|
|
13
|
+
constructor(root: Path, fs?: typeof NodeFS, os?: typeof nodeOS, directoryMode?: number);
|
|
14
14
|
resolve(relativeOrAbsolutePath: Path): Path;
|
|
15
|
-
store(relativeOrAbsolutePathToFile: Path, data: string | NodeJS.ArrayBufferView, encoding?: WriteFileOptions): Promise<Path>;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
store(relativeOrAbsolutePathToFile: Path, data: string | NodeJS.ArrayBufferView, encoding?: NodeFS.WriteFileOptions): Promise<Path>;
|
|
16
|
+
readFile(relativeOrAbsolutePathToFile: Path, options?: {
|
|
17
|
+
encoding?: null | undefined;
|
|
18
|
+
flag?: string | undefined;
|
|
19
|
+
}): Promise<Buffer>;
|
|
20
|
+
readFile(relativeOrAbsolutePathToFile: Path, options: {
|
|
21
|
+
encoding: BufferEncoding;
|
|
22
|
+
flag?: string | undefined;
|
|
23
|
+
} | NodeJS.BufferEncoding): Promise<string>;
|
|
24
|
+
readFileSync(relativeOrAbsolutePathToFile: Path, options?: {
|
|
25
|
+
encoding?: null | undefined;
|
|
26
|
+
flag?: string | undefined;
|
|
27
|
+
}): Buffer;
|
|
28
|
+
readFileSync(relativeOrAbsolutePathToFile: Path, options: {
|
|
29
|
+
encoding: BufferEncoding;
|
|
30
|
+
flag?: string | undefined;
|
|
31
|
+
} | NodeJS.BufferEncoding): string;
|
|
32
|
+
writeFile(relativeOrAbsolutePathToFile: Path, data: string | NodeJS.ArrayBufferView, options?: NodeFS.WriteFileOptions): Promise<Path>;
|
|
33
|
+
writeFileSync(relativeOrAbsolutePathToFile: Path, data: string | NodeJS.ArrayBufferView, options?: NodeFS.WriteFileOptions): Path;
|
|
34
|
+
createReadStream(relativeOrAbsolutePathToFile: Path): NodeFS.ReadStream;
|
|
35
|
+
createWriteStreamTo(relativeOrAbsolutePathToFile: Path): NodeFS.WriteStream;
|
|
36
|
+
stat(relativeOrAbsolutePathToFile: Path): Promise<NodeFS.Stats>;
|
|
19
37
|
exists(relativeOrAbsolutePathToFile: Path): boolean;
|
|
20
38
|
remove(relativeOrAbsolutePathToFileOrDirectory: Path): Promise<void>;
|
|
21
39
|
ensureDirectoryExistsAt(relativeOrAbsolutePathToDirectory: Path): Promise<Path>;
|
|
22
40
|
rename(source: Path, destination: Path): Promise<void>;
|
|
23
41
|
tempFilePath(prefix?: string, suffix?: string): Path;
|
|
24
|
-
private write;
|
|
25
42
|
}
|
|
26
43
|
//# sourceMappingURL=FileSystem.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileSystem.d.ts","sourceRoot":"","sources":["../../src/io/FileSystem.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FileSystem.d.ts","sourceRoot":"","sources":["../../src/io/FileSystem.ts"],"names":[],"mappings":";;;;AAAA,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC;AAGlC,OAAO,KAAK,KAAK,MAAM,MAAM,IAAI,CAAC;AAGlC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,qBAAa,UAAU;IAGf,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,aAAa;gBAHb,IAAI,EAAE,IAAI,EACV,EAAE,GAAE,OAAO,MAAmB,EAC9B,EAAE,GAAE,OAAO,MAAe,EAC1B,aAAa,SAAkD;IAI7E,OAAO,CAAC,sBAAsB,EAAE,IAAI,GAAG,IAAI;IAIrC,KAAK,CAAC,4BAA4B,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,eAAe,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKzI,QAAQ,CAAC,4BAA4B,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IACpI,QAAQ,CAAC,4BAA4B,EAAE,IAAI,EAAE,OAAO,EAAE;QAAE,QAAQ,EAAE,cAAc,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAAE,GAAG,MAAM,CAAC,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAKxJ,YAAY,CAAC,4BAA4B,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAAE,GAAG,MAAM;IAC/H,YAAY,CAAC,4BAA4B,EAAE,IAAI,EAAE,OAAO,EAAE;QAAE,QAAQ,EAAE,cAAc,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAAE,GAAG,MAAM,CAAC,cAAc,GAAG,MAAM;IAK7I,SAAS,CAAC,4BAA4B,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,eAAe,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAO5I,aAAa,CAAC,4BAA4B,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,eAAe,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,gBAAgB,GAAG,IAAI;IAOjI,gBAAgB,CAAC,4BAA4B,EAAE,IAAI,GAAG,MAAM,CAAC,UAAU;IAIvE,mBAAmB,CAAC,4BAA4B,EAAE,IAAI,GAAG,MAAM,CAAC,WAAW;IAI3E,IAAI,CAAC,4BAA4B,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;IAI/D,MAAM,CAAC,4BAA4B,EAAE,IAAI,GAAG,OAAO;IAI7C,MAAM,CAAC,uCAAuC,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BpE,uBAAuB,CAAC,iCAAiC,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IASrF,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD,YAAY,CAAC,MAAM,SAAK,EAAE,MAAM,SAAS,GAAG,IAAI;CAG1D"}
|
package/lib/io/FileSystem.js
CHANGED
|
@@ -24,10 +24,9 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
26
|
exports.FileSystem = void 0;
|
|
27
|
+
const nodeOS = __importStar(require("node:os"));
|
|
27
28
|
const cuid2_1 = require("@paralleldrive/cuid2");
|
|
28
29
|
const gracefulFS = __importStar(require("graceful-fs"));
|
|
29
|
-
const nodeOS = __importStar(require("os"));
|
|
30
|
-
const util_1 = require("util");
|
|
31
30
|
const Path_1 = require("./Path");
|
|
32
31
|
class FileSystem {
|
|
33
32
|
root;
|
|
@@ -43,10 +42,25 @@ class FileSystem {
|
|
|
43
42
|
resolve(relativeOrAbsolutePath) {
|
|
44
43
|
return this.root.resolve(relativeOrAbsolutePath);
|
|
45
44
|
}
|
|
46
|
-
store(relativeOrAbsolutePathToFile, data, encoding) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
async store(relativeOrAbsolutePathToFile, data, encoding) {
|
|
46
|
+
await this.ensureDirectoryExistsAt(relativeOrAbsolutePathToFile.directory());
|
|
47
|
+
return this.writeFile(relativeOrAbsolutePathToFile, data, encoding);
|
|
48
|
+
}
|
|
49
|
+
readFile(relativeOrAbsolutePathToFile, options) {
|
|
50
|
+
return this.fs.promises.readFile(this.resolve(relativeOrAbsolutePathToFile).value, options);
|
|
51
|
+
}
|
|
52
|
+
readFileSync(relativeOrAbsolutePathToFile, options) {
|
|
53
|
+
return this.fs.readFileSync(this.resolve(relativeOrAbsolutePathToFile).value, options);
|
|
54
|
+
}
|
|
55
|
+
async writeFile(relativeOrAbsolutePathToFile, data, options) {
|
|
56
|
+
const resolvedPath = this.resolve(relativeOrAbsolutePathToFile);
|
|
57
|
+
await this.fs.promises.writeFile(resolvedPath.value, data, options);
|
|
58
|
+
return resolvedPath;
|
|
59
|
+
}
|
|
60
|
+
writeFileSync(relativeOrAbsolutePathToFile, data, options) {
|
|
61
|
+
const resolvedPath = this.resolve(relativeOrAbsolutePathToFile);
|
|
62
|
+
this.fs.writeFileSync(resolvedPath.value, data, options);
|
|
63
|
+
return resolvedPath;
|
|
50
64
|
}
|
|
51
65
|
createReadStream(relativeOrAbsolutePathToFile) {
|
|
52
66
|
return this.fs.createReadStream(this.resolve(relativeOrAbsolutePathToFile).value);
|
|
@@ -55,64 +69,44 @@ class FileSystem {
|
|
|
55
69
|
return this.fs.createWriteStream(this.resolve(relativeOrAbsolutePathToFile).value);
|
|
56
70
|
}
|
|
57
71
|
stat(relativeOrAbsolutePathToFile) {
|
|
58
|
-
|
|
59
|
-
return stat(this.resolve(relativeOrAbsolutePathToFile).value);
|
|
72
|
+
return this.fs.promises.stat(this.resolve(relativeOrAbsolutePathToFile).value);
|
|
60
73
|
}
|
|
61
74
|
exists(relativeOrAbsolutePathToFile) {
|
|
62
75
|
return this.fs.existsSync(this.resolve(relativeOrAbsolutePathToFile).value);
|
|
63
76
|
}
|
|
64
|
-
remove(relativeOrAbsolutePathToFileOrDirectory) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
async remove(relativeOrAbsolutePathToFileOrDirectory) {
|
|
78
|
+
try {
|
|
79
|
+
const absolutePath = this.resolve(relativeOrAbsolutePathToFileOrDirectory);
|
|
80
|
+
const stat = await this.stat(relativeOrAbsolutePathToFileOrDirectory);
|
|
81
|
+
if (stat.isFile()) {
|
|
82
|
+
await this.fs.promises.unlink(absolutePath.value);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const entries = await this.fs.promises.readdir(absolutePath.value);
|
|
86
|
+
for (const entry of entries) {
|
|
87
|
+
await this.remove(absolutePath.join(new Path_1.Path(entry)));
|
|
88
|
+
}
|
|
89
|
+
await this.fs.promises.rmdir(absolutePath.value);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
74
93
|
if (error?.code === 'ENOENT') {
|
|
75
94
|
return void 0;
|
|
76
95
|
}
|
|
77
96
|
throw error;
|
|
78
|
-
}
|
|
97
|
+
}
|
|
79
98
|
}
|
|
80
|
-
ensureDirectoryExistsAt(relativeOrAbsolutePathToDirectory) {
|
|
99
|
+
async ensureDirectoryExistsAt(relativeOrAbsolutePathToDirectory) {
|
|
81
100
|
const absolutePath = this.resolve(relativeOrAbsolutePathToDirectory);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const current = parent.resolve(new Path_1.Path(child));
|
|
85
|
-
this.fs.mkdir(current.value, this.directoryMode, error => {
|
|
86
|
-
if (!error || error.code === 'EEXIST') {
|
|
87
|
-
return resolve(current);
|
|
88
|
-
}
|
|
89
|
-
// To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on Windows.
|
|
90
|
-
if (error.code === 'ENOENT') { // Throw the original parentDir error on `current` `ENOENT` failure.
|
|
91
|
-
throw new Error(`EACCES: permission denied, mkdir '${parent.value}'`);
|
|
92
|
-
}
|
|
93
|
-
const caughtError = !!~['EACCES', 'EPERM', 'EISDIR'].indexOf(error.code);
|
|
94
|
-
if (!caughtError || (caughtError && current.equals(relativeOrAbsolutePathToDirectory))) {
|
|
95
|
-
throw error; // Throw if it's just the last created dir.
|
|
96
|
-
}
|
|
97
|
-
return resolve(current);
|
|
98
|
-
});
|
|
99
|
-
}));
|
|
100
|
-
}, Promise.resolve(absolutePath.root()));
|
|
101
|
+
await this.fs.promises.mkdir(absolutePath.value, { recursive: true, mode: this.directoryMode });
|
|
102
|
+
return absolutePath;
|
|
101
103
|
}
|
|
102
104
|
rename(source, destination) {
|
|
103
|
-
|
|
104
|
-
return rename(source.value, destination.value);
|
|
105
|
+
return this.fs.promises.rename(source.value, destination.value);
|
|
105
106
|
}
|
|
106
107
|
tempFilePath(prefix = '', suffix = '.tmp') {
|
|
107
108
|
return Path_1.Path.from(this.fs.realpathSync(this.os.tmpdir()), `${prefix}${(0, cuid2_1.createId)()}${suffix}`);
|
|
108
109
|
}
|
|
109
|
-
write(path, data, encoding) {
|
|
110
|
-
return new Promise((resolve, reject) => {
|
|
111
|
-
this.fs.writeFile(path.value, data, encoding, error => error
|
|
112
|
-
? reject(error)
|
|
113
|
-
: resolve(path));
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
110
|
}
|
|
117
111
|
exports.FileSystem = FileSystem;
|
|
118
112
|
//# sourceMappingURL=FileSystem.js.map
|
package/lib/io/FileSystem.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileSystem.js","sourceRoot":"","sources":["../../src/io/FileSystem.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAgD;
|
|
1
|
+
{"version":3,"file":"FileSystem.js","sourceRoot":"","sources":["../../src/io/FileSystem.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TinyType } from 'tiny-types';
|
|
2
|
+
/**
|
|
3
|
+
* A tiny type describing a version number, like `1.2.3`
|
|
4
|
+
*/
|
|
5
|
+
export declare class Version extends TinyType {
|
|
6
|
+
private readonly version;
|
|
7
|
+
static fromJSON(version: string): Version;
|
|
8
|
+
constructor(version: string);
|
|
9
|
+
isLowerThan(other: Version): boolean;
|
|
10
|
+
isAtMost(other: Version): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* @param other
|
|
13
|
+
*/
|
|
14
|
+
isAtLeast(other: Version): boolean;
|
|
15
|
+
isHigherThan(other: Version): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* @returns
|
|
18
|
+
* Major version number of a given package version, i.e. `1` in `1.2.3`
|
|
19
|
+
*/
|
|
20
|
+
major(): number;
|
|
21
|
+
satisfies(range: string): boolean;
|
|
22
|
+
toString(): string;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=Version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Version.d.ts","sourceRoot":"","sources":["../../src/io/Version.ts"],"names":[],"mappings":"AACA,OAAO,EAA0C,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE9E;;GAEG;AACH,qBAAa,OAAQ,SAAQ,QAAQ;IAMrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;gBAIZ,OAAO,EAAE,MAAM;IAK5C,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;IAIpC,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;IAIjC;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;IAIlC,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;IAIrC;;;OAGG;IACH,KAAK,IAAI,MAAM;IAIf,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAIjC,QAAQ,IAAI,MAAM;CAGrB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Version = void 0;
|
|
7
|
+
const semver_1 = __importDefault(require("semver"));
|
|
8
|
+
const tiny_types_1 = require("tiny-types");
|
|
9
|
+
/**
|
|
10
|
+
* A tiny type describing a version number, like `1.2.3`
|
|
11
|
+
*/
|
|
12
|
+
class Version extends tiny_types_1.TinyType {
|
|
13
|
+
version;
|
|
14
|
+
static fromJSON(version) {
|
|
15
|
+
return new Version(version);
|
|
16
|
+
}
|
|
17
|
+
constructor(version) {
|
|
18
|
+
super();
|
|
19
|
+
this.version = version;
|
|
20
|
+
(0, tiny_types_1.ensure)('version', version, (0, tiny_types_1.isDefined)(), (0, tiny_types_1.isString)(), isValid());
|
|
21
|
+
}
|
|
22
|
+
isLowerThan(other) {
|
|
23
|
+
return semver_1.default.lt(this.version, other.version, { loose: false });
|
|
24
|
+
}
|
|
25
|
+
isAtMost(other) {
|
|
26
|
+
return semver_1.default.lte(this.version, other.version, { loose: false });
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* @param other
|
|
30
|
+
*/
|
|
31
|
+
isAtLeast(other) {
|
|
32
|
+
return semver_1.default.gte(this.version, other.version, { loose: false });
|
|
33
|
+
}
|
|
34
|
+
isHigherThan(other) {
|
|
35
|
+
return semver_1.default.gt(this.version, other.version, { loose: false });
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* @returns
|
|
39
|
+
* Major version number of a given package version, i.e. `1` in `1.2.3`
|
|
40
|
+
*/
|
|
41
|
+
major() {
|
|
42
|
+
return Number(this.version.split('.')[0]);
|
|
43
|
+
}
|
|
44
|
+
satisfies(range) {
|
|
45
|
+
return semver_1.default.satisfies(this.version, range, { loose: false });
|
|
46
|
+
}
|
|
47
|
+
toString() {
|
|
48
|
+
return `${this.version}`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.Version = Version;
|
|
52
|
+
/**
|
|
53
|
+
* @package
|
|
54
|
+
*/
|
|
55
|
+
function isValid() {
|
|
56
|
+
return tiny_types_1.Predicate.to(`be a valid version number`, (version) => !!semver_1.default.valid(version));
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=Version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Version.js","sourceRoot":"","sources":["../../src/io/Version.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAC5B,2CAA8E;AAE9E;;GAEG;AACH,MAAa,OAAQ,SAAQ,qBAAQ;IAMJ;IAJ7B,MAAM,CAAC,QAAQ,CAAC,OAAe;QAC3B,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAED,YAA6B,OAAe;QACxC,KAAK,EAAE,CAAC;QADiB,YAAO,GAAP,OAAO,CAAQ;QAExC,IAAA,mBAAM,EAAC,SAAS,EAAE,OAAO,EAAE,IAAA,sBAAS,GAAE,EAAE,IAAA,qBAAQ,GAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,WAAW,CAAC,KAAc;QACtB,OAAO,gBAAM,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,QAAQ,CAAC,KAAc;QACnB,OAAO,gBAAM,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACrE,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,KAAc;QACpB,OAAO,gBAAM,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,YAAY,CAAC,KAAc;QACvB,OAAO,gBAAM,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACpE,CAAC;IAED;;;OAGG;IACH,KAAK;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,SAAS,CAAC,KAAa;QACnB,OAAO,gBAAM,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,QAAQ;QACJ,OAAO,GAAI,IAAI,CAAC,OAAQ,EAAE,CAAC;IAC/B,CAAC;CACJ;AA7CD,0BA6CC;AAED;;GAEG;AACH,SAAS,OAAO;IACZ,OAAO,sBAAS,CAAC,EAAE,CAAC,2BAA2B,EAAE,CAAC,OAAe,EAAE,EAAE,CACjE,CAAC,CAAE,gBAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAC3B,CAAC;AACN,CAAC"}
|
package/lib/io/index.d.ts
CHANGED
package/lib/io/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/io/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,WAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/io/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC"}
|
package/lib/io/index.js
CHANGED
|
@@ -28,4 +28,5 @@ __exportStar(require("./Path"), exports);
|
|
|
28
28
|
__exportStar(require("./reflection"), exports);
|
|
29
29
|
__exportStar(require("./RequirementsHierarchy"), exports);
|
|
30
30
|
__exportStar(require("./trimmed"), exports);
|
|
31
|
+
__exportStar(require("./Version"), exports);
|
|
31
32
|
//# sourceMappingURL=index.js.map
|
package/lib/io/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/io/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B;AAC3B,mDAAiC;AACjC,2CAAyB;AACzB,+CAA6B;AAC7B,+CAA6B;AAC7B,uDAAqC;AACrC,2CAAyB;AACzB,8CAA4B;AAC5B,oDAAkC;AAClC,2CAAyB;AACzB,yCAAuB;AACvB,+CAA6B;AAC7B,0DAAwC;AACxC,4CAA0B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/io/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B;AAC3B,mDAAiC;AACjC,2CAAyB;AACzB,+CAA6B;AAC7B,+CAA6B;AAC7B,uDAAqC;AACrC,2CAAyB;AACzB,8CAA4B;AAC5B,oDAAkC;AAClC,2CAAyB;AACzB,yCAAuB;AACvB,+CAA6B;AAC7B,0DAAwC;AACxC,4CAA0B;AAC1B,4CAA0B"}
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import { Version } from '
|
|
1
|
+
import { Version } from '../Version';
|
|
2
2
|
/**
|
|
3
3
|
* Dynamically loads Node modules located relative to `cwd`.
|
|
4
4
|
*/
|
|
5
5
|
export declare class ModuleLoader {
|
|
6
6
|
readonly cwd: string;
|
|
7
|
+
readonly useRequireCache: boolean;
|
|
7
8
|
/**
|
|
8
9
|
* @param {string} cwd
|
|
9
10
|
* Current working directory, relative to which Node modules should be resolved.
|
|
11
|
+
* @param useRequireCache
|
|
12
|
+
* Whether to use Node's `require.cache`. Defaults to `true`.
|
|
13
|
+
* Set to `false` to force Node to reload required modules on every call.
|
|
10
14
|
*/
|
|
11
|
-
constructor(cwd: string);
|
|
15
|
+
constructor(cwd: string, useRequireCache?: boolean);
|
|
12
16
|
/**
|
|
13
17
|
* Returns `true` if a given module is available to be required, false otherwise.
|
|
14
18
|
*
|
|
@@ -23,7 +27,7 @@ export declare class ModuleLoader {
|
|
|
23
27
|
* NPM module id, for example `cucumber` or `@serenity-js/core`
|
|
24
28
|
*
|
|
25
29
|
* @returns
|
|
26
|
-
* Path a given Node module
|
|
30
|
+
* Path to a given Node.js module
|
|
27
31
|
*/
|
|
28
32
|
resolve(moduleId: string): string;
|
|
29
33
|
/**
|
|
@@ -32,6 +36,7 @@ export declare class ModuleLoader {
|
|
|
32
36
|
* @param moduleId
|
|
33
37
|
*/
|
|
34
38
|
require(moduleId: string): any;
|
|
39
|
+
private cachedIfNeeded;
|
|
35
40
|
/**
|
|
36
41
|
* Returns {@apilink Version} of module specified by `moduleId`, based on its `package.json`.
|
|
37
42
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModuleLoader.d.ts","sourceRoot":"","sources":["../../../src/io/loader/ModuleLoader.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"ModuleLoader.d.ts","sourceRoot":"","sources":["../../../src/io/loader/ModuleLoader.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC;;GAEG;AACH,qBAAa,YAAY;aAUD,GAAG,EAAE,MAAM;aACX,eAAe,EAAE,OAAO;IAT5C;;;;;;OAMG;gBAEiB,GAAG,EAAE,MAAM,EACX,eAAe,GAAE,OAAc;IAInD;;;;;OAKG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAQvC;;;;;;;;OAQG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAUjC;;;;OAIG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG;IAS9B,OAAO,CAAC,cAAc;IAQtB;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAGvC"}
|
|
@@ -27,18 +27,23 @@ exports.ModuleLoader = void 0;
|
|
|
27
27
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
28
28
|
const Module = require('module'); // No type definitions available
|
|
29
29
|
const path = __importStar(require("path")); // eslint-disable-line unicorn/import-style
|
|
30
|
-
const Version_1 = require("
|
|
30
|
+
const Version_1 = require("../Version");
|
|
31
31
|
/**
|
|
32
32
|
* Dynamically loads Node modules located relative to `cwd`.
|
|
33
33
|
*/
|
|
34
34
|
class ModuleLoader {
|
|
35
35
|
cwd;
|
|
36
|
+
useRequireCache;
|
|
36
37
|
/**
|
|
37
38
|
* @param {string} cwd
|
|
38
39
|
* Current working directory, relative to which Node modules should be resolved.
|
|
40
|
+
* @param useRequireCache
|
|
41
|
+
* Whether to use Node's `require.cache`. Defaults to `true`.
|
|
42
|
+
* Set to `false` to force Node to reload required modules on every call.
|
|
39
43
|
*/
|
|
40
|
-
constructor(cwd) {
|
|
44
|
+
constructor(cwd, useRequireCache = true) {
|
|
41
45
|
this.cwd = cwd;
|
|
46
|
+
this.useRequireCache = useRequireCache;
|
|
42
47
|
}
|
|
43
48
|
/**
|
|
44
49
|
* Returns `true` if a given module is available to be required, false otherwise.
|
|
@@ -61,7 +66,7 @@ class ModuleLoader {
|
|
|
61
66
|
* NPM module id, for example `cucumber` or `@serenity-js/core`
|
|
62
67
|
*
|
|
63
68
|
* @returns
|
|
64
|
-
* Path a given Node module
|
|
69
|
+
* Path to a given Node.js module
|
|
65
70
|
*/
|
|
66
71
|
resolve(moduleId) {
|
|
67
72
|
const fromFile = path.join(this.cwd, 'noop.js');
|
|
@@ -78,12 +83,18 @@ class ModuleLoader {
|
|
|
78
83
|
*/
|
|
79
84
|
require(moduleId) {
|
|
80
85
|
try {
|
|
81
|
-
return require(this.resolve(moduleId));
|
|
86
|
+
return require(this.cachedIfNeeded(this.resolve(moduleId)));
|
|
82
87
|
}
|
|
83
88
|
catch {
|
|
84
|
-
return require(moduleId);
|
|
89
|
+
return require(this.cachedIfNeeded(moduleId));
|
|
85
90
|
}
|
|
86
91
|
}
|
|
92
|
+
cachedIfNeeded(moduleId) {
|
|
93
|
+
if (!this.useRequireCache) {
|
|
94
|
+
delete require.cache[moduleId];
|
|
95
|
+
}
|
|
96
|
+
return moduleId;
|
|
97
|
+
}
|
|
87
98
|
/**
|
|
88
99
|
* Returns {@apilink Version} of module specified by `moduleId`, based on its `package.json`.
|
|
89
100
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModuleLoader.js","sourceRoot":"","sources":["../../../src/io/loader/ModuleLoader.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8DAA8D;AAC9D,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,gCAAgC;AAClE,2CAA6B,CAAC,2CAA2C;AAEzE,
|
|
1
|
+
{"version":3,"file":"ModuleLoader.js","sourceRoot":"","sources":["../../../src/io/loader/ModuleLoader.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8DAA8D;AAC9D,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,gCAAgC;AAClE,2CAA6B,CAAC,2CAA2C;AAEzE,wCAAqC;AAErC;;GAEG;AACH,MAAa,YAAY;IAUD;IACA;IATpB;;;;;;OAMG;IACH,YACoB,GAAW,EACX,kBAA2B,IAAI;QAD/B,QAAG,GAAH,GAAG,CAAQ;QACX,oBAAe,GAAf,eAAe,CAAgB;IAEnD,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,QAAgB;QACzB,IAAI;YACA,OAAO,CAAC,CAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;SACpC;QAAC,MAAM;YACJ,OAAO,KAAK,CAAC;SAChB;IACL,CAAC;IAED;;;;;;;;OAQG;IACH,OAAO,CAAC,QAAgB;QACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAEhD,OAAO,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE;YACrC,EAAE,EAAE,QAAQ;YACZ,QAAQ,EAAE,QAAQ;YAClB,KAAK,EAAE,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC;SAC3C,CAAC,CAAC;IACP,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,QAAgB;QACpB,IAAI;YACA,OAAO,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;SAC/D;QACD,MAAM;YACF,OAAO,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;SACjD;IACL,CAAC;IAEO,cAAc,CAAC,QAAgB;QACnC,IAAI,CAAE,IAAI,CAAC,eAAe,EAAE;YACxB,OAAO,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;SAClC;QAED,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,QAAgB;QACtB,OAAO,IAAI,iBAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAI,QAAS,eAAe,CAAC,CAAC,OAAO,CAAC,CAAC;IAC3E,CAAC;CACJ;AA9ED,oCA8EC"}
|
package/lib/io/loader/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/io/loader/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/io/loader/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC"}
|
package/lib/io/loader/index.js
CHANGED
|
@@ -18,5 +18,4 @@ __exportStar(require("./ClassDescriptionParser"), exports);
|
|
|
18
18
|
__exportStar(require("./ClassDescriptor"), exports);
|
|
19
19
|
__exportStar(require("./ClassLoader"), exports);
|
|
20
20
|
__exportStar(require("./ModuleLoader"), exports);
|
|
21
|
-
__exportStar(require("./Version"), exports);
|
|
22
21
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/io/loader/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2DAAyC;AACzC,oDAAkC;AAClC,gDAA8B;AAC9B,iDAA+B
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/io/loader/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2DAAyC;AACzC,oDAAkC;AAClC,gDAA8B;AAC9B,iDAA+B"}
|
|
@@ -7,7 +7,7 @@ import type { UsesAbilities } from './UsesAbilities';
|
|
|
7
7
|
*
|
|
8
8
|
* From the technical perspective, **abilities** act as **wrappers** around any **integration libraries** required
|
|
9
9
|
* to communicate with the external interfaces of system under test,
|
|
10
|
-
* such as {@apilink BrowseTheWeb|web browser drivers} or
|
|
10
|
+
* such as {@apilink BrowseTheWeb|web browser drivers} or an {@apilink CallAnApi|HTTP client}.
|
|
11
11
|
* They also enable [portability](/handbook/design/portable-test-code)
|
|
12
12
|
* of your test code across such integration libraries.
|
|
13
13
|
*
|
|
@@ -8,7 +8,7 @@ exports.Ability = void 0;
|
|
|
8
8
|
*
|
|
9
9
|
* From the technical perspective, **abilities** act as **wrappers** around any **integration libraries** required
|
|
10
10
|
* to communicate with the external interfaces of system under test,
|
|
11
|
-
* such as {@apilink BrowseTheWeb|web browser drivers} or
|
|
11
|
+
* such as {@apilink BrowseTheWeb|web browser drivers} or an {@apilink CallAnApi|HTTP client}.
|
|
12
12
|
* They also enable [portability](/handbook/design/portable-test-code)
|
|
13
13
|
* of your test code across such integration libraries.
|
|
14
14
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serenity-js/core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.20.0",
|
|
4
4
|
"description": "Serenity/JS Screenplay, reporting engine and core interfaces.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Jan Molak",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@types/diff": "5.0.9",
|
|
51
51
|
"@types/filenamify": "2.0.2",
|
|
52
52
|
"@types/mocha": "10.0.6",
|
|
53
|
-
"@types/semver": "7.5.
|
|
53
|
+
"@types/semver": "7.5.8",
|
|
54
54
|
"@types/validate-npm-package-name": "4.0.2",
|
|
55
55
|
"assertion-error-formatter": "3.0.0",
|
|
56
56
|
"c8": "9.1.0",
|
|
@@ -71,5 +71,5 @@
|
|
|
71
71
|
"engines": {
|
|
72
72
|
"node": "^16.13 || ^18.12 || ^20"
|
|
73
73
|
},
|
|
74
|
-
"gitHead": "
|
|
74
|
+
"gitHead": "2089afc60adb2751416fa6d800d4e7af95a99f25"
|
|
75
75
|
}
|
package/src/io/FileSystem.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
import * as nodeOS from 'node:os';
|
|
2
|
+
|
|
1
3
|
import { createId } from '@paralleldrive/cuid2';
|
|
2
|
-
import type * as
|
|
3
|
-
import type { WriteFileOptions } from 'fs';
|
|
4
|
+
import type * as NodeFS from 'fs';
|
|
4
5
|
import * as gracefulFS from 'graceful-fs';
|
|
5
|
-
import * as nodeOS from 'os';
|
|
6
|
-
import { promisify } from 'util';
|
|
7
6
|
|
|
8
7
|
import { Path } from './Path';
|
|
9
8
|
|
|
@@ -11,7 +10,7 @@ export class FileSystem {
|
|
|
11
10
|
|
|
12
11
|
constructor(
|
|
13
12
|
private readonly root: Path,
|
|
14
|
-
private readonly fs: typeof
|
|
13
|
+
private readonly fs: typeof NodeFS = gracefulFS,
|
|
15
14
|
private readonly os: typeof nodeOS = nodeOS,
|
|
16
15
|
private readonly directoryMode = Number.parseInt('0777', 8) & (~process.umask()),
|
|
17
16
|
) {
|
|
@@ -21,108 +20,93 @@ export class FileSystem {
|
|
|
21
20
|
return this.root.resolve(relativeOrAbsolutePath);
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
public store(relativeOrAbsolutePathToFile: Path, data: string | NodeJS.ArrayBufferView, encoding?: WriteFileOptions): Promise<Path> {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
public async store(relativeOrAbsolutePathToFile: Path, data: string | NodeJS.ArrayBufferView, encoding?: NodeFS.WriteFileOptions): Promise<Path> {
|
|
24
|
+
await this.ensureDirectoryExistsAt(relativeOrAbsolutePathToFile.directory());
|
|
25
|
+
return this.writeFile(relativeOrAbsolutePathToFile, data, encoding);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public readFile(relativeOrAbsolutePathToFile: Path, options?: { encoding?: null | undefined; flag?: string | undefined; }): Promise<Buffer>
|
|
29
|
+
public readFile(relativeOrAbsolutePathToFile: Path, options: { encoding: BufferEncoding; flag?: string | undefined; } | NodeJS.BufferEncoding): Promise<string>
|
|
30
|
+
public readFile(relativeOrAbsolutePathToFile: Path, options?: (NodeFS.ObjectEncodingOptions & { flag?: string | undefined; }) | NodeJS.BufferEncoding): Promise<string | Buffer> {
|
|
31
|
+
return this.fs.promises.readFile(this.resolve(relativeOrAbsolutePathToFile).value, options);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public readFileSync(relativeOrAbsolutePathToFile: Path, options?: { encoding?: null | undefined; flag?: string | undefined; }): Buffer
|
|
35
|
+
public readFileSync(relativeOrAbsolutePathToFile: Path, options: { encoding: BufferEncoding; flag?: string | undefined; } | NodeJS.BufferEncoding): string
|
|
36
|
+
public readFileSync(relativeOrAbsolutePathToFile: Path, options?: (NodeFS.ObjectEncodingOptions & { flag?: string | undefined; }) | NodeJS.BufferEncoding): string | Buffer {
|
|
37
|
+
return this.fs.readFileSync(this.resolve(relativeOrAbsolutePathToFile).value, options);
|
|
28
38
|
}
|
|
29
39
|
|
|
30
|
-
public
|
|
40
|
+
public async writeFile(relativeOrAbsolutePathToFile: Path, data: string | NodeJS.ArrayBufferView, options?: NodeFS.WriteFileOptions): Promise<Path> {
|
|
41
|
+
const resolvedPath = this.resolve(relativeOrAbsolutePathToFile);
|
|
42
|
+
await this.fs.promises.writeFile(resolvedPath.value, data, options);
|
|
43
|
+
|
|
44
|
+
return resolvedPath;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public writeFileSync(relativeOrAbsolutePathToFile: Path, data: string | NodeJS.ArrayBufferView, options?: NodeFS.WriteFileOptions): Path {
|
|
48
|
+
const resolvedPath = this.resolve(relativeOrAbsolutePathToFile);
|
|
49
|
+
this.fs.writeFileSync(resolvedPath.value, data, options);
|
|
50
|
+
|
|
51
|
+
return resolvedPath;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public createReadStream(relativeOrAbsolutePathToFile: Path): NodeFS.ReadStream {
|
|
31
55
|
return this.fs.createReadStream(this.resolve(relativeOrAbsolutePathToFile).value);
|
|
32
56
|
}
|
|
33
57
|
|
|
34
|
-
public createWriteStreamTo(relativeOrAbsolutePathToFile: Path):
|
|
58
|
+
public createWriteStreamTo(relativeOrAbsolutePathToFile: Path): NodeFS.WriteStream {
|
|
35
59
|
return this.fs.createWriteStream(this.resolve(relativeOrAbsolutePathToFile).value);
|
|
36
60
|
}
|
|
37
61
|
|
|
38
|
-
public stat(relativeOrAbsolutePathToFile: Path): Promise<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return stat(this.resolve(relativeOrAbsolutePathToFile).value);
|
|
62
|
+
public stat(relativeOrAbsolutePathToFile: Path): Promise<NodeFS.Stats> {
|
|
63
|
+
return this.fs.promises.stat(this.resolve(relativeOrAbsolutePathToFile).value);
|
|
42
64
|
}
|
|
43
65
|
|
|
44
66
|
public exists(relativeOrAbsolutePathToFile: Path): boolean {
|
|
45
67
|
return this.fs.existsSync(this.resolve(relativeOrAbsolutePathToFile).value);
|
|
46
68
|
}
|
|
47
69
|
|
|
48
|
-
public remove(relativeOrAbsolutePathToFileOrDirectory: Path): Promise<void> {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
: readdir(absolutePath.value)
|
|
62
|
-
.then(entries =>
|
|
63
|
-
Promise.all(entries.map(entry =>
|
|
64
|
-
this.remove(absolutePath.join(new Path(entry)))),
|
|
65
|
-
).then(() => rmdir(absolutePath.value)),
|
|
66
|
-
),
|
|
67
|
-
)
|
|
68
|
-
.then(() => void 0)
|
|
69
|
-
.catch(error => {
|
|
70
|
-
if (error?.code === 'ENOENT') {
|
|
71
|
-
return void 0;
|
|
70
|
+
public async remove(relativeOrAbsolutePathToFileOrDirectory: Path): Promise<void> {
|
|
71
|
+
try {
|
|
72
|
+
const absolutePath = this.resolve(relativeOrAbsolutePathToFileOrDirectory);
|
|
73
|
+
|
|
74
|
+
const stat = await this.stat(relativeOrAbsolutePathToFileOrDirectory);
|
|
75
|
+
|
|
76
|
+
if (stat.isFile()) {
|
|
77
|
+
await this.fs.promises.unlink(absolutePath.value);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
const entries = await this.fs.promises.readdir(absolutePath.value);
|
|
81
|
+
for (const entry of entries) {
|
|
82
|
+
await this.remove(absolutePath.join(new Path(entry)));
|
|
72
83
|
}
|
|
73
|
-
|
|
74
|
-
|
|
84
|
+
|
|
85
|
+
await this.fs.promises.rmdir(absolutePath.value);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (error?.code === 'ENOENT') {
|
|
90
|
+
return void 0;
|
|
91
|
+
}
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
75
94
|
}
|
|
76
95
|
|
|
77
|
-
public ensureDirectoryExistsAt(relativeOrAbsolutePathToDirectory: Path): Promise<Path> {
|
|
96
|
+
public async ensureDirectoryExistsAt(relativeOrAbsolutePathToDirectory: Path): Promise<Path> {
|
|
78
97
|
|
|
79
98
|
const absolutePath = this.resolve(relativeOrAbsolutePathToDirectory);
|
|
80
99
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
this.fs.mkdir(current.value, this.directoryMode, error => {
|
|
86
|
-
if (! error || error.code === 'EEXIST') {
|
|
87
|
-
return resolve(current);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on Windows.
|
|
91
|
-
if (error.code === 'ENOENT') { // Throw the original parentDir error on `current` `ENOENT` failure.
|
|
92
|
-
throw new Error(`EACCES: permission denied, mkdir '${ parent.value }'`);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const caughtError = !! ~['EACCES', 'EPERM', 'EISDIR'].indexOf(error.code);
|
|
96
|
-
if (! caughtError || (caughtError && current.equals(relativeOrAbsolutePathToDirectory))) {
|
|
97
|
-
throw error; // Throw if it's just the last created dir.
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return resolve(current);
|
|
101
|
-
});
|
|
102
|
-
}));
|
|
103
|
-
}, Promise.resolve(absolutePath.root()));
|
|
100
|
+
await this.fs.promises.mkdir(absolutePath.value, { recursive: true, mode: this.directoryMode });
|
|
101
|
+
|
|
102
|
+
return absolutePath;
|
|
104
103
|
}
|
|
105
104
|
|
|
106
105
|
public rename(source: Path, destination: Path): Promise<void> {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return rename(source.value, destination.value);
|
|
106
|
+
return this.fs.promises.rename(source.value, destination.value);
|
|
110
107
|
}
|
|
111
108
|
|
|
112
109
|
public tempFilePath(prefix = '', suffix = '.tmp'): Path {
|
|
113
110
|
return Path.from(this.fs.realpathSync(this.os.tmpdir()), `${ prefix }${ createId() }${ suffix }`);
|
|
114
111
|
}
|
|
115
|
-
|
|
116
|
-
private write(path: Path, data: string | NodeJS.ArrayBufferView, encoding?: WriteFileOptions): Promise<Path> {
|
|
117
|
-
return new Promise((resolve, reject) => {
|
|
118
|
-
this.fs.writeFile(
|
|
119
|
-
path.value,
|
|
120
|
-
data,
|
|
121
|
-
encoding,
|
|
122
|
-
error => error
|
|
123
|
-
? reject(error)
|
|
124
|
-
: resolve(path),
|
|
125
|
-
);
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
112
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import semver from 'semver';
|
|
2
|
+
import { ensure, isDefined, isString, Predicate, TinyType } from 'tiny-types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A tiny type describing a version number, like `1.2.3`
|
|
6
|
+
*/
|
|
7
|
+
export class Version extends TinyType {
|
|
8
|
+
|
|
9
|
+
static fromJSON(version: string): Version {
|
|
10
|
+
return new Version(version);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
constructor(private readonly version: string) {
|
|
14
|
+
super();
|
|
15
|
+
ensure('version', version, isDefined(), isString(), isValid());
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
isLowerThan(other: Version): boolean {
|
|
19
|
+
return semver.lt(this.version, other.version, { loose: false });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
isAtMost(other: Version): boolean {
|
|
23
|
+
return semver.lte(this.version, other.version, { loose: false });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param other
|
|
28
|
+
*/
|
|
29
|
+
isAtLeast(other: Version): boolean {
|
|
30
|
+
return semver.gte(this.version, other.version, { loose: false });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
isHigherThan(other: Version): boolean {
|
|
34
|
+
return semver.gt(this.version, other.version, { loose: false });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @returns
|
|
39
|
+
* Major version number of a given package version, i.e. `1` in `1.2.3`
|
|
40
|
+
*/
|
|
41
|
+
major(): number {
|
|
42
|
+
return Number(this.version.split('.')[0]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
satisfies(range: string): boolean {
|
|
46
|
+
return semver.satisfies(this.version, range, { loose: false });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
toString(): string {
|
|
50
|
+
return `${ this.version }`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @package
|
|
56
|
+
*/
|
|
57
|
+
function isValid(): Predicate<string> {
|
|
58
|
+
return Predicate.to(`be a valid version number`, (version: string) =>
|
|
59
|
+
!! semver.valid(version),
|
|
60
|
+
);
|
|
61
|
+
}
|
package/src/io/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const Module = require('module'); // No type definitions available
|
|
3
3
|
import * as path from 'path'; // eslint-disable-line unicorn/import-style
|
|
4
4
|
|
|
5
|
-
import { Version } from '
|
|
5
|
+
import { Version } from '../Version';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Dynamically loads Node modules located relative to `cwd`.
|
|
@@ -12,8 +12,14 @@ export class ModuleLoader {
|
|
|
12
12
|
/**
|
|
13
13
|
* @param {string} cwd
|
|
14
14
|
* Current working directory, relative to which Node modules should be resolved.
|
|
15
|
+
* @param useRequireCache
|
|
16
|
+
* Whether to use Node's `require.cache`. Defaults to `true`.
|
|
17
|
+
* Set to `false` to force Node to reload required modules on every call.
|
|
15
18
|
*/
|
|
16
|
-
constructor(
|
|
19
|
+
constructor(
|
|
20
|
+
public readonly cwd: string,
|
|
21
|
+
public readonly useRequireCache: boolean = true,
|
|
22
|
+
) {
|
|
17
23
|
}
|
|
18
24
|
|
|
19
25
|
/**
|
|
@@ -37,7 +43,7 @@ export class ModuleLoader {
|
|
|
37
43
|
* NPM module id, for example `cucumber` or `@serenity-js/core`
|
|
38
44
|
*
|
|
39
45
|
* @returns
|
|
40
|
-
* Path a given Node module
|
|
46
|
+
* Path to a given Node.js module
|
|
41
47
|
*/
|
|
42
48
|
resolve(moduleId: string): string {
|
|
43
49
|
const fromFile = path.join(this.cwd, 'noop.js');
|
|
@@ -56,13 +62,21 @@ export class ModuleLoader {
|
|
|
56
62
|
*/
|
|
57
63
|
require(moduleId: string): any {
|
|
58
64
|
try {
|
|
59
|
-
return require(this.resolve(moduleId));
|
|
65
|
+
return require(this.cachedIfNeeded(this.resolve(moduleId)));
|
|
60
66
|
}
|
|
61
67
|
catch {
|
|
62
|
-
return require(moduleId);
|
|
68
|
+
return require(this.cachedIfNeeded(moduleId));
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
71
|
|
|
72
|
+
private cachedIfNeeded(moduleId: string): string {
|
|
73
|
+
if (! this.useRequireCache) {
|
|
74
|
+
delete require.cache[moduleId];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return moduleId;
|
|
78
|
+
}
|
|
79
|
+
|
|
66
80
|
/**
|
|
67
81
|
* Returns {@apilink Version} of module specified by `moduleId`, based on its `package.json`.
|
|
68
82
|
*
|
package/src/io/loader/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { UsesAbilities } from './UsesAbilities';
|
|
|
8
8
|
*
|
|
9
9
|
* From the technical perspective, **abilities** act as **wrappers** around any **integration libraries** required
|
|
10
10
|
* to communicate with the external interfaces of system under test,
|
|
11
|
-
* such as {@apilink BrowseTheWeb|web browser drivers} or
|
|
11
|
+
* such as {@apilink BrowseTheWeb|web browser drivers} or an {@apilink CallAnApi|HTTP client}.
|
|
12
12
|
* They also enable [portability](/handbook/design/portable-test-code)
|
|
13
13
|
* of your test code across such integration libraries.
|
|
14
14
|
*
|