5etools-utils 0.10.30 → 0.11.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/lib/BrewTester.js +7 -1
- package/lib/TestJson.js +60 -23
- package/lib/TestJsonWorker.js +3 -2
- package/lib/UrlUtil.js +19 -0
- package/lib/UtilFs.js +5 -2
- package/package.json +1 -1
package/lib/BrewTester.js
CHANGED
|
@@ -20,6 +20,7 @@ class _BrewTesterJson {
|
|
|
20
20
|
const opts = program.opts();
|
|
21
21
|
|
|
22
22
|
const jsonTester = new JsonTester({mode, tagLog: this._LOG_TAG, fnGetSchemaId: () => "homebrew.json"});
|
|
23
|
+
await jsonTester.pInit();
|
|
23
24
|
|
|
24
25
|
let results;
|
|
25
26
|
if (program.args[0]) {
|
|
@@ -32,7 +33,7 @@ class _BrewTesterJson {
|
|
|
32
33
|
results = await jsonTester.pGetErrorsOnDirsWorkers({isFailFast: !this._IS_FAIL_SLOW});
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
const {errors, errorsFull} = results;
|
|
36
|
+
const {errors, errorsFull, isUnknownError = false} = results;
|
|
36
37
|
|
|
37
38
|
if (errors.length) {
|
|
38
39
|
if (!process.env.CI) {
|
|
@@ -45,6 +46,11 @@ class _BrewTesterJson {
|
|
|
45
46
|
process.exit(1);
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
if (isUnknownError) {
|
|
50
|
+
console.error(`Unknown error when testing! (See above logs)`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
48
54
|
if (!errors.length) Um.info(this._LOG_TAG, `Schema test passed.`);
|
|
49
55
|
}
|
|
50
56
|
}
|
package/lib/TestJson.js
CHANGED
|
@@ -11,6 +11,8 @@ import NP from "number-precision";
|
|
|
11
11
|
import * as Uf from "./UtilFs.js";
|
|
12
12
|
import Um from "./UtilMisc.js";
|
|
13
13
|
import {WorkerList, Deferred} from "./WorkerList.js";
|
|
14
|
+
import {ObjectWalker} from "./ObjectWalker.js";
|
|
15
|
+
import {UrlUtil} from "./UrlUtil.js";
|
|
14
16
|
|
|
15
17
|
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
|
|
16
18
|
|
|
@@ -87,7 +89,11 @@ class JsonTester {
|
|
|
87
89
|
});
|
|
88
90
|
// endregion
|
|
89
91
|
|
|
90
|
-
this.
|
|
92
|
+
this._pLoadSchema = null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async pInit () {
|
|
96
|
+
await this._pDoLoadSchemas();
|
|
91
97
|
}
|
|
92
98
|
|
|
93
99
|
_getFileErrors ({filePath}) { return `${filePath}\n${JSON.stringify(this._ajv.errors, null, 2)}`; }
|
|
@@ -141,30 +147,55 @@ class JsonTester {
|
|
|
141
147
|
);
|
|
142
148
|
}
|
|
143
149
|
|
|
144
|
-
|
|
145
|
-
|
|
150
|
+
async _pDoLoadSchemas () {
|
|
151
|
+
await (this._pLoadSchema ||= (async () => {
|
|
152
|
+
const remoteUrls = new Set();
|
|
146
153
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
Uf.listJsonFiles(this._dirSchema)
|
|
155
|
+
.forEach(filePath => {
|
|
156
|
+
filePath = path.normalize(filePath);
|
|
150
157
|
|
|
151
|
-
|
|
152
|
-
|
|
158
|
+
const relativeFilePath = path.relative(this._dirSchema, filePath)
|
|
159
|
+
.replace(/\\/g, "/");
|
|
153
160
|
|
|
154
|
-
|
|
155
|
-
|
|
161
|
+
const json = Uf.readJsonSync(filePath);
|
|
162
|
+
this._ajv.addSchema(json, relativeFilePath);
|
|
163
|
+
|
|
164
|
+
ObjectWalker.walk({
|
|
165
|
+
obj: json,
|
|
166
|
+
filePath,
|
|
167
|
+
primitiveHandlers: {
|
|
168
|
+
object: obj => {
|
|
169
|
+
if (!obj.$ref) return;
|
|
170
|
+
|
|
171
|
+
if (!UrlUtil.isUrl(obj.$ref, {protocols: ["http:", "https:"]})) return;
|
|
172
|
+
|
|
173
|
+
const url = new URL(obj.$ref);
|
|
174
|
+
url.hash = "";
|
|
175
|
+
remoteUrls.add(String(url));
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
});
|
|
156
180
|
|
|
157
|
-
|
|
181
|
+
if (!remoteUrls.size) return;
|
|
182
|
+
|
|
183
|
+
await Promise.all(
|
|
184
|
+
Array.from(remoteUrls).map(async remoteUrl => {
|
|
185
|
+
this._ajv.addSchema(await (await fetch(remoteUrl)).json(), remoteUrl);
|
|
186
|
+
}),
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
return true;
|
|
190
|
+
})());
|
|
158
191
|
}
|
|
159
192
|
|
|
160
|
-
|
|
161
|
-
this.
|
|
193
|
+
async _pHasSchema (schemaId) {
|
|
194
|
+
await this._pDoLoadSchemas();
|
|
162
195
|
return !!this._ajv.schemas[schemaId];
|
|
163
196
|
}
|
|
164
197
|
|
|
165
198
|
getFileErrors ({filePath} = {}) {
|
|
166
|
-
this._doLoadSchemas();
|
|
167
|
-
|
|
168
199
|
Um.info(this._tagLog, `\tValidating "${filePath}"...`);
|
|
169
200
|
|
|
170
201
|
const data = Uf.readJsonSync(filePath);
|
|
@@ -231,16 +262,19 @@ class JsonTester {
|
|
|
231
262
|
}
|
|
232
263
|
|
|
233
264
|
// region Verify that every file path maps to a valid schema
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
265
|
+
await Promise.all(
|
|
266
|
+
fileQueue.map(async filePath => {
|
|
267
|
+
const schemaId = this._fnGetSchemaId(filePath);
|
|
268
|
+
if (!schemaId) throw new Error(`Failed to get schema ID for file path "${filePath}"`);
|
|
269
|
+
if (!(await this._pHasSchema(schemaId))) throw new Error(`No schema loaded with schema ID "${schemaId}"`);
|
|
270
|
+
return schemaId;
|
|
271
|
+
}),
|
|
272
|
+
);
|
|
240
273
|
// endregion
|
|
241
274
|
|
|
242
275
|
const workerList = new WorkerList();
|
|
243
276
|
|
|
277
|
+
let cntFailures = 0;
|
|
244
278
|
const workers = [...new Array(cntWorkers)]
|
|
245
279
|
.map(() => {
|
|
246
280
|
// Relative `Worker` paths do not function in packages, so give an exact path
|
|
@@ -265,7 +299,10 @@ class JsonTester {
|
|
|
265
299
|
}
|
|
266
300
|
});
|
|
267
301
|
|
|
268
|
-
worker.on("error", e =>
|
|
302
|
+
worker.on("error", e => {
|
|
303
|
+
console.error(e);
|
|
304
|
+
cntFailures++;
|
|
305
|
+
});
|
|
269
306
|
|
|
270
307
|
worker.postMessage({
|
|
271
308
|
type: "init",
|
|
@@ -297,7 +334,7 @@ class JsonTester {
|
|
|
297
334
|
await Promise.all(workers.map(it => it.dIsActive?.promise));
|
|
298
335
|
await Promise.all(workers.map(it => it.terminate()));
|
|
299
336
|
|
|
300
|
-
return {errors, errorsFull};
|
|
337
|
+
return {errors, errorsFull, isUnknownError: !!cntFailures};
|
|
301
338
|
}
|
|
302
339
|
}
|
|
303
340
|
|
package/lib/TestJsonWorker.js
CHANGED
|
@@ -7,16 +7,17 @@ let jsonTester;
|
|
|
7
7
|
let isCancelled = false;
|
|
8
8
|
|
|
9
9
|
parentPort
|
|
10
|
-
.on("message", msg => {
|
|
10
|
+
.on("message", async msg => {
|
|
11
11
|
switch (msg.type) {
|
|
12
12
|
case "init": {
|
|
13
13
|
const {dirSchema, tagLog, strFnGetSchemaId} = msg.payload;
|
|
14
14
|
|
|
15
|
-
jsonTester
|
|
15
|
+
jsonTester ||= new JsonTester({
|
|
16
16
|
dirSchema,
|
|
17
17
|
tagLog,
|
|
18
18
|
fnGetSchemaId: eval(strFnGetSchemaId), // eslint-disable-line no-eval
|
|
19
19
|
});
|
|
20
|
+
await jsonTester.pInit();
|
|
20
21
|
|
|
21
22
|
parentPort.postMessage({
|
|
22
23
|
type: "ready",
|
package/lib/UrlUtil.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class UrlUtil {
|
|
2
|
+
/**
|
|
3
|
+
* @param str
|
|
4
|
+
* @param {?Array<string>} protocols
|
|
5
|
+
* @return {*|boolean}
|
|
6
|
+
*/
|
|
7
|
+
static isUrl (str, {protocols = null} = {}) {
|
|
8
|
+
let url;
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
url = new URL(str);
|
|
12
|
+
} catch (e) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (protocols == null) return true;
|
|
17
|
+
return protocols.includes(url.protocol);
|
|
18
|
+
}
|
|
19
|
+
}
|
package/lib/UtilFs.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
|
+
import {getCleanString} from "./UtilClean.js";
|
|
3
4
|
|
|
4
5
|
function isDirectory (path) {
|
|
5
6
|
return fs.lstatSync(path).isDirectory();
|
|
@@ -29,8 +30,10 @@ function readJsonSync (path, {isIncludeRaw = false} = {}) {
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
function writeJsonSync (filePath, data) {
|
|
33
|
-
|
|
33
|
+
function writeJsonSync (filePath, data, {isClean = false} = {}) {
|
|
34
|
+
let str = `${JSON.stringify(data, null, "\t")}\n`;
|
|
35
|
+
if (isClean) str = getCleanString(str);
|
|
36
|
+
return fs.writeFileSync(filePath, str, "utf-8");
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
/**
|