runtime-type-inspector 1.0.0 → 2.0.1
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/.eslintrc.js +480 -0
- package/README.md +12 -4
- package/jsconfig.json +10 -0
- package/package.json +32 -4
- package/plugin-parcel/index.mjs +4 -0
- package/plugin-rollup/index.mjs +78 -0
- package/plugin-webpack/index.mjs +4 -0
- package/rollup.config.mjs +183 -0
- package/src-runtime/assertType.mjs +54 -0
- package/src-runtime/createDiv.mjs +69 -0
- package/src-runtime/index.mjs +21 -0
- package/src-runtime/makeJSDoc.mjs +40 -0
- package/src-runtime/registerClass.mjs +11 -0
- package/src-runtime/registerTypedef.mjs +12 -0
- package/src-runtime/typecheckDiv.mjs +3 -0
- package/src-runtime/typecheckEvery.mjs +21 -0
- package/src-runtime/typecheckOptions.mjs +60 -0
- package/src-runtime/typecheckTable.mjs +14 -0
- package/src-runtime/typecheckWarn.mjs +23 -0
- package/src-runtime/validateArray.mjs +26 -0
- package/src-runtime/validateDivision.mjs +31 -0
- package/src-runtime/validateMap.mjs +36 -0
- package/src-runtime/validateNumber.mjs +29 -0
- package/src-runtime/validateObject.mjs +43 -0
- package/src-runtime/validateRecord.mjs +31 -0
- package/src-runtime/validateSet.mjs +26 -0
- package/src-runtime/validateTuple.mjs +28 -0
- package/src-runtime/validateType.mjs +214 -0
- package/src-runtime/validateTypedef.mjs +18 -0
- package/src-runtime/validateUnion.mjs +19 -0
- package/src-runtime/youCanAddABreakpointHere.mjs +11 -0
- package/src-transpiler/Stringifier.mjs +1268 -0
- package/src-transpiler/StringifierWithTypeAssertions.mjs +478 -0
- package/src-transpiler/addTypeChecks.mjs +20 -0
- package/src-transpiler/ast2json.mjs +13 -0
- package/src-transpiler/ast2jsonForComparison.mjs +28 -0
- package/src-transpiler/code2ast2code.mjs +16 -0
- package/src-transpiler/expandType.mjs +187 -0
- package/src-transpiler/expandTypeDepFree.mjs +122 -0
- package/src-transpiler/extractNameAndOptionality.mjs +23 -0
- package/src-transpiler/index.mjs +13 -0
- package/src-transpiler/parseJSDoc.mjs +69 -0
- package/src-transpiler/parseJSDocSetter.mjs +16 -0
- package/src-transpiler/parseJSDocTypedef.mjs +69 -0
- package/src-transpiler/simplifyType.mjs +31 -0
- package/src-transpiler/stat.mjs +13 -0
- package/ui/babel-core.mjs +5 -0
- package/ui/babel-helper-plugin-utils.mjs +7 -0
- package/ui/babel-parser.mjs +8 -0
- package/ui/babel-plugin-syntax-typescript.mjs +58 -0
- package/ui/importmap.js +38 -0
- package/ui/index.html +30 -0
- package/ui/index.mjs +379 -0
- package/src/index.mjs +0 -1
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
// 1st party Rollup plugins
|
|
5
|
+
import { createFilter } from '@rollup/pluginutils';
|
|
6
|
+
import { babel } from '@rollup/plugin-babel';
|
|
7
|
+
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
|
8
|
+
import terser from '@rollup/plugin-terser';
|
|
9
|
+
// 3rd party Rollup plugins
|
|
10
|
+
import dts from 'rollup-plugin-dts';
|
|
11
|
+
import { visualizer } from 'rollup-plugin-visualizer';
|
|
12
|
+
import json from '@rollup/plugin-json';
|
|
13
|
+
//import { addTypeChecks } from "./src-transpiler/typeInserter.mjs";
|
|
14
|
+
//const { addTypeChecks } = await import("runtime-type-inspector/src-transpiler/typeInserter.mjs");
|
|
15
|
+
import commonjs from '@rollup/plugin-commonjs';
|
|
16
|
+
import { runtimeTypeInspector } from './plugin-rollup/index.mjs';
|
|
17
|
+
/** @typedef {import('rollup').RollupOptions} RollupOptions */
|
|
18
|
+
/** @typedef {import('rollup').Plugin} Plugin */
|
|
19
|
+
/** @typedef {import('rollup').OutputOptions} OutputOptions */
|
|
20
|
+
/** @typedef {import('@rollup/plugin-babel').RollupBabelInputPluginOptions} RollupBabelInputPluginOptions */
|
|
21
|
+
/**
|
|
22
|
+
* The ES5 options for babel(...) plugin.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} buildType - Only 'debug' requires special handling so far.
|
|
25
|
+
* @returns {RollupBabelInputPluginOptions} The babel options.
|
|
26
|
+
*/
|
|
27
|
+
const es5Options = buildType => ({
|
|
28
|
+
babelHelpers: 'bundled',
|
|
29
|
+
babelrc: false,
|
|
30
|
+
comments: buildType === 'debug' || buildType === 'rti',
|
|
31
|
+
compact: false,
|
|
32
|
+
minified: false,
|
|
33
|
+
presets: [
|
|
34
|
+
[
|
|
35
|
+
'@babel/preset-env', {
|
|
36
|
+
loose: !true,
|
|
37
|
+
modules: false,
|
|
38
|
+
targets: {
|
|
39
|
+
ie: '11'
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
]
|
|
44
|
+
});
|
|
45
|
+
/**
|
|
46
|
+
* The ES6 options for babel(...) plugin.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} buildType - Only 'debug' requires special handling so far.
|
|
49
|
+
* @returns {RollupBabelInputPluginOptions} The babel options.
|
|
50
|
+
*/
|
|
51
|
+
const moduleOptions = buildType => ({
|
|
52
|
+
babelHelpers: 'bundled',
|
|
53
|
+
babelrc: false,
|
|
54
|
+
comments: buildType === 'debug',
|
|
55
|
+
compact: false,
|
|
56
|
+
minified: false,
|
|
57
|
+
presets: [
|
|
58
|
+
[
|
|
59
|
+
'@babel/preset-env', {
|
|
60
|
+
bugfixes: true,
|
|
61
|
+
loose: true,
|
|
62
|
+
modules: false,
|
|
63
|
+
targets: {
|
|
64
|
+
esmodules: true
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
]
|
|
69
|
+
});
|
|
70
|
+
/**
|
|
71
|
+
* Build a target that rollup is supposed to build.
|
|
72
|
+
*
|
|
73
|
+
* @param {'debug'|'release'|'profiler'|'min'} buildType - The build type.
|
|
74
|
+
* @param {'es5'|'es6'} moduleFormat - The module format.
|
|
75
|
+
* @returns {RollupOptions} One rollup target.
|
|
76
|
+
*/
|
|
77
|
+
function buildTarget(buildType, moduleFormat) {
|
|
78
|
+
const banner = {
|
|
79
|
+
debug: ' (DEBUG)',
|
|
80
|
+
release: ' (RELEASE)',
|
|
81
|
+
profiler: ' (PROFILE)',
|
|
82
|
+
min: ' (RELEASE)'
|
|
83
|
+
};
|
|
84
|
+
const outputPlugins = {
|
|
85
|
+
release: [],
|
|
86
|
+
min: [
|
|
87
|
+
terser()
|
|
88
|
+
]
|
|
89
|
+
};
|
|
90
|
+
if (process.env.treemap) {
|
|
91
|
+
outputPlugins.min.push(visualizer({
|
|
92
|
+
filename: 'treemap.html',
|
|
93
|
+
brotliSize: true,
|
|
94
|
+
gzipSize: true
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
if (process.env.treenet) {
|
|
98
|
+
outputPlugins.min.push(visualizer({
|
|
99
|
+
filename: 'treenet.html',
|
|
100
|
+
template: 'network'
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
if (process.env.treesun) {
|
|
104
|
+
outputPlugins.min.push(visualizer({
|
|
105
|
+
filename: 'treesun.html',
|
|
106
|
+
template: 'sunburst'
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
const name = 'rti-transpiler';
|
|
110
|
+
const outputFile = {
|
|
111
|
+
release: `build/${name}`,
|
|
112
|
+
debug: `build/${name}.dbg`,
|
|
113
|
+
profiler: `build/${name}.prf`,
|
|
114
|
+
min: `build/${name}.min`,
|
|
115
|
+
rti: `build/${name}.rti`,
|
|
116
|
+
};
|
|
117
|
+
const outputExtension = {
|
|
118
|
+
es5: '.js',
|
|
119
|
+
es6: '.mjs'
|
|
120
|
+
};
|
|
121
|
+
/** @type {OutputOptions} */
|
|
122
|
+
const outputOptions = {
|
|
123
|
+
plugins: outputPlugins[buildType || outputPlugins.release],
|
|
124
|
+
format: 'es',
|
|
125
|
+
indent: '\t',
|
|
126
|
+
sourcemap: false,
|
|
127
|
+
name: 'pc',
|
|
128
|
+
preserveModules: false,
|
|
129
|
+
file: `${outputFile[buildType]}${outputExtension[moduleFormat]}`
|
|
130
|
+
};
|
|
131
|
+
const babelOptions = {
|
|
132
|
+
es5: es5Options(buildType),
|
|
133
|
+
es6: moduleOptions(buildType)
|
|
134
|
+
};
|
|
135
|
+
const rootFile = 'src-transpiler/index.mjs';
|
|
136
|
+
console.log("buildType", buildType);
|
|
137
|
+
return {
|
|
138
|
+
input: rootFile,
|
|
139
|
+
output: outputOptions,
|
|
140
|
+
plugins: [
|
|
141
|
+
//runtimeTypeInspector(buildType === 'debug'),
|
|
142
|
+
babel(babelOptions[moduleFormat]),
|
|
143
|
+
//nodeResolve({
|
|
144
|
+
// browser: true
|
|
145
|
+
//}),
|
|
146
|
+
//commonjs(),
|
|
147
|
+
//json(),
|
|
148
|
+
],
|
|
149
|
+
///external: ['fs', 'path', 'url', 'sharp', 'onnxruntime-node', 'onnxruntime-web', 'stream/web'],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/** @type {RollupOptions} */
|
|
153
|
+
const target_types = {
|
|
154
|
+
input: 'types/index.d.ts',
|
|
155
|
+
output: [{
|
|
156
|
+
file: 'build/runtime-type-inspector.d.ts',
|
|
157
|
+
footer: 'export as namespace pc;',
|
|
158
|
+
format: 'es'
|
|
159
|
+
}],
|
|
160
|
+
plugins: [
|
|
161
|
+
dts()
|
|
162
|
+
]
|
|
163
|
+
};
|
|
164
|
+
export default (args) => {
|
|
165
|
+
/** @type {RollupOptions[]} */
|
|
166
|
+
let targets = [];
|
|
167
|
+
const envTarget = process.env.target ? process.env.target.toLowerCase() : null;
|
|
168
|
+
if (envTarget === 'types') {
|
|
169
|
+
targets.push(target_types);
|
|
170
|
+
} else if (envTarget === 'extras') {
|
|
171
|
+
targets = targets.concat(target_extras);
|
|
172
|
+
} else {
|
|
173
|
+
['release', 'debug', 'profiler', 'min', 'rti'].forEach((t) => {
|
|
174
|
+
['es5', 'es6'].forEach((m) => {
|
|
175
|
+
console.log("envTarget", envTarget);
|
|
176
|
+
if (envTarget === null || envTarget === t || envTarget === m || envTarget === `${t}_${m}`) {
|
|
177
|
+
targets.push(buildTarget(t, m));
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return targets;
|
|
183
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {typecheckOptions } from "./typecheckOptions.mjs";
|
|
2
|
+
import {typecheckWarnedTable} from "./typecheckTable.mjs";
|
|
3
|
+
import {typecheckWarn } from "./typecheckWarn.mjs";
|
|
4
|
+
import {validateType } from "./validateType.mjs";
|
|
5
|
+
/**
|
|
6
|
+
* @param {*} value - The actual value that we need to validate.
|
|
7
|
+
* @param {*} expect - The supposed type information of said value.
|
|
8
|
+
* @param {string} loc - String like `BoundingBox#compute`
|
|
9
|
+
* @param {string} name - Name of the argument
|
|
10
|
+
* @param {boolean} critical - Only `false` for unions.
|
|
11
|
+
* @returns {boolean} - Boolean indicating if a type is correct.
|
|
12
|
+
*/
|
|
13
|
+
export function assertType(value, expect, loc, name, critical = true) {
|
|
14
|
+
if (!expect) {
|
|
15
|
+
typecheckWarn("assertType> 'expect' always should be set");
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
const ret = validateType(value, expect, loc, name, critical);
|
|
19
|
+
if (!ret && critical) {
|
|
20
|
+
typecheckOptions.count++;
|
|
21
|
+
let expectStr = ', expected: ' + JSON.stringify(expect);
|
|
22
|
+
if (expectStr.length >= 40) {
|
|
23
|
+
expectStr = ', expected: ';
|
|
24
|
+
}
|
|
25
|
+
expectStr = '';
|
|
26
|
+
const msg = `${loc}> type of '${name}' is invalid${expectStr}`;
|
|
27
|
+
typecheckWarn(msg, { expect, value });
|
|
28
|
+
const warnObj = typecheckOptions.warned[msg];
|
|
29
|
+
if (!warnObj.tr) {
|
|
30
|
+
const tr = document.createElement('tr');
|
|
31
|
+
const dbg = document.createElement('td');
|
|
32
|
+
const dbgInput = document.createElement("input");
|
|
33
|
+
dbgInput.type = "checkbox";
|
|
34
|
+
dbgInput.onchange = () => {
|
|
35
|
+
warnObj.dbg = dbgInput.checked;
|
|
36
|
+
}
|
|
37
|
+
const count = document.createElement('td');
|
|
38
|
+
const desc = document.createElement('td');
|
|
39
|
+
desc.innerText = msg;
|
|
40
|
+
tr.append(dbg, count, desc);
|
|
41
|
+
dbg.append(dbgInput);
|
|
42
|
+
typecheckWarnedTable.append(tr);
|
|
43
|
+
warnObj.tr = tr;
|
|
44
|
+
}
|
|
45
|
+
const {tr, dbg} = warnObj;
|
|
46
|
+
if (dbg) {
|
|
47
|
+
debugger;
|
|
48
|
+
warnObj.dbg = false; // trigger only once to quickly get app running again
|
|
49
|
+
tr.children[0].children[0].checked = false; // update ui state
|
|
50
|
+
}
|
|
51
|
+
tr.children[1].textContent = warnObj.hits;
|
|
52
|
+
}
|
|
53
|
+
return ret;
|
|
54
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {typecheckOptions } from "./typecheckOptions.mjs";
|
|
2
|
+
import {typecheckWarnedTable} from "./typecheckTable.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* @param {HTMLDivElement} div - The <div>.
|
|
5
|
+
*/
|
|
6
|
+
function niceDiv(div) {
|
|
7
|
+
div.style.border = "1px solid #C1C1C1";
|
|
8
|
+
div.style.margin = "10px";
|
|
9
|
+
div.style.padding = "10px";
|
|
10
|
+
div.style.textAlign = "left";
|
|
11
|
+
div.style.lineHeight = "25px";
|
|
12
|
+
div.style.backgroundColor = "#F3F3F3";
|
|
13
|
+
div.style.borderRadius = "4px";
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* @todo
|
|
17
|
+
* Show number of validations too? Might be too noisy.
|
|
18
|
+
* @returns {HTMLDivElement}
|
|
19
|
+
*/
|
|
20
|
+
function createDiv() {
|
|
21
|
+
const div = document.createElement("div");
|
|
22
|
+
div.style.position = "absolute";
|
|
23
|
+
div.style.bottom = "0px";
|
|
24
|
+
div.style.right = "0px";
|
|
25
|
+
div.style.zIndex = "1";
|
|
26
|
+
niceDiv(div);
|
|
27
|
+
const spanErrors = document.createElement("span");
|
|
28
|
+
const span = document.createElement("span");
|
|
29
|
+
span.innerText = " Spam type reports:";
|
|
30
|
+
const input = document.createElement("input");
|
|
31
|
+
input.type = "checkbox";
|
|
32
|
+
const spamTypeReports = localStorage.getItem('rti-spam-type-reports');
|
|
33
|
+
input.checked = true;
|
|
34
|
+
if (spamTypeReports !== null) {
|
|
35
|
+
input.checked = spamTypeReports === 'spam' ? true : false;
|
|
36
|
+
}
|
|
37
|
+
const onchange = () => {
|
|
38
|
+
localStorage.setItem('rti-spam-type-reports', input.checked ? 'spam' : 'once');
|
|
39
|
+
if (input.checked) {
|
|
40
|
+
typecheckOptions.mode = "spam";
|
|
41
|
+
} else {
|
|
42
|
+
typecheckOptions.mode = "once";
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
input.onchange = onchange;
|
|
46
|
+
onchange(); // update mode from localStorage
|
|
47
|
+
input.checked = true;
|
|
48
|
+
const buttonHide = document.createElement("button");
|
|
49
|
+
buttonHide.textContent = 'Hide';
|
|
50
|
+
buttonHide.onclick = () => {
|
|
51
|
+
div.style.display = 'none';
|
|
52
|
+
};
|
|
53
|
+
div.append(spanErrors, span, input, buttonHide, typecheckWarnedTable);
|
|
54
|
+
div.style.maxHeight = '200px';
|
|
55
|
+
div.style.overflow = 'scroll';
|
|
56
|
+
const finalFunc = () => document.body.append(div);
|
|
57
|
+
// Add our <div> to <body> when possible
|
|
58
|
+
if (document.readyState === "complete") {
|
|
59
|
+
finalFunc();
|
|
60
|
+
} else {
|
|
61
|
+
// add when page is loaded
|
|
62
|
+
document.addEventListener("DOMContentLoaded", finalFunc);
|
|
63
|
+
}
|
|
64
|
+
setInterval(() => {
|
|
65
|
+
spanErrors.innerText = `Type validation errors: ${typecheckOptions.count}`;
|
|
66
|
+
}, 100);
|
|
67
|
+
return div;
|
|
68
|
+
}
|
|
69
|
+
export {createDiv};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export * from './assertType.mjs';
|
|
2
|
+
export * from './createDiv.mjs';
|
|
3
|
+
export * from './makeJSDoc.mjs';
|
|
4
|
+
export * from './registerClass.mjs';
|
|
5
|
+
export * from './registerTypedef.mjs';
|
|
6
|
+
export * from './typecheckDiv.mjs';
|
|
7
|
+
export * from './typecheckEvery.mjs';
|
|
8
|
+
export * from './typecheckOptions.mjs';
|
|
9
|
+
export * from './typecheckTable.mjs';
|
|
10
|
+
export * from './typecheckWarn.mjs';
|
|
11
|
+
export * from './validateArray.mjs';
|
|
12
|
+
export * from './validateDivision.mjs';
|
|
13
|
+
export * from './validateMap.mjs';
|
|
14
|
+
export * from './validateNumber.mjs';
|
|
15
|
+
export * from './validateObject.mjs';
|
|
16
|
+
export * from './validateRecord.mjs';
|
|
17
|
+
export * from './validateTuple.mjs';
|
|
18
|
+
export * from './validateType.mjs';
|
|
19
|
+
export * from './validateTypedef.mjs';
|
|
20
|
+
export * from './validateUnion.mjs';
|
|
21
|
+
export * from './youCanAddABreakpointHere.mjs';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @type {Record<string, string>}
|
|
3
|
+
*/
|
|
4
|
+
const jsdocCache = {};
|
|
5
|
+
/**
|
|
6
|
+
* @todo
|
|
7
|
+
* update jsdoc cache if it changes or keep all possible version to generate maybe multiple function signatures
|
|
8
|
+
* @example
|
|
9
|
+
* pc.makeJSDoc('VignetteEffect#render', ["inputTarget", "outputTarget", "rect"], [inputTarget, outputTarget, rect])
|
|
10
|
+
* @param {string} loc - The location.
|
|
11
|
+
* @param {string[]} names - The names
|
|
12
|
+
* @param {any[]} values - The values
|
|
13
|
+
*/
|
|
14
|
+
function makeJSDoc(loc, names, values) {
|
|
15
|
+
const n = names.length;
|
|
16
|
+
let jsdoc = ' /**\n';
|
|
17
|
+
for (var i = 0; i < n; i++) {
|
|
18
|
+
const name = names[i];
|
|
19
|
+
const value = values[i];
|
|
20
|
+
/** @type {string} */
|
|
21
|
+
let type = typeof value;
|
|
22
|
+
if (value === null) {
|
|
23
|
+
type = 'null';
|
|
24
|
+
} else if (value instanceof Object) {
|
|
25
|
+
type = value.constructor.name;
|
|
26
|
+
}
|
|
27
|
+
if (type !== 'string' && pc[type]) {
|
|
28
|
+
type = 'pc.' + type;
|
|
29
|
+
}
|
|
30
|
+
//debugger;
|
|
31
|
+
jsdoc += ` * @param {${type}} ${name} - todo/jsdoc\n`
|
|
32
|
+
}
|
|
33
|
+
jsdoc += ' */';
|
|
34
|
+
if (!jsdocCache[loc]) {
|
|
35
|
+
jsdocCache[loc] = jsdoc;
|
|
36
|
+
console.log(loc);
|
|
37
|
+
console.log(jsdoc);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export {jsdocCache, makeJSDoc};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @type {Record<string, object>}
|
|
3
|
+
*/
|
|
4
|
+
const typedefs = {};
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} name - Name of typedef.
|
|
7
|
+
* @param {Record<string, object>} typedef - The parsed typedef from source.
|
|
8
|
+
*/
|
|
9
|
+
function registerTypedef(name, typedef) {
|
|
10
|
+
typedefs[name] = typedef;
|
|
11
|
+
}
|
|
12
|
+
export {typedefs, registerTypedef};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This also works for an empty array, unlike `Array#every`.
|
|
3
|
+
* @example
|
|
4
|
+
* const a = [];
|
|
5
|
+
* a.length = 10;
|
|
6
|
+
* typecheckEvery(a, (e, i) => { console.log(e); return true; }
|
|
7
|
+
* @param {*[]} arr - The array to test.
|
|
8
|
+
* @param {Function} fn - The callback function to test each element with.
|
|
9
|
+
* @returns {boolean} True if every callback call is true aswell.
|
|
10
|
+
*/
|
|
11
|
+
function typecheckEvery(arr, fn) {
|
|
12
|
+
const n = arr.length;
|
|
13
|
+
for (let i = 0; i < n; i++) {
|
|
14
|
+
const ret = fn(arr[i], i, arr);
|
|
15
|
+
if (!ret) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
export {typecheckEvery};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
function typecheckReport() {
|
|
2
|
+
console.table(typecheckOptions.warned);
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* @param {Object<string, any>} obj - The object to clear.
|
|
6
|
+
*/
|
|
7
|
+
function clearObject(obj) {
|
|
8
|
+
Object.keys(obj).forEach(_ => delete obj[_]);
|
|
9
|
+
}
|
|
10
|
+
function typecheckReset() {
|
|
11
|
+
clearObject(typecheckOptions.warned);
|
|
12
|
+
}
|
|
13
|
+
const typecheckOptions = {
|
|
14
|
+
/**
|
|
15
|
+
* @type {'spam'|'once'} - In spam-mode every message is printed. In once-mode a cache is
|
|
16
|
+
* looked up to check if it was printed already. Spam can be too noisy if there are too
|
|
17
|
+
* many type errors, so the best way to keep it quiet is to fix the noisiest type issues first.
|
|
18
|
+
* Spam-mode basically retains the order, which mentally helps to figure out the actual issues.
|
|
19
|
+
*/
|
|
20
|
+
mode: 'spam',
|
|
21
|
+
/**
|
|
22
|
+
* @type {Record<string, {hits: number, tr: HTMLTableRowElement, dbg: boolean}>}
|
|
23
|
+
*/
|
|
24
|
+
warned: {},
|
|
25
|
+
logSuperfluousProperty: false,
|
|
26
|
+
/**
|
|
27
|
+
* @todo integrate PC/Transformers Examples browser + custom config for each project
|
|
28
|
+
*/
|
|
29
|
+
customTypes: {
|
|
30
|
+
AnimSetter(value) {
|
|
31
|
+
//console.log("@todo Is AnimSetter?", value);
|
|
32
|
+
return true;
|
|
33
|
+
},
|
|
34
|
+
AnimBinder(value) {
|
|
35
|
+
//console.log("@todo Is AnimBinder?", value);
|
|
36
|
+
return true;
|
|
37
|
+
},
|
|
38
|
+
AnimCurvePath(value) {
|
|
39
|
+
//console.log("@todo Is AnimCurvePath?", value);
|
|
40
|
+
return true;
|
|
41
|
+
},
|
|
42
|
+
ComponentData(value) {
|
|
43
|
+
//console.log("@todo Is ComponentData?", value);
|
|
44
|
+
return true;
|
|
45
|
+
},
|
|
46
|
+
Renderer(value) {
|
|
47
|
+
// E.g. instance of `ForwardRenderer`
|
|
48
|
+
//debugger;
|
|
49
|
+
return value?.constructor?.name?.endsWith("Renderer");
|
|
50
|
+
},
|
|
51
|
+
IArguments(value) {
|
|
52
|
+
//console.log("@todo Is IArguments?", value);
|
|
53
|
+
return true;
|
|
54
|
+
},
|
|
55
|
+
// @todo validate textures having width != 0 and height != 0
|
|
56
|
+
// @todo validate integers
|
|
57
|
+
},
|
|
58
|
+
count: 0,
|
|
59
|
+
};
|
|
60
|
+
export {typecheckReport, clearObject, typecheckReset, typecheckOptions};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const typecheckWarnedTable = document.createElement('table');
|
|
2
|
+
const tr = document.createElement('tr');
|
|
3
|
+
const th_0 = document.createElement('th');
|
|
4
|
+
const th_1 = document.createElement('th');
|
|
5
|
+
const th_2 = document.createElement('th');
|
|
6
|
+
th_0.innerText = 'dbg;';
|
|
7
|
+
th_1.innerText = 'Hits';
|
|
8
|
+
th_2.innerText = 'Message';
|
|
9
|
+
tr.append(th_0, th_1, th_2);
|
|
10
|
+
typecheckWarnedTable.append(tr);
|
|
11
|
+
//typecheckWarnedTable.style.maxHeight = '200px';
|
|
12
|
+
//typecheckWarnedTable.style.overflow = 'scroll';
|
|
13
|
+
//typecheckWarnedTable.style.textWrap = 'nowrap';
|
|
14
|
+
export {typecheckWarnedTable};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {typecheckOptions} from "./typecheckOptions.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* @param {string} msg - The main message.
|
|
4
|
+
* @param {...any} extra - Extra strings or objects etc.
|
|
5
|
+
*/
|
|
6
|
+
function typecheckWarn(msg, ...extra) {
|
|
7
|
+
const { mode, warned } = typecheckOptions;
|
|
8
|
+
warned[msg] = warned[msg] || { hits: 0 };
|
|
9
|
+
warned[msg].hits++;
|
|
10
|
+
switch (mode) {
|
|
11
|
+
case 'spam':
|
|
12
|
+
console.error(msg, ...extra);
|
|
13
|
+
break;
|
|
14
|
+
case 'once':
|
|
15
|
+
if (warned[msg].hits === 1) {
|
|
16
|
+
console.error(msg, ...extra);
|
|
17
|
+
}
|
|
18
|
+
break;
|
|
19
|
+
default:
|
|
20
|
+
console.error("typecheckWarn> unsupported mode:", mode);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export {typecheckWarn};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {assertType } from "./assertType.mjs";
|
|
2
|
+
import {typecheckEvery} from "./typecheckEvery.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* @param {*} value
|
|
5
|
+
* @param {*} expect
|
|
6
|
+
* @param {*} loc
|
|
7
|
+
* @param {*} name
|
|
8
|
+
* @param {*} critical
|
|
9
|
+
* @returns {boolean}
|
|
10
|
+
*/
|
|
11
|
+
function validateArray(value, expect, loc, name, critical) {
|
|
12
|
+
if (value && value instanceof Array) {
|
|
13
|
+
const { elementType } = expect;
|
|
14
|
+
// some that not validate -> type error
|
|
15
|
+
return typecheckEvery(value, (_, i) => assertType(
|
|
16
|
+
_,
|
|
17
|
+
elementType,
|
|
18
|
+
loc,
|
|
19
|
+
`${name}[${i}]`,
|
|
20
|
+
critical
|
|
21
|
+
));
|
|
22
|
+
}
|
|
23
|
+
// if it's not even an array -> type error
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
export {validateArray};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {typecheckWarn} from "./typecheckWarn.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* @param {number} lhs - The left hand side.
|
|
4
|
+
* @param {number} rhs - The right hand side.
|
|
5
|
+
* @example
|
|
6
|
+
* validateDivision( 1 , 2 ); // Outputs: 0.5
|
|
7
|
+
* validateDivision( 1 , "2"); // Warns: validateDivision> incompatible type pair
|
|
8
|
+
* validateDivision(10n, 2n); // Outputs: 5n
|
|
9
|
+
* @returns {number}
|
|
10
|
+
*/
|
|
11
|
+
function validateDivision(lhs, rhs) {
|
|
12
|
+
const ret = lhs / rhs;
|
|
13
|
+
const twoNumbers = typeof lhs === 'number' && typeof rhs === 'number';
|
|
14
|
+
const twoBigInts = typeof lhs === 'bigint' && typeof rhs === 'bigint';
|
|
15
|
+
const valid = twoNumbers || twoBigInts;
|
|
16
|
+
if (!valid) {
|
|
17
|
+
typecheckWarn(`validateDivision> incompatible type pair`, { lhs, rhs, twoNumbers, twoBigInts });
|
|
18
|
+
}
|
|
19
|
+
// If we got two bigint's, we are done, as isNaN and isFinite is only for "normal" numbers.
|
|
20
|
+
if (twoBigInts) {
|
|
21
|
+
return ret;
|
|
22
|
+
}
|
|
23
|
+
if (isNaN(ret)) {
|
|
24
|
+
typecheckWarn(`validateDivision> NaN`, { lhs, rhs });
|
|
25
|
+
}
|
|
26
|
+
if (!isFinite(ret)) {
|
|
27
|
+
typecheckWarn(`validateDivision> +-Infinity`, { lhs, rhs });
|
|
28
|
+
}
|
|
29
|
+
return ret;
|
|
30
|
+
}
|
|
31
|
+
export {validateDivision};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {assertType } from "./assertType.mjs";
|
|
2
|
+
import {typecheckWarn} from "./typecheckWarn.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* @param {*} value
|
|
5
|
+
* @param {*} expect
|
|
6
|
+
* @param {*} loc
|
|
7
|
+
* @param {*} name
|
|
8
|
+
* @param {*} critical
|
|
9
|
+
* @returns {boolean}
|
|
10
|
+
*/
|
|
11
|
+
function validateMap(value, expect, loc, name, critical) {
|
|
12
|
+
const { key, val } = expect;
|
|
13
|
+
if (key !== 'string') {
|
|
14
|
+
typecheckWarn(`${loc}> validateType> map> unhandled key '${key}'`);
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
if (val !== 'any') {
|
|
18
|
+
typecheckWarn(`${loc}> validateType> map> expected any, not '${value}'`);
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
let ret = true;
|
|
22
|
+
for (const [k, v] of value) {
|
|
23
|
+
const good = assertType(
|
|
24
|
+
v,
|
|
25
|
+
val,
|
|
26
|
+
loc,
|
|
27
|
+
`${name}.get('${key}')`,
|
|
28
|
+
critical
|
|
29
|
+
);
|
|
30
|
+
if (!good) {
|
|
31
|
+
ret = false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return ret;
|
|
35
|
+
}
|
|
36
|
+
export {validateMap};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {typecheckWarn} from "./typecheckWarn.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* Validates if a given property is neither NaN nor +-Infinity
|
|
4
|
+
* @param {Object<string|number, number>} obj - The object to test a property of.
|
|
5
|
+
* @param {string|number} prop - Name or index or property.
|
|
6
|
+
* @returns {boolean} True if property is a "proper" number (neither Nan nor Infinity)
|
|
7
|
+
*/
|
|
8
|
+
function validateNumber(obj, prop) {
|
|
9
|
+
const val = obj[prop];
|
|
10
|
+
const type = typeof obj;
|
|
11
|
+
if (val === null) {
|
|
12
|
+
typecheckWarn(`${type}#${prop} null`, { obj });
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
if (val === undefined) {
|
|
16
|
+
typecheckWarn(`${type}#${prop} undefined`, { obj });
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (isNaN(val)) {
|
|
20
|
+
typecheckWarn(`${type}#${prop} NaN`, { obj });
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (!isFinite(val)) {
|
|
24
|
+
typecheckWarn(`${type}#${prop} +-Infinity`, { obj });
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
export {validateNumber};
|