@formspec/build 0.1.0-alpha.27 → 0.1.0-alpha.29
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/LICENSE +21 -0
- package/README.md +3 -2
- package/dist/analyzer/class-analyzer.d.ts +12 -6
- 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 +1460 -143
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1458 -139
- 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 +1425 -141
- 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 +1425 -139
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +1416 -178
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +1416 -176
- 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 +7 -6
package/dist/index.cjs
CHANGED
|
@@ -46,6 +46,315 @@ module.exports = __toCommonJS(index_exports);
|
|
|
46
46
|
|
|
47
47
|
// src/canonicalize/chain-dsl-canonicalizer.ts
|
|
48
48
|
var import_internals = require("@formspec/core/internals");
|
|
49
|
+
|
|
50
|
+
// src/metadata/policy.ts
|
|
51
|
+
var NOOP_INFLECT = () => "";
|
|
52
|
+
function normalizePluralization(input) {
|
|
53
|
+
if (input?.mode === "infer-if-missing") {
|
|
54
|
+
return {
|
|
55
|
+
mode: "infer-if-missing",
|
|
56
|
+
infer: () => "",
|
|
57
|
+
inflect: input.inflect
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (input?.mode === "require-explicit") {
|
|
61
|
+
return {
|
|
62
|
+
mode: "require-explicit",
|
|
63
|
+
infer: () => "",
|
|
64
|
+
inflect: NOOP_INFLECT
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
mode: "disabled",
|
|
69
|
+
infer: () => "",
|
|
70
|
+
inflect: NOOP_INFLECT
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function normalizeScalarPolicy(input) {
|
|
74
|
+
if (input?.mode === "infer-if-missing") {
|
|
75
|
+
return {
|
|
76
|
+
mode: "infer-if-missing",
|
|
77
|
+
infer: input.infer,
|
|
78
|
+
pluralization: normalizePluralization(input.pluralization)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (input?.mode === "require-explicit") {
|
|
82
|
+
return {
|
|
83
|
+
mode: "require-explicit",
|
|
84
|
+
infer: () => "",
|
|
85
|
+
pluralization: normalizePluralization(input.pluralization)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
mode: "disabled",
|
|
90
|
+
infer: () => "",
|
|
91
|
+
pluralization: normalizePluralization(input?.pluralization)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function normalizeDeclarationPolicy(input) {
|
|
95
|
+
return {
|
|
96
|
+
apiName: normalizeScalarPolicy(input?.apiName),
|
|
97
|
+
displayName: normalizeScalarPolicy(input?.displayName)
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function normalizeMetadataPolicy(input) {
|
|
101
|
+
return {
|
|
102
|
+
type: normalizeDeclarationPolicy(input?.type),
|
|
103
|
+
field: normalizeDeclarationPolicy(input?.field),
|
|
104
|
+
method: normalizeDeclarationPolicy(input?.method)
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function getDeclarationMetadataPolicy(policy, declarationKind) {
|
|
108
|
+
return policy[declarationKind];
|
|
109
|
+
}
|
|
110
|
+
function makeMetadataContext(surface, declarationKind, logicalName, buildContext) {
|
|
111
|
+
return {
|
|
112
|
+
surface,
|
|
113
|
+
declarationKind,
|
|
114
|
+
logicalName,
|
|
115
|
+
...buildContext !== void 0 && { buildContext }
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/metadata/resolve.ts
|
|
120
|
+
function toExplicitScalar(value) {
|
|
121
|
+
return value !== void 0 && value.trim() !== "" ? { value, source: "explicit" } : void 0;
|
|
122
|
+
}
|
|
123
|
+
function toExplicitResolvedMetadata(explicit) {
|
|
124
|
+
if (explicit === void 0) {
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
const apiName = toExplicitScalar(explicit.apiName);
|
|
128
|
+
const displayName = toExplicitScalar(explicit.displayName);
|
|
129
|
+
const apiNamePlural = toExplicitScalar(explicit.apiNamePlural);
|
|
130
|
+
const displayNamePlural = toExplicitScalar(explicit.displayNamePlural);
|
|
131
|
+
const metadata = {
|
|
132
|
+
...apiName !== void 0 && { apiName },
|
|
133
|
+
...displayName !== void 0 && { displayName },
|
|
134
|
+
...apiNamePlural !== void 0 && { apiNamePlural },
|
|
135
|
+
...displayNamePlural !== void 0 && { displayNamePlural }
|
|
136
|
+
};
|
|
137
|
+
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
138
|
+
}
|
|
139
|
+
function resolveScalar(current, policy, context, metadataLabel) {
|
|
140
|
+
if (current !== void 0) {
|
|
141
|
+
return current;
|
|
142
|
+
}
|
|
143
|
+
if (policy.mode === "require-explicit") {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`Metadata policy requires explicit ${metadataLabel} for ${context.declarationKind} "${context.logicalName}" on the ${context.surface} surface.`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
if (policy.mode !== "infer-if-missing") {
|
|
149
|
+
return void 0;
|
|
150
|
+
}
|
|
151
|
+
const inferredValue = policy.infer(context);
|
|
152
|
+
return inferredValue.trim() !== "" ? { value: inferredValue, source: "inferred" } : void 0;
|
|
153
|
+
}
|
|
154
|
+
function resolvePlural(current, singular, policy, context, metadataLabel) {
|
|
155
|
+
if (current !== void 0) {
|
|
156
|
+
return current;
|
|
157
|
+
}
|
|
158
|
+
if (policy.mode === "require-explicit") {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`Metadata policy requires explicit ${metadataLabel} for ${context.declarationKind} "${context.logicalName}" on the ${context.surface} surface.`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
if (singular === void 0 || policy.mode !== "infer-if-missing") {
|
|
164
|
+
return void 0;
|
|
165
|
+
}
|
|
166
|
+
const pluralValue = policy.inflect({ ...context, singular: singular.value });
|
|
167
|
+
return pluralValue.trim() !== "" ? { value: pluralValue, source: "inferred" } : void 0;
|
|
168
|
+
}
|
|
169
|
+
function resolveResolvedMetadata(current, policy, context) {
|
|
170
|
+
const apiName = resolveScalar(current?.apiName, policy.apiName, context, "apiName");
|
|
171
|
+
const displayName = resolveScalar(
|
|
172
|
+
current?.displayName,
|
|
173
|
+
policy.displayName,
|
|
174
|
+
context,
|
|
175
|
+
"displayName"
|
|
176
|
+
);
|
|
177
|
+
const apiNamePlural = resolvePlural(
|
|
178
|
+
current?.apiNamePlural,
|
|
179
|
+
apiName,
|
|
180
|
+
policy.apiName.pluralization,
|
|
181
|
+
context,
|
|
182
|
+
"apiNamePlural"
|
|
183
|
+
);
|
|
184
|
+
const displayNamePlural = resolvePlural(
|
|
185
|
+
current?.displayNamePlural,
|
|
186
|
+
displayName,
|
|
187
|
+
policy.displayName.pluralization,
|
|
188
|
+
context,
|
|
189
|
+
"displayNamePlural"
|
|
190
|
+
);
|
|
191
|
+
if (apiName === void 0 && displayName === void 0 && apiNamePlural === void 0 && displayNamePlural === void 0) {
|
|
192
|
+
return void 0;
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
...apiName !== void 0 && { apiName },
|
|
196
|
+
...displayName !== void 0 && { displayName },
|
|
197
|
+
...apiNamePlural !== void 0 && { apiNamePlural },
|
|
198
|
+
...displayNamePlural !== void 0 && { displayNamePlural }
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function pickResolvedMetadataValue(baseValue, overlayValue) {
|
|
202
|
+
if (overlayValue?.source === "explicit") {
|
|
203
|
+
return overlayValue;
|
|
204
|
+
}
|
|
205
|
+
if (baseValue?.source === "explicit") {
|
|
206
|
+
return baseValue;
|
|
207
|
+
}
|
|
208
|
+
return baseValue ?? overlayValue;
|
|
209
|
+
}
|
|
210
|
+
function resolveTypeNodeMetadata(type, options) {
|
|
211
|
+
switch (type.kind) {
|
|
212
|
+
case "array":
|
|
213
|
+
return {
|
|
214
|
+
...type,
|
|
215
|
+
items: resolveTypeNodeMetadata(type.items, options)
|
|
216
|
+
};
|
|
217
|
+
case "object":
|
|
218
|
+
return {
|
|
219
|
+
...type,
|
|
220
|
+
properties: type.properties.map((property) => resolveObjectPropertyMetadata(property, options))
|
|
221
|
+
};
|
|
222
|
+
case "record":
|
|
223
|
+
return {
|
|
224
|
+
...type,
|
|
225
|
+
valueType: resolveTypeNodeMetadata(type.valueType, options)
|
|
226
|
+
};
|
|
227
|
+
case "union":
|
|
228
|
+
return {
|
|
229
|
+
...type,
|
|
230
|
+
members: type.members.map((member) => resolveTypeNodeMetadata(member, options))
|
|
231
|
+
};
|
|
232
|
+
case "reference":
|
|
233
|
+
case "primitive":
|
|
234
|
+
case "enum":
|
|
235
|
+
case "dynamic":
|
|
236
|
+
case "custom":
|
|
237
|
+
return type;
|
|
238
|
+
default: {
|
|
239
|
+
const _exhaustive = type;
|
|
240
|
+
return _exhaustive;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function resolveObjectPropertyMetadata(property, options) {
|
|
245
|
+
const metadata = resolveResolvedMetadata(property.metadata, options.policy.field, {
|
|
246
|
+
surface: options.surface,
|
|
247
|
+
declarationKind: "field",
|
|
248
|
+
logicalName: property.name,
|
|
249
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
250
|
+
});
|
|
251
|
+
return {
|
|
252
|
+
...property,
|
|
253
|
+
...metadata !== void 0 && { metadata },
|
|
254
|
+
type: resolveTypeNodeMetadata(property.type, options)
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function resolveFieldMetadataNode(field, options) {
|
|
258
|
+
const metadata = resolveResolvedMetadata(field.metadata, options.policy.field, {
|
|
259
|
+
surface: options.surface,
|
|
260
|
+
declarationKind: "field",
|
|
261
|
+
logicalName: field.name,
|
|
262
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
263
|
+
});
|
|
264
|
+
return {
|
|
265
|
+
...field,
|
|
266
|
+
...metadata !== void 0 && { metadata },
|
|
267
|
+
type: resolveTypeNodeMetadata(field.type, options)
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function resolveFormElementMetadata(element, options) {
|
|
271
|
+
switch (element.kind) {
|
|
272
|
+
case "field":
|
|
273
|
+
return resolveFieldMetadataNode(element, options);
|
|
274
|
+
case "group":
|
|
275
|
+
return {
|
|
276
|
+
...element,
|
|
277
|
+
elements: element.elements.map((child) => resolveFormElementMetadata(child, options))
|
|
278
|
+
};
|
|
279
|
+
case "conditional":
|
|
280
|
+
return {
|
|
281
|
+
...element,
|
|
282
|
+
elements: element.elements.map((child) => resolveFormElementMetadata(child, options))
|
|
283
|
+
};
|
|
284
|
+
default: {
|
|
285
|
+
const _exhaustive = element;
|
|
286
|
+
return _exhaustive;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function resolveTypeDefinitionMetadata(typeDefinition, options) {
|
|
291
|
+
const metadata = resolveResolvedMetadata(typeDefinition.metadata, options.policy.type, {
|
|
292
|
+
surface: options.surface,
|
|
293
|
+
declarationKind: "type",
|
|
294
|
+
logicalName: typeDefinition.name,
|
|
295
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
296
|
+
});
|
|
297
|
+
return {
|
|
298
|
+
...typeDefinition,
|
|
299
|
+
...metadata !== void 0 && { metadata },
|
|
300
|
+
type: resolveTypeNodeMetadata(typeDefinition.type, options)
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function resolveMetadata(explicit, policy, context) {
|
|
304
|
+
return resolveResolvedMetadata(toExplicitResolvedMetadata(explicit), policy, context);
|
|
305
|
+
}
|
|
306
|
+
function mergeResolvedMetadata(baseMetadata, overlayMetadata) {
|
|
307
|
+
const apiName = pickResolvedMetadataValue(baseMetadata?.apiName, overlayMetadata?.apiName);
|
|
308
|
+
const displayName = pickResolvedMetadataValue(
|
|
309
|
+
baseMetadata?.displayName,
|
|
310
|
+
overlayMetadata?.displayName
|
|
311
|
+
);
|
|
312
|
+
const apiNamePlural = pickResolvedMetadataValue(
|
|
313
|
+
baseMetadata?.apiNamePlural,
|
|
314
|
+
overlayMetadata?.apiNamePlural
|
|
315
|
+
);
|
|
316
|
+
const displayNamePlural = pickResolvedMetadataValue(
|
|
317
|
+
baseMetadata?.displayNamePlural,
|
|
318
|
+
overlayMetadata?.displayNamePlural
|
|
319
|
+
);
|
|
320
|
+
if (apiName === void 0 && displayName === void 0 && apiNamePlural === void 0 && displayNamePlural === void 0) {
|
|
321
|
+
return void 0;
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
...apiName !== void 0 && { apiName },
|
|
325
|
+
...displayName !== void 0 && { displayName },
|
|
326
|
+
...apiNamePlural !== void 0 && { apiNamePlural },
|
|
327
|
+
...displayNamePlural !== void 0 && { displayNamePlural }
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
function getSerializedName(logicalName, metadata) {
|
|
331
|
+
return metadata?.apiName?.value ?? logicalName;
|
|
332
|
+
}
|
|
333
|
+
function getDisplayName(metadata) {
|
|
334
|
+
return metadata?.displayName?.value;
|
|
335
|
+
}
|
|
336
|
+
function resolveFormIRMetadata(ir, options) {
|
|
337
|
+
const rootLogicalName = options.rootLogicalName ?? ir.name ?? "FormSpec";
|
|
338
|
+
const metadata = resolveResolvedMetadata(ir.metadata, options.policy.type, {
|
|
339
|
+
surface: options.surface,
|
|
340
|
+
declarationKind: "type",
|
|
341
|
+
logicalName: rootLogicalName,
|
|
342
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
343
|
+
});
|
|
344
|
+
return {
|
|
345
|
+
...ir,
|
|
346
|
+
...metadata !== void 0 && { metadata },
|
|
347
|
+
elements: ir.elements.map((element) => resolveFormElementMetadata(element, options)),
|
|
348
|
+
typeRegistry: Object.fromEntries(
|
|
349
|
+
Object.entries(ir.typeRegistry).map(([name, definition]) => [
|
|
350
|
+
name,
|
|
351
|
+
resolveTypeDefinitionMetadata(definition, options)
|
|
352
|
+
])
|
|
353
|
+
)
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// src/canonicalize/chain-dsl-canonicalizer.ts
|
|
49
358
|
var CHAIN_DSL_PROVENANCE = {
|
|
50
359
|
surface: "chain-dsl",
|
|
51
360
|
file: "",
|
|
@@ -61,57 +370,60 @@ function isConditional(el) {
|
|
|
61
370
|
function isField(el) {
|
|
62
371
|
return el._type === "field";
|
|
63
372
|
}
|
|
64
|
-
function canonicalizeChainDSL(form) {
|
|
373
|
+
function canonicalizeChainDSL(form, options) {
|
|
374
|
+
const metadataPolicy = normalizeMetadataPolicy(
|
|
375
|
+
options?.metadata ?? (0, import_internals._getFormSpecMetadataPolicy)(form)
|
|
376
|
+
);
|
|
65
377
|
return {
|
|
66
378
|
kind: "form-ir",
|
|
67
379
|
irVersion: import_internals.IR_VERSION,
|
|
68
|
-
elements: canonicalizeElements(form.elements),
|
|
380
|
+
elements: canonicalizeElements(form.elements, metadataPolicy),
|
|
69
381
|
rootAnnotations: [],
|
|
70
382
|
typeRegistry: {},
|
|
71
383
|
provenance: CHAIN_DSL_PROVENANCE
|
|
72
384
|
};
|
|
73
385
|
}
|
|
74
|
-
function canonicalizeElements(elements) {
|
|
75
|
-
return elements.map(canonicalizeElement);
|
|
386
|
+
function canonicalizeElements(elements, metadataPolicy) {
|
|
387
|
+
return elements.map((element) => canonicalizeElement(element, metadataPolicy));
|
|
76
388
|
}
|
|
77
|
-
function canonicalizeElement(element) {
|
|
389
|
+
function canonicalizeElement(element, metadataPolicy) {
|
|
78
390
|
if (isField(element)) {
|
|
79
|
-
return canonicalizeField(element);
|
|
391
|
+
return canonicalizeField(element, metadataPolicy);
|
|
80
392
|
}
|
|
81
393
|
if (isGroup(element)) {
|
|
82
|
-
return canonicalizeGroup(element);
|
|
394
|
+
return canonicalizeGroup(element, metadataPolicy);
|
|
83
395
|
}
|
|
84
396
|
if (isConditional(element)) {
|
|
85
|
-
return canonicalizeConditional(element);
|
|
397
|
+
return canonicalizeConditional(element, metadataPolicy);
|
|
86
398
|
}
|
|
87
399
|
const _exhaustive = element;
|
|
88
400
|
throw new Error(`Unknown element type: ${JSON.stringify(_exhaustive)}`);
|
|
89
401
|
}
|
|
90
|
-
function canonicalizeField(field) {
|
|
402
|
+
function canonicalizeField(field, metadataPolicy) {
|
|
91
403
|
switch (field._field) {
|
|
92
404
|
case "text":
|
|
93
|
-
return canonicalizeTextField(field);
|
|
405
|
+
return canonicalizeTextField(field, metadataPolicy);
|
|
94
406
|
case "number":
|
|
95
|
-
return canonicalizeNumberField(field);
|
|
407
|
+
return canonicalizeNumberField(field, metadataPolicy);
|
|
96
408
|
case "boolean":
|
|
97
|
-
return canonicalizeBooleanField(field);
|
|
409
|
+
return canonicalizeBooleanField(field, metadataPolicy);
|
|
98
410
|
case "enum":
|
|
99
|
-
return canonicalizeStaticEnumField(field);
|
|
411
|
+
return canonicalizeStaticEnumField(field, metadataPolicy);
|
|
100
412
|
case "dynamic_enum":
|
|
101
|
-
return canonicalizeDynamicEnumField(field);
|
|
413
|
+
return canonicalizeDynamicEnumField(field, metadataPolicy);
|
|
102
414
|
case "dynamic_schema":
|
|
103
|
-
return canonicalizeDynamicSchemaField(field);
|
|
415
|
+
return canonicalizeDynamicSchemaField(field, metadataPolicy);
|
|
104
416
|
case "array":
|
|
105
|
-
return canonicalizeArrayField(field);
|
|
417
|
+
return canonicalizeArrayField(field, metadataPolicy);
|
|
106
418
|
case "object":
|
|
107
|
-
return canonicalizeObjectField(field);
|
|
419
|
+
return canonicalizeObjectField(field, metadataPolicy);
|
|
108
420
|
default: {
|
|
109
421
|
const _exhaustive = field;
|
|
110
422
|
throw new Error(`Unknown field type: ${JSON.stringify(_exhaustive)}`);
|
|
111
423
|
}
|
|
112
424
|
}
|
|
113
425
|
}
|
|
114
|
-
function canonicalizeTextField(field) {
|
|
426
|
+
function canonicalizeTextField(field, metadataPolicy) {
|
|
115
427
|
const type = { kind: "primitive", primitiveKind: "string" };
|
|
116
428
|
const constraints = [];
|
|
117
429
|
if (field.minLength !== void 0) {
|
|
@@ -143,13 +455,14 @@ function canonicalizeTextField(field) {
|
|
|
143
455
|
}
|
|
144
456
|
return buildFieldNode(
|
|
145
457
|
field.name,
|
|
458
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
146
459
|
type,
|
|
147
460
|
field.required,
|
|
148
|
-
buildAnnotations(field
|
|
461
|
+
buildAnnotations(getExplicitDisplayName(field), field.placeholder),
|
|
149
462
|
constraints
|
|
150
463
|
);
|
|
151
464
|
}
|
|
152
|
-
function canonicalizeNumberField(field) {
|
|
465
|
+
function canonicalizeNumberField(field, metadataPolicy) {
|
|
153
466
|
const type = { kind: "primitive", primitiveKind: "number" };
|
|
154
467
|
const constraints = [];
|
|
155
468
|
if (field.min !== void 0) {
|
|
@@ -181,17 +494,24 @@ function canonicalizeNumberField(field) {
|
|
|
181
494
|
}
|
|
182
495
|
return buildFieldNode(
|
|
183
496
|
field.name,
|
|
497
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
184
498
|
type,
|
|
185
499
|
field.required,
|
|
186
|
-
buildAnnotations(field
|
|
500
|
+
buildAnnotations(getExplicitDisplayName(field)),
|
|
187
501
|
constraints
|
|
188
502
|
);
|
|
189
503
|
}
|
|
190
|
-
function canonicalizeBooleanField(field) {
|
|
504
|
+
function canonicalizeBooleanField(field, metadataPolicy) {
|
|
191
505
|
const type = { kind: "primitive", primitiveKind: "boolean" };
|
|
192
|
-
return buildFieldNode(
|
|
506
|
+
return buildFieldNode(
|
|
507
|
+
field.name,
|
|
508
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
509
|
+
type,
|
|
510
|
+
field.required,
|
|
511
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
512
|
+
);
|
|
193
513
|
}
|
|
194
|
-
function canonicalizeStaticEnumField(field) {
|
|
514
|
+
function canonicalizeStaticEnumField(field, metadataPolicy) {
|
|
195
515
|
const members = field.options.map((opt) => {
|
|
196
516
|
if (typeof opt === "string") {
|
|
197
517
|
return { value: opt };
|
|
@@ -199,28 +519,46 @@ function canonicalizeStaticEnumField(field) {
|
|
|
199
519
|
return { value: opt.id, displayName: opt.label };
|
|
200
520
|
});
|
|
201
521
|
const type = { kind: "enum", members };
|
|
202
|
-
return buildFieldNode(
|
|
522
|
+
return buildFieldNode(
|
|
523
|
+
field.name,
|
|
524
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
525
|
+
type,
|
|
526
|
+
field.required,
|
|
527
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
528
|
+
);
|
|
203
529
|
}
|
|
204
|
-
function canonicalizeDynamicEnumField(field) {
|
|
530
|
+
function canonicalizeDynamicEnumField(field, metadataPolicy) {
|
|
205
531
|
const type = {
|
|
206
532
|
kind: "dynamic",
|
|
207
533
|
dynamicKind: "enum",
|
|
208
534
|
sourceKey: field.source,
|
|
209
535
|
parameterFields: field.params ? [...field.params] : []
|
|
210
536
|
};
|
|
211
|
-
return buildFieldNode(
|
|
537
|
+
return buildFieldNode(
|
|
538
|
+
field.name,
|
|
539
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
540
|
+
type,
|
|
541
|
+
field.required,
|
|
542
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
543
|
+
);
|
|
212
544
|
}
|
|
213
|
-
function canonicalizeDynamicSchemaField(field) {
|
|
545
|
+
function canonicalizeDynamicSchemaField(field, metadataPolicy) {
|
|
214
546
|
const type = {
|
|
215
547
|
kind: "dynamic",
|
|
216
548
|
dynamicKind: "schema",
|
|
217
549
|
sourceKey: field.schemaSource,
|
|
218
550
|
parameterFields: []
|
|
219
551
|
};
|
|
220
|
-
return buildFieldNode(
|
|
552
|
+
return buildFieldNode(
|
|
553
|
+
field.name,
|
|
554
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
555
|
+
type,
|
|
556
|
+
field.required,
|
|
557
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
558
|
+
);
|
|
221
559
|
}
|
|
222
|
-
function canonicalizeArrayField(field) {
|
|
223
|
-
const itemProperties = buildObjectProperties(field.items);
|
|
560
|
+
function canonicalizeArrayField(field, metadataPolicy) {
|
|
561
|
+
const itemProperties = buildObjectProperties(field.items, metadataPolicy);
|
|
224
562
|
const itemsType = {
|
|
225
563
|
kind: "object",
|
|
226
564
|
properties: itemProperties,
|
|
@@ -248,37 +586,44 @@ function canonicalizeArrayField(field) {
|
|
|
248
586
|
}
|
|
249
587
|
return buildFieldNode(
|
|
250
588
|
field.name,
|
|
589
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
251
590
|
type,
|
|
252
591
|
field.required,
|
|
253
|
-
buildAnnotations(field
|
|
592
|
+
buildAnnotations(getExplicitDisplayName(field)),
|
|
254
593
|
constraints
|
|
255
594
|
);
|
|
256
595
|
}
|
|
257
|
-
function canonicalizeObjectField(field) {
|
|
258
|
-
const properties = buildObjectProperties(field.properties);
|
|
596
|
+
function canonicalizeObjectField(field, metadataPolicy) {
|
|
597
|
+
const properties = buildObjectProperties(field.properties, metadataPolicy);
|
|
259
598
|
const type = {
|
|
260
599
|
kind: "object",
|
|
261
600
|
properties,
|
|
262
601
|
additionalProperties: true
|
|
263
602
|
};
|
|
264
|
-
return buildFieldNode(
|
|
603
|
+
return buildFieldNode(
|
|
604
|
+
field.name,
|
|
605
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
606
|
+
type,
|
|
607
|
+
field.required,
|
|
608
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
609
|
+
);
|
|
265
610
|
}
|
|
266
|
-
function canonicalizeGroup(g) {
|
|
611
|
+
function canonicalizeGroup(g, metadataPolicy) {
|
|
267
612
|
return {
|
|
268
613
|
kind: "group",
|
|
269
614
|
label: g.label,
|
|
270
|
-
elements: canonicalizeElements(g.elements),
|
|
615
|
+
elements: canonicalizeElements(g.elements, metadataPolicy),
|
|
271
616
|
provenance: CHAIN_DSL_PROVENANCE
|
|
272
617
|
};
|
|
273
618
|
}
|
|
274
|
-
function canonicalizeConditional(c) {
|
|
619
|
+
function canonicalizeConditional(c, metadataPolicy) {
|
|
275
620
|
return {
|
|
276
621
|
kind: "conditional",
|
|
277
622
|
fieldName: c.field,
|
|
278
623
|
// Conditional values from the chain DSL are JSON-serializable primitives
|
|
279
624
|
// (strings, numbers, booleans) produced by the `is()` predicate helper.
|
|
280
625
|
value: assertJsonValue(c.value),
|
|
281
|
-
elements: canonicalizeElements(c.elements),
|
|
626
|
+
elements: canonicalizeElements(c.elements, metadataPolicy),
|
|
282
627
|
provenance: CHAIN_DSL_PROVENANCE
|
|
283
628
|
};
|
|
284
629
|
}
|
|
@@ -298,10 +643,11 @@ function assertJsonValue(v) {
|
|
|
298
643
|
}
|
|
299
644
|
throw new TypeError(`Conditional value is not a valid JsonValue: ${typeof v}`);
|
|
300
645
|
}
|
|
301
|
-
function buildFieldNode(name, type, required, annotations, constraints = []) {
|
|
646
|
+
function buildFieldNode(name, metadata, type, required, annotations, constraints = []) {
|
|
302
647
|
return {
|
|
303
648
|
kind: "field",
|
|
304
649
|
name,
|
|
650
|
+
...metadata !== void 0 && { metadata },
|
|
305
651
|
type,
|
|
306
652
|
required: required === true,
|
|
307
653
|
constraints,
|
|
@@ -331,13 +677,14 @@ function buildAnnotations(label, placeholder) {
|
|
|
331
677
|
}
|
|
332
678
|
return annotations;
|
|
333
679
|
}
|
|
334
|
-
function buildObjectProperties(elements, insideConditional = false) {
|
|
680
|
+
function buildObjectProperties(elements, metadataPolicy, insideConditional = false) {
|
|
335
681
|
const properties = [];
|
|
336
682
|
for (const el of elements) {
|
|
337
683
|
if (isField(el)) {
|
|
338
|
-
const fieldNode = canonicalizeField(el);
|
|
684
|
+
const fieldNode = canonicalizeField(el, metadataPolicy);
|
|
339
685
|
properties.push({
|
|
340
686
|
name: fieldNode.name,
|
|
687
|
+
...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
|
|
341
688
|
type: fieldNode.type,
|
|
342
689
|
// Fields inside a conditional branch are always optional in the
|
|
343
690
|
// data schema, regardless of their `required` flag — the condition
|
|
@@ -348,17 +695,34 @@ function buildObjectProperties(elements, insideConditional = false) {
|
|
|
348
695
|
provenance: CHAIN_DSL_PROVENANCE
|
|
349
696
|
});
|
|
350
697
|
} else if (isGroup(el)) {
|
|
351
|
-
properties.push(...buildObjectProperties(el.elements, insideConditional));
|
|
698
|
+
properties.push(...buildObjectProperties(el.elements, metadataPolicy, insideConditional));
|
|
352
699
|
} else if (isConditional(el)) {
|
|
353
|
-
properties.push(...buildObjectProperties(el.elements, true));
|
|
700
|
+
properties.push(...buildObjectProperties(el.elements, metadataPolicy, true));
|
|
354
701
|
}
|
|
355
702
|
}
|
|
356
703
|
return properties;
|
|
357
704
|
}
|
|
705
|
+
function getExplicitDisplayName(field) {
|
|
706
|
+
if (field.label !== void 0 && field.displayName !== void 0) {
|
|
707
|
+
throw new Error('Chain DSL fields cannot specify both "label" and "displayName".');
|
|
708
|
+
}
|
|
709
|
+
return field.displayName ?? field.label;
|
|
710
|
+
}
|
|
711
|
+
function resolveFieldMetadata(logicalName, field, metadataPolicy) {
|
|
712
|
+
const displayName = getExplicitDisplayName(field);
|
|
713
|
+
return resolveMetadata(
|
|
714
|
+
{
|
|
715
|
+
...field.apiName !== void 0 && { apiName: field.apiName },
|
|
716
|
+
...displayName !== void 0 && { displayName }
|
|
717
|
+
},
|
|
718
|
+
getDeclarationMetadataPolicy(metadataPolicy, "field"),
|
|
719
|
+
makeMetadataContext("chain-dsl", "field", logicalName)
|
|
720
|
+
);
|
|
721
|
+
}
|
|
358
722
|
|
|
359
723
|
// src/canonicalize/tsdoc-canonicalizer.ts
|
|
360
724
|
var import_internals2 = require("@formspec/core/internals");
|
|
361
|
-
function canonicalizeTSDoc(analysis, source) {
|
|
725
|
+
function canonicalizeTSDoc(analysis, source, options) {
|
|
362
726
|
const file = source?.file ?? "";
|
|
363
727
|
const provenance = {
|
|
364
728
|
surface: "tsdoc",
|
|
@@ -367,15 +731,21 @@ function canonicalizeTSDoc(analysis, source) {
|
|
|
367
731
|
column: 0
|
|
368
732
|
};
|
|
369
733
|
const elements = assembleElements(analysis.fields, analysis.fieldLayouts, provenance);
|
|
370
|
-
|
|
734
|
+
const ir = {
|
|
371
735
|
kind: "form-ir",
|
|
736
|
+
name: analysis.name,
|
|
372
737
|
irVersion: import_internals2.IR_VERSION,
|
|
373
738
|
elements,
|
|
739
|
+
...analysis.metadata !== void 0 && { metadata: analysis.metadata },
|
|
374
740
|
typeRegistry: analysis.typeRegistry,
|
|
375
741
|
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { rootAnnotations: analysis.annotations },
|
|
376
742
|
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { annotations: analysis.annotations },
|
|
377
743
|
provenance
|
|
378
744
|
};
|
|
745
|
+
return resolveFormIRMetadata(ir, {
|
|
746
|
+
policy: normalizeMetadataPolicy(options?.metadata),
|
|
747
|
+
surface: "tsdoc"
|
|
748
|
+
});
|
|
379
749
|
}
|
|
380
750
|
function assembleElements(fields, layouts, provenance) {
|
|
381
751
|
const elements = [];
|
|
@@ -432,6 +802,120 @@ function wrapInConditional(field, layout, provenance) {
|
|
|
432
802
|
return conditional;
|
|
433
803
|
}
|
|
434
804
|
|
|
805
|
+
// src/metadata/collision-guards.ts
|
|
806
|
+
function assertUniqueSerializedNames(entries, scope) {
|
|
807
|
+
const seen = /* @__PURE__ */ new Map();
|
|
808
|
+
for (const entry of entries) {
|
|
809
|
+
const previous = seen.get(entry.serializedName);
|
|
810
|
+
if (previous !== void 0) {
|
|
811
|
+
if (previous.logicalName === entry.logicalName && previous.category === entry.category) {
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
throw new Error(
|
|
815
|
+
`Serialized name collision in ${scope}: ${previous.category} "${previous.logicalName}" and ${entry.category} "${entry.logicalName}" both resolve to "${entry.serializedName}".`
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
seen.set(entry.serializedName, entry);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
function collectFlattenedFields(elements) {
|
|
822
|
+
const fields = [];
|
|
823
|
+
for (const element of elements) {
|
|
824
|
+
switch (element.kind) {
|
|
825
|
+
case "field":
|
|
826
|
+
fields.push(element);
|
|
827
|
+
break;
|
|
828
|
+
case "group":
|
|
829
|
+
case "conditional":
|
|
830
|
+
fields.push(...collectFlattenedFields(element.elements));
|
|
831
|
+
break;
|
|
832
|
+
default: {
|
|
833
|
+
const exhaustive = element;
|
|
834
|
+
void exhaustive;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
return fields;
|
|
839
|
+
}
|
|
840
|
+
function validateObjectProperties(properties, scope) {
|
|
841
|
+
assertUniqueSerializedNames(
|
|
842
|
+
properties.map((property) => ({
|
|
843
|
+
logicalName: property.name,
|
|
844
|
+
serializedName: getSerializedName(property.name, property.metadata),
|
|
845
|
+
category: "object property"
|
|
846
|
+
})),
|
|
847
|
+
scope
|
|
848
|
+
);
|
|
849
|
+
for (const property of properties) {
|
|
850
|
+
validateTypeNode(
|
|
851
|
+
property.type,
|
|
852
|
+
`${scope}.${getSerializedName(property.name, property.metadata)}`
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
function validateTypeNode(type, scope) {
|
|
857
|
+
switch (type.kind) {
|
|
858
|
+
case "array":
|
|
859
|
+
validateTypeNode(type.items, `${scope}[]`);
|
|
860
|
+
break;
|
|
861
|
+
case "object":
|
|
862
|
+
validateObjectProperties(type.properties, scope);
|
|
863
|
+
break;
|
|
864
|
+
case "record":
|
|
865
|
+
validateTypeNode(type.valueType, `${scope}.*`);
|
|
866
|
+
break;
|
|
867
|
+
case "union":
|
|
868
|
+
type.members.forEach((member, index) => {
|
|
869
|
+
validateTypeNode(member, `${scope}|${String(index)}`);
|
|
870
|
+
});
|
|
871
|
+
break;
|
|
872
|
+
case "reference":
|
|
873
|
+
case "primitive":
|
|
874
|
+
case "enum":
|
|
875
|
+
case "dynamic":
|
|
876
|
+
case "custom":
|
|
877
|
+
break;
|
|
878
|
+
default: {
|
|
879
|
+
const exhaustive = type;
|
|
880
|
+
void exhaustive;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
function validateTypeDefinitions(typeRegistry) {
|
|
885
|
+
const definitions = Object.values(typeRegistry);
|
|
886
|
+
assertUniqueSerializedNames(
|
|
887
|
+
definitions.map((definition) => ({
|
|
888
|
+
logicalName: definition.name,
|
|
889
|
+
serializedName: getSerializedName(definition.name, definition.metadata),
|
|
890
|
+
category: "type definition"
|
|
891
|
+
})),
|
|
892
|
+
"$defs"
|
|
893
|
+
);
|
|
894
|
+
for (const definition of definitions) {
|
|
895
|
+
validateTypeDefinition(definition);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
function validateTypeDefinition(definition) {
|
|
899
|
+
validateTypeNode(
|
|
900
|
+
definition.type,
|
|
901
|
+
`type "${getSerializedName(definition.name, definition.metadata)}"`
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
function assertNoSerializedNameCollisions(ir) {
|
|
905
|
+
assertUniqueSerializedNames(
|
|
906
|
+
collectFlattenedFields(ir.elements).map((field) => ({
|
|
907
|
+
logicalName: field.name,
|
|
908
|
+
serializedName: getSerializedName(field.name, field.metadata),
|
|
909
|
+
category: "field"
|
|
910
|
+
})),
|
|
911
|
+
"form root"
|
|
912
|
+
);
|
|
913
|
+
for (const field of collectFlattenedFields(ir.elements)) {
|
|
914
|
+
validateTypeNode(field.type, `field "${getSerializedName(field.name, field.metadata)}"`);
|
|
915
|
+
}
|
|
916
|
+
validateTypeDefinitions(ir.typeRegistry);
|
|
917
|
+
}
|
|
918
|
+
|
|
435
919
|
// src/json-schema/ir-generator.ts
|
|
436
920
|
function makeContext(options) {
|
|
437
921
|
const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
|
|
@@ -442,19 +926,33 @@ function makeContext(options) {
|
|
|
442
926
|
}
|
|
443
927
|
return {
|
|
444
928
|
defs: {},
|
|
929
|
+
typeNameMap: {},
|
|
930
|
+
typeRegistry: {},
|
|
445
931
|
extensionRegistry: options?.extensionRegistry,
|
|
446
932
|
vendorPrefix
|
|
447
933
|
};
|
|
448
934
|
}
|
|
449
935
|
function generateJsonSchemaFromIR(ir, options) {
|
|
450
|
-
|
|
936
|
+
assertNoSerializedNameCollisions(ir);
|
|
937
|
+
const ctx = {
|
|
938
|
+
...makeContext(options),
|
|
939
|
+
typeRegistry: ir.typeRegistry,
|
|
940
|
+
typeNameMap: Object.fromEntries(
|
|
941
|
+
Object.entries(ir.typeRegistry).map(([name, typeDef]) => [
|
|
942
|
+
name,
|
|
943
|
+
getSerializedName(name, typeDef.metadata)
|
|
944
|
+
])
|
|
945
|
+
)
|
|
946
|
+
};
|
|
451
947
|
for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
|
|
452
|
-
ctx.
|
|
948
|
+
const schemaName = ctx.typeNameMap[name] ?? name;
|
|
949
|
+
ctx.defs[schemaName] = generateTypeNode(typeDef.type, ctx);
|
|
950
|
+
applyResolvedMetadata(ctx.defs[schemaName], typeDef.metadata);
|
|
453
951
|
if (typeDef.constraints && typeDef.constraints.length > 0) {
|
|
454
|
-
applyConstraints(ctx.defs[
|
|
952
|
+
applyConstraints(ctx.defs[schemaName], typeDef.constraints, ctx);
|
|
455
953
|
}
|
|
456
954
|
if (typeDef.annotations && typeDef.annotations.length > 0) {
|
|
457
|
-
applyAnnotations(ctx.defs[
|
|
955
|
+
applyAnnotations(ctx.defs[schemaName], typeDef.annotations, ctx);
|
|
458
956
|
}
|
|
459
957
|
}
|
|
460
958
|
const properties = {};
|
|
@@ -467,6 +965,7 @@ function generateJsonSchemaFromIR(ir, options) {
|
|
|
467
965
|
properties,
|
|
468
966
|
...uniqueRequired.length > 0 && { required: uniqueRequired }
|
|
469
967
|
};
|
|
968
|
+
applyResolvedMetadata(result, ir.metadata);
|
|
470
969
|
if (ir.annotations && ir.annotations.length > 0) {
|
|
471
970
|
applyAnnotations(result, ir.annotations, ctx);
|
|
472
971
|
}
|
|
@@ -479,9 +978,9 @@ function collectFields(elements, properties, required, ctx) {
|
|
|
479
978
|
for (const element of elements) {
|
|
480
979
|
switch (element.kind) {
|
|
481
980
|
case "field":
|
|
482
|
-
properties[element.name] = generateFieldSchema(element, ctx);
|
|
981
|
+
properties[getSerializedName(element.name, element.metadata)] = generateFieldSchema(element, ctx);
|
|
483
982
|
if (element.required) {
|
|
484
|
-
required.push(element.name);
|
|
983
|
+
required.push(getSerializedName(element.name, element.metadata));
|
|
485
984
|
}
|
|
486
985
|
break;
|
|
487
986
|
case "group":
|
|
@@ -525,6 +1024,7 @@ function generateFieldSchema(field, ctx) {
|
|
|
525
1024
|
rootAnnotations.push(annotation);
|
|
526
1025
|
}
|
|
527
1026
|
}
|
|
1027
|
+
applyResolvedMetadata(schema, field.metadata);
|
|
528
1028
|
applyAnnotations(schema, rootAnnotations, ctx);
|
|
529
1029
|
if (itemStringSchema !== void 0) {
|
|
530
1030
|
applyAnnotations(itemStringSchema, itemAnnotations, ctx);
|
|
@@ -532,7 +1032,7 @@ function generateFieldSchema(field, ctx) {
|
|
|
532
1032
|
if (pathConstraints.length === 0) {
|
|
533
1033
|
return schema;
|
|
534
1034
|
}
|
|
535
|
-
return applyPathTargetedConstraints(schema, pathConstraints, ctx);
|
|
1035
|
+
return applyPathTargetedConstraints(schema, pathConstraints, ctx, field.type);
|
|
536
1036
|
}
|
|
537
1037
|
function isStringItemConstraint(constraint) {
|
|
538
1038
|
switch (constraint.constraintKind) {
|
|
@@ -544,9 +1044,11 @@ function isStringItemConstraint(constraint) {
|
|
|
544
1044
|
return false;
|
|
545
1045
|
}
|
|
546
1046
|
}
|
|
547
|
-
function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
|
|
1047
|
+
function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
|
|
548
1048
|
if (schema.type === "array" && schema.items) {
|
|
549
|
-
|
|
1049
|
+
const referencedType = typeNode?.kind === "reference" ? resolveReferencedType(typeNode, ctx) : void 0;
|
|
1050
|
+
const nestedType = typeNode?.kind === "array" ? typeNode.items : referencedType?.kind === "array" ? referencedType.items : void 0;
|
|
1051
|
+
schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx, nestedType);
|
|
550
1052
|
return schema;
|
|
551
1053
|
}
|
|
552
1054
|
const byTarget = /* @__PURE__ */ new Map();
|
|
@@ -561,7 +1063,7 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
|
|
|
561
1063
|
for (const [target, constraints] of byTarget) {
|
|
562
1064
|
const subSchema = {};
|
|
563
1065
|
applyConstraints(subSchema, constraints, ctx);
|
|
564
|
-
propertyOverrides[target] = subSchema;
|
|
1066
|
+
propertyOverrides[resolveSerializedPropertyName(target, typeNode, ctx)] = subSchema;
|
|
565
1067
|
}
|
|
566
1068
|
if (schema.$ref) {
|
|
567
1069
|
const { $ref, ...rest } = schema;
|
|
@@ -609,7 +1111,7 @@ function generateTypeNode(type, ctx) {
|
|
|
609
1111
|
case "union":
|
|
610
1112
|
return generateUnionType(type, ctx);
|
|
611
1113
|
case "reference":
|
|
612
|
-
return generateReferenceType(type);
|
|
1114
|
+
return generateReferenceType(type, ctx);
|
|
613
1115
|
case "dynamic":
|
|
614
1116
|
return generateDynamicType(type);
|
|
615
1117
|
case "custom":
|
|
@@ -650,9 +1152,10 @@ function generateObjectType(type, ctx) {
|
|
|
650
1152
|
const properties = {};
|
|
651
1153
|
const required = [];
|
|
652
1154
|
for (const prop of type.properties) {
|
|
653
|
-
|
|
1155
|
+
const propertyName = getSerializedName(prop.name, prop.metadata);
|
|
1156
|
+
properties[propertyName] = generatePropertySchema(prop, ctx);
|
|
654
1157
|
if (!prop.optional) {
|
|
655
|
-
required.push(
|
|
1158
|
+
required.push(propertyName);
|
|
656
1159
|
}
|
|
657
1160
|
}
|
|
658
1161
|
const schema = { type: "object", properties };
|
|
@@ -673,6 +1176,7 @@ function generateRecordType(type, ctx) {
|
|
|
673
1176
|
function generatePropertySchema(prop, ctx) {
|
|
674
1177
|
const schema = generateTypeNode(prop.type, ctx);
|
|
675
1178
|
applyConstraints(schema, prop.constraints, ctx);
|
|
1179
|
+
applyResolvedMetadata(schema, prop.metadata);
|
|
676
1180
|
applyAnnotations(schema, prop.annotations, ctx);
|
|
677
1181
|
return schema;
|
|
678
1182
|
}
|
|
@@ -701,8 +1205,28 @@ function isNullableUnion(type) {
|
|
|
701
1205
|
).length;
|
|
702
1206
|
return nullCount === 1;
|
|
703
1207
|
}
|
|
704
|
-
function generateReferenceType(type) {
|
|
705
|
-
return { $ref: `#/$defs/${type.name}` };
|
|
1208
|
+
function generateReferenceType(type, ctx) {
|
|
1209
|
+
return { $ref: `#/$defs/${ctx.typeNameMap[type.name] ?? type.name}` };
|
|
1210
|
+
}
|
|
1211
|
+
function applyResolvedMetadata(schema, metadata) {
|
|
1212
|
+
const displayName = getDisplayName(metadata);
|
|
1213
|
+
if (displayName !== void 0) {
|
|
1214
|
+
schema.title = displayName;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
function resolveReferencedType(type, ctx) {
|
|
1218
|
+
return ctx.typeRegistry[type.name]?.type;
|
|
1219
|
+
}
|
|
1220
|
+
function resolveSerializedPropertyName(logicalName, typeNode, ctx) {
|
|
1221
|
+
if (typeNode?.kind === "object") {
|
|
1222
|
+
const property = typeNode.properties.find((candidate) => candidate.name === logicalName);
|
|
1223
|
+
return property === void 0 ? logicalName : getSerializedName(property.name, property.metadata);
|
|
1224
|
+
}
|
|
1225
|
+
if (typeNode?.kind === "reference") {
|
|
1226
|
+
const referencedType = resolveReferencedType(typeNode, ctx);
|
|
1227
|
+
return referencedType === void 0 ? logicalName : resolveSerializedPropertyName(logicalName, referencedType, ctx);
|
|
1228
|
+
}
|
|
1229
|
+
return logicalName;
|
|
706
1230
|
}
|
|
707
1231
|
function generateDynamicType(type) {
|
|
708
1232
|
if (type.dynamicKind === "enum") {
|
|
@@ -782,7 +1306,7 @@ function applyAnnotations(schema, annotations, ctx) {
|
|
|
782
1306
|
for (const annotation of annotations) {
|
|
783
1307
|
switch (annotation.annotationKind) {
|
|
784
1308
|
case "displayName":
|
|
785
|
-
schema.title
|
|
1309
|
+
schema.title ??= annotation.value;
|
|
786
1310
|
break;
|
|
787
1311
|
case "description":
|
|
788
1312
|
schema.description = annotation.value;
|
|
@@ -869,7 +1393,10 @@ function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPr
|
|
|
869
1393
|
|
|
870
1394
|
// src/json-schema/generator.ts
|
|
871
1395
|
function generateJsonSchema(form, options) {
|
|
872
|
-
const ir = canonicalizeChainDSL(
|
|
1396
|
+
const ir = canonicalizeChainDSL(
|
|
1397
|
+
form,
|
|
1398
|
+
options?.metadata !== void 0 ? { metadata: options.metadata } : void 0
|
|
1399
|
+
);
|
|
873
1400
|
const internalOptions = options?.vendorPrefix === void 0 ? void 0 : { vendorPrefix: options.vendorPrefix };
|
|
874
1401
|
return generateJsonSchemaFromIR(ir, internalOptions);
|
|
875
1402
|
}
|
|
@@ -1039,13 +1566,21 @@ function combineRules(parentRule, childRule) {
|
|
|
1039
1566
|
}
|
|
1040
1567
|
};
|
|
1041
1568
|
}
|
|
1042
|
-
function
|
|
1043
|
-
const
|
|
1569
|
+
function getFieldDisplayName(field) {
|
|
1570
|
+
const resolvedDisplayName = getDisplayName(field.metadata);
|
|
1571
|
+
if (resolvedDisplayName !== void 0) {
|
|
1572
|
+
return resolvedDisplayName;
|
|
1573
|
+
}
|
|
1574
|
+
return field.annotations.find((annotation) => annotation.annotationKind === "displayName")?.value;
|
|
1575
|
+
}
|
|
1576
|
+
function fieldNodeToControl(field, fieldNameMap, parentRule) {
|
|
1044
1577
|
const placeholderAnnotation = field.annotations.find((a) => a.annotationKind === "placeholder");
|
|
1578
|
+
const serializedName = fieldNameMap.get(field.name) ?? getSerializedName(field.name, field.metadata);
|
|
1579
|
+
const displayName = getFieldDisplayName(field);
|
|
1045
1580
|
const control = {
|
|
1046
1581
|
type: "Control",
|
|
1047
|
-
scope: fieldToScope(
|
|
1048
|
-
...
|
|
1582
|
+
scope: fieldToScope(serializedName),
|
|
1583
|
+
...displayName !== void 0 && { label: displayName },
|
|
1049
1584
|
...placeholderAnnotation !== void 0 && {
|
|
1050
1585
|
options: { placeholder: placeholderAnnotation.value }
|
|
1051
1586
|
},
|
|
@@ -1053,30 +1588,30 @@ function fieldNodeToControl(field, parentRule) {
|
|
|
1053
1588
|
};
|
|
1054
1589
|
return control;
|
|
1055
1590
|
}
|
|
1056
|
-
function groupNodeToLayout(group, parentRule) {
|
|
1591
|
+
function groupNodeToLayout(group, fieldNameMap, parentRule) {
|
|
1057
1592
|
return {
|
|
1058
1593
|
type: "Group",
|
|
1059
1594
|
label: group.label,
|
|
1060
|
-
elements: irElementsToUiSchema(group.elements, parentRule),
|
|
1595
|
+
elements: irElementsToUiSchema(group.elements, fieldNameMap, parentRule),
|
|
1061
1596
|
...parentRule !== void 0 && { rule: parentRule }
|
|
1062
1597
|
};
|
|
1063
1598
|
}
|
|
1064
|
-
function irElementsToUiSchema(elements, parentRule) {
|
|
1599
|
+
function irElementsToUiSchema(elements, fieldNameMap, parentRule) {
|
|
1065
1600
|
const result = [];
|
|
1066
1601
|
for (const element of elements) {
|
|
1067
1602
|
switch (element.kind) {
|
|
1068
1603
|
case "field": {
|
|
1069
|
-
result.push(fieldNodeToControl(element, parentRule));
|
|
1604
|
+
result.push(fieldNodeToControl(element, fieldNameMap, parentRule));
|
|
1070
1605
|
break;
|
|
1071
1606
|
}
|
|
1072
1607
|
case "group": {
|
|
1073
|
-
result.push(groupNodeToLayout(element, parentRule));
|
|
1608
|
+
result.push(groupNodeToLayout(element, fieldNameMap, parentRule));
|
|
1074
1609
|
break;
|
|
1075
1610
|
}
|
|
1076
1611
|
case "conditional": {
|
|
1077
|
-
const newRule = createShowRule(element.fieldName, element.value);
|
|
1612
|
+
const newRule = createShowRule(fieldNameMap.get(element.fieldName) ?? element.fieldName, element.value);
|
|
1078
1613
|
const combinedRule = parentRule !== void 0 ? combineRules(parentRule, newRule) : newRule;
|
|
1079
|
-
const childElements = irElementsToUiSchema(element.elements, combinedRule);
|
|
1614
|
+
const childElements = irElementsToUiSchema(element.elements, fieldNameMap, combinedRule);
|
|
1080
1615
|
result.push(...childElements);
|
|
1081
1616
|
break;
|
|
1082
1617
|
}
|
|
@@ -1090,16 +1625,42 @@ function irElementsToUiSchema(elements, parentRule) {
|
|
|
1090
1625
|
return result;
|
|
1091
1626
|
}
|
|
1092
1627
|
function generateUiSchemaFromIR(ir) {
|
|
1628
|
+
assertNoSerializedNameCollisions(ir);
|
|
1629
|
+
const fieldNameMap = collectFieldNameMap(ir.elements);
|
|
1093
1630
|
const result = {
|
|
1094
1631
|
type: "VerticalLayout",
|
|
1095
|
-
elements: irElementsToUiSchema(ir.elements)
|
|
1632
|
+
elements: irElementsToUiSchema(ir.elements, fieldNameMap)
|
|
1096
1633
|
};
|
|
1097
1634
|
return parseOrThrow(uiSchema, result, "UI Schema");
|
|
1098
1635
|
}
|
|
1636
|
+
function collectFieldNameMap(elements) {
|
|
1637
|
+
const map = /* @__PURE__ */ new Map();
|
|
1638
|
+
for (const element of elements) {
|
|
1639
|
+
switch (element.kind) {
|
|
1640
|
+
case "field":
|
|
1641
|
+
map.set(element.name, getSerializedName(element.name, element.metadata));
|
|
1642
|
+
break;
|
|
1643
|
+
case "group":
|
|
1644
|
+
case "conditional":
|
|
1645
|
+
for (const [key, value] of collectFieldNameMap(element.elements)) {
|
|
1646
|
+
map.set(key, value);
|
|
1647
|
+
}
|
|
1648
|
+
break;
|
|
1649
|
+
default: {
|
|
1650
|
+
const _exhaustive = element;
|
|
1651
|
+
void _exhaustive;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
return map;
|
|
1656
|
+
}
|
|
1099
1657
|
|
|
1100
1658
|
// src/ui-schema/generator.ts
|
|
1101
|
-
function generateUiSchema(form) {
|
|
1102
|
-
const ir = canonicalizeChainDSL(
|
|
1659
|
+
function generateUiSchema(form, options) {
|
|
1660
|
+
const ir = canonicalizeChainDSL(
|
|
1661
|
+
form,
|
|
1662
|
+
options?.metadata !== void 0 ? { metadata: options.metadata } : void 0
|
|
1663
|
+
);
|
|
1103
1664
|
return generateUiSchemaFromIR(ir);
|
|
1104
1665
|
}
|
|
1105
1666
|
|
|
@@ -1258,6 +1819,7 @@ var path = __toESM(require("path"), 1);
|
|
|
1258
1819
|
|
|
1259
1820
|
// src/analyzer/class-analyzer.ts
|
|
1260
1821
|
var ts3 = __toESM(require("typescript"), 1);
|
|
1822
|
+
var import_internal2 = require("@formspec/analysis/internal");
|
|
1261
1823
|
|
|
1262
1824
|
// src/analyzer/jsdoc-constraints.ts
|
|
1263
1825
|
var ts2 = __toESM(require("typescript"), 1);
|
|
@@ -2135,7 +2697,76 @@ function makeParseOptions(extensionRegistry, fieldType, checker, subjectType, ho
|
|
|
2135
2697
|
...hostType !== void 0 && { hostType }
|
|
2136
2698
|
};
|
|
2137
2699
|
}
|
|
2138
|
-
function
|
|
2700
|
+
function makeExplicitScalarMetadata(value) {
|
|
2701
|
+
return value === void 0 || value === "" ? void 0 : { value, source: "explicit" };
|
|
2702
|
+
}
|
|
2703
|
+
function extractExplicitMetadata(node) {
|
|
2704
|
+
let apiName;
|
|
2705
|
+
let displayName;
|
|
2706
|
+
let apiNamePlural;
|
|
2707
|
+
let displayNamePlural;
|
|
2708
|
+
for (const tag of getLeadingParsedTags(node)) {
|
|
2709
|
+
const value = tag.argumentText.trim();
|
|
2710
|
+
if (value === "") {
|
|
2711
|
+
continue;
|
|
2712
|
+
}
|
|
2713
|
+
if (tag.normalizedTagName === "apiName") {
|
|
2714
|
+
if (tag.target === null) {
|
|
2715
|
+
apiName ??= value;
|
|
2716
|
+
} else if (tag.target.kind === "variant") {
|
|
2717
|
+
if (tag.target.rawText === "singular") {
|
|
2718
|
+
apiName ??= value;
|
|
2719
|
+
} else if (tag.target.rawText === "plural") {
|
|
2720
|
+
apiNamePlural ??= value;
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
continue;
|
|
2724
|
+
}
|
|
2725
|
+
if (tag.normalizedTagName === "displayName") {
|
|
2726
|
+
if (tag.target === null) {
|
|
2727
|
+
displayName ??= value;
|
|
2728
|
+
} else if (tag.target.kind === "variant") {
|
|
2729
|
+
if (tag.target.rawText === "singular") {
|
|
2730
|
+
displayName ??= value;
|
|
2731
|
+
} else if (tag.target.rawText === "plural") {
|
|
2732
|
+
displayNamePlural ??= value;
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
const resolvedApiName = makeExplicitScalarMetadata(apiName);
|
|
2738
|
+
const resolvedDisplayName = makeExplicitScalarMetadata(displayName);
|
|
2739
|
+
const resolvedApiNamePlural = makeExplicitScalarMetadata(apiNamePlural);
|
|
2740
|
+
const resolvedDisplayNamePlural = makeExplicitScalarMetadata(displayNamePlural);
|
|
2741
|
+
const metadata = {
|
|
2742
|
+
...resolvedApiName !== void 0 && { apiName: resolvedApiName },
|
|
2743
|
+
...resolvedDisplayName !== void 0 && { displayName: resolvedDisplayName },
|
|
2744
|
+
...resolvedApiNamePlural !== void 0 && { apiNamePlural: resolvedApiNamePlural },
|
|
2745
|
+
...resolvedDisplayNamePlural !== void 0 && {
|
|
2746
|
+
displayNamePlural: resolvedDisplayNamePlural
|
|
2747
|
+
}
|
|
2748
|
+
};
|
|
2749
|
+
return Object.keys(metadata).length === 0 ? void 0 : metadata;
|
|
2750
|
+
}
|
|
2751
|
+
function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, buildContext) {
|
|
2752
|
+
const explicit = extractExplicitMetadata(node);
|
|
2753
|
+
return resolveMetadata(
|
|
2754
|
+
{
|
|
2755
|
+
...explicit?.apiName !== void 0 && { apiName: explicit.apiName.value },
|
|
2756
|
+
...explicit?.displayName !== void 0 && { displayName: explicit.displayName.value },
|
|
2757
|
+
...explicit?.apiNamePlural !== void 0 && {
|
|
2758
|
+
apiNamePlural: explicit.apiNamePlural.value
|
|
2759
|
+
},
|
|
2760
|
+
...explicit?.displayNamePlural !== void 0 && {
|
|
2761
|
+
displayNamePlural: explicit.displayNamePlural.value
|
|
2762
|
+
}
|
|
2763
|
+
},
|
|
2764
|
+
getDeclarationMetadataPolicy(metadataPolicy, declarationKind),
|
|
2765
|
+
makeMetadataContext("tsdoc", declarationKind, logicalName, buildContext)
|
|
2766
|
+
);
|
|
2767
|
+
}
|
|
2768
|
+
function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy) {
|
|
2769
|
+
const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
|
|
2139
2770
|
const name = classDecl.name?.text ?? "AnonymousClass";
|
|
2140
2771
|
const fields = [];
|
|
2141
2772
|
const fieldLayouts = [];
|
|
@@ -2162,6 +2793,7 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
2162
2793
|
visiting,
|
|
2163
2794
|
diagnostics,
|
|
2164
2795
|
classType,
|
|
2796
|
+
normalizedMetadataPolicy,
|
|
2165
2797
|
extensionRegistry
|
|
2166
2798
|
);
|
|
2167
2799
|
if (fieldNode) {
|
|
@@ -2180,9 +2812,25 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
2180
2812
|
}
|
|
2181
2813
|
}
|
|
2182
2814
|
}
|
|
2815
|
+
const specializedFields = applyDeclarationDiscriminatorToFields(
|
|
2816
|
+
fields,
|
|
2817
|
+
classDecl,
|
|
2818
|
+
classType,
|
|
2819
|
+
checker,
|
|
2820
|
+
file,
|
|
2821
|
+
diagnostics,
|
|
2822
|
+
normalizedMetadataPolicy
|
|
2823
|
+
);
|
|
2824
|
+
const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, classDecl, {
|
|
2825
|
+
checker,
|
|
2826
|
+
declaration: classDecl,
|
|
2827
|
+
subjectType: classType,
|
|
2828
|
+
hostType: classType
|
|
2829
|
+
});
|
|
2183
2830
|
return {
|
|
2184
2831
|
name,
|
|
2185
|
-
|
|
2832
|
+
...metadata !== void 0 && { metadata },
|
|
2833
|
+
fields: specializedFields,
|
|
2186
2834
|
fieldLayouts,
|
|
2187
2835
|
typeRegistry,
|
|
2188
2836
|
...annotations.length > 0 && { annotations },
|
|
@@ -2191,7 +2839,8 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
2191
2839
|
staticMethods
|
|
2192
2840
|
};
|
|
2193
2841
|
}
|
|
2194
|
-
function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry) {
|
|
2842
|
+
function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy) {
|
|
2843
|
+
const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
|
|
2195
2844
|
const name = interfaceDecl.name.text;
|
|
2196
2845
|
const fields = [];
|
|
2197
2846
|
const typeRegistry = {};
|
|
@@ -2215,6 +2864,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
2215
2864
|
visiting,
|
|
2216
2865
|
diagnostics,
|
|
2217
2866
|
interfaceType,
|
|
2867
|
+
normalizedMetadataPolicy,
|
|
2218
2868
|
extensionRegistry
|
|
2219
2869
|
);
|
|
2220
2870
|
if (fieldNode) {
|
|
@@ -2222,10 +2872,26 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
2222
2872
|
}
|
|
2223
2873
|
}
|
|
2224
2874
|
}
|
|
2225
|
-
const
|
|
2875
|
+
const specializedFields = applyDeclarationDiscriminatorToFields(
|
|
2876
|
+
fields,
|
|
2877
|
+
interfaceDecl,
|
|
2878
|
+
interfaceType,
|
|
2879
|
+
checker,
|
|
2880
|
+
file,
|
|
2881
|
+
diagnostics,
|
|
2882
|
+
normalizedMetadataPolicy
|
|
2883
|
+
);
|
|
2884
|
+
const fieldLayouts = specializedFields.map(() => ({}));
|
|
2885
|
+
const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, interfaceDecl, {
|
|
2886
|
+
checker,
|
|
2887
|
+
declaration: interfaceDecl,
|
|
2888
|
+
subjectType: interfaceType,
|
|
2889
|
+
hostType: interfaceType
|
|
2890
|
+
});
|
|
2226
2891
|
return {
|
|
2227
2892
|
name,
|
|
2228
|
-
|
|
2893
|
+
...metadata !== void 0 && { metadata },
|
|
2894
|
+
fields: specializedFields,
|
|
2229
2895
|
fieldLayouts,
|
|
2230
2896
|
typeRegistry,
|
|
2231
2897
|
...annotations.length > 0 && { annotations },
|
|
@@ -2234,7 +2900,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
2234
2900
|
staticMethods: []
|
|
2235
2901
|
};
|
|
2236
2902
|
}
|
|
2237
|
-
function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
|
|
2903
|
+
function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry, metadataPolicy) {
|
|
2238
2904
|
if (!ts3.isTypeLiteralNode(typeAlias.type)) {
|
|
2239
2905
|
const sourceFile = typeAlias.getSourceFile();
|
|
2240
2906
|
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
@@ -2244,6 +2910,8 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
2244
2910
|
error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object type literal (found ${kindDesc})`
|
|
2245
2911
|
};
|
|
2246
2912
|
}
|
|
2913
|
+
const typeLiteral = typeAlias.type;
|
|
2914
|
+
const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
|
|
2247
2915
|
const name = typeAlias.name.text;
|
|
2248
2916
|
const fields = [];
|
|
2249
2917
|
const typeRegistry = {};
|
|
@@ -2257,7 +2925,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
2257
2925
|
const annotations = [...typeAliasDoc.annotations];
|
|
2258
2926
|
diagnostics.push(...typeAliasDoc.diagnostics);
|
|
2259
2927
|
const visiting = /* @__PURE__ */ new Set();
|
|
2260
|
-
for (const member of
|
|
2928
|
+
for (const member of typeLiteral.members) {
|
|
2261
2929
|
if (ts3.isPropertySignature(member)) {
|
|
2262
2930
|
const fieldNode = analyzeInterfacePropertyToIR(
|
|
2263
2931
|
member,
|
|
@@ -2267,6 +2935,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
2267
2935
|
visiting,
|
|
2268
2936
|
diagnostics,
|
|
2269
2937
|
aliasType,
|
|
2938
|
+
normalizedMetadataPolicy,
|
|
2270
2939
|
extensionRegistry
|
|
2271
2940
|
);
|
|
2272
2941
|
if (fieldNode) {
|
|
@@ -2274,12 +2943,28 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
2274
2943
|
}
|
|
2275
2944
|
}
|
|
2276
2945
|
}
|
|
2946
|
+
const specializedFields = applyDeclarationDiscriminatorToFields(
|
|
2947
|
+
fields,
|
|
2948
|
+
typeAlias,
|
|
2949
|
+
aliasType,
|
|
2950
|
+
checker,
|
|
2951
|
+
file,
|
|
2952
|
+
diagnostics,
|
|
2953
|
+
normalizedMetadataPolicy
|
|
2954
|
+
);
|
|
2955
|
+
const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, typeAlias, {
|
|
2956
|
+
checker,
|
|
2957
|
+
declaration: typeAlias,
|
|
2958
|
+
subjectType: aliasType,
|
|
2959
|
+
hostType: aliasType
|
|
2960
|
+
});
|
|
2277
2961
|
return {
|
|
2278
2962
|
ok: true,
|
|
2279
2963
|
analysis: {
|
|
2280
2964
|
name,
|
|
2281
|
-
|
|
2282
|
-
|
|
2965
|
+
...metadata !== void 0 && { metadata },
|
|
2966
|
+
fields: specializedFields,
|
|
2967
|
+
fieldLayouts: specializedFields.map(() => ({})),
|
|
2283
2968
|
typeRegistry,
|
|
2284
2969
|
...annotations.length > 0 && { annotations },
|
|
2285
2970
|
...diagnostics.length > 0 && { diagnostics },
|
|
@@ -2288,7 +2973,444 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
2288
2973
|
}
|
|
2289
2974
|
};
|
|
2290
2975
|
}
|
|
2291
|
-
function
|
|
2976
|
+
function makeAnalysisDiagnostic(code, message, primaryLocation, relatedLocations = []) {
|
|
2977
|
+
return {
|
|
2978
|
+
code,
|
|
2979
|
+
message,
|
|
2980
|
+
severity: "error",
|
|
2981
|
+
primaryLocation,
|
|
2982
|
+
relatedLocations
|
|
2983
|
+
};
|
|
2984
|
+
}
|
|
2985
|
+
function getLeadingParsedTags(node) {
|
|
2986
|
+
const sourceFile = node.getSourceFile();
|
|
2987
|
+
const sourceText = sourceFile.getFullText();
|
|
2988
|
+
const commentRanges = ts3.getLeadingCommentRanges(sourceText, node.getFullStart());
|
|
2989
|
+
if (commentRanges === void 0) {
|
|
2990
|
+
return [];
|
|
2991
|
+
}
|
|
2992
|
+
const parsedTags = [];
|
|
2993
|
+
for (const range of commentRanges) {
|
|
2994
|
+
if (range.kind !== ts3.SyntaxKind.MultiLineCommentTrivia) {
|
|
2995
|
+
continue;
|
|
2996
|
+
}
|
|
2997
|
+
const commentText = sourceText.slice(range.pos, range.end);
|
|
2998
|
+
if (!commentText.startsWith("/**")) {
|
|
2999
|
+
continue;
|
|
3000
|
+
}
|
|
3001
|
+
parsedTags.push(...(0, import_internal2.parseCommentBlock)(commentText, { offset: range.pos }).tags);
|
|
3002
|
+
}
|
|
3003
|
+
return parsedTags;
|
|
3004
|
+
}
|
|
3005
|
+
function resolveDiscriminatorProperty(node, checker, fieldName) {
|
|
3006
|
+
const subjectType = checker.getTypeAtLocation(node);
|
|
3007
|
+
const propertySymbol = subjectType.getProperty(fieldName);
|
|
3008
|
+
if (propertySymbol === void 0) {
|
|
3009
|
+
return null;
|
|
3010
|
+
}
|
|
3011
|
+
const declaration = propertySymbol.valueDeclaration ?? propertySymbol.declarations?.find(
|
|
3012
|
+
(candidate) => ts3.isPropertyDeclaration(candidate) || ts3.isPropertySignature(candidate)
|
|
3013
|
+
) ?? propertySymbol.declarations?.[0];
|
|
3014
|
+
return {
|
|
3015
|
+
declaration,
|
|
3016
|
+
type: checker.getTypeOfSymbolAtLocation(propertySymbol, declaration ?? node),
|
|
3017
|
+
optional: !!(propertySymbol.flags & ts3.SymbolFlags.Optional) || declaration !== void 0 && "questionToken" in declaration && declaration.questionToken !== void 0
|
|
3018
|
+
};
|
|
3019
|
+
}
|
|
3020
|
+
function isLocalTypeParameterName(node, typeParameterName) {
|
|
3021
|
+
return node.typeParameters?.some((typeParameter) => typeParameter.name.text === typeParameterName) ?? false;
|
|
3022
|
+
}
|
|
3023
|
+
function isNullishSemanticType(type) {
|
|
3024
|
+
if (type.flags & (ts3.TypeFlags.Null | ts3.TypeFlags.Undefined | ts3.TypeFlags.Void | ts3.TypeFlags.Unknown | ts3.TypeFlags.Any)) {
|
|
3025
|
+
return true;
|
|
3026
|
+
}
|
|
3027
|
+
return type.isUnion() && type.types.some((member) => isNullishSemanticType(member));
|
|
3028
|
+
}
|
|
3029
|
+
function isStringLikeSemanticType(type) {
|
|
3030
|
+
if (type.flags & ts3.TypeFlags.StringLike) {
|
|
3031
|
+
return true;
|
|
3032
|
+
}
|
|
3033
|
+
if (type.isUnion()) {
|
|
3034
|
+
return type.types.length > 0 && type.types.every((member) => isStringLikeSemanticType(member));
|
|
3035
|
+
}
|
|
3036
|
+
return false;
|
|
3037
|
+
}
|
|
3038
|
+
function extractDiscriminatorDirective(node, file, diagnostics) {
|
|
3039
|
+
const discriminatorTags = getLeadingParsedTags(node).filter(
|
|
3040
|
+
(tag) => tag.normalizedTagName === "discriminator"
|
|
3041
|
+
);
|
|
3042
|
+
if (discriminatorTags.length === 0) {
|
|
3043
|
+
return null;
|
|
3044
|
+
}
|
|
3045
|
+
const [firstTag, ...duplicateTags] = discriminatorTags;
|
|
3046
|
+
for (const _duplicateTag of duplicateTags) {
|
|
3047
|
+
diagnostics.push(
|
|
3048
|
+
makeAnalysisDiagnostic(
|
|
3049
|
+
"DUPLICATE_TAG",
|
|
3050
|
+
'Duplicate "@discriminator" tag. Only one discriminator declaration is allowed per declaration.',
|
|
3051
|
+
provenanceForNode(node, file)
|
|
3052
|
+
)
|
|
3053
|
+
);
|
|
3054
|
+
}
|
|
3055
|
+
if (firstTag === void 0) {
|
|
3056
|
+
return null;
|
|
3057
|
+
}
|
|
3058
|
+
const firstTarget = firstTag.target;
|
|
3059
|
+
if (firstTarget?.path === null || firstTarget?.valid !== true) {
|
|
3060
|
+
diagnostics.push(
|
|
3061
|
+
makeAnalysisDiagnostic(
|
|
3062
|
+
"INVALID_TAG_ARGUMENT",
|
|
3063
|
+
'Tag "@discriminator" requires a direct path target like ":kind".',
|
|
3064
|
+
provenanceForNode(node, file)
|
|
3065
|
+
)
|
|
3066
|
+
);
|
|
3067
|
+
return null;
|
|
3068
|
+
}
|
|
3069
|
+
if (firstTarget.path.segments.length !== 1) {
|
|
3070
|
+
diagnostics.push(
|
|
3071
|
+
makeAnalysisDiagnostic(
|
|
3072
|
+
"INVALID_TAG_ARGUMENT",
|
|
3073
|
+
'Tag "@discriminator" only supports direct property targets in v1; nested paths are out of scope.',
|
|
3074
|
+
provenanceForNode(node, file)
|
|
3075
|
+
)
|
|
3076
|
+
);
|
|
3077
|
+
return null;
|
|
3078
|
+
}
|
|
3079
|
+
const typeParameterName = firstTag.argumentText.trim();
|
|
3080
|
+
if (!/^[A-Za-z_$][\w$]*$/u.test(typeParameterName)) {
|
|
3081
|
+
diagnostics.push(
|
|
3082
|
+
makeAnalysisDiagnostic(
|
|
3083
|
+
"INVALID_TAG_ARGUMENT",
|
|
3084
|
+
'Tag "@discriminator" requires a local type parameter name as its source operand.',
|
|
3085
|
+
provenanceForNode(node, file)
|
|
3086
|
+
)
|
|
3087
|
+
);
|
|
3088
|
+
return null;
|
|
3089
|
+
}
|
|
3090
|
+
return {
|
|
3091
|
+
fieldName: firstTarget.path.segments[0] ?? firstTarget.rawText,
|
|
3092
|
+
typeParameterName,
|
|
3093
|
+
provenance: provenanceForNode(node, file)
|
|
3094
|
+
};
|
|
3095
|
+
}
|
|
3096
|
+
function validateDiscriminatorDirective(node, checker, file, diagnostics) {
|
|
3097
|
+
const directive = extractDiscriminatorDirective(node, file, diagnostics);
|
|
3098
|
+
if (directive === null) {
|
|
3099
|
+
return null;
|
|
3100
|
+
}
|
|
3101
|
+
if (!isLocalTypeParameterName(node, directive.typeParameterName)) {
|
|
3102
|
+
diagnostics.push(
|
|
3103
|
+
makeAnalysisDiagnostic(
|
|
3104
|
+
"INVALID_TAG_ARGUMENT",
|
|
3105
|
+
`Tag "@discriminator" references "${directive.typeParameterName}", but the source operand must be a type parameter declared on the same declaration.`,
|
|
3106
|
+
directive.provenance
|
|
3107
|
+
)
|
|
3108
|
+
);
|
|
3109
|
+
return null;
|
|
3110
|
+
}
|
|
3111
|
+
const property = resolveDiscriminatorProperty(node, checker, directive.fieldName);
|
|
3112
|
+
if (property === null) {
|
|
3113
|
+
diagnostics.push(
|
|
3114
|
+
makeAnalysisDiagnostic(
|
|
3115
|
+
"UNKNOWN_PATH_TARGET",
|
|
3116
|
+
`Tag "@discriminator" targets "${directive.fieldName}", but no direct property with that name exists on this declaration.`,
|
|
3117
|
+
directive.provenance
|
|
3118
|
+
)
|
|
3119
|
+
);
|
|
3120
|
+
return null;
|
|
3121
|
+
}
|
|
3122
|
+
if (property.optional) {
|
|
3123
|
+
diagnostics.push(
|
|
3124
|
+
makeAnalysisDiagnostic(
|
|
3125
|
+
"TYPE_MISMATCH",
|
|
3126
|
+
`Discriminator field "${directive.fieldName}" must be required; optional discriminator fields are not supported.`,
|
|
3127
|
+
directive.provenance,
|
|
3128
|
+
property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
|
|
3129
|
+
)
|
|
3130
|
+
);
|
|
3131
|
+
return null;
|
|
3132
|
+
}
|
|
3133
|
+
if (isNullishSemanticType(property.type)) {
|
|
3134
|
+
diagnostics.push(
|
|
3135
|
+
makeAnalysisDiagnostic(
|
|
3136
|
+
"TYPE_MISMATCH",
|
|
3137
|
+
`Discriminator field "${directive.fieldName}" must not be nullable.`,
|
|
3138
|
+
directive.provenance,
|
|
3139
|
+
property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
|
|
3140
|
+
)
|
|
3141
|
+
);
|
|
3142
|
+
return null;
|
|
3143
|
+
}
|
|
3144
|
+
if (!isStringLikeSemanticType(property.type)) {
|
|
3145
|
+
diagnostics.push(
|
|
3146
|
+
makeAnalysisDiagnostic(
|
|
3147
|
+
"TYPE_MISMATCH",
|
|
3148
|
+
`Discriminator field "${directive.fieldName}" must be string-like.`,
|
|
3149
|
+
directive.provenance,
|
|
3150
|
+
property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
|
|
3151
|
+
)
|
|
3152
|
+
);
|
|
3153
|
+
return null;
|
|
3154
|
+
}
|
|
3155
|
+
return directive;
|
|
3156
|
+
}
|
|
3157
|
+
function getConcreteTypeArgumentForDiscriminator(node, subjectType, checker, typeParameterName) {
|
|
3158
|
+
const typeParameterIndex = node.typeParameters?.findIndex(
|
|
3159
|
+
(typeParameter) => typeParameter.name.text === typeParameterName
|
|
3160
|
+
) ?? -1;
|
|
3161
|
+
if (typeParameterIndex < 0) {
|
|
3162
|
+
return null;
|
|
3163
|
+
}
|
|
3164
|
+
const referenceTypeArguments = (isTypeReference(subjectType) ? subjectType.typeArguments : void 0) ?? subjectType.aliasTypeArguments;
|
|
3165
|
+
if (referenceTypeArguments?.[typeParameterIndex] !== void 0) {
|
|
3166
|
+
return referenceTypeArguments[typeParameterIndex] ?? null;
|
|
3167
|
+
}
|
|
3168
|
+
const localTypeParameter = node.typeParameters?.[typeParameterIndex];
|
|
3169
|
+
return localTypeParameter === void 0 ? null : checker.getTypeAtLocation(localTypeParameter);
|
|
3170
|
+
}
|
|
3171
|
+
function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker, provenance, diagnostics) {
|
|
3172
|
+
const propertySymbol = boundType.getProperty(fieldName);
|
|
3173
|
+
if (propertySymbol === void 0) {
|
|
3174
|
+
return void 0;
|
|
3175
|
+
}
|
|
3176
|
+
const declaration = propertySymbol.valueDeclaration ?? propertySymbol.declarations?.[0];
|
|
3177
|
+
const anchorNode = declaration ?? boundType.symbol.declarations?.[0] ?? null;
|
|
3178
|
+
const resolvedAnchorNode = anchorNode ?? resolveNamedDiscriminatorDeclaration(boundType, checker);
|
|
3179
|
+
if (resolvedAnchorNode === null) {
|
|
3180
|
+
return void 0;
|
|
3181
|
+
}
|
|
3182
|
+
const propertyType = checker.getTypeOfSymbolAtLocation(
|
|
3183
|
+
propertySymbol,
|
|
3184
|
+
resolvedAnchorNode
|
|
3185
|
+
);
|
|
3186
|
+
if (propertyType.isStringLiteral()) {
|
|
3187
|
+
return propertyType.value;
|
|
3188
|
+
}
|
|
3189
|
+
if (propertyType.isUnion()) {
|
|
3190
|
+
const nonNullMembers = propertyType.types.filter(
|
|
3191
|
+
(member) => !(member.flags & (ts3.TypeFlags.Null | ts3.TypeFlags.Undefined))
|
|
3192
|
+
);
|
|
3193
|
+
if (nonNullMembers.length > 0 && nonNullMembers.every((member) => member.isStringLiteral())) {
|
|
3194
|
+
diagnostics.push(
|
|
3195
|
+
makeAnalysisDiagnostic(
|
|
3196
|
+
"INVALID_TAG_ARGUMENT",
|
|
3197
|
+
"Discriminator resolution for union-valued identity properties is out of scope for v1.",
|
|
3198
|
+
provenance
|
|
3199
|
+
)
|
|
3200
|
+
);
|
|
3201
|
+
return null;
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
return void 0;
|
|
3205
|
+
}
|
|
3206
|
+
function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
|
|
3207
|
+
const declaration = resolveNamedDiscriminatorDeclaration(boundType, checker);
|
|
3208
|
+
if (declaration === null) {
|
|
3209
|
+
return void 0;
|
|
3210
|
+
}
|
|
3211
|
+
const metadata = resolveNodeMetadata(
|
|
3212
|
+
metadataPolicy,
|
|
3213
|
+
"type",
|
|
3214
|
+
getDiscriminatorLogicalName(boundType, declaration, checker),
|
|
3215
|
+
declaration,
|
|
3216
|
+
{
|
|
3217
|
+
checker,
|
|
3218
|
+
declaration,
|
|
3219
|
+
subjectType: boundType
|
|
3220
|
+
}
|
|
3221
|
+
);
|
|
3222
|
+
return metadata?.apiName;
|
|
3223
|
+
}
|
|
3224
|
+
function resolveNamedDiscriminatorDeclaration(type, checker, seen = /* @__PURE__ */ new Set()) {
|
|
3225
|
+
if (seen.has(type)) {
|
|
3226
|
+
return null;
|
|
3227
|
+
}
|
|
3228
|
+
seen.add(type);
|
|
3229
|
+
const symbol = type.aliasSymbol ?? type.getSymbol();
|
|
3230
|
+
if (symbol !== void 0) {
|
|
3231
|
+
const aliased = symbol.flags & ts3.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : void 0;
|
|
3232
|
+
const targetSymbol = aliased ?? symbol;
|
|
3233
|
+
const declaration = targetSymbol.declarations?.find(
|
|
3234
|
+
(candidate) => ts3.isClassDeclaration(candidate) || ts3.isInterfaceDeclaration(candidate) || ts3.isTypeAliasDeclaration(candidate) || ts3.isEnumDeclaration(candidate)
|
|
3235
|
+
);
|
|
3236
|
+
if (declaration !== void 0) {
|
|
3237
|
+
if (ts3.isTypeAliasDeclaration(declaration) && ts3.isTypeReferenceNode(declaration.type) && checker.getTypeFromTypeNode(declaration.type) !== type) {
|
|
3238
|
+
return resolveNamedDiscriminatorDeclaration(
|
|
3239
|
+
checker.getTypeFromTypeNode(declaration.type),
|
|
3240
|
+
checker,
|
|
3241
|
+
seen
|
|
3242
|
+
);
|
|
3243
|
+
}
|
|
3244
|
+
return declaration;
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
return null;
|
|
3248
|
+
}
|
|
3249
|
+
function resolveDiscriminatorValue(boundType, fieldName, checker, provenance, diagnostics, metadataPolicy) {
|
|
3250
|
+
if (boundType === null) {
|
|
3251
|
+
diagnostics.push(
|
|
3252
|
+
makeAnalysisDiagnostic(
|
|
3253
|
+
"INVALID_TAG_ARGUMENT",
|
|
3254
|
+
"Discriminator resolution failed because no concrete type argument is available for the referenced type parameter.",
|
|
3255
|
+
provenance
|
|
3256
|
+
)
|
|
3257
|
+
);
|
|
3258
|
+
return null;
|
|
3259
|
+
}
|
|
3260
|
+
if (boundType.isStringLiteral()) {
|
|
3261
|
+
return boundType.value;
|
|
3262
|
+
}
|
|
3263
|
+
if (boundType.isUnion()) {
|
|
3264
|
+
const nonNullMembers = boundType.types.filter(
|
|
3265
|
+
(member) => !(member.flags & (ts3.TypeFlags.Null | ts3.TypeFlags.Undefined))
|
|
3266
|
+
);
|
|
3267
|
+
if (nonNullMembers.every((member) => member.isStringLiteral())) {
|
|
3268
|
+
diagnostics.push(
|
|
3269
|
+
makeAnalysisDiagnostic(
|
|
3270
|
+
"INVALID_TAG_ARGUMENT",
|
|
3271
|
+
"Discriminator resolution for unions of string literals is out of scope for v1.",
|
|
3272
|
+
provenance
|
|
3273
|
+
)
|
|
3274
|
+
);
|
|
3275
|
+
return null;
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
const literalIdentityValue = resolveLiteralDiscriminatorPropertyValue(
|
|
3279
|
+
boundType,
|
|
3280
|
+
fieldName,
|
|
3281
|
+
checker,
|
|
3282
|
+
provenance,
|
|
3283
|
+
diagnostics
|
|
3284
|
+
);
|
|
3285
|
+
if (literalIdentityValue !== void 0) {
|
|
3286
|
+
return literalIdentityValue;
|
|
3287
|
+
}
|
|
3288
|
+
const apiName = resolveDiscriminatorApiName(boundType, checker, metadataPolicy);
|
|
3289
|
+
if (apiName?.source === "explicit") {
|
|
3290
|
+
return apiName.value;
|
|
3291
|
+
}
|
|
3292
|
+
if (apiName?.source === "inferred") {
|
|
3293
|
+
return apiName.value;
|
|
3294
|
+
}
|
|
3295
|
+
diagnostics.push(
|
|
3296
|
+
makeAnalysisDiagnostic(
|
|
3297
|
+
"INVALID_TAG_ARGUMENT",
|
|
3298
|
+
"Discriminator resolution could not derive a JSON-facing discriminator value from the referenced type argument.",
|
|
3299
|
+
provenance
|
|
3300
|
+
)
|
|
3301
|
+
);
|
|
3302
|
+
return null;
|
|
3303
|
+
}
|
|
3304
|
+
function getDeclarationName(node) {
|
|
3305
|
+
if (ts3.isClassDeclaration(node) || ts3.isInterfaceDeclaration(node) || ts3.isTypeAliasDeclaration(node) || ts3.isEnumDeclaration(node)) {
|
|
3306
|
+
return node.name?.text ?? "anonymous";
|
|
3307
|
+
}
|
|
3308
|
+
return "anonymous";
|
|
3309
|
+
}
|
|
3310
|
+
function getResolvedTypeArguments(type) {
|
|
3311
|
+
return (isTypeReference(type) ? type.typeArguments : void 0) ?? type.aliasTypeArguments ?? [];
|
|
3312
|
+
}
|
|
3313
|
+
function getDiscriminatorLogicalName(type, declaration, checker) {
|
|
3314
|
+
const baseName = getDeclarationName(declaration);
|
|
3315
|
+
const typeArguments = getResolvedTypeArguments(type);
|
|
3316
|
+
return typeArguments.length === 0 ? baseName : buildInstantiatedReferenceName(baseName, typeArguments, checker);
|
|
3317
|
+
}
|
|
3318
|
+
function applyDeclarationDiscriminatorToFields(fields, node, subjectType, checker, file, diagnostics, metadataPolicy) {
|
|
3319
|
+
const directive = validateDiscriminatorDirective(node, checker, file, diagnostics);
|
|
3320
|
+
if (directive === null) {
|
|
3321
|
+
return [...fields];
|
|
3322
|
+
}
|
|
3323
|
+
const discriminatorValue = resolveDiscriminatorValue(
|
|
3324
|
+
getConcreteTypeArgumentForDiscriminator(
|
|
3325
|
+
node,
|
|
3326
|
+
subjectType,
|
|
3327
|
+
checker,
|
|
3328
|
+
directive.typeParameterName
|
|
3329
|
+
),
|
|
3330
|
+
directive.fieldName,
|
|
3331
|
+
checker,
|
|
3332
|
+
directive.provenance,
|
|
3333
|
+
diagnostics,
|
|
3334
|
+
metadataPolicy
|
|
3335
|
+
);
|
|
3336
|
+
if (discriminatorValue === null) {
|
|
3337
|
+
return [...fields];
|
|
3338
|
+
}
|
|
3339
|
+
return fields.map(
|
|
3340
|
+
(field) => field.name === directive.fieldName ? {
|
|
3341
|
+
...field,
|
|
3342
|
+
type: {
|
|
3343
|
+
kind: "enum",
|
|
3344
|
+
members: [{ value: discriminatorValue }]
|
|
3345
|
+
}
|
|
3346
|
+
} : field
|
|
3347
|
+
);
|
|
3348
|
+
}
|
|
3349
|
+
function buildInstantiatedReferenceName(baseName, typeArguments, checker) {
|
|
3350
|
+
const renderedArguments = typeArguments.map(
|
|
3351
|
+
(typeArgument) => checker.typeToString(typeArgument).replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "")
|
|
3352
|
+
).filter((value) => value !== "");
|
|
3353
|
+
return renderedArguments.length === 0 ? baseName : `${baseName}__${renderedArguments.join("__")}`;
|
|
3354
|
+
}
|
|
3355
|
+
function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy, extensionRegistry, diagnostics) {
|
|
3356
|
+
const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
|
|
3357
|
+
if (typeNode === void 0) {
|
|
3358
|
+
return [];
|
|
3359
|
+
}
|
|
3360
|
+
const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
|
|
3361
|
+
if (!ts3.isTypeReferenceNode(resolvedTypeNode) || resolvedTypeNode.typeArguments === void 0) {
|
|
3362
|
+
return [];
|
|
3363
|
+
}
|
|
3364
|
+
return resolvedTypeNode.typeArguments.map((argumentNode) => {
|
|
3365
|
+
const argumentType = checker.getTypeFromTypeNode(argumentNode);
|
|
3366
|
+
return {
|
|
3367
|
+
tsType: argumentType,
|
|
3368
|
+
typeNode: resolveTypeNode(
|
|
3369
|
+
argumentType,
|
|
3370
|
+
checker,
|
|
3371
|
+
file,
|
|
3372
|
+
typeRegistry,
|
|
3373
|
+
visiting,
|
|
3374
|
+
argumentNode,
|
|
3375
|
+
metadataPolicy,
|
|
3376
|
+
extensionRegistry,
|
|
3377
|
+
diagnostics
|
|
3378
|
+
)
|
|
3379
|
+
};
|
|
3380
|
+
});
|
|
3381
|
+
}
|
|
3382
|
+
function applyDiscriminatorToObjectProperties(properties, node, subjectType, checker, file, diagnostics, metadataPolicy) {
|
|
3383
|
+
const directive = validateDiscriminatorDirective(node, checker, file, diagnostics);
|
|
3384
|
+
if (directive === null) {
|
|
3385
|
+
return properties;
|
|
3386
|
+
}
|
|
3387
|
+
const discriminatorValue = resolveDiscriminatorValue(
|
|
3388
|
+
getConcreteTypeArgumentForDiscriminator(
|
|
3389
|
+
node,
|
|
3390
|
+
subjectType,
|
|
3391
|
+
checker,
|
|
3392
|
+
directive.typeParameterName
|
|
3393
|
+
),
|
|
3394
|
+
directive.fieldName,
|
|
3395
|
+
checker,
|
|
3396
|
+
directive.provenance,
|
|
3397
|
+
diagnostics,
|
|
3398
|
+
metadataPolicy
|
|
3399
|
+
);
|
|
3400
|
+
if (discriminatorValue === null) {
|
|
3401
|
+
return properties;
|
|
3402
|
+
}
|
|
3403
|
+
return properties.map(
|
|
3404
|
+
(property) => property.name === directive.fieldName ? {
|
|
3405
|
+
...property,
|
|
3406
|
+
type: {
|
|
3407
|
+
kind: "enum",
|
|
3408
|
+
members: [{ value: discriminatorValue }]
|
|
3409
|
+
}
|
|
3410
|
+
} : property
|
|
3411
|
+
);
|
|
3412
|
+
}
|
|
3413
|
+
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, metadataPolicy, extensionRegistry) {
|
|
2292
3414
|
if (!ts3.isIdentifier(prop.name)) {
|
|
2293
3415
|
return null;
|
|
2294
3416
|
}
|
|
@@ -2303,6 +3425,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
|
|
|
2303
3425
|
typeRegistry,
|
|
2304
3426
|
visiting,
|
|
2305
3427
|
prop,
|
|
3428
|
+
metadataPolicy,
|
|
2306
3429
|
extensionRegistry,
|
|
2307
3430
|
diagnostics
|
|
2308
3431
|
);
|
|
@@ -2326,9 +3449,16 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
|
|
|
2326
3449
|
annotations.push(defaultAnnotation);
|
|
2327
3450
|
}
|
|
2328
3451
|
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
3452
|
+
const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
|
|
3453
|
+
checker,
|
|
3454
|
+
declaration: prop,
|
|
3455
|
+
subjectType: tsType,
|
|
3456
|
+
hostType
|
|
3457
|
+
});
|
|
2329
3458
|
return {
|
|
2330
3459
|
kind: "field",
|
|
2331
3460
|
name,
|
|
3461
|
+
...metadata !== void 0 && { metadata },
|
|
2332
3462
|
type,
|
|
2333
3463
|
required: !optional,
|
|
2334
3464
|
constraints,
|
|
@@ -2336,7 +3466,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
|
|
|
2336
3466
|
provenance
|
|
2337
3467
|
};
|
|
2338
3468
|
}
|
|
2339
|
-
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, extensionRegistry) {
|
|
3469
|
+
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, metadataPolicy, extensionRegistry) {
|
|
2340
3470
|
if (!ts3.isIdentifier(prop.name)) {
|
|
2341
3471
|
return null;
|
|
2342
3472
|
}
|
|
@@ -2351,6 +3481,7 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
2351
3481
|
typeRegistry,
|
|
2352
3482
|
visiting,
|
|
2353
3483
|
prop,
|
|
3484
|
+
metadataPolicy,
|
|
2354
3485
|
extensionRegistry,
|
|
2355
3486
|
diagnostics
|
|
2356
3487
|
);
|
|
@@ -2370,9 +3501,16 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
2370
3501
|
let annotations = [];
|
|
2371
3502
|
annotations.push(...docResult.annotations);
|
|
2372
3503
|
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
3504
|
+
const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
|
|
3505
|
+
checker,
|
|
3506
|
+
declaration: prop,
|
|
3507
|
+
subjectType: tsType,
|
|
3508
|
+
hostType
|
|
3509
|
+
});
|
|
2373
3510
|
return {
|
|
2374
3511
|
kind: "field",
|
|
2375
3512
|
name,
|
|
3513
|
+
...metadata !== void 0 && { metadata },
|
|
2376
3514
|
type,
|
|
2377
3515
|
required: !optional,
|
|
2378
3516
|
constraints,
|
|
@@ -2497,7 +3635,7 @@ function getTypeNodeRegistrationName(typeNode) {
|
|
|
2497
3635
|
}
|
|
2498
3636
|
return null;
|
|
2499
3637
|
}
|
|
2500
|
-
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
3638
|
+
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2501
3639
|
const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
|
|
2502
3640
|
if (customType) {
|
|
2503
3641
|
return customType;
|
|
@@ -2509,6 +3647,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2509
3647
|
typeRegistry,
|
|
2510
3648
|
visiting,
|
|
2511
3649
|
sourceNode,
|
|
3650
|
+
metadataPolicy,
|
|
2512
3651
|
extensionRegistry,
|
|
2513
3652
|
diagnostics
|
|
2514
3653
|
);
|
|
@@ -2553,6 +3692,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2553
3692
|
typeRegistry,
|
|
2554
3693
|
visiting,
|
|
2555
3694
|
sourceNode,
|
|
3695
|
+
metadataPolicy,
|
|
2556
3696
|
extensionRegistry,
|
|
2557
3697
|
diagnostics
|
|
2558
3698
|
);
|
|
@@ -2565,6 +3705,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2565
3705
|
typeRegistry,
|
|
2566
3706
|
visiting,
|
|
2567
3707
|
sourceNode,
|
|
3708
|
+
metadataPolicy,
|
|
2568
3709
|
extensionRegistry,
|
|
2569
3710
|
diagnostics
|
|
2570
3711
|
);
|
|
@@ -2576,13 +3717,15 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2576
3717
|
file,
|
|
2577
3718
|
typeRegistry,
|
|
2578
3719
|
visiting,
|
|
3720
|
+
sourceNode,
|
|
3721
|
+
metadataPolicy,
|
|
2579
3722
|
extensionRegistry,
|
|
2580
3723
|
diagnostics
|
|
2581
3724
|
);
|
|
2582
3725
|
}
|
|
2583
3726
|
return { kind: "primitive", primitiveKind: "string" };
|
|
2584
3727
|
}
|
|
2585
|
-
function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
3728
|
+
function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2586
3729
|
if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
|
|
2587
3730
|
return null;
|
|
2588
3731
|
}
|
|
@@ -2602,14 +3745,21 @@ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiti
|
|
|
2602
3745
|
file,
|
|
2603
3746
|
makeParseOptions(extensionRegistry)
|
|
2604
3747
|
);
|
|
3748
|
+
const metadata = resolveNodeMetadata(metadataPolicy, "type", aliasName, aliasDecl, {
|
|
3749
|
+
checker,
|
|
3750
|
+
declaration: aliasDecl,
|
|
3751
|
+
subjectType: aliasType
|
|
3752
|
+
});
|
|
2605
3753
|
typeRegistry[aliasName] = {
|
|
2606
3754
|
name: aliasName,
|
|
3755
|
+
...metadata !== void 0 && { metadata },
|
|
2607
3756
|
type: resolveAliasedPrimitiveTarget(
|
|
2608
3757
|
aliasType,
|
|
2609
3758
|
checker,
|
|
2610
3759
|
file,
|
|
2611
3760
|
typeRegistry,
|
|
2612
3761
|
visiting,
|
|
3762
|
+
metadataPolicy,
|
|
2613
3763
|
extensionRegistry,
|
|
2614
3764
|
diagnostics
|
|
2615
3765
|
),
|
|
@@ -2638,7 +3788,7 @@ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
|
|
|
2638
3788
|
const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
|
|
2639
3789
|
return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
|
|
2640
3790
|
}
|
|
2641
|
-
function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, extensionRegistry, diagnostics) {
|
|
3791
|
+
function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2642
3792
|
const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
|
|
2643
3793
|
if (nestedAliasDecl !== void 0) {
|
|
2644
3794
|
return resolveAliasedPrimitiveTarget(
|
|
@@ -2647,6 +3797,7 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
|
|
|
2647
3797
|
file,
|
|
2648
3798
|
typeRegistry,
|
|
2649
3799
|
visiting,
|
|
3800
|
+
metadataPolicy,
|
|
2650
3801
|
extensionRegistry,
|
|
2651
3802
|
diagnostics
|
|
2652
3803
|
);
|
|
@@ -2658,11 +3809,12 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
|
|
|
2658
3809
|
typeRegistry,
|
|
2659
3810
|
visiting,
|
|
2660
3811
|
void 0,
|
|
3812
|
+
metadataPolicy,
|
|
2661
3813
|
extensionRegistry,
|
|
2662
3814
|
diagnostics
|
|
2663
3815
|
);
|
|
2664
3816
|
}
|
|
2665
|
-
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
3817
|
+
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2666
3818
|
const typeName = getNamedTypeName(type);
|
|
2667
3819
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
2668
3820
|
if (typeName && typeName in typeRegistry) {
|
|
@@ -2697,8 +3849,14 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2697
3849
|
return result;
|
|
2698
3850
|
}
|
|
2699
3851
|
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
3852
|
+
const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", typeName, namedDecl, {
|
|
3853
|
+
checker,
|
|
3854
|
+
declaration: namedDecl,
|
|
3855
|
+
subjectType: type
|
|
3856
|
+
}) : void 0;
|
|
2700
3857
|
typeRegistry[typeName] = {
|
|
2701
3858
|
name: typeName,
|
|
3859
|
+
...metadata !== void 0 && { metadata },
|
|
2702
3860
|
type: result,
|
|
2703
3861
|
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2704
3862
|
provenance: provenanceForDeclaration(namedDecl ?? sourceNode, file)
|
|
@@ -2752,6 +3910,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2752
3910
|
typeRegistry,
|
|
2753
3911
|
visiting,
|
|
2754
3912
|
nonNullMembers[0].sourceNode ?? sourceNode,
|
|
3913
|
+
metadataPolicy,
|
|
2755
3914
|
extensionRegistry,
|
|
2756
3915
|
diagnostics
|
|
2757
3916
|
);
|
|
@@ -2769,6 +3928,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2769
3928
|
typeRegistry,
|
|
2770
3929
|
visiting,
|
|
2771
3930
|
memberSourceNode ?? sourceNode,
|
|
3931
|
+
metadataPolicy,
|
|
2772
3932
|
extensionRegistry,
|
|
2773
3933
|
diagnostics
|
|
2774
3934
|
)
|
|
@@ -2778,7 +3938,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2778
3938
|
}
|
|
2779
3939
|
return registerNamed({ kind: "union", members });
|
|
2780
3940
|
}
|
|
2781
|
-
function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
3941
|
+
function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2782
3942
|
const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
|
|
2783
3943
|
const elementType = typeArgs?.[0];
|
|
2784
3944
|
const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
|
|
@@ -2789,12 +3949,13 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2789
3949
|
typeRegistry,
|
|
2790
3950
|
visiting,
|
|
2791
3951
|
elementSourceNode,
|
|
3952
|
+
metadataPolicy,
|
|
2792
3953
|
extensionRegistry,
|
|
2793
3954
|
diagnostics
|
|
2794
3955
|
) : { kind: "primitive", primitiveKind: "string" };
|
|
2795
3956
|
return { kind: "array", items };
|
|
2796
3957
|
}
|
|
2797
|
-
function tryResolveRecordType(type, checker, file, typeRegistry, visiting, extensionRegistry, diagnostics) {
|
|
3958
|
+
function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2798
3959
|
if (type.getProperties().length > 0) {
|
|
2799
3960
|
return null;
|
|
2800
3961
|
}
|
|
@@ -2809,6 +3970,7 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, exten
|
|
|
2809
3970
|
typeRegistry,
|
|
2810
3971
|
visiting,
|
|
2811
3972
|
void 0,
|
|
3973
|
+
metadataPolicy,
|
|
2812
3974
|
extensionRegistry,
|
|
2813
3975
|
diagnostics
|
|
2814
3976
|
);
|
|
@@ -2839,35 +4001,76 @@ function typeNodeContainsReference(type, targetName) {
|
|
|
2839
4001
|
}
|
|
2840
4002
|
}
|
|
2841
4003
|
}
|
|
2842
|
-
function
|
|
4004
|
+
function shouldEmitResolvedObjectProperty(property, declaration) {
|
|
4005
|
+
if (property.name.startsWith("__@")) {
|
|
4006
|
+
return false;
|
|
4007
|
+
}
|
|
4008
|
+
if (declaration !== void 0 && "name" in declaration && declaration.name !== void 0) {
|
|
4009
|
+
const name = declaration.name;
|
|
4010
|
+
if (ts3.isComputedPropertyName(name) || ts3.isPrivateIdentifier(name)) {
|
|
4011
|
+
return false;
|
|
4012
|
+
}
|
|
4013
|
+
if (!ts3.isIdentifier(name) && !ts3.isStringLiteral(name) && !ts3.isNumericLiteral(name)) {
|
|
4014
|
+
return false;
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
return true;
|
|
4018
|
+
}
|
|
4019
|
+
function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
4020
|
+
const collectedDiagnostics = diagnostics ?? [];
|
|
2843
4021
|
const typeName = getNamedTypeName(type);
|
|
2844
4022
|
const namedTypeName = typeName ?? void 0;
|
|
2845
4023
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
2846
|
-
const
|
|
4024
|
+
const referenceTypeArguments = extractReferenceTypeArguments(
|
|
4025
|
+
type,
|
|
4026
|
+
checker,
|
|
4027
|
+
file,
|
|
4028
|
+
typeRegistry,
|
|
4029
|
+
visiting,
|
|
4030
|
+
sourceNode,
|
|
4031
|
+
metadataPolicy,
|
|
4032
|
+
extensionRegistry,
|
|
4033
|
+
collectedDiagnostics
|
|
4034
|
+
);
|
|
4035
|
+
const instantiatedTypeName = namedTypeName !== void 0 && referenceTypeArguments.length > 0 ? buildInstantiatedReferenceName(
|
|
4036
|
+
namedTypeName,
|
|
4037
|
+
referenceTypeArguments.map((argument) => argument.tsType),
|
|
4038
|
+
checker
|
|
4039
|
+
) : void 0;
|
|
4040
|
+
const registryTypeName = instantiatedTypeName ?? namedTypeName;
|
|
4041
|
+
const shouldRegisterNamedType = registryTypeName !== void 0 && !(registryTypeName === "Record" && namedDecl?.getSourceFile().fileName !== file);
|
|
2847
4042
|
const clearNamedTypeRegistration = () => {
|
|
2848
|
-
if (
|
|
4043
|
+
if (registryTypeName === void 0 || !shouldRegisterNamedType) {
|
|
2849
4044
|
return;
|
|
2850
4045
|
}
|
|
2851
|
-
Reflect.deleteProperty(typeRegistry,
|
|
4046
|
+
Reflect.deleteProperty(typeRegistry, registryTypeName);
|
|
2852
4047
|
};
|
|
2853
4048
|
if (visiting.has(type)) {
|
|
2854
|
-
if (
|
|
2855
|
-
return {
|
|
4049
|
+
if (registryTypeName !== void 0 && shouldRegisterNamedType) {
|
|
4050
|
+
return {
|
|
4051
|
+
kind: "reference",
|
|
4052
|
+
name: registryTypeName,
|
|
4053
|
+
typeArguments: referenceTypeArguments.map((argument) => argument.typeNode)
|
|
4054
|
+
};
|
|
2856
4055
|
}
|
|
2857
4056
|
return { kind: "object", properties: [], additionalProperties: false };
|
|
2858
4057
|
}
|
|
2859
|
-
if (
|
|
2860
|
-
typeRegistry[
|
|
2861
|
-
name:
|
|
4058
|
+
if (registryTypeName !== void 0 && shouldRegisterNamedType && !typeRegistry[registryTypeName]) {
|
|
4059
|
+
typeRegistry[registryTypeName] = {
|
|
4060
|
+
name: registryTypeName,
|
|
2862
4061
|
type: RESOLVING_TYPE_PLACEHOLDER,
|
|
2863
4062
|
provenance: provenanceForDeclaration(namedDecl, file)
|
|
2864
4063
|
};
|
|
2865
4064
|
}
|
|
2866
4065
|
visiting.add(type);
|
|
2867
|
-
if (
|
|
2868
|
-
if (typeRegistry[
|
|
4066
|
+
if (registryTypeName !== void 0 && shouldRegisterNamedType && typeRegistry[registryTypeName]?.type !== void 0) {
|
|
4067
|
+
if (typeRegistry[registryTypeName].type !== RESOLVING_TYPE_PLACEHOLDER) {
|
|
2869
4068
|
visiting.delete(type);
|
|
2870
|
-
return {
|
|
4069
|
+
return {
|
|
4070
|
+
kind: "reference",
|
|
4071
|
+
name: registryTypeName,
|
|
4072
|
+
typeArguments: referenceTypeArguments.map((argument) => argument.typeNode)
|
|
4073
|
+
};
|
|
2871
4074
|
}
|
|
2872
4075
|
}
|
|
2873
4076
|
const recordNode = tryResolveRecordType(
|
|
@@ -2876,25 +4079,36 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensio
|
|
|
2876
4079
|
file,
|
|
2877
4080
|
typeRegistry,
|
|
2878
4081
|
visiting,
|
|
4082
|
+
metadataPolicy,
|
|
2879
4083
|
extensionRegistry,
|
|
2880
|
-
|
|
4084
|
+
collectedDiagnostics
|
|
2881
4085
|
);
|
|
2882
4086
|
if (recordNode) {
|
|
2883
4087
|
visiting.delete(type);
|
|
2884
|
-
if (
|
|
2885
|
-
const isRecursiveRecord = typeNodeContainsReference(recordNode.valueType,
|
|
4088
|
+
if (registryTypeName !== void 0 && shouldRegisterNamedType) {
|
|
4089
|
+
const isRecursiveRecord = typeNodeContainsReference(recordNode.valueType, registryTypeName);
|
|
2886
4090
|
if (!isRecursiveRecord) {
|
|
2887
4091
|
clearNamedTypeRegistration();
|
|
2888
4092
|
return recordNode;
|
|
2889
4093
|
}
|
|
2890
4094
|
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
2891
|
-
|
|
2892
|
-
|
|
4095
|
+
const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
|
|
4096
|
+
checker,
|
|
4097
|
+
declaration: namedDecl,
|
|
4098
|
+
subjectType: type
|
|
4099
|
+
}) : void 0;
|
|
4100
|
+
typeRegistry[registryTypeName] = {
|
|
4101
|
+
name: registryTypeName,
|
|
4102
|
+
...metadata !== void 0 && { metadata },
|
|
2893
4103
|
type: recordNode,
|
|
2894
4104
|
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2895
4105
|
provenance: provenanceForDeclaration(namedDecl, file)
|
|
2896
4106
|
};
|
|
2897
|
-
return {
|
|
4107
|
+
return {
|
|
4108
|
+
kind: "reference",
|
|
4109
|
+
name: registryTypeName,
|
|
4110
|
+
typeArguments: referenceTypeArguments.map((argument) => argument.typeNode)
|
|
4111
|
+
};
|
|
2898
4112
|
}
|
|
2899
4113
|
return recordNode;
|
|
2900
4114
|
}
|
|
@@ -2905,12 +4119,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensio
|
|
|
2905
4119
|
file,
|
|
2906
4120
|
typeRegistry,
|
|
2907
4121
|
visiting,
|
|
2908
|
-
|
|
4122
|
+
metadataPolicy,
|
|
4123
|
+
collectedDiagnostics,
|
|
2909
4124
|
extensionRegistry
|
|
2910
4125
|
);
|
|
2911
4126
|
for (const prop of type.getProperties()) {
|
|
2912
4127
|
const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
2913
4128
|
if (!declaration) continue;
|
|
4129
|
+
if (!shouldEmitResolvedObjectProperty(prop, declaration)) continue;
|
|
2914
4130
|
const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
|
|
2915
4131
|
const optional = !!(prop.flags & ts3.SymbolFlags.Optional);
|
|
2916
4132
|
const propTypeNode = resolveTypeNode(
|
|
@@ -2920,12 +4136,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensio
|
|
|
2920
4136
|
typeRegistry,
|
|
2921
4137
|
visiting,
|
|
2922
4138
|
declaration,
|
|
4139
|
+
metadataPolicy,
|
|
2923
4140
|
extensionRegistry,
|
|
2924
|
-
|
|
4141
|
+
collectedDiagnostics
|
|
2925
4142
|
);
|
|
2926
4143
|
const fieldNodeInfo = fieldInfoMap?.get(prop.name);
|
|
2927
4144
|
properties.push({
|
|
2928
4145
|
name: prop.name,
|
|
4146
|
+
...fieldNodeInfo?.metadata !== void 0 && { metadata: fieldNodeInfo.metadata },
|
|
2929
4147
|
type: propTypeNode,
|
|
2930
4148
|
optional,
|
|
2931
4149
|
constraints: fieldNodeInfo?.constraints ?? [],
|
|
@@ -2936,22 +4154,40 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensio
|
|
|
2936
4154
|
visiting.delete(type);
|
|
2937
4155
|
const objectNode = {
|
|
2938
4156
|
kind: "object",
|
|
2939
|
-
properties
|
|
4157
|
+
properties: namedDecl !== void 0 && (ts3.isClassDeclaration(namedDecl) || ts3.isInterfaceDeclaration(namedDecl) || ts3.isTypeAliasDeclaration(namedDecl)) ? applyDiscriminatorToObjectProperties(
|
|
4158
|
+
properties,
|
|
4159
|
+
namedDecl,
|
|
4160
|
+
type,
|
|
4161
|
+
checker,
|
|
4162
|
+
file,
|
|
4163
|
+
collectedDiagnostics,
|
|
4164
|
+
metadataPolicy
|
|
4165
|
+
) : properties,
|
|
2940
4166
|
additionalProperties: true
|
|
2941
4167
|
};
|
|
2942
|
-
if (
|
|
4168
|
+
if (registryTypeName !== void 0 && shouldRegisterNamedType) {
|
|
2943
4169
|
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
2944
|
-
|
|
2945
|
-
|
|
4170
|
+
const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
|
|
4171
|
+
checker,
|
|
4172
|
+
declaration: namedDecl,
|
|
4173
|
+
subjectType: type
|
|
4174
|
+
}) : void 0;
|
|
4175
|
+
typeRegistry[registryTypeName] = {
|
|
4176
|
+
name: registryTypeName,
|
|
4177
|
+
...metadata !== void 0 && { metadata },
|
|
2946
4178
|
type: objectNode,
|
|
2947
4179
|
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2948
4180
|
provenance: provenanceForDeclaration(namedDecl, file)
|
|
2949
4181
|
};
|
|
2950
|
-
return {
|
|
4182
|
+
return {
|
|
4183
|
+
kind: "reference",
|
|
4184
|
+
name: registryTypeName,
|
|
4185
|
+
typeArguments: referenceTypeArguments.map((argument) => argument.typeNode)
|
|
4186
|
+
};
|
|
2951
4187
|
}
|
|
2952
4188
|
return objectNode;
|
|
2953
4189
|
}
|
|
2954
|
-
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, diagnostics, extensionRegistry) {
|
|
4190
|
+
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, metadataPolicy, diagnostics, extensionRegistry) {
|
|
2955
4191
|
const symbols = [type.getSymbol(), type.aliasSymbol].filter(
|
|
2956
4192
|
(s) => s?.declarations != null && s.declarations.length > 0
|
|
2957
4193
|
);
|
|
@@ -2972,10 +4208,12 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2972
4208
|
visiting,
|
|
2973
4209
|
diagnostics,
|
|
2974
4210
|
hostType,
|
|
4211
|
+
metadataPolicy,
|
|
2975
4212
|
extensionRegistry
|
|
2976
4213
|
);
|
|
2977
4214
|
if (fieldNode) {
|
|
2978
4215
|
map.set(fieldNode.name, {
|
|
4216
|
+
...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
|
|
2979
4217
|
constraints: [...fieldNode.constraints],
|
|
2980
4218
|
annotations: [...fieldNode.annotations],
|
|
2981
4219
|
provenance: fieldNode.provenance
|
|
@@ -2993,6 +4231,7 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2993
4231
|
file,
|
|
2994
4232
|
typeRegistry,
|
|
2995
4233
|
visiting,
|
|
4234
|
+
metadataPolicy,
|
|
2996
4235
|
checker.getTypeAtLocation(interfaceDecl),
|
|
2997
4236
|
diagnostics,
|
|
2998
4237
|
extensionRegistry
|
|
@@ -3006,6 +4245,7 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
3006
4245
|
file,
|
|
3007
4246
|
typeRegistry,
|
|
3008
4247
|
visiting,
|
|
4248
|
+
metadataPolicy,
|
|
3009
4249
|
checker.getTypeAtLocation(typeAliasDecl),
|
|
3010
4250
|
diagnostics,
|
|
3011
4251
|
extensionRegistry
|
|
@@ -3057,7 +4297,7 @@ function isNullishTypeNode(typeNode) {
|
|
|
3057
4297
|
}
|
|
3058
4298
|
return ts3.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts3.SyntaxKind.NullKeyword || typeNode.literal.kind === ts3.SyntaxKind.UndefinedKeyword);
|
|
3059
4299
|
}
|
|
3060
|
-
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, hostType, diagnostics, extensionRegistry) {
|
|
4300
|
+
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, metadataPolicy, hostType, diagnostics, extensionRegistry) {
|
|
3061
4301
|
const map = /* @__PURE__ */ new Map();
|
|
3062
4302
|
for (const member of members) {
|
|
3063
4303
|
if (ts3.isPropertySignature(member)) {
|
|
@@ -3069,10 +4309,12 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, h
|
|
|
3069
4309
|
visiting,
|
|
3070
4310
|
diagnostics,
|
|
3071
4311
|
hostType,
|
|
4312
|
+
metadataPolicy,
|
|
3072
4313
|
extensionRegistry
|
|
3073
4314
|
);
|
|
3074
4315
|
if (fieldNode) {
|
|
3075
4316
|
map.set(fieldNode.name, {
|
|
4317
|
+
...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
|
|
3076
4318
|
constraints: [...fieldNode.constraints],
|
|
3077
4319
|
annotations: [...fieldNode.annotations],
|
|
3078
4320
|
provenance: fieldNode.provenance
|
|
@@ -3103,6 +4345,7 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
|
|
|
3103
4345
|
{},
|
|
3104
4346
|
/* @__PURE__ */ new Set(),
|
|
3105
4347
|
aliasDecl.type,
|
|
4348
|
+
void 0,
|
|
3106
4349
|
extensionRegistry
|
|
3107
4350
|
);
|
|
3108
4351
|
const constraints = extractJSDocConstraintNodes(
|
|
@@ -3288,19 +4531,37 @@ function findInterfaceByName(sourceFile, interfaceName) {
|
|
|
3288
4531
|
function findTypeAliasByName(sourceFile, aliasName) {
|
|
3289
4532
|
return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
|
|
3290
4533
|
}
|
|
3291
|
-
function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry) {
|
|
4534
|
+
function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy) {
|
|
3292
4535
|
const ctx = createProgramContext(filePath);
|
|
3293
|
-
return analyzeNamedTypeToIRFromProgramContext(
|
|
4536
|
+
return analyzeNamedTypeToIRFromProgramContext(
|
|
4537
|
+
ctx,
|
|
4538
|
+
filePath,
|
|
4539
|
+
typeName,
|
|
4540
|
+
extensionRegistry,
|
|
4541
|
+
metadataPolicy
|
|
4542
|
+
);
|
|
3294
4543
|
}
|
|
3295
|
-
function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry) {
|
|
4544
|
+
function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy) {
|
|
3296
4545
|
const analysisFilePath = path.resolve(filePath);
|
|
3297
4546
|
const classDecl = findClassByName(ctx.sourceFile, typeName);
|
|
3298
4547
|
if (classDecl !== null) {
|
|
3299
|
-
return analyzeClassToIR(
|
|
4548
|
+
return analyzeClassToIR(
|
|
4549
|
+
classDecl,
|
|
4550
|
+
ctx.checker,
|
|
4551
|
+
analysisFilePath,
|
|
4552
|
+
extensionRegistry,
|
|
4553
|
+
metadataPolicy
|
|
4554
|
+
);
|
|
3300
4555
|
}
|
|
3301
4556
|
const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
|
|
3302
4557
|
if (interfaceDecl !== null) {
|
|
3303
|
-
return analyzeInterfaceToIR(
|
|
4558
|
+
return analyzeInterfaceToIR(
|
|
4559
|
+
interfaceDecl,
|
|
4560
|
+
ctx.checker,
|
|
4561
|
+
analysisFilePath,
|
|
4562
|
+
extensionRegistry,
|
|
4563
|
+
metadataPolicy
|
|
4564
|
+
);
|
|
3304
4565
|
}
|
|
3305
4566
|
const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
|
|
3306
4567
|
if (typeAlias !== null) {
|
|
@@ -3308,7 +4569,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
|
|
|
3308
4569
|
typeAlias,
|
|
3309
4570
|
ctx.checker,
|
|
3310
4571
|
analysisFilePath,
|
|
3311
|
-
extensionRegistry
|
|
4572
|
+
extensionRegistry,
|
|
4573
|
+
metadataPolicy
|
|
3312
4574
|
);
|
|
3313
4575
|
if (result.ok) {
|
|
3314
4576
|
return result.analysis;
|
|
@@ -3321,9 +4583,9 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
|
|
|
3321
4583
|
}
|
|
3322
4584
|
|
|
3323
4585
|
// src/validate/constraint-validator.ts
|
|
3324
|
-
var
|
|
4586
|
+
var import_internal3 = require("@formspec/analysis/internal");
|
|
3325
4587
|
function validateFieldNode(ctx, field) {
|
|
3326
|
-
const analysis = (0,
|
|
4588
|
+
const analysis = (0, import_internal3.analyzeConstraintTargets)(
|
|
3327
4589
|
field.name,
|
|
3328
4590
|
field.type,
|
|
3329
4591
|
field.constraints,
|
|
@@ -3341,7 +4603,7 @@ function validateFieldNode(ctx, field) {
|
|
|
3341
4603
|
}
|
|
3342
4604
|
function validateObjectProperty(ctx, parentName, property) {
|
|
3343
4605
|
const qualifiedName = `${parentName}.${property.name}`;
|
|
3344
|
-
const analysis = (0,
|
|
4606
|
+
const analysis = (0, import_internal3.analyzeConstraintTargets)(
|
|
3345
4607
|
qualifiedName,
|
|
3346
4608
|
property.type,
|
|
3347
4609
|
property.constraints,
|
|
@@ -3401,7 +4663,11 @@ function generateClassSchemas(analysis, source, options) {
|
|
|
3401
4663
|
if (errorDiagnostics !== void 0 && errorDiagnostics.length > 0) {
|
|
3402
4664
|
throw new Error(formatValidationError(errorDiagnostics));
|
|
3403
4665
|
}
|
|
3404
|
-
const ir = canonicalizeTSDoc(
|
|
4666
|
+
const ir = canonicalizeTSDoc(
|
|
4667
|
+
analysis,
|
|
4668
|
+
source,
|
|
4669
|
+
options?.metadata !== void 0 ? { metadata: options.metadata } : void 0
|
|
4670
|
+
);
|
|
3405
4671
|
const validationResult = validateIR(ir, {
|
|
3406
4672
|
...options?.extensionRegistry !== void 0 && {
|
|
3407
4673
|
extensionRegistry: options.extensionRegistry
|
|
@@ -3438,13 +4704,15 @@ function generateSchemasFromClass(options) {
|
|
|
3438
4704
|
classDecl,
|
|
3439
4705
|
ctx.checker,
|
|
3440
4706
|
options.filePath,
|
|
3441
|
-
options.extensionRegistry
|
|
4707
|
+
options.extensionRegistry,
|
|
4708
|
+
options.metadata
|
|
3442
4709
|
);
|
|
3443
4710
|
return generateClassSchemas(
|
|
3444
4711
|
analysis,
|
|
3445
4712
|
{ file: options.filePath },
|
|
3446
4713
|
{
|
|
3447
4714
|
extensionRegistry: options.extensionRegistry,
|
|
4715
|
+
metadata: options.metadata,
|
|
3448
4716
|
vendorPrefix: options.vendorPrefix
|
|
3449
4717
|
}
|
|
3450
4718
|
);
|
|
@@ -3462,13 +4730,15 @@ function generateSchemasFromProgram(options) {
|
|
|
3462
4730
|
ctx,
|
|
3463
4731
|
options.filePath,
|
|
3464
4732
|
options.typeName,
|
|
3465
|
-
options.extensionRegistry
|
|
4733
|
+
options.extensionRegistry,
|
|
4734
|
+
options.metadata
|
|
3466
4735
|
);
|
|
3467
4736
|
return generateClassSchemas(
|
|
3468
4737
|
analysis,
|
|
3469
4738
|
{ file: options.filePath },
|
|
3470
4739
|
{
|
|
3471
4740
|
extensionRegistry: options.extensionRegistry,
|
|
4741
|
+
metadata: options.metadata,
|
|
3472
4742
|
vendorPrefix: options.vendorPrefix
|
|
3473
4743
|
}
|
|
3474
4744
|
);
|
|
@@ -3477,16 +4747,28 @@ function generateSchemasFromProgram(options) {
|
|
|
3477
4747
|
// src/generators/mixed-authoring.ts
|
|
3478
4748
|
function buildMixedAuthoringSchemas(options) {
|
|
3479
4749
|
const { filePath, typeName, overlays, ...schemaOptions } = options;
|
|
3480
|
-
const analysis = analyzeNamedTypeToIR(
|
|
3481
|
-
|
|
3482
|
-
|
|
4750
|
+
const analysis = analyzeNamedTypeToIR(
|
|
4751
|
+
filePath,
|
|
4752
|
+
typeName,
|
|
4753
|
+
schemaOptions.extensionRegistry,
|
|
4754
|
+
schemaOptions.metadata
|
|
4755
|
+
);
|
|
4756
|
+
const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays, schemaOptions.metadata);
|
|
4757
|
+
const ir = canonicalizeTSDoc(
|
|
4758
|
+
composedAnalysis,
|
|
4759
|
+
{ file: filePath },
|
|
4760
|
+
schemaOptions.metadata !== void 0 ? { metadata: schemaOptions.metadata } : void 0
|
|
4761
|
+
);
|
|
3483
4762
|
return {
|
|
3484
4763
|
jsonSchema: generateJsonSchemaFromIR(ir, schemaOptions),
|
|
3485
4764
|
uiSchema: generateUiSchemaFromIR(ir)
|
|
3486
4765
|
};
|
|
3487
4766
|
}
|
|
3488
|
-
function composeAnalysisWithOverlays(analysis, overlays) {
|
|
3489
|
-
const overlayIR = canonicalizeChainDSL(
|
|
4767
|
+
function composeAnalysisWithOverlays(analysis, overlays, metadata) {
|
|
4768
|
+
const overlayIR = canonicalizeChainDSL(
|
|
4769
|
+
overlays,
|
|
4770
|
+
metadata !== void 0 ? { metadata } : void 0
|
|
4771
|
+
);
|
|
3490
4772
|
const overlayFields = collectOverlayFields(overlayIR.elements);
|
|
3491
4773
|
if (overlayFields.length === 0) {
|
|
3492
4774
|
return analysis;
|
|
@@ -3542,8 +4824,10 @@ function collectOverlayFields(elements) {
|
|
|
3542
4824
|
}
|
|
3543
4825
|
function mergeFieldOverlay(baseField, overlayField, typeRegistry) {
|
|
3544
4826
|
assertSupportedOverlayField(baseField, overlayField);
|
|
4827
|
+
const metadata = mergeResolvedMetadata(baseField.metadata, overlayField.metadata);
|
|
3545
4828
|
return {
|
|
3546
4829
|
...baseField,
|
|
4830
|
+
...metadata !== void 0 && { metadata },
|
|
3547
4831
|
type: mergeFieldType(baseField, overlayField, typeRegistry),
|
|
3548
4832
|
annotations: mergeAnnotations(baseField.annotations, overlayField.annotations)
|
|
3549
4833
|
};
|
|
@@ -3654,12 +4938,12 @@ function annotationKey(annotation) {
|
|
|
3654
4938
|
function buildFormSchemas(form, options) {
|
|
3655
4939
|
return {
|
|
3656
4940
|
jsonSchema: generateJsonSchema(form, options),
|
|
3657
|
-
uiSchema: generateUiSchema(form)
|
|
4941
|
+
uiSchema: generateUiSchema(form, options)
|
|
3658
4942
|
};
|
|
3659
4943
|
}
|
|
3660
4944
|
function writeSchemas(form, options) {
|
|
3661
|
-
const { outDir, name = "schema", indent = 2, vendorPrefix } = options;
|
|
3662
|
-
const buildOptions = vendorPrefix === void 0 ? void 0 : { vendorPrefix };
|
|
4945
|
+
const { outDir, name = "schema", indent = 2, vendorPrefix, metadata } = options;
|
|
4946
|
+
const buildOptions = vendorPrefix === void 0 && metadata === void 0 ? void 0 : { vendorPrefix, metadata };
|
|
3663
4947
|
const { jsonSchema, uiSchema: uiSchema2 } = buildFormSchemas(form, buildOptions);
|
|
3664
4948
|
if (!fs.existsSync(outDir)) {
|
|
3665
4949
|
fs.mkdirSync(outDir, { recursive: true });
|