@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.js
CHANGED
|
@@ -1,5 +1,314 @@
|
|
|
1
1
|
// src/canonicalize/chain-dsl-canonicalizer.ts
|
|
2
|
-
import { IR_VERSION } from "@formspec/core/internals";
|
|
2
|
+
import { IR_VERSION, _getFormSpecMetadataPolicy } from "@formspec/core/internals";
|
|
3
|
+
|
|
4
|
+
// src/metadata/policy.ts
|
|
5
|
+
var NOOP_INFLECT = () => "";
|
|
6
|
+
function normalizePluralization(input) {
|
|
7
|
+
if (input?.mode === "infer-if-missing") {
|
|
8
|
+
return {
|
|
9
|
+
mode: "infer-if-missing",
|
|
10
|
+
infer: () => "",
|
|
11
|
+
inflect: input.inflect
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
if (input?.mode === "require-explicit") {
|
|
15
|
+
return {
|
|
16
|
+
mode: "require-explicit",
|
|
17
|
+
infer: () => "",
|
|
18
|
+
inflect: NOOP_INFLECT
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
mode: "disabled",
|
|
23
|
+
infer: () => "",
|
|
24
|
+
inflect: NOOP_INFLECT
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function normalizeScalarPolicy(input) {
|
|
28
|
+
if (input?.mode === "infer-if-missing") {
|
|
29
|
+
return {
|
|
30
|
+
mode: "infer-if-missing",
|
|
31
|
+
infer: input.infer,
|
|
32
|
+
pluralization: normalizePluralization(input.pluralization)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (input?.mode === "require-explicit") {
|
|
36
|
+
return {
|
|
37
|
+
mode: "require-explicit",
|
|
38
|
+
infer: () => "",
|
|
39
|
+
pluralization: normalizePluralization(input.pluralization)
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
mode: "disabled",
|
|
44
|
+
infer: () => "",
|
|
45
|
+
pluralization: normalizePluralization(input?.pluralization)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function normalizeDeclarationPolicy(input) {
|
|
49
|
+
return {
|
|
50
|
+
apiName: normalizeScalarPolicy(input?.apiName),
|
|
51
|
+
displayName: normalizeScalarPolicy(input?.displayName)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function normalizeMetadataPolicy(input) {
|
|
55
|
+
return {
|
|
56
|
+
type: normalizeDeclarationPolicy(input?.type),
|
|
57
|
+
field: normalizeDeclarationPolicy(input?.field),
|
|
58
|
+
method: normalizeDeclarationPolicy(input?.method)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function getDeclarationMetadataPolicy(policy, declarationKind) {
|
|
62
|
+
return policy[declarationKind];
|
|
63
|
+
}
|
|
64
|
+
function makeMetadataContext(surface, declarationKind, logicalName, buildContext) {
|
|
65
|
+
return {
|
|
66
|
+
surface,
|
|
67
|
+
declarationKind,
|
|
68
|
+
logicalName,
|
|
69
|
+
...buildContext !== void 0 && { buildContext }
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/metadata/resolve.ts
|
|
74
|
+
function toExplicitScalar(value) {
|
|
75
|
+
return value !== void 0 && value.trim() !== "" ? { value, source: "explicit" } : void 0;
|
|
76
|
+
}
|
|
77
|
+
function toExplicitResolvedMetadata(explicit) {
|
|
78
|
+
if (explicit === void 0) {
|
|
79
|
+
return void 0;
|
|
80
|
+
}
|
|
81
|
+
const apiName = toExplicitScalar(explicit.apiName);
|
|
82
|
+
const displayName = toExplicitScalar(explicit.displayName);
|
|
83
|
+
const apiNamePlural = toExplicitScalar(explicit.apiNamePlural);
|
|
84
|
+
const displayNamePlural = toExplicitScalar(explicit.displayNamePlural);
|
|
85
|
+
const metadata = {
|
|
86
|
+
...apiName !== void 0 && { apiName },
|
|
87
|
+
...displayName !== void 0 && { displayName },
|
|
88
|
+
...apiNamePlural !== void 0 && { apiNamePlural },
|
|
89
|
+
...displayNamePlural !== void 0 && { displayNamePlural }
|
|
90
|
+
};
|
|
91
|
+
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
92
|
+
}
|
|
93
|
+
function resolveScalar(current, policy, context, metadataLabel) {
|
|
94
|
+
if (current !== void 0) {
|
|
95
|
+
return current;
|
|
96
|
+
}
|
|
97
|
+
if (policy.mode === "require-explicit") {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Metadata policy requires explicit ${metadataLabel} for ${context.declarationKind} "${context.logicalName}" on the ${context.surface} surface.`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (policy.mode !== "infer-if-missing") {
|
|
103
|
+
return void 0;
|
|
104
|
+
}
|
|
105
|
+
const inferredValue = policy.infer(context);
|
|
106
|
+
return inferredValue.trim() !== "" ? { value: inferredValue, source: "inferred" } : void 0;
|
|
107
|
+
}
|
|
108
|
+
function resolvePlural(current, singular, policy, context, metadataLabel) {
|
|
109
|
+
if (current !== void 0) {
|
|
110
|
+
return current;
|
|
111
|
+
}
|
|
112
|
+
if (policy.mode === "require-explicit") {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Metadata policy requires explicit ${metadataLabel} for ${context.declarationKind} "${context.logicalName}" on the ${context.surface} surface.`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
if (singular === void 0 || policy.mode !== "infer-if-missing") {
|
|
118
|
+
return void 0;
|
|
119
|
+
}
|
|
120
|
+
const pluralValue = policy.inflect({ ...context, singular: singular.value });
|
|
121
|
+
return pluralValue.trim() !== "" ? { value: pluralValue, source: "inferred" } : void 0;
|
|
122
|
+
}
|
|
123
|
+
function resolveResolvedMetadata(current, policy, context) {
|
|
124
|
+
const apiName = resolveScalar(current?.apiName, policy.apiName, context, "apiName");
|
|
125
|
+
const displayName = resolveScalar(
|
|
126
|
+
current?.displayName,
|
|
127
|
+
policy.displayName,
|
|
128
|
+
context,
|
|
129
|
+
"displayName"
|
|
130
|
+
);
|
|
131
|
+
const apiNamePlural = resolvePlural(
|
|
132
|
+
current?.apiNamePlural,
|
|
133
|
+
apiName,
|
|
134
|
+
policy.apiName.pluralization,
|
|
135
|
+
context,
|
|
136
|
+
"apiNamePlural"
|
|
137
|
+
);
|
|
138
|
+
const displayNamePlural = resolvePlural(
|
|
139
|
+
current?.displayNamePlural,
|
|
140
|
+
displayName,
|
|
141
|
+
policy.displayName.pluralization,
|
|
142
|
+
context,
|
|
143
|
+
"displayNamePlural"
|
|
144
|
+
);
|
|
145
|
+
if (apiName === void 0 && displayName === void 0 && apiNamePlural === void 0 && displayNamePlural === void 0) {
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
...apiName !== void 0 && { apiName },
|
|
150
|
+
...displayName !== void 0 && { displayName },
|
|
151
|
+
...apiNamePlural !== void 0 && { apiNamePlural },
|
|
152
|
+
...displayNamePlural !== void 0 && { displayNamePlural }
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function pickResolvedMetadataValue(baseValue, overlayValue) {
|
|
156
|
+
if (overlayValue?.source === "explicit") {
|
|
157
|
+
return overlayValue;
|
|
158
|
+
}
|
|
159
|
+
if (baseValue?.source === "explicit") {
|
|
160
|
+
return baseValue;
|
|
161
|
+
}
|
|
162
|
+
return baseValue ?? overlayValue;
|
|
163
|
+
}
|
|
164
|
+
function resolveTypeNodeMetadata(type, options) {
|
|
165
|
+
switch (type.kind) {
|
|
166
|
+
case "array":
|
|
167
|
+
return {
|
|
168
|
+
...type,
|
|
169
|
+
items: resolveTypeNodeMetadata(type.items, options)
|
|
170
|
+
};
|
|
171
|
+
case "object":
|
|
172
|
+
return {
|
|
173
|
+
...type,
|
|
174
|
+
properties: type.properties.map((property) => resolveObjectPropertyMetadata(property, options))
|
|
175
|
+
};
|
|
176
|
+
case "record":
|
|
177
|
+
return {
|
|
178
|
+
...type,
|
|
179
|
+
valueType: resolveTypeNodeMetadata(type.valueType, options)
|
|
180
|
+
};
|
|
181
|
+
case "union":
|
|
182
|
+
return {
|
|
183
|
+
...type,
|
|
184
|
+
members: type.members.map((member) => resolveTypeNodeMetadata(member, options))
|
|
185
|
+
};
|
|
186
|
+
case "reference":
|
|
187
|
+
case "primitive":
|
|
188
|
+
case "enum":
|
|
189
|
+
case "dynamic":
|
|
190
|
+
case "custom":
|
|
191
|
+
return type;
|
|
192
|
+
default: {
|
|
193
|
+
const _exhaustive = type;
|
|
194
|
+
return _exhaustive;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function resolveObjectPropertyMetadata(property, options) {
|
|
199
|
+
const metadata = resolveResolvedMetadata(property.metadata, options.policy.field, {
|
|
200
|
+
surface: options.surface,
|
|
201
|
+
declarationKind: "field",
|
|
202
|
+
logicalName: property.name,
|
|
203
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
204
|
+
});
|
|
205
|
+
return {
|
|
206
|
+
...property,
|
|
207
|
+
...metadata !== void 0 && { metadata },
|
|
208
|
+
type: resolveTypeNodeMetadata(property.type, options)
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function resolveFieldMetadataNode(field, options) {
|
|
212
|
+
const metadata = resolveResolvedMetadata(field.metadata, options.policy.field, {
|
|
213
|
+
surface: options.surface,
|
|
214
|
+
declarationKind: "field",
|
|
215
|
+
logicalName: field.name,
|
|
216
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
217
|
+
});
|
|
218
|
+
return {
|
|
219
|
+
...field,
|
|
220
|
+
...metadata !== void 0 && { metadata },
|
|
221
|
+
type: resolveTypeNodeMetadata(field.type, options)
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function resolveFormElementMetadata(element, options) {
|
|
225
|
+
switch (element.kind) {
|
|
226
|
+
case "field":
|
|
227
|
+
return resolveFieldMetadataNode(element, options);
|
|
228
|
+
case "group":
|
|
229
|
+
return {
|
|
230
|
+
...element,
|
|
231
|
+
elements: element.elements.map((child) => resolveFormElementMetadata(child, options))
|
|
232
|
+
};
|
|
233
|
+
case "conditional":
|
|
234
|
+
return {
|
|
235
|
+
...element,
|
|
236
|
+
elements: element.elements.map((child) => resolveFormElementMetadata(child, options))
|
|
237
|
+
};
|
|
238
|
+
default: {
|
|
239
|
+
const _exhaustive = element;
|
|
240
|
+
return _exhaustive;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function resolveTypeDefinitionMetadata(typeDefinition, options) {
|
|
245
|
+
const metadata = resolveResolvedMetadata(typeDefinition.metadata, options.policy.type, {
|
|
246
|
+
surface: options.surface,
|
|
247
|
+
declarationKind: "type",
|
|
248
|
+
logicalName: typeDefinition.name,
|
|
249
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
250
|
+
});
|
|
251
|
+
return {
|
|
252
|
+
...typeDefinition,
|
|
253
|
+
...metadata !== void 0 && { metadata },
|
|
254
|
+
type: resolveTypeNodeMetadata(typeDefinition.type, options)
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function resolveMetadata(explicit, policy, context) {
|
|
258
|
+
return resolveResolvedMetadata(toExplicitResolvedMetadata(explicit), policy, context);
|
|
259
|
+
}
|
|
260
|
+
function mergeResolvedMetadata(baseMetadata, overlayMetadata) {
|
|
261
|
+
const apiName = pickResolvedMetadataValue(baseMetadata?.apiName, overlayMetadata?.apiName);
|
|
262
|
+
const displayName = pickResolvedMetadataValue(
|
|
263
|
+
baseMetadata?.displayName,
|
|
264
|
+
overlayMetadata?.displayName
|
|
265
|
+
);
|
|
266
|
+
const apiNamePlural = pickResolvedMetadataValue(
|
|
267
|
+
baseMetadata?.apiNamePlural,
|
|
268
|
+
overlayMetadata?.apiNamePlural
|
|
269
|
+
);
|
|
270
|
+
const displayNamePlural = pickResolvedMetadataValue(
|
|
271
|
+
baseMetadata?.displayNamePlural,
|
|
272
|
+
overlayMetadata?.displayNamePlural
|
|
273
|
+
);
|
|
274
|
+
if (apiName === void 0 && displayName === void 0 && apiNamePlural === void 0 && displayNamePlural === void 0) {
|
|
275
|
+
return void 0;
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
...apiName !== void 0 && { apiName },
|
|
279
|
+
...displayName !== void 0 && { displayName },
|
|
280
|
+
...apiNamePlural !== void 0 && { apiNamePlural },
|
|
281
|
+
...displayNamePlural !== void 0 && { displayNamePlural }
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function getSerializedName(logicalName, metadata) {
|
|
285
|
+
return metadata?.apiName?.value ?? logicalName;
|
|
286
|
+
}
|
|
287
|
+
function getDisplayName(metadata) {
|
|
288
|
+
return metadata?.displayName?.value;
|
|
289
|
+
}
|
|
290
|
+
function resolveFormIRMetadata(ir, options) {
|
|
291
|
+
const rootLogicalName = options.rootLogicalName ?? ir.name ?? "FormSpec";
|
|
292
|
+
const metadata = resolveResolvedMetadata(ir.metadata, options.policy.type, {
|
|
293
|
+
surface: options.surface,
|
|
294
|
+
declarationKind: "type",
|
|
295
|
+
logicalName: rootLogicalName,
|
|
296
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
297
|
+
});
|
|
298
|
+
return {
|
|
299
|
+
...ir,
|
|
300
|
+
...metadata !== void 0 && { metadata },
|
|
301
|
+
elements: ir.elements.map((element) => resolveFormElementMetadata(element, options)),
|
|
302
|
+
typeRegistry: Object.fromEntries(
|
|
303
|
+
Object.entries(ir.typeRegistry).map(([name, definition]) => [
|
|
304
|
+
name,
|
|
305
|
+
resolveTypeDefinitionMetadata(definition, options)
|
|
306
|
+
])
|
|
307
|
+
)
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// src/canonicalize/chain-dsl-canonicalizer.ts
|
|
3
312
|
var CHAIN_DSL_PROVENANCE = {
|
|
4
313
|
surface: "chain-dsl",
|
|
5
314
|
file: "",
|
|
@@ -15,57 +324,60 @@ function isConditional(el) {
|
|
|
15
324
|
function isField(el) {
|
|
16
325
|
return el._type === "field";
|
|
17
326
|
}
|
|
18
|
-
function canonicalizeChainDSL(form) {
|
|
327
|
+
function canonicalizeChainDSL(form, options) {
|
|
328
|
+
const metadataPolicy = normalizeMetadataPolicy(
|
|
329
|
+
options?.metadata ?? _getFormSpecMetadataPolicy(form)
|
|
330
|
+
);
|
|
19
331
|
return {
|
|
20
332
|
kind: "form-ir",
|
|
21
333
|
irVersion: IR_VERSION,
|
|
22
|
-
elements: canonicalizeElements(form.elements),
|
|
334
|
+
elements: canonicalizeElements(form.elements, metadataPolicy),
|
|
23
335
|
rootAnnotations: [],
|
|
24
336
|
typeRegistry: {},
|
|
25
337
|
provenance: CHAIN_DSL_PROVENANCE
|
|
26
338
|
};
|
|
27
339
|
}
|
|
28
|
-
function canonicalizeElements(elements) {
|
|
29
|
-
return elements.map(canonicalizeElement);
|
|
340
|
+
function canonicalizeElements(elements, metadataPolicy) {
|
|
341
|
+
return elements.map((element) => canonicalizeElement(element, metadataPolicy));
|
|
30
342
|
}
|
|
31
|
-
function canonicalizeElement(element) {
|
|
343
|
+
function canonicalizeElement(element, metadataPolicy) {
|
|
32
344
|
if (isField(element)) {
|
|
33
|
-
return canonicalizeField(element);
|
|
345
|
+
return canonicalizeField(element, metadataPolicy);
|
|
34
346
|
}
|
|
35
347
|
if (isGroup(element)) {
|
|
36
|
-
return canonicalizeGroup(element);
|
|
348
|
+
return canonicalizeGroup(element, metadataPolicy);
|
|
37
349
|
}
|
|
38
350
|
if (isConditional(element)) {
|
|
39
|
-
return canonicalizeConditional(element);
|
|
351
|
+
return canonicalizeConditional(element, metadataPolicy);
|
|
40
352
|
}
|
|
41
353
|
const _exhaustive = element;
|
|
42
354
|
throw new Error(`Unknown element type: ${JSON.stringify(_exhaustive)}`);
|
|
43
355
|
}
|
|
44
|
-
function canonicalizeField(field) {
|
|
356
|
+
function canonicalizeField(field, metadataPolicy) {
|
|
45
357
|
switch (field._field) {
|
|
46
358
|
case "text":
|
|
47
|
-
return canonicalizeTextField(field);
|
|
359
|
+
return canonicalizeTextField(field, metadataPolicy);
|
|
48
360
|
case "number":
|
|
49
|
-
return canonicalizeNumberField(field);
|
|
361
|
+
return canonicalizeNumberField(field, metadataPolicy);
|
|
50
362
|
case "boolean":
|
|
51
|
-
return canonicalizeBooleanField(field);
|
|
363
|
+
return canonicalizeBooleanField(field, metadataPolicy);
|
|
52
364
|
case "enum":
|
|
53
|
-
return canonicalizeStaticEnumField(field);
|
|
365
|
+
return canonicalizeStaticEnumField(field, metadataPolicy);
|
|
54
366
|
case "dynamic_enum":
|
|
55
|
-
return canonicalizeDynamicEnumField(field);
|
|
367
|
+
return canonicalizeDynamicEnumField(field, metadataPolicy);
|
|
56
368
|
case "dynamic_schema":
|
|
57
|
-
return canonicalizeDynamicSchemaField(field);
|
|
369
|
+
return canonicalizeDynamicSchemaField(field, metadataPolicy);
|
|
58
370
|
case "array":
|
|
59
|
-
return canonicalizeArrayField(field);
|
|
371
|
+
return canonicalizeArrayField(field, metadataPolicy);
|
|
60
372
|
case "object":
|
|
61
|
-
return canonicalizeObjectField(field);
|
|
373
|
+
return canonicalizeObjectField(field, metadataPolicy);
|
|
62
374
|
default: {
|
|
63
375
|
const _exhaustive = field;
|
|
64
376
|
throw new Error(`Unknown field type: ${JSON.stringify(_exhaustive)}`);
|
|
65
377
|
}
|
|
66
378
|
}
|
|
67
379
|
}
|
|
68
|
-
function canonicalizeTextField(field) {
|
|
380
|
+
function canonicalizeTextField(field, metadataPolicy) {
|
|
69
381
|
const type = { kind: "primitive", primitiveKind: "string" };
|
|
70
382
|
const constraints = [];
|
|
71
383
|
if (field.minLength !== void 0) {
|
|
@@ -97,13 +409,14 @@ function canonicalizeTextField(field) {
|
|
|
97
409
|
}
|
|
98
410
|
return buildFieldNode(
|
|
99
411
|
field.name,
|
|
412
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
100
413
|
type,
|
|
101
414
|
field.required,
|
|
102
|
-
buildAnnotations(field
|
|
415
|
+
buildAnnotations(getExplicitDisplayName(field), field.placeholder),
|
|
103
416
|
constraints
|
|
104
417
|
);
|
|
105
418
|
}
|
|
106
|
-
function canonicalizeNumberField(field) {
|
|
419
|
+
function canonicalizeNumberField(field, metadataPolicy) {
|
|
107
420
|
const type = { kind: "primitive", primitiveKind: "number" };
|
|
108
421
|
const constraints = [];
|
|
109
422
|
if (field.min !== void 0) {
|
|
@@ -135,17 +448,24 @@ function canonicalizeNumberField(field) {
|
|
|
135
448
|
}
|
|
136
449
|
return buildFieldNode(
|
|
137
450
|
field.name,
|
|
451
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
138
452
|
type,
|
|
139
453
|
field.required,
|
|
140
|
-
buildAnnotations(field
|
|
454
|
+
buildAnnotations(getExplicitDisplayName(field)),
|
|
141
455
|
constraints
|
|
142
456
|
);
|
|
143
457
|
}
|
|
144
|
-
function canonicalizeBooleanField(field) {
|
|
458
|
+
function canonicalizeBooleanField(field, metadataPolicy) {
|
|
145
459
|
const type = { kind: "primitive", primitiveKind: "boolean" };
|
|
146
|
-
return buildFieldNode(
|
|
460
|
+
return buildFieldNode(
|
|
461
|
+
field.name,
|
|
462
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
463
|
+
type,
|
|
464
|
+
field.required,
|
|
465
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
466
|
+
);
|
|
147
467
|
}
|
|
148
|
-
function canonicalizeStaticEnumField(field) {
|
|
468
|
+
function canonicalizeStaticEnumField(field, metadataPolicy) {
|
|
149
469
|
const members = field.options.map((opt) => {
|
|
150
470
|
if (typeof opt === "string") {
|
|
151
471
|
return { value: opt };
|
|
@@ -153,28 +473,46 @@ function canonicalizeStaticEnumField(field) {
|
|
|
153
473
|
return { value: opt.id, displayName: opt.label };
|
|
154
474
|
});
|
|
155
475
|
const type = { kind: "enum", members };
|
|
156
|
-
return buildFieldNode(
|
|
476
|
+
return buildFieldNode(
|
|
477
|
+
field.name,
|
|
478
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
479
|
+
type,
|
|
480
|
+
field.required,
|
|
481
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
482
|
+
);
|
|
157
483
|
}
|
|
158
|
-
function canonicalizeDynamicEnumField(field) {
|
|
484
|
+
function canonicalizeDynamicEnumField(field, metadataPolicy) {
|
|
159
485
|
const type = {
|
|
160
486
|
kind: "dynamic",
|
|
161
487
|
dynamicKind: "enum",
|
|
162
488
|
sourceKey: field.source,
|
|
163
489
|
parameterFields: field.params ? [...field.params] : []
|
|
164
490
|
};
|
|
165
|
-
return buildFieldNode(
|
|
491
|
+
return buildFieldNode(
|
|
492
|
+
field.name,
|
|
493
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
494
|
+
type,
|
|
495
|
+
field.required,
|
|
496
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
497
|
+
);
|
|
166
498
|
}
|
|
167
|
-
function canonicalizeDynamicSchemaField(field) {
|
|
499
|
+
function canonicalizeDynamicSchemaField(field, metadataPolicy) {
|
|
168
500
|
const type = {
|
|
169
501
|
kind: "dynamic",
|
|
170
502
|
dynamicKind: "schema",
|
|
171
503
|
sourceKey: field.schemaSource,
|
|
172
504
|
parameterFields: []
|
|
173
505
|
};
|
|
174
|
-
return buildFieldNode(
|
|
506
|
+
return buildFieldNode(
|
|
507
|
+
field.name,
|
|
508
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
509
|
+
type,
|
|
510
|
+
field.required,
|
|
511
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
512
|
+
);
|
|
175
513
|
}
|
|
176
|
-
function canonicalizeArrayField(field) {
|
|
177
|
-
const itemProperties = buildObjectProperties(field.items);
|
|
514
|
+
function canonicalizeArrayField(field, metadataPolicy) {
|
|
515
|
+
const itemProperties = buildObjectProperties(field.items, metadataPolicy);
|
|
178
516
|
const itemsType = {
|
|
179
517
|
kind: "object",
|
|
180
518
|
properties: itemProperties,
|
|
@@ -202,37 +540,44 @@ function canonicalizeArrayField(field) {
|
|
|
202
540
|
}
|
|
203
541
|
return buildFieldNode(
|
|
204
542
|
field.name,
|
|
543
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
205
544
|
type,
|
|
206
545
|
field.required,
|
|
207
|
-
buildAnnotations(field
|
|
546
|
+
buildAnnotations(getExplicitDisplayName(field)),
|
|
208
547
|
constraints
|
|
209
548
|
);
|
|
210
549
|
}
|
|
211
|
-
function canonicalizeObjectField(field) {
|
|
212
|
-
const properties = buildObjectProperties(field.properties);
|
|
550
|
+
function canonicalizeObjectField(field, metadataPolicy) {
|
|
551
|
+
const properties = buildObjectProperties(field.properties, metadataPolicy);
|
|
213
552
|
const type = {
|
|
214
553
|
kind: "object",
|
|
215
554
|
properties,
|
|
216
555
|
additionalProperties: true
|
|
217
556
|
};
|
|
218
|
-
return buildFieldNode(
|
|
557
|
+
return buildFieldNode(
|
|
558
|
+
field.name,
|
|
559
|
+
resolveFieldMetadata(field.name, field, metadataPolicy),
|
|
560
|
+
type,
|
|
561
|
+
field.required,
|
|
562
|
+
buildAnnotations(getExplicitDisplayName(field))
|
|
563
|
+
);
|
|
219
564
|
}
|
|
220
|
-
function canonicalizeGroup(g) {
|
|
565
|
+
function canonicalizeGroup(g, metadataPolicy) {
|
|
221
566
|
return {
|
|
222
567
|
kind: "group",
|
|
223
568
|
label: g.label,
|
|
224
|
-
elements: canonicalizeElements(g.elements),
|
|
569
|
+
elements: canonicalizeElements(g.elements, metadataPolicy),
|
|
225
570
|
provenance: CHAIN_DSL_PROVENANCE
|
|
226
571
|
};
|
|
227
572
|
}
|
|
228
|
-
function canonicalizeConditional(c) {
|
|
573
|
+
function canonicalizeConditional(c, metadataPolicy) {
|
|
229
574
|
return {
|
|
230
575
|
kind: "conditional",
|
|
231
576
|
fieldName: c.field,
|
|
232
577
|
// Conditional values from the chain DSL are JSON-serializable primitives
|
|
233
578
|
// (strings, numbers, booleans) produced by the `is()` predicate helper.
|
|
234
579
|
value: assertJsonValue(c.value),
|
|
235
|
-
elements: canonicalizeElements(c.elements),
|
|
580
|
+
elements: canonicalizeElements(c.elements, metadataPolicy),
|
|
236
581
|
provenance: CHAIN_DSL_PROVENANCE
|
|
237
582
|
};
|
|
238
583
|
}
|
|
@@ -252,10 +597,11 @@ function assertJsonValue(v) {
|
|
|
252
597
|
}
|
|
253
598
|
throw new TypeError(`Conditional value is not a valid JsonValue: ${typeof v}`);
|
|
254
599
|
}
|
|
255
|
-
function buildFieldNode(name, type, required, annotations, constraints = []) {
|
|
600
|
+
function buildFieldNode(name, metadata, type, required, annotations, constraints = []) {
|
|
256
601
|
return {
|
|
257
602
|
kind: "field",
|
|
258
603
|
name,
|
|
604
|
+
...metadata !== void 0 && { metadata },
|
|
259
605
|
type,
|
|
260
606
|
required: required === true,
|
|
261
607
|
constraints,
|
|
@@ -285,13 +631,14 @@ function buildAnnotations(label, placeholder) {
|
|
|
285
631
|
}
|
|
286
632
|
return annotations;
|
|
287
633
|
}
|
|
288
|
-
function buildObjectProperties(elements, insideConditional = false) {
|
|
634
|
+
function buildObjectProperties(elements, metadataPolicy, insideConditional = false) {
|
|
289
635
|
const properties = [];
|
|
290
636
|
for (const el of elements) {
|
|
291
637
|
if (isField(el)) {
|
|
292
|
-
const fieldNode = canonicalizeField(el);
|
|
638
|
+
const fieldNode = canonicalizeField(el, metadataPolicy);
|
|
293
639
|
properties.push({
|
|
294
640
|
name: fieldNode.name,
|
|
641
|
+
...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
|
|
295
642
|
type: fieldNode.type,
|
|
296
643
|
// Fields inside a conditional branch are always optional in the
|
|
297
644
|
// data schema, regardless of their `required` flag — the condition
|
|
@@ -302,17 +649,34 @@ function buildObjectProperties(elements, insideConditional = false) {
|
|
|
302
649
|
provenance: CHAIN_DSL_PROVENANCE
|
|
303
650
|
});
|
|
304
651
|
} else if (isGroup(el)) {
|
|
305
|
-
properties.push(...buildObjectProperties(el.elements, insideConditional));
|
|
652
|
+
properties.push(...buildObjectProperties(el.elements, metadataPolicy, insideConditional));
|
|
306
653
|
} else if (isConditional(el)) {
|
|
307
|
-
properties.push(...buildObjectProperties(el.elements, true));
|
|
654
|
+
properties.push(...buildObjectProperties(el.elements, metadataPolicy, true));
|
|
308
655
|
}
|
|
309
656
|
}
|
|
310
657
|
return properties;
|
|
311
658
|
}
|
|
659
|
+
function getExplicitDisplayName(field) {
|
|
660
|
+
if (field.label !== void 0 && field.displayName !== void 0) {
|
|
661
|
+
throw new Error('Chain DSL fields cannot specify both "label" and "displayName".');
|
|
662
|
+
}
|
|
663
|
+
return field.displayName ?? field.label;
|
|
664
|
+
}
|
|
665
|
+
function resolveFieldMetadata(logicalName, field, metadataPolicy) {
|
|
666
|
+
const displayName = getExplicitDisplayName(field);
|
|
667
|
+
return resolveMetadata(
|
|
668
|
+
{
|
|
669
|
+
...field.apiName !== void 0 && { apiName: field.apiName },
|
|
670
|
+
...displayName !== void 0 && { displayName }
|
|
671
|
+
},
|
|
672
|
+
getDeclarationMetadataPolicy(metadataPolicy, "field"),
|
|
673
|
+
makeMetadataContext("chain-dsl", "field", logicalName)
|
|
674
|
+
);
|
|
675
|
+
}
|
|
312
676
|
|
|
313
677
|
// src/canonicalize/tsdoc-canonicalizer.ts
|
|
314
678
|
import { IR_VERSION as IR_VERSION2 } from "@formspec/core/internals";
|
|
315
|
-
function canonicalizeTSDoc(analysis, source) {
|
|
679
|
+
function canonicalizeTSDoc(analysis, source, options) {
|
|
316
680
|
const file = source?.file ?? "";
|
|
317
681
|
const provenance = {
|
|
318
682
|
surface: "tsdoc",
|
|
@@ -321,15 +685,21 @@ function canonicalizeTSDoc(analysis, source) {
|
|
|
321
685
|
column: 0
|
|
322
686
|
};
|
|
323
687
|
const elements = assembleElements(analysis.fields, analysis.fieldLayouts, provenance);
|
|
324
|
-
|
|
688
|
+
const ir = {
|
|
325
689
|
kind: "form-ir",
|
|
690
|
+
name: analysis.name,
|
|
326
691
|
irVersion: IR_VERSION2,
|
|
327
692
|
elements,
|
|
693
|
+
...analysis.metadata !== void 0 && { metadata: analysis.metadata },
|
|
328
694
|
typeRegistry: analysis.typeRegistry,
|
|
329
695
|
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { rootAnnotations: analysis.annotations },
|
|
330
696
|
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { annotations: analysis.annotations },
|
|
331
697
|
provenance
|
|
332
698
|
};
|
|
699
|
+
return resolveFormIRMetadata(ir, {
|
|
700
|
+
policy: normalizeMetadataPolicy(options?.metadata),
|
|
701
|
+
surface: "tsdoc"
|
|
702
|
+
});
|
|
333
703
|
}
|
|
334
704
|
function assembleElements(fields, layouts, provenance) {
|
|
335
705
|
const elements = [];
|
|
@@ -386,6 +756,120 @@ function wrapInConditional(field, layout, provenance) {
|
|
|
386
756
|
return conditional;
|
|
387
757
|
}
|
|
388
758
|
|
|
759
|
+
// src/metadata/collision-guards.ts
|
|
760
|
+
function assertUniqueSerializedNames(entries, scope) {
|
|
761
|
+
const seen = /* @__PURE__ */ new Map();
|
|
762
|
+
for (const entry of entries) {
|
|
763
|
+
const previous = seen.get(entry.serializedName);
|
|
764
|
+
if (previous !== void 0) {
|
|
765
|
+
if (previous.logicalName === entry.logicalName && previous.category === entry.category) {
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
throw new Error(
|
|
769
|
+
`Serialized name collision in ${scope}: ${previous.category} "${previous.logicalName}" and ${entry.category} "${entry.logicalName}" both resolve to "${entry.serializedName}".`
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
seen.set(entry.serializedName, entry);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
function collectFlattenedFields(elements) {
|
|
776
|
+
const fields = [];
|
|
777
|
+
for (const element of elements) {
|
|
778
|
+
switch (element.kind) {
|
|
779
|
+
case "field":
|
|
780
|
+
fields.push(element);
|
|
781
|
+
break;
|
|
782
|
+
case "group":
|
|
783
|
+
case "conditional":
|
|
784
|
+
fields.push(...collectFlattenedFields(element.elements));
|
|
785
|
+
break;
|
|
786
|
+
default: {
|
|
787
|
+
const exhaustive = element;
|
|
788
|
+
void exhaustive;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
return fields;
|
|
793
|
+
}
|
|
794
|
+
function validateObjectProperties(properties, scope) {
|
|
795
|
+
assertUniqueSerializedNames(
|
|
796
|
+
properties.map((property) => ({
|
|
797
|
+
logicalName: property.name,
|
|
798
|
+
serializedName: getSerializedName(property.name, property.metadata),
|
|
799
|
+
category: "object property"
|
|
800
|
+
})),
|
|
801
|
+
scope
|
|
802
|
+
);
|
|
803
|
+
for (const property of properties) {
|
|
804
|
+
validateTypeNode(
|
|
805
|
+
property.type,
|
|
806
|
+
`${scope}.${getSerializedName(property.name, property.metadata)}`
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function validateTypeNode(type, scope) {
|
|
811
|
+
switch (type.kind) {
|
|
812
|
+
case "array":
|
|
813
|
+
validateTypeNode(type.items, `${scope}[]`);
|
|
814
|
+
break;
|
|
815
|
+
case "object":
|
|
816
|
+
validateObjectProperties(type.properties, scope);
|
|
817
|
+
break;
|
|
818
|
+
case "record":
|
|
819
|
+
validateTypeNode(type.valueType, `${scope}.*`);
|
|
820
|
+
break;
|
|
821
|
+
case "union":
|
|
822
|
+
type.members.forEach((member, index) => {
|
|
823
|
+
validateTypeNode(member, `${scope}|${String(index)}`);
|
|
824
|
+
});
|
|
825
|
+
break;
|
|
826
|
+
case "reference":
|
|
827
|
+
case "primitive":
|
|
828
|
+
case "enum":
|
|
829
|
+
case "dynamic":
|
|
830
|
+
case "custom":
|
|
831
|
+
break;
|
|
832
|
+
default: {
|
|
833
|
+
const exhaustive = type;
|
|
834
|
+
void exhaustive;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
function validateTypeDefinitions(typeRegistry) {
|
|
839
|
+
const definitions = Object.values(typeRegistry);
|
|
840
|
+
assertUniqueSerializedNames(
|
|
841
|
+
definitions.map((definition) => ({
|
|
842
|
+
logicalName: definition.name,
|
|
843
|
+
serializedName: getSerializedName(definition.name, definition.metadata),
|
|
844
|
+
category: "type definition"
|
|
845
|
+
})),
|
|
846
|
+
"$defs"
|
|
847
|
+
);
|
|
848
|
+
for (const definition of definitions) {
|
|
849
|
+
validateTypeDefinition(definition);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
function validateTypeDefinition(definition) {
|
|
853
|
+
validateTypeNode(
|
|
854
|
+
definition.type,
|
|
855
|
+
`type "${getSerializedName(definition.name, definition.metadata)}"`
|
|
856
|
+
);
|
|
857
|
+
}
|
|
858
|
+
function assertNoSerializedNameCollisions(ir) {
|
|
859
|
+
assertUniqueSerializedNames(
|
|
860
|
+
collectFlattenedFields(ir.elements).map((field) => ({
|
|
861
|
+
logicalName: field.name,
|
|
862
|
+
serializedName: getSerializedName(field.name, field.metadata),
|
|
863
|
+
category: "field"
|
|
864
|
+
})),
|
|
865
|
+
"form root"
|
|
866
|
+
);
|
|
867
|
+
for (const field of collectFlattenedFields(ir.elements)) {
|
|
868
|
+
validateTypeNode(field.type, `field "${getSerializedName(field.name, field.metadata)}"`);
|
|
869
|
+
}
|
|
870
|
+
validateTypeDefinitions(ir.typeRegistry);
|
|
871
|
+
}
|
|
872
|
+
|
|
389
873
|
// src/json-schema/ir-generator.ts
|
|
390
874
|
function makeContext(options) {
|
|
391
875
|
const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
|
|
@@ -396,19 +880,33 @@ function makeContext(options) {
|
|
|
396
880
|
}
|
|
397
881
|
return {
|
|
398
882
|
defs: {},
|
|
883
|
+
typeNameMap: {},
|
|
884
|
+
typeRegistry: {},
|
|
399
885
|
extensionRegistry: options?.extensionRegistry,
|
|
400
886
|
vendorPrefix
|
|
401
887
|
};
|
|
402
888
|
}
|
|
403
889
|
function generateJsonSchemaFromIR(ir, options) {
|
|
404
|
-
|
|
890
|
+
assertNoSerializedNameCollisions(ir);
|
|
891
|
+
const ctx = {
|
|
892
|
+
...makeContext(options),
|
|
893
|
+
typeRegistry: ir.typeRegistry,
|
|
894
|
+
typeNameMap: Object.fromEntries(
|
|
895
|
+
Object.entries(ir.typeRegistry).map(([name, typeDef]) => [
|
|
896
|
+
name,
|
|
897
|
+
getSerializedName(name, typeDef.metadata)
|
|
898
|
+
])
|
|
899
|
+
)
|
|
900
|
+
};
|
|
405
901
|
for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
|
|
406
|
-
ctx.
|
|
902
|
+
const schemaName = ctx.typeNameMap[name] ?? name;
|
|
903
|
+
ctx.defs[schemaName] = generateTypeNode(typeDef.type, ctx);
|
|
904
|
+
applyResolvedMetadata(ctx.defs[schemaName], typeDef.metadata);
|
|
407
905
|
if (typeDef.constraints && typeDef.constraints.length > 0) {
|
|
408
|
-
applyConstraints(ctx.defs[
|
|
906
|
+
applyConstraints(ctx.defs[schemaName], typeDef.constraints, ctx);
|
|
409
907
|
}
|
|
410
908
|
if (typeDef.annotations && typeDef.annotations.length > 0) {
|
|
411
|
-
applyAnnotations(ctx.defs[
|
|
909
|
+
applyAnnotations(ctx.defs[schemaName], typeDef.annotations, ctx);
|
|
412
910
|
}
|
|
413
911
|
}
|
|
414
912
|
const properties = {};
|
|
@@ -421,6 +919,7 @@ function generateJsonSchemaFromIR(ir, options) {
|
|
|
421
919
|
properties,
|
|
422
920
|
...uniqueRequired.length > 0 && { required: uniqueRequired }
|
|
423
921
|
};
|
|
922
|
+
applyResolvedMetadata(result, ir.metadata);
|
|
424
923
|
if (ir.annotations && ir.annotations.length > 0) {
|
|
425
924
|
applyAnnotations(result, ir.annotations, ctx);
|
|
426
925
|
}
|
|
@@ -433,9 +932,9 @@ function collectFields(elements, properties, required, ctx) {
|
|
|
433
932
|
for (const element of elements) {
|
|
434
933
|
switch (element.kind) {
|
|
435
934
|
case "field":
|
|
436
|
-
properties[element.name] = generateFieldSchema(element, ctx);
|
|
935
|
+
properties[getSerializedName(element.name, element.metadata)] = generateFieldSchema(element, ctx);
|
|
437
936
|
if (element.required) {
|
|
438
|
-
required.push(element.name);
|
|
937
|
+
required.push(getSerializedName(element.name, element.metadata));
|
|
439
938
|
}
|
|
440
939
|
break;
|
|
441
940
|
case "group":
|
|
@@ -479,6 +978,7 @@ function generateFieldSchema(field, ctx) {
|
|
|
479
978
|
rootAnnotations.push(annotation);
|
|
480
979
|
}
|
|
481
980
|
}
|
|
981
|
+
applyResolvedMetadata(schema, field.metadata);
|
|
482
982
|
applyAnnotations(schema, rootAnnotations, ctx);
|
|
483
983
|
if (itemStringSchema !== void 0) {
|
|
484
984
|
applyAnnotations(itemStringSchema, itemAnnotations, ctx);
|
|
@@ -486,7 +986,7 @@ function generateFieldSchema(field, ctx) {
|
|
|
486
986
|
if (pathConstraints.length === 0) {
|
|
487
987
|
return schema;
|
|
488
988
|
}
|
|
489
|
-
return applyPathTargetedConstraints(schema, pathConstraints, ctx);
|
|
989
|
+
return applyPathTargetedConstraints(schema, pathConstraints, ctx, field.type);
|
|
490
990
|
}
|
|
491
991
|
function isStringItemConstraint(constraint) {
|
|
492
992
|
switch (constraint.constraintKind) {
|
|
@@ -498,9 +998,11 @@ function isStringItemConstraint(constraint) {
|
|
|
498
998
|
return false;
|
|
499
999
|
}
|
|
500
1000
|
}
|
|
501
|
-
function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
|
|
1001
|
+
function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
|
|
502
1002
|
if (schema.type === "array" && schema.items) {
|
|
503
|
-
|
|
1003
|
+
const referencedType = typeNode?.kind === "reference" ? resolveReferencedType(typeNode, ctx) : void 0;
|
|
1004
|
+
const nestedType = typeNode?.kind === "array" ? typeNode.items : referencedType?.kind === "array" ? referencedType.items : void 0;
|
|
1005
|
+
schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx, nestedType);
|
|
504
1006
|
return schema;
|
|
505
1007
|
}
|
|
506
1008
|
const byTarget = /* @__PURE__ */ new Map();
|
|
@@ -515,7 +1017,7 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
|
|
|
515
1017
|
for (const [target, constraints] of byTarget) {
|
|
516
1018
|
const subSchema = {};
|
|
517
1019
|
applyConstraints(subSchema, constraints, ctx);
|
|
518
|
-
propertyOverrides[target] = subSchema;
|
|
1020
|
+
propertyOverrides[resolveSerializedPropertyName(target, typeNode, ctx)] = subSchema;
|
|
519
1021
|
}
|
|
520
1022
|
if (schema.$ref) {
|
|
521
1023
|
const { $ref, ...rest } = schema;
|
|
@@ -563,7 +1065,7 @@ function generateTypeNode(type, ctx) {
|
|
|
563
1065
|
case "union":
|
|
564
1066
|
return generateUnionType(type, ctx);
|
|
565
1067
|
case "reference":
|
|
566
|
-
return generateReferenceType(type);
|
|
1068
|
+
return generateReferenceType(type, ctx);
|
|
567
1069
|
case "dynamic":
|
|
568
1070
|
return generateDynamicType(type);
|
|
569
1071
|
case "custom":
|
|
@@ -604,9 +1106,10 @@ function generateObjectType(type, ctx) {
|
|
|
604
1106
|
const properties = {};
|
|
605
1107
|
const required = [];
|
|
606
1108
|
for (const prop of type.properties) {
|
|
607
|
-
|
|
1109
|
+
const propertyName = getSerializedName(prop.name, prop.metadata);
|
|
1110
|
+
properties[propertyName] = generatePropertySchema(prop, ctx);
|
|
608
1111
|
if (!prop.optional) {
|
|
609
|
-
required.push(
|
|
1112
|
+
required.push(propertyName);
|
|
610
1113
|
}
|
|
611
1114
|
}
|
|
612
1115
|
const schema = { type: "object", properties };
|
|
@@ -627,6 +1130,7 @@ function generateRecordType(type, ctx) {
|
|
|
627
1130
|
function generatePropertySchema(prop, ctx) {
|
|
628
1131
|
const schema = generateTypeNode(prop.type, ctx);
|
|
629
1132
|
applyConstraints(schema, prop.constraints, ctx);
|
|
1133
|
+
applyResolvedMetadata(schema, prop.metadata);
|
|
630
1134
|
applyAnnotations(schema, prop.annotations, ctx);
|
|
631
1135
|
return schema;
|
|
632
1136
|
}
|
|
@@ -655,8 +1159,28 @@ function isNullableUnion(type) {
|
|
|
655
1159
|
).length;
|
|
656
1160
|
return nullCount === 1;
|
|
657
1161
|
}
|
|
658
|
-
function generateReferenceType(type) {
|
|
659
|
-
return { $ref: `#/$defs/${type.name}` };
|
|
1162
|
+
function generateReferenceType(type, ctx) {
|
|
1163
|
+
return { $ref: `#/$defs/${ctx.typeNameMap[type.name] ?? type.name}` };
|
|
1164
|
+
}
|
|
1165
|
+
function applyResolvedMetadata(schema, metadata) {
|
|
1166
|
+
const displayName = getDisplayName(metadata);
|
|
1167
|
+
if (displayName !== void 0) {
|
|
1168
|
+
schema.title = displayName;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
function resolveReferencedType(type, ctx) {
|
|
1172
|
+
return ctx.typeRegistry[type.name]?.type;
|
|
1173
|
+
}
|
|
1174
|
+
function resolveSerializedPropertyName(logicalName, typeNode, ctx) {
|
|
1175
|
+
if (typeNode?.kind === "object") {
|
|
1176
|
+
const property = typeNode.properties.find((candidate) => candidate.name === logicalName);
|
|
1177
|
+
return property === void 0 ? logicalName : getSerializedName(property.name, property.metadata);
|
|
1178
|
+
}
|
|
1179
|
+
if (typeNode?.kind === "reference") {
|
|
1180
|
+
const referencedType = resolveReferencedType(typeNode, ctx);
|
|
1181
|
+
return referencedType === void 0 ? logicalName : resolveSerializedPropertyName(logicalName, referencedType, ctx);
|
|
1182
|
+
}
|
|
1183
|
+
return logicalName;
|
|
660
1184
|
}
|
|
661
1185
|
function generateDynamicType(type) {
|
|
662
1186
|
if (type.dynamicKind === "enum") {
|
|
@@ -736,7 +1260,7 @@ function applyAnnotations(schema, annotations, ctx) {
|
|
|
736
1260
|
for (const annotation of annotations) {
|
|
737
1261
|
switch (annotation.annotationKind) {
|
|
738
1262
|
case "displayName":
|
|
739
|
-
schema.title
|
|
1263
|
+
schema.title ??= annotation.value;
|
|
740
1264
|
break;
|
|
741
1265
|
case "description":
|
|
742
1266
|
schema.description = annotation.value;
|
|
@@ -823,7 +1347,10 @@ function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPr
|
|
|
823
1347
|
|
|
824
1348
|
// src/json-schema/generator.ts
|
|
825
1349
|
function generateJsonSchema(form, options) {
|
|
826
|
-
const ir = canonicalizeChainDSL(
|
|
1350
|
+
const ir = canonicalizeChainDSL(
|
|
1351
|
+
form,
|
|
1352
|
+
options?.metadata !== void 0 ? { metadata: options.metadata } : void 0
|
|
1353
|
+
);
|
|
827
1354
|
const internalOptions = options?.vendorPrefix === void 0 ? void 0 : { vendorPrefix: options.vendorPrefix };
|
|
828
1355
|
return generateJsonSchemaFromIR(ir, internalOptions);
|
|
829
1356
|
}
|
|
@@ -993,13 +1520,21 @@ function combineRules(parentRule, childRule) {
|
|
|
993
1520
|
}
|
|
994
1521
|
};
|
|
995
1522
|
}
|
|
996
|
-
function
|
|
997
|
-
const
|
|
1523
|
+
function getFieldDisplayName(field) {
|
|
1524
|
+
const resolvedDisplayName = getDisplayName(field.metadata);
|
|
1525
|
+
if (resolvedDisplayName !== void 0) {
|
|
1526
|
+
return resolvedDisplayName;
|
|
1527
|
+
}
|
|
1528
|
+
return field.annotations.find((annotation) => annotation.annotationKind === "displayName")?.value;
|
|
1529
|
+
}
|
|
1530
|
+
function fieldNodeToControl(field, fieldNameMap, parentRule) {
|
|
998
1531
|
const placeholderAnnotation = field.annotations.find((a) => a.annotationKind === "placeholder");
|
|
1532
|
+
const serializedName = fieldNameMap.get(field.name) ?? getSerializedName(field.name, field.metadata);
|
|
1533
|
+
const displayName = getFieldDisplayName(field);
|
|
999
1534
|
const control = {
|
|
1000
1535
|
type: "Control",
|
|
1001
|
-
scope: fieldToScope(
|
|
1002
|
-
...
|
|
1536
|
+
scope: fieldToScope(serializedName),
|
|
1537
|
+
...displayName !== void 0 && { label: displayName },
|
|
1003
1538
|
...placeholderAnnotation !== void 0 && {
|
|
1004
1539
|
options: { placeholder: placeholderAnnotation.value }
|
|
1005
1540
|
},
|
|
@@ -1007,30 +1542,30 @@ function fieldNodeToControl(field, parentRule) {
|
|
|
1007
1542
|
};
|
|
1008
1543
|
return control;
|
|
1009
1544
|
}
|
|
1010
|
-
function groupNodeToLayout(group, parentRule) {
|
|
1545
|
+
function groupNodeToLayout(group, fieldNameMap, parentRule) {
|
|
1011
1546
|
return {
|
|
1012
1547
|
type: "Group",
|
|
1013
1548
|
label: group.label,
|
|
1014
|
-
elements: irElementsToUiSchema(group.elements, parentRule),
|
|
1549
|
+
elements: irElementsToUiSchema(group.elements, fieldNameMap, parentRule),
|
|
1015
1550
|
...parentRule !== void 0 && { rule: parentRule }
|
|
1016
1551
|
};
|
|
1017
1552
|
}
|
|
1018
|
-
function irElementsToUiSchema(elements, parentRule) {
|
|
1553
|
+
function irElementsToUiSchema(elements, fieldNameMap, parentRule) {
|
|
1019
1554
|
const result = [];
|
|
1020
1555
|
for (const element of elements) {
|
|
1021
1556
|
switch (element.kind) {
|
|
1022
1557
|
case "field": {
|
|
1023
|
-
result.push(fieldNodeToControl(element, parentRule));
|
|
1558
|
+
result.push(fieldNodeToControl(element, fieldNameMap, parentRule));
|
|
1024
1559
|
break;
|
|
1025
1560
|
}
|
|
1026
1561
|
case "group": {
|
|
1027
|
-
result.push(groupNodeToLayout(element, parentRule));
|
|
1562
|
+
result.push(groupNodeToLayout(element, fieldNameMap, parentRule));
|
|
1028
1563
|
break;
|
|
1029
1564
|
}
|
|
1030
1565
|
case "conditional": {
|
|
1031
|
-
const newRule = createShowRule(element.fieldName, element.value);
|
|
1566
|
+
const newRule = createShowRule(fieldNameMap.get(element.fieldName) ?? element.fieldName, element.value);
|
|
1032
1567
|
const combinedRule = parentRule !== void 0 ? combineRules(parentRule, newRule) : newRule;
|
|
1033
|
-
const childElements = irElementsToUiSchema(element.elements, combinedRule);
|
|
1568
|
+
const childElements = irElementsToUiSchema(element.elements, fieldNameMap, combinedRule);
|
|
1034
1569
|
result.push(...childElements);
|
|
1035
1570
|
break;
|
|
1036
1571
|
}
|
|
@@ -1044,16 +1579,42 @@ function irElementsToUiSchema(elements, parentRule) {
|
|
|
1044
1579
|
return result;
|
|
1045
1580
|
}
|
|
1046
1581
|
function generateUiSchemaFromIR(ir) {
|
|
1582
|
+
assertNoSerializedNameCollisions(ir);
|
|
1583
|
+
const fieldNameMap = collectFieldNameMap(ir.elements);
|
|
1047
1584
|
const result = {
|
|
1048
1585
|
type: "VerticalLayout",
|
|
1049
|
-
elements: irElementsToUiSchema(ir.elements)
|
|
1586
|
+
elements: irElementsToUiSchema(ir.elements, fieldNameMap)
|
|
1050
1587
|
};
|
|
1051
1588
|
return parseOrThrow(uiSchema, result, "UI Schema");
|
|
1052
1589
|
}
|
|
1590
|
+
function collectFieldNameMap(elements) {
|
|
1591
|
+
const map = /* @__PURE__ */ new Map();
|
|
1592
|
+
for (const element of elements) {
|
|
1593
|
+
switch (element.kind) {
|
|
1594
|
+
case "field":
|
|
1595
|
+
map.set(element.name, getSerializedName(element.name, element.metadata));
|
|
1596
|
+
break;
|
|
1597
|
+
case "group":
|
|
1598
|
+
case "conditional":
|
|
1599
|
+
for (const [key, value] of collectFieldNameMap(element.elements)) {
|
|
1600
|
+
map.set(key, value);
|
|
1601
|
+
}
|
|
1602
|
+
break;
|
|
1603
|
+
default: {
|
|
1604
|
+
const _exhaustive = element;
|
|
1605
|
+
void _exhaustive;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
return map;
|
|
1610
|
+
}
|
|
1053
1611
|
|
|
1054
1612
|
// src/ui-schema/generator.ts
|
|
1055
|
-
function generateUiSchema(form) {
|
|
1056
|
-
const ir = canonicalizeChainDSL(
|
|
1613
|
+
function generateUiSchema(form, options) {
|
|
1614
|
+
const ir = canonicalizeChainDSL(
|
|
1615
|
+
form,
|
|
1616
|
+
options?.metadata !== void 0 ? { metadata: options.metadata } : void 0
|
|
1617
|
+
);
|
|
1057
1618
|
return generateUiSchemaFromIR(ir);
|
|
1058
1619
|
}
|
|
1059
1620
|
|
|
@@ -1212,6 +1773,9 @@ import * as path from "path";
|
|
|
1212
1773
|
|
|
1213
1774
|
// src/analyzer/class-analyzer.ts
|
|
1214
1775
|
import * as ts3 from "typescript";
|
|
1776
|
+
import {
|
|
1777
|
+
parseCommentBlock as parseCommentBlock2
|
|
1778
|
+
} from "@formspec/analysis/internal";
|
|
1215
1779
|
|
|
1216
1780
|
// src/analyzer/jsdoc-constraints.ts
|
|
1217
1781
|
import * as ts2 from "typescript";
|
|
@@ -2114,7 +2678,76 @@ function makeParseOptions(extensionRegistry, fieldType, checker, subjectType, ho
|
|
|
2114
2678
|
...hostType !== void 0 && { hostType }
|
|
2115
2679
|
};
|
|
2116
2680
|
}
|
|
2117
|
-
function
|
|
2681
|
+
function makeExplicitScalarMetadata(value) {
|
|
2682
|
+
return value === void 0 || value === "" ? void 0 : { value, source: "explicit" };
|
|
2683
|
+
}
|
|
2684
|
+
function extractExplicitMetadata(node) {
|
|
2685
|
+
let apiName;
|
|
2686
|
+
let displayName;
|
|
2687
|
+
let apiNamePlural;
|
|
2688
|
+
let displayNamePlural;
|
|
2689
|
+
for (const tag of getLeadingParsedTags(node)) {
|
|
2690
|
+
const value = tag.argumentText.trim();
|
|
2691
|
+
if (value === "") {
|
|
2692
|
+
continue;
|
|
2693
|
+
}
|
|
2694
|
+
if (tag.normalizedTagName === "apiName") {
|
|
2695
|
+
if (tag.target === null) {
|
|
2696
|
+
apiName ??= value;
|
|
2697
|
+
} else if (tag.target.kind === "variant") {
|
|
2698
|
+
if (tag.target.rawText === "singular") {
|
|
2699
|
+
apiName ??= value;
|
|
2700
|
+
} else if (tag.target.rawText === "plural") {
|
|
2701
|
+
apiNamePlural ??= value;
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
continue;
|
|
2705
|
+
}
|
|
2706
|
+
if (tag.normalizedTagName === "displayName") {
|
|
2707
|
+
if (tag.target === null) {
|
|
2708
|
+
displayName ??= value;
|
|
2709
|
+
} else if (tag.target.kind === "variant") {
|
|
2710
|
+
if (tag.target.rawText === "singular") {
|
|
2711
|
+
displayName ??= value;
|
|
2712
|
+
} else if (tag.target.rawText === "plural") {
|
|
2713
|
+
displayNamePlural ??= value;
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
const resolvedApiName = makeExplicitScalarMetadata(apiName);
|
|
2719
|
+
const resolvedDisplayName = makeExplicitScalarMetadata(displayName);
|
|
2720
|
+
const resolvedApiNamePlural = makeExplicitScalarMetadata(apiNamePlural);
|
|
2721
|
+
const resolvedDisplayNamePlural = makeExplicitScalarMetadata(displayNamePlural);
|
|
2722
|
+
const metadata = {
|
|
2723
|
+
...resolvedApiName !== void 0 && { apiName: resolvedApiName },
|
|
2724
|
+
...resolvedDisplayName !== void 0 && { displayName: resolvedDisplayName },
|
|
2725
|
+
...resolvedApiNamePlural !== void 0 && { apiNamePlural: resolvedApiNamePlural },
|
|
2726
|
+
...resolvedDisplayNamePlural !== void 0 && {
|
|
2727
|
+
displayNamePlural: resolvedDisplayNamePlural
|
|
2728
|
+
}
|
|
2729
|
+
};
|
|
2730
|
+
return Object.keys(metadata).length === 0 ? void 0 : metadata;
|
|
2731
|
+
}
|
|
2732
|
+
function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, buildContext) {
|
|
2733
|
+
const explicit = extractExplicitMetadata(node);
|
|
2734
|
+
return resolveMetadata(
|
|
2735
|
+
{
|
|
2736
|
+
...explicit?.apiName !== void 0 && { apiName: explicit.apiName.value },
|
|
2737
|
+
...explicit?.displayName !== void 0 && { displayName: explicit.displayName.value },
|
|
2738
|
+
...explicit?.apiNamePlural !== void 0 && {
|
|
2739
|
+
apiNamePlural: explicit.apiNamePlural.value
|
|
2740
|
+
},
|
|
2741
|
+
...explicit?.displayNamePlural !== void 0 && {
|
|
2742
|
+
displayNamePlural: explicit.displayNamePlural.value
|
|
2743
|
+
}
|
|
2744
|
+
},
|
|
2745
|
+
getDeclarationMetadataPolicy(metadataPolicy, declarationKind),
|
|
2746
|
+
makeMetadataContext("tsdoc", declarationKind, logicalName, buildContext)
|
|
2747
|
+
);
|
|
2748
|
+
}
|
|
2749
|
+
function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy) {
|
|
2750
|
+
const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
|
|
2118
2751
|
const name = classDecl.name?.text ?? "AnonymousClass";
|
|
2119
2752
|
const fields = [];
|
|
2120
2753
|
const fieldLayouts = [];
|
|
@@ -2141,6 +2774,7 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
2141
2774
|
visiting,
|
|
2142
2775
|
diagnostics,
|
|
2143
2776
|
classType,
|
|
2777
|
+
normalizedMetadataPolicy,
|
|
2144
2778
|
extensionRegistry
|
|
2145
2779
|
);
|
|
2146
2780
|
if (fieldNode) {
|
|
@@ -2159,9 +2793,25 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
2159
2793
|
}
|
|
2160
2794
|
}
|
|
2161
2795
|
}
|
|
2796
|
+
const specializedFields = applyDeclarationDiscriminatorToFields(
|
|
2797
|
+
fields,
|
|
2798
|
+
classDecl,
|
|
2799
|
+
classType,
|
|
2800
|
+
checker,
|
|
2801
|
+
file,
|
|
2802
|
+
diagnostics,
|
|
2803
|
+
normalizedMetadataPolicy
|
|
2804
|
+
);
|
|
2805
|
+
const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, classDecl, {
|
|
2806
|
+
checker,
|
|
2807
|
+
declaration: classDecl,
|
|
2808
|
+
subjectType: classType,
|
|
2809
|
+
hostType: classType
|
|
2810
|
+
});
|
|
2162
2811
|
return {
|
|
2163
2812
|
name,
|
|
2164
|
-
|
|
2813
|
+
...metadata !== void 0 && { metadata },
|
|
2814
|
+
fields: specializedFields,
|
|
2165
2815
|
fieldLayouts,
|
|
2166
2816
|
typeRegistry,
|
|
2167
2817
|
...annotations.length > 0 && { annotations },
|
|
@@ -2170,7 +2820,8 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
2170
2820
|
staticMethods
|
|
2171
2821
|
};
|
|
2172
2822
|
}
|
|
2173
|
-
function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry) {
|
|
2823
|
+
function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy) {
|
|
2824
|
+
const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
|
|
2174
2825
|
const name = interfaceDecl.name.text;
|
|
2175
2826
|
const fields = [];
|
|
2176
2827
|
const typeRegistry = {};
|
|
@@ -2194,6 +2845,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
2194
2845
|
visiting,
|
|
2195
2846
|
diagnostics,
|
|
2196
2847
|
interfaceType,
|
|
2848
|
+
normalizedMetadataPolicy,
|
|
2197
2849
|
extensionRegistry
|
|
2198
2850
|
);
|
|
2199
2851
|
if (fieldNode) {
|
|
@@ -2201,10 +2853,26 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
2201
2853
|
}
|
|
2202
2854
|
}
|
|
2203
2855
|
}
|
|
2204
|
-
const
|
|
2856
|
+
const specializedFields = applyDeclarationDiscriminatorToFields(
|
|
2857
|
+
fields,
|
|
2858
|
+
interfaceDecl,
|
|
2859
|
+
interfaceType,
|
|
2860
|
+
checker,
|
|
2861
|
+
file,
|
|
2862
|
+
diagnostics,
|
|
2863
|
+
normalizedMetadataPolicy
|
|
2864
|
+
);
|
|
2865
|
+
const fieldLayouts = specializedFields.map(() => ({}));
|
|
2866
|
+
const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, interfaceDecl, {
|
|
2867
|
+
checker,
|
|
2868
|
+
declaration: interfaceDecl,
|
|
2869
|
+
subjectType: interfaceType,
|
|
2870
|
+
hostType: interfaceType
|
|
2871
|
+
});
|
|
2205
2872
|
return {
|
|
2206
2873
|
name,
|
|
2207
|
-
|
|
2874
|
+
...metadata !== void 0 && { metadata },
|
|
2875
|
+
fields: specializedFields,
|
|
2208
2876
|
fieldLayouts,
|
|
2209
2877
|
typeRegistry,
|
|
2210
2878
|
...annotations.length > 0 && { annotations },
|
|
@@ -2213,7 +2881,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
2213
2881
|
staticMethods: []
|
|
2214
2882
|
};
|
|
2215
2883
|
}
|
|
2216
|
-
function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
|
|
2884
|
+
function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry, metadataPolicy) {
|
|
2217
2885
|
if (!ts3.isTypeLiteralNode(typeAlias.type)) {
|
|
2218
2886
|
const sourceFile = typeAlias.getSourceFile();
|
|
2219
2887
|
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
@@ -2223,6 +2891,8 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
2223
2891
|
error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object type literal (found ${kindDesc})`
|
|
2224
2892
|
};
|
|
2225
2893
|
}
|
|
2894
|
+
const typeLiteral = typeAlias.type;
|
|
2895
|
+
const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
|
|
2226
2896
|
const name = typeAlias.name.text;
|
|
2227
2897
|
const fields = [];
|
|
2228
2898
|
const typeRegistry = {};
|
|
@@ -2236,7 +2906,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
2236
2906
|
const annotations = [...typeAliasDoc.annotations];
|
|
2237
2907
|
diagnostics.push(...typeAliasDoc.diagnostics);
|
|
2238
2908
|
const visiting = /* @__PURE__ */ new Set();
|
|
2239
|
-
for (const member of
|
|
2909
|
+
for (const member of typeLiteral.members) {
|
|
2240
2910
|
if (ts3.isPropertySignature(member)) {
|
|
2241
2911
|
const fieldNode = analyzeInterfacePropertyToIR(
|
|
2242
2912
|
member,
|
|
@@ -2246,6 +2916,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
2246
2916
|
visiting,
|
|
2247
2917
|
diagnostics,
|
|
2248
2918
|
aliasType,
|
|
2919
|
+
normalizedMetadataPolicy,
|
|
2249
2920
|
extensionRegistry
|
|
2250
2921
|
);
|
|
2251
2922
|
if (fieldNode) {
|
|
@@ -2253,12 +2924,28 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
2253
2924
|
}
|
|
2254
2925
|
}
|
|
2255
2926
|
}
|
|
2927
|
+
const specializedFields = applyDeclarationDiscriminatorToFields(
|
|
2928
|
+
fields,
|
|
2929
|
+
typeAlias,
|
|
2930
|
+
aliasType,
|
|
2931
|
+
checker,
|
|
2932
|
+
file,
|
|
2933
|
+
diagnostics,
|
|
2934
|
+
normalizedMetadataPolicy
|
|
2935
|
+
);
|
|
2936
|
+
const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, typeAlias, {
|
|
2937
|
+
checker,
|
|
2938
|
+
declaration: typeAlias,
|
|
2939
|
+
subjectType: aliasType,
|
|
2940
|
+
hostType: aliasType
|
|
2941
|
+
});
|
|
2256
2942
|
return {
|
|
2257
2943
|
ok: true,
|
|
2258
2944
|
analysis: {
|
|
2259
2945
|
name,
|
|
2260
|
-
|
|
2261
|
-
|
|
2946
|
+
...metadata !== void 0 && { metadata },
|
|
2947
|
+
fields: specializedFields,
|
|
2948
|
+
fieldLayouts: specializedFields.map(() => ({})),
|
|
2262
2949
|
typeRegistry,
|
|
2263
2950
|
...annotations.length > 0 && { annotations },
|
|
2264
2951
|
...diagnostics.length > 0 && { diagnostics },
|
|
@@ -2267,7 +2954,444 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
2267
2954
|
}
|
|
2268
2955
|
};
|
|
2269
2956
|
}
|
|
2270
|
-
function
|
|
2957
|
+
function makeAnalysisDiagnostic(code, message, primaryLocation, relatedLocations = []) {
|
|
2958
|
+
return {
|
|
2959
|
+
code,
|
|
2960
|
+
message,
|
|
2961
|
+
severity: "error",
|
|
2962
|
+
primaryLocation,
|
|
2963
|
+
relatedLocations
|
|
2964
|
+
};
|
|
2965
|
+
}
|
|
2966
|
+
function getLeadingParsedTags(node) {
|
|
2967
|
+
const sourceFile = node.getSourceFile();
|
|
2968
|
+
const sourceText = sourceFile.getFullText();
|
|
2969
|
+
const commentRanges = ts3.getLeadingCommentRanges(sourceText, node.getFullStart());
|
|
2970
|
+
if (commentRanges === void 0) {
|
|
2971
|
+
return [];
|
|
2972
|
+
}
|
|
2973
|
+
const parsedTags = [];
|
|
2974
|
+
for (const range of commentRanges) {
|
|
2975
|
+
if (range.kind !== ts3.SyntaxKind.MultiLineCommentTrivia) {
|
|
2976
|
+
continue;
|
|
2977
|
+
}
|
|
2978
|
+
const commentText = sourceText.slice(range.pos, range.end);
|
|
2979
|
+
if (!commentText.startsWith("/**")) {
|
|
2980
|
+
continue;
|
|
2981
|
+
}
|
|
2982
|
+
parsedTags.push(...parseCommentBlock2(commentText, { offset: range.pos }).tags);
|
|
2983
|
+
}
|
|
2984
|
+
return parsedTags;
|
|
2985
|
+
}
|
|
2986
|
+
function resolveDiscriminatorProperty(node, checker, fieldName) {
|
|
2987
|
+
const subjectType = checker.getTypeAtLocation(node);
|
|
2988
|
+
const propertySymbol = subjectType.getProperty(fieldName);
|
|
2989
|
+
if (propertySymbol === void 0) {
|
|
2990
|
+
return null;
|
|
2991
|
+
}
|
|
2992
|
+
const declaration = propertySymbol.valueDeclaration ?? propertySymbol.declarations?.find(
|
|
2993
|
+
(candidate) => ts3.isPropertyDeclaration(candidate) || ts3.isPropertySignature(candidate)
|
|
2994
|
+
) ?? propertySymbol.declarations?.[0];
|
|
2995
|
+
return {
|
|
2996
|
+
declaration,
|
|
2997
|
+
type: checker.getTypeOfSymbolAtLocation(propertySymbol, declaration ?? node),
|
|
2998
|
+
optional: !!(propertySymbol.flags & ts3.SymbolFlags.Optional) || declaration !== void 0 && "questionToken" in declaration && declaration.questionToken !== void 0
|
|
2999
|
+
};
|
|
3000
|
+
}
|
|
3001
|
+
function isLocalTypeParameterName(node, typeParameterName) {
|
|
3002
|
+
return node.typeParameters?.some((typeParameter) => typeParameter.name.text === typeParameterName) ?? false;
|
|
3003
|
+
}
|
|
3004
|
+
function isNullishSemanticType(type) {
|
|
3005
|
+
if (type.flags & (ts3.TypeFlags.Null | ts3.TypeFlags.Undefined | ts3.TypeFlags.Void | ts3.TypeFlags.Unknown | ts3.TypeFlags.Any)) {
|
|
3006
|
+
return true;
|
|
3007
|
+
}
|
|
3008
|
+
return type.isUnion() && type.types.some((member) => isNullishSemanticType(member));
|
|
3009
|
+
}
|
|
3010
|
+
function isStringLikeSemanticType(type) {
|
|
3011
|
+
if (type.flags & ts3.TypeFlags.StringLike) {
|
|
3012
|
+
return true;
|
|
3013
|
+
}
|
|
3014
|
+
if (type.isUnion()) {
|
|
3015
|
+
return type.types.length > 0 && type.types.every((member) => isStringLikeSemanticType(member));
|
|
3016
|
+
}
|
|
3017
|
+
return false;
|
|
3018
|
+
}
|
|
3019
|
+
function extractDiscriminatorDirective(node, file, diagnostics) {
|
|
3020
|
+
const discriminatorTags = getLeadingParsedTags(node).filter(
|
|
3021
|
+
(tag) => tag.normalizedTagName === "discriminator"
|
|
3022
|
+
);
|
|
3023
|
+
if (discriminatorTags.length === 0) {
|
|
3024
|
+
return null;
|
|
3025
|
+
}
|
|
3026
|
+
const [firstTag, ...duplicateTags] = discriminatorTags;
|
|
3027
|
+
for (const _duplicateTag of duplicateTags) {
|
|
3028
|
+
diagnostics.push(
|
|
3029
|
+
makeAnalysisDiagnostic(
|
|
3030
|
+
"DUPLICATE_TAG",
|
|
3031
|
+
'Duplicate "@discriminator" tag. Only one discriminator declaration is allowed per declaration.',
|
|
3032
|
+
provenanceForNode(node, file)
|
|
3033
|
+
)
|
|
3034
|
+
);
|
|
3035
|
+
}
|
|
3036
|
+
if (firstTag === void 0) {
|
|
3037
|
+
return null;
|
|
3038
|
+
}
|
|
3039
|
+
const firstTarget = firstTag.target;
|
|
3040
|
+
if (firstTarget?.path === null || firstTarget?.valid !== true) {
|
|
3041
|
+
diagnostics.push(
|
|
3042
|
+
makeAnalysisDiagnostic(
|
|
3043
|
+
"INVALID_TAG_ARGUMENT",
|
|
3044
|
+
'Tag "@discriminator" requires a direct path target like ":kind".',
|
|
3045
|
+
provenanceForNode(node, file)
|
|
3046
|
+
)
|
|
3047
|
+
);
|
|
3048
|
+
return null;
|
|
3049
|
+
}
|
|
3050
|
+
if (firstTarget.path.segments.length !== 1) {
|
|
3051
|
+
diagnostics.push(
|
|
3052
|
+
makeAnalysisDiagnostic(
|
|
3053
|
+
"INVALID_TAG_ARGUMENT",
|
|
3054
|
+
'Tag "@discriminator" only supports direct property targets in v1; nested paths are out of scope.',
|
|
3055
|
+
provenanceForNode(node, file)
|
|
3056
|
+
)
|
|
3057
|
+
);
|
|
3058
|
+
return null;
|
|
3059
|
+
}
|
|
3060
|
+
const typeParameterName = firstTag.argumentText.trim();
|
|
3061
|
+
if (!/^[A-Za-z_$][\w$]*$/u.test(typeParameterName)) {
|
|
3062
|
+
diagnostics.push(
|
|
3063
|
+
makeAnalysisDiagnostic(
|
|
3064
|
+
"INVALID_TAG_ARGUMENT",
|
|
3065
|
+
'Tag "@discriminator" requires a local type parameter name as its source operand.',
|
|
3066
|
+
provenanceForNode(node, file)
|
|
3067
|
+
)
|
|
3068
|
+
);
|
|
3069
|
+
return null;
|
|
3070
|
+
}
|
|
3071
|
+
return {
|
|
3072
|
+
fieldName: firstTarget.path.segments[0] ?? firstTarget.rawText,
|
|
3073
|
+
typeParameterName,
|
|
3074
|
+
provenance: provenanceForNode(node, file)
|
|
3075
|
+
};
|
|
3076
|
+
}
|
|
3077
|
+
function validateDiscriminatorDirective(node, checker, file, diagnostics) {
|
|
3078
|
+
const directive = extractDiscriminatorDirective(node, file, diagnostics);
|
|
3079
|
+
if (directive === null) {
|
|
3080
|
+
return null;
|
|
3081
|
+
}
|
|
3082
|
+
if (!isLocalTypeParameterName(node, directive.typeParameterName)) {
|
|
3083
|
+
diagnostics.push(
|
|
3084
|
+
makeAnalysisDiagnostic(
|
|
3085
|
+
"INVALID_TAG_ARGUMENT",
|
|
3086
|
+
`Tag "@discriminator" references "${directive.typeParameterName}", but the source operand must be a type parameter declared on the same declaration.`,
|
|
3087
|
+
directive.provenance
|
|
3088
|
+
)
|
|
3089
|
+
);
|
|
3090
|
+
return null;
|
|
3091
|
+
}
|
|
3092
|
+
const property = resolveDiscriminatorProperty(node, checker, directive.fieldName);
|
|
3093
|
+
if (property === null) {
|
|
3094
|
+
diagnostics.push(
|
|
3095
|
+
makeAnalysisDiagnostic(
|
|
3096
|
+
"UNKNOWN_PATH_TARGET",
|
|
3097
|
+
`Tag "@discriminator" targets "${directive.fieldName}", but no direct property with that name exists on this declaration.`,
|
|
3098
|
+
directive.provenance
|
|
3099
|
+
)
|
|
3100
|
+
);
|
|
3101
|
+
return null;
|
|
3102
|
+
}
|
|
3103
|
+
if (property.optional) {
|
|
3104
|
+
diagnostics.push(
|
|
3105
|
+
makeAnalysisDiagnostic(
|
|
3106
|
+
"TYPE_MISMATCH",
|
|
3107
|
+
`Discriminator field "${directive.fieldName}" must be required; optional discriminator fields are not supported.`,
|
|
3108
|
+
directive.provenance,
|
|
3109
|
+
property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
|
|
3110
|
+
)
|
|
3111
|
+
);
|
|
3112
|
+
return null;
|
|
3113
|
+
}
|
|
3114
|
+
if (isNullishSemanticType(property.type)) {
|
|
3115
|
+
diagnostics.push(
|
|
3116
|
+
makeAnalysisDiagnostic(
|
|
3117
|
+
"TYPE_MISMATCH",
|
|
3118
|
+
`Discriminator field "${directive.fieldName}" must not be nullable.`,
|
|
3119
|
+
directive.provenance,
|
|
3120
|
+
property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
|
|
3121
|
+
)
|
|
3122
|
+
);
|
|
3123
|
+
return null;
|
|
3124
|
+
}
|
|
3125
|
+
if (!isStringLikeSemanticType(property.type)) {
|
|
3126
|
+
diagnostics.push(
|
|
3127
|
+
makeAnalysisDiagnostic(
|
|
3128
|
+
"TYPE_MISMATCH",
|
|
3129
|
+
`Discriminator field "${directive.fieldName}" must be string-like.`,
|
|
3130
|
+
directive.provenance,
|
|
3131
|
+
property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
|
|
3132
|
+
)
|
|
3133
|
+
);
|
|
3134
|
+
return null;
|
|
3135
|
+
}
|
|
3136
|
+
return directive;
|
|
3137
|
+
}
|
|
3138
|
+
function getConcreteTypeArgumentForDiscriminator(node, subjectType, checker, typeParameterName) {
|
|
3139
|
+
const typeParameterIndex = node.typeParameters?.findIndex(
|
|
3140
|
+
(typeParameter) => typeParameter.name.text === typeParameterName
|
|
3141
|
+
) ?? -1;
|
|
3142
|
+
if (typeParameterIndex < 0) {
|
|
3143
|
+
return null;
|
|
3144
|
+
}
|
|
3145
|
+
const referenceTypeArguments = (isTypeReference(subjectType) ? subjectType.typeArguments : void 0) ?? subjectType.aliasTypeArguments;
|
|
3146
|
+
if (referenceTypeArguments?.[typeParameterIndex] !== void 0) {
|
|
3147
|
+
return referenceTypeArguments[typeParameterIndex] ?? null;
|
|
3148
|
+
}
|
|
3149
|
+
const localTypeParameter = node.typeParameters?.[typeParameterIndex];
|
|
3150
|
+
return localTypeParameter === void 0 ? null : checker.getTypeAtLocation(localTypeParameter);
|
|
3151
|
+
}
|
|
3152
|
+
function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker, provenance, diagnostics) {
|
|
3153
|
+
const propertySymbol = boundType.getProperty(fieldName);
|
|
3154
|
+
if (propertySymbol === void 0) {
|
|
3155
|
+
return void 0;
|
|
3156
|
+
}
|
|
3157
|
+
const declaration = propertySymbol.valueDeclaration ?? propertySymbol.declarations?.[0];
|
|
3158
|
+
const anchorNode = declaration ?? boundType.symbol.declarations?.[0] ?? null;
|
|
3159
|
+
const resolvedAnchorNode = anchorNode ?? resolveNamedDiscriminatorDeclaration(boundType, checker);
|
|
3160
|
+
if (resolvedAnchorNode === null) {
|
|
3161
|
+
return void 0;
|
|
3162
|
+
}
|
|
3163
|
+
const propertyType = checker.getTypeOfSymbolAtLocation(
|
|
3164
|
+
propertySymbol,
|
|
3165
|
+
resolvedAnchorNode
|
|
3166
|
+
);
|
|
3167
|
+
if (propertyType.isStringLiteral()) {
|
|
3168
|
+
return propertyType.value;
|
|
3169
|
+
}
|
|
3170
|
+
if (propertyType.isUnion()) {
|
|
3171
|
+
const nonNullMembers = propertyType.types.filter(
|
|
3172
|
+
(member) => !(member.flags & (ts3.TypeFlags.Null | ts3.TypeFlags.Undefined))
|
|
3173
|
+
);
|
|
3174
|
+
if (nonNullMembers.length > 0 && nonNullMembers.every((member) => member.isStringLiteral())) {
|
|
3175
|
+
diagnostics.push(
|
|
3176
|
+
makeAnalysisDiagnostic(
|
|
3177
|
+
"INVALID_TAG_ARGUMENT",
|
|
3178
|
+
"Discriminator resolution for union-valued identity properties is out of scope for v1.",
|
|
3179
|
+
provenance
|
|
3180
|
+
)
|
|
3181
|
+
);
|
|
3182
|
+
return null;
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
return void 0;
|
|
3186
|
+
}
|
|
3187
|
+
function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
|
|
3188
|
+
const declaration = resolveNamedDiscriminatorDeclaration(boundType, checker);
|
|
3189
|
+
if (declaration === null) {
|
|
3190
|
+
return void 0;
|
|
3191
|
+
}
|
|
3192
|
+
const metadata = resolveNodeMetadata(
|
|
3193
|
+
metadataPolicy,
|
|
3194
|
+
"type",
|
|
3195
|
+
getDiscriminatorLogicalName(boundType, declaration, checker),
|
|
3196
|
+
declaration,
|
|
3197
|
+
{
|
|
3198
|
+
checker,
|
|
3199
|
+
declaration,
|
|
3200
|
+
subjectType: boundType
|
|
3201
|
+
}
|
|
3202
|
+
);
|
|
3203
|
+
return metadata?.apiName;
|
|
3204
|
+
}
|
|
3205
|
+
function resolveNamedDiscriminatorDeclaration(type, checker, seen = /* @__PURE__ */ new Set()) {
|
|
3206
|
+
if (seen.has(type)) {
|
|
3207
|
+
return null;
|
|
3208
|
+
}
|
|
3209
|
+
seen.add(type);
|
|
3210
|
+
const symbol = type.aliasSymbol ?? type.getSymbol();
|
|
3211
|
+
if (symbol !== void 0) {
|
|
3212
|
+
const aliased = symbol.flags & ts3.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : void 0;
|
|
3213
|
+
const targetSymbol = aliased ?? symbol;
|
|
3214
|
+
const declaration = targetSymbol.declarations?.find(
|
|
3215
|
+
(candidate) => ts3.isClassDeclaration(candidate) || ts3.isInterfaceDeclaration(candidate) || ts3.isTypeAliasDeclaration(candidate) || ts3.isEnumDeclaration(candidate)
|
|
3216
|
+
);
|
|
3217
|
+
if (declaration !== void 0) {
|
|
3218
|
+
if (ts3.isTypeAliasDeclaration(declaration) && ts3.isTypeReferenceNode(declaration.type) && checker.getTypeFromTypeNode(declaration.type) !== type) {
|
|
3219
|
+
return resolveNamedDiscriminatorDeclaration(
|
|
3220
|
+
checker.getTypeFromTypeNode(declaration.type),
|
|
3221
|
+
checker,
|
|
3222
|
+
seen
|
|
3223
|
+
);
|
|
3224
|
+
}
|
|
3225
|
+
return declaration;
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
return null;
|
|
3229
|
+
}
|
|
3230
|
+
function resolveDiscriminatorValue(boundType, fieldName, checker, provenance, diagnostics, metadataPolicy) {
|
|
3231
|
+
if (boundType === null) {
|
|
3232
|
+
diagnostics.push(
|
|
3233
|
+
makeAnalysisDiagnostic(
|
|
3234
|
+
"INVALID_TAG_ARGUMENT",
|
|
3235
|
+
"Discriminator resolution failed because no concrete type argument is available for the referenced type parameter.",
|
|
3236
|
+
provenance
|
|
3237
|
+
)
|
|
3238
|
+
);
|
|
3239
|
+
return null;
|
|
3240
|
+
}
|
|
3241
|
+
if (boundType.isStringLiteral()) {
|
|
3242
|
+
return boundType.value;
|
|
3243
|
+
}
|
|
3244
|
+
if (boundType.isUnion()) {
|
|
3245
|
+
const nonNullMembers = boundType.types.filter(
|
|
3246
|
+
(member) => !(member.flags & (ts3.TypeFlags.Null | ts3.TypeFlags.Undefined))
|
|
3247
|
+
);
|
|
3248
|
+
if (nonNullMembers.every((member) => member.isStringLiteral())) {
|
|
3249
|
+
diagnostics.push(
|
|
3250
|
+
makeAnalysisDiagnostic(
|
|
3251
|
+
"INVALID_TAG_ARGUMENT",
|
|
3252
|
+
"Discriminator resolution for unions of string literals is out of scope for v1.",
|
|
3253
|
+
provenance
|
|
3254
|
+
)
|
|
3255
|
+
);
|
|
3256
|
+
return null;
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
const literalIdentityValue = resolveLiteralDiscriminatorPropertyValue(
|
|
3260
|
+
boundType,
|
|
3261
|
+
fieldName,
|
|
3262
|
+
checker,
|
|
3263
|
+
provenance,
|
|
3264
|
+
diagnostics
|
|
3265
|
+
);
|
|
3266
|
+
if (literalIdentityValue !== void 0) {
|
|
3267
|
+
return literalIdentityValue;
|
|
3268
|
+
}
|
|
3269
|
+
const apiName = resolveDiscriminatorApiName(boundType, checker, metadataPolicy);
|
|
3270
|
+
if (apiName?.source === "explicit") {
|
|
3271
|
+
return apiName.value;
|
|
3272
|
+
}
|
|
3273
|
+
if (apiName?.source === "inferred") {
|
|
3274
|
+
return apiName.value;
|
|
3275
|
+
}
|
|
3276
|
+
diagnostics.push(
|
|
3277
|
+
makeAnalysisDiagnostic(
|
|
3278
|
+
"INVALID_TAG_ARGUMENT",
|
|
3279
|
+
"Discriminator resolution could not derive a JSON-facing discriminator value from the referenced type argument.",
|
|
3280
|
+
provenance
|
|
3281
|
+
)
|
|
3282
|
+
);
|
|
3283
|
+
return null;
|
|
3284
|
+
}
|
|
3285
|
+
function getDeclarationName(node) {
|
|
3286
|
+
if (ts3.isClassDeclaration(node) || ts3.isInterfaceDeclaration(node) || ts3.isTypeAliasDeclaration(node) || ts3.isEnumDeclaration(node)) {
|
|
3287
|
+
return node.name?.text ?? "anonymous";
|
|
3288
|
+
}
|
|
3289
|
+
return "anonymous";
|
|
3290
|
+
}
|
|
3291
|
+
function getResolvedTypeArguments(type) {
|
|
3292
|
+
return (isTypeReference(type) ? type.typeArguments : void 0) ?? type.aliasTypeArguments ?? [];
|
|
3293
|
+
}
|
|
3294
|
+
function getDiscriminatorLogicalName(type, declaration, checker) {
|
|
3295
|
+
const baseName = getDeclarationName(declaration);
|
|
3296
|
+
const typeArguments = getResolvedTypeArguments(type);
|
|
3297
|
+
return typeArguments.length === 0 ? baseName : buildInstantiatedReferenceName(baseName, typeArguments, checker);
|
|
3298
|
+
}
|
|
3299
|
+
function applyDeclarationDiscriminatorToFields(fields, node, subjectType, checker, file, diagnostics, metadataPolicy) {
|
|
3300
|
+
const directive = validateDiscriminatorDirective(node, checker, file, diagnostics);
|
|
3301
|
+
if (directive === null) {
|
|
3302
|
+
return [...fields];
|
|
3303
|
+
}
|
|
3304
|
+
const discriminatorValue = resolveDiscriminatorValue(
|
|
3305
|
+
getConcreteTypeArgumentForDiscriminator(
|
|
3306
|
+
node,
|
|
3307
|
+
subjectType,
|
|
3308
|
+
checker,
|
|
3309
|
+
directive.typeParameterName
|
|
3310
|
+
),
|
|
3311
|
+
directive.fieldName,
|
|
3312
|
+
checker,
|
|
3313
|
+
directive.provenance,
|
|
3314
|
+
diagnostics,
|
|
3315
|
+
metadataPolicy
|
|
3316
|
+
);
|
|
3317
|
+
if (discriminatorValue === null) {
|
|
3318
|
+
return [...fields];
|
|
3319
|
+
}
|
|
3320
|
+
return fields.map(
|
|
3321
|
+
(field) => field.name === directive.fieldName ? {
|
|
3322
|
+
...field,
|
|
3323
|
+
type: {
|
|
3324
|
+
kind: "enum",
|
|
3325
|
+
members: [{ value: discriminatorValue }]
|
|
3326
|
+
}
|
|
3327
|
+
} : field
|
|
3328
|
+
);
|
|
3329
|
+
}
|
|
3330
|
+
function buildInstantiatedReferenceName(baseName, typeArguments, checker) {
|
|
3331
|
+
const renderedArguments = typeArguments.map(
|
|
3332
|
+
(typeArgument) => checker.typeToString(typeArgument).replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "")
|
|
3333
|
+
).filter((value) => value !== "");
|
|
3334
|
+
return renderedArguments.length === 0 ? baseName : `${baseName}__${renderedArguments.join("__")}`;
|
|
3335
|
+
}
|
|
3336
|
+
function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy, extensionRegistry, diagnostics) {
|
|
3337
|
+
const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
|
|
3338
|
+
if (typeNode === void 0) {
|
|
3339
|
+
return [];
|
|
3340
|
+
}
|
|
3341
|
+
const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
|
|
3342
|
+
if (!ts3.isTypeReferenceNode(resolvedTypeNode) || resolvedTypeNode.typeArguments === void 0) {
|
|
3343
|
+
return [];
|
|
3344
|
+
}
|
|
3345
|
+
return resolvedTypeNode.typeArguments.map((argumentNode) => {
|
|
3346
|
+
const argumentType = checker.getTypeFromTypeNode(argumentNode);
|
|
3347
|
+
return {
|
|
3348
|
+
tsType: argumentType,
|
|
3349
|
+
typeNode: resolveTypeNode(
|
|
3350
|
+
argumentType,
|
|
3351
|
+
checker,
|
|
3352
|
+
file,
|
|
3353
|
+
typeRegistry,
|
|
3354
|
+
visiting,
|
|
3355
|
+
argumentNode,
|
|
3356
|
+
metadataPolicy,
|
|
3357
|
+
extensionRegistry,
|
|
3358
|
+
diagnostics
|
|
3359
|
+
)
|
|
3360
|
+
};
|
|
3361
|
+
});
|
|
3362
|
+
}
|
|
3363
|
+
function applyDiscriminatorToObjectProperties(properties, node, subjectType, checker, file, diagnostics, metadataPolicy) {
|
|
3364
|
+
const directive = validateDiscriminatorDirective(node, checker, file, diagnostics);
|
|
3365
|
+
if (directive === null) {
|
|
3366
|
+
return properties;
|
|
3367
|
+
}
|
|
3368
|
+
const discriminatorValue = resolveDiscriminatorValue(
|
|
3369
|
+
getConcreteTypeArgumentForDiscriminator(
|
|
3370
|
+
node,
|
|
3371
|
+
subjectType,
|
|
3372
|
+
checker,
|
|
3373
|
+
directive.typeParameterName
|
|
3374
|
+
),
|
|
3375
|
+
directive.fieldName,
|
|
3376
|
+
checker,
|
|
3377
|
+
directive.provenance,
|
|
3378
|
+
diagnostics,
|
|
3379
|
+
metadataPolicy
|
|
3380
|
+
);
|
|
3381
|
+
if (discriminatorValue === null) {
|
|
3382
|
+
return properties;
|
|
3383
|
+
}
|
|
3384
|
+
return properties.map(
|
|
3385
|
+
(property) => property.name === directive.fieldName ? {
|
|
3386
|
+
...property,
|
|
3387
|
+
type: {
|
|
3388
|
+
kind: "enum",
|
|
3389
|
+
members: [{ value: discriminatorValue }]
|
|
3390
|
+
}
|
|
3391
|
+
} : property
|
|
3392
|
+
);
|
|
3393
|
+
}
|
|
3394
|
+
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, metadataPolicy, extensionRegistry) {
|
|
2271
3395
|
if (!ts3.isIdentifier(prop.name)) {
|
|
2272
3396
|
return null;
|
|
2273
3397
|
}
|
|
@@ -2282,6 +3406,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
|
|
|
2282
3406
|
typeRegistry,
|
|
2283
3407
|
visiting,
|
|
2284
3408
|
prop,
|
|
3409
|
+
metadataPolicy,
|
|
2285
3410
|
extensionRegistry,
|
|
2286
3411
|
diagnostics
|
|
2287
3412
|
);
|
|
@@ -2305,9 +3430,16 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
|
|
|
2305
3430
|
annotations.push(defaultAnnotation);
|
|
2306
3431
|
}
|
|
2307
3432
|
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
3433
|
+
const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
|
|
3434
|
+
checker,
|
|
3435
|
+
declaration: prop,
|
|
3436
|
+
subjectType: tsType,
|
|
3437
|
+
hostType
|
|
3438
|
+
});
|
|
2308
3439
|
return {
|
|
2309
3440
|
kind: "field",
|
|
2310
3441
|
name,
|
|
3442
|
+
...metadata !== void 0 && { metadata },
|
|
2311
3443
|
type,
|
|
2312
3444
|
required: !optional,
|
|
2313
3445
|
constraints,
|
|
@@ -2315,7 +3447,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
|
|
|
2315
3447
|
provenance
|
|
2316
3448
|
};
|
|
2317
3449
|
}
|
|
2318
|
-
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, extensionRegistry) {
|
|
3450
|
+
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, metadataPolicy, extensionRegistry) {
|
|
2319
3451
|
if (!ts3.isIdentifier(prop.name)) {
|
|
2320
3452
|
return null;
|
|
2321
3453
|
}
|
|
@@ -2330,6 +3462,7 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
2330
3462
|
typeRegistry,
|
|
2331
3463
|
visiting,
|
|
2332
3464
|
prop,
|
|
3465
|
+
metadataPolicy,
|
|
2333
3466
|
extensionRegistry,
|
|
2334
3467
|
diagnostics
|
|
2335
3468
|
);
|
|
@@ -2349,9 +3482,16 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
2349
3482
|
let annotations = [];
|
|
2350
3483
|
annotations.push(...docResult.annotations);
|
|
2351
3484
|
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
3485
|
+
const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
|
|
3486
|
+
checker,
|
|
3487
|
+
declaration: prop,
|
|
3488
|
+
subjectType: tsType,
|
|
3489
|
+
hostType
|
|
3490
|
+
});
|
|
2352
3491
|
return {
|
|
2353
3492
|
kind: "field",
|
|
2354
3493
|
name,
|
|
3494
|
+
...metadata !== void 0 && { metadata },
|
|
2355
3495
|
type,
|
|
2356
3496
|
required: !optional,
|
|
2357
3497
|
constraints,
|
|
@@ -2476,7 +3616,7 @@ function getTypeNodeRegistrationName(typeNode) {
|
|
|
2476
3616
|
}
|
|
2477
3617
|
return null;
|
|
2478
3618
|
}
|
|
2479
|
-
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
3619
|
+
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2480
3620
|
const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
|
|
2481
3621
|
if (customType) {
|
|
2482
3622
|
return customType;
|
|
@@ -2488,6 +3628,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2488
3628
|
typeRegistry,
|
|
2489
3629
|
visiting,
|
|
2490
3630
|
sourceNode,
|
|
3631
|
+
metadataPolicy,
|
|
2491
3632
|
extensionRegistry,
|
|
2492
3633
|
diagnostics
|
|
2493
3634
|
);
|
|
@@ -2532,6 +3673,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2532
3673
|
typeRegistry,
|
|
2533
3674
|
visiting,
|
|
2534
3675
|
sourceNode,
|
|
3676
|
+
metadataPolicy,
|
|
2535
3677
|
extensionRegistry,
|
|
2536
3678
|
diagnostics
|
|
2537
3679
|
);
|
|
@@ -2544,6 +3686,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2544
3686
|
typeRegistry,
|
|
2545
3687
|
visiting,
|
|
2546
3688
|
sourceNode,
|
|
3689
|
+
metadataPolicy,
|
|
2547
3690
|
extensionRegistry,
|
|
2548
3691
|
diagnostics
|
|
2549
3692
|
);
|
|
@@ -2555,13 +3698,15 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2555
3698
|
file,
|
|
2556
3699
|
typeRegistry,
|
|
2557
3700
|
visiting,
|
|
3701
|
+
sourceNode,
|
|
3702
|
+
metadataPolicy,
|
|
2558
3703
|
extensionRegistry,
|
|
2559
3704
|
diagnostics
|
|
2560
3705
|
);
|
|
2561
3706
|
}
|
|
2562
3707
|
return { kind: "primitive", primitiveKind: "string" };
|
|
2563
3708
|
}
|
|
2564
|
-
function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
3709
|
+
function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2565
3710
|
if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
|
|
2566
3711
|
return null;
|
|
2567
3712
|
}
|
|
@@ -2581,14 +3726,21 @@ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiti
|
|
|
2581
3726
|
file,
|
|
2582
3727
|
makeParseOptions(extensionRegistry)
|
|
2583
3728
|
);
|
|
3729
|
+
const metadata = resolveNodeMetadata(metadataPolicy, "type", aliasName, aliasDecl, {
|
|
3730
|
+
checker,
|
|
3731
|
+
declaration: aliasDecl,
|
|
3732
|
+
subjectType: aliasType
|
|
3733
|
+
});
|
|
2584
3734
|
typeRegistry[aliasName] = {
|
|
2585
3735
|
name: aliasName,
|
|
3736
|
+
...metadata !== void 0 && { metadata },
|
|
2586
3737
|
type: resolveAliasedPrimitiveTarget(
|
|
2587
3738
|
aliasType,
|
|
2588
3739
|
checker,
|
|
2589
3740
|
file,
|
|
2590
3741
|
typeRegistry,
|
|
2591
3742
|
visiting,
|
|
3743
|
+
metadataPolicy,
|
|
2592
3744
|
extensionRegistry,
|
|
2593
3745
|
diagnostics
|
|
2594
3746
|
),
|
|
@@ -2617,7 +3769,7 @@ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
|
|
|
2617
3769
|
const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
|
|
2618
3770
|
return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
|
|
2619
3771
|
}
|
|
2620
|
-
function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, extensionRegistry, diagnostics) {
|
|
3772
|
+
function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2621
3773
|
const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
|
|
2622
3774
|
if (nestedAliasDecl !== void 0) {
|
|
2623
3775
|
return resolveAliasedPrimitiveTarget(
|
|
@@ -2626,6 +3778,7 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
|
|
|
2626
3778
|
file,
|
|
2627
3779
|
typeRegistry,
|
|
2628
3780
|
visiting,
|
|
3781
|
+
metadataPolicy,
|
|
2629
3782
|
extensionRegistry,
|
|
2630
3783
|
diagnostics
|
|
2631
3784
|
);
|
|
@@ -2637,11 +3790,12 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
|
|
|
2637
3790
|
typeRegistry,
|
|
2638
3791
|
visiting,
|
|
2639
3792
|
void 0,
|
|
3793
|
+
metadataPolicy,
|
|
2640
3794
|
extensionRegistry,
|
|
2641
3795
|
diagnostics
|
|
2642
3796
|
);
|
|
2643
3797
|
}
|
|
2644
|
-
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
3798
|
+
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2645
3799
|
const typeName = getNamedTypeName(type);
|
|
2646
3800
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
2647
3801
|
if (typeName && typeName in typeRegistry) {
|
|
@@ -2676,8 +3830,14 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2676
3830
|
return result;
|
|
2677
3831
|
}
|
|
2678
3832
|
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
3833
|
+
const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", typeName, namedDecl, {
|
|
3834
|
+
checker,
|
|
3835
|
+
declaration: namedDecl,
|
|
3836
|
+
subjectType: type
|
|
3837
|
+
}) : void 0;
|
|
2679
3838
|
typeRegistry[typeName] = {
|
|
2680
3839
|
name: typeName,
|
|
3840
|
+
...metadata !== void 0 && { metadata },
|
|
2681
3841
|
type: result,
|
|
2682
3842
|
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2683
3843
|
provenance: provenanceForDeclaration(namedDecl ?? sourceNode, file)
|
|
@@ -2731,6 +3891,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2731
3891
|
typeRegistry,
|
|
2732
3892
|
visiting,
|
|
2733
3893
|
nonNullMembers[0].sourceNode ?? sourceNode,
|
|
3894
|
+
metadataPolicy,
|
|
2734
3895
|
extensionRegistry,
|
|
2735
3896
|
diagnostics
|
|
2736
3897
|
);
|
|
@@ -2748,6 +3909,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2748
3909
|
typeRegistry,
|
|
2749
3910
|
visiting,
|
|
2750
3911
|
memberSourceNode ?? sourceNode,
|
|
3912
|
+
metadataPolicy,
|
|
2751
3913
|
extensionRegistry,
|
|
2752
3914
|
diagnostics
|
|
2753
3915
|
)
|
|
@@ -2757,7 +3919,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2757
3919
|
}
|
|
2758
3920
|
return registerNamed({ kind: "union", members });
|
|
2759
3921
|
}
|
|
2760
|
-
function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
|
|
3922
|
+
function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2761
3923
|
const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
|
|
2762
3924
|
const elementType = typeArgs?.[0];
|
|
2763
3925
|
const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
|
|
@@ -2768,12 +3930,13 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2768
3930
|
typeRegistry,
|
|
2769
3931
|
visiting,
|
|
2770
3932
|
elementSourceNode,
|
|
3933
|
+
metadataPolicy,
|
|
2771
3934
|
extensionRegistry,
|
|
2772
3935
|
diagnostics
|
|
2773
3936
|
) : { kind: "primitive", primitiveKind: "string" };
|
|
2774
3937
|
return { kind: "array", items };
|
|
2775
3938
|
}
|
|
2776
|
-
function tryResolveRecordType(type, checker, file, typeRegistry, visiting, extensionRegistry, diagnostics) {
|
|
3939
|
+
function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
2777
3940
|
if (type.getProperties().length > 0) {
|
|
2778
3941
|
return null;
|
|
2779
3942
|
}
|
|
@@ -2788,6 +3951,7 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, exten
|
|
|
2788
3951
|
typeRegistry,
|
|
2789
3952
|
visiting,
|
|
2790
3953
|
void 0,
|
|
3954
|
+
metadataPolicy,
|
|
2791
3955
|
extensionRegistry,
|
|
2792
3956
|
diagnostics
|
|
2793
3957
|
);
|
|
@@ -2818,35 +3982,76 @@ function typeNodeContainsReference(type, targetName) {
|
|
|
2818
3982
|
}
|
|
2819
3983
|
}
|
|
2820
3984
|
}
|
|
2821
|
-
function
|
|
3985
|
+
function shouldEmitResolvedObjectProperty(property, declaration) {
|
|
3986
|
+
if (property.name.startsWith("__@")) {
|
|
3987
|
+
return false;
|
|
3988
|
+
}
|
|
3989
|
+
if (declaration !== void 0 && "name" in declaration && declaration.name !== void 0) {
|
|
3990
|
+
const name = declaration.name;
|
|
3991
|
+
if (ts3.isComputedPropertyName(name) || ts3.isPrivateIdentifier(name)) {
|
|
3992
|
+
return false;
|
|
3993
|
+
}
|
|
3994
|
+
if (!ts3.isIdentifier(name) && !ts3.isStringLiteral(name) && !ts3.isNumericLiteral(name)) {
|
|
3995
|
+
return false;
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
return true;
|
|
3999
|
+
}
|
|
4000
|
+
function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
|
|
4001
|
+
const collectedDiagnostics = diagnostics ?? [];
|
|
2822
4002
|
const typeName = getNamedTypeName(type);
|
|
2823
4003
|
const namedTypeName = typeName ?? void 0;
|
|
2824
4004
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
2825
|
-
const
|
|
4005
|
+
const referenceTypeArguments = extractReferenceTypeArguments(
|
|
4006
|
+
type,
|
|
4007
|
+
checker,
|
|
4008
|
+
file,
|
|
4009
|
+
typeRegistry,
|
|
4010
|
+
visiting,
|
|
4011
|
+
sourceNode,
|
|
4012
|
+
metadataPolicy,
|
|
4013
|
+
extensionRegistry,
|
|
4014
|
+
collectedDiagnostics
|
|
4015
|
+
);
|
|
4016
|
+
const instantiatedTypeName = namedTypeName !== void 0 && referenceTypeArguments.length > 0 ? buildInstantiatedReferenceName(
|
|
4017
|
+
namedTypeName,
|
|
4018
|
+
referenceTypeArguments.map((argument) => argument.tsType),
|
|
4019
|
+
checker
|
|
4020
|
+
) : void 0;
|
|
4021
|
+
const registryTypeName = instantiatedTypeName ?? namedTypeName;
|
|
4022
|
+
const shouldRegisterNamedType = registryTypeName !== void 0 && !(registryTypeName === "Record" && namedDecl?.getSourceFile().fileName !== file);
|
|
2826
4023
|
const clearNamedTypeRegistration = () => {
|
|
2827
|
-
if (
|
|
4024
|
+
if (registryTypeName === void 0 || !shouldRegisterNamedType) {
|
|
2828
4025
|
return;
|
|
2829
4026
|
}
|
|
2830
|
-
Reflect.deleteProperty(typeRegistry,
|
|
4027
|
+
Reflect.deleteProperty(typeRegistry, registryTypeName);
|
|
2831
4028
|
};
|
|
2832
4029
|
if (visiting.has(type)) {
|
|
2833
|
-
if (
|
|
2834
|
-
return {
|
|
4030
|
+
if (registryTypeName !== void 0 && shouldRegisterNamedType) {
|
|
4031
|
+
return {
|
|
4032
|
+
kind: "reference",
|
|
4033
|
+
name: registryTypeName,
|
|
4034
|
+
typeArguments: referenceTypeArguments.map((argument) => argument.typeNode)
|
|
4035
|
+
};
|
|
2835
4036
|
}
|
|
2836
4037
|
return { kind: "object", properties: [], additionalProperties: false };
|
|
2837
4038
|
}
|
|
2838
|
-
if (
|
|
2839
|
-
typeRegistry[
|
|
2840
|
-
name:
|
|
4039
|
+
if (registryTypeName !== void 0 && shouldRegisterNamedType && !typeRegistry[registryTypeName]) {
|
|
4040
|
+
typeRegistry[registryTypeName] = {
|
|
4041
|
+
name: registryTypeName,
|
|
2841
4042
|
type: RESOLVING_TYPE_PLACEHOLDER,
|
|
2842
4043
|
provenance: provenanceForDeclaration(namedDecl, file)
|
|
2843
4044
|
};
|
|
2844
4045
|
}
|
|
2845
4046
|
visiting.add(type);
|
|
2846
|
-
if (
|
|
2847
|
-
if (typeRegistry[
|
|
4047
|
+
if (registryTypeName !== void 0 && shouldRegisterNamedType && typeRegistry[registryTypeName]?.type !== void 0) {
|
|
4048
|
+
if (typeRegistry[registryTypeName].type !== RESOLVING_TYPE_PLACEHOLDER) {
|
|
2848
4049
|
visiting.delete(type);
|
|
2849
|
-
return {
|
|
4050
|
+
return {
|
|
4051
|
+
kind: "reference",
|
|
4052
|
+
name: registryTypeName,
|
|
4053
|
+
typeArguments: referenceTypeArguments.map((argument) => argument.typeNode)
|
|
4054
|
+
};
|
|
2850
4055
|
}
|
|
2851
4056
|
}
|
|
2852
4057
|
const recordNode = tryResolveRecordType(
|
|
@@ -2855,25 +4060,36 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensio
|
|
|
2855
4060
|
file,
|
|
2856
4061
|
typeRegistry,
|
|
2857
4062
|
visiting,
|
|
4063
|
+
metadataPolicy,
|
|
2858
4064
|
extensionRegistry,
|
|
2859
|
-
|
|
4065
|
+
collectedDiagnostics
|
|
2860
4066
|
);
|
|
2861
4067
|
if (recordNode) {
|
|
2862
4068
|
visiting.delete(type);
|
|
2863
|
-
if (
|
|
2864
|
-
const isRecursiveRecord = typeNodeContainsReference(recordNode.valueType,
|
|
4069
|
+
if (registryTypeName !== void 0 && shouldRegisterNamedType) {
|
|
4070
|
+
const isRecursiveRecord = typeNodeContainsReference(recordNode.valueType, registryTypeName);
|
|
2865
4071
|
if (!isRecursiveRecord) {
|
|
2866
4072
|
clearNamedTypeRegistration();
|
|
2867
4073
|
return recordNode;
|
|
2868
4074
|
}
|
|
2869
4075
|
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
2870
|
-
|
|
2871
|
-
|
|
4076
|
+
const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
|
|
4077
|
+
checker,
|
|
4078
|
+
declaration: namedDecl,
|
|
4079
|
+
subjectType: type
|
|
4080
|
+
}) : void 0;
|
|
4081
|
+
typeRegistry[registryTypeName] = {
|
|
4082
|
+
name: registryTypeName,
|
|
4083
|
+
...metadata !== void 0 && { metadata },
|
|
2872
4084
|
type: recordNode,
|
|
2873
4085
|
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2874
4086
|
provenance: provenanceForDeclaration(namedDecl, file)
|
|
2875
4087
|
};
|
|
2876
|
-
return {
|
|
4088
|
+
return {
|
|
4089
|
+
kind: "reference",
|
|
4090
|
+
name: registryTypeName,
|
|
4091
|
+
typeArguments: referenceTypeArguments.map((argument) => argument.typeNode)
|
|
4092
|
+
};
|
|
2877
4093
|
}
|
|
2878
4094
|
return recordNode;
|
|
2879
4095
|
}
|
|
@@ -2884,12 +4100,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensio
|
|
|
2884
4100
|
file,
|
|
2885
4101
|
typeRegistry,
|
|
2886
4102
|
visiting,
|
|
2887
|
-
|
|
4103
|
+
metadataPolicy,
|
|
4104
|
+
collectedDiagnostics,
|
|
2888
4105
|
extensionRegistry
|
|
2889
4106
|
);
|
|
2890
4107
|
for (const prop of type.getProperties()) {
|
|
2891
4108
|
const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
2892
4109
|
if (!declaration) continue;
|
|
4110
|
+
if (!shouldEmitResolvedObjectProperty(prop, declaration)) continue;
|
|
2893
4111
|
const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
|
|
2894
4112
|
const optional = !!(prop.flags & ts3.SymbolFlags.Optional);
|
|
2895
4113
|
const propTypeNode = resolveTypeNode(
|
|
@@ -2899,12 +4117,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensio
|
|
|
2899
4117
|
typeRegistry,
|
|
2900
4118
|
visiting,
|
|
2901
4119
|
declaration,
|
|
4120
|
+
metadataPolicy,
|
|
2902
4121
|
extensionRegistry,
|
|
2903
|
-
|
|
4122
|
+
collectedDiagnostics
|
|
2904
4123
|
);
|
|
2905
4124
|
const fieldNodeInfo = fieldInfoMap?.get(prop.name);
|
|
2906
4125
|
properties.push({
|
|
2907
4126
|
name: prop.name,
|
|
4127
|
+
...fieldNodeInfo?.metadata !== void 0 && { metadata: fieldNodeInfo.metadata },
|
|
2908
4128
|
type: propTypeNode,
|
|
2909
4129
|
optional,
|
|
2910
4130
|
constraints: fieldNodeInfo?.constraints ?? [],
|
|
@@ -2915,22 +4135,40 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensio
|
|
|
2915
4135
|
visiting.delete(type);
|
|
2916
4136
|
const objectNode = {
|
|
2917
4137
|
kind: "object",
|
|
2918
|
-
properties
|
|
4138
|
+
properties: namedDecl !== void 0 && (ts3.isClassDeclaration(namedDecl) || ts3.isInterfaceDeclaration(namedDecl) || ts3.isTypeAliasDeclaration(namedDecl)) ? applyDiscriminatorToObjectProperties(
|
|
4139
|
+
properties,
|
|
4140
|
+
namedDecl,
|
|
4141
|
+
type,
|
|
4142
|
+
checker,
|
|
4143
|
+
file,
|
|
4144
|
+
collectedDiagnostics,
|
|
4145
|
+
metadataPolicy
|
|
4146
|
+
) : properties,
|
|
2919
4147
|
additionalProperties: true
|
|
2920
4148
|
};
|
|
2921
|
-
if (
|
|
4149
|
+
if (registryTypeName !== void 0 && shouldRegisterNamedType) {
|
|
2922
4150
|
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
2923
|
-
|
|
2924
|
-
|
|
4151
|
+
const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
|
|
4152
|
+
checker,
|
|
4153
|
+
declaration: namedDecl,
|
|
4154
|
+
subjectType: type
|
|
4155
|
+
}) : void 0;
|
|
4156
|
+
typeRegistry[registryTypeName] = {
|
|
4157
|
+
name: registryTypeName,
|
|
4158
|
+
...metadata !== void 0 && { metadata },
|
|
2925
4159
|
type: objectNode,
|
|
2926
4160
|
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2927
4161
|
provenance: provenanceForDeclaration(namedDecl, file)
|
|
2928
4162
|
};
|
|
2929
|
-
return {
|
|
4163
|
+
return {
|
|
4164
|
+
kind: "reference",
|
|
4165
|
+
name: registryTypeName,
|
|
4166
|
+
typeArguments: referenceTypeArguments.map((argument) => argument.typeNode)
|
|
4167
|
+
};
|
|
2930
4168
|
}
|
|
2931
4169
|
return objectNode;
|
|
2932
4170
|
}
|
|
2933
|
-
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, diagnostics, extensionRegistry) {
|
|
4171
|
+
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, metadataPolicy, diagnostics, extensionRegistry) {
|
|
2934
4172
|
const symbols = [type.getSymbol(), type.aliasSymbol].filter(
|
|
2935
4173
|
(s) => s?.declarations != null && s.declarations.length > 0
|
|
2936
4174
|
);
|
|
@@ -2951,10 +4189,12 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2951
4189
|
visiting,
|
|
2952
4190
|
diagnostics,
|
|
2953
4191
|
hostType,
|
|
4192
|
+
metadataPolicy,
|
|
2954
4193
|
extensionRegistry
|
|
2955
4194
|
);
|
|
2956
4195
|
if (fieldNode) {
|
|
2957
4196
|
map.set(fieldNode.name, {
|
|
4197
|
+
...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
|
|
2958
4198
|
constraints: [...fieldNode.constraints],
|
|
2959
4199
|
annotations: [...fieldNode.annotations],
|
|
2960
4200
|
provenance: fieldNode.provenance
|
|
@@ -2972,6 +4212,7 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2972
4212
|
file,
|
|
2973
4213
|
typeRegistry,
|
|
2974
4214
|
visiting,
|
|
4215
|
+
metadataPolicy,
|
|
2975
4216
|
checker.getTypeAtLocation(interfaceDecl),
|
|
2976
4217
|
diagnostics,
|
|
2977
4218
|
extensionRegistry
|
|
@@ -2985,6 +4226,7 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2985
4226
|
file,
|
|
2986
4227
|
typeRegistry,
|
|
2987
4228
|
visiting,
|
|
4229
|
+
metadataPolicy,
|
|
2988
4230
|
checker.getTypeAtLocation(typeAliasDecl),
|
|
2989
4231
|
diagnostics,
|
|
2990
4232
|
extensionRegistry
|
|
@@ -3036,7 +4278,7 @@ function isNullishTypeNode(typeNode) {
|
|
|
3036
4278
|
}
|
|
3037
4279
|
return ts3.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts3.SyntaxKind.NullKeyword || typeNode.literal.kind === ts3.SyntaxKind.UndefinedKeyword);
|
|
3038
4280
|
}
|
|
3039
|
-
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, hostType, diagnostics, extensionRegistry) {
|
|
4281
|
+
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, metadataPolicy, hostType, diagnostics, extensionRegistry) {
|
|
3040
4282
|
const map = /* @__PURE__ */ new Map();
|
|
3041
4283
|
for (const member of members) {
|
|
3042
4284
|
if (ts3.isPropertySignature(member)) {
|
|
@@ -3048,10 +4290,12 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, h
|
|
|
3048
4290
|
visiting,
|
|
3049
4291
|
diagnostics,
|
|
3050
4292
|
hostType,
|
|
4293
|
+
metadataPolicy,
|
|
3051
4294
|
extensionRegistry
|
|
3052
4295
|
);
|
|
3053
4296
|
if (fieldNode) {
|
|
3054
4297
|
map.set(fieldNode.name, {
|
|
4298
|
+
...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
|
|
3055
4299
|
constraints: [...fieldNode.constraints],
|
|
3056
4300
|
annotations: [...fieldNode.annotations],
|
|
3057
4301
|
provenance: fieldNode.provenance
|
|
@@ -3082,6 +4326,7 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
|
|
|
3082
4326
|
{},
|
|
3083
4327
|
/* @__PURE__ */ new Set(),
|
|
3084
4328
|
aliasDecl.type,
|
|
4329
|
+
void 0,
|
|
3085
4330
|
extensionRegistry
|
|
3086
4331
|
);
|
|
3087
4332
|
const constraints = extractJSDocConstraintNodes(
|
|
@@ -3267,19 +4512,37 @@ function findInterfaceByName(sourceFile, interfaceName) {
|
|
|
3267
4512
|
function findTypeAliasByName(sourceFile, aliasName) {
|
|
3268
4513
|
return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
|
|
3269
4514
|
}
|
|
3270
|
-
function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry) {
|
|
4515
|
+
function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy) {
|
|
3271
4516
|
const ctx = createProgramContext(filePath);
|
|
3272
|
-
return analyzeNamedTypeToIRFromProgramContext(
|
|
4517
|
+
return analyzeNamedTypeToIRFromProgramContext(
|
|
4518
|
+
ctx,
|
|
4519
|
+
filePath,
|
|
4520
|
+
typeName,
|
|
4521
|
+
extensionRegistry,
|
|
4522
|
+
metadataPolicy
|
|
4523
|
+
);
|
|
3273
4524
|
}
|
|
3274
|
-
function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry) {
|
|
4525
|
+
function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy) {
|
|
3275
4526
|
const analysisFilePath = path.resolve(filePath);
|
|
3276
4527
|
const classDecl = findClassByName(ctx.sourceFile, typeName);
|
|
3277
4528
|
if (classDecl !== null) {
|
|
3278
|
-
return analyzeClassToIR(
|
|
4529
|
+
return analyzeClassToIR(
|
|
4530
|
+
classDecl,
|
|
4531
|
+
ctx.checker,
|
|
4532
|
+
analysisFilePath,
|
|
4533
|
+
extensionRegistry,
|
|
4534
|
+
metadataPolicy
|
|
4535
|
+
);
|
|
3279
4536
|
}
|
|
3280
4537
|
const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
|
|
3281
4538
|
if (interfaceDecl !== null) {
|
|
3282
|
-
return analyzeInterfaceToIR(
|
|
4539
|
+
return analyzeInterfaceToIR(
|
|
4540
|
+
interfaceDecl,
|
|
4541
|
+
ctx.checker,
|
|
4542
|
+
analysisFilePath,
|
|
4543
|
+
extensionRegistry,
|
|
4544
|
+
metadataPolicy
|
|
4545
|
+
);
|
|
3283
4546
|
}
|
|
3284
4547
|
const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
|
|
3285
4548
|
if (typeAlias !== null) {
|
|
@@ -3287,7 +4550,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
|
|
|
3287
4550
|
typeAlias,
|
|
3288
4551
|
ctx.checker,
|
|
3289
4552
|
analysisFilePath,
|
|
3290
|
-
extensionRegistry
|
|
4553
|
+
extensionRegistry,
|
|
4554
|
+
metadataPolicy
|
|
3291
4555
|
);
|
|
3292
4556
|
if (result.ok) {
|
|
3293
4557
|
return result.analysis;
|
|
@@ -3382,7 +4646,11 @@ function generateClassSchemas(analysis, source, options) {
|
|
|
3382
4646
|
if (errorDiagnostics !== void 0 && errorDiagnostics.length > 0) {
|
|
3383
4647
|
throw new Error(formatValidationError(errorDiagnostics));
|
|
3384
4648
|
}
|
|
3385
|
-
const ir = canonicalizeTSDoc(
|
|
4649
|
+
const ir = canonicalizeTSDoc(
|
|
4650
|
+
analysis,
|
|
4651
|
+
source,
|
|
4652
|
+
options?.metadata !== void 0 ? { metadata: options.metadata } : void 0
|
|
4653
|
+
);
|
|
3386
4654
|
const validationResult = validateIR(ir, {
|
|
3387
4655
|
...options?.extensionRegistry !== void 0 && {
|
|
3388
4656
|
extensionRegistry: options.extensionRegistry
|
|
@@ -3419,13 +4687,15 @@ function generateSchemasFromClass(options) {
|
|
|
3419
4687
|
classDecl,
|
|
3420
4688
|
ctx.checker,
|
|
3421
4689
|
options.filePath,
|
|
3422
|
-
options.extensionRegistry
|
|
4690
|
+
options.extensionRegistry,
|
|
4691
|
+
options.metadata
|
|
3423
4692
|
);
|
|
3424
4693
|
return generateClassSchemas(
|
|
3425
4694
|
analysis,
|
|
3426
4695
|
{ file: options.filePath },
|
|
3427
4696
|
{
|
|
3428
4697
|
extensionRegistry: options.extensionRegistry,
|
|
4698
|
+
metadata: options.metadata,
|
|
3429
4699
|
vendorPrefix: options.vendorPrefix
|
|
3430
4700
|
}
|
|
3431
4701
|
);
|
|
@@ -3443,13 +4713,15 @@ function generateSchemasFromProgram(options) {
|
|
|
3443
4713
|
ctx,
|
|
3444
4714
|
options.filePath,
|
|
3445
4715
|
options.typeName,
|
|
3446
|
-
options.extensionRegistry
|
|
4716
|
+
options.extensionRegistry,
|
|
4717
|
+
options.metadata
|
|
3447
4718
|
);
|
|
3448
4719
|
return generateClassSchemas(
|
|
3449
4720
|
analysis,
|
|
3450
4721
|
{ file: options.filePath },
|
|
3451
4722
|
{
|
|
3452
4723
|
extensionRegistry: options.extensionRegistry,
|
|
4724
|
+
metadata: options.metadata,
|
|
3453
4725
|
vendorPrefix: options.vendorPrefix
|
|
3454
4726
|
}
|
|
3455
4727
|
);
|
|
@@ -3458,16 +4730,28 @@ function generateSchemasFromProgram(options) {
|
|
|
3458
4730
|
// src/generators/mixed-authoring.ts
|
|
3459
4731
|
function buildMixedAuthoringSchemas(options) {
|
|
3460
4732
|
const { filePath, typeName, overlays, ...schemaOptions } = options;
|
|
3461
|
-
const analysis = analyzeNamedTypeToIR(
|
|
3462
|
-
|
|
3463
|
-
|
|
4733
|
+
const analysis = analyzeNamedTypeToIR(
|
|
4734
|
+
filePath,
|
|
4735
|
+
typeName,
|
|
4736
|
+
schemaOptions.extensionRegistry,
|
|
4737
|
+
schemaOptions.metadata
|
|
4738
|
+
);
|
|
4739
|
+
const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays, schemaOptions.metadata);
|
|
4740
|
+
const ir = canonicalizeTSDoc(
|
|
4741
|
+
composedAnalysis,
|
|
4742
|
+
{ file: filePath },
|
|
4743
|
+
schemaOptions.metadata !== void 0 ? { metadata: schemaOptions.metadata } : void 0
|
|
4744
|
+
);
|
|
3464
4745
|
return {
|
|
3465
4746
|
jsonSchema: generateJsonSchemaFromIR(ir, schemaOptions),
|
|
3466
4747
|
uiSchema: generateUiSchemaFromIR(ir)
|
|
3467
4748
|
};
|
|
3468
4749
|
}
|
|
3469
|
-
function composeAnalysisWithOverlays(analysis, overlays) {
|
|
3470
|
-
const overlayIR = canonicalizeChainDSL(
|
|
4750
|
+
function composeAnalysisWithOverlays(analysis, overlays, metadata) {
|
|
4751
|
+
const overlayIR = canonicalizeChainDSL(
|
|
4752
|
+
overlays,
|
|
4753
|
+
metadata !== void 0 ? { metadata } : void 0
|
|
4754
|
+
);
|
|
3471
4755
|
const overlayFields = collectOverlayFields(overlayIR.elements);
|
|
3472
4756
|
if (overlayFields.length === 0) {
|
|
3473
4757
|
return analysis;
|
|
@@ -3523,8 +4807,10 @@ function collectOverlayFields(elements) {
|
|
|
3523
4807
|
}
|
|
3524
4808
|
function mergeFieldOverlay(baseField, overlayField, typeRegistry) {
|
|
3525
4809
|
assertSupportedOverlayField(baseField, overlayField);
|
|
4810
|
+
const metadata = mergeResolvedMetadata(baseField.metadata, overlayField.metadata);
|
|
3526
4811
|
return {
|
|
3527
4812
|
...baseField,
|
|
4813
|
+
...metadata !== void 0 && { metadata },
|
|
3528
4814
|
type: mergeFieldType(baseField, overlayField, typeRegistry),
|
|
3529
4815
|
annotations: mergeAnnotations(baseField.annotations, overlayField.annotations)
|
|
3530
4816
|
};
|
|
@@ -3635,12 +4921,12 @@ function annotationKey(annotation) {
|
|
|
3635
4921
|
function buildFormSchemas(form, options) {
|
|
3636
4922
|
return {
|
|
3637
4923
|
jsonSchema: generateJsonSchema(form, options),
|
|
3638
|
-
uiSchema: generateUiSchema(form)
|
|
4924
|
+
uiSchema: generateUiSchema(form, options)
|
|
3639
4925
|
};
|
|
3640
4926
|
}
|
|
3641
4927
|
function writeSchemas(form, options) {
|
|
3642
|
-
const { outDir, name = "schema", indent = 2, vendorPrefix } = options;
|
|
3643
|
-
const buildOptions = vendorPrefix === void 0 ? void 0 : { vendorPrefix };
|
|
4928
|
+
const { outDir, name = "schema", indent = 2, vendorPrefix, metadata } = options;
|
|
4929
|
+
const buildOptions = vendorPrefix === void 0 && metadata === void 0 ? void 0 : { vendorPrefix, metadata };
|
|
3644
4930
|
const { jsonSchema, uiSchema: uiSchema2 } = buildFormSchemas(form, buildOptions);
|
|
3645
4931
|
if (!fs.existsSync(outDir)) {
|
|
3646
4932
|
fs.mkdirSync(outDir, { recursive: true });
|