@runtime-type-inspector/repl 1.0.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.
@@ -0,0 +1,73 @@
1
+ /*! Ace-diff | github.com/ace-diff/ace-diff */
2
+ .acediff__wrap{
3
+ display:-webkit-box;
4
+ display:-ms-flexbox;
5
+ display:flex;
6
+ -webkit-box-orient:horizontal;
7
+ -webkit-box-direction:normal;
8
+ -ms-flex-direction:row;
9
+ flex-direction:row;
10
+ position:absolute;
11
+ bottom:0;
12
+ top:0;
13
+ left:0;
14
+ height:100%;
15
+ width:100%;
16
+ overflow:auto
17
+ }
18
+ .acediff__gutter{
19
+ -webkit-box-flex:0;
20
+ -ms-flex:0 0 60px;
21
+ flex:0 0 60px;
22
+ border-left:1px solid #bcbcbc;
23
+ border-right:1px solid #bcbcbc;
24
+ overflow:hidden
25
+ }
26
+ .acediff__gutter,.acediff__gutter svg{
27
+ background-color:#efefef
28
+ }
29
+ .acediff__left,.acediff__right{
30
+ height:100%;
31
+ -webkit-box-flex:1;
32
+ -ms-flex:1;
33
+ flex:1
34
+ }
35
+ .acediff__diffLine{
36
+ background-color:#d8f2ff;
37
+ border-top:1px solid #a2d7f2;
38
+ border-bottom:1px solid #a2d7f2;
39
+ position:absolute;
40
+ z-index:4
41
+ }
42
+ .acediff__diffLine.targetOnly{
43
+ height:0!important;
44
+ border-top:1px solid #a2d7f2;
45
+ border-bottom:0;
46
+ position:absolute
47
+ }
48
+ .acediff__connector{
49
+ fill:#d8f2ff;
50
+ stroke:#a2d7f2
51
+ }
52
+ .acediff__copy--left,.acediff__copy--right{
53
+ position:relative
54
+ }
55
+ .acediff__copy--left div,.acediff__copy--right div{
56
+ color:#000;
57
+ text-shadow:1px 1px hsla(0,0%,100%,.7);
58
+ position:absolute;
59
+ margin:2px 3px;
60
+ cursor:pointer
61
+ }
62
+ .acediff__copy--right div:hover{
63
+ color:#004ea0
64
+ }
65
+ .acediff__copy--left{
66
+ float:right
67
+ }
68
+ .acediff__copy--left div{
69
+ right:0
70
+ }
71
+ .acediff__copy--left div:hover{
72
+ color:#c98100
73
+ }
@@ -0,0 +1,7 @@
1
+ // Test: await import("@babel/helper-plugin-utils")
2
+ /** @type {Record<string, any>} */
3
+ const exports = {};
4
+ globalThis.exports = exports;
5
+ await import("../node_modules/@babel/helper-plugin-utils/lib/index.js");
6
+ export const {declare, declarePreset} = exports;
7
+ export default exports;
@@ -0,0 +1,8 @@
1
+ // Note: this is still ugly, but Babel 8 should be "nice" ESM (when it's out)
2
+ // Test: const {parse} = await import("@babel/parser");
3
+ /** @type {Record<string, any>} */
4
+ const exports = {};
5
+ globalThis.exports = exports;
6
+ await import("./node_modules/@babel/parser/lib/index.js");
7
+ export const {parse, parseExpression, tokTypes} = exports;
8
+ export default exports;
@@ -0,0 +1,58 @@
1
+
2
+ // Test: const {babelPluginSyntaxTypeScript} = await import("@babel/plugin-syntax-typescript");
3
+ /*
4
+ const {parse} = await import("@babel/parser");
5
+ parse('type tmp = 123;', {
6
+ plugins: [babelPluginSyntaxTypeScript?]
7
+ })
8
+ */
9
+ import {declare} from '@babel/helper-plugin-utils';
10
+ /**
11
+ * @param {any[]} plugins
12
+ * @param {string} name
13
+ */
14
+ function removePlugin(plugins, name) {
15
+ const indices = [];
16
+ plugins.forEach((plugin, i) => {
17
+ const n = Array.isArray(plugin) ? plugin[0] : plugin;
18
+ if (n === name) {
19
+ indices.unshift(i);
20
+ }
21
+ });
22
+ for (const i of indices) {
23
+ plugins.splice(i, 1);
24
+ }
25
+ };
26
+ const babelPluginSyntaxTypeScript = declare((api, opts) => {
27
+ api.assertVersion(7);
28
+ const {
29
+ disallowAmbiguousJSXLike,
30
+ dts
31
+ } = opts;
32
+ {
33
+ var {
34
+ isTSX
35
+ } = opts;
36
+ }
37
+ return {
38
+ name: "syntax-typescript",
39
+ manipulateOptions(opts, parserOpts) {
40
+ {
41
+ const {
42
+ plugins
43
+ } = parserOpts;
44
+ removePlugin(plugins, "flow");
45
+ removePlugin(plugins, "jsx");
46
+ plugins.push("objectRestSpread", "classProperties");
47
+ if (isTSX) {
48
+ plugins.push("jsx");
49
+ }
50
+ }
51
+ parserOpts.plugins.push(["typescript", {
52
+ disallowAmbiguousJSXLike,
53
+ dts
54
+ }]);
55
+ }
56
+ };
57
+ });
58
+ export {babelPluginSyntaxTypeScript};
package/importmap.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @param {string} content - The content.
3
+ * @returns {string} data:text string
4
+ */
5
+ function importFile(content) {
6
+ return "data:text/javascript;base64," + btoa(content);
7
+ }
8
+ const imports = {
9
+ "@runtime-type-inspector/runtime" : '../src-runtime/index.mjs',
10
+ "@runtime-type-inspector/transpiler": '../src-transpiler/index.mjs',
11
+ "@babel/parser": "./babel-parser.mjs",
12
+ //"@babel/helper-plugin-utils": "./babel-helper-plugin-utils.mjs",
13
+ //"@babel/plugin-syntax-typescript": "./babel-plugin-syntax-typescript.mjs",
14
+ "fs": importFile("export default {};"),
15
+ "typescript": importFile("export default ts;"), // UMD import
16
+ };
17
+ if (location.host === 'runtimetypeinspector.org' || location.port === '7000') {
18
+ imports['@runtime-type-inspector/runtime' ] = './node_modules/@runtime-type-inspector/runtime/index.mjs';
19
+ imports['@runtime-type-inspector/transpiler'] = './node_modules/@runtime-type-inspector/transpiler/index.mjs';
20
+ }
21
+ const importmap = document.createElement("script");
22
+ importmap.type = "importmap";
23
+ importmap.textContent = JSON.stringify({imports});
24
+ const dom = document.body || document.head;
25
+ if (!dom) {
26
+ throw new Error("neither <body> nor <head> available to append importmap");
27
+ }
28
+ dom.append(importmap);
package/index.html ADDED
@@ -0,0 +1,33 @@
1
+ <html>
2
+ <head>
3
+ <script src="importmap.js"></script>
4
+ <script src="node_modules/ace-builds/src/ace.js"></script>
5
+ <script src="node_modules/typescript/lib/typescript.js"></script>
6
+ </head>
7
+ <body>
8
+ <select id="action">
9
+ <option value="typechecking">Add full typechecking</option>
10
+ <option value="code2ast2code">code2ast2code (testing Stringifier class)</option>
11
+ <option value="ast">Show AST from JS</option>
12
+ <option value="ast-ts">Show AST from TS</option>
13
+ <option value="ast-babel-ts">Show AST from Babel-TS</option>
14
+ <option value="jsdoc">Show parsed JSDoc</option>
15
+ <option value="expand-type">expandType</option>
16
+ <option value="eval">Eval</option>
17
+ </select>
18
+ <button onclick="runAction()">Run action</button>
19
+ <select id="preferred-expand-type">
20
+ <option value="expandTypeTS">expandTypeTS</option>
21
+ <option value="expandTypeDepFree">expandTypeDepFree</option>
22
+ </select>
23
+ <button id="repl">REPL</button>
24
+ <button id="button-compare-ast" onclick="compareAST()">Compare AST</button>
25
+ <button id="button-set-hash" onclick="setHash()">Set hash</button>
26
+ Left: <input id="left" type="checkbox" checked></input>
27
+ Right: <input id="right" type="checkbox" checked></input>
28
+ <script type="module">
29
+ import * as Index from "./index.mjs";
30
+ Object.assign(window, {...Index});
31
+ </script>
32
+ </body>
33
+ </html>
package/index.mjs ADDED
@@ -0,0 +1,473 @@
1
+ import {parse} from '@babel/parser';
2
+ import {
3
+ parseJSDoc,
4
+ addTypeChecks,
5
+ expandType,
6
+ expandTypeBabelTS,
7
+ expandTypeDepFree,
8
+ code2ast2code,
9
+ ast2jsonForComparison
10
+ } from '@runtime-type-inspector/transpiler';
11
+ import * as ti from '@runtime-type-inspector/transpiler';
12
+ import * as rti from '@runtime-type-inspector/runtime';
13
+ const hashvars = new Map(location.hash.slice(1).split('&').map(_ => _.split('=')));
14
+ if (!hashvars.get('action')) {
15
+ hashvars.set('action', 'typechecking');
16
+ }
17
+ const currentAction = hashvars.get('action');
18
+ const selectAction = document.getElementById("action");
19
+ if (!(selectAction instanceof HTMLSelectElement)) {
20
+ throw new Error('This module requires a <select id="action" ...');
21
+ }
22
+ const selectPreferredExpandType = document.getElementById('preferred-expand-type');
23
+ if (!(selectPreferredExpandType instanceof HTMLSelectElement)) {
24
+ throw new Error('This module requires a <select id="action" ...');
25
+ }
26
+ function setHash() {
27
+ const left = btoa(getLeft());
28
+ const right = btoa(getRight());
29
+ location.hash = `action=${selectAction.value}&left=${left}&right=${right}`;
30
+ }
31
+ // selectPreferredExpandType.value;
32
+ selectAction.value = currentAction;
33
+ const buttonREPL = document.getElementById('repl');
34
+ if (!(buttonREPL instanceof HTMLButtonElement)) {
35
+ throw new Error('This module requires a <button id="repl" ...');
36
+ }
37
+ const inputLeft = document.getElementById('left');
38
+ if (!(inputLeft instanceof HTMLInputElement)) {
39
+ throw 'This module requires a <input id="left" ...';
40
+ }
41
+ const inputRight = document.getElementById('right');
42
+ if (!(inputRight instanceof HTMLInputElement)) {
43
+ throw 'This module requires a <input id="right" ...';
44
+ }
45
+ inputLeft.onchange = (event) => {
46
+ const {checked} = event.target;
47
+ if (checked) {
48
+ // Unhide right editor and resize right one to half size again
49
+ aceDivLeft.style.display = "";
50
+ setHalfSizeLeft();
51
+ setHalfSizeRight();
52
+ } else {
53
+ // Hide left editor and resize right one to full size
54
+ aceDivLeft.style.display = "none";
55
+ Object.assign(aceDivRight.style, {
56
+ position: "absolute",
57
+ left: "0vw",
58
+ top: "40px",
59
+ width: "100vw",
60
+ height: "calc(100vh - 40px)",
61
+ });
62
+ }
63
+ }
64
+ inputRight.onchange = (event) => {
65
+ const {checked} = event.target;
66
+ if (checked) {
67
+ // Unhide left editor and resize right one to half size again
68
+ aceDivRight.style.display = "";
69
+ setHalfSizeLeft();
70
+ setHalfSizeRight();
71
+ } else {
72
+ // Hide left editor and resize right one to full size
73
+ aceDivRight.style.display = "none";
74
+ Object.assign(aceDivLeft.style, {
75
+ position: "absolute",
76
+ left: "0vw",
77
+ top: "40px",
78
+ width: "100vw",
79
+ height: "calc(100vh - 40px)",
80
+ });
81
+ }
82
+ }
83
+ function getPreferredExpandType() {
84
+ const {value} = selectPreferredExpandType;
85
+ if (value == 'expandTypeTS') {
86
+ return expandType;
87
+ }
88
+ return expandTypeDepFree;
89
+ }
90
+ function getCodeForAction() {
91
+ switch (getAction()) {
92
+ case 'typechecking':
93
+ return "const ret = addTypeChecks(jsdoc, {expandType: getPreferredExpandType()});\nsetRight(ret);";
94
+ case 'jsdoc':
95
+ return "const ret = parseJSDoc(jsdoc);\nsetRight(JSON.stringify(ret, null, 2));";
96
+ case 'code2ast2code':
97
+ return "const ret = code2ast2code(jsdoc);\nsetRight(ret);";
98
+ case 'expand-type':
99
+ return 'setRight(expandTypeAll(jsdoc));';
100
+ }
101
+ return '// This action has no special code';
102
+ }
103
+ /**
104
+ * @param {string} name - The name.
105
+ * @param {any} data - Function or object.
106
+ * @returns {string}
107
+ */
108
+ function data2code(name, data) {
109
+ if (data instanceof Function) {
110
+ return data.toString();
111
+ } else if (data instanceof Object && data !== null) {
112
+ return `let ${name} = ${JSON.stringify(data, null, 2)};`;
113
+ }
114
+ return `// data2code> unhandled type: name=${name}, type of data: ${typeof data}`;
115
+ }
116
+ /**
117
+ * Since Asserter extends Stringifier, it needs to appear afterwards.
118
+ * @param {string} a
119
+ * @param {string} b
120
+ * @returns {-1|0|1}
121
+ */
122
+ function compareBaseClasses(a, b) {
123
+ if (a.startsWith("class Asserter") && b.startsWith("class Stringifier")) {
124
+ return 1;
125
+ }
126
+ if (b.startsWith("class Asserter") && a.startsWith("class Stringifier")) {
127
+ return -1;
128
+ }
129
+ return 0;
130
+ }
131
+ function activateREPL() {
132
+ const code = Object.entries(ti)
133
+ .map(([key, val]) => data2code(key, val))
134
+ .sort(compareBaseClasses)
135
+ .join('\n');
136
+ const codeLocal = [getPreferredExpandType, expandTypeAll].join('\n');
137
+ const leftContent = aceEditorLeft.getValue().replaceAll('`', '\\`');
138
+ const leftContentAsCode = `const jsdoc = \`${leftContent}\`;`;
139
+ const out = [leftContentAsCode, code, codeLocal, getCodeForAction()].join('\n');
140
+ setLeft(out);
141
+ // @ts-ignore
142
+ selectAction.value = "eval";
143
+ }
144
+ buttonREPL.onclick = activateREPL;
145
+ Object.assign(window, {
146
+ parse,
147
+ ti, ...ti,
148
+ rti, ...rti,
149
+ });
150
+ let lastStats = {};
151
+ function statsPrint() {
152
+ console.table(lastStats);
153
+ }
154
+ /**
155
+ * @param {string} value
156
+ */
157
+ function setLeft(value) {
158
+ if (typeof value !== 'string') {
159
+ aceEditorRight.setValue(`Cannot set type ${typeof value}`);
160
+ return;
161
+ }
162
+ aceEditorLeft.setValue(value);
163
+ aceEditorLeft.clearSelection(); // setValue() selects everything, so unselect it now
164
+ }
165
+ /**
166
+ * @param {string} value
167
+ */
168
+ function setRight(value) {
169
+ if (typeof value !== 'string') {
170
+ aceEditorRight.setValue(`Cannot set type ${typeof value}`);
171
+ return;
172
+ }
173
+ aceEditorRight.setValue(value);
174
+ aceEditorRight.clearSelection(); // setValue() selects everything, so unselect it now
175
+ }
176
+ function getLeft() {
177
+ return aceEditorLeft.getValue();
178
+ }
179
+ function getRight() {
180
+ return aceEditorRight.getValue();
181
+ }
182
+ function actionAST() {
183
+ const ast = parse(getLeft(), {sourceType: 'module'});
184
+ const out = JSON.stringify(ast, function (name, val) {
185
+ if (name === "loc" || name === "start" || name === "end") {
186
+ return undefined; // remove
187
+ }
188
+ return val; // keep
189
+ }, 2);
190
+ setRight(out);
191
+ }
192
+ function actionAST_TS() {
193
+ const str = getLeft();
194
+ const ast = ts.createSourceFile('repl.ts', str, ts.ScriptTarget.Latest, false /*setParentNodes*/);
195
+ const out = JSON.stringify(ast, function(name, val) {
196
+ if (name === "parent") {
197
+ return undefined; // remove
198
+ }
199
+ return val; // keep
200
+ }, 2);
201
+ setRight(out);
202
+ }
203
+ function actionAST_BabelTS() {
204
+ const str = getLeft();
205
+ const ast = parse(str, {plugins: ['typescript'], sourceType: "module"});
206
+ const out = JSON.stringify(ast, null, 2);
207
+ setRight(out);
208
+ }
209
+ /**
210
+ * @param {string} type - The type like `...string`.
211
+ * @returns {string} Structured output of all expandType functions.
212
+ */
213
+ function expandTypeAll(type) {
214
+ let out = '';
215
+ out += '// expandTypeTS:\n';
216
+ try {
217
+ const ts = expandType(type);
218
+ out += JSON.stringify(ts, null, 2) + '\n';
219
+ } catch (e) {
220
+ out += e + '\n';
221
+ }
222
+ out += '// expandTypeBabelTS:\n';
223
+ try {
224
+ const babelts = expandTypeBabelTS(type);
225
+ out += JSON.stringify(babelts, null, 2) + '\n';
226
+ } catch (e) {
227
+ out += e + '\n';
228
+ }
229
+ out += '// expandTypeDepFree:\n';
230
+ try {
231
+ const depFree = expandTypeDepFree(type);
232
+ out += JSON.stringify(depFree, null, 2) + '\n';
233
+ } catch (e) {
234
+ out += e + '\n';
235
+ }
236
+ return out;
237
+ }
238
+ function actionExpandType() {
239
+ setRight(expandTypeAll(getLeft()));
240
+ }
241
+ function actionJSDoc() {
242
+ const ret = parseJSDoc(getLeft());
243
+ /** @type {string} */
244
+ let out;
245
+ if (ret === undefined) {
246
+ out = '// parseJSDoc returned undefined';
247
+ } else {
248
+ out = '// parseJSDoc output:\n' + JSON.stringify(ret, null, 2);
249
+ }
250
+ setRight(out);
251
+ }
252
+ function actionTypeChecking() {
253
+ setRight(addTypeChecks(getLeft(), {expandType: getPreferredExpandType()}));
254
+ }
255
+ /**
256
+ * @todo use Monaco Diff Editor
257
+ * @param {string} left - Left source code.
258
+ * @param {string} right - Right source code.
259
+ */
260
+ function compareAST(left = getLeft(), right = getRight()) {
261
+ const l = parse(left , {sourceType: 'module'});
262
+ const r = parse(right, {sourceType: 'module'});
263
+ const ljson = ast2jsonForComparison(l);
264
+ const rjson = ast2jsonForComparison(r);
265
+ const test = ljson === rjson;
266
+ const _ = test ? ' ' : ' NOT ';
267
+ console.log(`AST is ${_} equal`);
268
+ // todo add conversion buttons
269
+ //console.log('ljson', ljson);
270
+ //console.log('rjson', rjson);
271
+ setLeft(ljson);
272
+ setRight(rjson);
273
+ }
274
+ function actionCode2Ast2Code() {
275
+ const content = getLeft();
276
+ const out = code2ast2code(content);
277
+ if (!out) {
278
+ setRight("// code2ast2code failed");
279
+ return;
280
+ }
281
+ setRight(out);
282
+ // compareAST(content, out);
283
+ }
284
+ async function runAction() {
285
+ const action = getAction();
286
+ switch (action) {
287
+ case 'ast':
288
+ await actionAST();
289
+ break;
290
+ case 'ast-ts':
291
+ await actionAST_TS();
292
+ break;
293
+ case 'ast-babel-ts':
294
+ await actionAST_BabelTS();
295
+ break;
296
+ case 'jsdoc':
297
+ await actionJSDoc();
298
+ break;
299
+ case 'typechecking':
300
+ await actionTypeChecking();
301
+ break;
302
+ case 'code2ast2code':
303
+ await actionCode2Ast2Code();
304
+ break;
305
+ case 'eval':
306
+ eval(aceEditorLeft.getValue());
307
+ break;
308
+ case 'expand-type':
309
+ await actionExpandType();
310
+ break;
311
+ default:
312
+ setRight(`Action ${action} not implemented`);
313
+ }
314
+ }
315
+ async function insertTypes() {
316
+ const ret = await postData('insertTypes', {
317
+ file: aceEditorLeft.getValue()
318
+ });
319
+ ret.logs?.map(_ => console.log(..._));
320
+ ret.warns?.map(_ => console.warn(..._));
321
+ console.log(ret);
322
+ if (ret.success) {
323
+ lastStats = ret.stats;
324
+ statsPrint();
325
+ aceEditorRight.setValue(ret.result);
326
+ if (ret.insertTypesAndEval) {
327
+ eval(ret.result.replaceAll('export ', ''));
328
+ }
329
+ } else {
330
+ aceEditorRight.setValue(JSON.stringify(ret, null, 2));
331
+ }
332
+ aceEditorRight.clearSelection(); // setValue() selects everything, so unselect it now
333
+ }
334
+ /**
335
+ * @typedef {'typechecking'|'code2ast2code'|'ast'|'ast-ts'|'jsdoc'|'eval'|'expand-type'} Action
336
+ */
337
+ /** @returns {Action} */
338
+ const getAction = () => selectAction.value;
339
+ /**
340
+ * @param {string} id - The ID.
341
+ * @returns {HTMLDivElement} - The DIV element.
342
+ */
343
+ function createDivWithId(id) {
344
+ let div = document.getElementById(id);
345
+ if (!div) {
346
+ div = document.createElement('div');
347
+ div.id = id;
348
+ }
349
+ if (!(div instanceof HTMLDivElement)) {
350
+ throw "createDivWithId> div creation failed";
351
+ }
352
+ return div;
353
+ }
354
+ const aceDivLeft = createDivWithId("aceDivLeft");
355
+ const aceDivRight = createDivWithId("aceDivRight");
356
+ document.body.append(aceDivLeft, aceDivRight);
357
+ function setHalfSizeLeft() {
358
+ Object.assign(aceDivLeft.style, {
359
+ position: "absolute",
360
+ left: "0px",
361
+ top: "40px",
362
+ width: "50vw",
363
+ height: "calc(100vh - 40px)",
364
+ });
365
+ }
366
+ function setHalfSizeRight() {
367
+ Object.assign(aceDivRight.style, {
368
+ position: "absolute",
369
+ left: "50vw",
370
+ top: "40px",
371
+ width: "50vw",
372
+ height: "calc(100vh - 40px)",
373
+ });
374
+ }
375
+ setHalfSizeLeft();
376
+ setHalfSizeRight();
377
+ /**
378
+ * @param {string} id - ID of editor.
379
+ * @param {string} txt - Initial text of editor.
380
+ * @param {Function} execShiftEnter - Function for Shift+Enter
381
+ * @param {Function} execAltEnter - Function for Alt+Enter
382
+ * @returns {ACE.Editor} The ACE editor.
383
+ */
384
+ function setupAce(id, txt, execShiftEnter, execAltEnter) {
385
+ const aceEditor = ace.edit(id);
386
+ aceEditor.setFontSize(20);
387
+ aceEditor.setTheme('ace/theme/chrome');
388
+ aceEditor.session.setMode('ace/mode/javascript');
389
+ aceEditor.session.setUseWorker(false);
390
+ aceEditor.session.setOptions({
391
+ tabSize: 2,
392
+ useSoftTabs: true
393
+ });
394
+ aceEditor.setValue(txt);
395
+ aceEditor.clearSelection(); // setValue() selects everything, so unselect it now
396
+ aceEditor.commands.addCommand({
397
+ name: 'Insert types',
398
+ bindKey: {win: 'Shift-Enter', mac: 'Shift-Enter'},
399
+ exec: execShiftEnter
400
+ });
401
+ aceEditor.commands.addCommand({
402
+ name: 'Show AST',
403
+ bindKey: {win: 'Alt-Enter', mac: 'Alt-Enter'},
404
+ exec: execAltEnter
405
+ });
406
+ return aceEditor;
407
+ }
408
+ const aceEditorLeft = setupAce(
409
+ 'aceDivLeft',
410
+ `// Shift-Enter: convert content of left editor,
411
+ // write result to right editor
412
+ // Tip: open F12/DevTools to see errors and warnings
413
+ // Press Shift-Enter in right editor to eval result.
414
+ /**
415
+ * @param {number} a
416
+ * @param {number} b
417
+ */
418
+ function add(a, b) {
419
+ return a + b;
420
+ }
421
+ /** @type {number[]} */
422
+ const arr = [10_20];
423
+ const [a, b] = arr;
424
+ const ret = add(a, b);
425
+ console.log("ret", ret);
426
+ `,
427
+ //editor => insertTypes(),
428
+ editor => runAction(),
429
+ editor => actionAST()
430
+ );
431
+ const aceEditorRight = setupAce(
432
+ 'aceDivRight',
433
+ '// Result editor',
434
+ editor => runRightEditor(),
435
+ editor => console.log("right alt")
436
+ );
437
+ function runRightEditor() {
438
+ const src = getRight();
439
+ if (src.includes('import') || src.includes('export')) {
440
+ const script = document.createElement("script");
441
+ script.type = "module";
442
+ script.textContent = src;
443
+ document.head.append(script);
444
+ return;
445
+ }
446
+ eval(src);
447
+ }
448
+ if (hashvars.get('left')) {
449
+ setLeft(atob(hashvars.get('left')));
450
+ }
451
+ if (hashvars.get('right')) {
452
+ setRight(atob(hashvars.get('right')));
453
+ }
454
+ export {
455
+ setHash,
456
+ hashvars,
457
+ selectAction,
458
+ selectPreferredExpandType,
459
+ buttonREPL,
460
+ aceDivLeft,
461
+ aceDivRight,
462
+ aceEditorLeft,
463
+ aceEditorRight,
464
+ setLeft,
465
+ setRight,
466
+ getLeft,
467
+ getRight,
468
+ compareAST,
469
+ getPreferredExpandType,
470
+ expandTypeAll,
471
+ runAction,
472
+ data2code,
473
+ };
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@runtime-type-inspector/repl",
3
+ "version": "1.0.0",
4
+ "description": "REPL of RuntimeTypeInspector.js",
5
+ "main": "index.html",
6
+ "dependencies": {
7
+ "@runtime-type-inspector/transpiler": "^3.0.3",
8
+ "ace-builds": "^1.28.0",
9
+ "express": "^4.18.2"
10
+ },
11
+ "author": "Hermann Rolfes (kungfooman)",
12
+ "license": "MIT",
13
+ "publishConfig": {"access": "public"}
14
+ }
package/serve.cjs ADDED
@@ -0,0 +1,24 @@
1
+ var express = require('express');
2
+
3
+ express.static.mime.define({'text/javascript': ['mjs']});
4
+ var app = express();
5
+
6
+ app.use(express.static('.'));
7
+
8
+ //Serves all the request which includes /images in the url from Images folder
9
+ app.use('/images', express.static(__dirname + '/node_modules'));
10
+ app.use('/', express.static(__dirname));
11
+
12
+ var server = app.listen(7000);
13
+
14
+ /*
15
+ const express = require('express');
16
+ const app = express();
17
+ const port = 6000;
18
+ app.get('/', (req, res) => {
19
+ res.send('Hello World!');
20
+ });
21
+ app.listen(port, () => {
22
+ console.log(`Example app listening on port ${port}`);
23
+ });
24
+ */
package/test.css ADDED
@@ -0,0 +1,4 @@
1
+ #buttons {
2
+ overflow: scroll;
3
+ height: 80vh;
4
+ }
package/test.html ADDED
@@ -0,0 +1,43 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>RTI Test</title>
5
+ <script src="./importmap.js"></script>
6
+ <script src="../node_modules/typescript/lib/typescript.js"></script>
7
+ <!-- Include Ace editor first -->
8
+ <!--
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.3.3/ace.js"></script>
10
+ -->
11
+ <script src="../node_modules/ace-builds/src/ace.js"></script>
12
+ <!-- Include Ace-diff JS and CSS (using unpkg.com CDN in this case) -->
13
+ <script src="https://unpkg.com/ace-diff@^2"></script>
14
+ <link href="./ace-diff@2.3.0.css" rel="stylesheet">
15
+ <link href="./test.css" rel="stylesheet">
16
+ <!-- Bootstrap for additional styling of the demo -->
17
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.1/css/bootstrap.min.css" />
18
+ <style>
19
+ #buttons {
20
+ overflow: scroll;
21
+ height: 80vh;
22
+ }
23
+ </style>
24
+ </head>
25
+ <body>
26
+ <div class="container">
27
+ <h1>RTI Test</h1>
28
+ <div class="row">
29
+ <div class="col-sm-4">
30
+ <div id="buttons"></div>
31
+ <p>A red test requires more work, while a green test passes.</p>
32
+ <hr>
33
+ <h2>Troubleshooting</h2>
34
+ <p>If you run into a problem please open an issue on GitHub.</p>
35
+ </div>
36
+ <div class="col-sm-8" id="here">
37
+ <div class="custom"></div>
38
+ </div>
39
+ </div>
40
+ </div>
41
+ <script type="module" src="test.js"></script>
42
+ </body>
43
+ </html>
package/test.js ADDED
@@ -0,0 +1,75 @@
1
+ import {addTypeChecks, expandType} from "@runtime-type-inspector/transpiler";
2
+ /**
3
+ * @param {string} a - Left text.
4
+ * @param {string} b - Right text.
5
+ * @returns {object} AceDiff editor instance.
6
+ */
7
+ function addDiff(a, b) {
8
+ const element = document.createElement('div');
9
+ document.getElementById("here").innerHTML = '';
10
+ document.getElementById("here").append(element);
11
+ return new AceDiff({
12
+ element,
13
+ left: {
14
+ content: a,
15
+ },
16
+ right: {
17
+ content: b,
18
+ },
19
+ });
20
+ }
21
+ /**
22
+ * @param {*} input - Source code to normalize.
23
+ * @returns {string} Normalized output.
24
+ */
25
+ function normalize(input) {
26
+ // Remove multiple newlines into one
27
+ // I would rather not do it, but Stringifier needs a bit more love in other areas:
28
+ // - multiline array output when elements surpass a max-col option
29
+ let output = input.replace(/\n+/g, '\n').trim();
30
+ /** @todo Remove bunch of whitespaces from test outputs */
31
+ output = output
32
+ .split('\n')
33
+ .filter(_ => _.trim().length)
34
+ .map(_ => _.trim())
35
+ .join('\n');
36
+ return output;
37
+ }
38
+ async function main() {
39
+ const resp = await fetch("../test/typechecking.json");
40
+ const json = await resp.json();
41
+ const buttonsDiv = document.getElementById('buttons');
42
+ if (!(buttonsDiv instanceof HTMLDivElement)) {
43
+ throw new Error("buttons isn't a <div>");
44
+ }
45
+ const aceDiffer = addDiff('1\n2\n3\n4\n', '1\n2\n33\n4\n');
46
+ /** @type {HTMLButtonElement[]} */
47
+ const buttons = [];
48
+ for (const {input, output} of json) {
49
+ const button = document.createElement('button');
50
+ buttonsDiv.append(button);
51
+ button.innerText = input;
52
+ const res = await fetch('../' + input);
53
+ const txtInput = await res.text();
54
+ const res2 = await fetch('../' + output);
55
+ const txtOutput = await res2.text();
56
+ const actualResult = addTypeChecks(txtInput, {expandType, addHeader: false});
57
+ const success = normalize(actualResult) === normalize(txtOutput);
58
+ button.style.backgroundColor = success ? 'lime' : 'red';
59
+ button.onclick = async () => {
60
+ aceDiffer.editors.left.ace.setValue(txtInput);
61
+ aceDiffer.editors.left.ace.clearSelection();
62
+ let rightText = txtOutput;
63
+ if (!success) {
64
+ rightText += `\n\n// Actual result:\n${actualResult}`;
65
+ }
66
+ aceDiffer.editors.right.ace.setValue(rightText);
67
+ aceDiffer.editors.right.ace.clearSelection();
68
+ //console.log({input, output});
69
+ };
70
+ buttons.push(button);
71
+ }
72
+ buttons[1].onclick();
73
+ Object.assign(window, {aceDiffer, json});
74
+ }
75
+ main();