@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.cjs
CHANGED
|
@@ -53,6 +53,282 @@ module.exports = __toCommonJS(internals_exports);
|
|
|
53
53
|
|
|
54
54
|
// src/canonicalize/chain-dsl-canonicalizer.ts
|
|
55
55
|
var import_internals = require("@formspec/core/internals");
|
|
56
|
+
|
|
57
|
+
// src/metadata/policy.ts
|
|
58
|
+
var NOOP_INFLECT = () => "";
|
|
59
|
+
function normalizePluralization(input) {
|
|
60
|
+
if (input?.mode === "infer-if-missing") {
|
|
61
|
+
return {
|
|
62
|
+
mode: "infer-if-missing",
|
|
63
|
+
infer: () => "",
|
|
64
|
+
inflect: input.inflect
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (input?.mode === "require-explicit") {
|
|
68
|
+
return {
|
|
69
|
+
mode: "require-explicit",
|
|
70
|
+
infer: () => "",
|
|
71
|
+
inflect: NOOP_INFLECT
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
mode: "disabled",
|
|
76
|
+
infer: () => "",
|
|
77
|
+
inflect: NOOP_INFLECT
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function normalizeScalarPolicy(input) {
|
|
81
|
+
if (input?.mode === "infer-if-missing") {
|
|
82
|
+
return {
|
|
83
|
+
mode: "infer-if-missing",
|
|
84
|
+
infer: input.infer,
|
|
85
|
+
pluralization: normalizePluralization(input.pluralization)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (input?.mode === "require-explicit") {
|
|
89
|
+
return {
|
|
90
|
+
mode: "require-explicit",
|
|
91
|
+
infer: () => "",
|
|
92
|
+
pluralization: normalizePluralization(input.pluralization)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
mode: "disabled",
|
|
97
|
+
infer: () => "",
|
|
98
|
+
pluralization: normalizePluralization(input?.pluralization)
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function normalizeDeclarationPolicy(input) {
|
|
102
|
+
return {
|
|
103
|
+
apiName: normalizeScalarPolicy(input?.apiName),
|
|
104
|
+
displayName: normalizeScalarPolicy(input?.displayName)
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function normalizeMetadataPolicy(input) {
|
|
108
|
+
return {
|
|
109
|
+
type: normalizeDeclarationPolicy(input?.type),
|
|
110
|
+
field: normalizeDeclarationPolicy(input?.field),
|
|
111
|
+
method: normalizeDeclarationPolicy(input?.method)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function getDeclarationMetadataPolicy(policy, declarationKind) {
|
|
115
|
+
return policy[declarationKind];
|
|
116
|
+
}
|
|
117
|
+
function makeMetadataContext(surface, declarationKind, logicalName, buildContext) {
|
|
118
|
+
return {
|
|
119
|
+
surface,
|
|
120
|
+
declarationKind,
|
|
121
|
+
logicalName,
|
|
122
|
+
...buildContext !== void 0 && { buildContext }
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/metadata/resolve.ts
|
|
127
|
+
function toExplicitScalar(value) {
|
|
128
|
+
return value !== void 0 && value.trim() !== "" ? { value, source: "explicit" } : void 0;
|
|
129
|
+
}
|
|
130
|
+
function toExplicitResolvedMetadata(explicit) {
|
|
131
|
+
if (explicit === void 0) {
|
|
132
|
+
return void 0;
|
|
133
|
+
}
|
|
134
|
+
const apiName = toExplicitScalar(explicit.apiName);
|
|
135
|
+
const displayName = toExplicitScalar(explicit.displayName);
|
|
136
|
+
const apiNamePlural = toExplicitScalar(explicit.apiNamePlural);
|
|
137
|
+
const displayNamePlural = toExplicitScalar(explicit.displayNamePlural);
|
|
138
|
+
const metadata = {
|
|
139
|
+
...apiName !== void 0 && { apiName },
|
|
140
|
+
...displayName !== void 0 && { displayName },
|
|
141
|
+
...apiNamePlural !== void 0 && { apiNamePlural },
|
|
142
|
+
...displayNamePlural !== void 0 && { displayNamePlural }
|
|
143
|
+
};
|
|
144
|
+
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
145
|
+
}
|
|
146
|
+
function resolveScalar(current, policy, context, metadataLabel) {
|
|
147
|
+
if (current !== void 0) {
|
|
148
|
+
return current;
|
|
149
|
+
}
|
|
150
|
+
if (policy.mode === "require-explicit") {
|
|
151
|
+
throw new Error(
|
|
152
|
+
`Metadata policy requires explicit ${metadataLabel} for ${context.declarationKind} "${context.logicalName}" on the ${context.surface} surface.`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
if (policy.mode !== "infer-if-missing") {
|
|
156
|
+
return void 0;
|
|
157
|
+
}
|
|
158
|
+
const inferredValue = policy.infer(context);
|
|
159
|
+
return inferredValue.trim() !== "" ? { value: inferredValue, source: "inferred" } : void 0;
|
|
160
|
+
}
|
|
161
|
+
function resolvePlural(current, singular, policy, context, metadataLabel) {
|
|
162
|
+
if (current !== void 0) {
|
|
163
|
+
return current;
|
|
164
|
+
}
|
|
165
|
+
if (policy.mode === "require-explicit") {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`Metadata policy requires explicit ${metadataLabel} for ${context.declarationKind} "${context.logicalName}" on the ${context.surface} surface.`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
if (singular === void 0 || policy.mode !== "infer-if-missing") {
|
|
171
|
+
return void 0;
|
|
172
|
+
}
|
|
173
|
+
const pluralValue = policy.inflect({ ...context, singular: singular.value });
|
|
174
|
+
return pluralValue.trim() !== "" ? { value: pluralValue, source: "inferred" } : void 0;
|
|
175
|
+
}
|
|
176
|
+
function resolveResolvedMetadata(current, policy, context) {
|
|
177
|
+
const apiName = resolveScalar(current?.apiName, policy.apiName, context, "apiName");
|
|
178
|
+
const displayName = resolveScalar(
|
|
179
|
+
current?.displayName,
|
|
180
|
+
policy.displayName,
|
|
181
|
+
context,
|
|
182
|
+
"displayName"
|
|
183
|
+
);
|
|
184
|
+
const apiNamePlural = resolvePlural(
|
|
185
|
+
current?.apiNamePlural,
|
|
186
|
+
apiName,
|
|
187
|
+
policy.apiName.pluralization,
|
|
188
|
+
context,
|
|
189
|
+
"apiNamePlural"
|
|
190
|
+
);
|
|
191
|
+
const displayNamePlural = resolvePlural(
|
|
192
|
+
current?.displayNamePlural,
|
|
193
|
+
displayName,
|
|
194
|
+
policy.displayName.pluralization,
|
|
195
|
+
context,
|
|
196
|
+
"displayNamePlural"
|
|
197
|
+
);
|
|
198
|
+
if (apiName === void 0 && displayName === void 0 && apiNamePlural === void 0 && displayNamePlural === void 0) {
|
|
199
|
+
return void 0;
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
...apiName !== void 0 && { apiName },
|
|
203
|
+
...displayName !== void 0 && { displayName },
|
|
204
|
+
...apiNamePlural !== void 0 && { apiNamePlural },
|
|
205
|
+
...displayNamePlural !== void 0 && { displayNamePlural }
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function resolveTypeNodeMetadata(type, options) {
|
|
209
|
+
switch (type.kind) {
|
|
210
|
+
case "array":
|
|
211
|
+
return {
|
|
212
|
+
...type,
|
|
213
|
+
items: resolveTypeNodeMetadata(type.items, options)
|
|
214
|
+
};
|
|
215
|
+
case "object":
|
|
216
|
+
return {
|
|
217
|
+
...type,
|
|
218
|
+
properties: type.properties.map((property) => resolveObjectPropertyMetadata(property, options))
|
|
219
|
+
};
|
|
220
|
+
case "record":
|
|
221
|
+
return {
|
|
222
|
+
...type,
|
|
223
|
+
valueType: resolveTypeNodeMetadata(type.valueType, options)
|
|
224
|
+
};
|
|
225
|
+
case "union":
|
|
226
|
+
return {
|
|
227
|
+
...type,
|
|
228
|
+
members: type.members.map((member) => resolveTypeNodeMetadata(member, options))
|
|
229
|
+
};
|
|
230
|
+
case "reference":
|
|
231
|
+
case "primitive":
|
|
232
|
+
case "enum":
|
|
233
|
+
case "dynamic":
|
|
234
|
+
case "custom":
|
|
235
|
+
return type;
|
|
236
|
+
default: {
|
|
237
|
+
const _exhaustive = type;
|
|
238
|
+
return _exhaustive;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function resolveObjectPropertyMetadata(property, options) {
|
|
243
|
+
const metadata = resolveResolvedMetadata(property.metadata, options.policy.field, {
|
|
244
|
+
surface: options.surface,
|
|
245
|
+
declarationKind: "field",
|
|
246
|
+
logicalName: property.name,
|
|
247
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
248
|
+
});
|
|
249
|
+
return {
|
|
250
|
+
...property,
|
|
251
|
+
...metadata !== void 0 && { metadata },
|
|
252
|
+
type: resolveTypeNodeMetadata(property.type, options)
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function resolveFieldMetadataNode(field, options) {
|
|
256
|
+
const metadata = resolveResolvedMetadata(field.metadata, options.policy.field, {
|
|
257
|
+
surface: options.surface,
|
|
258
|
+
declarationKind: "field",
|
|
259
|
+
logicalName: field.name,
|
|
260
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
261
|
+
});
|
|
262
|
+
return {
|
|
263
|
+
...field,
|
|
264
|
+
...metadata !== void 0 && { metadata },
|
|
265
|
+
type: resolveTypeNodeMetadata(field.type, options)
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function resolveFormElementMetadata(element, options) {
|
|
269
|
+
switch (element.kind) {
|
|
270
|
+
case "field":
|
|
271
|
+
return resolveFieldMetadataNode(element, options);
|
|
272
|
+
case "group":
|
|
273
|
+
return {
|
|
274
|
+
...element,
|
|
275
|
+
elements: element.elements.map((child) => resolveFormElementMetadata(child, options))
|
|
276
|
+
};
|
|
277
|
+
case "conditional":
|
|
278
|
+
return {
|
|
279
|
+
...element,
|
|
280
|
+
elements: element.elements.map((child) => resolveFormElementMetadata(child, options))
|
|
281
|
+
};
|
|
282
|
+
default: {
|
|
283
|
+
const _exhaustive = element;
|
|
284
|
+
return _exhaustive;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function resolveTypeDefinitionMetadata(typeDefinition, options) {
|
|
289
|
+
const metadata = resolveResolvedMetadata(typeDefinition.metadata, options.policy.type, {
|
|
290
|
+
surface: options.surface,
|
|
291
|
+
declarationKind: "type",
|
|
292
|
+
logicalName: typeDefinition.name,
|
|
293
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
294
|
+
});
|
|
295
|
+
return {
|
|
296
|
+
...typeDefinition,
|
|
297
|
+
...metadata !== void 0 && { metadata },
|
|
298
|
+
type: resolveTypeNodeMetadata(typeDefinition.type, options)
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function resolveMetadata(explicit, policy, context) {
|
|
302
|
+
return resolveResolvedMetadata(toExplicitResolvedMetadata(explicit), policy, context);
|
|
303
|
+
}
|
|
304
|
+
function getSerializedName(logicalName, metadata) {
|
|
305
|
+
return metadata?.apiName?.value ?? logicalName;
|
|
306
|
+
}
|
|
307
|
+
function getDisplayName(metadata) {
|
|
308
|
+
return metadata?.displayName?.value;
|
|
309
|
+
}
|
|
310
|
+
function resolveFormIRMetadata(ir, options) {
|
|
311
|
+
const rootLogicalName = options.rootLogicalName ?? ir.name ?? "FormSpec";
|
|
312
|
+
const metadata = resolveResolvedMetadata(ir.metadata, options.policy.type, {
|
|
313
|
+
surface: options.surface,
|
|
314
|
+
declarationKind: "type",
|
|
315
|
+
logicalName: rootLogicalName,
|
|
316
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
317
|
+
});
|
|
318
|
+
return {
|
|
319
|
+
...ir,
|
|
320
|
+
...metadata !== void 0 && { metadata },
|
|
321
|
+
elements: ir.elements.map((element) => resolveFormElementMetadata(element, options)),
|
|
322
|
+
typeRegistry: Object.fromEntries(
|
|
323
|
+
Object.entries(ir.typeRegistry).map(([name, definition]) => [
|
|
324
|
+
name,
|
|
325
|
+
resolveTypeDefinitionMetadata(definition, options)
|
|
326
|
+
])
|
|
327
|
+
)
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// src/canonicalize/chain-dsl-canonicalizer.ts
|
|
56
332
|
var CHAIN_DSL_PROVENANCE = {
|
|
57
333
|
surface: "chain-dsl",
|
|
58
334
|
file: "",
|
|
@@ -68,57 +344,60 @@ function isConditional(el) {
|
|
|
68
344
|
function isField(el) {
|
|
69
345
|
return el._type === "field";
|
|
70
346
|
}
|
|
71
|
-
function canonicalizeChainDSL(form) {
|
|
347
|
+
function canonicalizeChainDSL(form, options) {
|
|
348
|
+
const metadataPolicy = normalizeMetadataPolicy(
|
|
349
|
+
options?.metadata ?? (0, import_internals._getFormSpecMetadataPolicy)(form)
|
|
350
|
+
);
|
|
72
351
|
return {
|
|
73
352
|
kind: "form-ir",
|
|
74
353
|
irVersion: import_internals.IR_VERSION,
|
|
75
|
-
elements: canonicalizeElements(form.elements),
|
|
354
|
+
elements: canonicalizeElements(form.elements, metadataPolicy),
|
|
76
355
|
rootAnnotations: [],
|
|
77
356
|
typeRegistry: {},
|
|
78
357
|
provenance: CHAIN_DSL_PROVENANCE
|
|
79
358
|
};
|
|
80
359
|
}
|
|
81
|
-
function canonicalizeElements(elements) {
|
|
82
|
-
return elements.map(canonicalizeElement);
|
|
360
|
+
function canonicalizeElements(elements, metadataPolicy) {
|
|
361
|
+
return elements.map((element) => canonicalizeElement(element, metadataPolicy));
|
|
83
362
|
}
|
|
84
|
-
function canonicalizeElement(element) {
|
|
363
|
+
function canonicalizeElement(element, metadataPolicy) {
|
|
85
364
|
if (isField(element)) {
|
|
86
|
-
return canonicalizeField(element);
|
|
365
|
+
return canonicalizeField(element, metadataPolicy);
|
|
87
366
|
}
|
|
88
367
|
if (isGroup(element)) {
|
|
89
|
-
return canonicalizeGroup(element);
|
|
368
|
+
return canonicalizeGroup(element, metadataPolicy);
|
|
90
369
|
}
|
|
91
370
|
if (isConditional(element)) {
|
|
92
|
-
return canonicalizeConditional(element);
|
|
371
|
+
return canonicalizeConditional(element, metadataPolicy);
|
|
93
372
|
}
|
|
94
373
|
const _exhaustive = element;
|
|
95
374
|
throw new Error(`Unknown element type: ${JSON.stringify(_exhaustive)}`);
|
|
96
375
|
}
|
|
97
|
-
function canonicalizeField(field) {
|
|
376
|
+
function canonicalizeField(field, metadataPolicy) {
|
|
98
377
|
switch (field._field) {
|
|
99
378
|
case "text":
|
|
100
|
-
return canonicalizeTextField(field);
|
|
379
|
+
return canonicalizeTextField(field, metadataPolicy);
|
|
101
380
|
case "number":
|
|
102
|
-
return canonicalizeNumberField(field);
|
|
381
|
+
return canonicalizeNumberField(field, metadataPolicy);
|
|
103
382
|
case "boolean":
|
|
104
|
-
return canonicalizeBooleanField(field);
|
|
383
|
+
return canonicalizeBooleanField(field, metadataPolicy);
|
|
105
384
|
case "enum":
|
|
106
|
-
return canonicalizeStaticEnumField(field);
|
|
385
|
+
return canonicalizeStaticEnumField(field, metadataPolicy);
|
|
107
386
|
case "dynamic_enum":
|
|
108
|
-
return canonicalizeDynamicEnumField(field);
|
|
387
|
+
return canonicalizeDynamicEnumField(field, metadataPolicy);
|
|
109
388
|
case "dynamic_schema":
|
|
110
|
-
return canonicalizeDynamicSchemaField(field);
|
|
389
|
+
return canonicalizeDynamicSchemaField(field, metadataPolicy);
|
|
111
390
|
case "array":
|
|
112
|
-
return canonicalizeArrayField(field);
|
|
391
|
+
return canonicalizeArrayField(field, metadataPolicy);
|
|
113
392
|
case "object":
|
|
114
|
-
return canonicalizeObjectField(field);
|
|
393
|
+
return canonicalizeObjectField(field, metadataPolicy);
|
|
115
394
|
default: {
|
|
116
395
|
const _exhaustive = field;
|
|
117
396
|
throw new Error(`Unknown field type: ${JSON.stringify(_exhaustive)}`);
|
|
118
397
|
}
|
|
119
398
|
}
|
|
120
399
|
}
|
|
121
|
-
function canonicalizeTextField(field) {
|
|
400
|
+
function canonicalizeTextField(field, metadataPolicy) {
|
|
122
401
|
const type = { kind: "primitive", primitiveKind: "string" };
|
|
123
402
|
const constraints = [];
|
|
124
403
|
if (field.minLength !== void 0) {
|
|
@@ -150,13 +429,14 @@ function canonicalizeTextField(field) {
|
|
|
150
429
|
}
|
|
151
430
|
return buildFieldNode(
|
|
152
431
|
field.name,
|
|
432
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
153
433
|
type,
|
|
154
434
|
field.required,
|
|
155
|
-
buildAnnotations(field
|
|
435
|
+
buildAnnotations(getExplicitDisplayName(field), field.placeholder),
|
|
156
436
|
constraints
|
|
157
437
|
);
|
|
158
438
|
}
|
|
159
|
-
function canonicalizeNumberField(field) {
|
|
439
|
+
function canonicalizeNumberField(field, metadataPolicy) {
|
|
160
440
|
const type = { kind: "primitive", primitiveKind: "number" };
|
|
161
441
|
const constraints = [];
|
|
162
442
|
if (field.min !== void 0) {
|
|
@@ -188,17 +468,24 @@ function canonicalizeNumberField(field) {
|
|
|
188
468
|
}
|
|
189
469
|
return buildFieldNode(
|
|
190
470
|
field.name,
|
|
471
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
191
472
|
type,
|
|
192
473
|
field.required,
|
|
193
|
-
buildAnnotations(field
|
|
474
|
+
buildAnnotations(getExplicitDisplayName(field)),
|
|
194
475
|
constraints
|
|
195
476
|
);
|
|
196
477
|
}
|
|
197
|
-
function canonicalizeBooleanField(field) {
|
|
478
|
+
function canonicalizeBooleanField(field, metadataPolicy) {
|
|
198
479
|
const type = { kind: "primitive", primitiveKind: "boolean" };
|
|
199
|
-
return buildFieldNode(
|
|
480
|
+
return buildFieldNode(
|
|
481
|
+
field.name,
|
|
482
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
483
|
+
type,
|
|
484
|
+
field.required,
|
|
485
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
486
|
+
);
|
|
200
487
|
}
|
|
201
|
-
function canonicalizeStaticEnumField(field) {
|
|
488
|
+
function canonicalizeStaticEnumField(field, metadataPolicy) {
|
|
202
489
|
const members = field.options.map((opt) => {
|
|
203
490
|
if (typeof opt === "string") {
|
|
204
491
|
return { value: opt };
|
|
@@ -206,28 +493,46 @@ function canonicalizeStaticEnumField(field) {
|
|
|
206
493
|
return { value: opt.id, displayName: opt.label };
|
|
207
494
|
});
|
|
208
495
|
const type = { kind: "enum", members };
|
|
209
|
-
return buildFieldNode(
|
|
496
|
+
return buildFieldNode(
|
|
497
|
+
field.name,
|
|
498
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
499
|
+
type,
|
|
500
|
+
field.required,
|
|
501
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
502
|
+
);
|
|
210
503
|
}
|
|
211
|
-
function canonicalizeDynamicEnumField(field) {
|
|
504
|
+
function canonicalizeDynamicEnumField(field, metadataPolicy) {
|
|
212
505
|
const type = {
|
|
213
506
|
kind: "dynamic",
|
|
214
507
|
dynamicKind: "enum",
|
|
215
508
|
sourceKey: field.source,
|
|
216
509
|
parameterFields: field.params ? [...field.params] : []
|
|
217
510
|
};
|
|
218
|
-
return buildFieldNode(
|
|
511
|
+
return buildFieldNode(
|
|
512
|
+
field.name,
|
|
513
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
514
|
+
type,
|
|
515
|
+
field.required,
|
|
516
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
517
|
+
);
|
|
219
518
|
}
|
|
220
|
-
function canonicalizeDynamicSchemaField(field) {
|
|
519
|
+
function canonicalizeDynamicSchemaField(field, metadataPolicy) {
|
|
221
520
|
const type = {
|
|
222
521
|
kind: "dynamic",
|
|
223
522
|
dynamicKind: "schema",
|
|
224
523
|
sourceKey: field.schemaSource,
|
|
225
524
|
parameterFields: []
|
|
226
525
|
};
|
|
227
|
-
return buildFieldNode(
|
|
526
|
+
return buildFieldNode(
|
|
527
|
+
field.name,
|
|
528
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
529
|
+
type,
|
|
530
|
+
field.required,
|
|
531
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
532
|
+
);
|
|
228
533
|
}
|
|
229
|
-
function canonicalizeArrayField(field) {
|
|
230
|
-
const itemProperties = buildObjectProperties(field.items);
|
|
534
|
+
function canonicalizeArrayField(field, metadataPolicy) {
|
|
535
|
+
const itemProperties = buildObjectProperties(field.items, metadataPolicy);
|
|
231
536
|
const itemsType = {
|
|
232
537
|
kind: "object",
|
|
233
538
|
properties: itemProperties,
|
|
@@ -255,37 +560,44 @@ function canonicalizeArrayField(field) {
|
|
|
255
560
|
}
|
|
256
561
|
return buildFieldNode(
|
|
257
562
|
field.name,
|
|
563
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
258
564
|
type,
|
|
259
565
|
field.required,
|
|
260
|
-
buildAnnotations(field
|
|
566
|
+
buildAnnotations(getExplicitDisplayName(field)),
|
|
261
567
|
constraints
|
|
262
568
|
);
|
|
263
569
|
}
|
|
264
|
-
function canonicalizeObjectField(field) {
|
|
265
|
-
const properties = buildObjectProperties(field.properties);
|
|
570
|
+
function canonicalizeObjectField(field, metadataPolicy) {
|
|
571
|
+
const properties = buildObjectProperties(field.properties, metadataPolicy);
|
|
266
572
|
const type = {
|
|
267
573
|
kind: "object",
|
|
268
574
|
properties,
|
|
269
575
|
additionalProperties: true
|
|
270
576
|
};
|
|
271
|
-
return buildFieldNode(
|
|
577
|
+
return buildFieldNode(
|
|
578
|
+
field.name,
|
|
579
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
580
|
+
type,
|
|
581
|
+
field.required,
|
|
582
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
583
|
+
);
|
|
272
584
|
}
|
|
273
|
-
function canonicalizeGroup(g) {
|
|
585
|
+
function canonicalizeGroup(g, metadataPolicy) {
|
|
274
586
|
return {
|
|
275
587
|
kind: "group",
|
|
276
588
|
label: g.label,
|
|
277
|
-
elements: canonicalizeElements(g.elements),
|
|
589
|
+
elements: canonicalizeElements(g.elements, metadataPolicy),
|
|
278
590
|
provenance: CHAIN_DSL_PROVENANCE
|
|
279
591
|
};
|
|
280
592
|
}
|
|
281
|
-
function canonicalizeConditional(c) {
|
|
593
|
+
function canonicalizeConditional(c, metadataPolicy) {
|
|
282
594
|
return {
|
|
283
595
|
kind: "conditional",
|
|
284
596
|
fieldName: c.field,
|
|
285
597
|
// Conditional values from the chain DSL are JSON-serializable primitives
|
|
286
598
|
// (strings, numbers, booleans) produced by the `is()` predicate helper.
|
|
287
599
|
value: assertJsonValue(c.value),
|
|
288
|
-
elements: canonicalizeElements(c.elements),
|
|
600
|
+
elements: canonicalizeElements(c.elements, metadataPolicy),
|
|
289
601
|
provenance: CHAIN_DSL_PROVENANCE
|
|
290
602
|
};
|
|
291
603
|
}
|
|
@@ -305,10 +617,11 @@ function assertJsonValue(v) {
|
|
|
305
617
|
}
|
|
306
618
|
throw new TypeError(`Conditional value is not a valid JsonValue: ${typeof v}`);
|
|
307
619
|
}
|
|
308
|
-
function buildFieldNode(name, type, required, annotations, constraints = []) {
|
|
620
|
+
function buildFieldNode(name, metadata, type, required, annotations, constraints = []) {
|
|
309
621
|
return {
|
|
310
622
|
kind: "field",
|
|
311
623
|
name,
|
|
624
|
+
...metadata !== void 0 && { metadata },
|
|
312
625
|
type,
|
|
313
626
|
required: required === true,
|
|
314
627
|
constraints,
|
|
@@ -338,13 +651,14 @@ function buildAnnotations(label, placeholder) {
|
|
|
338
651
|
}
|
|
339
652
|
return annotations;
|
|
340
653
|
}
|
|
341
|
-
function buildObjectProperties(elements, insideConditional = false) {
|
|
654
|
+
function buildObjectProperties(elements, metadataPolicy, insideConditional = false) {
|
|
342
655
|
const properties = [];
|
|
343
656
|
for (const el of elements) {
|
|
344
657
|
if (isField(el)) {
|
|
345
|
-
const fieldNode = canonicalizeField(el);
|
|
658
|
+
const fieldNode = canonicalizeField(el, metadataPolicy);
|
|
346
659
|
properties.push({
|
|
347
660
|
name: fieldNode.name,
|
|
661
|
+
...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
|
|
348
662
|
type: fieldNode.type,
|
|
349
663
|
// Fields inside a conditional branch are always optional in the
|
|
350
664
|
// data schema, regardless of their `required` flag — the condition
|
|
@@ -355,17 +669,34 @@ function buildObjectProperties(elements, insideConditional = false) {
|
|
|
355
669
|
provenance: CHAIN_DSL_PROVENANCE
|
|
356
670
|
});
|
|
357
671
|
} else if (isGroup(el)) {
|
|
358
|
-
properties.push(...buildObjectProperties(el.elements, insideConditional));
|
|
672
|
+
properties.push(...buildObjectProperties(el.elements, metadataPolicy, insideConditional));
|
|
359
673
|
} else if (isConditional(el)) {
|
|
360
|
-
properties.push(...buildObjectProperties(el.elements, true));
|
|
674
|
+
properties.push(...buildObjectProperties(el.elements, metadataPolicy, true));
|
|
361
675
|
}
|
|
362
676
|
}
|
|
363
677
|
return properties;
|
|
364
678
|
}
|
|
679
|
+
function getExplicitDisplayName(field) {
|
|
680
|
+
if (field.label !== void 0 && field.displayName !== void 0) {
|
|
681
|
+
throw new Error('Chain DSL fields cannot specify both "label" and "displayName".');
|
|
682
|
+
}
|
|
683
|
+
return field.displayName ?? field.label;
|
|
684
|
+
}
|
|
685
|
+
function resolveFieldMetadata(logicalName, field, metadataPolicy) {
|
|
686
|
+
const displayName = getExplicitDisplayName(field);
|
|
687
|
+
return resolveMetadata(
|
|
688
|
+
{
|
|
689
|
+
...field.apiName !== void 0 && { apiName: field.apiName },
|
|
690
|
+
...displayName !== void 0 && { displayName }
|
|
691
|
+
},
|
|
692
|
+
getDeclarationMetadataPolicy(metadataPolicy, "field"),
|
|
693
|
+
makeMetadataContext("chain-dsl", "field", logicalName)
|
|
694
|
+
);
|
|
695
|
+
}
|
|
365
696
|
|
|
366
697
|
// src/canonicalize/tsdoc-canonicalizer.ts
|
|
367
698
|
var import_internals2 = require("@formspec/core/internals");
|
|
368
|
-
function canonicalizeTSDoc(analysis, source) {
|
|
699
|
+
function canonicalizeTSDoc(analysis, source, options) {
|
|
369
700
|
const file = source?.file ?? "";
|
|
370
701
|
const provenance = {
|
|
371
702
|
surface: "tsdoc",
|
|
@@ -374,15 +705,21 @@ function canonicalizeTSDoc(analysis, source) {
|
|
|
374
705
|
column: 0
|
|
375
706
|
};
|
|
376
707
|
const elements = assembleElements(analysis.fields, analysis.fieldLayouts, provenance);
|
|
377
|
-
|
|
708
|
+
const ir = {
|
|
378
709
|
kind: "form-ir",
|
|
710
|
+
name: analysis.name,
|
|
379
711
|
irVersion: import_internals2.IR_VERSION,
|
|
380
712
|
elements,
|
|
713
|
+
...analysis.metadata !== void 0 && { metadata: analysis.metadata },
|
|
381
714
|
typeRegistry: analysis.typeRegistry,
|
|
382
715
|
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { rootAnnotations: analysis.annotations },
|
|
383
716
|
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { annotations: analysis.annotations },
|
|
384
717
|
provenance
|
|
385
718
|
};
|
|
719
|
+
return resolveFormIRMetadata(ir, {
|
|
720
|
+
policy: normalizeMetadataPolicy(options?.metadata),
|
|
721
|
+
surface: "tsdoc"
|
|
722
|
+
});
|
|
386
723
|
}
|
|
387
724
|
function assembleElements(fields, layouts, provenance) {
|
|
388
725
|
const elements = [];
|
|
@@ -1323,7 +1660,76 @@ function makeParseOptions(extensionRegistry, fieldType, checker, subjectType, ho
|
|
|
1323
1660
|
...hostType !== void 0 && { hostType }
|
|
1324
1661
|
};
|
|
1325
1662
|
}
|
|
1326
|
-
function
|
|
1663
|
+
function makeExplicitScalarMetadata(value) {
|
|
1664
|
+
return value === void 0 || value === "" ? void 0 : { value, source: "explicit" };
|
|
1665
|
+
}
|
|
1666
|
+
function extractExplicitMetadata(node) {
|
|
1667
|
+
let apiName;
|
|
1668
|
+
let displayName;
|
|
1669
|
+
let apiNamePlural;
|
|
1670
|
+
let displayNamePlural;
|
|
1671
|
+
for (const tag of getLeadingParsedTags(node)) {
|
|
1672
|
+
const value = tag.argumentText.trim();
|
|
1673
|
+
if (value === "") {
|
|
1674
|
+
continue;
|
|
1675
|
+
}
|
|
1676
|
+
if (tag.normalizedTagName === "apiName") {
|
|
1677
|
+
if (tag.target === null) {
|
|
1678
|
+
apiName ??= value;
|
|
1679
|
+
} else if (tag.target.kind === "variant") {
|
|
1680
|
+
if (tag.target.rawText === "singular") {
|
|
1681
|
+
apiName ??= value;
|
|
1682
|
+
} else if (tag.target.rawText === "plural") {
|
|
1683
|
+
apiNamePlural ??= value;
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
continue;
|
|
1687
|
+
}
|
|
1688
|
+
if (tag.normalizedTagName === "displayName") {
|
|
1689
|
+
if (tag.target === null) {
|
|
1690
|
+
displayName ??= value;
|
|
1691
|
+
} else if (tag.target.kind === "variant") {
|
|
1692
|
+
if (tag.target.rawText === "singular") {
|
|
1693
|
+
displayName ??= value;
|
|
1694
|
+
} else if (tag.target.rawText === "plural") {
|
|
1695
|
+
displayNamePlural ??= value;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
const resolvedApiName = makeExplicitScalarMetadata(apiName);
|
|
1701
|
+
const resolvedDisplayName = makeExplicitScalarMetadata(displayName);
|
|
1702
|
+
const resolvedApiNamePlural = makeExplicitScalarMetadata(apiNamePlural);
|
|
1703
|
+
const resolvedDisplayNamePlural = makeExplicitScalarMetadata(displayNamePlural);
|
|
1704
|
+
const metadata = {
|
|
1705
|
+
...resolvedApiName !== void 0 && { apiName: resolvedApiName },
|
|
1706
|
+
...resolvedDisplayName !== void 0 && { displayName: resolvedDisplayName },
|
|
1707
|
+
...resolvedApiNamePlural !== void 0 && { apiNamePlural: resolvedApiNamePlural },
|
|
1708
|
+
...resolvedDisplayNamePlural !== void 0 && {
|
|
1709
|
+
displayNamePlural: resolvedDisplayNamePlural
|
|
1710
|
+
}
|
|
1711
|
+
};
|
|
1712
|
+
return Object.keys(metadata).length === 0 ? void 0 : metadata;
|
|
1713
|
+
}
|
|
1714
|
+
function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, buildContext) {
|
|
1715
|
+
const explicit = extractExplicitMetadata(node);
|
|
1716
|
+
return resolveMetadata(
|
|
1717
|
+
{
|
|
1718
|
+
...explicit?.apiName !== void 0 && { apiName: explicit.apiName.value },
|
|
1719
|
+
...explicit?.displayName !== void 0 && { displayName: explicit.displayName.value },
|
|
1720
|
+
...explicit?.apiNamePlural !== void 0 && {
|
|
1721
|
+
apiNamePlural: explicit.apiNamePlural.value
|
|
1722
|
+
},
|
|
1723
|
+
...explicit?.displayNamePlural !== void 0 && {
|
|
1724
|
+
displayNamePlural: explicit.displayNamePlural.value
|
|
1725
|
+
}
|
|
1726
|
+
},
|
|
1727
|
+
getDeclarationMetadataPolicy(metadataPolicy, declarationKind),
|
|
1728
|
+
makeMetadataContext("tsdoc", declarationKind, logicalName, buildContext)
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
1731
|
+
function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy) {
|
|
1732
|
+
const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
|
|
1327
1733
|
const name = classDecl.name?.text ?? "AnonymousClass";
|
|
1328
1734
|
const fields = [];
|
|
1329
1735
|
const fieldLayouts = [];
|
|
@@ -1350,6 +1756,7 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
1350
1756
|
visiting,
|
|
1351
1757
|
diagnostics,
|
|
1352
1758
|
classType,
|
|
1759
|
+
normalizedMetadataPolicy,
|
|
1353
1760
|
extensionRegistry
|
|
1354
1761
|
);
|
|
1355
1762
|
if (fieldNode) {
|
|
@@ -1374,10 +1781,18 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
1374
1781
|
classType,
|
|
1375
1782
|
checker,
|
|
1376
1783
|
file,
|
|
1377
|
-
diagnostics
|
|
1784
|
+
diagnostics,
|
|
1785
|
+
normalizedMetadataPolicy
|
|
1378
1786
|
);
|
|
1787
|
+
const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, classDecl, {
|
|
1788
|
+
checker,
|
|
1789
|
+
declaration: classDecl,
|
|
1790
|
+
subjectType: classType,
|
|
1791
|
+
hostType: classType
|
|
1792
|
+
});
|
|
1379
1793
|
return {
|
|
1380
1794
|
name,
|
|
1795
|
+
...metadata !== void 0 && { metadata },
|
|
1381
1796
|
fields: specializedFields,
|
|
1382
1797
|
fieldLayouts,
|
|
1383
1798
|
typeRegistry,
|
|
@@ -1387,7 +1802,8 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
1387
1802
|
staticMethods
|
|
1388
1803
|
};
|
|
1389
1804
|
}
|
|
1390
|
-
function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry) {
|
|
1805
|
+
function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy) {
|
|
1806
|
+
const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
|
|
1391
1807
|
const name = interfaceDecl.name.text;
|
|
1392
1808
|
const fields = [];
|
|
1393
1809
|
const typeRegistry = {};
|
|
@@ -1411,6 +1827,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
1411
1827
|
visiting,
|
|
1412
1828
|
diagnostics,
|
|
1413
1829
|
interfaceType,
|
|
1830
|
+
normalizedMetadataPolicy,
|
|
1414
1831
|
extensionRegistry
|
|
1415
1832
|
);
|
|
1416
1833
|
if (fieldNode) {
|
|
@@ -1424,11 +1841,19 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
1424
1841
|
interfaceType,
|
|
1425
1842
|
checker,
|
|
1426
1843
|
file,
|
|
1427
|
-
diagnostics
|
|
1844
|
+
diagnostics,
|
|
1845
|
+
normalizedMetadataPolicy
|
|
1428
1846
|
);
|
|
1429
1847
|
const fieldLayouts = specializedFields.map(() => ({}));
|
|
1848
|
+
const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, interfaceDecl, {
|
|
1849
|
+
checker,
|
|
1850
|
+
declaration: interfaceDecl,
|
|
1851
|
+
subjectType: interfaceType,
|
|
1852
|
+
hostType: interfaceType
|
|
1853
|
+
});
|
|
1430
1854
|
return {
|
|
1431
1855
|
name,
|
|
1856
|
+
...metadata !== void 0 && { metadata },
|
|
1432
1857
|
fields: specializedFields,
|
|
1433
1858
|
fieldLayouts,
|
|
1434
1859
|
typeRegistry,
|
|
@@ -1438,7 +1863,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
1438
1863
|
staticMethods: []
|
|
1439
1864
|
};
|
|
1440
1865
|
}
|
|
1441
|
-
function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
|
|
1866
|
+
function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry, metadataPolicy) {
|
|
1442
1867
|
if (!ts3.isTypeLiteralNode(typeAlias.type)) {
|
|
1443
1868
|
const sourceFile = typeAlias.getSourceFile();
|
|
1444
1869
|
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
@@ -1448,6 +1873,8 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
1448
1873
|
error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object type literal (found ${kindDesc})`
|
|
1449
1874
|
};
|
|
1450
1875
|
}
|
|
1876
|
+
const typeLiteral = typeAlias.type;
|
|
1877
|
+
const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
|
|
1451
1878
|
const name = typeAlias.name.text;
|
|
1452
1879
|
const fields = [];
|
|
1453
1880
|
const typeRegistry = {};
|
|
@@ -1461,7 +1888,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
1461
1888
|
const annotations = [...typeAliasDoc.annotations];
|
|
1462
1889
|
diagnostics.push(...typeAliasDoc.diagnostics);
|
|
1463
1890
|
const visiting = /* @__PURE__ */ new Set();
|
|
1464
|
-
for (const member of
|
|
1891
|
+
for (const member of typeLiteral.members) {
|
|
1465
1892
|
if (ts3.isPropertySignature(member)) {
|
|
1466
1893
|
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1467
1894
|
member,
|
|
@@ -1471,6 +1898,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
1471
1898
|
visiting,
|
|
1472
1899
|
diagnostics,
|
|
1473
1900
|
aliasType,
|
|
1901
|
+
normalizedMetadataPolicy,
|
|
1474
1902
|
extensionRegistry
|
|
1475
1903
|
);
|
|
1476
1904
|
if (fieldNode) {
|
|
@@ -1484,12 +1912,20 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
1484
1912
|
aliasType,
|
|
1485
1913
|
checker,
|
|
1486
1914
|
file,
|
|
1487
|
-
diagnostics
|
|
1915
|
+
diagnostics,
|
|
1916
|
+
normalizedMetadataPolicy
|
|
1488
1917
|
);
|
|
1918
|
+
const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, typeAlias, {
|
|
1919
|
+
checker,
|
|
1920
|
+
declaration: typeAlias,
|
|
1921
|
+
subjectType: aliasType,
|
|
1922
|
+
hostType: aliasType
|
|
1923
|
+
});
|
|
1489
1924
|
return {
|
|
1490
1925
|
ok: true,
|
|
1491
1926
|
analysis: {
|
|
1492
1927
|
name,
|
|
1928
|
+
...metadata !== void 0 && { metadata },
|
|
1493
1929
|
fields: specializedFields,
|
|
1494
1930
|
fieldLayouts: specializedFields.map(() => ({})),
|
|
1495
1931
|
typeRegistry,
|
|
@@ -1529,31 +1965,20 @@ function getLeadingParsedTags(node) {
|
|
|
1529
1965
|
}
|
|
1530
1966
|
return parsedTags;
|
|
1531
1967
|
}
|
|
1532
|
-
function
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
return member;
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
return null;
|
|
1540
|
-
}
|
|
1541
|
-
if (ts3.isInterfaceDeclaration(node)) {
|
|
1542
|
-
for (const member of node.members) {
|
|
1543
|
-
if (ts3.isPropertySignature(member) && ts3.isIdentifier(member.name) && member.name.text === fieldName) {
|
|
1544
|
-
return member;
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1968
|
+
function resolveDiscriminatorProperty(node, checker, fieldName) {
|
|
1969
|
+
const subjectType = checker.getTypeAtLocation(node);
|
|
1970
|
+
const propertySymbol = subjectType.getProperty(fieldName);
|
|
1971
|
+
if (propertySymbol === void 0) {
|
|
1547
1972
|
return null;
|
|
1548
1973
|
}
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1974
|
+
const declaration = propertySymbol.valueDeclaration ?? propertySymbol.declarations?.find(
|
|
1975
|
+
(candidate) => ts3.isPropertyDeclaration(candidate) || ts3.isPropertySignature(candidate)
|
|
1976
|
+
) ?? propertySymbol.declarations?.[0];
|
|
1977
|
+
return {
|
|
1978
|
+
declaration,
|
|
1979
|
+
type: checker.getTypeOfSymbolAtLocation(propertySymbol, declaration ?? node),
|
|
1980
|
+
optional: !!(propertySymbol.flags & ts3.SymbolFlags.Optional) || declaration !== void 0 && "questionToken" in declaration && declaration.questionToken !== void 0
|
|
1981
|
+
};
|
|
1557
1982
|
}
|
|
1558
1983
|
function isLocalTypeParameterName(node, typeParameterName) {
|
|
1559
1984
|
return node.typeParameters?.some((typeParameter) => typeParameter.name.text === typeParameterName) ?? false;
|
|
@@ -1646,8 +2071,8 @@ function validateDiscriminatorDirective(node, checker, file, diagnostics) {
|
|
|
1646
2071
|
);
|
|
1647
2072
|
return null;
|
|
1648
2073
|
}
|
|
1649
|
-
const
|
|
1650
|
-
if (
|
|
2074
|
+
const property = resolveDiscriminatorProperty(node, checker, directive.fieldName);
|
|
2075
|
+
if (property === null) {
|
|
1651
2076
|
diagnostics.push(
|
|
1652
2077
|
makeAnalysisDiagnostic(
|
|
1653
2078
|
"UNKNOWN_PATH_TARGET",
|
|
@@ -1657,36 +2082,35 @@ function validateDiscriminatorDirective(node, checker, file, diagnostics) {
|
|
|
1657
2082
|
);
|
|
1658
2083
|
return null;
|
|
1659
2084
|
}
|
|
1660
|
-
if (
|
|
2085
|
+
if (property.optional) {
|
|
1661
2086
|
diagnostics.push(
|
|
1662
2087
|
makeAnalysisDiagnostic(
|
|
1663
2088
|
"TYPE_MISMATCH",
|
|
1664
2089
|
`Discriminator field "${directive.fieldName}" must be required; optional discriminator fields are not supported.`,
|
|
1665
2090
|
directive.provenance,
|
|
1666
|
-
[provenanceForNode(
|
|
2091
|
+
property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
|
|
1667
2092
|
)
|
|
1668
2093
|
);
|
|
1669
2094
|
return null;
|
|
1670
2095
|
}
|
|
1671
|
-
|
|
1672
|
-
if (isNullishSemanticType(propertyType)) {
|
|
2096
|
+
if (isNullishSemanticType(property.type)) {
|
|
1673
2097
|
diagnostics.push(
|
|
1674
2098
|
makeAnalysisDiagnostic(
|
|
1675
2099
|
"TYPE_MISMATCH",
|
|
1676
2100
|
`Discriminator field "${directive.fieldName}" must not be nullable.`,
|
|
1677
2101
|
directive.provenance,
|
|
1678
|
-
[provenanceForNode(
|
|
2102
|
+
property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
|
|
1679
2103
|
)
|
|
1680
2104
|
);
|
|
1681
2105
|
return null;
|
|
1682
2106
|
}
|
|
1683
|
-
if (!isStringLikeSemanticType(
|
|
2107
|
+
if (!isStringLikeSemanticType(property.type)) {
|
|
1684
2108
|
diagnostics.push(
|
|
1685
2109
|
makeAnalysisDiagnostic(
|
|
1686
2110
|
"TYPE_MISMATCH",
|
|
1687
2111
|
`Discriminator field "${directive.fieldName}" must be string-like.`,
|
|
1688
2112
|
directive.provenance,
|
|
1689
|
-
[provenanceForNode(
|
|
2113
|
+
property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
|
|
1690
2114
|
)
|
|
1691
2115
|
);
|
|
1692
2116
|
return null;
|
|
@@ -1707,25 +2131,58 @@ function getConcreteTypeArgumentForDiscriminator(node, subjectType, checker, typ
|
|
|
1707
2131
|
const localTypeParameter = node.typeParameters?.[typeParameterIndex];
|
|
1708
2132
|
return localTypeParameter === void 0 ? null : checker.getTypeAtLocation(localTypeParameter);
|
|
1709
2133
|
}
|
|
1710
|
-
function
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
2134
|
+
function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker, provenance, diagnostics) {
|
|
2135
|
+
const propertySymbol = boundType.getProperty(fieldName);
|
|
2136
|
+
if (propertySymbol === void 0) {
|
|
2137
|
+
return void 0;
|
|
2138
|
+
}
|
|
2139
|
+
const declaration = propertySymbol.valueDeclaration ?? propertySymbol.declarations?.[0];
|
|
2140
|
+
const anchorNode = declaration ?? boundType.symbol.declarations?.[0] ?? null;
|
|
2141
|
+
const resolvedAnchorNode = anchorNode ?? resolveNamedDiscriminatorDeclaration(boundType, checker);
|
|
2142
|
+
if (resolvedAnchorNode === null) {
|
|
2143
|
+
return void 0;
|
|
2144
|
+
}
|
|
2145
|
+
const propertyType = checker.getTypeOfSymbolAtLocation(
|
|
2146
|
+
propertySymbol,
|
|
2147
|
+
resolvedAnchorNode
|
|
2148
|
+
);
|
|
2149
|
+
if (propertyType.isStringLiteral()) {
|
|
2150
|
+
return propertyType.value;
|
|
2151
|
+
}
|
|
2152
|
+
if (propertyType.isUnion()) {
|
|
2153
|
+
const nonNullMembers = propertyType.types.filter(
|
|
2154
|
+
(member) => !(member.flags & (ts3.TypeFlags.Null | ts3.TypeFlags.Undefined))
|
|
2155
|
+
);
|
|
2156
|
+
if (nonNullMembers.length > 0 && nonNullMembers.every((member) => member.isStringLiteral())) {
|
|
2157
|
+
diagnostics.push(
|
|
2158
|
+
makeAnalysisDiagnostic(
|
|
2159
|
+
"INVALID_TAG_ARGUMENT",
|
|
2160
|
+
"Discriminator resolution for union-valued identity properties is out of scope for v1.",
|
|
2161
|
+
provenance
|
|
2162
|
+
)
|
|
2163
|
+
);
|
|
2164
|
+
return null;
|
|
1723
2165
|
}
|
|
1724
2166
|
}
|
|
1725
|
-
return
|
|
2167
|
+
return void 0;
|
|
1726
2168
|
}
|
|
1727
|
-
function
|
|
1728
|
-
|
|
2169
|
+
function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
|
|
2170
|
+
const declaration = resolveNamedDiscriminatorDeclaration(boundType, checker);
|
|
2171
|
+
if (declaration === null) {
|
|
2172
|
+
return void 0;
|
|
2173
|
+
}
|
|
2174
|
+
const metadata = resolveNodeMetadata(
|
|
2175
|
+
metadataPolicy,
|
|
2176
|
+
"type",
|
|
2177
|
+
getDiscriminatorLogicalName(boundType, declaration, checker),
|
|
2178
|
+
declaration,
|
|
2179
|
+
{
|
|
2180
|
+
checker,
|
|
2181
|
+
declaration,
|
|
2182
|
+
subjectType: boundType
|
|
2183
|
+
}
|
|
2184
|
+
);
|
|
2185
|
+
return metadata?.apiName;
|
|
1729
2186
|
}
|
|
1730
2187
|
function resolveNamedDiscriminatorDeclaration(type, checker, seen = /* @__PURE__ */ new Set()) {
|
|
1731
2188
|
if (seen.has(type)) {
|
|
@@ -1752,7 +2209,7 @@ function resolveNamedDiscriminatorDeclaration(type, checker, seen = /* @__PURE__
|
|
|
1752
2209
|
}
|
|
1753
2210
|
return null;
|
|
1754
2211
|
}
|
|
1755
|
-
function resolveDiscriminatorValue(boundType, checker, provenance, diagnostics) {
|
|
2212
|
+
function resolveDiscriminatorValue(boundType, fieldName, checker, provenance, diagnostics, metadataPolicy) {
|
|
1756
2213
|
if (boundType === null) {
|
|
1757
2214
|
diagnostics.push(
|
|
1758
2215
|
makeAnalysisDiagnostic(
|
|
@@ -1781,9 +2238,22 @@ function resolveDiscriminatorValue(boundType, checker, provenance, diagnostics)
|
|
|
1781
2238
|
return null;
|
|
1782
2239
|
}
|
|
1783
2240
|
}
|
|
1784
|
-
const
|
|
1785
|
-
|
|
1786
|
-
|
|
2241
|
+
const literalIdentityValue = resolveLiteralDiscriminatorPropertyValue(
|
|
2242
|
+
boundType,
|
|
2243
|
+
fieldName,
|
|
2244
|
+
checker,
|
|
2245
|
+
provenance,
|
|
2246
|
+
diagnostics
|
|
2247
|
+
);
|
|
2248
|
+
if (literalIdentityValue !== void 0) {
|
|
2249
|
+
return literalIdentityValue;
|
|
2250
|
+
}
|
|
2251
|
+
const apiName = resolveDiscriminatorApiName(boundType, checker, metadataPolicy);
|
|
2252
|
+
if (apiName?.source === "explicit") {
|
|
2253
|
+
return apiName.value;
|
|
2254
|
+
}
|
|
2255
|
+
if (apiName?.source === "inferred") {
|
|
2256
|
+
return apiName.value;
|
|
1787
2257
|
}
|
|
1788
2258
|
diagnostics.push(
|
|
1789
2259
|
makeAnalysisDiagnostic(
|
|
@@ -1800,7 +2270,15 @@ function getDeclarationName(node) {
|
|
|
1800
2270
|
}
|
|
1801
2271
|
return "anonymous";
|
|
1802
2272
|
}
|
|
1803
|
-
function
|
|
2273
|
+
function getResolvedTypeArguments(type) {
|
|
2274
|
+
return (isTypeReference(type) ? type.typeArguments : void 0) ?? type.aliasTypeArguments ?? [];
|
|
2275
|
+
}
|
|
2276
|
+
function getDiscriminatorLogicalName(type, declaration, checker) {
|
|
2277
|
+
const baseName = getDeclarationName(declaration);
|
|
2278
|
+
const typeArguments = getResolvedTypeArguments(type);
|
|
2279
|
+
return typeArguments.length === 0 ? baseName : buildInstantiatedReferenceName(baseName, typeArguments, checker);
|
|
2280
|
+
}
|
|
2281
|
+
function applyDeclarationDiscriminatorToFields(fields, node, subjectType, checker, file, diagnostics, metadataPolicy) {
|
|
1804
2282
|
const directive = validateDiscriminatorDirective(node, checker, file, diagnostics);
|
|
1805
2283
|
if (directive === null) {
|
|
1806
2284
|
return [...fields];
|
|
@@ -1812,9 +2290,11 @@ function applyDeclarationDiscriminatorToFields(fields, node, subjectType, checke
|
|
|
1812
2290
|
checker,
|
|
1813
2291
|
directive.typeParameterName
|
|
1814
2292
|
),
|
|
2293
|
+
directive.fieldName,
|
|
1815
2294
|
checker,
|
|
1816
2295
|
directive.provenance,
|
|
1817
|
-
diagnostics
|
|
2296
|
+
diagnostics,
|
|
2297
|
+
metadataPolicy
|
|
1818
2298
|
);
|
|
1819
2299
|
if (discriminatorValue === null) {
|
|
1820
2300
|
return [...fields];
|
|
@@ -1835,7 +2315,7 @@ function buildInstantiatedReferenceName(baseName, typeArguments, checker) {
|
|
|
1835
2315
|
).filter((value) => value !== "");
|
|
1836
2316
|
return renderedArguments.length === 0 ? baseName : `${baseName}__${renderedArguments.join("__")}`;
|
|
1837
2317
|
}
|
|
1838
|
-
function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
2318
|
+
function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy, extensionRegistry, diagnostics) {
|
|
1839
2319
|
const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
|
|
1840
2320
|
if (typeNode === void 0) {
|
|
1841
2321
|
return [];
|
|
@@ -1855,13 +2335,14 @@ function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiti
|
|
|
1855
2335
|
typeRegistry,
|
|
1856
2336
|
visiting,
|
|
1857
2337
|
argumentNode,
|
|
2338
|
+
metadataPolicy,
|
|
1858
2339
|
extensionRegistry,
|
|
1859
2340
|
diagnostics
|
|
1860
2341
|
)
|
|
1861
2342
|
};
|
|
1862
2343
|
});
|
|
1863
2344
|
}
|
|
1864
|
-
function applyDiscriminatorToObjectProperties(properties, node, subjectType, checker, file, diagnostics) {
|
|
2345
|
+
function applyDiscriminatorToObjectProperties(properties, node, subjectType, checker, file, diagnostics, metadataPolicy) {
|
|
1865
2346
|
const directive = validateDiscriminatorDirective(node, checker, file, diagnostics);
|
|
1866
2347
|
if (directive === null) {
|
|
1867
2348
|
return properties;
|
|
@@ -1873,9 +2354,11 @@ function applyDiscriminatorToObjectProperties(properties, node, subjectType, che
|
|
|
1873
2354
|
checker,
|
|
1874
2355
|
directive.typeParameterName
|
|
1875
2356
|
),
|
|
2357
|
+
directive.fieldName,
|
|
1876
2358
|
checker,
|
|
1877
2359
|
directive.provenance,
|
|
1878
|
-
diagnostics
|
|
2360
|
+
diagnostics,
|
|
2361
|
+
metadataPolicy
|
|
1879
2362
|
);
|
|
1880
2363
|
if (discriminatorValue === null) {
|
|
1881
2364
|
return properties;
|
|
@@ -1890,7 +2373,7 @@ function applyDiscriminatorToObjectProperties(properties, node, subjectType, che
|
|
|
1890
2373
|
} : property
|
|
1891
2374
|
);
|
|
1892
2375
|
}
|
|
1893
|
-
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, extensionRegistry) {
|
|
2376
|
+
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, metadataPolicy, extensionRegistry) {
|
|
1894
2377
|
if (!ts3.isIdentifier(prop.name)) {
|
|
1895
2378
|
return null;
|
|
1896
2379
|
}
|
|
@@ -1905,6 +2388,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
|
|
|
1905
2388
|
typeRegistry,
|
|
1906
2389
|
visiting,
|
|
1907
2390
|
prop,
|
|
2391
|
+
metadataPolicy,
|
|
1908
2392
|
extensionRegistry,
|
|
1909
2393
|
diagnostics
|
|
1910
2394
|
);
|
|
@@ -1928,9 +2412,16 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
|
|
|
1928
2412
|
annotations.push(defaultAnnotation);
|
|
1929
2413
|
}
|
|
1930
2414
|
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
2415
|
+
const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
|
|
2416
|
+
checker,
|
|
2417
|
+
declaration: prop,
|
|
2418
|
+
subjectType: tsType,
|
|
2419
|
+
hostType
|
|
2420
|
+
});
|
|
1931
2421
|
return {
|
|
1932
2422
|
kind: "field",
|
|
1933
2423
|
name,
|
|
2424
|
+
...metadata !== void 0 && { metadata },
|
|
1934
2425
|
type,
|
|
1935
2426
|
required: !optional,
|
|
1936
2427
|
constraints,
|
|
@@ -1938,7 +2429,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
|
|
|
1938
2429
|
provenance
|
|
1939
2430
|
};
|
|
1940
2431
|
}
|
|
1941
|
-
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, extensionRegistry) {
|
|
2432
|
+
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, metadataPolicy, extensionRegistry) {
|
|
1942
2433
|
if (!ts3.isIdentifier(prop.name)) {
|
|
1943
2434
|
return null;
|
|
1944
2435
|
}
|
|
@@ -1953,6 +2444,7 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
1953
2444
|
typeRegistry,
|
|
1954
2445
|
visiting,
|
|
1955
2446
|
prop,
|
|
2447
|
+
metadataPolicy,
|
|
1956
2448
|
extensionRegistry,
|
|
1957
2449
|
diagnostics
|
|
1958
2450
|
);
|
|
@@ -1972,9 +2464,16 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
1972
2464
|
let annotations = [];
|
|
1973
2465
|
annotations.push(...docResult.annotations);
|
|
1974
2466
|
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
2467
|
+
const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
|
|
2468
|
+
checker,
|
|
2469
|
+
declaration: prop,
|
|
2470
|
+
subjectType: tsType,
|
|
2471
|
+
hostType
|
|
2472
|
+
});
|
|
1975
2473
|
return {
|
|
1976
2474
|
kind: "field",
|
|
1977
2475
|
name,
|
|
2476
|
+
...metadata !== void 0 && { metadata },
|
|
1978
2477
|
type,
|
|
1979
2478
|
required: !optional,
|
|
1980
2479
|
constraints,
|
|
@@ -2099,7 +2598,7 @@ function getTypeNodeRegistrationName(typeNode) {
|
|
|
2099
2598
|
}
|
|
2100
2599
|
return null;
|
|
2101
2600
|
}
|
|
2102
|
-
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
2601
|
+
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2103
2602
|
const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
|
|
2104
2603
|
if (customType) {
|
|
2105
2604
|
return customType;
|
|
@@ -2111,6 +2610,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2111
2610
|
typeRegistry,
|
|
2112
2611
|
visiting,
|
|
2113
2612
|
sourceNode,
|
|
2613
|
+
metadataPolicy,
|
|
2114
2614
|
extensionRegistry,
|
|
2115
2615
|
diagnostics
|
|
2116
2616
|
);
|
|
@@ -2155,6 +2655,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2155
2655
|
typeRegistry,
|
|
2156
2656
|
visiting,
|
|
2157
2657
|
sourceNode,
|
|
2658
|
+
metadataPolicy,
|
|
2158
2659
|
extensionRegistry,
|
|
2159
2660
|
diagnostics
|
|
2160
2661
|
);
|
|
@@ -2167,6 +2668,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2167
2668
|
typeRegistry,
|
|
2168
2669
|
visiting,
|
|
2169
2670
|
sourceNode,
|
|
2671
|
+
metadataPolicy,
|
|
2170
2672
|
extensionRegistry,
|
|
2171
2673
|
diagnostics
|
|
2172
2674
|
);
|
|
@@ -2179,13 +2681,14 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2179
2681
|
typeRegistry,
|
|
2180
2682
|
visiting,
|
|
2181
2683
|
sourceNode,
|
|
2684
|
+
metadataPolicy,
|
|
2182
2685
|
extensionRegistry,
|
|
2183
2686
|
diagnostics
|
|
2184
2687
|
);
|
|
2185
2688
|
}
|
|
2186
2689
|
return { kind: "primitive", primitiveKind: "string" };
|
|
2187
2690
|
}
|
|
2188
|
-
function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
2691
|
+
function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2189
2692
|
if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
|
|
2190
2693
|
return null;
|
|
2191
2694
|
}
|
|
@@ -2205,14 +2708,21 @@ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiti
|
|
|
2205
2708
|
file,
|
|
2206
2709
|
makeParseOptions(extensionRegistry)
|
|
2207
2710
|
);
|
|
2711
|
+
const metadata = resolveNodeMetadata(metadataPolicy, "type", aliasName, aliasDecl, {
|
|
2712
|
+
checker,
|
|
2713
|
+
declaration: aliasDecl,
|
|
2714
|
+
subjectType: aliasType
|
|
2715
|
+
});
|
|
2208
2716
|
typeRegistry[aliasName] = {
|
|
2209
2717
|
name: aliasName,
|
|
2718
|
+
...metadata !== void 0 && { metadata },
|
|
2210
2719
|
type: resolveAliasedPrimitiveTarget(
|
|
2211
2720
|
aliasType,
|
|
2212
2721
|
checker,
|
|
2213
2722
|
file,
|
|
2214
2723
|
typeRegistry,
|
|
2215
2724
|
visiting,
|
|
2725
|
+
metadataPolicy,
|
|
2216
2726
|
extensionRegistry,
|
|
2217
2727
|
diagnostics
|
|
2218
2728
|
),
|
|
@@ -2241,7 +2751,7 @@ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
|
|
|
2241
2751
|
const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
|
|
2242
2752
|
return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
|
|
2243
2753
|
}
|
|
2244
|
-
function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, extensionRegistry, diagnostics) {
|
|
2754
|
+
function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2245
2755
|
const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
|
|
2246
2756
|
if (nestedAliasDecl !== void 0) {
|
|
2247
2757
|
return resolveAliasedPrimitiveTarget(
|
|
@@ -2250,6 +2760,7 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
|
|
|
2250
2760
|
file,
|
|
2251
2761
|
typeRegistry,
|
|
2252
2762
|
visiting,
|
|
2763
|
+
metadataPolicy,
|
|
2253
2764
|
extensionRegistry,
|
|
2254
2765
|
diagnostics
|
|
2255
2766
|
);
|
|
@@ -2261,11 +2772,12 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
|
|
|
2261
2772
|
typeRegistry,
|
|
2262
2773
|
visiting,
|
|
2263
2774
|
void 0,
|
|
2775
|
+
metadataPolicy,
|
|
2264
2776
|
extensionRegistry,
|
|
2265
2777
|
diagnostics
|
|
2266
2778
|
);
|
|
2267
2779
|
}
|
|
2268
|
-
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
2780
|
+
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2269
2781
|
const typeName = getNamedTypeName(type);
|
|
2270
2782
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
2271
2783
|
if (typeName && typeName in typeRegistry) {
|
|
@@ -2300,8 +2812,14 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2300
2812
|
return result;
|
|
2301
2813
|
}
|
|
2302
2814
|
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
2815
|
+
const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", typeName, namedDecl, {
|
|
2816
|
+
checker,
|
|
2817
|
+
declaration: namedDecl,
|
|
2818
|
+
subjectType: type
|
|
2819
|
+
}) : void 0;
|
|
2303
2820
|
typeRegistry[typeName] = {
|
|
2304
2821
|
name: typeName,
|
|
2822
|
+
...metadata !== void 0 && { metadata },
|
|
2305
2823
|
type: result,
|
|
2306
2824
|
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2307
2825
|
provenance: provenanceForDeclaration(namedDecl ?? sourceNode, file)
|
|
@@ -2355,6 +2873,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2355
2873
|
typeRegistry,
|
|
2356
2874
|
visiting,
|
|
2357
2875
|
nonNullMembers[0].sourceNode ?? sourceNode,
|
|
2876
|
+
metadataPolicy,
|
|
2358
2877
|
extensionRegistry,
|
|
2359
2878
|
diagnostics
|
|
2360
2879
|
);
|
|
@@ -2372,6 +2891,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2372
2891
|
typeRegistry,
|
|
2373
2892
|
visiting,
|
|
2374
2893
|
memberSourceNode ?? sourceNode,
|
|
2894
|
+
metadataPolicy,
|
|
2375
2895
|
extensionRegistry,
|
|
2376
2896
|
diagnostics
|
|
2377
2897
|
)
|
|
@@ -2381,7 +2901,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2381
2901
|
}
|
|
2382
2902
|
return registerNamed({ kind: "union", members });
|
|
2383
2903
|
}
|
|
2384
|
-
function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
2904
|
+
function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2385
2905
|
const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
|
|
2386
2906
|
const elementType = typeArgs?.[0];
|
|
2387
2907
|
const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
|
|
@@ -2392,12 +2912,13 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2392
2912
|
typeRegistry,
|
|
2393
2913
|
visiting,
|
|
2394
2914
|
elementSourceNode,
|
|
2915
|
+
metadataPolicy,
|
|
2395
2916
|
extensionRegistry,
|
|
2396
2917
|
diagnostics
|
|
2397
2918
|
) : { kind: "primitive", primitiveKind: "string" };
|
|
2398
2919
|
return { kind: "array", items };
|
|
2399
2920
|
}
|
|
2400
|
-
function tryResolveRecordType(type, checker, file, typeRegistry, visiting, extensionRegistry, diagnostics) {
|
|
2921
|
+
function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2401
2922
|
if (type.getProperties().length > 0) {
|
|
2402
2923
|
return null;
|
|
2403
2924
|
}
|
|
@@ -2412,6 +2933,7 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, exten
|
|
|
2412
2933
|
typeRegistry,
|
|
2413
2934
|
visiting,
|
|
2414
2935
|
void 0,
|
|
2936
|
+
metadataPolicy,
|
|
2415
2937
|
extensionRegistry,
|
|
2416
2938
|
diagnostics
|
|
2417
2939
|
);
|
|
@@ -2442,7 +2964,22 @@ function typeNodeContainsReference(type, targetName) {
|
|
|
2442
2964
|
}
|
|
2443
2965
|
}
|
|
2444
2966
|
}
|
|
2445
|
-
function
|
|
2967
|
+
function shouldEmitResolvedObjectProperty(property, declaration) {
|
|
2968
|
+
if (property.name.startsWith("__@")) {
|
|
2969
|
+
return false;
|
|
2970
|
+
}
|
|
2971
|
+
if (declaration !== void 0 && "name" in declaration && declaration.name !== void 0) {
|
|
2972
|
+
const name = declaration.name;
|
|
2973
|
+
if (ts3.isComputedPropertyName(name) || ts3.isPrivateIdentifier(name)) {
|
|
2974
|
+
return false;
|
|
2975
|
+
}
|
|
2976
|
+
if (!ts3.isIdentifier(name) && !ts3.isStringLiteral(name) && !ts3.isNumericLiteral(name)) {
|
|
2977
|
+
return false;
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
return true;
|
|
2981
|
+
}
|
|
2982
|
+
function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2446
2983
|
const collectedDiagnostics = diagnostics ?? [];
|
|
2447
2984
|
const typeName = getNamedTypeName(type);
|
|
2448
2985
|
const namedTypeName = typeName ?? void 0;
|
|
@@ -2454,6 +2991,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
|
|
|
2454
2991
|
typeRegistry,
|
|
2455
2992
|
visiting,
|
|
2456
2993
|
sourceNode,
|
|
2994
|
+
metadataPolicy,
|
|
2457
2995
|
extensionRegistry,
|
|
2458
2996
|
collectedDiagnostics
|
|
2459
2997
|
);
|
|
@@ -2504,6 +3042,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
|
|
|
2504
3042
|
file,
|
|
2505
3043
|
typeRegistry,
|
|
2506
3044
|
visiting,
|
|
3045
|
+
metadataPolicy,
|
|
2507
3046
|
extensionRegistry,
|
|
2508
3047
|
collectedDiagnostics
|
|
2509
3048
|
);
|
|
@@ -2516,8 +3055,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
|
|
|
2516
3055
|
return recordNode;
|
|
2517
3056
|
}
|
|
2518
3057
|
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
3058
|
+
const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
|
|
3059
|
+
checker,
|
|
3060
|
+
declaration: namedDecl,
|
|
3061
|
+
subjectType: type
|
|
3062
|
+
}) : void 0;
|
|
2519
3063
|
typeRegistry[registryTypeName] = {
|
|
2520
3064
|
name: registryTypeName,
|
|
3065
|
+
...metadata !== void 0 && { metadata },
|
|
2521
3066
|
type: recordNode,
|
|
2522
3067
|
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2523
3068
|
provenance: provenanceForDeclaration(namedDecl, file)
|
|
@@ -2537,12 +3082,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
|
|
|
2537
3082
|
file,
|
|
2538
3083
|
typeRegistry,
|
|
2539
3084
|
visiting,
|
|
3085
|
+
metadataPolicy,
|
|
2540
3086
|
collectedDiagnostics,
|
|
2541
3087
|
extensionRegistry
|
|
2542
3088
|
);
|
|
2543
3089
|
for (const prop of type.getProperties()) {
|
|
2544
3090
|
const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
2545
3091
|
if (!declaration) continue;
|
|
3092
|
+
if (!shouldEmitResolvedObjectProperty(prop, declaration)) continue;
|
|
2546
3093
|
const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
|
|
2547
3094
|
const optional = !!(prop.flags & ts3.SymbolFlags.Optional);
|
|
2548
3095
|
const propTypeNode = resolveTypeNode(
|
|
@@ -2552,12 +3099,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
|
|
|
2552
3099
|
typeRegistry,
|
|
2553
3100
|
visiting,
|
|
2554
3101
|
declaration,
|
|
3102
|
+
metadataPolicy,
|
|
2555
3103
|
extensionRegistry,
|
|
2556
3104
|
collectedDiagnostics
|
|
2557
3105
|
);
|
|
2558
3106
|
const fieldNodeInfo = fieldInfoMap?.get(prop.name);
|
|
2559
3107
|
properties.push({
|
|
2560
3108
|
name: prop.name,
|
|
3109
|
+
...fieldNodeInfo?.metadata !== void 0 && { metadata: fieldNodeInfo.metadata },
|
|
2561
3110
|
type: propTypeNode,
|
|
2562
3111
|
optional,
|
|
2563
3112
|
constraints: fieldNodeInfo?.constraints ?? [],
|
|
@@ -2574,14 +3123,21 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
|
|
|
2574
3123
|
type,
|
|
2575
3124
|
checker,
|
|
2576
3125
|
file,
|
|
2577
|
-
collectedDiagnostics
|
|
3126
|
+
collectedDiagnostics,
|
|
3127
|
+
metadataPolicy
|
|
2578
3128
|
) : properties,
|
|
2579
3129
|
additionalProperties: true
|
|
2580
3130
|
};
|
|
2581
3131
|
if (registryTypeName !== void 0 && shouldRegisterNamedType) {
|
|
2582
3132
|
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
3133
|
+
const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
|
|
3134
|
+
checker,
|
|
3135
|
+
declaration: namedDecl,
|
|
3136
|
+
subjectType: type
|
|
3137
|
+
}) : void 0;
|
|
2583
3138
|
typeRegistry[registryTypeName] = {
|
|
2584
3139
|
name: registryTypeName,
|
|
3140
|
+
...metadata !== void 0 && { metadata },
|
|
2585
3141
|
type: objectNode,
|
|
2586
3142
|
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2587
3143
|
provenance: provenanceForDeclaration(namedDecl, file)
|
|
@@ -2594,7 +3150,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
|
|
|
2594
3150
|
}
|
|
2595
3151
|
return objectNode;
|
|
2596
3152
|
}
|
|
2597
|
-
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, diagnostics, extensionRegistry) {
|
|
3153
|
+
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, metadataPolicy, diagnostics, extensionRegistry) {
|
|
2598
3154
|
const symbols = [type.getSymbol(), type.aliasSymbol].filter(
|
|
2599
3155
|
(s) => s?.declarations != null && s.declarations.length > 0
|
|
2600
3156
|
);
|
|
@@ -2615,10 +3171,12 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2615
3171
|
visiting,
|
|
2616
3172
|
diagnostics,
|
|
2617
3173
|
hostType,
|
|
3174
|
+
metadataPolicy,
|
|
2618
3175
|
extensionRegistry
|
|
2619
3176
|
);
|
|
2620
3177
|
if (fieldNode) {
|
|
2621
3178
|
map.set(fieldNode.name, {
|
|
3179
|
+
...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
|
|
2622
3180
|
constraints: [...fieldNode.constraints],
|
|
2623
3181
|
annotations: [...fieldNode.annotations],
|
|
2624
3182
|
provenance: fieldNode.provenance
|
|
@@ -2636,6 +3194,7 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2636
3194
|
file,
|
|
2637
3195
|
typeRegistry,
|
|
2638
3196
|
visiting,
|
|
3197
|
+
metadataPolicy,
|
|
2639
3198
|
checker.getTypeAtLocation(interfaceDecl),
|
|
2640
3199
|
diagnostics,
|
|
2641
3200
|
extensionRegistry
|
|
@@ -2649,6 +3208,7 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2649
3208
|
file,
|
|
2650
3209
|
typeRegistry,
|
|
2651
3210
|
visiting,
|
|
3211
|
+
metadataPolicy,
|
|
2652
3212
|
checker.getTypeAtLocation(typeAliasDecl),
|
|
2653
3213
|
diagnostics,
|
|
2654
3214
|
extensionRegistry
|
|
@@ -2700,7 +3260,7 @@ function isNullishTypeNode(typeNode) {
|
|
|
2700
3260
|
}
|
|
2701
3261
|
return ts3.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts3.SyntaxKind.NullKeyword || typeNode.literal.kind === ts3.SyntaxKind.UndefinedKeyword);
|
|
2702
3262
|
}
|
|
2703
|
-
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, hostType, diagnostics, extensionRegistry) {
|
|
3263
|
+
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, metadataPolicy, hostType, diagnostics, extensionRegistry) {
|
|
2704
3264
|
const map = /* @__PURE__ */ new Map();
|
|
2705
3265
|
for (const member of members) {
|
|
2706
3266
|
if (ts3.isPropertySignature(member)) {
|
|
@@ -2712,10 +3272,12 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, h
|
|
|
2712
3272
|
visiting,
|
|
2713
3273
|
diagnostics,
|
|
2714
3274
|
hostType,
|
|
3275
|
+
metadataPolicy,
|
|
2715
3276
|
extensionRegistry
|
|
2716
3277
|
);
|
|
2717
3278
|
if (fieldNode) {
|
|
2718
3279
|
map.set(fieldNode.name, {
|
|
3280
|
+
...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
|
|
2719
3281
|
constraints: [...fieldNode.constraints],
|
|
2720
3282
|
annotations: [...fieldNode.annotations],
|
|
2721
3283
|
provenance: fieldNode.provenance
|
|
@@ -2746,6 +3308,7 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
|
|
|
2746
3308
|
{},
|
|
2747
3309
|
/* @__PURE__ */ new Set(),
|
|
2748
3310
|
aliasDecl.type,
|
|
3311
|
+
void 0,
|
|
2749
3312
|
extensionRegistry
|
|
2750
3313
|
);
|
|
2751
3314
|
const constraints = extractJSDocConstraintNodes(
|
|
@@ -2931,15 +3494,27 @@ function findInterfaceByName(sourceFile, interfaceName) {
|
|
|
2931
3494
|
function findTypeAliasByName(sourceFile, aliasName) {
|
|
2932
3495
|
return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
|
|
2933
3496
|
}
|
|
2934
|
-
function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry) {
|
|
3497
|
+
function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy) {
|
|
2935
3498
|
const analysisFilePath = path.resolve(filePath);
|
|
2936
3499
|
const classDecl = findClassByName(ctx.sourceFile, typeName);
|
|
2937
3500
|
if (classDecl !== null) {
|
|
2938
|
-
return analyzeClassToIR(
|
|
3501
|
+
return analyzeClassToIR(
|
|
3502
|
+
classDecl,
|
|
3503
|
+
ctx.checker,
|
|
3504
|
+
analysisFilePath,
|
|
3505
|
+
extensionRegistry,
|
|
3506
|
+
metadataPolicy
|
|
3507
|
+
);
|
|
2939
3508
|
}
|
|
2940
3509
|
const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
|
|
2941
3510
|
if (interfaceDecl !== null) {
|
|
2942
|
-
return analyzeInterfaceToIR(
|
|
3511
|
+
return analyzeInterfaceToIR(
|
|
3512
|
+
interfaceDecl,
|
|
3513
|
+
ctx.checker,
|
|
3514
|
+
analysisFilePath,
|
|
3515
|
+
extensionRegistry,
|
|
3516
|
+
metadataPolicy
|
|
3517
|
+
);
|
|
2943
3518
|
}
|
|
2944
3519
|
const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
|
|
2945
3520
|
if (typeAlias !== null) {
|
|
@@ -2947,7 +3522,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
|
|
|
2947
3522
|
typeAlias,
|
|
2948
3523
|
ctx.checker,
|
|
2949
3524
|
analysisFilePath,
|
|
2950
|
-
extensionRegistry
|
|
3525
|
+
extensionRegistry,
|
|
3526
|
+
metadataPolicy
|
|
2951
3527
|
);
|
|
2952
3528
|
if (result.ok) {
|
|
2953
3529
|
return result.analysis;
|
|
@@ -2962,6 +3538,120 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
|
|
|
2962
3538
|
// src/generators/class-schema.ts
|
|
2963
3539
|
var ts5 = require("typescript");
|
|
2964
3540
|
|
|
3541
|
+
// src/metadata/collision-guards.ts
|
|
3542
|
+
function assertUniqueSerializedNames(entries, scope) {
|
|
3543
|
+
const seen = /* @__PURE__ */ new Map();
|
|
3544
|
+
for (const entry of entries) {
|
|
3545
|
+
const previous = seen.get(entry.serializedName);
|
|
3546
|
+
if (previous !== void 0) {
|
|
3547
|
+
if (previous.logicalName === entry.logicalName && previous.category === entry.category) {
|
|
3548
|
+
continue;
|
|
3549
|
+
}
|
|
3550
|
+
throw new Error(
|
|
3551
|
+
`Serialized name collision in ${scope}: ${previous.category} "${previous.logicalName}" and ${entry.category} "${entry.logicalName}" both resolve to "${entry.serializedName}".`
|
|
3552
|
+
);
|
|
3553
|
+
}
|
|
3554
|
+
seen.set(entry.serializedName, entry);
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
function collectFlattenedFields(elements) {
|
|
3558
|
+
const fields = [];
|
|
3559
|
+
for (const element of elements) {
|
|
3560
|
+
switch (element.kind) {
|
|
3561
|
+
case "field":
|
|
3562
|
+
fields.push(element);
|
|
3563
|
+
break;
|
|
3564
|
+
case "group":
|
|
3565
|
+
case "conditional":
|
|
3566
|
+
fields.push(...collectFlattenedFields(element.elements));
|
|
3567
|
+
break;
|
|
3568
|
+
default: {
|
|
3569
|
+
const exhaustive = element;
|
|
3570
|
+
void exhaustive;
|
|
3571
|
+
}
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3574
|
+
return fields;
|
|
3575
|
+
}
|
|
3576
|
+
function validateObjectProperties(properties, scope) {
|
|
3577
|
+
assertUniqueSerializedNames(
|
|
3578
|
+
properties.map((property) => ({
|
|
3579
|
+
logicalName: property.name,
|
|
3580
|
+
serializedName: getSerializedName(property.name, property.metadata),
|
|
3581
|
+
category: "object property"
|
|
3582
|
+
})),
|
|
3583
|
+
scope
|
|
3584
|
+
);
|
|
3585
|
+
for (const property of properties) {
|
|
3586
|
+
validateTypeNode(
|
|
3587
|
+
property.type,
|
|
3588
|
+
`${scope}.${getSerializedName(property.name, property.metadata)}`
|
|
3589
|
+
);
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
function validateTypeNode(type, scope) {
|
|
3593
|
+
switch (type.kind) {
|
|
3594
|
+
case "array":
|
|
3595
|
+
validateTypeNode(type.items, `${scope}[]`);
|
|
3596
|
+
break;
|
|
3597
|
+
case "object":
|
|
3598
|
+
validateObjectProperties(type.properties, scope);
|
|
3599
|
+
break;
|
|
3600
|
+
case "record":
|
|
3601
|
+
validateTypeNode(type.valueType, `${scope}.*`);
|
|
3602
|
+
break;
|
|
3603
|
+
case "union":
|
|
3604
|
+
type.members.forEach((member, index) => {
|
|
3605
|
+
validateTypeNode(member, `${scope}|${String(index)}`);
|
|
3606
|
+
});
|
|
3607
|
+
break;
|
|
3608
|
+
case "reference":
|
|
3609
|
+
case "primitive":
|
|
3610
|
+
case "enum":
|
|
3611
|
+
case "dynamic":
|
|
3612
|
+
case "custom":
|
|
3613
|
+
break;
|
|
3614
|
+
default: {
|
|
3615
|
+
const exhaustive = type;
|
|
3616
|
+
void exhaustive;
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
function validateTypeDefinitions(typeRegistry) {
|
|
3621
|
+
const definitions = Object.values(typeRegistry);
|
|
3622
|
+
assertUniqueSerializedNames(
|
|
3623
|
+
definitions.map((definition) => ({
|
|
3624
|
+
logicalName: definition.name,
|
|
3625
|
+
serializedName: getSerializedName(definition.name, definition.metadata),
|
|
3626
|
+
category: "type definition"
|
|
3627
|
+
})),
|
|
3628
|
+
"$defs"
|
|
3629
|
+
);
|
|
3630
|
+
for (const definition of definitions) {
|
|
3631
|
+
validateTypeDefinition(definition);
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
function validateTypeDefinition(definition) {
|
|
3635
|
+
validateTypeNode(
|
|
3636
|
+
definition.type,
|
|
3637
|
+
`type "${getSerializedName(definition.name, definition.metadata)}"`
|
|
3638
|
+
);
|
|
3639
|
+
}
|
|
3640
|
+
function assertNoSerializedNameCollisions(ir) {
|
|
3641
|
+
assertUniqueSerializedNames(
|
|
3642
|
+
collectFlattenedFields(ir.elements).map((field) => ({
|
|
3643
|
+
logicalName: field.name,
|
|
3644
|
+
serializedName: getSerializedName(field.name, field.metadata),
|
|
3645
|
+
category: "field"
|
|
3646
|
+
})),
|
|
3647
|
+
"form root"
|
|
3648
|
+
);
|
|
3649
|
+
for (const field of collectFlattenedFields(ir.elements)) {
|
|
3650
|
+
validateTypeNode(field.type, `field "${getSerializedName(field.name, field.metadata)}"`);
|
|
3651
|
+
}
|
|
3652
|
+
validateTypeDefinitions(ir.typeRegistry);
|
|
3653
|
+
}
|
|
3654
|
+
|
|
2965
3655
|
// src/json-schema/ir-generator.ts
|
|
2966
3656
|
function makeContext(options) {
|
|
2967
3657
|
const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
|
|
@@ -2972,19 +3662,33 @@ function makeContext(options) {
|
|
|
2972
3662
|
}
|
|
2973
3663
|
return {
|
|
2974
3664
|
defs: {},
|
|
3665
|
+
typeNameMap: {},
|
|
3666
|
+
typeRegistry: {},
|
|
2975
3667
|
extensionRegistry: options?.extensionRegistry,
|
|
2976
3668
|
vendorPrefix
|
|
2977
3669
|
};
|
|
2978
3670
|
}
|
|
2979
3671
|
function generateJsonSchemaFromIR(ir, options) {
|
|
2980
|
-
|
|
3672
|
+
assertNoSerializedNameCollisions(ir);
|
|
3673
|
+
const ctx = {
|
|
3674
|
+
...makeContext(options),
|
|
3675
|
+
typeRegistry: ir.typeRegistry,
|
|
3676
|
+
typeNameMap: Object.fromEntries(
|
|
3677
|
+
Object.entries(ir.typeRegistry).map(([name, typeDef]) => [
|
|
3678
|
+
name,
|
|
3679
|
+
getSerializedName(name, typeDef.metadata)
|
|
3680
|
+
])
|
|
3681
|
+
)
|
|
3682
|
+
};
|
|
2981
3683
|
for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
|
|
2982
|
-
ctx.
|
|
3684
|
+
const schemaName = ctx.typeNameMap[name] ?? name;
|
|
3685
|
+
ctx.defs[schemaName] = generateTypeNode(typeDef.type, ctx);
|
|
3686
|
+
applyResolvedMetadata(ctx.defs[schemaName], typeDef.metadata);
|
|
2983
3687
|
if (typeDef.constraints && typeDef.constraints.length > 0) {
|
|
2984
|
-
applyConstraints(ctx.defs[
|
|
3688
|
+
applyConstraints(ctx.defs[schemaName], typeDef.constraints, ctx);
|
|
2985
3689
|
}
|
|
2986
3690
|
if (typeDef.annotations && typeDef.annotations.length > 0) {
|
|
2987
|
-
applyAnnotations(ctx.defs[
|
|
3691
|
+
applyAnnotations(ctx.defs[schemaName], typeDef.annotations, ctx);
|
|
2988
3692
|
}
|
|
2989
3693
|
}
|
|
2990
3694
|
const properties = {};
|
|
@@ -2997,6 +3701,7 @@ function generateJsonSchemaFromIR(ir, options) {
|
|
|
2997
3701
|
properties,
|
|
2998
3702
|
...uniqueRequired.length > 0 && { required: uniqueRequired }
|
|
2999
3703
|
};
|
|
3704
|
+
applyResolvedMetadata(result, ir.metadata);
|
|
3000
3705
|
if (ir.annotations && ir.annotations.length > 0) {
|
|
3001
3706
|
applyAnnotations(result, ir.annotations, ctx);
|
|
3002
3707
|
}
|
|
@@ -3009,9 +3714,9 @@ function collectFields(elements, properties, required, ctx) {
|
|
|
3009
3714
|
for (const element of elements) {
|
|
3010
3715
|
switch (element.kind) {
|
|
3011
3716
|
case "field":
|
|
3012
|
-
properties[element.name] = generateFieldSchema(element, ctx);
|
|
3717
|
+
properties[getSerializedName(element.name, element.metadata)] = generateFieldSchema(element, ctx);
|
|
3013
3718
|
if (element.required) {
|
|
3014
|
-
required.push(element.name);
|
|
3719
|
+
required.push(getSerializedName(element.name, element.metadata));
|
|
3015
3720
|
}
|
|
3016
3721
|
break;
|
|
3017
3722
|
case "group":
|
|
@@ -3055,6 +3760,7 @@ function generateFieldSchema(field, ctx) {
|
|
|
3055
3760
|
rootAnnotations.push(annotation);
|
|
3056
3761
|
}
|
|
3057
3762
|
}
|
|
3763
|
+
applyResolvedMetadata(schema, field.metadata);
|
|
3058
3764
|
applyAnnotations(schema, rootAnnotations, ctx);
|
|
3059
3765
|
if (itemStringSchema !== void 0) {
|
|
3060
3766
|
applyAnnotations(itemStringSchema, itemAnnotations, ctx);
|
|
@@ -3062,7 +3768,7 @@ function generateFieldSchema(field, ctx) {
|
|
|
3062
3768
|
if (pathConstraints.length === 0) {
|
|
3063
3769
|
return schema;
|
|
3064
3770
|
}
|
|
3065
|
-
return applyPathTargetedConstraints(schema, pathConstraints, ctx);
|
|
3771
|
+
return applyPathTargetedConstraints(schema, pathConstraints, ctx, field.type);
|
|
3066
3772
|
}
|
|
3067
3773
|
function isStringItemConstraint(constraint) {
|
|
3068
3774
|
switch (constraint.constraintKind) {
|
|
@@ -3074,9 +3780,11 @@ function isStringItemConstraint(constraint) {
|
|
|
3074
3780
|
return false;
|
|
3075
3781
|
}
|
|
3076
3782
|
}
|
|
3077
|
-
function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
|
|
3783
|
+
function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
|
|
3078
3784
|
if (schema.type === "array" && schema.items) {
|
|
3079
|
-
|
|
3785
|
+
const referencedType = typeNode?.kind === "reference" ? resolveReferencedType(typeNode, ctx) : void 0;
|
|
3786
|
+
const nestedType = typeNode?.kind === "array" ? typeNode.items : referencedType?.kind === "array" ? referencedType.items : void 0;
|
|
3787
|
+
schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx, nestedType);
|
|
3080
3788
|
return schema;
|
|
3081
3789
|
}
|
|
3082
3790
|
const byTarget = /* @__PURE__ */ new Map();
|
|
@@ -3091,7 +3799,7 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
|
|
|
3091
3799
|
for (const [target, constraints] of byTarget) {
|
|
3092
3800
|
const subSchema = {};
|
|
3093
3801
|
applyConstraints(subSchema, constraints, ctx);
|
|
3094
|
-
propertyOverrides[target] = subSchema;
|
|
3802
|
+
propertyOverrides[resolveSerializedPropertyName(target, typeNode, ctx)] = subSchema;
|
|
3095
3803
|
}
|
|
3096
3804
|
if (schema.$ref) {
|
|
3097
3805
|
const { $ref, ...rest } = schema;
|
|
@@ -3139,7 +3847,7 @@ function generateTypeNode(type, ctx) {
|
|
|
3139
3847
|
case "union":
|
|
3140
3848
|
return generateUnionType(type, ctx);
|
|
3141
3849
|
case "reference":
|
|
3142
|
-
return generateReferenceType(type);
|
|
3850
|
+
return generateReferenceType(type, ctx);
|
|
3143
3851
|
case "dynamic":
|
|
3144
3852
|
return generateDynamicType(type);
|
|
3145
3853
|
case "custom":
|
|
@@ -3180,9 +3888,10 @@ function generateObjectType(type, ctx) {
|
|
|
3180
3888
|
const properties = {};
|
|
3181
3889
|
const required = [];
|
|
3182
3890
|
for (const prop of type.properties) {
|
|
3183
|
-
|
|
3891
|
+
const propertyName = getSerializedName(prop.name, prop.metadata);
|
|
3892
|
+
properties[propertyName] = generatePropertySchema(prop, ctx);
|
|
3184
3893
|
if (!prop.optional) {
|
|
3185
|
-
required.push(
|
|
3894
|
+
required.push(propertyName);
|
|
3186
3895
|
}
|
|
3187
3896
|
}
|
|
3188
3897
|
const schema = { type: "object", properties };
|
|
@@ -3203,6 +3912,7 @@ function generateRecordType(type, ctx) {
|
|
|
3203
3912
|
function generatePropertySchema(prop, ctx) {
|
|
3204
3913
|
const schema = generateTypeNode(prop.type, ctx);
|
|
3205
3914
|
applyConstraints(schema, prop.constraints, ctx);
|
|
3915
|
+
applyResolvedMetadata(schema, prop.metadata);
|
|
3206
3916
|
applyAnnotations(schema, prop.annotations, ctx);
|
|
3207
3917
|
return schema;
|
|
3208
3918
|
}
|
|
@@ -3231,8 +3941,28 @@ function isNullableUnion(type) {
|
|
|
3231
3941
|
).length;
|
|
3232
3942
|
return nullCount === 1;
|
|
3233
3943
|
}
|
|
3234
|
-
function generateReferenceType(type) {
|
|
3235
|
-
return { $ref: `#/$defs/${type.name}` };
|
|
3944
|
+
function generateReferenceType(type, ctx) {
|
|
3945
|
+
return { $ref: `#/$defs/${ctx.typeNameMap[type.name] ?? type.name}` };
|
|
3946
|
+
}
|
|
3947
|
+
function applyResolvedMetadata(schema, metadata) {
|
|
3948
|
+
const displayName = getDisplayName(metadata);
|
|
3949
|
+
if (displayName !== void 0) {
|
|
3950
|
+
schema.title = displayName;
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
function resolveReferencedType(type, ctx) {
|
|
3954
|
+
return ctx.typeRegistry[type.name]?.type;
|
|
3955
|
+
}
|
|
3956
|
+
function resolveSerializedPropertyName(logicalName, typeNode, ctx) {
|
|
3957
|
+
if (typeNode?.kind === "object") {
|
|
3958
|
+
const property = typeNode.properties.find((candidate) => candidate.name === logicalName);
|
|
3959
|
+
return property === void 0 ? logicalName : getSerializedName(property.name, property.metadata);
|
|
3960
|
+
}
|
|
3961
|
+
if (typeNode?.kind === "reference") {
|
|
3962
|
+
const referencedType = resolveReferencedType(typeNode, ctx);
|
|
3963
|
+
return referencedType === void 0 ? logicalName : resolveSerializedPropertyName(logicalName, referencedType, ctx);
|
|
3964
|
+
}
|
|
3965
|
+
return logicalName;
|
|
3236
3966
|
}
|
|
3237
3967
|
function generateDynamicType(type) {
|
|
3238
3968
|
if (type.dynamicKind === "enum") {
|
|
@@ -3312,7 +4042,7 @@ function applyAnnotations(schema, annotations, ctx) {
|
|
|
3312
4042
|
for (const annotation of annotations) {
|
|
3313
4043
|
switch (annotation.annotationKind) {
|
|
3314
4044
|
case "displayName":
|
|
3315
|
-
schema.title
|
|
4045
|
+
schema.title ??= annotation.value;
|
|
3316
4046
|
break;
|
|
3317
4047
|
case "description":
|
|
3318
4048
|
schema.description = annotation.value;
|
|
@@ -3562,13 +4292,21 @@ function combineRules(parentRule, childRule) {
|
|
|
3562
4292
|
}
|
|
3563
4293
|
};
|
|
3564
4294
|
}
|
|
3565
|
-
function
|
|
3566
|
-
const
|
|
4295
|
+
function getFieldDisplayName(field) {
|
|
4296
|
+
const resolvedDisplayName = getDisplayName(field.metadata);
|
|
4297
|
+
if (resolvedDisplayName !== void 0) {
|
|
4298
|
+
return resolvedDisplayName;
|
|
4299
|
+
}
|
|
4300
|
+
return field.annotations.find((annotation) => annotation.annotationKind === "displayName")?.value;
|
|
4301
|
+
}
|
|
4302
|
+
function fieldNodeToControl(field, fieldNameMap, parentRule) {
|
|
3567
4303
|
const placeholderAnnotation = field.annotations.find((a) => a.annotationKind === "placeholder");
|
|
4304
|
+
const serializedName = fieldNameMap.get(field.name) ?? getSerializedName(field.name, field.metadata);
|
|
4305
|
+
const displayName = getFieldDisplayName(field);
|
|
3568
4306
|
const control = {
|
|
3569
4307
|
type: "Control",
|
|
3570
|
-
scope: fieldToScope(
|
|
3571
|
-
...
|
|
4308
|
+
scope: fieldToScope(serializedName),
|
|
4309
|
+
...displayName !== void 0 && { label: displayName },
|
|
3572
4310
|
...placeholderAnnotation !== void 0 && {
|
|
3573
4311
|
options: { placeholder: placeholderAnnotation.value }
|
|
3574
4312
|
},
|
|
@@ -3576,30 +4314,30 @@ function fieldNodeToControl(field, parentRule) {
|
|
|
3576
4314
|
};
|
|
3577
4315
|
return control;
|
|
3578
4316
|
}
|
|
3579
|
-
function groupNodeToLayout(group, parentRule) {
|
|
4317
|
+
function groupNodeToLayout(group, fieldNameMap, parentRule) {
|
|
3580
4318
|
return {
|
|
3581
4319
|
type: "Group",
|
|
3582
4320
|
label: group.label,
|
|
3583
|
-
elements: irElementsToUiSchema(group.elements, parentRule),
|
|
4321
|
+
elements: irElementsToUiSchema(group.elements, fieldNameMap, parentRule),
|
|
3584
4322
|
...parentRule !== void 0 && { rule: parentRule }
|
|
3585
4323
|
};
|
|
3586
4324
|
}
|
|
3587
|
-
function irElementsToUiSchema(elements, parentRule) {
|
|
4325
|
+
function irElementsToUiSchema(elements, fieldNameMap, parentRule) {
|
|
3588
4326
|
const result = [];
|
|
3589
4327
|
for (const element of elements) {
|
|
3590
4328
|
switch (element.kind) {
|
|
3591
4329
|
case "field": {
|
|
3592
|
-
result.push(fieldNodeToControl(element, parentRule));
|
|
4330
|
+
result.push(fieldNodeToControl(element, fieldNameMap, parentRule));
|
|
3593
4331
|
break;
|
|
3594
4332
|
}
|
|
3595
4333
|
case "group": {
|
|
3596
|
-
result.push(groupNodeToLayout(element, parentRule));
|
|
4334
|
+
result.push(groupNodeToLayout(element, fieldNameMap, parentRule));
|
|
3597
4335
|
break;
|
|
3598
4336
|
}
|
|
3599
4337
|
case "conditional": {
|
|
3600
|
-
const newRule = createShowRule(element.fieldName, element.value);
|
|
4338
|
+
const newRule = createShowRule(fieldNameMap.get(element.fieldName) ?? element.fieldName, element.value);
|
|
3601
4339
|
const combinedRule = parentRule !== void 0 ? combineRules(parentRule, newRule) : newRule;
|
|
3602
|
-
const childElements = irElementsToUiSchema(element.elements, combinedRule);
|
|
4340
|
+
const childElements = irElementsToUiSchema(element.elements, fieldNameMap, combinedRule);
|
|
3603
4341
|
result.push(...childElements);
|
|
3604
4342
|
break;
|
|
3605
4343
|
}
|
|
@@ -3613,12 +4351,35 @@ function irElementsToUiSchema(elements, parentRule) {
|
|
|
3613
4351
|
return result;
|
|
3614
4352
|
}
|
|
3615
4353
|
function generateUiSchemaFromIR(ir) {
|
|
4354
|
+
assertNoSerializedNameCollisions(ir);
|
|
4355
|
+
const fieldNameMap = collectFieldNameMap(ir.elements);
|
|
3616
4356
|
const result = {
|
|
3617
4357
|
type: "VerticalLayout",
|
|
3618
|
-
elements: irElementsToUiSchema(ir.elements)
|
|
4358
|
+
elements: irElementsToUiSchema(ir.elements, fieldNameMap)
|
|
3619
4359
|
};
|
|
3620
4360
|
return parseOrThrow(uiSchema, result, "UI Schema");
|
|
3621
4361
|
}
|
|
4362
|
+
function collectFieldNameMap(elements) {
|
|
4363
|
+
const map = /* @__PURE__ */ new Map();
|
|
4364
|
+
for (const element of elements) {
|
|
4365
|
+
switch (element.kind) {
|
|
4366
|
+
case "field":
|
|
4367
|
+
map.set(element.name, getSerializedName(element.name, element.metadata));
|
|
4368
|
+
break;
|
|
4369
|
+
case "group":
|
|
4370
|
+
case "conditional":
|
|
4371
|
+
for (const [key, value] of collectFieldNameMap(element.elements)) {
|
|
4372
|
+
map.set(key, value);
|
|
4373
|
+
}
|
|
4374
|
+
break;
|
|
4375
|
+
default: {
|
|
4376
|
+
const _exhaustive = element;
|
|
4377
|
+
void _exhaustive;
|
|
4378
|
+
}
|
|
4379
|
+
}
|
|
4380
|
+
}
|
|
4381
|
+
return map;
|
|
4382
|
+
}
|
|
3622
4383
|
|
|
3623
4384
|
// src/validate/constraint-validator.ts
|
|
3624
4385
|
var import_internal3 = require("@formspec/analysis/internal");
|
|
@@ -3701,7 +4462,11 @@ function generateClassSchemas(analysis, source, options) {
|
|
|
3701
4462
|
if (errorDiagnostics !== void 0 && errorDiagnostics.length > 0) {
|
|
3702
4463
|
throw new Error(formatValidationError(errorDiagnostics));
|
|
3703
4464
|
}
|
|
3704
|
-
const ir = canonicalizeTSDoc(
|
|
4465
|
+
const ir = canonicalizeTSDoc(
|
|
4466
|
+
analysis,
|
|
4467
|
+
source,
|
|
4468
|
+
options?.metadata !== void 0 ? { metadata: options.metadata } : void 0
|
|
4469
|
+
);
|
|
3705
4470
|
const validationResult = validateIR(ir, {
|
|
3706
4471
|
...options?.extensionRegistry !== void 0 && {
|
|
3707
4472
|
extensionRegistry: options.extensionRegistry
|
|
@@ -3823,6 +4588,7 @@ function typeToJsonSchema(type, checker) {
|
|
|
3823
4588
|
visiting,
|
|
3824
4589
|
void 0,
|
|
3825
4590
|
void 0,
|
|
4591
|
+
void 0,
|
|
3826
4592
|
diagnostics
|
|
3827
4593
|
);
|
|
3828
4594
|
if (diagnostics.length > 0) {
|