Skip to content

Commit 44d965a

Browse files
authored
Remove TypePanel instantiation to make it mandatory to instantiate TypePanel manually (#198)
* Remove TypePanel instantiation * Refactor some functions from `options.js` into `TypePanel` for proper multi-context support in same page * Fix test_runtime.js by adding `clearObject` back * Fix ESLint * Nicely stack multi-contexts via `TypePanel.divAll` * Fix example in repl/test-divide.worker.js for worker testing * repl/index.js: Instantiate TypePanel for default REPL content
1 parent 71d61e2 commit 44d965a

File tree

7 files changed

+56
-51
lines changed

7 files changed

+56
-51
lines changed

.eslintrc.cjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,8 @@ module.exports = {
439439
"jsdoc/require-property-description": "error",
440440
"jsdoc/require-property-name": "error",
441441
"jsdoc/require-property-type": "error",
442-
"jsdoc/require-returns": "error",
442+
// https://github.com/gajus/eslint-plugin-jsdoc/issues/503
443+
"jsdoc/require-returns": ["error", {checkGetters: false}],
443444
"jsdoc/require-returns-check": "error",
444445
"jsdoc/require-returns-description": "error",
445446
"jsdoc/require-returns-type": "error",

repl/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ const aceEditorLeft = setupAce(
439439
// write result to right editor
440440
// Tip: open F12/DevTools to see errors and warnings
441441
// Press Shift-Enter in right editor to eval result.
442+
import {TypePanel} from '@runtime-type-inspector/runtime';
442443
/**
443444
* @param {number} a
444445
* @param {number} b
@@ -451,6 +452,8 @@ const arr = [10_20];
451452
const [a, b] = arr;
452453
const ret = add(a, b);
453454
console.log("ret", ret);
455+
const typePanel = new TypePanel();
456+
Object.assign(window, {add, typePanel});
454457
`,
455458
//editor => insertTypes(),
456459
editor => runAction(),

repl/test-divide.worker.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ f();
2323
setInterval(f, 2000); // Test spam mode
2424

2525
/*
26-
import {typePanel} from '@runtime-type-inspector/runtime';
26+
import {TypePanel} from '@runtime-type-inspector/runtime';
2727
import {WorkerWithImportMapViaBedfordsShim} from 'worker-with-import-map';
2828
// console.log("WorkerWithImportMapViaBedfordsShim", WorkerWithImportMapViaBedfordsShim);
2929
// const url = './test-add.worker.js';
@@ -32,6 +32,7 @@ const worker = new WorkerWithImportMapViaBedfordsShim(url, {
3232
importMap: 'inherit'
3333
});
3434
console.log("worker", worker);
35+
const typePanel = new TypePanel();
3536
worker.addEventListener('message', (e) => {
3637
// console.log('addEventListener message', e.data);
3738
if (e.data.type !== 'rti') {

src-runtime/TypePanel.js

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ function isEnabled() {
4747
return tmp === null || tmp === 'true';
4848
}
4949
class TypePanel {
50+
/** @type {HTMLDivElement | null} */
51+
static divAll = null;
5052
div = document.createElement('div');
5153
inputEnable = document.createElement('input');
5254
spanErrors = document.createElement('span');
@@ -60,15 +62,19 @@ class TypePanel {
6062
buttonSaveState = document.createElement('button');
6163
buttonClear = document.createElement('button');
6264
warnedTable = createTable();
65+
/** @type {Record<string, import('./Warning.js').Warning>} */
66+
warnings = {};
6367
constructor() {
6468
const {
6569
div, inputEnable, spanErrors, span, select, option_spam, option_once, option_never,
6670
buttonHide, buttonLoadState, buttonSaveState, buttonClear, warnedTable,
6771
} = this;
68-
div.style.position = "absolute";
69-
div.style.bottom = "0px";
70-
div.style.right = "0px";
71-
div.style.zIndex = "10";
72+
TypePanel.divAll ??= document.createElement('div');
73+
const {divAll} = TypePanel;
74+
divAll.style.position = "absolute";
75+
divAll.style.bottom = "0px";
76+
divAll.style.right = "0px";
77+
divAll.style.zIndex = "10";
7278
niceDiv(div);
7379
inputEnable.checked = isEnabled();
7480
inputEnable.type = "checkbox";
@@ -111,7 +117,8 @@ class TypePanel {
111117
div.append(inputEnable, spanErrors, span, select, buttonHide, buttonLoadState, buttonSaveState, buttonClear, warnedTable);
112118
div.style.maxHeight = '200px';
113119
div.style.overflow = 'scroll';
114-
const finalFunc = () => document.body.append(div);
120+
divAll.append(div);
121+
const finalFunc = () => document.body.append(divAll);
115122
// Add our <div> to <body> when possible
116123
if (document.readyState === "complete") {
117124
finalFunc();
@@ -150,6 +157,9 @@ class TypePanel {
150157
localStorage.setItem('rti-enabled', 'true');
151158
this.sendEnabledDisabledStateToWorker();
152159
}
160+
report() {
161+
console.table(this.warnings);
162+
}
153163
lastKnownCountWithStatus = '0-true';
154164
sendEnabledDisabledStateToWorker() {
155165
// Problem: First time the worker may not even have started and `this.eventSources.size === 0`
@@ -171,11 +181,11 @@ class TypePanel {
171181
});
172182
}
173183
clear() {
174-
const {warned} = options;
175-
for (const key in warned) {
176-
const warning = warned[key];
184+
const {warnings} = this;
185+
for (const key in warnings) {
186+
const warning = warnings[key];
177187
warning.tr.remove();
178-
delete warned[key];
188+
delete warnings[key];
179189
}
180190
}
181191
get state() {
@@ -184,8 +194,8 @@ class TypePanel {
184194
/**
185195
* @todo I would rather save loc/name because it's less likely to change in future... to keep state URL's alive
186196
*/
187-
for (const key in options.warned) {
188-
const e = options.warned[key];
197+
for (const key in this.warnings) {
198+
const e = this.warnings[key];
189199
const {state} = e;
190200
if (state) {
191201
const {loc, name} = e;
@@ -210,12 +220,13 @@ class TypePanel {
210220
if (!json) {
211221
return false;
212222
}
223+
const {warnings} = this;
213224
for (const e of json) {
214225
const {loc, name, state} = e;
215226
/** @type {Warning|undefined} */
216227
let foundWarning;
217-
for (const key in options.warned) {
218-
const warning = options.warned[key];
228+
for (const key in warnings) {
229+
const warning = warnings[key];
219230
if (warning.loc === loc && warning.name === name) {
220231
foundWarning = warning;
221232
break;
@@ -225,7 +236,7 @@ class TypePanel {
225236
if (!foundWarning) {
226237
foundWarning = new Warning('msg', 'value', 'expect', loc, name);
227238
this.warnedTable?.append(foundWarning.tr);
228-
options.warned[`${loc}-${name}`] = foundWarning;
239+
warnings[`${loc}-${name}`] = foundWarning;
229240
}
230241
foundWarning.state = state;
231242
}
@@ -244,8 +255,9 @@ class TypePanel {
244255
get eventSources() {
245256
/** @type {Set<EventTarget | MessageEventSource>} */
246257
const eventSources = new Set();
247-
for (const key in options.warned) {
248-
const warning = options.warned[key];
258+
const {warnings} = this;
259+
for (const key in warnings) {
260+
const warning = warnings[key];
249261
if (warning.eventSource) {
250262
eventSources.add(warning.eventSource);
251263
}
@@ -259,11 +271,11 @@ class TypePanel {
259271
const {value, expect, loc, name, valueToString, strings, extras = [], key} = event.data;
260272
const msg = `${loc}> The '${name}' argument has an invalid type. ${strings.join(' ')}`.trim();
261273
this.updateErrorCount();
262-
let warnObj = options.warned[key];
274+
let warnObj = this.warnings[key];
263275
if (!warnObj) {
264276
warnObj = new Warning(msg, value, expect, loc, name);
265277
this.warnedTable?.append(warnObj.tr);
266-
options.warned[key] = warnObj;
278+
this.warnings[key] = warnObj;
267279
}
268280
warnObj.event = event;
269281
warnObj.hits++;
@@ -278,7 +290,7 @@ class TypePanel {
278290
*/
279291
deleteBreakpoint(event) {
280292
const {key} = event.data;
281-
const warnObj = options.warned[key];
293+
const warnObj = this.warnings[key];
282294
if (!warnObj) {
283295
console.warn("warnObj doesn't exist", {key});
284296
return;
@@ -301,10 +313,4 @@ class TypePanel {
301313
this.sendEnabledDisabledStateToWorker();
302314
}
303315
}
304-
/** @type {TypePanel | undefined} */
305-
let typePanel;
306-
// @todo create UI explicitly programmatically inside e.g. src/index.rti.js of the projects using it.
307-
if (typeof importScripts === 'undefined') {
308-
typePanel = new TypePanel();
309-
}
310-
export {niceDiv, TypePanel, typePanel};
316+
export {niceDiv, TypePanel};

src-runtime/Warning.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ class Warning {
5858
key: `${this.loc}-${this.name}`
5959
});
6060
}
61+
/**
62+
* Trigger `debugger;` next time this error is hit.
63+
*/
6164
get dbg() {
6265
return this._dbg;
6366
}
@@ -100,13 +103,19 @@ class Warning {
100103
// key: `${this.loc}-${this.name}`
101104
// });
102105
}
106+
/**
107+
* Prevent F12/DevTools spamming for errors that occur often, even in "spam" mode.
108+
*/
103109
get hidden() {
104110
return this._hidden;
105111
}
106112
set hits(_) {
107113
this._hits = _;
108114
this.td_count.textContent = _ + '';
109115
}
116+
/**
117+
* How often this error occured.
118+
*/
110119
get hits() {
111120
return this._hits;
112121
}

src-runtime/options.js

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
/**
2-
* @todo move into new Warning class, should be unused everywhere
3-
* @typedef {object} TypeError
4-
* @property {number} hits - How often this error occured.
5-
* @property {HTMLTableRowElement} tr - The <tr>.
6-
* @property {boolean} dbg - Trigger `debugger;` next time this error is hit.
7-
* @property {boolean} hide - Prevent F12/DevTools spamming for errors that occur often,
8-
* even in "spam" mode.
2+
* @todo Move everything into TypePanel, since every panel/worker should have its own controls.
93
*/
104
const options = {
115
enabled: true,
@@ -16,21 +10,7 @@ const options = {
1610
* Spam-mode basically retains the order, which mentally helps to figure out the actual issues.
1711
*/
1812
mode: 'spam',
19-
/** @type {Record<string, import('./Warning.js').Warning>} */
20-
warned: {},
2113
logSuperfluousProperty: false,
2214
count: 0,
2315
};
24-
function report() {
25-
console.table(options.warned);
26-
}
27-
/**
28-
* @param {Object<string, any>} obj - The object to clear.
29-
*/
30-
function clearObject(obj) {
31-
Object.keys(obj).forEach(_ => delete obj[_]);
32-
}
33-
function reset() {
34-
clearObject(options.warned);
35-
}
36-
export {report, clearObject, reset, options};
16+
export {options};

test_runtime.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ import {expandType } from './src-transpiler/expandType.js';
33
import {validateType } from './src-runtime/validateType.js';
44
import {validateUnion } from './src-runtime/validateUnion.js';
55
import {validateTuple } from './src-runtime/validateTuple.js';
6-
import {clearObject } from './src-runtime/options.js';
76
import {typedefs } from './src-runtime/registerTypedef.js';
7+
/**
8+
* @param {Object<string, any>} obj - The object to clear.
9+
*/
10+
function clearObject(obj) {
11+
Object.keys(obj).forEach(_ => delete obj[_]);
12+
}
813
const warn = () => undefined;
914
// We expect all functions to return true.
1015
const tests = [

0 commit comments

Comments
 (0)