tutuca 0.9.98 → 0.9.100
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/README.md +5 -3
- package/dist/tutuca-cli.js +29 -15
- package/dist/tutuca-components.js +2444 -0
- package/dist/tutuca-dev.ext.js +18 -18
- package/dist/tutuca-dev.js +18 -18
- package/dist/tutuca-dev.min.js +2 -2
- package/dist/tutuca-extra.ext.js +2 -2
- package/dist/tutuca-extra.js +2 -2
- package/dist/tutuca-extra.min.js +1 -1
- package/dist/tutuca-storybook.js +154 -16
- package/dist/tutuca.ext.js +2 -2
- package/dist/tutuca.js +2 -2
- package/dist/tutuca.min.js +1 -1
- package/package.json +3 -1
- package/skill/tutuca/cli.md +10 -68
- package/skill/tutuca/core.md +25 -74
- package/skill/tutuca/semantics.md +4 -3
- package/skill/tutuca/testing.md +3 -6
- package/skill/tutuca-source/tutuca.ext.js +2 -2
|
@@ -0,0 +1,2444 @@
|
|
|
1
|
+
// src/components/data/data.js
|
|
2
|
+
import { component as component3, html as html3, IMap as IMap2, ISet as ISet2, List as List2, OMap as OMap2, OrderedSet as OrderedSet2 } from "tutuca";
|
|
3
|
+
|
|
4
|
+
// src/components/data/immutable-inspector.js
|
|
5
|
+
import { component as component2, html as html2, IMap, ISet, List, OMap, OrderedSet, Record, Seq, Stack } from "tutuca";
|
|
6
|
+
|
|
7
|
+
// src/components/data/json.js
|
|
8
|
+
import { component, html } from "tutuca";
|
|
9
|
+
var JsonNull = component({
|
|
10
|
+
name: "JsonNull",
|
|
11
|
+
fields: {},
|
|
12
|
+
view: html`<span
|
|
13
|
+
class="font-mono text-sm leading-tight text-warning italic"
|
|
14
|
+
>null</span
|
|
15
|
+
>`
|
|
16
|
+
});
|
|
17
|
+
var JsonBoolean = component({
|
|
18
|
+
name: "JsonBoolean",
|
|
19
|
+
fields: { value: false },
|
|
20
|
+
methods: {
|
|
21
|
+
text() {
|
|
22
|
+
return this.value ? "true" : "false";
|
|
23
|
+
},
|
|
24
|
+
cssClass() {
|
|
25
|
+
const base = "font-mono text-sm leading-tight";
|
|
26
|
+
return `${base} ${this.value ? "text-success" : "text-warning"}`;
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
view: html`<span :class="$cssClass" @text="$text"></span>`,
|
|
30
|
+
views: {
|
|
31
|
+
_margauiClasses: html`<p class="text-success text-warning"></p>`
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
var JsonNumber = component({
|
|
35
|
+
name: "JsonNumber",
|
|
36
|
+
fields: { value: 0 },
|
|
37
|
+
methods: {
|
|
38
|
+
text() {
|
|
39
|
+
return String(this.value);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
view: html`<span
|
|
43
|
+
class="font-mono text-sm leading-tight text-info"
|
|
44
|
+
@text="$text"
|
|
45
|
+
></span>`
|
|
46
|
+
});
|
|
47
|
+
var JsonString = component({
|
|
48
|
+
name: "JsonString",
|
|
49
|
+
fields: { value: "" },
|
|
50
|
+
methods: {
|
|
51
|
+
text() {
|
|
52
|
+
return JSON.stringify(this.value ?? "");
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
view: html`<span
|
|
56
|
+
class="font-mono text-sm leading-tight inline-block max-w-xs truncate align-bottom"
|
|
57
|
+
:title=".value"
|
|
58
|
+
@text="$text"
|
|
59
|
+
></span>`
|
|
60
|
+
});
|
|
61
|
+
var compositeFields = {
|
|
62
|
+
items: [],
|
|
63
|
+
isExpanded: false,
|
|
64
|
+
itemsPerPage: 10,
|
|
65
|
+
currentPage: 0
|
|
66
|
+
};
|
|
67
|
+
var compositeMethods = {
|
|
68
|
+
arrowText() {
|
|
69
|
+
return this.isExpanded ? "▼" : "▶";
|
|
70
|
+
},
|
|
71
|
+
isItemsEmpty() {
|
|
72
|
+
return this.items.size === 0;
|
|
73
|
+
},
|
|
74
|
+
pageCount() {
|
|
75
|
+
return Math.max(1, Math.ceil(this.items.size / this.itemsPerPage));
|
|
76
|
+
},
|
|
77
|
+
pageStart() {
|
|
78
|
+
return this.currentPage * this.itemsPerPage;
|
|
79
|
+
},
|
|
80
|
+
pageEnd() {
|
|
81
|
+
return Math.min(this.items.size, this.pageStart() + this.itemsPerPage);
|
|
82
|
+
},
|
|
83
|
+
hasPagination() {
|
|
84
|
+
return this.items.size > this.itemsPerPage;
|
|
85
|
+
},
|
|
86
|
+
showPagination() {
|
|
87
|
+
return this.isExpanded && this.hasPagination();
|
|
88
|
+
},
|
|
89
|
+
cannotPrevPage() {
|
|
90
|
+
return this.currentPage <= 0;
|
|
91
|
+
},
|
|
92
|
+
cannotNextPage() {
|
|
93
|
+
return this.currentPage >= this.pageCount() - 1;
|
|
94
|
+
},
|
|
95
|
+
pageIndicatorText() {
|
|
96
|
+
return `${this.currentPage + 1} / ${this.pageCount()}`;
|
|
97
|
+
},
|
|
98
|
+
prevPage() {
|
|
99
|
+
return this.currentPage > 0 ? this.setCurrentPage(this.currentPage - 1) : this;
|
|
100
|
+
},
|
|
101
|
+
nextPage() {
|
|
102
|
+
return this.currentPage < this.pageCount() - 1 ? this.setCurrentPage(this.currentPage + 1) : this;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
var compositeAlter = {
|
|
106
|
+
getPageRange() {
|
|
107
|
+
return { start: this.pageStart(), end: this.pageEnd() };
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
function makeCompositeView({
|
|
111
|
+
typeClass = "",
|
|
112
|
+
borderClass = "border-base-content/10",
|
|
113
|
+
toggleHandler = "$toggleIsExpanded"
|
|
114
|
+
} = {}) {
|
|
115
|
+
return html`<span class="font-mono text-sm leading-tight inline-block">
|
|
116
|
+
<span class="inline-flex items-center gap-2">
|
|
117
|
+
<button
|
|
118
|
+
type="button"
|
|
119
|
+
class="cursor-pointer text-base-content/70 hover:text-base-content inline-flex items-center gap-1"
|
|
120
|
+
:disabled="$isItemsEmpty"
|
|
121
|
+
@on.click="${toggleHandler}"
|
|
122
|
+
>
|
|
123
|
+
<span @text="$arrowText"></span>
|
|
124
|
+
<span class="${typeClass}" @text="$typeText"></span>
|
|
125
|
+
<span @text="$countText"></span>
|
|
126
|
+
</button>
|
|
127
|
+
<div @show="$showPagination" class="join">
|
|
128
|
+
<button
|
|
129
|
+
type="button"
|
|
130
|
+
class="join-item btn btn-xs"
|
|
131
|
+
:disabled="$cannotPrevPage"
|
|
132
|
+
@on.click="$prevPage"
|
|
133
|
+
>
|
|
134
|
+
«
|
|
135
|
+
</button>
|
|
136
|
+
<span
|
|
137
|
+
class="join-item badge font-mono text-xs"
|
|
138
|
+
@text="$pageIndicatorText"
|
|
139
|
+
></span>
|
|
140
|
+
<button
|
|
141
|
+
type="button"
|
|
142
|
+
class="join-item btn btn-xs"
|
|
143
|
+
:disabled="$cannotNextPage"
|
|
144
|
+
@on.click="$nextPage"
|
|
145
|
+
>
|
|
146
|
+
»
|
|
147
|
+
</button>
|
|
148
|
+
</div>
|
|
149
|
+
</span>
|
|
150
|
+
<div
|
|
151
|
+
@show=".isExpanded"
|
|
152
|
+
class="ml-1 flex flex-col gap-0.5 border-l ${borderClass} pl-2 mt-0.5"
|
|
153
|
+
>
|
|
154
|
+
<x render-each=".items" loop-with="getPageRange"></x>
|
|
155
|
+
</div>
|
|
156
|
+
</span>`;
|
|
157
|
+
}
|
|
158
|
+
var compositeView = makeCompositeView();
|
|
159
|
+
var JsonArray = component({
|
|
160
|
+
name: "JsonArray",
|
|
161
|
+
fields: compositeFields,
|
|
162
|
+
methods: {
|
|
163
|
+
...compositeMethods,
|
|
164
|
+
typeText() {
|
|
165
|
+
return "Array";
|
|
166
|
+
},
|
|
167
|
+
countText() {
|
|
168
|
+
return `(${this.items.size})`;
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
alter: compositeAlter,
|
|
172
|
+
view: compositeView
|
|
173
|
+
});
|
|
174
|
+
var JsonObject = component({
|
|
175
|
+
name: "JsonObject",
|
|
176
|
+
fields: compositeFields,
|
|
177
|
+
methods: {
|
|
178
|
+
...compositeMethods,
|
|
179
|
+
typeText() {
|
|
180
|
+
return "Object";
|
|
181
|
+
},
|
|
182
|
+
countText() {
|
|
183
|
+
return `{${this.items.size}}`;
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
alter: compositeAlter,
|
|
187
|
+
view: compositeView
|
|
188
|
+
});
|
|
189
|
+
var JsonProperty = component({
|
|
190
|
+
name: "JsonProperty",
|
|
191
|
+
fields: {
|
|
192
|
+
key: "",
|
|
193
|
+
child: null
|
|
194
|
+
},
|
|
195
|
+
view: html`<div class="flex items-center gap-2 leading-tight">
|
|
196
|
+
<span class="text-base-content/60 font-mono text-sm" @text=".key"></span>
|
|
197
|
+
<span class="text-base-content/30">:</span>
|
|
198
|
+
<x render=".child"></x>
|
|
199
|
+
</div>`
|
|
200
|
+
});
|
|
201
|
+
var JsonViewer = component({
|
|
202
|
+
name: "JsonViewer",
|
|
203
|
+
fields: {
|
|
204
|
+
value: null
|
|
205
|
+
},
|
|
206
|
+
methods: {
|
|
207
|
+
toggleIsExpanded() {
|
|
208
|
+
return typeof this.value?.toggleIsExpanded === "function" ? this.setValue(this.value.toggleIsExpanded()) : this;
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
statics: {
|
|
212
|
+
fromData(data) {
|
|
213
|
+
return this.make({ value: classifyData(data) });
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
view: html`<span class="contents"><x render=".value"></x></span>`
|
|
217
|
+
});
|
|
218
|
+
function classifyJson(data, recurse = classifyJson) {
|
|
219
|
+
if (data === null || data === undefined)
|
|
220
|
+
return JsonNull.make({});
|
|
221
|
+
const t = typeof data;
|
|
222
|
+
if (t === "boolean")
|
|
223
|
+
return JsonBoolean.make({ value: data });
|
|
224
|
+
if (t === "number")
|
|
225
|
+
return JsonNumber.make({ value: data });
|
|
226
|
+
if (t === "string")
|
|
227
|
+
return JsonString.make({ value: data });
|
|
228
|
+
if (Array.isArray(data)) {
|
|
229
|
+
const items = data.map((v, i) => JsonProperty.make({ key: String(i), child: recurse(v) }));
|
|
230
|
+
return JsonArray.make({ items });
|
|
231
|
+
}
|
|
232
|
+
if (t === "object") {
|
|
233
|
+
const proto = Object.getPrototypeOf(data);
|
|
234
|
+
if (proto === Object.prototype || proto === null) {
|
|
235
|
+
const items = Object.entries(data).map(([k, v]) => JsonProperty.make({ key: k, child: recurse(v) }));
|
|
236
|
+
return JsonObject.make({ items });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
function classifyData(data, recurse = classifyData) {
|
|
242
|
+
if (data === null || data === undefined)
|
|
243
|
+
return JsonNull.make({});
|
|
244
|
+
const t = typeof data;
|
|
245
|
+
if (t === "boolean")
|
|
246
|
+
return JsonBoolean.make({ value: data });
|
|
247
|
+
if (t === "number")
|
|
248
|
+
return JsonNumber.make({ value: data });
|
|
249
|
+
if (t === "string")
|
|
250
|
+
return JsonString.make({ value: data });
|
|
251
|
+
if (Array.isArray(data)) {
|
|
252
|
+
const items = data.map((v, i) => JsonProperty.make({ key: String(i), child: recurse(v) }));
|
|
253
|
+
return JsonArray.make({ items });
|
|
254
|
+
}
|
|
255
|
+
if (t === "object") {
|
|
256
|
+
const items = Object.entries(data).map(([k, v]) => JsonProperty.make({ key: k, child: recurse(v) }));
|
|
257
|
+
return JsonObject.make({ items });
|
|
258
|
+
}
|
|
259
|
+
return JsonNull.make({});
|
|
260
|
+
}
|
|
261
|
+
function chain(...classifiers) {
|
|
262
|
+
const recurse = (data) => {
|
|
263
|
+
for (const c of classifiers) {
|
|
264
|
+
const result = c(data, recurse);
|
|
265
|
+
if (result != null)
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
return null;
|
|
269
|
+
};
|
|
270
|
+
return recurse;
|
|
271
|
+
}
|
|
272
|
+
function getComponents() {
|
|
273
|
+
return [
|
|
274
|
+
JsonViewer,
|
|
275
|
+
JsonProperty,
|
|
276
|
+
JsonNull,
|
|
277
|
+
JsonBoolean,
|
|
278
|
+
JsonNumber,
|
|
279
|
+
JsonString,
|
|
280
|
+
JsonArray,
|
|
281
|
+
JsonObject
|
|
282
|
+
];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/components/data/immutable-inspector.js
|
|
286
|
+
var immutableContainerView = makeCompositeView({
|
|
287
|
+
typeClass: "text-accent",
|
|
288
|
+
borderClass: "border-accent"
|
|
289
|
+
});
|
|
290
|
+
function fmtMapKey(k) {
|
|
291
|
+
if (k === null || k === undefined)
|
|
292
|
+
return String(k);
|
|
293
|
+
const t = typeof k;
|
|
294
|
+
if (t === "string" || t === "number" || t === "boolean")
|
|
295
|
+
return String(k);
|
|
296
|
+
if (List.isList(k))
|
|
297
|
+
return `List[${k.size}]`;
|
|
298
|
+
if (OMap.isOrderedMap(k))
|
|
299
|
+
return `OrderedMap[${k.size}]`;
|
|
300
|
+
if (IMap.isMap(k))
|
|
301
|
+
return `Map[${k.size}]`;
|
|
302
|
+
if (OrderedSet.isOrderedSet(k))
|
|
303
|
+
return `OrderedSet[${k.size}]`;
|
|
304
|
+
if (ISet.isSet(k))
|
|
305
|
+
return `Set[${k.size}]`;
|
|
306
|
+
return String(k);
|
|
307
|
+
}
|
|
308
|
+
var ImEntry = component2({
|
|
309
|
+
name: "ImEntry",
|
|
310
|
+
fields: { key: "", child: null, showKey: true },
|
|
311
|
+
view: html2`<div class="flex items-center gap-2 leading-tight">
|
|
312
|
+
<span
|
|
313
|
+
@show=".showKey"
|
|
314
|
+
class="text-base-content/60 font-mono text-sm"
|
|
315
|
+
@text=".key"
|
|
316
|
+
></span>
|
|
317
|
+
<span @show=".showKey" class="text-base-content/30">:</span>
|
|
318
|
+
<x render=".child"></x>
|
|
319
|
+
</div>`
|
|
320
|
+
});
|
|
321
|
+
var ImList = component2({
|
|
322
|
+
name: "ImList",
|
|
323
|
+
fields: compositeFields,
|
|
324
|
+
methods: {
|
|
325
|
+
...compositeMethods,
|
|
326
|
+
typeText() {
|
|
327
|
+
return "List";
|
|
328
|
+
},
|
|
329
|
+
countText() {
|
|
330
|
+
return `[${this.items.size}]`;
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
alter: compositeAlter,
|
|
334
|
+
statics: {
|
|
335
|
+
fromData(list, recurse) {
|
|
336
|
+
const items = list.toArray().map((v, i) => ImEntry.make({ key: String(i), child: recurse(v) }));
|
|
337
|
+
return this.make({ items });
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
view: immutableContainerView
|
|
341
|
+
});
|
|
342
|
+
var ImStack = component2({
|
|
343
|
+
name: "ImStack",
|
|
344
|
+
fields: compositeFields,
|
|
345
|
+
methods: {
|
|
346
|
+
...compositeMethods,
|
|
347
|
+
typeText() {
|
|
348
|
+
return "Stack";
|
|
349
|
+
},
|
|
350
|
+
countText() {
|
|
351
|
+
return `[${this.items.size}]`;
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
alter: compositeAlter,
|
|
355
|
+
statics: {
|
|
356
|
+
fromData(stack, recurse) {
|
|
357
|
+
const items = stack.toArray().map((v, i) => ImEntry.make({ key: String(i), child: recurse(v) }));
|
|
358
|
+
return this.make({ items });
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
view: immutableContainerView
|
|
362
|
+
});
|
|
363
|
+
var ImMap = component2({
|
|
364
|
+
name: "ImMap",
|
|
365
|
+
fields: compositeFields,
|
|
366
|
+
methods: {
|
|
367
|
+
...compositeMethods,
|
|
368
|
+
typeText() {
|
|
369
|
+
return "Map";
|
|
370
|
+
},
|
|
371
|
+
countText() {
|
|
372
|
+
return `{${this.items.size}}`;
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
alter: compositeAlter,
|
|
376
|
+
statics: {
|
|
377
|
+
fromData(map, recurse) {
|
|
378
|
+
const items = [];
|
|
379
|
+
map.forEach((v, k) => {
|
|
380
|
+
items.push(ImEntry.make({ key: fmtMapKey(k), child: recurse(v) }));
|
|
381
|
+
});
|
|
382
|
+
return this.make({ items });
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
view: immutableContainerView
|
|
386
|
+
});
|
|
387
|
+
var ImOMap = component2({
|
|
388
|
+
name: "ImOMap",
|
|
389
|
+
fields: compositeFields,
|
|
390
|
+
methods: {
|
|
391
|
+
...compositeMethods,
|
|
392
|
+
typeText() {
|
|
393
|
+
return "OrderedMap";
|
|
394
|
+
},
|
|
395
|
+
countText() {
|
|
396
|
+
return `{${this.items.size}}`;
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
alter: compositeAlter,
|
|
400
|
+
statics: {
|
|
401
|
+
fromData(map, recurse) {
|
|
402
|
+
const items = [];
|
|
403
|
+
map.forEach((v, k) => {
|
|
404
|
+
items.push(ImEntry.make({ key: fmtMapKey(k), child: recurse(v) }));
|
|
405
|
+
});
|
|
406
|
+
return this.make({ items });
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
view: immutableContainerView
|
|
410
|
+
});
|
|
411
|
+
var ImSet = component2({
|
|
412
|
+
name: "ImSet",
|
|
413
|
+
fields: compositeFields,
|
|
414
|
+
methods: {
|
|
415
|
+
...compositeMethods,
|
|
416
|
+
typeText() {
|
|
417
|
+
return "Set";
|
|
418
|
+
},
|
|
419
|
+
countText() {
|
|
420
|
+
return `{${this.items.size}}`;
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
alter: compositeAlter,
|
|
424
|
+
statics: {
|
|
425
|
+
fromData(set, recurse) {
|
|
426
|
+
const items = set.toArray().map((v) => ImEntry.make({ key: "", showKey: false, child: recurse(v) }));
|
|
427
|
+
return this.make({ items });
|
|
428
|
+
}
|
|
429
|
+
},
|
|
430
|
+
view: immutableContainerView
|
|
431
|
+
});
|
|
432
|
+
var ImOSet = component2({
|
|
433
|
+
name: "ImOSet",
|
|
434
|
+
fields: compositeFields,
|
|
435
|
+
methods: {
|
|
436
|
+
...compositeMethods,
|
|
437
|
+
typeText() {
|
|
438
|
+
return "OrderedSet";
|
|
439
|
+
},
|
|
440
|
+
countText() {
|
|
441
|
+
return `{${this.items.size}}`;
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
alter: compositeAlter,
|
|
445
|
+
statics: {
|
|
446
|
+
fromData(set, recurse) {
|
|
447
|
+
const items = set.toArray().map((v) => ImEntry.make({ key: "", showKey: false, child: recurse(v) }));
|
|
448
|
+
return this.make({ items });
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
view: immutableContainerView
|
|
452
|
+
});
|
|
453
|
+
var ImRecord = component2({
|
|
454
|
+
name: "ImRecord",
|
|
455
|
+
fields: { ...compositeFields, recordName: "" },
|
|
456
|
+
methods: {
|
|
457
|
+
...compositeMethods,
|
|
458
|
+
typeText() {
|
|
459
|
+
return this.recordName ? `Record ${this.recordName}` : "Record";
|
|
460
|
+
},
|
|
461
|
+
countText() {
|
|
462
|
+
return `{${this.items.size}}`;
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
alter: compositeAlter,
|
|
466
|
+
statics: {
|
|
467
|
+
fromData(rec, recurse) {
|
|
468
|
+
const recordName = rec._name || rec.constructor.name || "Record";
|
|
469
|
+
const items = [];
|
|
470
|
+
rec.toSeq().forEach((v, k) => {
|
|
471
|
+
items.push(ImEntry.make({ key: String(k), child: recurse(v) }));
|
|
472
|
+
});
|
|
473
|
+
return this.make({ recordName, items });
|
|
474
|
+
}
|
|
475
|
+
},
|
|
476
|
+
view: immutableContainerView
|
|
477
|
+
});
|
|
478
|
+
var ImRange = component2({
|
|
479
|
+
name: "ImRange",
|
|
480
|
+
fields: { rangeText: "" },
|
|
481
|
+
statics: {
|
|
482
|
+
fromData(seq) {
|
|
483
|
+
const { _start: start, _end: end, _step: step } = seq;
|
|
484
|
+
const rangeText = step === 1 ? `${start}…${end}` : `${start}…${end} by ${step}`;
|
|
485
|
+
return this.make({ rangeText });
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
view: html2`<span
|
|
489
|
+
class="font-mono text-sm leading-tight inline-flex items-center gap-1"
|
|
490
|
+
>
|
|
491
|
+
<span class="font-mono text-accent">Range</span>
|
|
492
|
+
<span class="text-base-content/70" @text=".rangeText"></span>
|
|
493
|
+
</span>`
|
|
494
|
+
});
|
|
495
|
+
var ImInspector = component2({
|
|
496
|
+
name: "ImInspector",
|
|
497
|
+
fields: { value: null },
|
|
498
|
+
methods: {
|
|
499
|
+
toggleIsExpanded() {
|
|
500
|
+
return typeof this.value?.toggleIsExpanded === "function" ? this.setValue(this.value.toggleIsExpanded()) : this;
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
statics: {
|
|
504
|
+
fromData(data) {
|
|
505
|
+
return this.make({ value: dispatch(data) });
|
|
506
|
+
}
|
|
507
|
+
},
|
|
508
|
+
view: html2`<span class="contents"><x render=".value"></x></span>`
|
|
509
|
+
});
|
|
510
|
+
function classifyImmutable(data, recurse) {
|
|
511
|
+
if (Seq.isSeq(data) && data._start !== undefined && data._end !== undefined && data._step !== undefined) {
|
|
512
|
+
return ImRange.Class.fromData(data, recurse);
|
|
513
|
+
}
|
|
514
|
+
if (Record.isRecord(data))
|
|
515
|
+
return ImRecord.Class.fromData(data, recurse);
|
|
516
|
+
if (List.isList(data))
|
|
517
|
+
return ImList.Class.fromData(data, recurse);
|
|
518
|
+
if (Stack.isStack(data))
|
|
519
|
+
return ImStack.Class.fromData(data, recurse);
|
|
520
|
+
if (OMap.isOrderedMap(data))
|
|
521
|
+
return ImOMap.Class.fromData(data, recurse);
|
|
522
|
+
if (IMap.isMap(data))
|
|
523
|
+
return ImMap.Class.fromData(data, recurse);
|
|
524
|
+
if (OrderedSet.isOrderedSet(data))
|
|
525
|
+
return ImOSet.Class.fromData(data, recurse);
|
|
526
|
+
if (ISet.isSet(data))
|
|
527
|
+
return ImSet.Class.fromData(data, recurse);
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
var dispatch = chain(classifyImmutable, classifyData);
|
|
531
|
+
function getComponents2() {
|
|
532
|
+
return [
|
|
533
|
+
ImInspector,
|
|
534
|
+
ImEntry,
|
|
535
|
+
ImList,
|
|
536
|
+
ImStack,
|
|
537
|
+
ImMap,
|
|
538
|
+
ImOMap,
|
|
539
|
+
ImSet,
|
|
540
|
+
ImOSet,
|
|
541
|
+
ImRecord,
|
|
542
|
+
ImRange,
|
|
543
|
+
...getComponents()
|
|
544
|
+
];
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// src/components/data/data.js
|
|
548
|
+
var JsUndefined = component3({
|
|
549
|
+
name: "JsUndefined",
|
|
550
|
+
fields: {},
|
|
551
|
+
view: html3`<span
|
|
552
|
+
class="font-mono text-sm leading-tight text-warning italic"
|
|
553
|
+
>undefined</span
|
|
554
|
+
>`
|
|
555
|
+
});
|
|
556
|
+
var JsBigInt = component3({
|
|
557
|
+
name: "JsBigInt",
|
|
558
|
+
fields: { value: "" },
|
|
559
|
+
methods: {
|
|
560
|
+
text() {
|
|
561
|
+
return `${this.value}n`;
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
view: html3`<span
|
|
565
|
+
class="font-mono text-sm leading-tight text-info"
|
|
566
|
+
@text="$text"
|
|
567
|
+
></span>`
|
|
568
|
+
});
|
|
569
|
+
var JsSymbol = component3({
|
|
570
|
+
name: "JsSymbol",
|
|
571
|
+
fields: { description: "" },
|
|
572
|
+
view: html3`<span class="font-mono text-sm leading-tight"
|
|
573
|
+
><span class="text-info">Symbol</span
|
|
574
|
+
><span class="text-base-content/40">: </span
|
|
575
|
+
><span @text=".description"></span
|
|
576
|
+
></span>`
|
|
577
|
+
});
|
|
578
|
+
var JsFunction = component3({
|
|
579
|
+
name: "JsFunction",
|
|
580
|
+
fields: { name: "", kind: "function" },
|
|
581
|
+
methods: {
|
|
582
|
+
hasIdentifier() {
|
|
583
|
+
return this.kind !== "arrow" && this.name !== "";
|
|
584
|
+
},
|
|
585
|
+
prefixText() {
|
|
586
|
+
if (this.kind === "arrow")
|
|
587
|
+
return "() => { … }";
|
|
588
|
+
if (this.kind === "class")
|
|
589
|
+
return this.name ? "class " : "class { … }";
|
|
590
|
+
return this.name ? "function " : "function () { … }";
|
|
591
|
+
},
|
|
592
|
+
suffixText() {
|
|
593
|
+
if (this.kind === "class")
|
|
594
|
+
return " { … }";
|
|
595
|
+
return "() { … }";
|
|
596
|
+
}
|
|
597
|
+
},
|
|
598
|
+
view: html3`<span class="font-mono text-sm leading-tight"
|
|
599
|
+
><span class="text-base-content/40" @text="$prefixText"></span
|
|
600
|
+
><span
|
|
601
|
+
class="text-info"
|
|
602
|
+
@show="$hasIdentifier"
|
|
603
|
+
@text=".name"
|
|
604
|
+
></span
|
|
605
|
+
><span
|
|
606
|
+
class="text-base-content/40"
|
|
607
|
+
@show="$hasIdentifier"
|
|
608
|
+
@text="$suffixText"
|
|
609
|
+
></span
|
|
610
|
+
></span>`
|
|
611
|
+
});
|
|
612
|
+
var JsDate = component3({
|
|
613
|
+
name: "JsDate",
|
|
614
|
+
fields: { value: "" },
|
|
615
|
+
view: html3`<span class="font-mono text-sm leading-tight"
|
|
616
|
+
><span class="text-info">Date</span
|
|
617
|
+
><span class="text-base-content/40">: </span
|
|
618
|
+
><span @text=".value"></span
|
|
619
|
+
></span>`
|
|
620
|
+
});
|
|
621
|
+
var JsRegExp = component3({
|
|
622
|
+
name: "JsRegExp",
|
|
623
|
+
fields: { value: "" },
|
|
624
|
+
view: html3`<span
|
|
625
|
+
class="font-mono text-sm leading-tight text-error"
|
|
626
|
+
@text=".value"
|
|
627
|
+
></span>`
|
|
628
|
+
});
|
|
629
|
+
var JsError = component3({
|
|
630
|
+
name: "JsError",
|
|
631
|
+
fields: { errorName: "Error", message: "" },
|
|
632
|
+
view: html3`<span class="font-mono text-sm leading-tight"
|
|
633
|
+
><span class="text-info" @text=".errorName"></span
|
|
634
|
+
><span class="text-base-content/40">: </span
|
|
635
|
+
><span
|
|
636
|
+
class="inline-block max-w-xs truncate align-bottom"
|
|
637
|
+
:title=".message"
|
|
638
|
+
@text=".message"
|
|
639
|
+
></span
|
|
640
|
+
></span>`
|
|
641
|
+
});
|
|
642
|
+
var JsSetItem = component3({
|
|
643
|
+
name: "JsSetItem",
|
|
644
|
+
fields: { child: null },
|
|
645
|
+
view: html3`<div class="flex items-center gap-2 leading-tight">
|
|
646
|
+
<x render=".child"></x>
|
|
647
|
+
</div>`
|
|
648
|
+
});
|
|
649
|
+
function fmtAnyKey(k) {
|
|
650
|
+
if (k === null)
|
|
651
|
+
return "null";
|
|
652
|
+
if (k === undefined)
|
|
653
|
+
return "undefined";
|
|
654
|
+
const t = typeof k;
|
|
655
|
+
if (t === "string" || t === "number" || t === "boolean" || t === "bigint")
|
|
656
|
+
return String(k);
|
|
657
|
+
if (t === "symbol")
|
|
658
|
+
return String(k);
|
|
659
|
+
if (t === "function")
|
|
660
|
+
return `ƒ ${k.name || "(anonymous)"}()`;
|
|
661
|
+
if (Array.isArray(k))
|
|
662
|
+
return `Array(${k.length})`;
|
|
663
|
+
if (k instanceof Map)
|
|
664
|
+
return `Map(${k.size})`;
|
|
665
|
+
if (k instanceof Set)
|
|
666
|
+
return `Set(${k.size})`;
|
|
667
|
+
if (k instanceof Date)
|
|
668
|
+
return k.toISOString();
|
|
669
|
+
if (List2.isList(k))
|
|
670
|
+
return `List[${k.size}]`;
|
|
671
|
+
if (OMap2.isOrderedMap(k))
|
|
672
|
+
return `OrderedMap[${k.size}]`;
|
|
673
|
+
if (IMap2.isMap(k))
|
|
674
|
+
return `Map[${k.size}]`;
|
|
675
|
+
if (OrderedSet2.isOrderedSet(k))
|
|
676
|
+
return `OrderedSet[${k.size}]`;
|
|
677
|
+
if (ISet2.isSet(k))
|
|
678
|
+
return `Set[${k.size}]`;
|
|
679
|
+
const ctor = k.constructor?.name;
|
|
680
|
+
return ctor && ctor !== "Object" ? `${ctor} {…}` : Object.prototype.toString.call(k);
|
|
681
|
+
}
|
|
682
|
+
var JsMap = component3({
|
|
683
|
+
name: "JsMap",
|
|
684
|
+
fields: compositeFields,
|
|
685
|
+
methods: {
|
|
686
|
+
...compositeMethods,
|
|
687
|
+
typeText() {
|
|
688
|
+
return "Map";
|
|
689
|
+
},
|
|
690
|
+
countText() {
|
|
691
|
+
return `(${this.items.size})`;
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
alter: compositeAlter,
|
|
695
|
+
statics: {
|
|
696
|
+
fromData(map, recurse) {
|
|
697
|
+
const items = [];
|
|
698
|
+
map.forEach((v, k) => {
|
|
699
|
+
items.push(JsonProperty.make({ key: fmtAnyKey(k), child: recurse(v) }));
|
|
700
|
+
});
|
|
701
|
+
return this.make({ items });
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
view: compositeView
|
|
705
|
+
});
|
|
706
|
+
var JsSet = component3({
|
|
707
|
+
name: "JsSet",
|
|
708
|
+
fields: compositeFields,
|
|
709
|
+
methods: {
|
|
710
|
+
...compositeMethods,
|
|
711
|
+
typeText() {
|
|
712
|
+
return "Set";
|
|
713
|
+
},
|
|
714
|
+
countText() {
|
|
715
|
+
return `(${this.items.size})`;
|
|
716
|
+
}
|
|
717
|
+
},
|
|
718
|
+
alter: compositeAlter,
|
|
719
|
+
statics: {
|
|
720
|
+
fromData(set, recurse) {
|
|
721
|
+
const items = [...set].map((v) => JsSetItem.make({ child: recurse(v) }));
|
|
722
|
+
return this.make({ items });
|
|
723
|
+
}
|
|
724
|
+
},
|
|
725
|
+
view: compositeView
|
|
726
|
+
});
|
|
727
|
+
var JsClassInstance = component3({
|
|
728
|
+
name: "JsClassInstance",
|
|
729
|
+
fields: { ...compositeFields, className: "Object" },
|
|
730
|
+
methods: {
|
|
731
|
+
...compositeMethods,
|
|
732
|
+
typeText() {
|
|
733
|
+
return this.className;
|
|
734
|
+
},
|
|
735
|
+
countText() {
|
|
736
|
+
return `{${this.items.size}}`;
|
|
737
|
+
}
|
|
738
|
+
},
|
|
739
|
+
alter: compositeAlter,
|
|
740
|
+
statics: {
|
|
741
|
+
fromData(obj, recurse) {
|
|
742
|
+
const className = obj.constructor?.name || "Object";
|
|
743
|
+
const items = [];
|
|
744
|
+
try {
|
|
745
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
746
|
+
items.push(JsonProperty.make({ key: k, child: recurse(v) }));
|
|
747
|
+
}
|
|
748
|
+
} catch (_e) {}
|
|
749
|
+
return this.make({ className, items });
|
|
750
|
+
}
|
|
751
|
+
},
|
|
752
|
+
view: compositeView
|
|
753
|
+
});
|
|
754
|
+
function classifyJsExtra(data, recurse) {
|
|
755
|
+
if (data === undefined)
|
|
756
|
+
return JsUndefined.make({});
|
|
757
|
+
const t = typeof data;
|
|
758
|
+
if (t === "symbol")
|
|
759
|
+
return JsSymbol.make({ description: data.description ?? "" });
|
|
760
|
+
if (t === "function") {
|
|
761
|
+
let kind = "function";
|
|
762
|
+
const src = String(data);
|
|
763
|
+
if (/^(?:async\s+)?class\s/.test(src))
|
|
764
|
+
kind = "class";
|
|
765
|
+
else if (!/^(?:async\s+)?function\b/.test(src) && /=>/.test(src))
|
|
766
|
+
kind = "arrow";
|
|
767
|
+
return JsFunction.make({ name: data.name ?? "", kind });
|
|
768
|
+
}
|
|
769
|
+
if (t === "bigint")
|
|
770
|
+
return JsBigInt.make({ value: String(data) });
|
|
771
|
+
if (t !== "object" || data === null)
|
|
772
|
+
return null;
|
|
773
|
+
if (data instanceof Date)
|
|
774
|
+
return JsDate.make({ value: data.toISOString() });
|
|
775
|
+
if (data instanceof RegExp)
|
|
776
|
+
return JsRegExp.make({ value: String(data) });
|
|
777
|
+
if (data instanceof Error)
|
|
778
|
+
return JsError.make({
|
|
779
|
+
errorName: data.name ?? "Error",
|
|
780
|
+
message: data.message ?? ""
|
|
781
|
+
});
|
|
782
|
+
if (data instanceof Map)
|
|
783
|
+
return JsMap.Class.fromData(data, recurse);
|
|
784
|
+
if (data instanceof Set)
|
|
785
|
+
return JsSet.Class.fromData(data, recurse);
|
|
786
|
+
const proto = Object.getPrototypeOf(data);
|
|
787
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
788
|
+
return JsClassInstance.Class.fromData(data, recurse);
|
|
789
|
+
}
|
|
790
|
+
return null;
|
|
791
|
+
}
|
|
792
|
+
var dispatch2 = chain(classifyImmutable, classifyJsExtra, classifyJson);
|
|
793
|
+
var DataInspector = component3({
|
|
794
|
+
name: "DataInspector",
|
|
795
|
+
fields: { value: null },
|
|
796
|
+
methods: {
|
|
797
|
+
toggleIsExpanded() {
|
|
798
|
+
return typeof this.value?.toggleIsExpanded === "function" ? this.setValue(this.value.toggleIsExpanded()) : this;
|
|
799
|
+
}
|
|
800
|
+
},
|
|
801
|
+
statics: {
|
|
802
|
+
fromData(data) {
|
|
803
|
+
return this.make({ value: dispatch2(data) });
|
|
804
|
+
}
|
|
805
|
+
},
|
|
806
|
+
view: html3`<span class="contents"><x render=".value"></x></span>`
|
|
807
|
+
});
|
|
808
|
+
function getComponents3() {
|
|
809
|
+
return [
|
|
810
|
+
DataInspector,
|
|
811
|
+
JsUndefined,
|
|
812
|
+
JsBigInt,
|
|
813
|
+
JsSymbol,
|
|
814
|
+
JsFunction,
|
|
815
|
+
JsDate,
|
|
816
|
+
JsRegExp,
|
|
817
|
+
JsError,
|
|
818
|
+
JsMap,
|
|
819
|
+
JsSet,
|
|
820
|
+
JsClassInstance,
|
|
821
|
+
JsSetItem,
|
|
822
|
+
...getComponents2()
|
|
823
|
+
];
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// src/components/data/json-schema.js
|
|
827
|
+
import { component as component4, html as html4 } from "tutuca";
|
|
828
|
+
var schemaNodeFields = {
|
|
829
|
+
...compositeFields,
|
|
830
|
+
typeLabel: "",
|
|
831
|
+
badges: [],
|
|
832
|
+
title: "",
|
|
833
|
+
description: "",
|
|
834
|
+
deprecated: false
|
|
835
|
+
};
|
|
836
|
+
function schemaNodeMethods(extra = {}) {
|
|
837
|
+
return {
|
|
838
|
+
...compositeMethods,
|
|
839
|
+
typeText() {
|
|
840
|
+
return this.typeLabel;
|
|
841
|
+
},
|
|
842
|
+
...extra
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
var GRID_BODY = html4`<div
|
|
846
|
+
@show=".isExpanded"
|
|
847
|
+
class="ml-1 grid gap-x-2 gap-y-0.5 items-baseline border-l border-base-content/10 pl-2 mt-0.5"
|
|
848
|
+
style="grid-template-columns: auto 1fr"
|
|
849
|
+
>
|
|
850
|
+
<x render-each=".items" loop-with="getPageRange"></x>
|
|
851
|
+
</div>`;
|
|
852
|
+
var LIST_BODY = html4`<div
|
|
853
|
+
@show=".isExpanded"
|
|
854
|
+
class="ml-1 flex flex-col gap-0.5 border-l border-base-content/10 pl-2 mt-0.5"
|
|
855
|
+
>
|
|
856
|
+
<x render-each=".items" loop-with="getPageRange"></x>
|
|
857
|
+
</div>`;
|
|
858
|
+
function makeSchemaView(accent, body = GRID_BODY) {
|
|
859
|
+
return html4`<span class="font-mono text-sm leading-tight inline-block">
|
|
860
|
+
<span class="inline-flex items-center gap-2 flex-wrap">
|
|
861
|
+
<button
|
|
862
|
+
type="button"
|
|
863
|
+
class="cursor-pointer text-base-content/70 hover:text-base-content inline-flex items-center gap-1"
|
|
864
|
+
:disabled="$isItemsEmpty"
|
|
865
|
+
@on.click="$toggleIsExpanded"
|
|
866
|
+
>
|
|
867
|
+
<span @hide="$isItemsEmpty" @text="$arrowText"></span>
|
|
868
|
+
<span class="${accent} font-semibold" @text="$typeText"></span>
|
|
869
|
+
<span class="text-base-content/50" @text="$countText"></span>
|
|
870
|
+
</button>
|
|
871
|
+
<span
|
|
872
|
+
@each=".badges"
|
|
873
|
+
class="badge badge-xs badge-ghost font-mono font-normal"
|
|
874
|
+
><x text="@value"></x
|
|
875
|
+
></span>
|
|
876
|
+
<span @show=".deprecated" class="badge badge-xs badge-warning"
|
|
877
|
+
>deprecated</span
|
|
878
|
+
>
|
|
879
|
+
<div @show="$showPagination" class="join">
|
|
880
|
+
<button
|
|
881
|
+
type="button"
|
|
882
|
+
class="join-item btn btn-xs"
|
|
883
|
+
:disabled="$cannotPrevPage"
|
|
884
|
+
@on.click="$prevPage"
|
|
885
|
+
>
|
|
886
|
+
«
|
|
887
|
+
</button>
|
|
888
|
+
<span
|
|
889
|
+
class="join-item badge font-mono text-xs"
|
|
890
|
+
@text="$pageIndicatorText"
|
|
891
|
+
></span>
|
|
892
|
+
<button
|
|
893
|
+
type="button"
|
|
894
|
+
class="join-item btn btn-xs"
|
|
895
|
+
:disabled="$cannotNextPage"
|
|
896
|
+
@on.click="$nextPage"
|
|
897
|
+
>
|
|
898
|
+
»
|
|
899
|
+
</button>
|
|
900
|
+
</div>
|
|
901
|
+
</span>
|
|
902
|
+
<span
|
|
903
|
+
@show="truthy? .title"
|
|
904
|
+
class="block text-base-content/80 italic"
|
|
905
|
+
@text=".title"
|
|
906
|
+
></span>
|
|
907
|
+
<span
|
|
908
|
+
@show="truthy? .description"
|
|
909
|
+
class="block text-base-content/50"
|
|
910
|
+
@text=".description"
|
|
911
|
+
></span>
|
|
912
|
+
${body}
|
|
913
|
+
</span>`;
|
|
914
|
+
}
|
|
915
|
+
function metaOf(schema) {
|
|
916
|
+
return {
|
|
917
|
+
title: typeof schema.title === "string" ? schema.title : "",
|
|
918
|
+
description: typeof schema.description === "string" ? schema.description : "",
|
|
919
|
+
deprecated: schema.deprecated === true
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
function displayType(schema, fallback) {
|
|
923
|
+
const t = schema.type;
|
|
924
|
+
if (Array.isArray(t))
|
|
925
|
+
return t.join(" | ");
|
|
926
|
+
if (typeof t === "string")
|
|
927
|
+
return t;
|
|
928
|
+
return fallback;
|
|
929
|
+
}
|
|
930
|
+
function simpleScalarType(schema) {
|
|
931
|
+
if (schema == null || typeof schema !== "object")
|
|
932
|
+
return null;
|
|
933
|
+
const t = schema.type;
|
|
934
|
+
const typeOk = typeof t === "string" || Array.isArray(t) && t.every((x) => typeof x === "string");
|
|
935
|
+
if (!typeOk)
|
|
936
|
+
return null;
|
|
937
|
+
if (Object.keys(schema).some((k) => k !== "type"))
|
|
938
|
+
return null;
|
|
939
|
+
return Array.isArray(t) ? t.join(" | ") : t;
|
|
940
|
+
}
|
|
941
|
+
function combinatorPhrasing(kind) {
|
|
942
|
+
if (kind === "allOf")
|
|
943
|
+
return "all of";
|
|
944
|
+
if (kind === "anyOf")
|
|
945
|
+
return "any of";
|
|
946
|
+
if (kind === "oneOf")
|
|
947
|
+
return "one of";
|
|
948
|
+
return "combination";
|
|
949
|
+
}
|
|
950
|
+
function collectBadges(schema) {
|
|
951
|
+
const b = [];
|
|
952
|
+
if (schema.format != null)
|
|
953
|
+
b.push(`format: ${schema.format}`);
|
|
954
|
+
if (schema.minLength != null)
|
|
955
|
+
b.push(`min length: ${schema.minLength}`);
|
|
956
|
+
if (schema.maxLength != null)
|
|
957
|
+
b.push(`max length: ${schema.maxLength}`);
|
|
958
|
+
if (schema.pattern != null)
|
|
959
|
+
b.push(`pattern: /${schema.pattern}/`);
|
|
960
|
+
if (schema.minimum != null)
|
|
961
|
+
b.push(`≥ ${schema.minimum}`);
|
|
962
|
+
if (schema.maximum != null)
|
|
963
|
+
b.push(`≤ ${schema.maximum}`);
|
|
964
|
+
if (schema.exclusiveMinimum != null)
|
|
965
|
+
b.push(`> ${schema.exclusiveMinimum}`);
|
|
966
|
+
if (schema.exclusiveMaximum != null)
|
|
967
|
+
b.push(`< ${schema.exclusiveMaximum}`);
|
|
968
|
+
if (schema.multipleOf != null)
|
|
969
|
+
b.push(`multiple of ${schema.multipleOf}`);
|
|
970
|
+
if (schema.minItems != null)
|
|
971
|
+
b.push(`min items: ${schema.minItems}`);
|
|
972
|
+
if (schema.maxItems != null)
|
|
973
|
+
b.push(`max items: ${schema.maxItems}`);
|
|
974
|
+
if (schema.uniqueItems)
|
|
975
|
+
b.push("unique items");
|
|
976
|
+
if (schema.minProperties != null)
|
|
977
|
+
b.push(`min properties: ${schema.minProperties}`);
|
|
978
|
+
if (schema.maxProperties != null)
|
|
979
|
+
b.push(`max properties: ${schema.maxProperties}`);
|
|
980
|
+
if (schema.additionalProperties === false)
|
|
981
|
+
b.push("no additional properties");
|
|
982
|
+
if (schema.readOnly)
|
|
983
|
+
b.push("read-only");
|
|
984
|
+
if (schema.writeOnly)
|
|
985
|
+
b.push("write-only");
|
|
986
|
+
return b;
|
|
987
|
+
}
|
|
988
|
+
function valueBranch(label, value) {
|
|
989
|
+
return SchemaBranch.make({
|
|
990
|
+
label,
|
|
991
|
+
child: JsonViewer.Class.fromData(value),
|
|
992
|
+
labelClass: "text-info"
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
function collectExtras(schema, recurse, primary) {
|
|
996
|
+
const rows = [];
|
|
997
|
+
if (primary !== "const" && "const" in schema)
|
|
998
|
+
rows.push(valueBranch("const", schema.const));
|
|
999
|
+
if (primary !== "enum" && Array.isArray(schema.enum))
|
|
1000
|
+
rows.push(valueBranch("enum", schema.enum));
|
|
1001
|
+
if ("default" in schema)
|
|
1002
|
+
rows.push(valueBranch("default", schema.default));
|
|
1003
|
+
if ("examples" in schema)
|
|
1004
|
+
rows.push(valueBranch("examples", schema.examples));
|
|
1005
|
+
if (primary !== "combinator") {
|
|
1006
|
+
for (const k of ["allOf", "anyOf", "oneOf"]) {
|
|
1007
|
+
if (Array.isArray(schema[k])) {
|
|
1008
|
+
for (const s of schema[k]) {
|
|
1009
|
+
rows.push(SchemaBranch.make({
|
|
1010
|
+
label: combinatorPhrasing(k),
|
|
1011
|
+
child: recurse(s),
|
|
1012
|
+
labelClass: "text-accent"
|
|
1013
|
+
}));
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
if (primary !== "not" && schema.not !== undefined) {
|
|
1019
|
+
rows.push(SchemaBranch.make({
|
|
1020
|
+
label: "not",
|
|
1021
|
+
child: recurse(schema.not),
|
|
1022
|
+
labelClass: "text-error"
|
|
1023
|
+
}));
|
|
1024
|
+
}
|
|
1025
|
+
if (primary !== "conditional") {
|
|
1026
|
+
if (schema.if !== undefined)
|
|
1027
|
+
rows.push(SchemaBranch.make({ label: "if", child: recurse(schema.if) }));
|
|
1028
|
+
if (schema.then !== undefined)
|
|
1029
|
+
rows.push(SchemaBranch.make({ label: "then", child: recurse(schema.then) }));
|
|
1030
|
+
if (schema.else !== undefined)
|
|
1031
|
+
rows.push(SchemaBranch.make({ label: "else", child: recurse(schema.else) }));
|
|
1032
|
+
}
|
|
1033
|
+
const defs = schema.$defs || schema.definitions;
|
|
1034
|
+
if (defs && typeof defs === "object") {
|
|
1035
|
+
for (const [name, s] of Object.entries(defs)) {
|
|
1036
|
+
rows.push(SchemaBranch.make({
|
|
1037
|
+
label: `$defs/${name}`,
|
|
1038
|
+
child: recurse(s),
|
|
1039
|
+
labelClass: "text-secondary"
|
|
1040
|
+
}));
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
return rows;
|
|
1044
|
+
}
|
|
1045
|
+
var SchemaProperty = component4({
|
|
1046
|
+
name: "SchemaProperty",
|
|
1047
|
+
fields: { key: "", child: null, required: false },
|
|
1048
|
+
view: html4`<div class="contents">
|
|
1049
|
+
<span class="font-mono text-sm flex items-center gap-1 leading-tight">
|
|
1050
|
+
<span class="text-base-content/60" @text=".key"></span>
|
|
1051
|
+
<span @show=".required" class="text-error font-bold" title="required"
|
|
1052
|
+
>*</span
|
|
1053
|
+
>
|
|
1054
|
+
<span class="text-base-content/30">:</span>
|
|
1055
|
+
</span>
|
|
1056
|
+
<x render=".child"></x>
|
|
1057
|
+
</div>`
|
|
1058
|
+
});
|
|
1059
|
+
var SchemaBranch = component4({
|
|
1060
|
+
name: "SchemaBranch",
|
|
1061
|
+
fields: { label: "", child: null, labelClass: "text-accent" },
|
|
1062
|
+
methods: {
|
|
1063
|
+
labelCssClass() {
|
|
1064
|
+
return `font-mono text-sm ${this.labelClass}`;
|
|
1065
|
+
}
|
|
1066
|
+
},
|
|
1067
|
+
view: html4`<div class="contents">
|
|
1068
|
+
<span class="flex items-center gap-1 leading-tight">
|
|
1069
|
+
<span :class="$labelCssClass" @text=".label"></span>
|
|
1070
|
+
<span class="text-base-content/30">:</span>
|
|
1071
|
+
</span>
|
|
1072
|
+
<x render=".child"></x>
|
|
1073
|
+
</div>`,
|
|
1074
|
+
views: {
|
|
1075
|
+
_margauiClasses: html4`<p
|
|
1076
|
+
class="text-accent text-secondary text-info text-error"
|
|
1077
|
+
></p>`
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
var SchemaScalar = component4({
|
|
1081
|
+
name: "SchemaScalar",
|
|
1082
|
+
fields: schemaNodeFields,
|
|
1083
|
+
methods: schemaNodeMethods({
|
|
1084
|
+
countText() {
|
|
1085
|
+
return "";
|
|
1086
|
+
}
|
|
1087
|
+
}),
|
|
1088
|
+
alter: compositeAlter,
|
|
1089
|
+
statics: {
|
|
1090
|
+
fromData(schema, recurse) {
|
|
1091
|
+
return this.make({
|
|
1092
|
+
typeLabel: displayType(schema, "any"),
|
|
1093
|
+
badges: collectBadges(schema),
|
|
1094
|
+
...metaOf(schema),
|
|
1095
|
+
items: collectExtras(schema, recurse, "scalar")
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
},
|
|
1099
|
+
view: makeSchemaView("text-success")
|
|
1100
|
+
});
|
|
1101
|
+
var SchemaObject = component4({
|
|
1102
|
+
name: "SchemaObject",
|
|
1103
|
+
fields: schemaNodeFields,
|
|
1104
|
+
methods: schemaNodeMethods({
|
|
1105
|
+
countText() {
|
|
1106
|
+
return this.items.size > 0 ? `{${this.items.size}}` : "";
|
|
1107
|
+
}
|
|
1108
|
+
}),
|
|
1109
|
+
alter: compositeAlter,
|
|
1110
|
+
statics: {
|
|
1111
|
+
fromData(schema, recurse) {
|
|
1112
|
+
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
1113
|
+
const items = [];
|
|
1114
|
+
if (schema.properties && typeof schema.properties === "object") {
|
|
1115
|
+
for (const [k, v] of Object.entries(schema.properties)) {
|
|
1116
|
+
items.push(SchemaProperty.make({
|
|
1117
|
+
key: k,
|
|
1118
|
+
required: required.includes(k),
|
|
1119
|
+
child: recurse(v)
|
|
1120
|
+
}));
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
if (schema.patternProperties && typeof schema.patternProperties === "object") {
|
|
1124
|
+
for (const [pat, v] of Object.entries(schema.patternProperties)) {
|
|
1125
|
+
items.push(SchemaBranch.make({
|
|
1126
|
+
label: `/${pat}/`,
|
|
1127
|
+
child: recurse(v),
|
|
1128
|
+
labelClass: "text-secondary"
|
|
1129
|
+
}));
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
|
|
1133
|
+
items.push(SchemaBranch.make({
|
|
1134
|
+
label: "additional properties",
|
|
1135
|
+
child: recurse(schema.additionalProperties),
|
|
1136
|
+
labelClass: "text-secondary"
|
|
1137
|
+
}));
|
|
1138
|
+
}
|
|
1139
|
+
if (schema.propertyNames !== undefined) {
|
|
1140
|
+
items.push(SchemaBranch.make({
|
|
1141
|
+
label: "property names",
|
|
1142
|
+
child: recurse(schema.propertyNames),
|
|
1143
|
+
labelClass: "text-secondary"
|
|
1144
|
+
}));
|
|
1145
|
+
}
|
|
1146
|
+
items.push(...collectExtras(schema, recurse, "object"));
|
|
1147
|
+
return this.make({
|
|
1148
|
+
typeLabel: displayType(schema, "object"),
|
|
1149
|
+
badges: collectBadges(schema),
|
|
1150
|
+
...metaOf(schema),
|
|
1151
|
+
items
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
},
|
|
1155
|
+
view: makeSchemaView("text-primary")
|
|
1156
|
+
});
|
|
1157
|
+
var SchemaArray = component4({
|
|
1158
|
+
name: "SchemaArray",
|
|
1159
|
+
fields: schemaNodeFields,
|
|
1160
|
+
methods: schemaNodeMethods({
|
|
1161
|
+
countText() {
|
|
1162
|
+
return this.items.size > 0 ? `[${this.items.size}]` : "";
|
|
1163
|
+
}
|
|
1164
|
+
}),
|
|
1165
|
+
alter: compositeAlter,
|
|
1166
|
+
statics: {
|
|
1167
|
+
fromData(schema, recurse) {
|
|
1168
|
+
const items = [];
|
|
1169
|
+
const isTuple = Array.isArray(schema.prefixItems);
|
|
1170
|
+
const inlineType = !isTuple && !Array.isArray(schema.items) ? simpleScalarType(schema.items) : null;
|
|
1171
|
+
if (isTuple) {
|
|
1172
|
+
schema.prefixItems.forEach((s, i) => {
|
|
1173
|
+
items.push(SchemaBranch.make({ label: `item ${i}`, child: recurse(s) }));
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
if (Array.isArray(schema.items)) {
|
|
1177
|
+
schema.items.forEach((s, i) => {
|
|
1178
|
+
items.push(SchemaBranch.make({ label: `item ${i}`, child: recurse(s) }));
|
|
1179
|
+
});
|
|
1180
|
+
} else if (schema.items !== undefined && inlineType == null) {
|
|
1181
|
+
items.push(SchemaBranch.make({ label: "items", child: recurse(schema.items) }));
|
|
1182
|
+
}
|
|
1183
|
+
if (schema.contains !== undefined) {
|
|
1184
|
+
items.push(SchemaBranch.make({
|
|
1185
|
+
label: "contains",
|
|
1186
|
+
child: recurse(schema.contains)
|
|
1187
|
+
}));
|
|
1188
|
+
}
|
|
1189
|
+
items.push(...collectExtras(schema, recurse, "array"));
|
|
1190
|
+
const typeLabel = isTuple ? "tuple" : inlineType != null ? `array of ${inlineType}` : displayType(schema, "array");
|
|
1191
|
+
return this.make({
|
|
1192
|
+
typeLabel,
|
|
1193
|
+
badges: collectBadges(schema),
|
|
1194
|
+
...metaOf(schema),
|
|
1195
|
+
items
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
},
|
|
1199
|
+
view: makeSchemaView("text-primary")
|
|
1200
|
+
});
|
|
1201
|
+
var SchemaEnum = component4({
|
|
1202
|
+
name: "SchemaEnum",
|
|
1203
|
+
fields: schemaNodeFields,
|
|
1204
|
+
methods: schemaNodeMethods({
|
|
1205
|
+
countText() {
|
|
1206
|
+
return this.items.size > 0 ? `(${this.items.size})` : "";
|
|
1207
|
+
}
|
|
1208
|
+
}),
|
|
1209
|
+
alter: compositeAlter,
|
|
1210
|
+
statics: {
|
|
1211
|
+
fromData(schema, recurse) {
|
|
1212
|
+
const members = Array.isArray(schema.enum) ? schema.enum : [];
|
|
1213
|
+
const items = members.map((v) => JsonViewer.Class.fromData(v));
|
|
1214
|
+
items.push(...collectExtras(schema, recurse, "enum"));
|
|
1215
|
+
return this.make({
|
|
1216
|
+
typeLabel: "enum",
|
|
1217
|
+
badges: collectBadges(schema),
|
|
1218
|
+
...metaOf(schema),
|
|
1219
|
+
items
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
},
|
|
1223
|
+
view: makeSchemaView("text-info", LIST_BODY)
|
|
1224
|
+
});
|
|
1225
|
+
var SchemaConst = component4({
|
|
1226
|
+
name: "SchemaConst",
|
|
1227
|
+
fields: { value: null },
|
|
1228
|
+
statics: {
|
|
1229
|
+
fromData(schema) {
|
|
1230
|
+
return this.make({ value: JsonViewer.Class.fromData(schema.const) });
|
|
1231
|
+
}
|
|
1232
|
+
},
|
|
1233
|
+
view: html4`<span
|
|
1234
|
+
class="font-mono text-sm leading-tight inline-flex items-center gap-2"
|
|
1235
|
+
>
|
|
1236
|
+
<span class="text-info font-semibold">const</span>
|
|
1237
|
+
<span class="text-base-content/30">=</span>
|
|
1238
|
+
<x render=".value"></x>
|
|
1239
|
+
</span>`
|
|
1240
|
+
});
|
|
1241
|
+
var SchemaCombinator = component4({
|
|
1242
|
+
name: "SchemaCombinator",
|
|
1243
|
+
fields: schemaNodeFields,
|
|
1244
|
+
methods: schemaNodeMethods({
|
|
1245
|
+
countText() {
|
|
1246
|
+
return this.items.size > 0 ? `(${this.items.size})` : "";
|
|
1247
|
+
}
|
|
1248
|
+
}),
|
|
1249
|
+
alter: compositeAlter,
|
|
1250
|
+
statics: {
|
|
1251
|
+
fromData(schema, recurse) {
|
|
1252
|
+
const items = [];
|
|
1253
|
+
let kind = null;
|
|
1254
|
+
for (const k of ["allOf", "anyOf", "oneOf"]) {
|
|
1255
|
+
if (Array.isArray(schema[k])) {
|
|
1256
|
+
kind = kind ?? k;
|
|
1257
|
+
for (const s of schema[k])
|
|
1258
|
+
items.push(recurse(s));
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
items.push(...collectExtras(schema, recurse, "combinator"));
|
|
1262
|
+
return this.make({
|
|
1263
|
+
typeLabel: combinatorPhrasing(kind),
|
|
1264
|
+
badges: collectBadges(schema),
|
|
1265
|
+
...metaOf(schema),
|
|
1266
|
+
items
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
},
|
|
1270
|
+
view: makeSchemaView("text-accent", LIST_BODY)
|
|
1271
|
+
});
|
|
1272
|
+
var SchemaConditional = component4({
|
|
1273
|
+
name: "SchemaConditional",
|
|
1274
|
+
fields: {
|
|
1275
|
+
ifNode: null,
|
|
1276
|
+
thenNode: null,
|
|
1277
|
+
elseNode: null,
|
|
1278
|
+
title: "",
|
|
1279
|
+
description: "",
|
|
1280
|
+
deprecated: false
|
|
1281
|
+
},
|
|
1282
|
+
statics: {
|
|
1283
|
+
fromData(schema, recurse) {
|
|
1284
|
+
return this.make({
|
|
1285
|
+
ifNode: schema.if !== undefined ? recurse(schema.if) : null,
|
|
1286
|
+
thenNode: schema.then !== undefined ? recurse(schema.then) : null,
|
|
1287
|
+
elseNode: schema.else !== undefined ? recurse(schema.else) : null,
|
|
1288
|
+
...metaOf(schema)
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
},
|
|
1292
|
+
view: html4`<span class="font-mono text-sm leading-tight inline-block">
|
|
1293
|
+
<span class="inline-flex items-center gap-2 flex-wrap">
|
|
1294
|
+
<span class="text-accent font-semibold">conditional</span>
|
|
1295
|
+
<span @show=".deprecated" class="badge badge-xs badge-warning"
|
|
1296
|
+
>deprecated</span
|
|
1297
|
+
>
|
|
1298
|
+
</span>
|
|
1299
|
+
<span
|
|
1300
|
+
@show="truthy? .title"
|
|
1301
|
+
class="block text-base-content/80 italic"
|
|
1302
|
+
@text=".title"
|
|
1303
|
+
></span>
|
|
1304
|
+
<span
|
|
1305
|
+
@show="truthy? .description"
|
|
1306
|
+
class="block text-base-content/50"
|
|
1307
|
+
@text=".description"
|
|
1308
|
+
></span>
|
|
1309
|
+
<div
|
|
1310
|
+
class="ml-1 grid gap-x-2 gap-y-0.5 items-baseline border-l border-base-content/10 pl-2 mt-0.5"
|
|
1311
|
+
style="grid-template-columns: auto 1fr"
|
|
1312
|
+
>
|
|
1313
|
+
<div @show="truthy? .ifNode" class="contents">
|
|
1314
|
+
<span class="font-mono text-sm flex items-center gap-1 leading-tight">
|
|
1315
|
+
<span class="text-info">if</span>
|
|
1316
|
+
<span class="text-base-content/30">:</span>
|
|
1317
|
+
</span>
|
|
1318
|
+
<x render=".ifNode"></x>
|
|
1319
|
+
</div>
|
|
1320
|
+
<div @show="truthy? .thenNode" class="contents">
|
|
1321
|
+
<span class="font-mono text-sm flex items-center gap-1 leading-tight">
|
|
1322
|
+
<span class="text-success">then</span>
|
|
1323
|
+
<span class="text-base-content/30">:</span>
|
|
1324
|
+
</span>
|
|
1325
|
+
<x render=".thenNode"></x>
|
|
1326
|
+
</div>
|
|
1327
|
+
<div @show="truthy? .elseNode" class="contents">
|
|
1328
|
+
<span class="font-mono text-sm flex items-center gap-1 leading-tight">
|
|
1329
|
+
<span class="text-warning">else</span>
|
|
1330
|
+
<span class="text-base-content/30">:</span>
|
|
1331
|
+
</span>
|
|
1332
|
+
<x render=".elseNode"></x>
|
|
1333
|
+
</div>
|
|
1334
|
+
</div>
|
|
1335
|
+
</span>`
|
|
1336
|
+
});
|
|
1337
|
+
var SchemaNot = component4({
|
|
1338
|
+
name: "SchemaNot",
|
|
1339
|
+
fields: { child: null },
|
|
1340
|
+
statics: {
|
|
1341
|
+
fromData(schema, recurse) {
|
|
1342
|
+
return this.make({ child: recurse(schema.not) });
|
|
1343
|
+
}
|
|
1344
|
+
},
|
|
1345
|
+
view: html4`<span
|
|
1346
|
+
class="font-mono text-sm leading-tight inline-flex items-center gap-2"
|
|
1347
|
+
>
|
|
1348
|
+
<span class="text-error font-semibold">not</span>
|
|
1349
|
+
<span class="text-base-content/30">:</span>
|
|
1350
|
+
<x render=".child"></x>
|
|
1351
|
+
</span>`
|
|
1352
|
+
});
|
|
1353
|
+
var SchemaBoolean = component4({
|
|
1354
|
+
name: "SchemaBoolean",
|
|
1355
|
+
fields: { value: true },
|
|
1356
|
+
methods: {
|
|
1357
|
+
text() {
|
|
1358
|
+
return this.value ? "any value (true)" : "no value allowed (false)";
|
|
1359
|
+
},
|
|
1360
|
+
cssClass() {
|
|
1361
|
+
const base = "font-mono text-sm leading-tight italic";
|
|
1362
|
+
return `${base} ${this.value ? "text-success" : "text-error"}`;
|
|
1363
|
+
}
|
|
1364
|
+
},
|
|
1365
|
+
view: html4`<span :class="$cssClass" @text="$text"></span>`,
|
|
1366
|
+
views: {
|
|
1367
|
+
_margauiClasses: html4`<p class="text-success text-error"></p>`
|
|
1368
|
+
}
|
|
1369
|
+
});
|
|
1370
|
+
var SchemaRef = component4({
|
|
1371
|
+
name: "SchemaRef",
|
|
1372
|
+
fields: { target: "" },
|
|
1373
|
+
view: html4`<span class="font-mono text-sm leading-tight text-secondary"
|
|
1374
|
+
>→ <span @text=".target"></span
|
|
1375
|
+
></span>`
|
|
1376
|
+
});
|
|
1377
|
+
var SchemaViewer = component4({
|
|
1378
|
+
name: "SchemaViewer",
|
|
1379
|
+
fields: {
|
|
1380
|
+
value: null,
|
|
1381
|
+
raw: null,
|
|
1382
|
+
rawSchema: null,
|
|
1383
|
+
showRaw: false
|
|
1384
|
+
},
|
|
1385
|
+
methods: {
|
|
1386
|
+
toggleIsExpanded() {
|
|
1387
|
+
return typeof this.value?.toggleIsExpanded === "function" ? this.setValue(this.value.toggleIsExpanded()) : this;
|
|
1388
|
+
},
|
|
1389
|
+
toggleLabel() {
|
|
1390
|
+
return this.showRaw ? "high-level view" : "raw schema";
|
|
1391
|
+
}
|
|
1392
|
+
},
|
|
1393
|
+
statics: {
|
|
1394
|
+
fromData(schema) {
|
|
1395
|
+
return this.make({
|
|
1396
|
+
value: dispatch3(schema),
|
|
1397
|
+
raw: JsonViewer.Class.fromData(schema),
|
|
1398
|
+
rawSchema: schema
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
},
|
|
1402
|
+
view: html4`<div class="inline-flex flex-col items-start gap-1">
|
|
1403
|
+
<button
|
|
1404
|
+
type="button"
|
|
1405
|
+
class="btn btn-xs btn-ghost self-end"
|
|
1406
|
+
@on.click="$toggleShowRaw"
|
|
1407
|
+
@text="$toggleLabel"
|
|
1408
|
+
></button>
|
|
1409
|
+
<x render=".value" hide=".showRaw"></x>
|
|
1410
|
+
<x render=".raw" show=".showRaw"></x>
|
|
1411
|
+
</div>`
|
|
1412
|
+
});
|
|
1413
|
+
function hasObjectKeywords(schema) {
|
|
1414
|
+
const t = schema.type;
|
|
1415
|
+
if (t === "object" || Array.isArray(t) && t.includes("object"))
|
|
1416
|
+
return true;
|
|
1417
|
+
return schema.properties !== undefined || schema.required !== undefined || schema.patternProperties !== undefined || schema.propertyNames !== undefined || schema.additionalProperties !== undefined || schema.minProperties != null || schema.maxProperties != null;
|
|
1418
|
+
}
|
|
1419
|
+
function hasArrayKeywords(schema) {
|
|
1420
|
+
const t = schema.type;
|
|
1421
|
+
if (t === "array" || Array.isArray(t) && t.includes("array"))
|
|
1422
|
+
return true;
|
|
1423
|
+
return schema.items !== undefined || schema.prefixItems !== undefined || schema.contains !== undefined || schema.minItems != null || schema.maxItems != null || schema.uniqueItems !== undefined;
|
|
1424
|
+
}
|
|
1425
|
+
function classifySchema(schema, recurse = classifySchema) {
|
|
1426
|
+
if (schema === true || schema === false)
|
|
1427
|
+
return SchemaBoolean.make({ value: schema });
|
|
1428
|
+
if (schema === null || typeof schema !== "object")
|
|
1429
|
+
return JsonViewer.Class.fromData(schema);
|
|
1430
|
+
if (typeof schema.$ref === "string")
|
|
1431
|
+
return SchemaRef.make({ target: schema.$ref });
|
|
1432
|
+
if (hasObjectKeywords(schema))
|
|
1433
|
+
return SchemaObject.Class.fromData(schema, recurse);
|
|
1434
|
+
if (hasArrayKeywords(schema))
|
|
1435
|
+
return SchemaArray.Class.fromData(schema, recurse);
|
|
1436
|
+
if (Array.isArray(schema.enum))
|
|
1437
|
+
return SchemaEnum.Class.fromData(schema, recurse);
|
|
1438
|
+
if ("const" in schema)
|
|
1439
|
+
return SchemaConst.Class.fromData(schema, recurse);
|
|
1440
|
+
if (schema.allOf || schema.anyOf || schema.oneOf)
|
|
1441
|
+
return SchemaCombinator.Class.fromData(schema, recurse);
|
|
1442
|
+
if (schema.not !== undefined)
|
|
1443
|
+
return SchemaNot.Class.fromData(schema, recurse);
|
|
1444
|
+
if (schema.if !== undefined || schema.then !== undefined || schema.else !== undefined)
|
|
1445
|
+
return SchemaConditional.Class.fromData(schema, recurse);
|
|
1446
|
+
return SchemaScalar.Class.fromData(schema, recurse);
|
|
1447
|
+
}
|
|
1448
|
+
var dispatch3 = chain(classifySchema);
|
|
1449
|
+
function getComponents4() {
|
|
1450
|
+
return [
|
|
1451
|
+
SchemaViewer,
|
|
1452
|
+
SchemaScalar,
|
|
1453
|
+
SchemaObject,
|
|
1454
|
+
SchemaArray,
|
|
1455
|
+
SchemaEnum,
|
|
1456
|
+
SchemaConst,
|
|
1457
|
+
SchemaCombinator,
|
|
1458
|
+
SchemaConditional,
|
|
1459
|
+
SchemaNot,
|
|
1460
|
+
SchemaProperty,
|
|
1461
|
+
SchemaBranch,
|
|
1462
|
+
SchemaBoolean,
|
|
1463
|
+
SchemaRef,
|
|
1464
|
+
...getComponents()
|
|
1465
|
+
];
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
// src/components/tutuca/component-inspector.js
|
|
1469
|
+
import { component as component5, html as html5 } from "tutuca";
|
|
1470
|
+
function introspectComponent(comp) {
|
|
1471
|
+
const { fields, methods } = comp.Class.getMetaClass();
|
|
1472
|
+
return {
|
|
1473
|
+
name: comp.name,
|
|
1474
|
+
id: comp.id,
|
|
1475
|
+
fields: Object.entries(fields).map(([name, f]) => ({
|
|
1476
|
+
name,
|
|
1477
|
+
type: f.type,
|
|
1478
|
+
defaultValue: f.defaultValue
|
|
1479
|
+
})),
|
|
1480
|
+
methods: Object.keys(methods),
|
|
1481
|
+
input: Object.keys(comp.input ?? {}),
|
|
1482
|
+
receive: Object.keys(comp.receive ?? {}),
|
|
1483
|
+
bubble: Object.keys(comp.bubble ?? {}),
|
|
1484
|
+
response: Object.keys(comp.response ?? {}),
|
|
1485
|
+
alter: Object.keys(comp.alter ?? {}),
|
|
1486
|
+
statics: Object.keys(comp.spec?.statics ?? {}),
|
|
1487
|
+
views: Object.values(comp.views ?? {}).map((v) => ({
|
|
1488
|
+
name: v.name,
|
|
1489
|
+
rawView: v.rawView
|
|
1490
|
+
}))
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
var sectionView = makeCompositeView({
|
|
1494
|
+
typeClass: "font-semibold",
|
|
1495
|
+
borderClass: "border-base-content/15",
|
|
1496
|
+
toggleHandler: "toggle isCtrl"
|
|
1497
|
+
});
|
|
1498
|
+
var CompName = component5({
|
|
1499
|
+
name: "CompName",
|
|
1500
|
+
fields: { name: "" },
|
|
1501
|
+
view: html5`<span
|
|
1502
|
+
class="font-mono text-sm leading-tight badge badge-sm badge-ghost"
|
|
1503
|
+
@text=".name"
|
|
1504
|
+
></span>`
|
|
1505
|
+
});
|
|
1506
|
+
var CompField = component5({
|
|
1507
|
+
name: "CompField",
|
|
1508
|
+
fields: { name: "", typeName: "", child: null },
|
|
1509
|
+
view: html5`<div class="flex items-center gap-2 leading-tight">
|
|
1510
|
+
<span class="text-base-content/70 font-mono text-sm" @text=".name"></span>
|
|
1511
|
+
<span class="text-base-content/30">:</span>
|
|
1512
|
+
<span class="text-base-content/50 font-mono text-xs" @text=".typeName"></span>
|
|
1513
|
+
<span class="text-base-content/30">=</span>
|
|
1514
|
+
<x render=".child"></x>
|
|
1515
|
+
</div>`
|
|
1516
|
+
});
|
|
1517
|
+
var CompView = component5({
|
|
1518
|
+
name: "CompView",
|
|
1519
|
+
fields: { name: "", rawView: "", isExpanded: false },
|
|
1520
|
+
methods: {
|
|
1521
|
+
arrowText() {
|
|
1522
|
+
return this.isExpanded ? "▼" : "▶";
|
|
1523
|
+
}
|
|
1524
|
+
},
|
|
1525
|
+
input: {
|
|
1526
|
+
toggle(isCtrl, ctx) {
|
|
1527
|
+
if (isCtrl) {
|
|
1528
|
+
ctx.bubble("toggleAllViews", [!this.isExpanded]);
|
|
1529
|
+
return this;
|
|
1530
|
+
}
|
|
1531
|
+
return this.toggleIsExpanded();
|
|
1532
|
+
}
|
|
1533
|
+
},
|
|
1534
|
+
view: html5`<div class="flex flex-col gap-0.5 leading-tight">
|
|
1535
|
+
<button
|
|
1536
|
+
type="button"
|
|
1537
|
+
class="cursor-pointer text-base-content/70 hover:text-base-content inline-flex items-center gap-1 self-start"
|
|
1538
|
+
@on.click="toggle isCtrl"
|
|
1539
|
+
>
|
|
1540
|
+
<span @text="$arrowText"></span>
|
|
1541
|
+
<span class="font-mono text-sm" @text=".name"></span>
|
|
1542
|
+
</button>
|
|
1543
|
+
<pre
|
|
1544
|
+
@show=".isExpanded"
|
|
1545
|
+
class="text-xs bg-base-200 rounded p-2 overflow-x-auto whitespace-pre-wrap"
|
|
1546
|
+
><code @text=".rawView"></code></pre>
|
|
1547
|
+
</div>`
|
|
1548
|
+
});
|
|
1549
|
+
var CompSection = component5({
|
|
1550
|
+
name: "CompSection",
|
|
1551
|
+
fields: { ...compositeFields, label: "" },
|
|
1552
|
+
methods: {
|
|
1553
|
+
...compositeMethods,
|
|
1554
|
+
typeText() {
|
|
1555
|
+
return this.label;
|
|
1556
|
+
},
|
|
1557
|
+
countText() {
|
|
1558
|
+
return `(${this.items.size})`;
|
|
1559
|
+
}
|
|
1560
|
+
},
|
|
1561
|
+
input: {
|
|
1562
|
+
toggle(isCtrl, ctx) {
|
|
1563
|
+
if (isCtrl) {
|
|
1564
|
+
ctx.bubble("toggleAllSections", [!this.isExpanded]);
|
|
1565
|
+
return this;
|
|
1566
|
+
}
|
|
1567
|
+
return this.toggleIsExpanded();
|
|
1568
|
+
}
|
|
1569
|
+
},
|
|
1570
|
+
alter: compositeAlter,
|
|
1571
|
+
view: sectionView
|
|
1572
|
+
});
|
|
1573
|
+
var ComponentInspector = component5({
|
|
1574
|
+
name: "ComponentInspector",
|
|
1575
|
+
fields: { compName: "", compId: 0, sections: [] },
|
|
1576
|
+
methods: {
|
|
1577
|
+
idText() {
|
|
1578
|
+
return `#${this.compId}`;
|
|
1579
|
+
},
|
|
1580
|
+
setAllSections(state) {
|
|
1581
|
+
return this.setSections(this.sections.map((s) => s.setIsExpanded(state)));
|
|
1582
|
+
},
|
|
1583
|
+
expandAll() {
|
|
1584
|
+
return this.setAllSections(true);
|
|
1585
|
+
},
|
|
1586
|
+
collapseAll() {
|
|
1587
|
+
return this.setAllSections(false);
|
|
1588
|
+
},
|
|
1589
|
+
setAllViews(state) {
|
|
1590
|
+
return this.setSections(this.sections.map((s) => s.label === "Views" ? s.setItems(s.items.map((v) => v.setIsExpanded(state))) : s));
|
|
1591
|
+
},
|
|
1592
|
+
expandAllViews() {
|
|
1593
|
+
return this.setAllViews(true);
|
|
1594
|
+
},
|
|
1595
|
+
collapseAllViews() {
|
|
1596
|
+
return this.setAllViews(false);
|
|
1597
|
+
}
|
|
1598
|
+
},
|
|
1599
|
+
bubble: {
|
|
1600
|
+
toggleAllSections(state) {
|
|
1601
|
+
return this.setAllSections(state);
|
|
1602
|
+
},
|
|
1603
|
+
toggleAllViews(state) {
|
|
1604
|
+
return this.setAllViews(state);
|
|
1605
|
+
}
|
|
1606
|
+
},
|
|
1607
|
+
statics: {
|
|
1608
|
+
fromData(comp) {
|
|
1609
|
+
const d = introspectComponent(comp);
|
|
1610
|
+
const sections = [];
|
|
1611
|
+
const add = (label, items, isExpanded = false) => {
|
|
1612
|
+
if (items.length > 0) {
|
|
1613
|
+
sections.push(CompSection.make({ label, items, isExpanded }));
|
|
1614
|
+
}
|
|
1615
|
+
};
|
|
1616
|
+
const names = (list) => list.map((name) => CompName.make({ name }));
|
|
1617
|
+
add("Fields", d.fields.map((f) => CompField.make({
|
|
1618
|
+
name: f.name,
|
|
1619
|
+
typeName: f.type,
|
|
1620
|
+
child: ImInspector.Class.fromData(f.defaultValue)
|
|
1621
|
+
})), true);
|
|
1622
|
+
add("Methods", names(d.methods));
|
|
1623
|
+
add("Input", names(d.input));
|
|
1624
|
+
add("Receive", names(d.receive));
|
|
1625
|
+
add("Bubble", names(d.bubble));
|
|
1626
|
+
add("Response", names(d.response));
|
|
1627
|
+
add("Alter", names(d.alter));
|
|
1628
|
+
add("Statics", names(d.statics));
|
|
1629
|
+
add("Views", d.views.map((v) => CompView.make({ name: v.name, rawView: v.rawView })));
|
|
1630
|
+
return this.make({ compName: d.name, compId: d.id, sections });
|
|
1631
|
+
}
|
|
1632
|
+
},
|
|
1633
|
+
view: html5`<div class="font-mono text-sm leading-tight flex flex-col gap-1">
|
|
1634
|
+
<div class="inline-flex items-center gap-2">
|
|
1635
|
+
<span class="font-semibold" @text=".compName"></span>
|
|
1636
|
+
<span class="text-base-content/40 text-xs" @text="$idText"></span>
|
|
1637
|
+
</div>
|
|
1638
|
+
<x render-each=".sections"></x>
|
|
1639
|
+
</div>`
|
|
1640
|
+
});
|
|
1641
|
+
function getComponents5() {
|
|
1642
|
+
return [ComponentInspector, CompSection, CompField, CompName, CompView, ...getComponents2()];
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
// src/components/tutuca/instance-inspector.js
|
|
1646
|
+
import { component as component8, html as html8, isRecord } from "tutuca";
|
|
1647
|
+
|
|
1648
|
+
// src/components/tutuca/lint-inspector.js
|
|
1649
|
+
import { component as component6, html as html6 } from "tutuca";
|
|
1650
|
+
var tagDisplay = (tag) => tag ? String(tag).toLowerCase() : "";
|
|
1651
|
+
function originSuffix(info) {
|
|
1652
|
+
if (!info)
|
|
1653
|
+
return "";
|
|
1654
|
+
const parts = [];
|
|
1655
|
+
if (info.originAttr) {
|
|
1656
|
+
const branch = info.branch ? `[${info.branch}]` : "";
|
|
1657
|
+
parts.push(`in ${info.originAttr}${branch}`);
|
|
1658
|
+
}
|
|
1659
|
+
if (info.handlerName) {
|
|
1660
|
+
parts.push(`handler '${info.handlerName}'${info.argIndex !== undefined ? ` arg ${info.argIndex}` : ""}`);
|
|
1661
|
+
}
|
|
1662
|
+
const t = tagDisplay(info.tag);
|
|
1663
|
+
if (t && t !== "x")
|
|
1664
|
+
parts.push(`on <${t}>`);
|
|
1665
|
+
return parts.length ? ` (${parts.join(", ")})` : "";
|
|
1666
|
+
}
|
|
1667
|
+
function tagSuffix(info) {
|
|
1668
|
+
const t = tagDisplay(info?.tag);
|
|
1669
|
+
return t && t !== "x" ? ` on <${t}>` : "";
|
|
1670
|
+
}
|
|
1671
|
+
function eventSuffix(info) {
|
|
1672
|
+
if (info?.originAttr)
|
|
1673
|
+
return ` in ${info.originAttr}`;
|
|
1674
|
+
if (info?.eventName)
|
|
1675
|
+
return ` in @on.${info.eventName}`;
|
|
1676
|
+
return "";
|
|
1677
|
+
}
|
|
1678
|
+
function humanizeId(id) {
|
|
1679
|
+
if (!id)
|
|
1680
|
+
return "Lint finding";
|
|
1681
|
+
const s = id.toLowerCase().replace(/_/g, " ");
|
|
1682
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
1683
|
+
}
|
|
1684
|
+
function lintMessage(id, info = {}) {
|
|
1685
|
+
switch (id) {
|
|
1686
|
+
case "FIELD_VAL_NOT_DEFINED":
|
|
1687
|
+
return `Field '.${info.name}' is not defined${originSuffix(info)}`;
|
|
1688
|
+
case "FIELD_VAL_IS_METHOD":
|
|
1689
|
+
return `'.${info.name}' reads a field, but '${info.name}' is a method — use '$${info.name}'${originSuffix(info)}`;
|
|
1690
|
+
case "METHOD_VAL_NOT_DEFINED":
|
|
1691
|
+
return `Method '$${info.name}' is not defined${originSuffix(info)}`;
|
|
1692
|
+
case "METHOD_VAL_IS_FIELD":
|
|
1693
|
+
return `'$${info.name}' calls a method, but '${info.name}' is a field — use '.${info.name}'${originSuffix(info)}`;
|
|
1694
|
+
case "INPUT_HANDLER_NOT_IMPLEMENTED":
|
|
1695
|
+
return `Input handler '${info.name}' is not implemented${eventSuffix(info)}`;
|
|
1696
|
+
case "INPUT_HANDLER_METHOD_NOT_IMPLEMENTED":
|
|
1697
|
+
return `Method '$${info.name}' is not implemented${eventSuffix(info)}`;
|
|
1698
|
+
case "INPUT_HANDLER_NOT_REFERENCED":
|
|
1699
|
+
return `Input handler '${info.name}' is defined but never used`;
|
|
1700
|
+
case "ALT_HANDLER_NOT_DEFINED":
|
|
1701
|
+
return `Alter handler '${info.name}' is not defined${originSuffix(info)}`;
|
|
1702
|
+
case "DYN_VAL_NOT_DEFINED":
|
|
1703
|
+
return `Dynamic variable '*${info.name}' is not defined${originSuffix(info)}`;
|
|
1704
|
+
case "UNKNOWN_COMPONENT_NAME":
|
|
1705
|
+
return `Unknown component '${info.name}'${originSuffix(info)}`;
|
|
1706
|
+
case "UNKNOWN_DIRECTIVE":
|
|
1707
|
+
return `Unknown directive '@${info.name}'${tagSuffix(info)}`;
|
|
1708
|
+
case "IF_NO_BRANCH_SET":
|
|
1709
|
+
return `'@if.${info.attr}' has no '@then' or '@else' branch${tagSuffix(info)}`;
|
|
1710
|
+
case "REDUNDANT_TEMPLATE_STRING":
|
|
1711
|
+
return `Redundant template string — '{${info.simpler}}' should be just '${info.simpler}'${originSuffix(info)}`;
|
|
1712
|
+
case "PLACEHOLDERLESS_TEMPLATE_STRING":
|
|
1713
|
+
return `Template string has no dynamic parts — use the literal ${info.literal} instead${originSuffix(info)}`;
|
|
1714
|
+
case "ASYNC_HANDLER":
|
|
1715
|
+
return `Handler '${info.name}' in '${info.channel}' is async — handlers must be synchronous`;
|
|
1716
|
+
case "UNKNOWN_COMPONENT_SPEC_KEY":
|
|
1717
|
+
return `Unknown component spec key '${info.key}' — ignored at runtime`;
|
|
1718
|
+
default:
|
|
1719
|
+
return `${humanizeId(id)}${originSuffix(info)}`;
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
function suggestionText(s) {
|
|
1723
|
+
if (!s)
|
|
1724
|
+
return "";
|
|
1725
|
+
if (s.to != null)
|
|
1726
|
+
return `${s.from ?? ""} → ${s.to}`;
|
|
1727
|
+
return s.text ?? s.kind ?? "";
|
|
1728
|
+
}
|
|
1729
|
+
var componentView = makeCompositeView({
|
|
1730
|
+
typeClass: "font-semibold",
|
|
1731
|
+
borderClass: "border-base-content/15"
|
|
1732
|
+
});
|
|
1733
|
+
var LintFinding = component6({
|
|
1734
|
+
name: "LintFinding",
|
|
1735
|
+
fields: { id: "", level: "", message: "", suggestion: "", detail: null },
|
|
1736
|
+
methods: {
|
|
1737
|
+
levelBadgeClass() {
|
|
1738
|
+
const base = "badge badge-sm badge-soft";
|
|
1739
|
+
switch (this.level) {
|
|
1740
|
+
case "error":
|
|
1741
|
+
return `${base} badge-error`;
|
|
1742
|
+
case "warn":
|
|
1743
|
+
return `${base} badge-warning`;
|
|
1744
|
+
default:
|
|
1745
|
+
return `${base} badge-neutral`;
|
|
1746
|
+
}
|
|
1747
|
+
},
|
|
1748
|
+
hasSuggestion() {
|
|
1749
|
+
return this.suggestion !== "";
|
|
1750
|
+
},
|
|
1751
|
+
hasDetail() {
|
|
1752
|
+
return this.detail != null;
|
|
1753
|
+
}
|
|
1754
|
+
},
|
|
1755
|
+
views: {
|
|
1756
|
+
_palette: html6`<span
|
|
1757
|
+
class="badge-soft badge-error badge-warning badge-neutral"
|
|
1758
|
+
></span>`
|
|
1759
|
+
},
|
|
1760
|
+
view: html6`<div class="flex flex-col gap-1.5 py-1.5 leading-snug">
|
|
1761
|
+
<div class="flex items-baseline gap-2 flex-wrap">
|
|
1762
|
+
<span :class="$levelBadgeClass" @text=".level"></span>
|
|
1763
|
+
<span class="text-sm text-base-content/90" @text=".message"></span>
|
|
1764
|
+
</div>
|
|
1765
|
+
<div
|
|
1766
|
+
@show="$hasSuggestion"
|
|
1767
|
+
class="ml-3 flex items-baseline gap-1.5 text-xs text-base-content/60"
|
|
1768
|
+
>
|
|
1769
|
+
<span>fix</span>
|
|
1770
|
+
<span class="font-mono" @text=".suggestion"></span>
|
|
1771
|
+
</div>
|
|
1772
|
+
<div @show="$hasDetail" class="ml-3"><x render=".detail"></x></div>
|
|
1773
|
+
</div>`
|
|
1774
|
+
});
|
|
1775
|
+
function buildFinding(f) {
|
|
1776
|
+
const info = f.info ?? {};
|
|
1777
|
+
const ctx = f.context ?? {};
|
|
1778
|
+
return LintFinding.make({
|
|
1779
|
+
id: f.id ?? "",
|
|
1780
|
+
level: f.level ?? "",
|
|
1781
|
+
message: lintMessage(f.id, info),
|
|
1782
|
+
suggestion: suggestionText(f.suggestion),
|
|
1783
|
+
detail: ImInspector.Class.fromData({ id: f.id, info, context: ctx })
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
var LintComponent = component6({
|
|
1787
|
+
name: "LintComponent",
|
|
1788
|
+
fields: { ...compositeFields, componentName: "", errors: 0, warns: 0, hints: 0 },
|
|
1789
|
+
methods: {
|
|
1790
|
+
...compositeMethods,
|
|
1791
|
+
typeText() {
|
|
1792
|
+
return this.componentName;
|
|
1793
|
+
},
|
|
1794
|
+
countText() {
|
|
1795
|
+
const parts = [];
|
|
1796
|
+
if (this.errors)
|
|
1797
|
+
parts.push(`${this.errors} error${this.errors === 1 ? "" : "s"}`);
|
|
1798
|
+
if (this.warns)
|
|
1799
|
+
parts.push(`${this.warns} warning${this.warns === 1 ? "" : "s"}`);
|
|
1800
|
+
if (this.hints)
|
|
1801
|
+
parts.push(`${this.hints} hint${this.hints === 1 ? "" : "s"}`);
|
|
1802
|
+
return parts.length ? parts.join(", ") : "ok";
|
|
1803
|
+
}
|
|
1804
|
+
},
|
|
1805
|
+
alter: compositeAlter,
|
|
1806
|
+
view: componentView
|
|
1807
|
+
});
|
|
1808
|
+
var LintReport = component6({
|
|
1809
|
+
name: "LintReport",
|
|
1810
|
+
fields: {
|
|
1811
|
+
title: "",
|
|
1812
|
+
path: "",
|
|
1813
|
+
errors: 0,
|
|
1814
|
+
warnings: 0,
|
|
1815
|
+
hints: 0,
|
|
1816
|
+
clean: true,
|
|
1817
|
+
components: []
|
|
1818
|
+
},
|
|
1819
|
+
methods: {
|
|
1820
|
+
errText() {
|
|
1821
|
+
return `${this.errors} error${this.errors === 1 ? "" : "s"}`;
|
|
1822
|
+
},
|
|
1823
|
+
warnText() {
|
|
1824
|
+
return `${this.warnings} warning${this.warnings === 1 ? "" : "s"}`;
|
|
1825
|
+
},
|
|
1826
|
+
hintText() {
|
|
1827
|
+
return `${this.hints} hint${this.hints === 1 ? "" : "s"}`;
|
|
1828
|
+
},
|
|
1829
|
+
hasErrors() {
|
|
1830
|
+
return this.errors > 0;
|
|
1831
|
+
},
|
|
1832
|
+
hasWarnings() {
|
|
1833
|
+
return this.warnings > 0;
|
|
1834
|
+
},
|
|
1835
|
+
hasHints() {
|
|
1836
|
+
return this.hints > 0;
|
|
1837
|
+
}
|
|
1838
|
+
},
|
|
1839
|
+
statics: {
|
|
1840
|
+
fromData(report, { title = "Lint", path = "" } = {}) {
|
|
1841
|
+
const comps = report?.components ?? [];
|
|
1842
|
+
let errors = 0;
|
|
1843
|
+
let warnings = 0;
|
|
1844
|
+
let hints = 0;
|
|
1845
|
+
const built = [];
|
|
1846
|
+
for (const c of comps) {
|
|
1847
|
+
const findings = c.findings ?? [];
|
|
1848
|
+
if (findings.length === 0)
|
|
1849
|
+
continue;
|
|
1850
|
+
const e = findings.filter((f) => f.level === "error").length;
|
|
1851
|
+
const w = findings.filter((f) => f.level === "warn").length;
|
|
1852
|
+
const h = findings.filter((f) => f.level === "hint").length;
|
|
1853
|
+
errors += e;
|
|
1854
|
+
warnings += w;
|
|
1855
|
+
hints += h;
|
|
1856
|
+
built.push(LintComponent.make({
|
|
1857
|
+
componentName: c.componentName,
|
|
1858
|
+
errors: e,
|
|
1859
|
+
warns: w,
|
|
1860
|
+
hints: h,
|
|
1861
|
+
items: findings.map(buildFinding),
|
|
1862
|
+
isExpanded: e > 0
|
|
1863
|
+
}));
|
|
1864
|
+
}
|
|
1865
|
+
return this.make({
|
|
1866
|
+
title,
|
|
1867
|
+
path,
|
|
1868
|
+
errors,
|
|
1869
|
+
warnings,
|
|
1870
|
+
hints,
|
|
1871
|
+
clean: built.length === 0,
|
|
1872
|
+
components: built
|
|
1873
|
+
});
|
|
1874
|
+
}
|
|
1875
|
+
},
|
|
1876
|
+
views: {
|
|
1877
|
+
_palette: html6`<span
|
|
1878
|
+
class="badge-soft badge-error badge-warning badge-neutral badge-success"
|
|
1879
|
+
></span>`
|
|
1880
|
+
},
|
|
1881
|
+
view: html6`<div class="font-mono text-sm leading-snug flex flex-col gap-3">
|
|
1882
|
+
<div class="flex items-center gap-2 flex-wrap">
|
|
1883
|
+
<span class="font-semibold" @text=".title"></span>
|
|
1884
|
+
<span class="text-base-content/40 text-xs" @text=".path"></span>
|
|
1885
|
+
<span
|
|
1886
|
+
@show="$hasErrors"
|
|
1887
|
+
class="badge badge-sm badge-soft badge-error"
|
|
1888
|
+
@text="$errText"
|
|
1889
|
+
></span>
|
|
1890
|
+
<span
|
|
1891
|
+
@show="$hasWarnings"
|
|
1892
|
+
class="badge badge-sm badge-soft badge-warning"
|
|
1893
|
+
@text="$warnText"
|
|
1894
|
+
></span>
|
|
1895
|
+
<span
|
|
1896
|
+
@show="$hasHints"
|
|
1897
|
+
class="badge badge-sm badge-soft badge-neutral"
|
|
1898
|
+
@text="$hintText"
|
|
1899
|
+
></span>
|
|
1900
|
+
<span
|
|
1901
|
+
@show=".clean"
|
|
1902
|
+
class="badge badge-sm badge-soft badge-success"
|
|
1903
|
+
>✓ clean</span
|
|
1904
|
+
>
|
|
1905
|
+
</div>
|
|
1906
|
+
<x render-each=".components"></x>
|
|
1907
|
+
</div>`
|
|
1908
|
+
});
|
|
1909
|
+
function getComponents6() {
|
|
1910
|
+
return [LintReport, LintComponent, LintFinding, ...getComponents2()];
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
// src/components/tutuca/test-inspector.js
|
|
1914
|
+
import { component as component7, html as html7 } from "tutuca";
|
|
1915
|
+
function collectTests(getTests) {
|
|
1916
|
+
const root = { children: [] };
|
|
1917
|
+
const stack = [root];
|
|
1918
|
+
const describe = (head, optsOrFn, maybeFn) => {
|
|
1919
|
+
const fn = typeof maybeFn === "function" ? maybeFn : optsOrFn;
|
|
1920
|
+
const title = typeof head === "string" ? head : head?.name ?? String(head);
|
|
1921
|
+
const node = { title, children: [] };
|
|
1922
|
+
stack[stack.length - 1].children.push(node);
|
|
1923
|
+
stack.push(node);
|
|
1924
|
+
try {
|
|
1925
|
+
fn();
|
|
1926
|
+
} finally {
|
|
1927
|
+
stack.pop();
|
|
1928
|
+
}
|
|
1929
|
+
};
|
|
1930
|
+
const test = (title) => {
|
|
1931
|
+
stack[stack.length - 1].children.push({ title });
|
|
1932
|
+
};
|
|
1933
|
+
const noop = () => {};
|
|
1934
|
+
getTests({ describe, test, expect: noop, drive: async () => null });
|
|
1935
|
+
return root.children;
|
|
1936
|
+
}
|
|
1937
|
+
var suiteView = makeCompositeView({
|
|
1938
|
+
typeClass: "font-semibold",
|
|
1939
|
+
borderClass: "border-base-content/15",
|
|
1940
|
+
toggleHandler: "toggle isCtrl"
|
|
1941
|
+
});
|
|
1942
|
+
var TestCase = component7({
|
|
1943
|
+
name: "TestCase",
|
|
1944
|
+
fields: { title: "", status: "", durationMs: 0, message: "", detail: null },
|
|
1945
|
+
methods: {
|
|
1946
|
+
mark() {
|
|
1947
|
+
switch (this.status) {
|
|
1948
|
+
case "pass":
|
|
1949
|
+
return "✓";
|
|
1950
|
+
case "fail":
|
|
1951
|
+
return "✗";
|
|
1952
|
+
case "skip":
|
|
1953
|
+
return "○";
|
|
1954
|
+
default:
|
|
1955
|
+
return "•";
|
|
1956
|
+
}
|
|
1957
|
+
},
|
|
1958
|
+
markClass() {
|
|
1959
|
+
const base = "font-mono text-sm";
|
|
1960
|
+
switch (this.status) {
|
|
1961
|
+
case "pass":
|
|
1962
|
+
return `${base} text-success`;
|
|
1963
|
+
case "fail":
|
|
1964
|
+
return `${base} text-error`;
|
|
1965
|
+
default:
|
|
1966
|
+
return `${base} text-base-content/40`;
|
|
1967
|
+
}
|
|
1968
|
+
},
|
|
1969
|
+
durText() {
|
|
1970
|
+
return this.status && this.status !== "skip" ? `(${Math.round(this.durationMs)}ms)` : "";
|
|
1971
|
+
},
|
|
1972
|
+
hasMessage() {
|
|
1973
|
+
return this.message !== "";
|
|
1974
|
+
},
|
|
1975
|
+
hasDetail() {
|
|
1976
|
+
return this.detail != null;
|
|
1977
|
+
}
|
|
1978
|
+
},
|
|
1979
|
+
views: {
|
|
1980
|
+
_palette: html7`<span class="text-success text-error text-base-content/40"></span>`
|
|
1981
|
+
},
|
|
1982
|
+
view: html7`<div class="flex flex-col gap-0.5 leading-tight">
|
|
1983
|
+
<div class="flex items-center gap-2">
|
|
1984
|
+
<span :class="$markClass" @text="$mark"></span>
|
|
1985
|
+
<span class="font-mono text-sm" @text=".title"></span>
|
|
1986
|
+
<span class="text-base-content/40 text-xs" @text="$durText"></span>
|
|
1987
|
+
</div>
|
|
1988
|
+
<div @show="$hasMessage" class="ml-5 text-error text-xs" @text=".message"></div>
|
|
1989
|
+
<div @show="$hasDetail" class="ml-5"><x render=".detail"></x></div>
|
|
1990
|
+
</div>`
|
|
1991
|
+
});
|
|
1992
|
+
var TestSuite = component7({
|
|
1993
|
+
name: "TestSuite",
|
|
1994
|
+
fields: { ...compositeFields, title: "", summary: "" },
|
|
1995
|
+
methods: {
|
|
1996
|
+
...compositeMethods,
|
|
1997
|
+
typeText() {
|
|
1998
|
+
return this.title;
|
|
1999
|
+
},
|
|
2000
|
+
countText() {
|
|
2001
|
+
return this.summary || `(${this.items.size})`;
|
|
2002
|
+
}
|
|
2003
|
+
},
|
|
2004
|
+
input: {
|
|
2005
|
+
toggle(isCtrl, ctx) {
|
|
2006
|
+
if (isCtrl) {
|
|
2007
|
+
ctx.bubble("toggleAll", [!this.isExpanded]);
|
|
2008
|
+
return this;
|
|
2009
|
+
}
|
|
2010
|
+
return this.toggleIsExpanded();
|
|
2011
|
+
}
|
|
2012
|
+
},
|
|
2013
|
+
alter: compositeAlter,
|
|
2014
|
+
view: suiteView
|
|
2015
|
+
});
|
|
2016
|
+
function setSuiteTreeExpanded(node, state) {
|
|
2017
|
+
if (typeof node.setIsExpanded !== "function")
|
|
2018
|
+
return node;
|
|
2019
|
+
return node.setIsExpanded(state).setItems(node.items.map((c) => setSuiteTreeExpanded(c, state)));
|
|
2020
|
+
}
|
|
2021
|
+
function buildDefNode(node) {
|
|
2022
|
+
if (Array.isArray(node.children)) {
|
|
2023
|
+
return TestSuite.make({
|
|
2024
|
+
title: node.title,
|
|
2025
|
+
items: node.children.map(buildDefNode)
|
|
2026
|
+
});
|
|
2027
|
+
}
|
|
2028
|
+
return TestCase.make({ title: node.title });
|
|
2029
|
+
}
|
|
2030
|
+
function buildResultNode(node) {
|
|
2031
|
+
if (Array.isArray(node.children)) {
|
|
2032
|
+
const built = node.children.map(buildResultNode);
|
|
2033
|
+
const pass = built.reduce((n, b) => n + b.pass, 0);
|
|
2034
|
+
const fail = built.reduce((n, b) => n + b.fail, 0);
|
|
2035
|
+
const skip = built.reduce((n, b) => n + b.skip, 0);
|
|
2036
|
+
const summary = `✓${pass}${fail ? ` ✗${fail}` : ""}${skip ? ` ○${skip}` : ""}`;
|
|
2037
|
+
const comp2 = TestSuite.make({
|
|
2038
|
+
title: node.title,
|
|
2039
|
+
summary,
|
|
2040
|
+
items: built.map((b) => b.comp),
|
|
2041
|
+
isExpanded: fail > 0
|
|
2042
|
+
});
|
|
2043
|
+
return { comp: comp2, pass, fail, skip };
|
|
2044
|
+
}
|
|
2045
|
+
const status = node.status ?? "";
|
|
2046
|
+
const err = node.error ?? null;
|
|
2047
|
+
const hasDiff = err != null && (("expected" in err) || ("actual" in err));
|
|
2048
|
+
const comp = TestCase.make({
|
|
2049
|
+
title: node.title,
|
|
2050
|
+
status,
|
|
2051
|
+
durationMs: node.durationMs ?? 0,
|
|
2052
|
+
message: err?.message ?? "",
|
|
2053
|
+
detail: hasDiff ? ImInspector.Class.fromData({ expected: err.expected, actual: err.actual }) : null
|
|
2054
|
+
});
|
|
2055
|
+
return {
|
|
2056
|
+
comp,
|
|
2057
|
+
pass: status === "pass" ? 1 : 0,
|
|
2058
|
+
fail: status === "fail" ? 1 : 0,
|
|
2059
|
+
skip: status === "skip" ? 1 : 0
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
2062
|
+
var TestReport = component7({
|
|
2063
|
+
name: "TestReport",
|
|
2064
|
+
fields: {
|
|
2065
|
+
title: "",
|
|
2066
|
+
path: "",
|
|
2067
|
+
pass: 0,
|
|
2068
|
+
fail: 0,
|
|
2069
|
+
skip: 0,
|
|
2070
|
+
hasCounts: false,
|
|
2071
|
+
suites: []
|
|
2072
|
+
},
|
|
2073
|
+
methods: {
|
|
2074
|
+
passText() {
|
|
2075
|
+
return `✓ ${this.pass}`;
|
|
2076
|
+
},
|
|
2077
|
+
failText() {
|
|
2078
|
+
return `✗ ${this.fail}`;
|
|
2079
|
+
},
|
|
2080
|
+
skipText() {
|
|
2081
|
+
return `○ ${this.skip}`;
|
|
2082
|
+
},
|
|
2083
|
+
hasFailures() {
|
|
2084
|
+
return this.hasCounts && this.fail > 0;
|
|
2085
|
+
},
|
|
2086
|
+
hasSkips() {
|
|
2087
|
+
return this.hasCounts && this.skip > 0;
|
|
2088
|
+
},
|
|
2089
|
+
setAllSuites(state) {
|
|
2090
|
+
return this.setSuites(this.suites.map((s) => setSuiteTreeExpanded(s, state)));
|
|
2091
|
+
}
|
|
2092
|
+
},
|
|
2093
|
+
bubble: {
|
|
2094
|
+
toggleAll(state) {
|
|
2095
|
+
return this.setAllSuites(state);
|
|
2096
|
+
}
|
|
2097
|
+
},
|
|
2098
|
+
statics: {
|
|
2099
|
+
fromTests(source, { title = "Tests", path = "" } = {}) {
|
|
2100
|
+
const suites = Array.isArray(source) ? source : source?.suites ?? [];
|
|
2101
|
+
return this.make({
|
|
2102
|
+
title,
|
|
2103
|
+
path,
|
|
2104
|
+
hasCounts: false,
|
|
2105
|
+
suites: suites.map(buildDefNode)
|
|
2106
|
+
});
|
|
2107
|
+
},
|
|
2108
|
+
fromResults(report) {
|
|
2109
|
+
const mod = report?.modules ? report.modules[0] : report;
|
|
2110
|
+
const { path = "", suites = [], counts = {} } = mod ?? {};
|
|
2111
|
+
return this.make({
|
|
2112
|
+
title: "Test run",
|
|
2113
|
+
path,
|
|
2114
|
+
pass: counts.pass ?? 0,
|
|
2115
|
+
fail: counts.fail ?? 0,
|
|
2116
|
+
skip: counts.skip ?? 0,
|
|
2117
|
+
hasCounts: true,
|
|
2118
|
+
suites: suites.map((s) => buildResultNode(s).comp)
|
|
2119
|
+
});
|
|
2120
|
+
}
|
|
2121
|
+
},
|
|
2122
|
+
view: html7`<div class="font-mono text-sm leading-tight flex flex-col gap-1">
|
|
2123
|
+
<div class="flex items-center gap-2 flex-wrap">
|
|
2124
|
+
<span class="font-semibold" @text=".title"></span>
|
|
2125
|
+
<span class="text-base-content/40 text-xs" @text=".path"></span>
|
|
2126
|
+
<span
|
|
2127
|
+
@show=".hasCounts"
|
|
2128
|
+
class="badge badge-sm badge-soft badge-success"
|
|
2129
|
+
@text="$passText"
|
|
2130
|
+
></span>
|
|
2131
|
+
<span
|
|
2132
|
+
@show="$hasFailures"
|
|
2133
|
+
class="badge badge-sm badge-soft badge-error"
|
|
2134
|
+
@text="$failText"
|
|
2135
|
+
></span>
|
|
2136
|
+
<span
|
|
2137
|
+
@show="$hasSkips"
|
|
2138
|
+
class="badge badge-sm badge-soft badge-neutral"
|
|
2139
|
+
@text="$skipText"
|
|
2140
|
+
></span>
|
|
2141
|
+
</div>
|
|
2142
|
+
<x render-each=".suites"></x>
|
|
2143
|
+
</div>`
|
|
2144
|
+
});
|
|
2145
|
+
function getComponents7() {
|
|
2146
|
+
return [TestReport, TestSuite, TestCase, ...getComponents2()];
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
// src/components/tutuca/instance-inspector.js
|
|
2150
|
+
var isComponentInstance = (v) => isRecord(v) && typeof v?.constructor?.getMetaClass === "function";
|
|
2151
|
+
var fieldsView = makeCompositeView({
|
|
2152
|
+
typeClass: "font-semibold",
|
|
2153
|
+
borderClass: "border-base-content/15"
|
|
2154
|
+
});
|
|
2155
|
+
var InstanceFields = component8({
|
|
2156
|
+
name: "InstanceFields",
|
|
2157
|
+
fields: { ...compositeFields, typeName: "" },
|
|
2158
|
+
methods: {
|
|
2159
|
+
...compositeMethods,
|
|
2160
|
+
typeText() {
|
|
2161
|
+
return this.typeName;
|
|
2162
|
+
},
|
|
2163
|
+
countText() {
|
|
2164
|
+
return `{${this.items.size}}`;
|
|
2165
|
+
}
|
|
2166
|
+
},
|
|
2167
|
+
alter: compositeAlter,
|
|
2168
|
+
statics: {
|
|
2169
|
+
fromData(instance, comp) {
|
|
2170
|
+
const d = introspectComponent(comp);
|
|
2171
|
+
const items = d.fields.map((f) => ImEntry.make({
|
|
2172
|
+
key: f.name,
|
|
2173
|
+
child: ImInspector.Class.fromData(instance.get(f.name))
|
|
2174
|
+
}));
|
|
2175
|
+
return this.make({ typeName: d.name, items, isExpanded: true });
|
|
2176
|
+
}
|
|
2177
|
+
},
|
|
2178
|
+
view: fieldsView
|
|
2179
|
+
});
|
|
2180
|
+
var InstanceInspector = component8({
|
|
2181
|
+
name: "InstanceInspector",
|
|
2182
|
+
fields: { value: null },
|
|
2183
|
+
methods: {
|
|
2184
|
+
toggleIsExpanded() {
|
|
2185
|
+
return typeof this.value?.toggleIsExpanded === "function" ? this.setValue(this.value.toggleIsExpanded()) : this;
|
|
2186
|
+
}
|
|
2187
|
+
},
|
|
2188
|
+
statics: {
|
|
2189
|
+
fromData(instance, comp) {
|
|
2190
|
+
const value = comp && isComponentInstance(instance) ? InstanceFields.Class.fromData(instance, comp) : ImInspector.Class.fromData(instance);
|
|
2191
|
+
return this.make({ value });
|
|
2192
|
+
}
|
|
2193
|
+
},
|
|
2194
|
+
view: html8`<span class="contents"><x render=".value"></x></span>`
|
|
2195
|
+
});
|
|
2196
|
+
function asTestView(tests) {
|
|
2197
|
+
if (tests == null)
|
|
2198
|
+
return null;
|
|
2199
|
+
return isComponentInstance(tests) ? tests : TestReport.Class.fromResults(tests);
|
|
2200
|
+
}
|
|
2201
|
+
function asLintView(lint) {
|
|
2202
|
+
if (lint == null)
|
|
2203
|
+
return null;
|
|
2204
|
+
return isComponentInstance(lint) ? lint : LintReport.Class.fromData(lint);
|
|
2205
|
+
}
|
|
2206
|
+
var InstanceExplorer = component8({
|
|
2207
|
+
name: "InstanceExplorer",
|
|
2208
|
+
fields: {
|
|
2209
|
+
activeTab: "instance",
|
|
2210
|
+
instanceView: null,
|
|
2211
|
+
componentView: null,
|
|
2212
|
+
testView: null,
|
|
2213
|
+
lintView: null,
|
|
2214
|
+
hasComponent: false,
|
|
2215
|
+
hasTests: false,
|
|
2216
|
+
hasLint: false
|
|
2217
|
+
},
|
|
2218
|
+
statics: {
|
|
2219
|
+
fromData(instance, comp, { tests = null, lint = null } = {}) {
|
|
2220
|
+
const testView = asTestView(tests);
|
|
2221
|
+
const lintView = asLintView(lint);
|
|
2222
|
+
return this.make({
|
|
2223
|
+
instanceView: InstanceInspector.Class.fromData(instance, comp),
|
|
2224
|
+
componentView: comp ? ComponentInspector.Class.fromData(comp) : null,
|
|
2225
|
+
testView,
|
|
2226
|
+
lintView,
|
|
2227
|
+
hasComponent: !!comp,
|
|
2228
|
+
hasTests: !!testView,
|
|
2229
|
+
hasLint: !!lintView
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
},
|
|
2233
|
+
view: html8`<div class="font-mono text-sm leading-snug flex flex-col gap-3">
|
|
2234
|
+
<div role="tablist" class="tabs">
|
|
2235
|
+
<a
|
|
2236
|
+
role="tab"
|
|
2237
|
+
@if.class="equals? .activeTab 'instance'"
|
|
2238
|
+
@then="'tab tab-active'"
|
|
2239
|
+
@else="'tab'"
|
|
2240
|
+
@on.click="$setActiveTab 'instance'"
|
|
2241
|
+
>
|
|
2242
|
+
Instance
|
|
2243
|
+
</a>
|
|
2244
|
+
<a
|
|
2245
|
+
role="tab"
|
|
2246
|
+
@show=".hasComponent"
|
|
2247
|
+
@if.class="equals? .activeTab 'component'"
|
|
2248
|
+
@then="'tab tab-active'"
|
|
2249
|
+
@else="'tab'"
|
|
2250
|
+
@on.click="$setActiveTab 'component'"
|
|
2251
|
+
>
|
|
2252
|
+
Component
|
|
2253
|
+
</a>
|
|
2254
|
+
<a
|
|
2255
|
+
role="tab"
|
|
2256
|
+
@show=".hasTests"
|
|
2257
|
+
@if.class="equals? .activeTab 'tests'"
|
|
2258
|
+
@then="'tab tab-active'"
|
|
2259
|
+
@else="'tab'"
|
|
2260
|
+
@on.click="$setActiveTab 'tests'"
|
|
2261
|
+
>
|
|
2262
|
+
Tests
|
|
2263
|
+
</a>
|
|
2264
|
+
<a
|
|
2265
|
+
role="tab"
|
|
2266
|
+
@show=".hasLint"
|
|
2267
|
+
@if.class="equals? .activeTab 'lint'"
|
|
2268
|
+
@then="'tab tab-active'"
|
|
2269
|
+
@else="'tab'"
|
|
2270
|
+
@on.click="$setActiveTab 'lint'"
|
|
2271
|
+
>
|
|
2272
|
+
Lint
|
|
2273
|
+
</a>
|
|
2274
|
+
</div>
|
|
2275
|
+
<div @show="equals? .activeTab 'instance'">
|
|
2276
|
+
<x render=".instanceView"></x>
|
|
2277
|
+
</div>
|
|
2278
|
+
<div @show="equals? .activeTab 'component'">
|
|
2279
|
+
<div @show=".hasComponent"><x render=".componentView"></x></div>
|
|
2280
|
+
<div @hide=".hasComponent" class="text-base-content/50 text-xs">
|
|
2281
|
+
Pass the Component descriptor to inspect its definition.
|
|
2282
|
+
</div>
|
|
2283
|
+
</div>
|
|
2284
|
+
<div @show="equals? .activeTab 'tests'">
|
|
2285
|
+
<x render=".testView"></x>
|
|
2286
|
+
</div>
|
|
2287
|
+
<div @show="equals? .activeTab 'lint'">
|
|
2288
|
+
<x render=".lintView"></x>
|
|
2289
|
+
</div>
|
|
2290
|
+
</div>`
|
|
2291
|
+
});
|
|
2292
|
+
function getComponents8() {
|
|
2293
|
+
const all = [
|
|
2294
|
+
InstanceExplorer,
|
|
2295
|
+
InstanceInspector,
|
|
2296
|
+
InstanceFields,
|
|
2297
|
+
...getComponents5(),
|
|
2298
|
+
...getComponents7(),
|
|
2299
|
+
...getComponents6()
|
|
2300
|
+
];
|
|
2301
|
+
const seen = new Set;
|
|
2302
|
+
return all.filter((c) => {
|
|
2303
|
+
if (seen.has(c.name))
|
|
2304
|
+
return false;
|
|
2305
|
+
seen.add(c.name);
|
|
2306
|
+
return true;
|
|
2307
|
+
});
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
// src/components/build-views.js
|
|
2311
|
+
async function buildInspectorViews(value, scope, { getTests = null, components = [], dev = null, name } = {}) {
|
|
2312
|
+
const comp = isComponentInstance(value) ? scope.getCompFor(value) : null;
|
|
2313
|
+
const out = {
|
|
2314
|
+
instanceView: InstanceInspector.Class.fromData(value, scope.getCompFor(value)),
|
|
2315
|
+
componentView: null,
|
|
2316
|
+
lintView: null,
|
|
2317
|
+
testView: null,
|
|
2318
|
+
hasInspect: true,
|
|
2319
|
+
hasComponent: false,
|
|
2320
|
+
hasLint: false,
|
|
2321
|
+
hasTest: false
|
|
2322
|
+
};
|
|
2323
|
+
if (comp) {
|
|
2324
|
+
out.componentView = ComponentInspector.Class.fromData(comp);
|
|
2325
|
+
out.hasComponent = true;
|
|
2326
|
+
if (dev) {
|
|
2327
|
+
const findings = dev.shadowCheckComponent(comp);
|
|
2328
|
+
out.lintView = LintReport.Class.fromData({
|
|
2329
|
+
components: [{ componentName: comp.name, findings }]
|
|
2330
|
+
});
|
|
2331
|
+
out.hasLint = true;
|
|
2332
|
+
if (getTests) {
|
|
2333
|
+
const report = await dev.runTests({
|
|
2334
|
+
getTests,
|
|
2335
|
+
components,
|
|
2336
|
+
expect: dev.expect,
|
|
2337
|
+
name: name === undefined ? comp.name : name
|
|
2338
|
+
});
|
|
2339
|
+
out.testView = TestReport.Class.fromResults(report);
|
|
2340
|
+
out.hasTest = true;
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
return out;
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
// src/components/index.js
|
|
2348
|
+
function getComponents9() {
|
|
2349
|
+
const all = [
|
|
2350
|
+
...getComponents5(),
|
|
2351
|
+
...getComponents8(),
|
|
2352
|
+
...getComponents6(),
|
|
2353
|
+
...getComponents7(),
|
|
2354
|
+
...getComponents3(),
|
|
2355
|
+
...getComponents4(),
|
|
2356
|
+
...getComponents2(),
|
|
2357
|
+
...getComponents()
|
|
2358
|
+
];
|
|
2359
|
+
const seen = new Set;
|
|
2360
|
+
return all.filter((c) => {
|
|
2361
|
+
if (seen.has(c.name))
|
|
2362
|
+
return false;
|
|
2363
|
+
seen.add(c.name);
|
|
2364
|
+
return true;
|
|
2365
|
+
});
|
|
2366
|
+
}
|
|
2367
|
+
export {
|
|
2368
|
+
makeCompositeView,
|
|
2369
|
+
lintMessage,
|
|
2370
|
+
isComponentInstance,
|
|
2371
|
+
introspectComponent,
|
|
2372
|
+
getComponents9 as getComponents,
|
|
2373
|
+
fmtMapKey,
|
|
2374
|
+
fmtAnyKey,
|
|
2375
|
+
compositeView,
|
|
2376
|
+
compositeMethods,
|
|
2377
|
+
compositeFields,
|
|
2378
|
+
compositeAlter,
|
|
2379
|
+
collectTests,
|
|
2380
|
+
classifySchema,
|
|
2381
|
+
classifyJson,
|
|
2382
|
+
classifyJsExtra,
|
|
2383
|
+
classifyImmutable,
|
|
2384
|
+
classifyData,
|
|
2385
|
+
chain,
|
|
2386
|
+
buildInspectorViews,
|
|
2387
|
+
TestSuite,
|
|
2388
|
+
TestReport,
|
|
2389
|
+
TestCase,
|
|
2390
|
+
SchemaViewer,
|
|
2391
|
+
SchemaScalar,
|
|
2392
|
+
SchemaRef,
|
|
2393
|
+
SchemaProperty,
|
|
2394
|
+
SchemaObject,
|
|
2395
|
+
SchemaNot,
|
|
2396
|
+
SchemaEnum,
|
|
2397
|
+
SchemaConst,
|
|
2398
|
+
SchemaConditional,
|
|
2399
|
+
SchemaCombinator,
|
|
2400
|
+
SchemaBranch,
|
|
2401
|
+
SchemaBoolean,
|
|
2402
|
+
SchemaArray,
|
|
2403
|
+
LintReport,
|
|
2404
|
+
LintFinding,
|
|
2405
|
+
LintComponent,
|
|
2406
|
+
JsonViewer,
|
|
2407
|
+
JsonString,
|
|
2408
|
+
JsonProperty,
|
|
2409
|
+
JsonObject,
|
|
2410
|
+
JsonNumber,
|
|
2411
|
+
JsonNull,
|
|
2412
|
+
JsonBoolean,
|
|
2413
|
+
JsonArray,
|
|
2414
|
+
JsUndefined,
|
|
2415
|
+
JsSymbol,
|
|
2416
|
+
JsSetItem,
|
|
2417
|
+
JsSet,
|
|
2418
|
+
JsRegExp,
|
|
2419
|
+
JsMap,
|
|
2420
|
+
JsFunction,
|
|
2421
|
+
JsError,
|
|
2422
|
+
JsDate,
|
|
2423
|
+
JsClassInstance,
|
|
2424
|
+
JsBigInt,
|
|
2425
|
+
InstanceInspector,
|
|
2426
|
+
InstanceFields,
|
|
2427
|
+
InstanceExplorer,
|
|
2428
|
+
ImStack,
|
|
2429
|
+
ImSet,
|
|
2430
|
+
ImRecord,
|
|
2431
|
+
ImRange,
|
|
2432
|
+
ImOSet,
|
|
2433
|
+
ImOMap,
|
|
2434
|
+
ImMap,
|
|
2435
|
+
ImList,
|
|
2436
|
+
ImInspector,
|
|
2437
|
+
ImEntry,
|
|
2438
|
+
DataInspector,
|
|
2439
|
+
ComponentInspector,
|
|
2440
|
+
CompView,
|
|
2441
|
+
CompSection,
|
|
2442
|
+
CompName,
|
|
2443
|
+
CompField
|
|
2444
|
+
};
|