@spyglassmc/mcdoc 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/binder/index.js +122 -191
- package/lib/index.d.ts +1 -0
- package/lib/index.js +4 -4
- package/lib/node/index.d.ts +9 -4
- package/lib/node/index.js +77 -131
- package/lib/parser/index.d.ts +13 -1
- package/lib/parser/index.js +112 -190
- package/lib/runtime/attribute/builtin.d.ts +3 -0
- package/lib/runtime/attribute/builtin.js +130 -0
- package/lib/runtime/attribute/index.d.ts +22 -0
- package/lib/runtime/attribute/index.js +22 -0
- package/lib/runtime/attribute/validator.d.ts +16 -0
- package/lib/runtime/attribute/validator.js +85 -0
- package/lib/runtime/checker/context.d.ts +34 -0
- package/lib/runtime/checker/context.js +17 -0
- package/lib/runtime/checker/error.d.ts +70 -0
- package/lib/runtime/checker/error.js +352 -0
- package/lib/runtime/checker/index.d.ts +80 -0
- package/lib/runtime/checker/index.js +914 -0
- package/lib/runtime/completer/index.d.ts +20 -0
- package/lib/runtime/completer/index.js +123 -0
- package/lib/runtime/index.d.ts +5 -0
- package/lib/runtime/index.js +5 -0
- package/lib/type/index.d.ts +73 -92
- package/lib/type/index.js +341 -422
- package/lib/uri_processors.js +2 -8
- package/package.json +3 -3
|
@@ -0,0 +1,914 @@
|
|
|
1
|
+
import { Range, Source } from '@spyglassmc/core';
|
|
2
|
+
import { localize } from '@spyglassmc/locales';
|
|
3
|
+
import { TypeDefSymbolData } from '../../binder/index.js';
|
|
4
|
+
import { McdocType, NumericRange } from '../../type/index.js';
|
|
5
|
+
import { handleAttributes } from '../attribute/index.js';
|
|
6
|
+
import { McdocCheckerContext } from './context.js';
|
|
7
|
+
import { condenseAndPropagate } from './error.js';
|
|
8
|
+
export * from './context.js';
|
|
9
|
+
export * from './error.js';
|
|
10
|
+
export function reference(node, path, ctx) {
|
|
11
|
+
typeDefinition(node, { kind: 'reference', path }, ctx);
|
|
12
|
+
}
|
|
13
|
+
export function dispatcher(node, registry, index, ctx) {
|
|
14
|
+
const parallelIndices = typeof index === 'string'
|
|
15
|
+
? [{ kind: 'static', value: index }]
|
|
16
|
+
: Array.isArray(index)
|
|
17
|
+
? index
|
|
18
|
+
: [index];
|
|
19
|
+
typeDefinition(node, { kind: 'dispatcher', registry, parallelIndices }, ctx);
|
|
20
|
+
}
|
|
21
|
+
export function isAssignable(assignValue, typeDef, ctx, isEquivalent) {
|
|
22
|
+
if (assignValue.kind === 'literal' && typeDef.kind === 'literal'
|
|
23
|
+
&& assignValue.value.kind === typeDef.value.kind
|
|
24
|
+
&& !assignValue.attributes && !typeDef.attributes) {
|
|
25
|
+
return assignValue.value.value === typeDef.value.value;
|
|
26
|
+
}
|
|
27
|
+
let ans = true;
|
|
28
|
+
const newCtx = McdocCheckerContext.create(ctx, {
|
|
29
|
+
isEquivalent,
|
|
30
|
+
getChildren: (_, d) => {
|
|
31
|
+
switch (d.kind) {
|
|
32
|
+
case 'list':
|
|
33
|
+
const vals = getPossibleTypes(d.item);
|
|
34
|
+
return [vals.map(v => ({ originalNode: v, inferredType: v }))];
|
|
35
|
+
case 'byte_array':
|
|
36
|
+
return [[{ originalNode: { kind: 'byte' }, inferredType: { kind: 'byte' } }]];
|
|
37
|
+
case 'int_array':
|
|
38
|
+
return [[{ originalNode: { kind: 'int' }, inferredType: { kind: 'int' } }]];
|
|
39
|
+
case 'long_array':
|
|
40
|
+
return [[{ originalNode: { kind: 'long' }, inferredType: { kind: 'long' } }]];
|
|
41
|
+
case 'struct':
|
|
42
|
+
return d.fields.map(f => {
|
|
43
|
+
const vals = getPossibleTypes(f.type);
|
|
44
|
+
return {
|
|
45
|
+
attributes: f.attributes,
|
|
46
|
+
key: { originalNode: f.key, inferredType: f.key },
|
|
47
|
+
possibleValues: vals.map(v => ({ originalNode: v, inferredType: v })),
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
case 'tuple':
|
|
51
|
+
return d.items.map(f => {
|
|
52
|
+
const vals = getPossibleTypes(f);
|
|
53
|
+
return vals.map(v => ({ originalNode: v, inferredType: v }));
|
|
54
|
+
});
|
|
55
|
+
default:
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
reportError: () => {
|
|
60
|
+
ans = false;
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
const node = {
|
|
64
|
+
parent: undefined,
|
|
65
|
+
runtimeKey: undefined,
|
|
66
|
+
possibleValues: [],
|
|
67
|
+
};
|
|
68
|
+
node.possibleValues = getPossibleTypes(typeDef).map(v => ({
|
|
69
|
+
entryNode: node,
|
|
70
|
+
node: { originalNode: v, inferredType: v },
|
|
71
|
+
children: [],
|
|
72
|
+
definitionsByParent: [],
|
|
73
|
+
}));
|
|
74
|
+
// TODO add bail option to allow checking logic to bail on first error
|
|
75
|
+
typeDefinition(getPossibleTypes(assignValue).map(v => ({ originalNode: v, inferredType: v })), typeDef, newCtx);
|
|
76
|
+
return ans;
|
|
77
|
+
}
|
|
78
|
+
export function typeDefinition(runtimeValues, typeDef, ctx) {
|
|
79
|
+
const rootNode = {
|
|
80
|
+
parent: undefined,
|
|
81
|
+
runtimeKey: undefined,
|
|
82
|
+
possibleValues: [],
|
|
83
|
+
};
|
|
84
|
+
rootNode.possibleValues = runtimeValues.map(n => ({
|
|
85
|
+
node: n,
|
|
86
|
+
entryNode: rootNode,
|
|
87
|
+
definitionsByParent: [],
|
|
88
|
+
children: [],
|
|
89
|
+
}));
|
|
90
|
+
for (const value of rootNode.possibleValues) {
|
|
91
|
+
const simplifiedRoot = simplify(typeDef, { ctx, node: value });
|
|
92
|
+
const validRootDefinitions = simplifiedRoot.kind === 'union'
|
|
93
|
+
? simplifiedRoot.members
|
|
94
|
+
: [simplifiedRoot];
|
|
95
|
+
value.definitionsByParent = [{
|
|
96
|
+
parents: [],
|
|
97
|
+
keyDefinition: undefined,
|
|
98
|
+
runtimeNode: value,
|
|
99
|
+
originalTypeDef: typeDef,
|
|
100
|
+
condensedErrors: [],
|
|
101
|
+
validDefinitions: [],
|
|
102
|
+
}];
|
|
103
|
+
value.definitionsByParent[0].validDefinitions = validRootDefinitions.map(d => ({
|
|
104
|
+
groupNode: value.definitionsByParent[0],
|
|
105
|
+
typeDef: d,
|
|
106
|
+
children: [],
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
const nodeQueue = [rootNode];
|
|
110
|
+
while (nodeQueue.length !== 0) {
|
|
111
|
+
const node = nodeQueue.shift();
|
|
112
|
+
for (const value of node.possibleValues) {
|
|
113
|
+
const inferredSimplified = simplify(value.node.inferredType, { ctx, node: value });
|
|
114
|
+
const children = ctx.getChildren(value.node.originalNode, inferredSimplified);
|
|
115
|
+
const childNodes = children.map(c => {
|
|
116
|
+
const ans = {
|
|
117
|
+
parent: value,
|
|
118
|
+
runtimeKey: !Array.isArray(c) ? c.key : undefined,
|
|
119
|
+
possibleValues: [],
|
|
120
|
+
};
|
|
121
|
+
ans.possibleValues = (Array.isArray(c) ? c : c.possibleValues).map(v => ({
|
|
122
|
+
entryNode: ans,
|
|
123
|
+
node: v,
|
|
124
|
+
definitionsByParent: [],
|
|
125
|
+
condensedErrors: [],
|
|
126
|
+
children: [],
|
|
127
|
+
}));
|
|
128
|
+
return ans;
|
|
129
|
+
});
|
|
130
|
+
for (const definitionGroup of value.definitionsByParent) {
|
|
131
|
+
const definitionErrors = [];
|
|
132
|
+
if (definitionGroup.validDefinitions.length === 0) {
|
|
133
|
+
// nothing can be assigned to an empty union
|
|
134
|
+
definitionGroup.condensedErrors = [[{
|
|
135
|
+
kind: 'type_mismatch',
|
|
136
|
+
node: value.node,
|
|
137
|
+
expected: [],
|
|
138
|
+
}]];
|
|
139
|
+
}
|
|
140
|
+
for (const def of definitionGroup.validDefinitions) {
|
|
141
|
+
const { errors, childDefinitions } = checkShallowly(value.node, inferredSimplified, children, def.typeDef, ctx);
|
|
142
|
+
definitionErrors.push({ definition: def, errors });
|
|
143
|
+
for (let i = 0; i < childDefinitions.length; i++) {
|
|
144
|
+
const childDef = childDefinitions[i];
|
|
145
|
+
if (!childDef) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const child = childNodes[i];
|
|
149
|
+
const existingDef = child.possibleValues.length > 0
|
|
150
|
+
? child.possibleValues[0].definitionsByParent
|
|
151
|
+
.find(d => (d.keyDefinition === undefined || childDef.keyType === undefined
|
|
152
|
+
? d.keyDefinition === undefined
|
|
153
|
+
: McdocType.equals(d.keyDefinition, childDef.keyType))
|
|
154
|
+
&& McdocType.equals(d.originalTypeDef, childDef.type))
|
|
155
|
+
: undefined;
|
|
156
|
+
for (const childValue of child.possibleValues) {
|
|
157
|
+
if (existingDef) {
|
|
158
|
+
existingDef.parents.push(def);
|
|
159
|
+
def.children.push(existingDef);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
// TODO We need some sort of map / local cache which keeps track of the original
|
|
163
|
+
// non-simplified types and see if they have been compared yet. This is needed
|
|
164
|
+
// for structures that are cyclic, to essentially bail out once we are comparing
|
|
165
|
+
// the same types again and just collect the errors of the lower depth.
|
|
166
|
+
// This will currently lead to a stack overflow error when e.g. comparing two
|
|
167
|
+
// text component definitions
|
|
168
|
+
const simplified = simplify(childDef.type, { ctx, node: childValue });
|
|
169
|
+
const childDefinitionGroup = {
|
|
170
|
+
parents: [def],
|
|
171
|
+
runtimeNode: childValue,
|
|
172
|
+
keyDefinition: childDef.keyType,
|
|
173
|
+
originalTypeDef: childDef.type,
|
|
174
|
+
validDefinitions: [],
|
|
175
|
+
condensedErrors: [],
|
|
176
|
+
desc: childDef.desc,
|
|
177
|
+
};
|
|
178
|
+
childDefinitionGroup.validDefinitions =
|
|
179
|
+
(simplified.kind === 'union' ? simplified.members : [simplified])
|
|
180
|
+
.map(d => ({
|
|
181
|
+
groupNode: childDefinitionGroup,
|
|
182
|
+
typeDef: d,
|
|
183
|
+
children: [],
|
|
184
|
+
}));
|
|
185
|
+
childValue.definitionsByParent.push(childDefinitionGroup);
|
|
186
|
+
def.children.push(childDefinitionGroup);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
condenseAndPropagate(definitionGroup, definitionErrors);
|
|
191
|
+
}
|
|
192
|
+
value.children = childNodes;
|
|
193
|
+
nodeQueue.push(...childNodes);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (ctx.attachTypeInfo) {
|
|
197
|
+
for (const node of rootNode.possibleValues) {
|
|
198
|
+
attachTypeInfo(node, ctx);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
for (const error of rootNode.possibleValues
|
|
202
|
+
.flatMap(v => v.definitionsByParent)
|
|
203
|
+
.flatMap(d => d.condensedErrors)
|
|
204
|
+
.flat()) {
|
|
205
|
+
if (error) {
|
|
206
|
+
ctx.reportError(error);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function attachTypeInfo(node, ctx) {
|
|
211
|
+
const definitions = node.definitionsByParent.flatMap(d => d.validDefinitions);
|
|
212
|
+
if (definitions.length === 1) {
|
|
213
|
+
const { typeDef, groupNode } = definitions[0];
|
|
214
|
+
ctx.attachTypeInfo?.(node.node.originalNode, typeDef, groupNode.desc);
|
|
215
|
+
handleStringAttachers(node.node, typeDef, ctx);
|
|
216
|
+
if (node.entryNode.runtimeKey && groupNode.keyDefinition) {
|
|
217
|
+
ctx.attachTypeInfo?.(node.entryNode.runtimeKey.originalNode, groupNode.keyDefinition, groupNode.desc);
|
|
218
|
+
handleStringAttachers(node.entryNode.runtimeKey, groupNode.keyDefinition, ctx);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
else if (definitions.length > 1) {
|
|
222
|
+
ctx.attachTypeInfo?.(node.node.originalNode, {
|
|
223
|
+
kind: 'union',
|
|
224
|
+
members: definitions.map(d => d.typeDef),
|
|
225
|
+
});
|
|
226
|
+
if (node.entryNode.runtimeKey) {
|
|
227
|
+
ctx.attachTypeInfo?.(node.entryNode.runtimeKey.originalNode, {
|
|
228
|
+
kind: 'union',
|
|
229
|
+
members: node.definitionsByParent
|
|
230
|
+
.map(d => d.keyDefinition)
|
|
231
|
+
.filter((d) => d !== undefined),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
// when there are multiple valid definitions, we don't run any string parsers.
|
|
235
|
+
}
|
|
236
|
+
for (const child of node.children.flatMap(c => c.possibleValues)) {
|
|
237
|
+
attachTypeInfo(child, ctx);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function handleStringAttachers(runtimeValue, typeDef, ctx) {
|
|
241
|
+
const { stringAttacher } = ctx;
|
|
242
|
+
if (!stringAttacher) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
handleAttributes(typeDef.attributes, ctx, (handler, config) => {
|
|
246
|
+
const parser = handler.stringParser?.(config, typeDef, ctx);
|
|
247
|
+
if (!parser) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
stringAttacher(runtimeValue.originalNode, (node) => {
|
|
251
|
+
const src = new Source(node.value, node.valueMap);
|
|
252
|
+
const start = src.cursor;
|
|
253
|
+
const child = parser(src, ctx);
|
|
254
|
+
if (!child) {
|
|
255
|
+
ctx.err.report(localize('expected', localize('mcdoc.runtime.checker.value')), Range.create(start, src.skipRemaining()));
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
else if (src.canRead()) {
|
|
259
|
+
ctx.err.report(localize('mcdoc.runtime.checker.trailing'), Range.create(src.cursor, src.skipRemaining()));
|
|
260
|
+
}
|
|
261
|
+
node.children = [child];
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
function checkShallowly(runtimeNode, simplifiedInferred, children, typeDef, ctx) {
|
|
266
|
+
const typeDefValueType = getValueType(typeDef);
|
|
267
|
+
const runtimeValueType = getValueType(simplifiedInferred);
|
|
268
|
+
const childDefinitions = Array(children.length)
|
|
269
|
+
.fill(undefined);
|
|
270
|
+
if ((typeDef.kind !== 'any' && typeDef.kind !== 'unsafe'
|
|
271
|
+
&& simplifiedInferred.kind !== 'unsafe'
|
|
272
|
+
&& runtimeValueType.kind !== typeDefValueType.kind
|
|
273
|
+
&& !ctx.isEquivalent(runtimeValueType, typeDefValueType))) {
|
|
274
|
+
return {
|
|
275
|
+
childDefinitions,
|
|
276
|
+
errors: [{ kind: 'type_mismatch', node: runtimeNode, expected: [typeDef] }],
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
const errors = [];
|
|
280
|
+
let assignable = true;
|
|
281
|
+
handleAttributes(typeDef.attributes, ctx, (handler, config) => {
|
|
282
|
+
if (handler.checkInferred?.(config, simplifiedInferred, ctx) === false) {
|
|
283
|
+
assignable = false;
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
if (!assignable) {
|
|
287
|
+
errors.push({ kind: 'internal', node: runtimeNode });
|
|
288
|
+
}
|
|
289
|
+
if ((typeDef.kind === 'literal'
|
|
290
|
+
&& (simplifiedInferred.kind !== 'literal'
|
|
291
|
+
|| typeDef.value.value !== simplifiedInferred.value.value))
|
|
292
|
+
// TODO handle enum field attributes
|
|
293
|
+
|| (typeDef.kind === 'enum'
|
|
294
|
+
&& (simplifiedInferred.kind !== 'literal'
|
|
295
|
+
|| !typeDef.values.some(v => v.value === simplifiedInferred.value.value)))) {
|
|
296
|
+
return {
|
|
297
|
+
childDefinitions,
|
|
298
|
+
errors: [{ kind: 'type_mismatch', node: runtimeNode, expected: [typeDef] }],
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
switch (typeDef.kind) {
|
|
302
|
+
case 'any':
|
|
303
|
+
case 'unsafe':
|
|
304
|
+
break;
|
|
305
|
+
case 'byte':
|
|
306
|
+
case 'short':
|
|
307
|
+
case 'int':
|
|
308
|
+
case 'long':
|
|
309
|
+
case 'float':
|
|
310
|
+
case 'double':
|
|
311
|
+
if (typeDef.valueRange
|
|
312
|
+
&& simplifiedInferred.kind === 'literal'
|
|
313
|
+
&& typeof simplifiedInferred.value.value === 'number'
|
|
314
|
+
&& !NumericRange.isInRange(typeDef.valueRange, simplifiedInferred.value.value)) {
|
|
315
|
+
errors.push({
|
|
316
|
+
kind: 'number_out_of_range',
|
|
317
|
+
node: runtimeNode,
|
|
318
|
+
ranges: [typeDef.valueRange],
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
break;
|
|
322
|
+
case 'string':
|
|
323
|
+
if (typeDef.lengthRange
|
|
324
|
+
&& simplifiedInferred.kind === 'literal'
|
|
325
|
+
&& simplifiedInferred.value.kind === 'string'
|
|
326
|
+
&& !NumericRange.isInRange(typeDef.lengthRange, simplifiedInferred.value.value.length)) {
|
|
327
|
+
errors.push({
|
|
328
|
+
kind: 'invalid_string_length',
|
|
329
|
+
node: runtimeNode,
|
|
330
|
+
ranges: [typeDef.lengthRange],
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
break;
|
|
334
|
+
case 'struct': {
|
|
335
|
+
const literalKvps = new Map();
|
|
336
|
+
const otherKvps = [];
|
|
337
|
+
for (let i = 0; i < children.length; i++) {
|
|
338
|
+
const child = children[i];
|
|
339
|
+
if (Array.isArray(child)) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
if (child.key.inferredType.kind === 'literal'
|
|
343
|
+
&& child.key.inferredType.value.kind === 'string') {
|
|
344
|
+
const existing = literalKvps.get(child.key.inferredType.value.value);
|
|
345
|
+
if (existing) {
|
|
346
|
+
// duplicate key
|
|
347
|
+
existing.values.push({ pair: child, index: i });
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
literalKvps.set(child.key.inferredType.value.value, {
|
|
351
|
+
values: [{ pair: child, index: i }],
|
|
352
|
+
definition: undefined,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
otherKvps.push({ value: child, index: i });
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
for (const pair of typeDef.fields) {
|
|
361
|
+
const otherKvpMatches = [];
|
|
362
|
+
let foundMatch = false;
|
|
363
|
+
if (pair.key.kind === 'literal' && pair.key.value.kind === 'string') {
|
|
364
|
+
const runtimeChild = literalKvps.get(pair.key.value.value);
|
|
365
|
+
if (runtimeChild) {
|
|
366
|
+
foundMatch = true;
|
|
367
|
+
runtimeChild.definition = { keyType: pair.key, type: pair.type, desc: pair.desc };
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (!foundMatch) {
|
|
371
|
+
for (let i = 0; i < otherKvps.length; i++) {
|
|
372
|
+
const kvp = otherKvps[i];
|
|
373
|
+
if (isAssignable(kvp.value.key.inferredType, pair.key, ctx, ctx.isEquivalent)) {
|
|
374
|
+
foundMatch = true;
|
|
375
|
+
otherKvps.splice(i, 1);
|
|
376
|
+
otherKvpMatches.push(kvp.index);
|
|
377
|
+
i--;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
for (const kvp of literalKvps.entries()) {
|
|
381
|
+
if (!kvp[1].definition
|
|
382
|
+
&& isAssignable({ kind: 'literal', value: { kind: 'string', value: kvp[0] } }, pair.key, ctx, ctx.isEquivalent)) {
|
|
383
|
+
foundMatch = true;
|
|
384
|
+
kvp[1].definition = { keyType: pair.key, type: pair.type, desc: pair.desc };
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
for (const match of otherKvpMatches) {
|
|
389
|
+
childDefinitions[match] = { keyType: pair.key, type: pair.type, desc: pair.desc };
|
|
390
|
+
}
|
|
391
|
+
if (!foundMatch
|
|
392
|
+
&& !ctx.allowMissingKeys
|
|
393
|
+
&& pair.key.kind === 'literal'
|
|
394
|
+
&& pair.key.value.kind === 'string'
|
|
395
|
+
&& pair.optional !== true) {
|
|
396
|
+
errors.push({ kind: 'missing_key', node: runtimeNode, keys: [pair.key.value.value] });
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
for (const kvp of literalKvps.values()) {
|
|
400
|
+
for (const value of kvp.values) {
|
|
401
|
+
childDefinitions[value.index] = kvp.definition;
|
|
402
|
+
if (kvp.values.length > 1) {
|
|
403
|
+
errors.push({ kind: 'duplicate_key', node: value.pair.key });
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
for (let i = 0; i < children.length; i++) {
|
|
408
|
+
const childDef = childDefinitions[i];
|
|
409
|
+
const child = children[i];
|
|
410
|
+
if (childDef === undefined) {
|
|
411
|
+
if (Array.isArray(child)) {
|
|
412
|
+
// This should never happen
|
|
413
|
+
errors.push(...child.map(v => ({
|
|
414
|
+
kind: 'expected_key_value_pair',
|
|
415
|
+
node: v,
|
|
416
|
+
})));
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
errors.push({ kind: 'unknown_key', node: child.key });
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
case 'list':
|
|
426
|
+
case 'byte_array':
|
|
427
|
+
case 'int_array':
|
|
428
|
+
case 'long_array': {
|
|
429
|
+
let itemType;
|
|
430
|
+
switch (typeDef.kind) {
|
|
431
|
+
case 'list':
|
|
432
|
+
itemType = typeDef.item;
|
|
433
|
+
break;
|
|
434
|
+
case 'byte_array':
|
|
435
|
+
itemType = { kind: 'byte', valueRange: typeDef.valueRange };
|
|
436
|
+
break;
|
|
437
|
+
case 'int_array':
|
|
438
|
+
itemType = { kind: 'int', valueRange: typeDef.valueRange };
|
|
439
|
+
break;
|
|
440
|
+
case 'long_array':
|
|
441
|
+
itemType = { kind: 'long', valueRange: typeDef.valueRange };
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
for (let i = 0; i < childDefinitions.length; i++) {
|
|
445
|
+
childDefinitions[i] = { type: itemType };
|
|
446
|
+
}
|
|
447
|
+
if (typeDef.lengthRange && !NumericRange.isInRange(typeDef.lengthRange, children.length)) {
|
|
448
|
+
errors.push({
|
|
449
|
+
kind: 'invalid_collection_length',
|
|
450
|
+
node: runtimeNode,
|
|
451
|
+
ranges: [typeDef.lengthRange],
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
case 'tuple': {
|
|
457
|
+
for (let i = 0; i < children.length; i++) {
|
|
458
|
+
const child = children[i];
|
|
459
|
+
if (i < typeDef.items.length) {
|
|
460
|
+
childDefinitions[i] = { type: typeDef.items[i] };
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
// This really should always be an array, just to handle this gracefully
|
|
464
|
+
const values = Array.isArray(child) ? child : [...child.possibleValues, child.key];
|
|
465
|
+
errors.push(...values.map(v => ({
|
|
466
|
+
kind: 'unknown_tuple_element',
|
|
467
|
+
node: v,
|
|
468
|
+
})));
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
if (typeDef.items.length > children.length) {
|
|
472
|
+
errors.push({
|
|
473
|
+
kind: 'invalid_collection_length',
|
|
474
|
+
node: runtimeNode,
|
|
475
|
+
ranges: [{ kind: 0b00, max: typeDef.items.length, min: typeDef.items.length }],
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return { childDefinitions, errors };
|
|
482
|
+
}
|
|
483
|
+
export function getPossibleTypes(typeDef) {
|
|
484
|
+
return typeDef.kind === 'union' ? typeDef.members.flatMap(m => getPossibleTypes(m)) : [typeDef];
|
|
485
|
+
}
|
|
486
|
+
export function simplify(typeDef, context) {
|
|
487
|
+
function wrap(typeDef) {
|
|
488
|
+
if (!typeDef.attributes?.length) {
|
|
489
|
+
return typeDef;
|
|
490
|
+
}
|
|
491
|
+
handleAttributes(typeDef.attributes, context.ctx, (handler, config) => {
|
|
492
|
+
if (handler.mapType) {
|
|
493
|
+
typeDef = handler.mapType(config, typeDef, context.ctx);
|
|
494
|
+
}
|
|
495
|
+
});
|
|
496
|
+
return typeDef;
|
|
497
|
+
}
|
|
498
|
+
switch (typeDef.kind) {
|
|
499
|
+
case 'reference':
|
|
500
|
+
return wrap(simplifyReference(typeDef, context));
|
|
501
|
+
case 'dispatcher':
|
|
502
|
+
return wrap(simplifyDispatcher(typeDef, context));
|
|
503
|
+
case 'indexed':
|
|
504
|
+
return wrap(simplifyIndexed(typeDef, context));
|
|
505
|
+
case 'union':
|
|
506
|
+
return wrap(simplifyUnion(typeDef, context));
|
|
507
|
+
case 'struct':
|
|
508
|
+
return wrap(simplifyStruct(typeDef, context));
|
|
509
|
+
case 'list':
|
|
510
|
+
return wrap(simplifyList(typeDef, context));
|
|
511
|
+
case 'tuple':
|
|
512
|
+
return wrap(simplifyTuple(typeDef, context));
|
|
513
|
+
case 'enum':
|
|
514
|
+
return wrap(simplifyEnum(typeDef, context));
|
|
515
|
+
case 'concrete':
|
|
516
|
+
return wrap(simplifyConcrete(typeDef, context));
|
|
517
|
+
case 'template':
|
|
518
|
+
return wrap(simplifyTemplate(typeDef, context));
|
|
519
|
+
case 'mapped':
|
|
520
|
+
return wrap(simplifyMapped(typeDef, context));
|
|
521
|
+
default:
|
|
522
|
+
return wrap(typeDef);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
function simplifyReference(typeDef, context) {
|
|
526
|
+
if (!typeDef.path) {
|
|
527
|
+
// TODO when does this happen?
|
|
528
|
+
context.ctx.logger.warn(`Tried to access empty reference`);
|
|
529
|
+
return { kind: 'union', members: [] };
|
|
530
|
+
}
|
|
531
|
+
const mapped = context.typeMapping?.[typeDef.path];
|
|
532
|
+
if (mapped) {
|
|
533
|
+
return mapped;
|
|
534
|
+
}
|
|
535
|
+
// TODO Probably need to keep original symbol around in some way to support "go to definition"
|
|
536
|
+
const symbol = context.ctx.symbols.query(context.ctx.doc, 'mcdoc', typeDef.path);
|
|
537
|
+
const def = symbol.getData(TypeDefSymbolData.is)?.typeDef;
|
|
538
|
+
if (!def) {
|
|
539
|
+
context.ctx.logger.warn(`Tried to access unknown reference ${typeDef.path}`);
|
|
540
|
+
return { kind: 'union', members: [] };
|
|
541
|
+
}
|
|
542
|
+
const simplifiedDef = simplify(def, context);
|
|
543
|
+
if (typeDef.attributes?.length) {
|
|
544
|
+
return {
|
|
545
|
+
...simplifiedDef,
|
|
546
|
+
attributes: [...typeDef.attributes, ...simplifiedDef.attributes ?? []],
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
return simplifiedDef;
|
|
550
|
+
}
|
|
551
|
+
function simplifyDispatcher(typeDef, context) {
|
|
552
|
+
const dispatcher = context.ctx.symbols.query(context.ctx.doc, 'mcdoc/dispatcher', typeDef.registry).symbol
|
|
553
|
+
?.members;
|
|
554
|
+
if (!dispatcher) {
|
|
555
|
+
context.ctx.logger.warn(`Tried to access unknown dispatcher ${typeDef.registry}`);
|
|
556
|
+
return { kind: 'union', members: [] };
|
|
557
|
+
}
|
|
558
|
+
return resolveIndices(typeDef.parallelIndices, dispatcher, context);
|
|
559
|
+
}
|
|
560
|
+
function simplifyIndexed(typeDef, context) {
|
|
561
|
+
const child = simplify(typeDef.child, {
|
|
562
|
+
...context,
|
|
563
|
+
typeArgs: [],
|
|
564
|
+
structFields: undefined,
|
|
565
|
+
});
|
|
566
|
+
if (child.kind !== 'struct') {
|
|
567
|
+
context.ctx.logger.warn(`Tried to index un-indexable type ${child.kind}`);
|
|
568
|
+
return { kind: 'union', members: [] };
|
|
569
|
+
}
|
|
570
|
+
const symbolMap = {};
|
|
571
|
+
for (const field of child.fields) {
|
|
572
|
+
if (field.key.kind === 'literal' && field.key.value.kind === 'string') {
|
|
573
|
+
symbolMap[field.key.value.value] = {
|
|
574
|
+
data: {
|
|
575
|
+
typeDef: field.type,
|
|
576
|
+
},
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return resolveIndices(typeDef.parallelIndices, symbolMap, context);
|
|
581
|
+
}
|
|
582
|
+
function resolveIndices(parallelIndices, symbolMap, context) {
|
|
583
|
+
let values = [];
|
|
584
|
+
let unkownTypeDef = false;
|
|
585
|
+
function getUnknownTypeDef() {
|
|
586
|
+
if (unkownTypeDef === false) {
|
|
587
|
+
const data = symbolMap['%unknown']?.data;
|
|
588
|
+
unkownTypeDef = TypeDefSymbolData.is(data) ? data.typeDef : undefined;
|
|
589
|
+
}
|
|
590
|
+
return unkownTypeDef;
|
|
591
|
+
}
|
|
592
|
+
for (const index of parallelIndices) {
|
|
593
|
+
let lookup = [];
|
|
594
|
+
if (index.kind === 'static') {
|
|
595
|
+
if (index.value === '%fallback') {
|
|
596
|
+
values = Object.values(symbolMap)
|
|
597
|
+
.map(e => e.data)
|
|
598
|
+
.filter(TypeDefSymbolData.is)
|
|
599
|
+
.map(f => f.typeDef);
|
|
600
|
+
break;
|
|
601
|
+
}
|
|
602
|
+
if (index.value.startsWith('minecraft:')) {
|
|
603
|
+
lookup.push(index.value.substring(10));
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
lookup.push(index.value);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
let possibilities = context.isMember
|
|
611
|
+
? [{ value: context.node, key: context.node.entryNode.runtimeKey }]
|
|
612
|
+
: [{
|
|
613
|
+
value: context.node.entryNode.parent,
|
|
614
|
+
key: context.node.entryNode.runtimeKey,
|
|
615
|
+
}];
|
|
616
|
+
for (const entry of index.accessor) {
|
|
617
|
+
if (typeof entry !== 'string' && entry.keyword === 'parent') {
|
|
618
|
+
possibilities = possibilities.map(n => ({
|
|
619
|
+
value: n.value?.entryNode.parent,
|
|
620
|
+
key: n.value?.entryNode.runtimeKey,
|
|
621
|
+
}));
|
|
622
|
+
}
|
|
623
|
+
else if (typeof entry !== 'string' && entry.keyword === 'key') {
|
|
624
|
+
possibilities = possibilities.map(p => ({
|
|
625
|
+
value: p.key
|
|
626
|
+
? { node: p.key, entryNode: { parent: p.value, runtimeKey: p.key } }
|
|
627
|
+
: undefined,
|
|
628
|
+
key: undefined,
|
|
629
|
+
}));
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
else if (typeof entry === 'string') {
|
|
633
|
+
const newPossibilities = [];
|
|
634
|
+
for (const node of possibilities) {
|
|
635
|
+
const possibleChildren = node.value
|
|
636
|
+
? context.ctx.getChildren(node.value.node.originalNode, simplify(node.value.node.inferredType, { ...context, node: node.value })).filter(child => {
|
|
637
|
+
if (!Array.isArray(child)) {
|
|
638
|
+
return child.key.inferredType.kind === 'literal'
|
|
639
|
+
&& child.key.inferredType.value.kind === 'string'
|
|
640
|
+
&& child.key.inferredType.value.value === entry;
|
|
641
|
+
}
|
|
642
|
+
// TODO if it's a list, consider all list items.
|
|
643
|
+
// This should probably work recursively if we have a list of lists.
|
|
644
|
+
return false;
|
|
645
|
+
}) // We don't consider arrays yet, see above.
|
|
646
|
+
.flatMap(c => c.possibleValues.map(v => ({
|
|
647
|
+
value: {
|
|
648
|
+
node: v,
|
|
649
|
+
entryNode: { parent: node.value, runtimeKey: c.key },
|
|
650
|
+
},
|
|
651
|
+
key: undefined,
|
|
652
|
+
})))
|
|
653
|
+
: [{ value: undefined, key: undefined }];
|
|
654
|
+
newPossibilities.push(...possibleChildren);
|
|
655
|
+
}
|
|
656
|
+
possibilities = newPossibilities;
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
lookup.push('%none');
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
for (const value of possibilities.map(p => p.value?.node)) {
|
|
664
|
+
if (value?.inferredType.kind === 'literal' && value.inferredType.value.kind === 'string') {
|
|
665
|
+
const ans = value.inferredType.value.value;
|
|
666
|
+
if (ans.startsWith('minecraft:')) {
|
|
667
|
+
lookup.push(ans.substring(10));
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
lookup.push(ans);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
else {
|
|
674
|
+
lookup.push('%none');
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
if (lookup.length === 0) {
|
|
679
|
+
lookup = ['%none'];
|
|
680
|
+
}
|
|
681
|
+
const currentValues = lookup.map(v => {
|
|
682
|
+
const data = symbolMap[v]?.data;
|
|
683
|
+
return TypeDefSymbolData.is(data) ? data.typeDef : getUnknownTypeDef();
|
|
684
|
+
});
|
|
685
|
+
if (currentValues.includes(undefined)) {
|
|
686
|
+
// fallback case
|
|
687
|
+
return { kind: 'any' };
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
values.push(...currentValues.map(v => v));
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
if (values.length === 1) {
|
|
694
|
+
// avoid overhead from union
|
|
695
|
+
return simplify(values[0], context);
|
|
696
|
+
}
|
|
697
|
+
return simplifyUnion({ kind: 'union', members: values }, context);
|
|
698
|
+
}
|
|
699
|
+
function simplifyUnion(typeDef, context) {
|
|
700
|
+
const filterCanonical = context.ctx.requireCanonical
|
|
701
|
+
&& typeDef.members.some(m => m.attributes?.some(a => a.name === 'canonical'));
|
|
702
|
+
const validMembers = typeDef.members
|
|
703
|
+
.filter(member => {
|
|
704
|
+
if (filterCanonical && !member.attributes?.some(a => a.name === 'canonical')) {
|
|
705
|
+
return false;
|
|
706
|
+
}
|
|
707
|
+
let keep = true;
|
|
708
|
+
handleAttributes(member.attributes, context.ctx, (handler, config) => {
|
|
709
|
+
if (!keep || !handler.filterElement) {
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
if (!handler.filterElement(config, context.ctx)) {
|
|
713
|
+
keep = false;
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
return keep;
|
|
717
|
+
});
|
|
718
|
+
if (validMembers.length === 1) {
|
|
719
|
+
// Avoid needing to manually add struct fields to parent if we are in a spread operation
|
|
720
|
+
return simplify(validMembers[0], context);
|
|
721
|
+
}
|
|
722
|
+
const members = [];
|
|
723
|
+
for (const member of validMembers) {
|
|
724
|
+
const simplified = simplify(member, {
|
|
725
|
+
...context,
|
|
726
|
+
structFields: undefined,
|
|
727
|
+
});
|
|
728
|
+
if (simplified.kind === 'union') {
|
|
729
|
+
members.push(...simplified.members);
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
members.push(simplified);
|
|
733
|
+
}
|
|
734
|
+
if (context.structFields && members.length > 1) {
|
|
735
|
+
return { kind: 'union', members: [] };
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
if (members.length === 1) {
|
|
739
|
+
// This should basically never happen, only when a union member resolves to an empty union.
|
|
740
|
+
// Apply struct fields to parent if we are inside a spread operation.
|
|
741
|
+
if (members[0].kind === 'struct' && context.structFields) {
|
|
742
|
+
for (const field of members[0].fields) {
|
|
743
|
+
if (field.key.kind === 'literal' && field.key.value.kind === 'string') {
|
|
744
|
+
context.structFields.literalFields.set(field.key.value.value, field);
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
context.structFields.complexFields.push(field);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return members[0];
|
|
752
|
+
}
|
|
753
|
+
return { ...typeDef, kind: 'union', members };
|
|
754
|
+
}
|
|
755
|
+
function simplifyStruct(typeDef, context) {
|
|
756
|
+
const literalFields = context.structFields?.literalFields
|
|
757
|
+
?? new Map();
|
|
758
|
+
let complexFields = context.structFields?.complexFields ?? [];
|
|
759
|
+
function addField(key, field) {
|
|
760
|
+
handleAttributes(field.attributes, context.ctx, (handler, config) => {
|
|
761
|
+
if (handler.mapField) {
|
|
762
|
+
field = handler.mapField(config, field, context.ctx);
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
if (typeof key === 'string') {
|
|
766
|
+
literalFields.set(key, field);
|
|
767
|
+
}
|
|
768
|
+
else if (key.kind === 'literal' && key.value.kind === 'string' && !key.attributes?.length) {
|
|
769
|
+
literalFields.set(key.value.value, field);
|
|
770
|
+
}
|
|
771
|
+
else if (key.kind === 'union') {
|
|
772
|
+
key.members.forEach(m => addField(m, { ...field, optional: true }));
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
// Only keep fields where the new key is not assignable to an existing field
|
|
776
|
+
complexFields = complexFields.filter(other => !McdocType.equals(key, typeof other.key === 'string'
|
|
777
|
+
? { kind: 'literal', value: { kind: 'string', value: other.key } }
|
|
778
|
+
: other.key));
|
|
779
|
+
complexFields.push({ ...field, key });
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
for (const field of typeDef.fields) {
|
|
783
|
+
let keep = true;
|
|
784
|
+
handleAttributes(field.attributes, context.ctx, (handler, config) => {
|
|
785
|
+
if (keep && handler.filterElement?.(config, context.ctx) === false) {
|
|
786
|
+
keep = false;
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
if (!keep) {
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
if (field.kind === 'pair') {
|
|
793
|
+
// Don't simplify the value here. We need to have the correct `node` and `parents`, which we
|
|
794
|
+
// cannot deterministically find for non-string keys.
|
|
795
|
+
// Instead, this method will be called by every struct child by the outer checking method.
|
|
796
|
+
const structKey = typeof field.key === 'string'
|
|
797
|
+
? field.key
|
|
798
|
+
: simplify(field.key, { ...context, isMember: true, typeArgs: [] });
|
|
799
|
+
const mappedField = context.typeMapping
|
|
800
|
+
? {
|
|
801
|
+
...field,
|
|
802
|
+
type: {
|
|
803
|
+
kind: 'mapped',
|
|
804
|
+
child: field.type,
|
|
805
|
+
mapping: context.typeMapping,
|
|
806
|
+
},
|
|
807
|
+
}
|
|
808
|
+
: field;
|
|
809
|
+
addField(structKey, mappedField);
|
|
810
|
+
}
|
|
811
|
+
else {
|
|
812
|
+
const simplifiedSpread = simplify(field.type, {
|
|
813
|
+
...context,
|
|
814
|
+
isMember: true,
|
|
815
|
+
typeArgs: [],
|
|
816
|
+
structFields: { literalFields, complexFields },
|
|
817
|
+
});
|
|
818
|
+
// Recursive calls above will already modify literal and complex fields passed by the
|
|
819
|
+
// context, so no need to handle them here.
|
|
820
|
+
// In case we are spreading sth that resolves to `any`, allow any other additional field.
|
|
821
|
+
if (simplifiedSpread.kind === 'any') {
|
|
822
|
+
addField({ kind: 'any' }, { kind: 'pair', key: { kind: 'any' }, type: { kind: 'any' } });
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
if (context.structFields) {
|
|
827
|
+
// In this case we are spreading a struct and the fields have been added to the parent map
|
|
828
|
+
// from context now.
|
|
829
|
+
return { kind: 'struct', fields: [] };
|
|
830
|
+
}
|
|
831
|
+
// Literal fields may still be assignable to complex fields,
|
|
832
|
+
// however this is currently not seen as an issue
|
|
833
|
+
return {
|
|
834
|
+
kind: 'struct',
|
|
835
|
+
fields: [
|
|
836
|
+
...complexFields,
|
|
837
|
+
...[...literalFields.entries()].map(([key, field]) => ({
|
|
838
|
+
...field,
|
|
839
|
+
key: { kind: 'literal', value: { kind: 'string', value: key } },
|
|
840
|
+
})),
|
|
841
|
+
],
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
function simplifyList(typeDef, context) {
|
|
845
|
+
if (!context.typeMapping) {
|
|
846
|
+
return typeDef;
|
|
847
|
+
}
|
|
848
|
+
return {
|
|
849
|
+
...typeDef,
|
|
850
|
+
item: { kind: 'mapped', child: typeDef.item, mapping: context.typeMapping },
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
function simplifyTuple(typeDef, context) {
|
|
854
|
+
if (!context.typeMapping) {
|
|
855
|
+
return typeDef;
|
|
856
|
+
}
|
|
857
|
+
return {
|
|
858
|
+
...typeDef,
|
|
859
|
+
items: typeDef.items.map(item => ({
|
|
860
|
+
kind: 'mapped',
|
|
861
|
+
child: item,
|
|
862
|
+
mapping: context.typeMapping,
|
|
863
|
+
})),
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
function simplifyEnum(typeDef, context) {
|
|
867
|
+
const filteredValues = typeDef.values.filter(value => {
|
|
868
|
+
let keep = true;
|
|
869
|
+
handleAttributes(value.attributes, context.ctx, (handler, config) => {
|
|
870
|
+
if (!keep || !handler.filterElement) {
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
if (!handler.filterElement(config, context.ctx)) {
|
|
874
|
+
keep = false;
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
return keep;
|
|
878
|
+
});
|
|
879
|
+
return { ...typeDef, enumKind: typeDef.enumKind ?? 'int', values: filteredValues };
|
|
880
|
+
}
|
|
881
|
+
function simplifyConcrete(typeDef, context) {
|
|
882
|
+
const simplifiedArgs = typeDef.typeArgs.map(arg => simplify(arg, context));
|
|
883
|
+
return simplify(typeDef.child, { ...context, typeArgs: simplifiedArgs });
|
|
884
|
+
}
|
|
885
|
+
function simplifyTemplate(typeDef, context) {
|
|
886
|
+
if (context.typeArgs?.length !== typeDef.typeParams.length) {
|
|
887
|
+
context.ctx.logger.warn(`Expected ${typeDef.typeParams.length} mcdoc type args for ${McdocType.toString(typeDef.child)}, but got ${context.typeArgs?.length ?? 0}`);
|
|
888
|
+
}
|
|
889
|
+
const mapping = Object.fromEntries(typeDef.typeParams.map((param, i) => {
|
|
890
|
+
const arg = context.typeArgs?.[i] ?? { kind: 'union', members: [] };
|
|
891
|
+
return [param.path, arg];
|
|
892
|
+
}));
|
|
893
|
+
return simplify(typeDef.child, { ...context, typeArgs: [], typeMapping: mapping });
|
|
894
|
+
}
|
|
895
|
+
function simplifyMapped(typeDef, context) {
|
|
896
|
+
// Mapped types that were created in simplify are always simplified
|
|
897
|
+
// types already, in which case this will be a cheap operation, but
|
|
898
|
+
// this is necessary for type safety
|
|
899
|
+
const simplifiedMapping = Object.fromEntries(Object.entries(typeDef.mapping).map(([path, param]) => {
|
|
900
|
+
return [path, simplify(param, context)];
|
|
901
|
+
}));
|
|
902
|
+
return simplify(typeDef.child, { ...context, typeMapping: simplifiedMapping });
|
|
903
|
+
}
|
|
904
|
+
function getValueType(type) {
|
|
905
|
+
switch (type.kind) {
|
|
906
|
+
case 'literal':
|
|
907
|
+
return { kind: type.value.kind };
|
|
908
|
+
case 'enum':
|
|
909
|
+
return { kind: type.enumKind };
|
|
910
|
+
default:
|
|
911
|
+
return type;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
//# sourceMappingURL=index.js.map
|