@formspec/build 0.1.0-alpha.28 → 0.1.0-alpha.30
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/dist/analyzer/class-analyzer.d.ts +11 -5
- package/dist/analyzer/class-analyzer.d.ts.map +1 -1
- package/dist/analyzer/program.d.ts +3 -2
- package/dist/analyzer/program.d.ts.map +1 -1
- package/dist/browser.cjs +485 -76
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +486 -77
- package/dist/browser.js.map +1 -1
- package/dist/build-alpha.d.ts +18 -2
- package/dist/build-beta.d.ts +18 -2
- package/dist/build-internal.d.ts +18 -2
- package/dist/build.d.ts +18 -2
- package/dist/canonicalize/chain-dsl-canonicalizer.d.ts +5 -2
- package/dist/canonicalize/chain-dsl-canonicalizer.d.ts.map +1 -1
- package/dist/canonicalize/tsdoc-canonicalizer.d.ts +5 -1
- package/dist/canonicalize/tsdoc-canonicalizer.d.ts.map +1 -1
- package/dist/cli.cjs +1031 -170
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1032 -171
- package/dist/cli.js.map +1 -1
- package/dist/generators/class-schema.d.ts +6 -1
- package/dist/generators/class-schema.d.ts.map +1 -1
- package/dist/generators/method-schema.d.ts.map +1 -1
- package/dist/generators/mixed-authoring.d.ts.map +1 -1
- package/dist/index.cjs +998 -170
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +999 -171
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +921 -155
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +922 -156
- package/dist/internals.js.map +1 -1
- package/dist/json-schema/generator.d.ts +3 -1
- package/dist/json-schema/generator.d.ts.map +1 -1
- package/dist/json-schema/ir-generator.d.ts.map +1 -1
- package/dist/metadata/collision-guards.d.ts +3 -0
- package/dist/metadata/collision-guards.d.ts.map +1 -0
- package/dist/metadata/index.d.ts +7 -0
- package/dist/metadata/index.d.ts.map +1 -0
- package/dist/metadata/policy.d.ts +11 -0
- package/dist/metadata/policy.d.ts.map +1 -0
- package/dist/metadata/resolve.d.ts +20 -0
- package/dist/metadata/resolve.d.ts.map +1 -0
- package/dist/ui-schema/generator.d.ts +11 -2
- package/dist/ui-schema/generator.d.ts.map +1 -1
- package/dist/ui-schema/ir-generator.d.ts +2 -1
- package/dist/ui-schema/ir-generator.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/internals.js
CHANGED
|
@@ -1,5 +1,281 @@
|
|
|
1
1
|
// src/canonicalize/chain-dsl-canonicalizer.ts
|
|
2
|
-
import { IR_VERSION } from "@formspec/core/internals";
|
|
2
|
+
import { IR_VERSION, _getFormSpecMetadataPolicy } from "@formspec/core/internals";
|
|
3
|
+
|
|
4
|
+
// src/metadata/policy.ts
|
|
5
|
+
var NOOP_INFLECT = () => "";
|
|
6
|
+
function normalizePluralization(input) {
|
|
7
|
+
if (input?.mode === "infer-if-missing") {
|
|
8
|
+
return {
|
|
9
|
+
mode: "infer-if-missing",
|
|
10
|
+
infer: () => "",
|
|
11
|
+
inflect: input.inflect
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
if (input?.mode === "require-explicit") {
|
|
15
|
+
return {
|
|
16
|
+
mode: "require-explicit",
|
|
17
|
+
infer: () => "",
|
|
18
|
+
inflect: NOOP_INFLECT
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
mode: "disabled",
|
|
23
|
+
infer: () => "",
|
|
24
|
+
inflect: NOOP_INFLECT
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function normalizeScalarPolicy(input) {
|
|
28
|
+
if (input?.mode === "infer-if-missing") {
|
|
29
|
+
return {
|
|
30
|
+
mode: "infer-if-missing",
|
|
31
|
+
infer: input.infer,
|
|
32
|
+
pluralization: normalizePluralization(input.pluralization)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (input?.mode === "require-explicit") {
|
|
36
|
+
return {
|
|
37
|
+
mode: "require-explicit",
|
|
38
|
+
infer: () => "",
|
|
39
|
+
pluralization: normalizePluralization(input.pluralization)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
mode: "disabled",
|
|
44
|
+
infer: () => "",
|
|
45
|
+
pluralization: normalizePluralization(input?.pluralization)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function normalizeDeclarationPolicy(input) {
|
|
49
|
+
return {
|
|
50
|
+
apiName: normalizeScalarPolicy(input?.apiName),
|
|
51
|
+
displayName: normalizeScalarPolicy(input?.displayName)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function normalizeMetadataPolicy(input) {
|
|
55
|
+
return {
|
|
56
|
+
type: normalizeDeclarationPolicy(input?.type),
|
|
57
|
+
field: normalizeDeclarationPolicy(input?.field),
|
|
58
|
+
method: normalizeDeclarationPolicy(input?.method)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function getDeclarationMetadataPolicy(policy, declarationKind) {
|
|
62
|
+
return policy[declarationKind];
|
|
63
|
+
}
|
|
64
|
+
function makeMetadataContext(surface, declarationKind, logicalName, buildContext) {
|
|
65
|
+
return {
|
|
66
|
+
surface,
|
|
67
|
+
declarationKind,
|
|
68
|
+
logicalName,
|
|
69
|
+
...buildContext !== void 0 && { buildContext }
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/metadata/resolve.ts
|
|
74
|
+
function toExplicitScalar(value) {
|
|
75
|
+
return value !== void 0 && value.trim() !== "" ? { value, source: "explicit" } : void 0;
|
|
76
|
+
}
|
|
77
|
+
function toExplicitResolvedMetadata(explicit) {
|
|
78
|
+
if (explicit === void 0) {
|
|
79
|
+
return void 0;
|
|
80
|
+
}
|
|
81
|
+
const apiName = toExplicitScalar(explicit.apiName);
|
|
82
|
+
const displayName = toExplicitScalar(explicit.displayName);
|
|
83
|
+
const apiNamePlural = toExplicitScalar(explicit.apiNamePlural);
|
|
84
|
+
const displayNamePlural = toExplicitScalar(explicit.displayNamePlural);
|
|
85
|
+
const metadata = {
|
|
86
|
+
...apiName !== void 0 && { apiName },
|
|
87
|
+
...displayName !== void 0 && { displayName },
|
|
88
|
+
...apiNamePlural !== void 0 && { apiNamePlural },
|
|
89
|
+
...displayNamePlural !== void 0 && { displayNamePlural }
|
|
90
|
+
};
|
|
91
|
+
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
92
|
+
}
|
|
93
|
+
function resolveScalar(current, policy, context, metadataLabel) {
|
|
94
|
+
if (current !== void 0) {
|
|
95
|
+
return current;
|
|
96
|
+
}
|
|
97
|
+
if (policy.mode === "require-explicit") {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Metadata policy requires explicit ${metadataLabel} for ${context.declarationKind} "${context.logicalName}" on the ${context.surface} surface.`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (policy.mode !== "infer-if-missing") {
|
|
103
|
+
return void 0;
|
|
104
|
+
}
|
|
105
|
+
const inferredValue = policy.infer(context);
|
|
106
|
+
return inferredValue.trim() !== "" ? { value: inferredValue, source: "inferred" } : void 0;
|
|
107
|
+
}
|
|
108
|
+
function resolvePlural(current, singular, policy, context, metadataLabel) {
|
|
109
|
+
if (current !== void 0) {
|
|
110
|
+
return current;
|
|
111
|
+
}
|
|
112
|
+
if (policy.mode === "require-explicit") {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Metadata policy requires explicit ${metadataLabel} for ${context.declarationKind} "${context.logicalName}" on the ${context.surface} surface.`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
if (singular === void 0 || policy.mode !== "infer-if-missing") {
|
|
118
|
+
return void 0;
|
|
119
|
+
}
|
|
120
|
+
const pluralValue = policy.inflect({ ...context, singular: singular.value });
|
|
121
|
+
return pluralValue.trim() !== "" ? { value: pluralValue, source: "inferred" } : void 0;
|
|
122
|
+
}
|
|
123
|
+
function resolveResolvedMetadata(current, policy, context) {
|
|
124
|
+
const apiName = resolveScalar(current?.apiName, policy.apiName, context, "apiName");
|
|
125
|
+
const displayName = resolveScalar(
|
|
126
|
+
current?.displayName,
|
|
127
|
+
policy.displayName,
|
|
128
|
+
context,
|
|
129
|
+
"displayName"
|
|
130
|
+
);
|
|
131
|
+
const apiNamePlural = resolvePlural(
|
|
132
|
+
current?.apiNamePlural,
|
|
133
|
+
apiName,
|
|
134
|
+
policy.apiName.pluralization,
|
|
135
|
+
context,
|
|
136
|
+
"apiNamePlural"
|
|
137
|
+
);
|
|
138
|
+
const displayNamePlural = resolvePlural(
|
|
139
|
+
current?.displayNamePlural,
|
|
140
|
+
displayName,
|
|
141
|
+
policy.displayName.pluralization,
|
|
142
|
+
context,
|
|
143
|
+
"displayNamePlural"
|
|
144
|
+
);
|
|
145
|
+
if (apiName === void 0 && displayName === void 0 && apiNamePlural === void 0 && displayNamePlural === void 0) {
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
...apiName !== void 0 && { apiName },
|
|
150
|
+
...displayName !== void 0 && { displayName },
|
|
151
|
+
...apiNamePlural !== void 0 && { apiNamePlural },
|
|
152
|
+
...displayNamePlural !== void 0 && { displayNamePlural }
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function resolveTypeNodeMetadata(type, options) {
|
|
156
|
+
switch (type.kind) {
|
|
157
|
+
case "array":
|
|
158
|
+
return {
|
|
159
|
+
...type,
|
|
160
|
+
items: resolveTypeNodeMetadata(type.items, options)
|
|
161
|
+
};
|
|
162
|
+
case "object":
|
|
163
|
+
return {
|
|
164
|
+
...type,
|
|
165
|
+
properties: type.properties.map((property) => resolveObjectPropertyMetadata(property, options))
|
|
166
|
+
};
|
|
167
|
+
case "record":
|
|
168
|
+
return {
|
|
169
|
+
...type,
|
|
170
|
+
valueType: resolveTypeNodeMetadata(type.valueType, options)
|
|
171
|
+
};
|
|
172
|
+
case "union":
|
|
173
|
+
return {
|
|
174
|
+
...type,
|
|
175
|
+
members: type.members.map((member) => resolveTypeNodeMetadata(member, options))
|
|
176
|
+
};
|
|
177
|
+
case "reference":
|
|
178
|
+
case "primitive":
|
|
179
|
+
case "enum":
|
|
180
|
+
case "dynamic":
|
|
181
|
+
case "custom":
|
|
182
|
+
return type;
|
|
183
|
+
default: {
|
|
184
|
+
const _exhaustive = type;
|
|
185
|
+
return _exhaustive;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function resolveObjectPropertyMetadata(property, options) {
|
|
190
|
+
const metadata = resolveResolvedMetadata(property.metadata, options.policy.field, {
|
|
191
|
+
surface: options.surface,
|
|
192
|
+
declarationKind: "field",
|
|
193
|
+
logicalName: property.name,
|
|
194
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
195
|
+
});
|
|
196
|
+
return {
|
|
197
|
+
...property,
|
|
198
|
+
...metadata !== void 0 && { metadata },
|
|
199
|
+
type: resolveTypeNodeMetadata(property.type, options)
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function resolveFieldMetadataNode(field, options) {
|
|
203
|
+
const metadata = resolveResolvedMetadata(field.metadata, options.policy.field, {
|
|
204
|
+
surface: options.surface,
|
|
205
|
+
declarationKind: "field",
|
|
206
|
+
logicalName: field.name,
|
|
207
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
208
|
+
});
|
|
209
|
+
return {
|
|
210
|
+
...field,
|
|
211
|
+
...metadata !== void 0 && { metadata },
|
|
212
|
+
type: resolveTypeNodeMetadata(field.type, options)
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
function resolveFormElementMetadata(element, options) {
|
|
216
|
+
switch (element.kind) {
|
|
217
|
+
case "field":
|
|
218
|
+
return resolveFieldMetadataNode(element, options);
|
|
219
|
+
case "group":
|
|
220
|
+
return {
|
|
221
|
+
...element,
|
|
222
|
+
elements: element.elements.map((child) => resolveFormElementMetadata(child, options))
|
|
223
|
+
};
|
|
224
|
+
case "conditional":
|
|
225
|
+
return {
|
|
226
|
+
...element,
|
|
227
|
+
elements: element.elements.map((child) => resolveFormElementMetadata(child, options))
|
|
228
|
+
};
|
|
229
|
+
default: {
|
|
230
|
+
const _exhaustive = element;
|
|
231
|
+
return _exhaustive;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function resolveTypeDefinitionMetadata(typeDefinition, options) {
|
|
236
|
+
const metadata = resolveResolvedMetadata(typeDefinition.metadata, options.policy.type, {
|
|
237
|
+
surface: options.surface,
|
|
238
|
+
declarationKind: "type",
|
|
239
|
+
logicalName: typeDefinition.name,
|
|
240
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
241
|
+
});
|
|
242
|
+
return {
|
|
243
|
+
...typeDefinition,
|
|
244
|
+
...metadata !== void 0 && { metadata },
|
|
245
|
+
type: resolveTypeNodeMetadata(typeDefinition.type, options)
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function resolveMetadata(explicit, policy, context) {
|
|
249
|
+
return resolveResolvedMetadata(toExplicitResolvedMetadata(explicit), policy, context);
|
|
250
|
+
}
|
|
251
|
+
function getSerializedName(logicalName, metadata) {
|
|
252
|
+
return metadata?.apiName?.value ?? logicalName;
|
|
253
|
+
}
|
|
254
|
+
function getDisplayName(metadata) {
|
|
255
|
+
return metadata?.displayName?.value;
|
|
256
|
+
}
|
|
257
|
+
function resolveFormIRMetadata(ir, options) {
|
|
258
|
+
const rootLogicalName = options.rootLogicalName ?? ir.name ?? "FormSpec";
|
|
259
|
+
const metadata = resolveResolvedMetadata(ir.metadata, options.policy.type, {
|
|
260
|
+
surface: options.surface,
|
|
261
|
+
declarationKind: "type",
|
|
262
|
+
logicalName: rootLogicalName,
|
|
263
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
264
|
+
});
|
|
265
|
+
return {
|
|
266
|
+
...ir,
|
|
267
|
+
...metadata !== void 0 && { metadata },
|
|
268
|
+
elements: ir.elements.map((element) => resolveFormElementMetadata(element, options)),
|
|
269
|
+
typeRegistry: Object.fromEntries(
|
|
270
|
+
Object.entries(ir.typeRegistry).map(([name, definition]) => [
|
|
271
|
+
name,
|
|
272
|
+
resolveTypeDefinitionMetadata(definition, options)
|
|
273
|
+
])
|
|
274
|
+
)
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/canonicalize/chain-dsl-canonicalizer.ts
|
|
3
279
|
var CHAIN_DSL_PROVENANCE = {
|
|
4
280
|
surface: "chain-dsl",
|
|
5
281
|
file: "",
|
|
@@ -15,57 +291,60 @@ function isConditional(el) {
|
|
|
15
291
|
function isField(el) {
|
|
16
292
|
return el._type === "field";
|
|
17
293
|
}
|
|
18
|
-
function canonicalizeChainDSL(form) {
|
|
294
|
+
function canonicalizeChainDSL(form, options) {
|
|
295
|
+
const metadataPolicy = normalizeMetadataPolicy(
|
|
296
|
+
options?.metadata ?? _getFormSpecMetadataPolicy(form)
|
|
297
|
+
);
|
|
19
298
|
return {
|
|
20
299
|
kind: "form-ir",
|
|
21
300
|
irVersion: IR_VERSION,
|
|
22
|
-
elements: canonicalizeElements(form.elements),
|
|
301
|
+
elements: canonicalizeElements(form.elements, metadataPolicy),
|
|
23
302
|
rootAnnotations: [],
|
|
24
303
|
typeRegistry: {},
|
|
25
304
|
provenance: CHAIN_DSL_PROVENANCE
|
|
26
305
|
};
|
|
27
306
|
}
|
|
28
|
-
function canonicalizeElements(elements) {
|
|
29
|
-
return elements.map(canonicalizeElement);
|
|
307
|
+
function canonicalizeElements(elements, metadataPolicy) {
|
|
308
|
+
return elements.map((element) => canonicalizeElement(element, metadataPolicy));
|
|
30
309
|
}
|
|
31
|
-
function canonicalizeElement(element) {
|
|
310
|
+
function canonicalizeElement(element, metadataPolicy) {
|
|
32
311
|
if (isField(element)) {
|
|
33
|
-
return canonicalizeField(element);
|
|
312
|
+
return canonicalizeField(element, metadataPolicy);
|
|
34
313
|
}
|
|
35
314
|
if (isGroup(element)) {
|
|
36
|
-
return canonicalizeGroup(element);
|
|
315
|
+
return canonicalizeGroup(element, metadataPolicy);
|
|
37
316
|
}
|
|
38
317
|
if (isConditional(element)) {
|
|
39
|
-
return canonicalizeConditional(element);
|
|
318
|
+
return canonicalizeConditional(element, metadataPolicy);
|
|
40
319
|
}
|
|
41
320
|
const _exhaustive = element;
|
|
42
321
|
throw new Error(`Unknown element type: ${JSON.stringify(_exhaustive)}`);
|
|
43
322
|
}
|
|
44
|
-
function canonicalizeField(field) {
|
|
323
|
+
function canonicalizeField(field, metadataPolicy) {
|
|
45
324
|
switch (field._field) {
|
|
46
325
|
case "text":
|
|
47
|
-
return canonicalizeTextField(field);
|
|
326
|
+
return canonicalizeTextField(field, metadataPolicy);
|
|
48
327
|
case "number":
|
|
49
|
-
return canonicalizeNumberField(field);
|
|
328
|
+
return canonicalizeNumberField(field, metadataPolicy);
|
|
50
329
|
case "boolean":
|
|
51
|
-
return canonicalizeBooleanField(field);
|
|
330
|
+
return canonicalizeBooleanField(field, metadataPolicy);
|
|
52
331
|
case "enum":
|
|
53
|
-
return canonicalizeStaticEnumField(field);
|
|
332
|
+
return canonicalizeStaticEnumField(field, metadataPolicy);
|
|
54
333
|
case "dynamic_enum":
|
|
55
|
-
return canonicalizeDynamicEnumField(field);
|
|
334
|
+
return canonicalizeDynamicEnumField(field, metadataPolicy);
|
|
56
335
|
case "dynamic_schema":
|
|
57
|
-
return canonicalizeDynamicSchemaField(field);
|
|
336
|
+
return canonicalizeDynamicSchemaField(field, metadataPolicy);
|
|
58
337
|
case "array":
|
|
59
|
-
return canonicalizeArrayField(field);
|
|
338
|
+
return canonicalizeArrayField(field, metadataPolicy);
|
|
60
339
|
case "object":
|
|
61
|
-
return canonicalizeObjectField(field);
|
|
340
|
+
return canonicalizeObjectField(field, metadataPolicy);
|
|
62
341
|
default: {
|
|
63
342
|
const _exhaustive = field;
|
|
64
343
|
throw new Error(`Unknown field type: ${JSON.stringify(_exhaustive)}`);
|
|
65
344
|
}
|
|
66
345
|
}
|
|
67
346
|
}
|
|
68
|
-
function canonicalizeTextField(field) {
|
|
347
|
+
function canonicalizeTextField(field, metadataPolicy) {
|
|
69
348
|
const type = { kind: "primitive", primitiveKind: "string" };
|
|
70
349
|
const constraints = [];
|
|
71
350
|
if (field.minLength !== void 0) {
|
|
@@ -97,13 +376,14 @@ function canonicalizeTextField(field) {
|
|
|
97
376
|
}
|
|
98
377
|
return buildFieldNode(
|
|
99
378
|
field.name,
|
|
379
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
100
380
|
type,
|
|
101
381
|
field.required,
|
|
102
|
-
buildAnnotations(field
|
|
382
|
+
buildAnnotations(getExplicitDisplayName(field), field.placeholder),
|
|
103
383
|
constraints
|
|
104
384
|
);
|
|
105
385
|
}
|
|
106
|
-
function canonicalizeNumberField(field) {
|
|
386
|
+
function canonicalizeNumberField(field, metadataPolicy) {
|
|
107
387
|
const type = { kind: "primitive", primitiveKind: "number" };
|
|
108
388
|
const constraints = [];
|
|
109
389
|
if (field.min !== void 0) {
|
|
@@ -135,17 +415,24 @@ function canonicalizeNumberField(field) {
|
|
|
135
415
|
}
|
|
136
416
|
return buildFieldNode(
|
|
137
417
|
field.name,
|
|
418
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
138
419
|
type,
|
|
139
420
|
field.required,
|
|
140
|
-
buildAnnotations(field
|
|
421
|
+
buildAnnotations(getExplicitDisplayName(field)),
|
|
141
422
|
constraints
|
|
142
423
|
);
|
|
143
424
|
}
|
|
144
|
-
function canonicalizeBooleanField(field) {
|
|
425
|
+
function canonicalizeBooleanField(field, metadataPolicy) {
|
|
145
426
|
const type = { kind: "primitive", primitiveKind: "boolean" };
|
|
146
|
-
return buildFieldNode(
|
|
427
|
+
return buildFieldNode(
|
|
428
|
+
field.name,
|
|
429
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
430
|
+
type,
|
|
431
|
+
field.required,
|
|
432
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
433
|
+
);
|
|
147
434
|
}
|
|
148
|
-
function canonicalizeStaticEnumField(field) {
|
|
435
|
+
function canonicalizeStaticEnumField(field, metadataPolicy) {
|
|
149
436
|
const members = field.options.map((opt) => {
|
|
150
437
|
if (typeof opt === "string") {
|
|
151
438
|
return { value: opt };
|
|
@@ -153,28 +440,46 @@ function canonicalizeStaticEnumField(field) {
|
|
|
153
440
|
return { value: opt.id, displayName: opt.label };
|
|
154
441
|
});
|
|
155
442
|
const type = { kind: "enum", members };
|
|
156
|
-
return buildFieldNode(
|
|
443
|
+
return buildFieldNode(
|
|
444
|
+
field.name,
|
|
445
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
446
|
+
type,
|
|
447
|
+
field.required,
|
|
448
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
449
|
+
);
|
|
157
450
|
}
|
|
158
|
-
function canonicalizeDynamicEnumField(field) {
|
|
451
|
+
function canonicalizeDynamicEnumField(field, metadataPolicy) {
|
|
159
452
|
const type = {
|
|
160
453
|
kind: "dynamic",
|
|
161
454
|
dynamicKind: "enum",
|
|
162
455
|
sourceKey: field.source,
|
|
163
456
|
parameterFields: field.params ? [...field.params] : []
|
|
164
457
|
};
|
|
165
|
-
return buildFieldNode(
|
|
458
|
+
return buildFieldNode(
|
|
459
|
+
field.name,
|
|
460
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
461
|
+
type,
|
|
462
|
+
field.required,
|
|
463
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
464
|
+
);
|
|
166
465
|
}
|
|
167
|
-
function canonicalizeDynamicSchemaField(field) {
|
|
466
|
+
function canonicalizeDynamicSchemaField(field, metadataPolicy) {
|
|
168
467
|
const type = {
|
|
169
468
|
kind: "dynamic",
|
|
170
469
|
dynamicKind: "schema",
|
|
171
470
|
sourceKey: field.schemaSource,
|
|
172
471
|
parameterFields: []
|
|
173
472
|
};
|
|
174
|
-
return buildFieldNode(
|
|
473
|
+
return buildFieldNode(
|
|
474
|
+
field.name,
|
|
475
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
476
|
+
type,
|
|
477
|
+
field.required,
|
|
478
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
479
|
+
);
|
|
175
480
|
}
|
|
176
|
-
function canonicalizeArrayField(field) {
|
|
177
|
-
const itemProperties = buildObjectProperties(field.items);
|
|
481
|
+
function canonicalizeArrayField(field, metadataPolicy) {
|
|
482
|
+
const itemProperties = buildObjectProperties(field.items, metadataPolicy);
|
|
178
483
|
const itemsType = {
|
|
179
484
|
kind: "object",
|
|
180
485
|
properties: itemProperties,
|
|
@@ -202,37 +507,44 @@ function canonicalizeArrayField(field) {
|
|
|
202
507
|
}
|
|
203
508
|
return buildFieldNode(
|
|
204
509
|
field.name,
|
|
510
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
205
511
|
type,
|
|
206
512
|
field.required,
|
|
207
|
-
buildAnnotations(field
|
|
513
|
+
buildAnnotations(getExplicitDisplayName(field)),
|
|
208
514
|
constraints
|
|
209
515
|
);
|
|
210
516
|
}
|
|
211
|
-
function canonicalizeObjectField(field) {
|
|
212
|
-
const properties = buildObjectProperties(field.properties);
|
|
517
|
+
function canonicalizeObjectField(field, metadataPolicy) {
|
|
518
|
+
const properties = buildObjectProperties(field.properties, metadataPolicy);
|
|
213
519
|
const type = {
|
|
214
520
|
kind: "object",
|
|
215
521
|
properties,
|
|
216
522
|
additionalProperties: true
|
|
217
523
|
};
|
|
218
|
-
return buildFieldNode(
|
|
524
|
+
return buildFieldNode(
|
|
525
|
+
field.name,
|
|
526
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
527
|
+
type,
|
|
528
|
+
field.required,
|
|
529
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
530
|
+
);
|
|
219
531
|
}
|
|
220
|
-
function canonicalizeGroup(g) {
|
|
532
|
+
function canonicalizeGroup(g, metadataPolicy) {
|
|
221
533
|
return {
|
|
222
534
|
kind: "group",
|
|
223
535
|
label: g.label,
|
|
224
|
-
elements: canonicalizeElements(g.elements),
|
|
536
|
+
elements: canonicalizeElements(g.elements, metadataPolicy),
|
|
225
537
|
provenance: CHAIN_DSL_PROVENANCE
|
|
226
538
|
};
|
|
227
539
|
}
|
|
228
|
-
function canonicalizeConditional(c) {
|
|
540
|
+
function canonicalizeConditional(c, metadataPolicy) {
|
|
229
541
|
return {
|
|
230
542
|
kind: "conditional",
|
|
231
543
|
fieldName: c.field,
|
|
232
544
|
// Conditional values from the chain DSL are JSON-serializable primitives
|
|
233
545
|
// (strings, numbers, booleans) produced by the `is()` predicate helper.
|
|
234
546
|
value: assertJsonValue(c.value),
|
|
235
|
-
elements: canonicalizeElements(c.elements),
|
|
547
|
+
elements: canonicalizeElements(c.elements, metadataPolicy),
|
|
236
548
|
provenance: CHAIN_DSL_PROVENANCE
|
|
237
549
|
};
|
|
238
550
|
}
|
|
@@ -252,10 +564,11 @@ function assertJsonValue(v) {
|
|
|
252
564
|
}
|
|
253
565
|
throw new TypeError(`Conditional value is not a valid JsonValue: ${typeof v}`);
|
|
254
566
|
}
|
|
255
|
-
function buildFieldNode(name, type, required, annotations, constraints = []) {
|
|
567
|
+
function buildFieldNode(name, metadata, type, required, annotations, constraints = []) {
|
|
256
568
|
return {
|
|
257
569
|
kind: "field",
|
|
258
570
|
name,
|
|
571
|
+
...metadata !== void 0 && { metadata },
|
|
259
572
|
type,
|
|
260
573
|
required: required === true,
|
|
261
574
|
constraints,
|
|
@@ -285,13 +598,14 @@ function buildAnnotations(label, placeholder) {
|
|
|
285
598
|
}
|
|
286
599
|
return annotations;
|
|
287
600
|
}
|
|
288
|
-
function buildObjectProperties(elements, insideConditional = false) {
|
|
601
|
+
function buildObjectProperties(elements, metadataPolicy, insideConditional = false) {
|
|
289
602
|
const properties = [];
|
|
290
603
|
for (const el of elements) {
|
|
291
604
|
if (isField(el)) {
|
|
292
|
-
const fieldNode = canonicalizeField(el);
|
|
605
|
+
const fieldNode = canonicalizeField(el, metadataPolicy);
|
|
293
606
|
properties.push({
|
|
294
607
|
name: fieldNode.name,
|
|
608
|
+
...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
|
|
295
609
|
type: fieldNode.type,
|
|
296
610
|
// Fields inside a conditional branch are always optional in the
|
|
297
611
|
// data schema, regardless of their `required` flag — the condition
|
|
@@ -302,17 +616,34 @@ function buildObjectProperties(elements, insideConditional = false) {
|
|
|
302
616
|
provenance: CHAIN_DSL_PROVENANCE
|
|
303
617
|
});
|
|
304
618
|
} else if (isGroup(el)) {
|
|
305
|
-
properties.push(...buildObjectProperties(el.elements, insideConditional));
|
|
619
|
+
properties.push(...buildObjectProperties(el.elements, metadataPolicy, insideConditional));
|
|
306
620
|
} else if (isConditional(el)) {
|
|
307
|
-
properties.push(...buildObjectProperties(el.elements, true));
|
|
621
|
+
properties.push(...buildObjectProperties(el.elements, metadataPolicy, true));
|
|
308
622
|
}
|
|
309
623
|
}
|
|
310
624
|
return properties;
|
|
311
625
|
}
|
|
626
|
+
function getExplicitDisplayName(field) {
|
|
627
|
+
if (field.label !== void 0 && field.displayName !== void 0) {
|
|
628
|
+
throw new Error('Chain DSL fields cannot specify both "label" and "displayName".');
|
|
629
|
+
}
|
|
630
|
+
return field.displayName ?? field.label;
|
|
631
|
+
}
|
|
632
|
+
function resolveFieldMetadata(logicalName, field, metadataPolicy) {
|
|
633
|
+
const displayName = getExplicitDisplayName(field);
|
|
634
|
+
return resolveMetadata(
|
|
635
|
+
{
|
|
636
|
+
...field.apiName !== void 0 && { apiName: field.apiName },
|
|
637
|
+
...displayName !== void 0 && { displayName }
|
|
638
|
+
},
|
|
639
|
+
getDeclarationMetadataPolicy(metadataPolicy, "field"),
|
|
640
|
+
makeMetadataContext("chain-dsl", "field", logicalName)
|
|
641
|
+
);
|
|
642
|
+
}
|
|
312
643
|
|
|
313
644
|
// src/canonicalize/tsdoc-canonicalizer.ts
|
|
314
645
|
import { IR_VERSION as IR_VERSION2 } from "@formspec/core/internals";
|
|
315
|
-
function canonicalizeTSDoc(analysis, source) {
|
|
646
|
+
function canonicalizeTSDoc(analysis, source, options) {
|
|
316
647
|
const file = source?.file ?? "";
|
|
317
648
|
const provenance = {
|
|
318
649
|
surface: "tsdoc",
|
|
@@ -321,15 +652,21 @@ function canonicalizeTSDoc(analysis, source) {
|
|
|
321
652
|
column: 0
|
|
322
653
|
};
|
|
323
654
|
const elements = assembleElements(analysis.fields, analysis.fieldLayouts, provenance);
|
|
324
|
-
|
|
655
|
+
const ir = {
|
|
325
656
|
kind: "form-ir",
|
|
657
|
+
name: analysis.name,
|
|
326
658
|
irVersion: IR_VERSION2,
|
|
327
659
|
elements,
|
|
660
|
+
...analysis.metadata !== void 0 && { metadata: analysis.metadata },
|
|
328
661
|
typeRegistry: analysis.typeRegistry,
|
|
329
662
|
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { rootAnnotations: analysis.annotations },
|
|
330
663
|
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { annotations: analysis.annotations },
|
|
331
664
|
provenance
|
|
332
665
|
};
|
|
666
|
+
return resolveFormIRMetadata(ir, {
|
|
667
|
+
policy: normalizeMetadataPolicy(options?.metadata),
|
|
668
|
+
surface: "tsdoc"
|
|
669
|
+
});
|
|
333
670
|
}
|
|
334
671
|
function assembleElements(fields, layouts, provenance) {
|
|
335
672
|
const elements = [];
|
|
@@ -1297,7 +1634,76 @@ function makeParseOptions(extensionRegistry, fieldType, checker, subjectType, ho
|
|
|
1297
1634
|
...hostType !== void 0 && { hostType }
|
|
1298
1635
|
};
|
|
1299
1636
|
}
|
|
1300
|
-
function
|
|
1637
|
+
function makeExplicitScalarMetadata(value) {
|
|
1638
|
+
return value === void 0 || value === "" ? void 0 : { value, source: "explicit" };
|
|
1639
|
+
}
|
|
1640
|
+
function extractExplicitMetadata(node) {
|
|
1641
|
+
let apiName;
|
|
1642
|
+
let displayName;
|
|
1643
|
+
let apiNamePlural;
|
|
1644
|
+
let displayNamePlural;
|
|
1645
|
+
for (const tag of getLeadingParsedTags(node)) {
|
|
1646
|
+
const value = tag.argumentText.trim();
|
|
1647
|
+
if (value === "") {
|
|
1648
|
+
continue;
|
|
1649
|
+
}
|
|
1650
|
+
if (tag.normalizedTagName === "apiName") {
|
|
1651
|
+
if (tag.target === null) {
|
|
1652
|
+
apiName ??= value;
|
|
1653
|
+
} else if (tag.target.kind === "variant") {
|
|
1654
|
+
if (tag.target.rawText === "singular") {
|
|
1655
|
+
apiName ??= value;
|
|
1656
|
+
} else if (tag.target.rawText === "plural") {
|
|
1657
|
+
apiNamePlural ??= value;
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
continue;
|
|
1661
|
+
}
|
|
1662
|
+
if (tag.normalizedTagName === "displayName") {
|
|
1663
|
+
if (tag.target === null) {
|
|
1664
|
+
displayName ??= value;
|
|
1665
|
+
} else if (tag.target.kind === "variant") {
|
|
1666
|
+
if (tag.target.rawText === "singular") {
|
|
1667
|
+
displayName ??= value;
|
|
1668
|
+
} else if (tag.target.rawText === "plural") {
|
|
1669
|
+
displayNamePlural ??= value;
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
const resolvedApiName = makeExplicitScalarMetadata(apiName);
|
|
1675
|
+
const resolvedDisplayName = makeExplicitScalarMetadata(displayName);
|
|
1676
|
+
const resolvedApiNamePlural = makeExplicitScalarMetadata(apiNamePlural);
|
|
1677
|
+
const resolvedDisplayNamePlural = makeExplicitScalarMetadata(displayNamePlural);
|
|
1678
|
+
const metadata = {
|
|
1679
|
+
...resolvedApiName !== void 0 && { apiName: resolvedApiName },
|
|
1680
|
+
...resolvedDisplayName !== void 0 && { displayName: resolvedDisplayName },
|
|
1681
|
+
...resolvedApiNamePlural !== void 0 && { apiNamePlural: resolvedApiNamePlural },
|
|
1682
|
+
...resolvedDisplayNamePlural !== void 0 && {
|
|
1683
|
+
displayNamePlural: resolvedDisplayNamePlural
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
return Object.keys(metadata).length === 0 ? void 0 : metadata;
|
|
1687
|
+
}
|
|
1688
|
+
function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, buildContext) {
|
|
1689
|
+
const explicit = extractExplicitMetadata(node);
|
|
1690
|
+
return resolveMetadata(
|
|
1691
|
+
{
|
|
1692
|
+
...explicit?.apiName !== void 0 && { apiName: explicit.apiName.value },
|
|
1693
|
+
...explicit?.displayName !== void 0 && { displayName: explicit.displayName.value },
|
|
1694
|
+
...explicit?.apiNamePlural !== void 0 && {
|
|
1695
|
+
apiNamePlural: explicit.apiNamePlural.value
|
|
1696
|
+
},
|
|
1697
|
+
...explicit?.displayNamePlural !== void 0 && {
|
|
1698
|
+
displayNamePlural: explicit.displayNamePlural.value
|
|
1699
|
+
}
|
|
1700
|
+
},
|
|
1701
|
+
getDeclarationMetadataPolicy(metadataPolicy, declarationKind),
|
|
1702
|
+
makeMetadataContext("tsdoc", declarationKind, logicalName, buildContext)
|
|
1703
|
+
);
|
|
1704
|
+
}
|
|
1705
|
+
function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy) {
|
|
1706
|
+
const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
|
|
1301
1707
|
const name = classDecl.name?.text ?? "AnonymousClass";
|
|
1302
1708
|
const fields = [];
|
|
1303
1709
|
const fieldLayouts = [];
|
|
@@ -1324,6 +1730,7 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
1324
1730
|
visiting,
|
|
1325
1731
|
diagnostics,
|
|
1326
1732
|
classType,
|
|
1733
|
+
normalizedMetadataPolicy,
|
|
1327
1734
|
extensionRegistry
|
|
1328
1735
|
);
|
|
1329
1736
|
if (fieldNode) {
|
|
@@ -1348,10 +1755,18 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
1348
1755
|
classType,
|
|
1349
1756
|
checker,
|
|
1350
1757
|
file,
|
|
1351
|
-
diagnostics
|
|
1758
|
+
diagnostics,
|
|
1759
|
+
normalizedMetadataPolicy
|
|
1352
1760
|
);
|
|
1761
|
+
const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, classDecl, {
|
|
1762
|
+
checker,
|
|
1763
|
+
declaration: classDecl,
|
|
1764
|
+
subjectType: classType,
|
|
1765
|
+
hostType: classType
|
|
1766
|
+
});
|
|
1353
1767
|
return {
|
|
1354
1768
|
name,
|
|
1769
|
+
...metadata !== void 0 && { metadata },
|
|
1355
1770
|
fields: specializedFields,
|
|
1356
1771
|
fieldLayouts,
|
|
1357
1772
|
typeRegistry,
|
|
@@ -1361,7 +1776,8 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
1361
1776
|
staticMethods
|
|
1362
1777
|
};
|
|
1363
1778
|
}
|
|
1364
|
-
function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry) {
|
|
1779
|
+
function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy) {
|
|
1780
|
+
const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
|
|
1365
1781
|
const name = interfaceDecl.name.text;
|
|
1366
1782
|
const fields = [];
|
|
1367
1783
|
const typeRegistry = {};
|
|
@@ -1385,6 +1801,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
1385
1801
|
visiting,
|
|
1386
1802
|
diagnostics,
|
|
1387
1803
|
interfaceType,
|
|
1804
|
+
normalizedMetadataPolicy,
|
|
1388
1805
|
extensionRegistry
|
|
1389
1806
|
);
|
|
1390
1807
|
if (fieldNode) {
|
|
@@ -1398,11 +1815,19 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
1398
1815
|
interfaceType,
|
|
1399
1816
|
checker,
|
|
1400
1817
|
file,
|
|
1401
|
-
diagnostics
|
|
1818
|
+
diagnostics,
|
|
1819
|
+
normalizedMetadataPolicy
|
|
1402
1820
|
);
|
|
1403
1821
|
const fieldLayouts = specializedFields.map(() => ({}));
|
|
1822
|
+
const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, interfaceDecl, {
|
|
1823
|
+
checker,
|
|
1824
|
+
declaration: interfaceDecl,
|
|
1825
|
+
subjectType: interfaceType,
|
|
1826
|
+
hostType: interfaceType
|
|
1827
|
+
});
|
|
1404
1828
|
return {
|
|
1405
1829
|
name,
|
|
1830
|
+
...metadata !== void 0 && { metadata },
|
|
1406
1831
|
fields: specializedFields,
|
|
1407
1832
|
fieldLayouts,
|
|
1408
1833
|
typeRegistry,
|
|
@@ -1412,7 +1837,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
1412
1837
|
staticMethods: []
|
|
1413
1838
|
};
|
|
1414
1839
|
}
|
|
1415
|
-
function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
|
|
1840
|
+
function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry, metadataPolicy) {
|
|
1416
1841
|
if (!ts3.isTypeLiteralNode(typeAlias.type)) {
|
|
1417
1842
|
const sourceFile = typeAlias.getSourceFile();
|
|
1418
1843
|
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
@@ -1422,6 +1847,8 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
1422
1847
|
error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object type literal (found ${kindDesc})`
|
|
1423
1848
|
};
|
|
1424
1849
|
}
|
|
1850
|
+
const typeLiteral = typeAlias.type;
|
|
1851
|
+
const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
|
|
1425
1852
|
const name = typeAlias.name.text;
|
|
1426
1853
|
const fields = [];
|
|
1427
1854
|
const typeRegistry = {};
|
|
@@ -1435,7 +1862,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
1435
1862
|
const annotations = [...typeAliasDoc.annotations];
|
|
1436
1863
|
diagnostics.push(...typeAliasDoc.diagnostics);
|
|
1437
1864
|
const visiting = /* @__PURE__ */ new Set();
|
|
1438
|
-
for (const member of
|
|
1865
|
+
for (const member of typeLiteral.members) {
|
|
1439
1866
|
if (ts3.isPropertySignature(member)) {
|
|
1440
1867
|
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1441
1868
|
member,
|
|
@@ -1445,6 +1872,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
1445
1872
|
visiting,
|
|
1446
1873
|
diagnostics,
|
|
1447
1874
|
aliasType,
|
|
1875
|
+
normalizedMetadataPolicy,
|
|
1448
1876
|
extensionRegistry
|
|
1449
1877
|
);
|
|
1450
1878
|
if (fieldNode) {
|
|
@@ -1458,12 +1886,20 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
1458
1886
|
aliasType,
|
|
1459
1887
|
checker,
|
|
1460
1888
|
file,
|
|
1461
|
-
diagnostics
|
|
1889
|
+
diagnostics,
|
|
1890
|
+
normalizedMetadataPolicy
|
|
1462
1891
|
);
|
|
1892
|
+
const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, typeAlias, {
|
|
1893
|
+
checker,
|
|
1894
|
+
declaration: typeAlias,
|
|
1895
|
+
subjectType: aliasType,
|
|
1896
|
+
hostType: aliasType
|
|
1897
|
+
});
|
|
1463
1898
|
return {
|
|
1464
1899
|
ok: true,
|
|
1465
1900
|
analysis: {
|
|
1466
1901
|
name,
|
|
1902
|
+
...metadata !== void 0 && { metadata },
|
|
1467
1903
|
fields: specializedFields,
|
|
1468
1904
|
fieldLayouts: specializedFields.map(() => ({})),
|
|
1469
1905
|
typeRegistry,
|
|
@@ -1503,31 +1939,20 @@ function getLeadingParsedTags(node) {
|
|
|
1503
1939
|
}
|
|
1504
1940
|
return parsedTags;
|
|
1505
1941
|
}
|
|
1506
|
-
function
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
return member;
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
return null;
|
|
1514
|
-
}
|
|
1515
|
-
if (ts3.isInterfaceDeclaration(node)) {
|
|
1516
|
-
for (const member of node.members) {
|
|
1517
|
-
if (ts3.isPropertySignature(member) && ts3.isIdentifier(member.name) && member.name.text === fieldName) {
|
|
1518
|
-
return member;
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1942
|
+
function resolveDiscriminatorProperty(node, checker, fieldName) {
|
|
1943
|
+
const subjectType = checker.getTypeAtLocation(node);
|
|
1944
|
+
const propertySymbol = subjectType.getProperty(fieldName);
|
|
1945
|
+
if (propertySymbol === void 0) {
|
|
1521
1946
|
return null;
|
|
1522
1947
|
}
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1948
|
+
const declaration = propertySymbol.valueDeclaration ?? propertySymbol.declarations?.find(
|
|
1949
|
+
(candidate) => ts3.isPropertyDeclaration(candidate) || ts3.isPropertySignature(candidate)
|
|
1950
|
+
) ?? propertySymbol.declarations?.[0];
|
|
1951
|
+
return {
|
|
1952
|
+
declaration,
|
|
1953
|
+
type: checker.getTypeOfSymbolAtLocation(propertySymbol, declaration ?? node),
|
|
1954
|
+
optional: !!(propertySymbol.flags & ts3.SymbolFlags.Optional) || declaration !== void 0 && "questionToken" in declaration && declaration.questionToken !== void 0
|
|
1955
|
+
};
|
|
1531
1956
|
}
|
|
1532
1957
|
function isLocalTypeParameterName(node, typeParameterName) {
|
|
1533
1958
|
return node.typeParameters?.some((typeParameter) => typeParameter.name.text === typeParameterName) ?? false;
|
|
@@ -1620,8 +2045,8 @@ function validateDiscriminatorDirective(node, checker, file, diagnostics) {
|
|
|
1620
2045
|
);
|
|
1621
2046
|
return null;
|
|
1622
2047
|
}
|
|
1623
|
-
const
|
|
1624
|
-
if (
|
|
2048
|
+
const property = resolveDiscriminatorProperty(node, checker, directive.fieldName);
|
|
2049
|
+
if (property === null) {
|
|
1625
2050
|
diagnostics.push(
|
|
1626
2051
|
makeAnalysisDiagnostic(
|
|
1627
2052
|
"UNKNOWN_PATH_TARGET",
|
|
@@ -1631,36 +2056,35 @@ function validateDiscriminatorDirective(node, checker, file, diagnostics) {
|
|
|
1631
2056
|
);
|
|
1632
2057
|
return null;
|
|
1633
2058
|
}
|
|
1634
|
-
if (
|
|
2059
|
+
if (property.optional) {
|
|
1635
2060
|
diagnostics.push(
|
|
1636
2061
|
makeAnalysisDiagnostic(
|
|
1637
2062
|
"TYPE_MISMATCH",
|
|
1638
2063
|
`Discriminator field "${directive.fieldName}" must be required; optional discriminator fields are not supported.`,
|
|
1639
2064
|
directive.provenance,
|
|
1640
|
-
[provenanceForNode(
|
|
2065
|
+
property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
|
|
1641
2066
|
)
|
|
1642
2067
|
);
|
|
1643
2068
|
return null;
|
|
1644
2069
|
}
|
|
1645
|
-
|
|
1646
|
-
if (isNullishSemanticType(propertyType)) {
|
|
2070
|
+
if (isNullishSemanticType(property.type)) {
|
|
1647
2071
|
diagnostics.push(
|
|
1648
2072
|
makeAnalysisDiagnostic(
|
|
1649
2073
|
"TYPE_MISMATCH",
|
|
1650
2074
|
`Discriminator field "${directive.fieldName}" must not be nullable.`,
|
|
1651
2075
|
directive.provenance,
|
|
1652
|
-
[provenanceForNode(
|
|
2076
|
+
property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
|
|
1653
2077
|
)
|
|
1654
2078
|
);
|
|
1655
2079
|
return null;
|
|
1656
2080
|
}
|
|
1657
|
-
if (!isStringLikeSemanticType(
|
|
2081
|
+
if (!isStringLikeSemanticType(property.type)) {
|
|
1658
2082
|
diagnostics.push(
|
|
1659
2083
|
makeAnalysisDiagnostic(
|
|
1660
2084
|
"TYPE_MISMATCH",
|
|
1661
2085
|
`Discriminator field "${directive.fieldName}" must be string-like.`,
|
|
1662
2086
|
directive.provenance,
|
|
1663
|
-
[provenanceForNode(
|
|
2087
|
+
property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
|
|
1664
2088
|
)
|
|
1665
2089
|
);
|
|
1666
2090
|
return null;
|
|
@@ -1681,25 +2105,58 @@ function getConcreteTypeArgumentForDiscriminator(node, subjectType, checker, typ
|
|
|
1681
2105
|
const localTypeParameter = node.typeParameters?.[typeParameterIndex];
|
|
1682
2106
|
return localTypeParameter === void 0 ? null : checker.getTypeAtLocation(localTypeParameter);
|
|
1683
2107
|
}
|
|
1684
|
-
function
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
2108
|
+
function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker, provenance, diagnostics) {
|
|
2109
|
+
const propertySymbol = boundType.getProperty(fieldName);
|
|
2110
|
+
if (propertySymbol === void 0) {
|
|
2111
|
+
return void 0;
|
|
2112
|
+
}
|
|
2113
|
+
const declaration = propertySymbol.valueDeclaration ?? propertySymbol.declarations?.[0];
|
|
2114
|
+
const anchorNode = declaration ?? boundType.symbol.declarations?.[0] ?? null;
|
|
2115
|
+
const resolvedAnchorNode = anchorNode ?? resolveNamedDiscriminatorDeclaration(boundType, checker);
|
|
2116
|
+
if (resolvedAnchorNode === null) {
|
|
2117
|
+
return void 0;
|
|
2118
|
+
}
|
|
2119
|
+
const propertyType = checker.getTypeOfSymbolAtLocation(
|
|
2120
|
+
propertySymbol,
|
|
2121
|
+
resolvedAnchorNode
|
|
2122
|
+
);
|
|
2123
|
+
if (propertyType.isStringLiteral()) {
|
|
2124
|
+
return propertyType.value;
|
|
2125
|
+
}
|
|
2126
|
+
if (propertyType.isUnion()) {
|
|
2127
|
+
const nonNullMembers = propertyType.types.filter(
|
|
2128
|
+
(member) => !(member.flags & (ts3.TypeFlags.Null | ts3.TypeFlags.Undefined))
|
|
2129
|
+
);
|
|
2130
|
+
if (nonNullMembers.length > 0 && nonNullMembers.every((member) => member.isStringLiteral())) {
|
|
2131
|
+
diagnostics.push(
|
|
2132
|
+
makeAnalysisDiagnostic(
|
|
2133
|
+
"INVALID_TAG_ARGUMENT",
|
|
2134
|
+
"Discriminator resolution for union-valued identity properties is out of scope for v1.",
|
|
2135
|
+
provenance
|
|
2136
|
+
)
|
|
2137
|
+
);
|
|
2138
|
+
return null;
|
|
1697
2139
|
}
|
|
1698
2140
|
}
|
|
1699
|
-
return
|
|
2141
|
+
return void 0;
|
|
1700
2142
|
}
|
|
1701
|
-
function
|
|
1702
|
-
|
|
2143
|
+
function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
|
|
2144
|
+
const declaration = resolveNamedDiscriminatorDeclaration(boundType, checker);
|
|
2145
|
+
if (declaration === null) {
|
|
2146
|
+
return void 0;
|
|
2147
|
+
}
|
|
2148
|
+
const metadata = resolveNodeMetadata(
|
|
2149
|
+
metadataPolicy,
|
|
2150
|
+
"type",
|
|
2151
|
+
getDiscriminatorLogicalName(boundType, declaration, checker),
|
|
2152
|
+
declaration,
|
|
2153
|
+
{
|
|
2154
|
+
checker,
|
|
2155
|
+
declaration,
|
|
2156
|
+
subjectType: boundType
|
|
2157
|
+
}
|
|
2158
|
+
);
|
|
2159
|
+
return metadata?.apiName;
|
|
1703
2160
|
}
|
|
1704
2161
|
function resolveNamedDiscriminatorDeclaration(type, checker, seen = /* @__PURE__ */ new Set()) {
|
|
1705
2162
|
if (seen.has(type)) {
|
|
@@ -1726,7 +2183,7 @@ function resolveNamedDiscriminatorDeclaration(type, checker, seen = /* @__PURE__
|
|
|
1726
2183
|
}
|
|
1727
2184
|
return null;
|
|
1728
2185
|
}
|
|
1729
|
-
function resolveDiscriminatorValue(boundType, checker, provenance, diagnostics) {
|
|
2186
|
+
function resolveDiscriminatorValue(boundType, fieldName, checker, provenance, diagnostics, metadataPolicy) {
|
|
1730
2187
|
if (boundType === null) {
|
|
1731
2188
|
diagnostics.push(
|
|
1732
2189
|
makeAnalysisDiagnostic(
|
|
@@ -1755,9 +2212,22 @@ function resolveDiscriminatorValue(boundType, checker, provenance, diagnostics)
|
|
|
1755
2212
|
return null;
|
|
1756
2213
|
}
|
|
1757
2214
|
}
|
|
1758
|
-
const
|
|
1759
|
-
|
|
1760
|
-
|
|
2215
|
+
const literalIdentityValue = resolveLiteralDiscriminatorPropertyValue(
|
|
2216
|
+
boundType,
|
|
2217
|
+
fieldName,
|
|
2218
|
+
checker,
|
|
2219
|
+
provenance,
|
|
2220
|
+
diagnostics
|
|
2221
|
+
);
|
|
2222
|
+
if (literalIdentityValue !== void 0) {
|
|
2223
|
+
return literalIdentityValue;
|
|
2224
|
+
}
|
|
2225
|
+
const apiName = resolveDiscriminatorApiName(boundType, checker, metadataPolicy);
|
|
2226
|
+
if (apiName?.source === "explicit") {
|
|
2227
|
+
return apiName.value;
|
|
2228
|
+
}
|
|
2229
|
+
if (apiName?.source === "inferred") {
|
|
2230
|
+
return apiName.value;
|
|
1761
2231
|
}
|
|
1762
2232
|
diagnostics.push(
|
|
1763
2233
|
makeAnalysisDiagnostic(
|
|
@@ -1774,7 +2244,15 @@ function getDeclarationName(node) {
|
|
|
1774
2244
|
}
|
|
1775
2245
|
return "anonymous";
|
|
1776
2246
|
}
|
|
1777
|
-
function
|
|
2247
|
+
function getResolvedTypeArguments(type) {
|
|
2248
|
+
return (isTypeReference(type) ? type.typeArguments : void 0) ?? type.aliasTypeArguments ?? [];
|
|
2249
|
+
}
|
|
2250
|
+
function getDiscriminatorLogicalName(type, declaration, checker) {
|
|
2251
|
+
const baseName = getDeclarationName(declaration);
|
|
2252
|
+
const typeArguments = getResolvedTypeArguments(type);
|
|
2253
|
+
return typeArguments.length === 0 ? baseName : buildInstantiatedReferenceName(baseName, typeArguments, checker);
|
|
2254
|
+
}
|
|
2255
|
+
function applyDeclarationDiscriminatorToFields(fields, node, subjectType, checker, file, diagnostics, metadataPolicy) {
|
|
1778
2256
|
const directive = validateDiscriminatorDirective(node, checker, file, diagnostics);
|
|
1779
2257
|
if (directive === null) {
|
|
1780
2258
|
return [...fields];
|
|
@@ -1786,9 +2264,11 @@ function applyDeclarationDiscriminatorToFields(fields, node, subjectType, checke
|
|
|
1786
2264
|
checker,
|
|
1787
2265
|
directive.typeParameterName
|
|
1788
2266
|
),
|
|
2267
|
+
directive.fieldName,
|
|
1789
2268
|
checker,
|
|
1790
2269
|
directive.provenance,
|
|
1791
|
-
diagnostics
|
|
2270
|
+
diagnostics,
|
|
2271
|
+
metadataPolicy
|
|
1792
2272
|
);
|
|
1793
2273
|
if (discriminatorValue === null) {
|
|
1794
2274
|
return [...fields];
|
|
@@ -1809,7 +2289,7 @@ function buildInstantiatedReferenceName(baseName, typeArguments, checker) {
|
|
|
1809
2289
|
).filter((value) => value !== "");
|
|
1810
2290
|
return renderedArguments.length === 0 ? baseName : `${baseName}__${renderedArguments.join("__")}`;
|
|
1811
2291
|
}
|
|
1812
|
-
function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
2292
|
+
function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy, extensionRegistry, diagnostics) {
|
|
1813
2293
|
const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
|
|
1814
2294
|
if (typeNode === void 0) {
|
|
1815
2295
|
return [];
|
|
@@ -1829,13 +2309,14 @@ function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiti
|
|
|
1829
2309
|
typeRegistry,
|
|
1830
2310
|
visiting,
|
|
1831
2311
|
argumentNode,
|
|
2312
|
+
metadataPolicy,
|
|
1832
2313
|
extensionRegistry,
|
|
1833
2314
|
diagnostics
|
|
1834
2315
|
)
|
|
1835
2316
|
};
|
|
1836
2317
|
});
|
|
1837
2318
|
}
|
|
1838
|
-
function applyDiscriminatorToObjectProperties(properties, node, subjectType, checker, file, diagnostics) {
|
|
2319
|
+
function applyDiscriminatorToObjectProperties(properties, node, subjectType, checker, file, diagnostics, metadataPolicy) {
|
|
1839
2320
|
const directive = validateDiscriminatorDirective(node, checker, file, diagnostics);
|
|
1840
2321
|
if (directive === null) {
|
|
1841
2322
|
return properties;
|
|
@@ -1847,9 +2328,11 @@ function applyDiscriminatorToObjectProperties(properties, node, subjectType, che
|
|
|
1847
2328
|
checker,
|
|
1848
2329
|
directive.typeParameterName
|
|
1849
2330
|
),
|
|
2331
|
+
directive.fieldName,
|
|
1850
2332
|
checker,
|
|
1851
2333
|
directive.provenance,
|
|
1852
|
-
diagnostics
|
|
2334
|
+
diagnostics,
|
|
2335
|
+
metadataPolicy
|
|
1853
2336
|
);
|
|
1854
2337
|
if (discriminatorValue === null) {
|
|
1855
2338
|
return properties;
|
|
@@ -1864,7 +2347,7 @@ function applyDiscriminatorToObjectProperties(properties, node, subjectType, che
|
|
|
1864
2347
|
} : property
|
|
1865
2348
|
);
|
|
1866
2349
|
}
|
|
1867
|
-
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, extensionRegistry) {
|
|
2350
|
+
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, metadataPolicy, extensionRegistry) {
|
|
1868
2351
|
if (!ts3.isIdentifier(prop.name)) {
|
|
1869
2352
|
return null;
|
|
1870
2353
|
}
|
|
@@ -1879,6 +2362,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
|
|
|
1879
2362
|
typeRegistry,
|
|
1880
2363
|
visiting,
|
|
1881
2364
|
prop,
|
|
2365
|
+
metadataPolicy,
|
|
1882
2366
|
extensionRegistry,
|
|
1883
2367
|
diagnostics
|
|
1884
2368
|
);
|
|
@@ -1902,9 +2386,16 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
|
|
|
1902
2386
|
annotations.push(defaultAnnotation);
|
|
1903
2387
|
}
|
|
1904
2388
|
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
2389
|
+
const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
|
|
2390
|
+
checker,
|
|
2391
|
+
declaration: prop,
|
|
2392
|
+
subjectType: tsType,
|
|
2393
|
+
hostType
|
|
2394
|
+
});
|
|
1905
2395
|
return {
|
|
1906
2396
|
kind: "field",
|
|
1907
2397
|
name,
|
|
2398
|
+
...metadata !== void 0 && { metadata },
|
|
1908
2399
|
type,
|
|
1909
2400
|
required: !optional,
|
|
1910
2401
|
constraints,
|
|
@@ -1912,7 +2403,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
|
|
|
1912
2403
|
provenance
|
|
1913
2404
|
};
|
|
1914
2405
|
}
|
|
1915
|
-
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, extensionRegistry) {
|
|
2406
|
+
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, metadataPolicy, extensionRegistry) {
|
|
1916
2407
|
if (!ts3.isIdentifier(prop.name)) {
|
|
1917
2408
|
return null;
|
|
1918
2409
|
}
|
|
@@ -1927,6 +2418,7 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
1927
2418
|
typeRegistry,
|
|
1928
2419
|
visiting,
|
|
1929
2420
|
prop,
|
|
2421
|
+
metadataPolicy,
|
|
1930
2422
|
extensionRegistry,
|
|
1931
2423
|
diagnostics
|
|
1932
2424
|
);
|
|
@@ -1946,9 +2438,16 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
1946
2438
|
let annotations = [];
|
|
1947
2439
|
annotations.push(...docResult.annotations);
|
|
1948
2440
|
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
2441
|
+
const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
|
|
2442
|
+
checker,
|
|
2443
|
+
declaration: prop,
|
|
2444
|
+
subjectType: tsType,
|
|
2445
|
+
hostType
|
|
2446
|
+
});
|
|
1949
2447
|
return {
|
|
1950
2448
|
kind: "field",
|
|
1951
2449
|
name,
|
|
2450
|
+
...metadata !== void 0 && { metadata },
|
|
1952
2451
|
type,
|
|
1953
2452
|
required: !optional,
|
|
1954
2453
|
constraints,
|
|
@@ -2073,7 +2572,7 @@ function getTypeNodeRegistrationName(typeNode) {
|
|
|
2073
2572
|
}
|
|
2074
2573
|
return null;
|
|
2075
2574
|
}
|
|
2076
|
-
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
2575
|
+
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2077
2576
|
const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
|
|
2078
2577
|
if (customType) {
|
|
2079
2578
|
return customType;
|
|
@@ -2085,6 +2584,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2085
2584
|
typeRegistry,
|
|
2086
2585
|
visiting,
|
|
2087
2586
|
sourceNode,
|
|
2587
|
+
metadataPolicy,
|
|
2088
2588
|
extensionRegistry,
|
|
2089
2589
|
diagnostics
|
|
2090
2590
|
);
|
|
@@ -2129,6 +2629,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2129
2629
|
typeRegistry,
|
|
2130
2630
|
visiting,
|
|
2131
2631
|
sourceNode,
|
|
2632
|
+
metadataPolicy,
|
|
2132
2633
|
extensionRegistry,
|
|
2133
2634
|
diagnostics
|
|
2134
2635
|
);
|
|
@@ -2141,6 +2642,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2141
2642
|
typeRegistry,
|
|
2142
2643
|
visiting,
|
|
2143
2644
|
sourceNode,
|
|
2645
|
+
metadataPolicy,
|
|
2144
2646
|
extensionRegistry,
|
|
2145
2647
|
diagnostics
|
|
2146
2648
|
);
|
|
@@ -2153,13 +2655,14 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2153
2655
|
typeRegistry,
|
|
2154
2656
|
visiting,
|
|
2155
2657
|
sourceNode,
|
|
2658
|
+
metadataPolicy,
|
|
2156
2659
|
extensionRegistry,
|
|
2157
2660
|
diagnostics
|
|
2158
2661
|
);
|
|
2159
2662
|
}
|
|
2160
2663
|
return { kind: "primitive", primitiveKind: "string" };
|
|
2161
2664
|
}
|
|
2162
|
-
function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
2665
|
+
function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2163
2666
|
if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
|
|
2164
2667
|
return null;
|
|
2165
2668
|
}
|
|
@@ -2179,14 +2682,21 @@ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiti
|
|
|
2179
2682
|
file,
|
|
2180
2683
|
makeParseOptions(extensionRegistry)
|
|
2181
2684
|
);
|
|
2685
|
+
const metadata = resolveNodeMetadata(metadataPolicy, "type", aliasName, aliasDecl, {
|
|
2686
|
+
checker,
|
|
2687
|
+
declaration: aliasDecl,
|
|
2688
|
+
subjectType: aliasType
|
|
2689
|
+
});
|
|
2182
2690
|
typeRegistry[aliasName] = {
|
|
2183
2691
|
name: aliasName,
|
|
2692
|
+
...metadata !== void 0 && { metadata },
|
|
2184
2693
|
type: resolveAliasedPrimitiveTarget(
|
|
2185
2694
|
aliasType,
|
|
2186
2695
|
checker,
|
|
2187
2696
|
file,
|
|
2188
2697
|
typeRegistry,
|
|
2189
2698
|
visiting,
|
|
2699
|
+
metadataPolicy,
|
|
2190
2700
|
extensionRegistry,
|
|
2191
2701
|
diagnostics
|
|
2192
2702
|
),
|
|
@@ -2215,7 +2725,7 @@ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
|
|
|
2215
2725
|
const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
|
|
2216
2726
|
return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
|
|
2217
2727
|
}
|
|
2218
|
-
function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, extensionRegistry, diagnostics) {
|
|
2728
|
+
function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2219
2729
|
const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
|
|
2220
2730
|
if (nestedAliasDecl !== void 0) {
|
|
2221
2731
|
return resolveAliasedPrimitiveTarget(
|
|
@@ -2224,6 +2734,7 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
|
|
|
2224
2734
|
file,
|
|
2225
2735
|
typeRegistry,
|
|
2226
2736
|
visiting,
|
|
2737
|
+
metadataPolicy,
|
|
2227
2738
|
extensionRegistry,
|
|
2228
2739
|
diagnostics
|
|
2229
2740
|
);
|
|
@@ -2235,11 +2746,12 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
|
|
|
2235
2746
|
typeRegistry,
|
|
2236
2747
|
visiting,
|
|
2237
2748
|
void 0,
|
|
2749
|
+
metadataPolicy,
|
|
2238
2750
|
extensionRegistry,
|
|
2239
2751
|
diagnostics
|
|
2240
2752
|
);
|
|
2241
2753
|
}
|
|
2242
|
-
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
2754
|
+
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2243
2755
|
const typeName = getNamedTypeName(type);
|
|
2244
2756
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
2245
2757
|
if (typeName && typeName in typeRegistry) {
|
|
@@ -2274,8 +2786,14 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2274
2786
|
return result;
|
|
2275
2787
|
}
|
|
2276
2788
|
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
2789
|
+
const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", typeName, namedDecl, {
|
|
2790
|
+
checker,
|
|
2791
|
+
declaration: namedDecl,
|
|
2792
|
+
subjectType: type
|
|
2793
|
+
}) : void 0;
|
|
2277
2794
|
typeRegistry[typeName] = {
|
|
2278
2795
|
name: typeName,
|
|
2796
|
+
...metadata !== void 0 && { metadata },
|
|
2279
2797
|
type: result,
|
|
2280
2798
|
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2281
2799
|
provenance: provenanceForDeclaration(namedDecl ?? sourceNode, file)
|
|
@@ -2329,6 +2847,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2329
2847
|
typeRegistry,
|
|
2330
2848
|
visiting,
|
|
2331
2849
|
nonNullMembers[0].sourceNode ?? sourceNode,
|
|
2850
|
+
metadataPolicy,
|
|
2332
2851
|
extensionRegistry,
|
|
2333
2852
|
diagnostics
|
|
2334
2853
|
);
|
|
@@ -2346,6 +2865,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2346
2865
|
typeRegistry,
|
|
2347
2866
|
visiting,
|
|
2348
2867
|
memberSourceNode ?? sourceNode,
|
|
2868
|
+
metadataPolicy,
|
|
2349
2869
|
extensionRegistry,
|
|
2350
2870
|
diagnostics
|
|
2351
2871
|
)
|
|
@@ -2355,7 +2875,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2355
2875
|
}
|
|
2356
2876
|
return registerNamed({ kind: "union", members });
|
|
2357
2877
|
}
|
|
2358
|
-
function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
2878
|
+
function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2359
2879
|
const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
|
|
2360
2880
|
const elementType = typeArgs?.[0];
|
|
2361
2881
|
const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
|
|
@@ -2366,12 +2886,13 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2366
2886
|
typeRegistry,
|
|
2367
2887
|
visiting,
|
|
2368
2888
|
elementSourceNode,
|
|
2889
|
+
metadataPolicy,
|
|
2369
2890
|
extensionRegistry,
|
|
2370
2891
|
diagnostics
|
|
2371
2892
|
) : { kind: "primitive", primitiveKind: "string" };
|
|
2372
2893
|
return { kind: "array", items };
|
|
2373
2894
|
}
|
|
2374
|
-
function tryResolveRecordType(type, checker, file, typeRegistry, visiting, extensionRegistry, diagnostics) {
|
|
2895
|
+
function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2375
2896
|
if (type.getProperties().length > 0) {
|
|
2376
2897
|
return null;
|
|
2377
2898
|
}
|
|
@@ -2386,6 +2907,7 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, exten
|
|
|
2386
2907
|
typeRegistry,
|
|
2387
2908
|
visiting,
|
|
2388
2909
|
void 0,
|
|
2910
|
+
metadataPolicy,
|
|
2389
2911
|
extensionRegistry,
|
|
2390
2912
|
diagnostics
|
|
2391
2913
|
);
|
|
@@ -2416,7 +2938,22 @@ function typeNodeContainsReference(type, targetName) {
|
|
|
2416
2938
|
}
|
|
2417
2939
|
}
|
|
2418
2940
|
}
|
|
2419
|
-
function
|
|
2941
|
+
function shouldEmitResolvedObjectProperty(property, declaration) {
|
|
2942
|
+
if (property.name.startsWith("__@")) {
|
|
2943
|
+
return false;
|
|
2944
|
+
}
|
|
2945
|
+
if (declaration !== void 0 && "name" in declaration && declaration.name !== void 0) {
|
|
2946
|
+
const name = declaration.name;
|
|
2947
|
+
if (ts3.isComputedPropertyName(name) || ts3.isPrivateIdentifier(name)) {
|
|
2948
|
+
return false;
|
|
2949
|
+
}
|
|
2950
|
+
if (!ts3.isIdentifier(name) && !ts3.isStringLiteral(name) && !ts3.isNumericLiteral(name)) {
|
|
2951
|
+
return false;
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
return true;
|
|
2955
|
+
}
|
|
2956
|
+
function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2420
2957
|
const collectedDiagnostics = diagnostics ?? [];
|
|
2421
2958
|
const typeName = getNamedTypeName(type);
|
|
2422
2959
|
const namedTypeName = typeName ?? void 0;
|
|
@@ -2428,6 +2965,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
|
|
|
2428
2965
|
typeRegistry,
|
|
2429
2966
|
visiting,
|
|
2430
2967
|
sourceNode,
|
|
2968
|
+
metadataPolicy,
|
|
2431
2969
|
extensionRegistry,
|
|
2432
2970
|
collectedDiagnostics
|
|
2433
2971
|
);
|
|
@@ -2478,6 +3016,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
|
|
|
2478
3016
|
file,
|
|
2479
3017
|
typeRegistry,
|
|
2480
3018
|
visiting,
|
|
3019
|
+
metadataPolicy,
|
|
2481
3020
|
extensionRegistry,
|
|
2482
3021
|
collectedDiagnostics
|
|
2483
3022
|
);
|
|
@@ -2490,8 +3029,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
|
|
|
2490
3029
|
return recordNode;
|
|
2491
3030
|
}
|
|
2492
3031
|
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
3032
|
+
const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
|
|
3033
|
+
checker,
|
|
3034
|
+
declaration: namedDecl,
|
|
3035
|
+
subjectType: type
|
|
3036
|
+
}) : void 0;
|
|
2493
3037
|
typeRegistry[registryTypeName] = {
|
|
2494
3038
|
name: registryTypeName,
|
|
3039
|
+
...metadata !== void 0 && { metadata },
|
|
2495
3040
|
type: recordNode,
|
|
2496
3041
|
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2497
3042
|
provenance: provenanceForDeclaration(namedDecl, file)
|
|
@@ -2511,12 +3056,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
|
|
|
2511
3056
|
file,
|
|
2512
3057
|
typeRegistry,
|
|
2513
3058
|
visiting,
|
|
3059
|
+
metadataPolicy,
|
|
2514
3060
|
collectedDiagnostics,
|
|
2515
3061
|
extensionRegistry
|
|
2516
3062
|
);
|
|
2517
3063
|
for (const prop of type.getProperties()) {
|
|
2518
3064
|
const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
2519
3065
|
if (!declaration) continue;
|
|
3066
|
+
if (!shouldEmitResolvedObjectProperty(prop, declaration)) continue;
|
|
2520
3067
|
const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
|
|
2521
3068
|
const optional = !!(prop.flags & ts3.SymbolFlags.Optional);
|
|
2522
3069
|
const propTypeNode = resolveTypeNode(
|
|
@@ -2526,12 +3073,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
|
|
|
2526
3073
|
typeRegistry,
|
|
2527
3074
|
visiting,
|
|
2528
3075
|
declaration,
|
|
3076
|
+
metadataPolicy,
|
|
2529
3077
|
extensionRegistry,
|
|
2530
3078
|
collectedDiagnostics
|
|
2531
3079
|
);
|
|
2532
3080
|
const fieldNodeInfo = fieldInfoMap?.get(prop.name);
|
|
2533
3081
|
properties.push({
|
|
2534
3082
|
name: prop.name,
|
|
3083
|
+
...fieldNodeInfo?.metadata !== void 0 && { metadata: fieldNodeInfo.metadata },
|
|
2535
3084
|
type: propTypeNode,
|
|
2536
3085
|
optional,
|
|
2537
3086
|
constraints: fieldNodeInfo?.constraints ?? [],
|
|
@@ -2548,14 +3097,21 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
|
|
|
2548
3097
|
type,
|
|
2549
3098
|
checker,
|
|
2550
3099
|
file,
|
|
2551
|
-
collectedDiagnostics
|
|
3100
|
+
collectedDiagnostics,
|
|
3101
|
+
metadataPolicy
|
|
2552
3102
|
) : properties,
|
|
2553
3103
|
additionalProperties: true
|
|
2554
3104
|
};
|
|
2555
3105
|
if (registryTypeName !== void 0 && shouldRegisterNamedType) {
|
|
2556
3106
|
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
3107
|
+
const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
|
|
3108
|
+
checker,
|
|
3109
|
+
declaration: namedDecl,
|
|
3110
|
+
subjectType: type
|
|
3111
|
+
}) : void 0;
|
|
2557
3112
|
typeRegistry[registryTypeName] = {
|
|
2558
3113
|
name: registryTypeName,
|
|
3114
|
+
...metadata !== void 0 && { metadata },
|
|
2559
3115
|
type: objectNode,
|
|
2560
3116
|
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2561
3117
|
provenance: provenanceForDeclaration(namedDecl, file)
|
|
@@ -2568,7 +3124,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
|
|
|
2568
3124
|
}
|
|
2569
3125
|
return objectNode;
|
|
2570
3126
|
}
|
|
2571
|
-
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, diagnostics, extensionRegistry) {
|
|
3127
|
+
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, metadataPolicy, diagnostics, extensionRegistry) {
|
|
2572
3128
|
const symbols = [type.getSymbol(), type.aliasSymbol].filter(
|
|
2573
3129
|
(s) => s?.declarations != null && s.declarations.length > 0
|
|
2574
3130
|
);
|
|
@@ -2589,10 +3145,12 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2589
3145
|
visiting,
|
|
2590
3146
|
diagnostics,
|
|
2591
3147
|
hostType,
|
|
3148
|
+
metadataPolicy,
|
|
2592
3149
|
extensionRegistry
|
|
2593
3150
|
);
|
|
2594
3151
|
if (fieldNode) {
|
|
2595
3152
|
map.set(fieldNode.name, {
|
|
3153
|
+
...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
|
|
2596
3154
|
constraints: [...fieldNode.constraints],
|
|
2597
3155
|
annotations: [...fieldNode.annotations],
|
|
2598
3156
|
provenance: fieldNode.provenance
|
|
@@ -2610,6 +3168,7 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2610
3168
|
file,
|
|
2611
3169
|
typeRegistry,
|
|
2612
3170
|
visiting,
|
|
3171
|
+
metadataPolicy,
|
|
2613
3172
|
checker.getTypeAtLocation(interfaceDecl),
|
|
2614
3173
|
diagnostics,
|
|
2615
3174
|
extensionRegistry
|
|
@@ -2623,6 +3182,7 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2623
3182
|
file,
|
|
2624
3183
|
typeRegistry,
|
|
2625
3184
|
visiting,
|
|
3185
|
+
metadataPolicy,
|
|
2626
3186
|
checker.getTypeAtLocation(typeAliasDecl),
|
|
2627
3187
|
diagnostics,
|
|
2628
3188
|
extensionRegistry
|
|
@@ -2674,7 +3234,7 @@ function isNullishTypeNode(typeNode) {
|
|
|
2674
3234
|
}
|
|
2675
3235
|
return ts3.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts3.SyntaxKind.NullKeyword || typeNode.literal.kind === ts3.SyntaxKind.UndefinedKeyword);
|
|
2676
3236
|
}
|
|
2677
|
-
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, hostType, diagnostics, extensionRegistry) {
|
|
3237
|
+
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, metadataPolicy, hostType, diagnostics, extensionRegistry) {
|
|
2678
3238
|
const map = /* @__PURE__ */ new Map();
|
|
2679
3239
|
for (const member of members) {
|
|
2680
3240
|
if (ts3.isPropertySignature(member)) {
|
|
@@ -2686,10 +3246,12 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, h
|
|
|
2686
3246
|
visiting,
|
|
2687
3247
|
diagnostics,
|
|
2688
3248
|
hostType,
|
|
3249
|
+
metadataPolicy,
|
|
2689
3250
|
extensionRegistry
|
|
2690
3251
|
);
|
|
2691
3252
|
if (fieldNode) {
|
|
2692
3253
|
map.set(fieldNode.name, {
|
|
3254
|
+
...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
|
|
2693
3255
|
constraints: [...fieldNode.constraints],
|
|
2694
3256
|
annotations: [...fieldNode.annotations],
|
|
2695
3257
|
provenance: fieldNode.provenance
|
|
@@ -2720,6 +3282,7 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
|
|
|
2720
3282
|
{},
|
|
2721
3283
|
/* @__PURE__ */ new Set(),
|
|
2722
3284
|
aliasDecl.type,
|
|
3285
|
+
void 0,
|
|
2723
3286
|
extensionRegistry
|
|
2724
3287
|
);
|
|
2725
3288
|
const constraints = extractJSDocConstraintNodes(
|
|
@@ -2905,15 +3468,27 @@ function findInterfaceByName(sourceFile, interfaceName) {
|
|
|
2905
3468
|
function findTypeAliasByName(sourceFile, aliasName) {
|
|
2906
3469
|
return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
|
|
2907
3470
|
}
|
|
2908
|
-
function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry) {
|
|
3471
|
+
function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy) {
|
|
2909
3472
|
const analysisFilePath = path.resolve(filePath);
|
|
2910
3473
|
const classDecl = findClassByName(ctx.sourceFile, typeName);
|
|
2911
3474
|
if (classDecl !== null) {
|
|
2912
|
-
return analyzeClassToIR(
|
|
3475
|
+
return analyzeClassToIR(
|
|
3476
|
+
classDecl,
|
|
3477
|
+
ctx.checker,
|
|
3478
|
+
analysisFilePath,
|
|
3479
|
+
extensionRegistry,
|
|
3480
|
+
metadataPolicy
|
|
3481
|
+
);
|
|
2913
3482
|
}
|
|
2914
3483
|
const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
|
|
2915
3484
|
if (interfaceDecl !== null) {
|
|
2916
|
-
return analyzeInterfaceToIR(
|
|
3485
|
+
return analyzeInterfaceToIR(
|
|
3486
|
+
interfaceDecl,
|
|
3487
|
+
ctx.checker,
|
|
3488
|
+
analysisFilePath,
|
|
3489
|
+
extensionRegistry,
|
|
3490
|
+
metadataPolicy
|
|
3491
|
+
);
|
|
2917
3492
|
}
|
|
2918
3493
|
const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
|
|
2919
3494
|
if (typeAlias !== null) {
|
|
@@ -2921,7 +3496,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
|
|
|
2921
3496
|
typeAlias,
|
|
2922
3497
|
ctx.checker,
|
|
2923
3498
|
analysisFilePath,
|
|
2924
|
-
extensionRegistry
|
|
3499
|
+
extensionRegistry,
|
|
3500
|
+
metadataPolicy
|
|
2925
3501
|
);
|
|
2926
3502
|
if (result.ok) {
|
|
2927
3503
|
return result.analysis;
|
|
@@ -2936,6 +3512,120 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
|
|
|
2936
3512
|
// src/generators/class-schema.ts
|
|
2937
3513
|
import "typescript";
|
|
2938
3514
|
|
|
3515
|
+
// src/metadata/collision-guards.ts
|
|
3516
|
+
function assertUniqueSerializedNames(entries, scope) {
|
|
3517
|
+
const seen = /* @__PURE__ */ new Map();
|
|
3518
|
+
for (const entry of entries) {
|
|
3519
|
+
const previous = seen.get(entry.serializedName);
|
|
3520
|
+
if (previous !== void 0) {
|
|
3521
|
+
if (previous.logicalName === entry.logicalName && previous.category === entry.category) {
|
|
3522
|
+
continue;
|
|
3523
|
+
}
|
|
3524
|
+
throw new Error(
|
|
3525
|
+
`Serialized name collision in ${scope}: ${previous.category} "${previous.logicalName}" and ${entry.category} "${entry.logicalName}" both resolve to "${entry.serializedName}".`
|
|
3526
|
+
);
|
|
3527
|
+
}
|
|
3528
|
+
seen.set(entry.serializedName, entry);
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3531
|
+
function collectFlattenedFields(elements) {
|
|
3532
|
+
const fields = [];
|
|
3533
|
+
for (const element of elements) {
|
|
3534
|
+
switch (element.kind) {
|
|
3535
|
+
case "field":
|
|
3536
|
+
fields.push(element);
|
|
3537
|
+
break;
|
|
3538
|
+
case "group":
|
|
3539
|
+
case "conditional":
|
|
3540
|
+
fields.push(...collectFlattenedFields(element.elements));
|
|
3541
|
+
break;
|
|
3542
|
+
default: {
|
|
3543
|
+
const exhaustive = element;
|
|
3544
|
+
void exhaustive;
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
return fields;
|
|
3549
|
+
}
|
|
3550
|
+
function validateObjectProperties(properties, scope) {
|
|
3551
|
+
assertUniqueSerializedNames(
|
|
3552
|
+
properties.map((property) => ({
|
|
3553
|
+
logicalName: property.name,
|
|
3554
|
+
serializedName: getSerializedName(property.name, property.metadata),
|
|
3555
|
+
category: "object property"
|
|
3556
|
+
})),
|
|
3557
|
+
scope
|
|
3558
|
+
);
|
|
3559
|
+
for (const property of properties) {
|
|
3560
|
+
validateTypeNode(
|
|
3561
|
+
property.type,
|
|
3562
|
+
`${scope}.${getSerializedName(property.name, property.metadata)}`
|
|
3563
|
+
);
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
function validateTypeNode(type, scope) {
|
|
3567
|
+
switch (type.kind) {
|
|
3568
|
+
case "array":
|
|
3569
|
+
validateTypeNode(type.items, `${scope}[]`);
|
|
3570
|
+
break;
|
|
3571
|
+
case "object":
|
|
3572
|
+
validateObjectProperties(type.properties, scope);
|
|
3573
|
+
break;
|
|
3574
|
+
case "record":
|
|
3575
|
+
validateTypeNode(type.valueType, `${scope}.*`);
|
|
3576
|
+
break;
|
|
3577
|
+
case "union":
|
|
3578
|
+
type.members.forEach((member, index) => {
|
|
3579
|
+
validateTypeNode(member, `${scope}|${String(index)}`);
|
|
3580
|
+
});
|
|
3581
|
+
break;
|
|
3582
|
+
case "reference":
|
|
3583
|
+
case "primitive":
|
|
3584
|
+
case "enum":
|
|
3585
|
+
case "dynamic":
|
|
3586
|
+
case "custom":
|
|
3587
|
+
break;
|
|
3588
|
+
default: {
|
|
3589
|
+
const exhaustive = type;
|
|
3590
|
+
void exhaustive;
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
function validateTypeDefinitions(typeRegistry) {
|
|
3595
|
+
const definitions = Object.values(typeRegistry);
|
|
3596
|
+
assertUniqueSerializedNames(
|
|
3597
|
+
definitions.map((definition) => ({
|
|
3598
|
+
logicalName: definition.name,
|
|
3599
|
+
serializedName: getSerializedName(definition.name, definition.metadata),
|
|
3600
|
+
category: "type definition"
|
|
3601
|
+
})),
|
|
3602
|
+
"$defs"
|
|
3603
|
+
);
|
|
3604
|
+
for (const definition of definitions) {
|
|
3605
|
+
validateTypeDefinition(definition);
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
function validateTypeDefinition(definition) {
|
|
3609
|
+
validateTypeNode(
|
|
3610
|
+
definition.type,
|
|
3611
|
+
`type "${getSerializedName(definition.name, definition.metadata)}"`
|
|
3612
|
+
);
|
|
3613
|
+
}
|
|
3614
|
+
function assertNoSerializedNameCollisions(ir) {
|
|
3615
|
+
assertUniqueSerializedNames(
|
|
3616
|
+
collectFlattenedFields(ir.elements).map((field) => ({
|
|
3617
|
+
logicalName: field.name,
|
|
3618
|
+
serializedName: getSerializedName(field.name, field.metadata),
|
|
3619
|
+
category: "field"
|
|
3620
|
+
})),
|
|
3621
|
+
"form root"
|
|
3622
|
+
);
|
|
3623
|
+
for (const field of collectFlattenedFields(ir.elements)) {
|
|
3624
|
+
validateTypeNode(field.type, `field "${getSerializedName(field.name, field.metadata)}"`);
|
|
3625
|
+
}
|
|
3626
|
+
validateTypeDefinitions(ir.typeRegistry);
|
|
3627
|
+
}
|
|
3628
|
+
|
|
2939
3629
|
// src/json-schema/ir-generator.ts
|
|
2940
3630
|
function makeContext(options) {
|
|
2941
3631
|
const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
|
|
@@ -2946,19 +3636,33 @@ function makeContext(options) {
|
|
|
2946
3636
|
}
|
|
2947
3637
|
return {
|
|
2948
3638
|
defs: {},
|
|
3639
|
+
typeNameMap: {},
|
|
3640
|
+
typeRegistry: {},
|
|
2949
3641
|
extensionRegistry: options?.extensionRegistry,
|
|
2950
3642
|
vendorPrefix
|
|
2951
3643
|
};
|
|
2952
3644
|
}
|
|
2953
3645
|
function generateJsonSchemaFromIR(ir, options) {
|
|
2954
|
-
|
|
3646
|
+
assertNoSerializedNameCollisions(ir);
|
|
3647
|
+
const ctx = {
|
|
3648
|
+
...makeContext(options),
|
|
3649
|
+
typeRegistry: ir.typeRegistry,
|
|
3650
|
+
typeNameMap: Object.fromEntries(
|
|
3651
|
+
Object.entries(ir.typeRegistry).map(([name, typeDef]) => [
|
|
3652
|
+
name,
|
|
3653
|
+
getSerializedName(name, typeDef.metadata)
|
|
3654
|
+
])
|
|
3655
|
+
)
|
|
3656
|
+
};
|
|
2955
3657
|
for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
|
|
2956
|
-
ctx.
|
|
3658
|
+
const schemaName = ctx.typeNameMap[name] ?? name;
|
|
3659
|
+
ctx.defs[schemaName] = generateTypeNode(typeDef.type, ctx);
|
|
3660
|
+
applyResolvedMetadata(ctx.defs[schemaName], typeDef.metadata);
|
|
2957
3661
|
if (typeDef.constraints && typeDef.constraints.length > 0) {
|
|
2958
|
-
applyConstraints(ctx.defs[
|
|
3662
|
+
applyConstraints(ctx.defs[schemaName], typeDef.constraints, ctx);
|
|
2959
3663
|
}
|
|
2960
3664
|
if (typeDef.annotations && typeDef.annotations.length > 0) {
|
|
2961
|
-
applyAnnotations(ctx.defs[
|
|
3665
|
+
applyAnnotations(ctx.defs[schemaName], typeDef.annotations, ctx);
|
|
2962
3666
|
}
|
|
2963
3667
|
}
|
|
2964
3668
|
const properties = {};
|
|
@@ -2971,6 +3675,7 @@ function generateJsonSchemaFromIR(ir, options) {
|
|
|
2971
3675
|
properties,
|
|
2972
3676
|
...uniqueRequired.length > 0 && { required: uniqueRequired }
|
|
2973
3677
|
};
|
|
3678
|
+
applyResolvedMetadata(result, ir.metadata);
|
|
2974
3679
|
if (ir.annotations && ir.annotations.length > 0) {
|
|
2975
3680
|
applyAnnotations(result, ir.annotations, ctx);
|
|
2976
3681
|
}
|
|
@@ -2983,9 +3688,9 @@ function collectFields(elements, properties, required, ctx) {
|
|
|
2983
3688
|
for (const element of elements) {
|
|
2984
3689
|
switch (element.kind) {
|
|
2985
3690
|
case "field":
|
|
2986
|
-
properties[element.name] = generateFieldSchema(element, ctx);
|
|
3691
|
+
properties[getSerializedName(element.name, element.metadata)] = generateFieldSchema(element, ctx);
|
|
2987
3692
|
if (element.required) {
|
|
2988
|
-
required.push(element.name);
|
|
3693
|
+
required.push(getSerializedName(element.name, element.metadata));
|
|
2989
3694
|
}
|
|
2990
3695
|
break;
|
|
2991
3696
|
case "group":
|
|
@@ -3029,6 +3734,7 @@ function generateFieldSchema(field, ctx) {
|
|
|
3029
3734
|
rootAnnotations.push(annotation);
|
|
3030
3735
|
}
|
|
3031
3736
|
}
|
|
3737
|
+
applyResolvedMetadata(schema, field.metadata);
|
|
3032
3738
|
applyAnnotations(schema, rootAnnotations, ctx);
|
|
3033
3739
|
if (itemStringSchema !== void 0) {
|
|
3034
3740
|
applyAnnotations(itemStringSchema, itemAnnotations, ctx);
|
|
@@ -3036,7 +3742,7 @@ function generateFieldSchema(field, ctx) {
|
|
|
3036
3742
|
if (pathConstraints.length === 0) {
|
|
3037
3743
|
return schema;
|
|
3038
3744
|
}
|
|
3039
|
-
return applyPathTargetedConstraints(schema, pathConstraints, ctx);
|
|
3745
|
+
return applyPathTargetedConstraints(schema, pathConstraints, ctx, field.type);
|
|
3040
3746
|
}
|
|
3041
3747
|
function isStringItemConstraint(constraint) {
|
|
3042
3748
|
switch (constraint.constraintKind) {
|
|
@@ -3048,9 +3754,11 @@ function isStringItemConstraint(constraint) {
|
|
|
3048
3754
|
return false;
|
|
3049
3755
|
}
|
|
3050
3756
|
}
|
|
3051
|
-
function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
|
|
3757
|
+
function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
|
|
3052
3758
|
if (schema.type === "array" && schema.items) {
|
|
3053
|
-
|
|
3759
|
+
const referencedType = typeNode?.kind === "reference" ? resolveReferencedType(typeNode, ctx) : void 0;
|
|
3760
|
+
const nestedType = typeNode?.kind === "array" ? typeNode.items : referencedType?.kind === "array" ? referencedType.items : void 0;
|
|
3761
|
+
schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx, nestedType);
|
|
3054
3762
|
return schema;
|
|
3055
3763
|
}
|
|
3056
3764
|
const byTarget = /* @__PURE__ */ new Map();
|
|
@@ -3065,7 +3773,7 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
|
|
|
3065
3773
|
for (const [target, constraints] of byTarget) {
|
|
3066
3774
|
const subSchema = {};
|
|
3067
3775
|
applyConstraints(subSchema, constraints, ctx);
|
|
3068
|
-
propertyOverrides[target] = subSchema;
|
|
3776
|
+
propertyOverrides[resolveSerializedPropertyName(target, typeNode, ctx)] = subSchema;
|
|
3069
3777
|
}
|
|
3070
3778
|
if (schema.$ref) {
|
|
3071
3779
|
const { $ref, ...rest } = schema;
|
|
@@ -3113,7 +3821,7 @@ function generateTypeNode(type, ctx) {
|
|
|
3113
3821
|
case "union":
|
|
3114
3822
|
return generateUnionType(type, ctx);
|
|
3115
3823
|
case "reference":
|
|
3116
|
-
return generateReferenceType(type);
|
|
3824
|
+
return generateReferenceType(type, ctx);
|
|
3117
3825
|
case "dynamic":
|
|
3118
3826
|
return generateDynamicType(type);
|
|
3119
3827
|
case "custom":
|
|
@@ -3154,9 +3862,10 @@ function generateObjectType(type, ctx) {
|
|
|
3154
3862
|
const properties = {};
|
|
3155
3863
|
const required = [];
|
|
3156
3864
|
for (const prop of type.properties) {
|
|
3157
|
-
|
|
3865
|
+
const propertyName = getSerializedName(prop.name, prop.metadata);
|
|
3866
|
+
properties[propertyName] = generatePropertySchema(prop, ctx);
|
|
3158
3867
|
if (!prop.optional) {
|
|
3159
|
-
required.push(
|
|
3868
|
+
required.push(propertyName);
|
|
3160
3869
|
}
|
|
3161
3870
|
}
|
|
3162
3871
|
const schema = { type: "object", properties };
|
|
@@ -3177,6 +3886,7 @@ function generateRecordType(type, ctx) {
|
|
|
3177
3886
|
function generatePropertySchema(prop, ctx) {
|
|
3178
3887
|
const schema = generateTypeNode(prop.type, ctx);
|
|
3179
3888
|
applyConstraints(schema, prop.constraints, ctx);
|
|
3889
|
+
applyResolvedMetadata(schema, prop.metadata);
|
|
3180
3890
|
applyAnnotations(schema, prop.annotations, ctx);
|
|
3181
3891
|
return schema;
|
|
3182
3892
|
}
|
|
@@ -3205,8 +3915,28 @@ function isNullableUnion(type) {
|
|
|
3205
3915
|
).length;
|
|
3206
3916
|
return nullCount === 1;
|
|
3207
3917
|
}
|
|
3208
|
-
function generateReferenceType(type) {
|
|
3209
|
-
return { $ref: `#/$defs/${type.name}` };
|
|
3918
|
+
function generateReferenceType(type, ctx) {
|
|
3919
|
+
return { $ref: `#/$defs/${ctx.typeNameMap[type.name] ?? type.name}` };
|
|
3920
|
+
}
|
|
3921
|
+
function applyResolvedMetadata(schema, metadata) {
|
|
3922
|
+
const displayName = getDisplayName(metadata);
|
|
3923
|
+
if (displayName !== void 0) {
|
|
3924
|
+
schema.title = displayName;
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
function resolveReferencedType(type, ctx) {
|
|
3928
|
+
return ctx.typeRegistry[type.name]?.type;
|
|
3929
|
+
}
|
|
3930
|
+
function resolveSerializedPropertyName(logicalName, typeNode, ctx) {
|
|
3931
|
+
if (typeNode?.kind === "object") {
|
|
3932
|
+
const property = typeNode.properties.find((candidate) => candidate.name === logicalName);
|
|
3933
|
+
return property === void 0 ? logicalName : getSerializedName(property.name, property.metadata);
|
|
3934
|
+
}
|
|
3935
|
+
if (typeNode?.kind === "reference") {
|
|
3936
|
+
const referencedType = resolveReferencedType(typeNode, ctx);
|
|
3937
|
+
return referencedType === void 0 ? logicalName : resolveSerializedPropertyName(logicalName, referencedType, ctx);
|
|
3938
|
+
}
|
|
3939
|
+
return logicalName;
|
|
3210
3940
|
}
|
|
3211
3941
|
function generateDynamicType(type) {
|
|
3212
3942
|
if (type.dynamicKind === "enum") {
|
|
@@ -3286,7 +4016,7 @@ function applyAnnotations(schema, annotations, ctx) {
|
|
|
3286
4016
|
for (const annotation of annotations) {
|
|
3287
4017
|
switch (annotation.annotationKind) {
|
|
3288
4018
|
case "displayName":
|
|
3289
|
-
schema.title
|
|
4019
|
+
schema.title ??= annotation.value;
|
|
3290
4020
|
break;
|
|
3291
4021
|
case "description":
|
|
3292
4022
|
schema.description = annotation.value;
|
|
@@ -3536,13 +4266,21 @@ function combineRules(parentRule, childRule) {
|
|
|
3536
4266
|
}
|
|
3537
4267
|
};
|
|
3538
4268
|
}
|
|
3539
|
-
function
|
|
3540
|
-
const
|
|
4269
|
+
function getFieldDisplayName(field) {
|
|
4270
|
+
const resolvedDisplayName = getDisplayName(field.metadata);
|
|
4271
|
+
if (resolvedDisplayName !== void 0) {
|
|
4272
|
+
return resolvedDisplayName;
|
|
4273
|
+
}
|
|
4274
|
+
return field.annotations.find((annotation) => annotation.annotationKind === "displayName")?.value;
|
|
4275
|
+
}
|
|
4276
|
+
function fieldNodeToControl(field, fieldNameMap, parentRule) {
|
|
3541
4277
|
const placeholderAnnotation = field.annotations.find((a) => a.annotationKind === "placeholder");
|
|
4278
|
+
const serializedName = fieldNameMap.get(field.name) ?? getSerializedName(field.name, field.metadata);
|
|
4279
|
+
const displayName = getFieldDisplayName(field);
|
|
3542
4280
|
const control = {
|
|
3543
4281
|
type: "Control",
|
|
3544
|
-
scope: fieldToScope(
|
|
3545
|
-
...
|
|
4282
|
+
scope: fieldToScope(serializedName),
|
|
4283
|
+
...displayName !== void 0 && { label: displayName },
|
|
3546
4284
|
...placeholderAnnotation !== void 0 && {
|
|
3547
4285
|
options: { placeholder: placeholderAnnotation.value }
|
|
3548
4286
|
},
|
|
@@ -3550,30 +4288,30 @@ function fieldNodeToControl(field, parentRule) {
|
|
|
3550
4288
|
};
|
|
3551
4289
|
return control;
|
|
3552
4290
|
}
|
|
3553
|
-
function groupNodeToLayout(group, parentRule) {
|
|
4291
|
+
function groupNodeToLayout(group, fieldNameMap, parentRule) {
|
|
3554
4292
|
return {
|
|
3555
4293
|
type: "Group",
|
|
3556
4294
|
label: group.label,
|
|
3557
|
-
elements: irElementsToUiSchema(group.elements, parentRule),
|
|
4295
|
+
elements: irElementsToUiSchema(group.elements, fieldNameMap, parentRule),
|
|
3558
4296
|
...parentRule !== void 0 && { rule: parentRule }
|
|
3559
4297
|
};
|
|
3560
4298
|
}
|
|
3561
|
-
function irElementsToUiSchema(elements, parentRule) {
|
|
4299
|
+
function irElementsToUiSchema(elements, fieldNameMap, parentRule) {
|
|
3562
4300
|
const result = [];
|
|
3563
4301
|
for (const element of elements) {
|
|
3564
4302
|
switch (element.kind) {
|
|
3565
4303
|
case "field": {
|
|
3566
|
-
result.push(fieldNodeToControl(element, parentRule));
|
|
4304
|
+
result.push(fieldNodeToControl(element, fieldNameMap, parentRule));
|
|
3567
4305
|
break;
|
|
3568
4306
|
}
|
|
3569
4307
|
case "group": {
|
|
3570
|
-
result.push(groupNodeToLayout(element, parentRule));
|
|
4308
|
+
result.push(groupNodeToLayout(element, fieldNameMap, parentRule));
|
|
3571
4309
|
break;
|
|
3572
4310
|
}
|
|
3573
4311
|
case "conditional": {
|
|
3574
|
-
const newRule = createShowRule(element.fieldName, element.value);
|
|
4312
|
+
const newRule = createShowRule(fieldNameMap.get(element.fieldName) ?? element.fieldName, element.value);
|
|
3575
4313
|
const combinedRule = parentRule !== void 0 ? combineRules(parentRule, newRule) : newRule;
|
|
3576
|
-
const childElements = irElementsToUiSchema(element.elements, combinedRule);
|
|
4314
|
+
const childElements = irElementsToUiSchema(element.elements, fieldNameMap, combinedRule);
|
|
3577
4315
|
result.push(...childElements);
|
|
3578
4316
|
break;
|
|
3579
4317
|
}
|
|
@@ -3587,12 +4325,35 @@ function irElementsToUiSchema(elements, parentRule) {
|
|
|
3587
4325
|
return result;
|
|
3588
4326
|
}
|
|
3589
4327
|
function generateUiSchemaFromIR(ir) {
|
|
4328
|
+
assertNoSerializedNameCollisions(ir);
|
|
4329
|
+
const fieldNameMap = collectFieldNameMap(ir.elements);
|
|
3590
4330
|
const result = {
|
|
3591
4331
|
type: "VerticalLayout",
|
|
3592
|
-
elements: irElementsToUiSchema(ir.elements)
|
|
4332
|
+
elements: irElementsToUiSchema(ir.elements, fieldNameMap)
|
|
3593
4333
|
};
|
|
3594
4334
|
return parseOrThrow(uiSchema, result, "UI Schema");
|
|
3595
4335
|
}
|
|
4336
|
+
function collectFieldNameMap(elements) {
|
|
4337
|
+
const map = /* @__PURE__ */ new Map();
|
|
4338
|
+
for (const element of elements) {
|
|
4339
|
+
switch (element.kind) {
|
|
4340
|
+
case "field":
|
|
4341
|
+
map.set(element.name, getSerializedName(element.name, element.metadata));
|
|
4342
|
+
break;
|
|
4343
|
+
case "group":
|
|
4344
|
+
case "conditional":
|
|
4345
|
+
for (const [key, value] of collectFieldNameMap(element.elements)) {
|
|
4346
|
+
map.set(key, value);
|
|
4347
|
+
}
|
|
4348
|
+
break;
|
|
4349
|
+
default: {
|
|
4350
|
+
const _exhaustive = element;
|
|
4351
|
+
void _exhaustive;
|
|
4352
|
+
}
|
|
4353
|
+
}
|
|
4354
|
+
}
|
|
4355
|
+
return map;
|
|
4356
|
+
}
|
|
3596
4357
|
|
|
3597
4358
|
// src/validate/constraint-validator.ts
|
|
3598
4359
|
import {
|
|
@@ -3677,7 +4438,11 @@ function generateClassSchemas(analysis, source, options) {
|
|
|
3677
4438
|
if (errorDiagnostics !== void 0 && errorDiagnostics.length > 0) {
|
|
3678
4439
|
throw new Error(formatValidationError(errorDiagnostics));
|
|
3679
4440
|
}
|
|
3680
|
-
const ir = canonicalizeTSDoc(
|
|
4441
|
+
const ir = canonicalizeTSDoc(
|
|
4442
|
+
analysis,
|
|
4443
|
+
source,
|
|
4444
|
+
options?.metadata !== void 0 ? { metadata: options.metadata } : void 0
|
|
4445
|
+
);
|
|
3681
4446
|
const validationResult = validateIR(ir, {
|
|
3682
4447
|
...options?.extensionRegistry !== void 0 && {
|
|
3683
4448
|
extensionRegistry: options.extensionRegistry
|
|
@@ -3799,6 +4564,7 @@ function typeToJsonSchema(type, checker) {
|
|
|
3799
4564
|
visiting,
|
|
3800
4565
|
void 0,
|
|
3801
4566
|
void 0,
|
|
4567
|
+
void 0,
|
|
3802
4568
|
diagnostics
|
|
3803
4569
|
);
|
|
3804
4570
|
if (diagnostics.length > 0) {
|