5etools-utils 0.1.0 → 0.1.3
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/Api.js +4 -0
- package/lib/TestJson.js +121 -0
- package/lib/UtilFs.js +64 -0
- package/lib/UtilMisc.js +22 -0
- package/package.json +11 -2
- package/.editorconfig +0 -7
- package/.eslintrc.js +0 -184
- package/HelloWorld.js +0 -3
package/lib/Api.js
ADDED
package/lib/TestJson.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
|
|
3
|
+
import Ajv from "ajv";
|
|
4
|
+
import * as jsonSourceMap from "json-source-map";
|
|
5
|
+
|
|
6
|
+
import * as uf from "./UtilFs.js";
|
|
7
|
+
import * as um from "./UtilMisc.js";
|
|
8
|
+
|
|
9
|
+
const _IS_SORT_RESULTS = !process.env.VET_TEST_JSON_RESULTS_UNSORTED;
|
|
10
|
+
const _IS_TRIM_RESULTS = !process.env.VET_TEST_JSON_RESULTS_UNTRIMMED;
|
|
11
|
+
|
|
12
|
+
class JsonTester {
|
|
13
|
+
static _RE_DATE = /^\d\d\d\d-\d\d-\d\d$/;
|
|
14
|
+
|
|
15
|
+
constructor (
|
|
16
|
+
{
|
|
17
|
+
dirSchema,
|
|
18
|
+
tagLog = "JSON",
|
|
19
|
+
},
|
|
20
|
+
) {
|
|
21
|
+
this._dirSchema = dirSchema;
|
|
22
|
+
this._tagLog = tagLog;
|
|
23
|
+
|
|
24
|
+
// region Set up validator
|
|
25
|
+
this._ajv = new Ajv({
|
|
26
|
+
allowUnionTypes: true,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
this._ajv.addKeyword({
|
|
30
|
+
keyword: "version",
|
|
31
|
+
validate: false,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
this._ajv.addFormat(
|
|
35
|
+
"date",
|
|
36
|
+
{
|
|
37
|
+
validate: str => JsonTester._RE_DATE.test(str),
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
// endregion
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Add any implicit data to the JSON
|
|
45
|
+
*/
|
|
46
|
+
_addImplicits (obj, lastKey) {
|
|
47
|
+
if (typeof obj !== "object") return;
|
|
48
|
+
if (obj == null) return;
|
|
49
|
+
if (obj instanceof Array) obj.forEach(it => this._addImplicits(it, lastKey));
|
|
50
|
+
else {
|
|
51
|
+
// "obj.mode" will be set if this is in a "_copy" etc. block
|
|
52
|
+
if (lastKey === "spellcasting" && !obj.mode) obj.type = obj.type || "spellcasting";
|
|
53
|
+
|
|
54
|
+
Object.entries(obj).forEach(([k, v]) => this._addImplicits(v, k));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_getFileErrors ({filePath}) { return `${filePath}\n${JSON.stringify(ajv.errors, null, 2)}`; }
|
|
59
|
+
|
|
60
|
+
_handleError ({filePath, errors, errorsFull, data}) {
|
|
61
|
+
if (!process.env.CI) errorsFull.push(this._getFileErrors({filePath}));
|
|
62
|
+
|
|
63
|
+
// Sort the deepest errors to the bottom, as these are the ones we're most likely to be the ones we care about
|
|
64
|
+
// manually checking.
|
|
65
|
+
if (_IS_SORT_RESULTS) {
|
|
66
|
+
this._ajv.errors.sort((a, b) => (a.instancePath.length ?? -1) - (b.instancePath.length ?? -1));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// If there are an excessive number of errors, it's probably a junk entry; show only the first error and let the
|
|
70
|
+
// user figure it out.
|
|
71
|
+
if (_IS_TRIM_RESULTS && this._ajv.errors.length > 5) {
|
|
72
|
+
console.error(`(${this._ajv.errors.length} errors found, showing (hopefully) most-relevant one)`);
|
|
73
|
+
this._ajv.errors = this._ajv.errors.slice(-1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Add line numbers
|
|
77
|
+
const sourceMap = jsonSourceMap.stringify(data, null, "\t");
|
|
78
|
+
this._ajv.errors.forEach(it => {
|
|
79
|
+
const errorPointer = sourceMap.pointers[it.instancePath];
|
|
80
|
+
it.lineNumberStart = errorPointer.value.line;
|
|
81
|
+
it.lineNumberEnd = errorPointer.valueEnd.line;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const error = this._getFileErrors({filePath});
|
|
85
|
+
um.error(this._tagLog, error);
|
|
86
|
+
errors.push(error);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
getErrors () {
|
|
90
|
+
um.info(this._tagLog, `Validating JSON against schema`);
|
|
91
|
+
|
|
92
|
+
uf.listJsonFiles(this._dirSchema)
|
|
93
|
+
.forEach(filePath => {
|
|
94
|
+
filePath = path.normalize(filePath);
|
|
95
|
+
const contents = uf.readJSON(filePath);
|
|
96
|
+
|
|
97
|
+
const relativeFilePath = path.relative(this._dirSchema, filePath)
|
|
98
|
+
.replace(/\\/g, "/");
|
|
99
|
+
|
|
100
|
+
this._ajv.addSchema(contents, relativeFilePath);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const errors = [];
|
|
104
|
+
const errorsFull = [];
|
|
105
|
+
|
|
106
|
+
uf.runOnDirs((dir) => {
|
|
107
|
+
uf.listJsonFiles(dir)
|
|
108
|
+
.forEach(filePath => {
|
|
109
|
+
const data = uf.readJSON(filePath);
|
|
110
|
+
this._addImplicits(data);
|
|
111
|
+
const valid = this._ajv.validate("homebrew.json", data);
|
|
112
|
+
|
|
113
|
+
if (!valid) this._handleError({filePath, errors, errorsFull, data});
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return {errors, errorsFull};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export {JsonTester};
|
package/lib/UtilFs.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
|
|
3
|
+
function isDirectory (path) {
|
|
4
|
+
return fs.lstatSync(path).isDirectory();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function readJSON (path) {
|
|
8
|
+
try {
|
|
9
|
+
if (fs.existsSync(path)) {
|
|
10
|
+
let str = fs.readFileSync(path, "utf8");
|
|
11
|
+
// Strip BOM(s)
|
|
12
|
+
while (str.charCodeAt(0) === 0xFEFF) str = str.slice(1);
|
|
13
|
+
return JSON.parse(str);
|
|
14
|
+
} else {
|
|
15
|
+
const parts = path.split(/[\\/]+/g);
|
|
16
|
+
const dir = parts.slice(0, -1).join("/");
|
|
17
|
+
const originalName = parts[parts.length - 1];
|
|
18
|
+
const filenames = fs.readdirSync(dir, "utf8");
|
|
19
|
+
const filename = filenames.find(it => it.toLowerCase() === originalName.toLowerCase());
|
|
20
|
+
if (filename) return JSON.parse(fs.readFileSync(`${dir}/${filename}`, "utf8"));
|
|
21
|
+
else throw new Error(`Could not find file "${path}"`);
|
|
22
|
+
}
|
|
23
|
+
} catch (e) {
|
|
24
|
+
e.message += ` (Path: ${path})`;
|
|
25
|
+
throw e;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function listJsonFiles (dir) {
|
|
30
|
+
const dirContent = fs.readdirSync(dir, "utf8")
|
|
31
|
+
.map(file => `${dir}/${file}`);
|
|
32
|
+
return dirContent.reduce((acc, file) => {
|
|
33
|
+
if (isDirectory(file)) acc.push(...listJsonFiles(file));
|
|
34
|
+
else {
|
|
35
|
+
if (file.toLowerCase().endsWith(".json")) acc.push(file);
|
|
36
|
+
}
|
|
37
|
+
return acc;
|
|
38
|
+
}, []);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function runOnDirs (fn) {
|
|
42
|
+
fs.readdirSync(".", "utf8")
|
|
43
|
+
.filter(dir => isDirectory(dir) && !dir.startsWith(".") && !dir.startsWith("_") && dir !== "node_modules")
|
|
44
|
+
.forEach(dir => fn(dir));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function mkDirs (pathToCreate) {
|
|
48
|
+
pathToCreate
|
|
49
|
+
.split(/[\\/]/g)
|
|
50
|
+
.reduce((currentPath, folder) => {
|
|
51
|
+
currentPath += `${folder}/`;
|
|
52
|
+
if (!fs.existsSync(currentPath)) {
|
|
53
|
+
fs.mkdirSync(currentPath);
|
|
54
|
+
}
|
|
55
|
+
return currentPath;
|
|
56
|
+
}, "");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export {
|
|
60
|
+
readJSON,
|
|
61
|
+
listJsonFiles,
|
|
62
|
+
runOnDirs,
|
|
63
|
+
mkDirs,
|
|
64
|
+
};
|
package/lib/UtilMisc.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
function _taggedConsole (fn, tag, ...args) {
|
|
2
|
+
const expandedTag = tag.padStart(12, " ");
|
|
3
|
+
fn(`[${expandedTag}]`, ...args);
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function warn (tag, ...args) {
|
|
7
|
+
_taggedConsole(console.warn, tag, ...args);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function info (tag, ...args) {
|
|
11
|
+
_taggedConsole(console.info, tag, ...args);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function error (tag, ...args) {
|
|
15
|
+
_taggedConsole(console.error, tag, ...args);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
warn,
|
|
20
|
+
info,
|
|
21
|
+
error,
|
|
22
|
+
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "5etools-utils",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Shared utilities for the 5etools ecosystem.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"main": "lib/Api.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib"
|
|
9
|
+
],
|
|
10
|
+
"directories": {
|
|
11
|
+
"lib": "./lib"
|
|
12
|
+
},
|
|
6
13
|
"scripts": {
|
|
7
|
-
"test": "
|
|
14
|
+
"test": "npm run test:js",
|
|
15
|
+
"test:js": "eslint lib",
|
|
16
|
+
"lint:js": "eslint lib --fix"
|
|
8
17
|
},
|
|
9
18
|
"repository": {
|
|
10
19
|
"type": "git",
|
package/.editorconfig
DELETED
package/.eslintrc.js
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
"extends": "eslint:recommended",
|
|
3
|
-
"env": {
|
|
4
|
-
"browser": true,
|
|
5
|
-
"es6": true,
|
|
6
|
-
"jquery": true
|
|
7
|
-
},
|
|
8
|
-
"parserOptions": {
|
|
9
|
-
"ecmaVersion": "latest",
|
|
10
|
-
"sourceType": "module"
|
|
11
|
-
},
|
|
12
|
-
"rules": {
|
|
13
|
-
"accessor-pairs": "off",
|
|
14
|
-
"arrow-spacing": ["error", {"before": true, "after": true}],
|
|
15
|
-
"block-spacing": ["error", "always"],
|
|
16
|
-
"brace-style": ["error", "1tbs", {"allowSingleLine": true}],
|
|
17
|
-
"comma-dangle": ["error", {
|
|
18
|
-
"arrays": "always-multiline",
|
|
19
|
-
"objects": "always-multiline",
|
|
20
|
-
"imports": "always-multiline",
|
|
21
|
-
"exports": "always-multiline",
|
|
22
|
-
"functions": "always-multiline"
|
|
23
|
-
}],
|
|
24
|
-
"comma-spacing": ["error", {"before": false, "after": true}],
|
|
25
|
-
"comma-style": ["error", "last"],
|
|
26
|
-
"constructor-super": "error",
|
|
27
|
-
"curly": ["error", "multi-line"],
|
|
28
|
-
"dot-location": ["error", "property"],
|
|
29
|
-
"eqeqeq": ["error", "always", {"null": "ignore"}],
|
|
30
|
-
"func-call-spacing": ["error", "never"],
|
|
31
|
-
"generator-star-spacing": ["error", {"before": false, "after": true}],
|
|
32
|
-
"handle-callback-err": ["error", "^(err|error)$"],
|
|
33
|
-
"indent": [
|
|
34
|
-
"error",
|
|
35
|
-
"tab",
|
|
36
|
-
{
|
|
37
|
-
"SwitchCase": 1
|
|
38
|
-
}
|
|
39
|
-
],
|
|
40
|
-
"key-spacing": ["error", {"beforeColon": false, "afterColon": true}],
|
|
41
|
-
"keyword-spacing": ["error", {"before": true, "after": true}],
|
|
42
|
-
"new-cap": ["error", {"newIsCap": true, "capIsNew": false}],
|
|
43
|
-
"new-parens": "error",
|
|
44
|
-
"no-array-constructor": "error",
|
|
45
|
-
"no-caller": "error",
|
|
46
|
-
"no-class-assign": "error",
|
|
47
|
-
"no-compare-neg-zero": "error",
|
|
48
|
-
"no-cond-assign": "error",
|
|
49
|
-
"no-const-assign": "error",
|
|
50
|
-
"no-constant-condition": ["error", {"checkLoops": false}],
|
|
51
|
-
"no-control-regex": "error",
|
|
52
|
-
"no-debugger": "error",
|
|
53
|
-
"no-delete-var": "error",
|
|
54
|
-
"no-dupe-args": "error",
|
|
55
|
-
"no-dupe-class-members": "error",
|
|
56
|
-
"no-dupe-keys": "error",
|
|
57
|
-
"no-duplicate-case": "error",
|
|
58
|
-
"no-empty-character-class": "error",
|
|
59
|
-
"no-empty-pattern": "error",
|
|
60
|
-
"no-eval": "error",
|
|
61
|
-
"no-ex-assign": "error",
|
|
62
|
-
"no-extra-bind": "error",
|
|
63
|
-
"no-extra-boolean-cast": "error",
|
|
64
|
-
"no-extra-parens": ["error", "functions"],
|
|
65
|
-
"no-fallthrough": "error",
|
|
66
|
-
"no-floating-decimal": "error",
|
|
67
|
-
"no-func-assign": "error",
|
|
68
|
-
"no-global-assign": "error",
|
|
69
|
-
"no-implied-eval": "error",
|
|
70
|
-
"no-inner-declarations": ["error", "functions"],
|
|
71
|
-
"no-invalid-regexp": "error",
|
|
72
|
-
"no-irregular-whitespace": "error",
|
|
73
|
-
"no-iterator": "error",
|
|
74
|
-
"no-label-var": "error",
|
|
75
|
-
"no-labels": ["error", {"allowLoop": true, "allowSwitch": false}],
|
|
76
|
-
"no-lone-blocks": "error",
|
|
77
|
-
"no-mixed-operators": ["error", {
|
|
78
|
-
"groups": [
|
|
79
|
-
["==", "!=", "===", "!==", ">", ">=", "<", "<="],
|
|
80
|
-
["&&", "||"],
|
|
81
|
-
["in", "instanceof"]
|
|
82
|
-
],
|
|
83
|
-
"allowSamePrecedence": true
|
|
84
|
-
}],
|
|
85
|
-
"no-mixed-spaces-and-tabs": "error",
|
|
86
|
-
"no-multi-spaces": "error",
|
|
87
|
-
"no-multi-str": "error",
|
|
88
|
-
"no-multiple-empty-lines": ["error", {"max": 1, "maxEOF": 0}],
|
|
89
|
-
"no-negated-in-lhs": "error",
|
|
90
|
-
"no-new": "error",
|
|
91
|
-
"no-new-func": "error",
|
|
92
|
-
"no-new-object": "error",
|
|
93
|
-
"no-new-require": "error",
|
|
94
|
-
"no-new-symbol": "error",
|
|
95
|
-
"no-new-wrappers": "error",
|
|
96
|
-
"no-obj-calls": "error",
|
|
97
|
-
"no-octal": "error",
|
|
98
|
-
"no-octal-escape": "error",
|
|
99
|
-
"no-path-concat": "error",
|
|
100
|
-
"no-proto": "error",
|
|
101
|
-
"no-redeclare": "error",
|
|
102
|
-
"no-regex-spaces": "error",
|
|
103
|
-
"no-return-await": "error",
|
|
104
|
-
"no-self-assign": "error",
|
|
105
|
-
"no-self-compare": "error",
|
|
106
|
-
"no-sequences": "error",
|
|
107
|
-
"no-shadow-restricted-names": "error",
|
|
108
|
-
"no-sparse-arrays": "error",
|
|
109
|
-
"no-template-curly-in-string": "error",
|
|
110
|
-
"no-this-before-super": "error",
|
|
111
|
-
"no-throw-literal": "error",
|
|
112
|
-
"no-trailing-spaces": "error",
|
|
113
|
-
"no-undef": "off",
|
|
114
|
-
"no-undef-init": "error",
|
|
115
|
-
"no-unexpected-multiline": "error",
|
|
116
|
-
"no-unmodified-loop-condition": "error",
|
|
117
|
-
"no-unneeded-ternary": ["error", {"defaultAssignment": false}],
|
|
118
|
-
"no-unreachable": "error",
|
|
119
|
-
"no-unsafe-finally": "error",
|
|
120
|
-
"no-unsafe-negation": "error",
|
|
121
|
-
"no-unused-expressions": ["error", {
|
|
122
|
-
"allowShortCircuit": true,
|
|
123
|
-
"allowTernary": true,
|
|
124
|
-
"allowTaggedTemplates": true
|
|
125
|
-
}],
|
|
126
|
-
"no-unused-vars": "off",
|
|
127
|
-
"no-use-before-define": ["error", {"functions": false, "classes": false, "variables": false}],
|
|
128
|
-
"no-useless-call": "error",
|
|
129
|
-
"no-useless-computed-key": "error",
|
|
130
|
-
"no-useless-constructor": "error",
|
|
131
|
-
"no-useless-escape": "error",
|
|
132
|
-
"no-useless-rename": "error",
|
|
133
|
-
"no-useless-return": "error",
|
|
134
|
-
"no-whitespace-before-property": "error",
|
|
135
|
-
"no-with": "error",
|
|
136
|
-
"object-property-newline": ["error", {"allowMultiplePropertiesPerLine": true}],
|
|
137
|
-
"one-var": ["error", {"initialized": "never"}],
|
|
138
|
-
"operator-linebreak": ["error", "after", {
|
|
139
|
-
"overrides": {
|
|
140
|
-
"?": "before",
|
|
141
|
-
":": "before",
|
|
142
|
-
"+": "before",
|
|
143
|
-
"-": "before",
|
|
144
|
-
"*": "before",
|
|
145
|
-
"/": "before",
|
|
146
|
-
"||": "before",
|
|
147
|
-
"&&": "before"
|
|
148
|
-
}
|
|
149
|
-
}],
|
|
150
|
-
"padded-blocks": ["error", {"blocks": "never", "switches": "never", "classes": "never"}],
|
|
151
|
-
"prefer-promise-reject-errors": "error",
|
|
152
|
-
"rest-spread-spacing": ["error", "never"],
|
|
153
|
-
"semi": ["warn", "always"],
|
|
154
|
-
"semi-spacing": ["error", {"before": false, "after": true}],
|
|
155
|
-
"space-before-blocks": ["error", "always"],
|
|
156
|
-
"space-before-function-paren": ["error", "always"],
|
|
157
|
-
"space-in-parens": ["error", "never"],
|
|
158
|
-
"space-infix-ops": "error",
|
|
159
|
-
"space-unary-ops": ["error", {"words": true, "nonwords": false}],
|
|
160
|
-
"spaced-comment": ["error", "always", {
|
|
161
|
-
"line": {"markers": ["*package", "!", "/", ",", "="]},
|
|
162
|
-
"block": {
|
|
163
|
-
"balanced": true,
|
|
164
|
-
"markers": ["*package", "!", ",", ":", "::", "flow-include"],
|
|
165
|
-
"exceptions": ["*"]
|
|
166
|
-
}
|
|
167
|
-
}],
|
|
168
|
-
"symbol-description": "error",
|
|
169
|
-
"template-curly-spacing": ["error", "never"],
|
|
170
|
-
"template-tag-spacing": ["error", "never"],
|
|
171
|
-
"unicode-bom": ["error", "never"],
|
|
172
|
-
"use-isnan": "error",
|
|
173
|
-
"valid-typeof": ["error", {"requireStringLiterals": true}],
|
|
174
|
-
"wrap-iife": ["error", "any", {"functionPrototypeMethods": true}],
|
|
175
|
-
"yield-star-spacing": ["error", "both"],
|
|
176
|
-
"yoda": ["error", "never"],
|
|
177
|
-
"no-prototype-builtins": "off",
|
|
178
|
-
"require-atomic-updates": "off",
|
|
179
|
-
"no-console": "error",
|
|
180
|
-
"prefer-template": "error",
|
|
181
|
-
"quotes": ["error", "double", {"allowTemplateLiterals": true}],
|
|
182
|
-
"no-var": "error"
|
|
183
|
-
}
|
|
184
|
-
};
|
package/HelloWorld.js
DELETED