@formspec/build 0.1.0-alpha.16 → 0.1.0-alpha.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/fixtures/example-numeric-extension.d.ts +20 -0
- package/dist/__tests__/fixtures/example-numeric-extension.d.ts.map +1 -0
- package/dist/__tests__/fixtures/mixed-authoring-shipping-address.d.ts +1 -0
- package/dist/__tests__/fixtures/mixed-authoring-shipping-address.d.ts.map +1 -1
- package/dist/__tests__/numeric-extension.integration.test.d.ts +2 -0
- package/dist/__tests__/numeric-extension.integration.test.d.ts.map +1 -0
- package/dist/analyzer/class-analyzer.d.ts +5 -4
- package/dist/analyzer/class-analyzer.d.ts.map +1 -1
- package/dist/analyzer/jsdoc-constraints.d.ts +3 -2
- package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -1
- package/dist/analyzer/tsdoc-parser.d.ts +18 -2
- package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
- package/dist/browser.cjs +199 -4
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +199 -4
- package/dist/browser.js.map +1 -1
- package/dist/build.d.ts +28 -2
- package/dist/cli.cjs +547 -84
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +547 -84
- package/dist/cli.js.map +1 -1
- package/dist/extensions/registry.d.ts +25 -1
- package/dist/extensions/registry.d.ts.map +1 -1
- package/dist/generators/class-schema.d.ts +4 -4
- package/dist/generators/class-schema.d.ts.map +1 -1
- package/dist/index.cjs +546 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +546 -84
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +645 -73
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +643 -71
- package/dist/internals.js.map +1 -1
- package/dist/validate/constraint-validator.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -776,7 +776,12 @@ function applyCustomConstraint(schema, constraint, ctx) {
|
|
|
776
776
|
`Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
|
|
777
777
|
);
|
|
778
778
|
}
|
|
779
|
-
|
|
779
|
+
assignVendorPrefixedExtensionKeywords(
|
|
780
|
+
schema,
|
|
781
|
+
registration.toJsonSchema(constraint.payload, ctx.vendorPrefix),
|
|
782
|
+
ctx.vendorPrefix,
|
|
783
|
+
`custom constraint "${constraint.constraintId}"`
|
|
784
|
+
);
|
|
780
785
|
}
|
|
781
786
|
function applyCustomAnnotation(schema, annotation, ctx) {
|
|
782
787
|
const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
|
|
@@ -788,7 +793,22 @@ function applyCustomAnnotation(schema, annotation, ctx) {
|
|
|
788
793
|
if (registration.toJsonSchema === void 0) {
|
|
789
794
|
return;
|
|
790
795
|
}
|
|
791
|
-
|
|
796
|
+
assignVendorPrefixedExtensionKeywords(
|
|
797
|
+
schema,
|
|
798
|
+
registration.toJsonSchema(annotation.value, ctx.vendorPrefix),
|
|
799
|
+
ctx.vendorPrefix,
|
|
800
|
+
`custom annotation "${annotation.annotationId}"`
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPrefix, source) {
|
|
804
|
+
for (const [key, value] of Object.entries(extensionSchema)) {
|
|
805
|
+
if (!key.startsWith(`${vendorPrefix}-`)) {
|
|
806
|
+
throw new Error(
|
|
807
|
+
`Cannot apply ${source}: extension hooks may only emit "${vendorPrefix}-*" JSON Schema keywords`
|
|
808
|
+
);
|
|
809
|
+
}
|
|
810
|
+
schema[key] = value;
|
|
811
|
+
}
|
|
792
812
|
}
|
|
793
813
|
|
|
794
814
|
// src/json-schema/generator.ts
|
|
@@ -1041,7 +1061,10 @@ function getSchemaExtension(schema, key) {
|
|
|
1041
1061
|
// src/extensions/registry.ts
|
|
1042
1062
|
function createExtensionRegistry(extensions) {
|
|
1043
1063
|
const typeMap = /* @__PURE__ */ new Map();
|
|
1064
|
+
const typeNameMap = /* @__PURE__ */ new Map();
|
|
1044
1065
|
const constraintMap = /* @__PURE__ */ new Map();
|
|
1066
|
+
const constraintTagMap = /* @__PURE__ */ new Map();
|
|
1067
|
+
const builtinBroadeningMap = /* @__PURE__ */ new Map();
|
|
1045
1068
|
const annotationMap = /* @__PURE__ */ new Map();
|
|
1046
1069
|
for (const ext of extensions) {
|
|
1047
1070
|
if (ext.types !== void 0) {
|
|
@@ -1051,6 +1074,27 @@ function createExtensionRegistry(extensions) {
|
|
|
1051
1074
|
throw new Error(`Duplicate custom type ID: "${qualifiedId}"`);
|
|
1052
1075
|
}
|
|
1053
1076
|
typeMap.set(qualifiedId, type);
|
|
1077
|
+
for (const sourceTypeName of type.tsTypeNames ?? [type.typeName]) {
|
|
1078
|
+
if (typeNameMap.has(sourceTypeName)) {
|
|
1079
|
+
throw new Error(`Duplicate custom type source name: "${sourceTypeName}"`);
|
|
1080
|
+
}
|
|
1081
|
+
typeNameMap.set(sourceTypeName, {
|
|
1082
|
+
extensionId: ext.extensionId,
|
|
1083
|
+
registration: type
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
if (type.builtinConstraintBroadenings !== void 0) {
|
|
1087
|
+
for (const broadening of type.builtinConstraintBroadenings) {
|
|
1088
|
+
const key = `${qualifiedId}:${broadening.tagName}`;
|
|
1089
|
+
if (builtinBroadeningMap.has(key)) {
|
|
1090
|
+
throw new Error(`Duplicate built-in constraint broadening: "${key}"`);
|
|
1091
|
+
}
|
|
1092
|
+
builtinBroadeningMap.set(key, {
|
|
1093
|
+
extensionId: ext.extensionId,
|
|
1094
|
+
registration: broadening
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1054
1098
|
}
|
|
1055
1099
|
}
|
|
1056
1100
|
if (ext.constraints !== void 0) {
|
|
@@ -1062,6 +1106,17 @@ function createExtensionRegistry(extensions) {
|
|
|
1062
1106
|
constraintMap.set(qualifiedId, constraint);
|
|
1063
1107
|
}
|
|
1064
1108
|
}
|
|
1109
|
+
if (ext.constraintTags !== void 0) {
|
|
1110
|
+
for (const tag of ext.constraintTags) {
|
|
1111
|
+
if (constraintTagMap.has(tag.tagName)) {
|
|
1112
|
+
throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
|
|
1113
|
+
}
|
|
1114
|
+
constraintTagMap.set(tag.tagName, {
|
|
1115
|
+
extensionId: ext.extensionId,
|
|
1116
|
+
registration: tag
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1065
1120
|
if (ext.annotations !== void 0) {
|
|
1066
1121
|
for (const annotation of ext.annotations) {
|
|
1067
1122
|
const qualifiedId = `${ext.extensionId}/${annotation.annotationName}`;
|
|
@@ -1075,7 +1130,10 @@ function createExtensionRegistry(extensions) {
|
|
|
1075
1130
|
return {
|
|
1076
1131
|
extensions,
|
|
1077
1132
|
findType: (typeId) => typeMap.get(typeId),
|
|
1133
|
+
findTypeByName: (typeName) => typeNameMap.get(typeName),
|
|
1078
1134
|
findConstraint: (constraintId) => constraintMap.get(constraintId),
|
|
1135
|
+
findConstraintTag: (tagName) => constraintTagMap.get(tagName),
|
|
1136
|
+
findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
|
|
1079
1137
|
findAnnotation: (annotationId) => annotationMap.get(annotationId)
|
|
1080
1138
|
};
|
|
1081
1139
|
}
|
|
@@ -1261,7 +1319,7 @@ var LENGTH_CONSTRAINT_MAP = {
|
|
|
1261
1319
|
maxItems: "maxItems"
|
|
1262
1320
|
};
|
|
1263
1321
|
var TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions", "defaultValue"]);
|
|
1264
|
-
function createFormSpecTSDocConfig() {
|
|
1322
|
+
function createFormSpecTSDocConfig(extensionTagNames = []) {
|
|
1265
1323
|
const config = new TSDocConfiguration();
|
|
1266
1324
|
for (const tagName of Object.keys(BUILTIN_CONSTRAINT_DEFINITIONS)) {
|
|
1267
1325
|
config.addTagDefinition(
|
|
@@ -1281,14 +1339,34 @@ function createFormSpecTSDocConfig() {
|
|
|
1281
1339
|
})
|
|
1282
1340
|
);
|
|
1283
1341
|
}
|
|
1342
|
+
for (const tagName of extensionTagNames) {
|
|
1343
|
+
config.addTagDefinition(
|
|
1344
|
+
new TSDocTagDefinition({
|
|
1345
|
+
tagName: "@" + tagName,
|
|
1346
|
+
syntaxKind: TSDocTagSyntaxKind.BlockTag,
|
|
1347
|
+
allowMultiple: true
|
|
1348
|
+
})
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1284
1351
|
return config;
|
|
1285
1352
|
}
|
|
1286
|
-
var
|
|
1287
|
-
function getParser() {
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1353
|
+
var parserCache = /* @__PURE__ */ new Map();
|
|
1354
|
+
function getParser(options) {
|
|
1355
|
+
const extensionTagNames = [
|
|
1356
|
+
...options?.extensionRegistry?.extensions.flatMap(
|
|
1357
|
+
(extension) => (extension.constraintTags ?? []).map((tag) => tag.tagName)
|
|
1358
|
+
) ?? []
|
|
1359
|
+
].sort();
|
|
1360
|
+
const cacheKey = extensionTagNames.join("|");
|
|
1361
|
+
const existing = parserCache.get(cacheKey);
|
|
1362
|
+
if (existing) {
|
|
1363
|
+
return existing;
|
|
1364
|
+
}
|
|
1365
|
+
const parser = new TSDocParser(createFormSpecTSDocConfig(extensionTagNames));
|
|
1366
|
+
parserCache.set(cacheKey, parser);
|
|
1367
|
+
return parser;
|
|
1368
|
+
}
|
|
1369
|
+
function parseTSDocTags(node, file = "", options) {
|
|
1292
1370
|
const constraints = [];
|
|
1293
1371
|
const annotations = [];
|
|
1294
1372
|
let displayName;
|
|
@@ -1309,7 +1387,7 @@ function parseTSDocTags(node, file = "") {
|
|
|
1309
1387
|
if (!commentText.startsWith("/**")) {
|
|
1310
1388
|
continue;
|
|
1311
1389
|
}
|
|
1312
|
-
const parser = getParser();
|
|
1390
|
+
const parser = getParser(options);
|
|
1313
1391
|
const parserContext = parser.parseRange(
|
|
1314
1392
|
TextRange.fromStringRange(sourceText, range.pos, range.end)
|
|
1315
1393
|
);
|
|
@@ -1348,7 +1426,7 @@ function parseTSDocTags(node, file = "") {
|
|
|
1348
1426
|
const expectedType = isBuiltinConstraintName(tagName) ? BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
|
|
1349
1427
|
if (text === "" && expectedType !== "boolean") continue;
|
|
1350
1428
|
const provenance = provenanceForComment(range, sourceFile, file, tagName);
|
|
1351
|
-
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
1429
|
+
const constraintNode = parseConstraintValue(tagName, text, provenance, options);
|
|
1352
1430
|
if (constraintNode) {
|
|
1353
1431
|
constraints.push(constraintNode);
|
|
1354
1432
|
}
|
|
@@ -1408,7 +1486,7 @@ function parseTSDocTags(node, file = "") {
|
|
|
1408
1486
|
annotations.push(defaultValueNode);
|
|
1409
1487
|
continue;
|
|
1410
1488
|
}
|
|
1411
|
-
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
1489
|
+
const constraintNode = parseConstraintValue(tagName, text, provenance, options);
|
|
1412
1490
|
if (constraintNode) {
|
|
1413
1491
|
constraints.push(constraintNode);
|
|
1414
1492
|
}
|
|
@@ -1464,7 +1542,11 @@ function extractPlainText(node) {
|
|
|
1464
1542
|
}
|
|
1465
1543
|
return result;
|
|
1466
1544
|
}
|
|
1467
|
-
function parseConstraintValue(tagName, text, provenance) {
|
|
1545
|
+
function parseConstraintValue(tagName, text, provenance, options) {
|
|
1546
|
+
const customConstraint = parseExtensionConstraintValue(tagName, text, provenance, options);
|
|
1547
|
+
if (customConstraint) {
|
|
1548
|
+
return customConstraint;
|
|
1549
|
+
}
|
|
1468
1550
|
if (!isBuiltinConstraintName(tagName)) {
|
|
1469
1551
|
return null;
|
|
1470
1552
|
}
|
|
@@ -1569,6 +1651,83 @@ function parseConstraintValue(tagName, text, provenance) {
|
|
|
1569
1651
|
provenance
|
|
1570
1652
|
};
|
|
1571
1653
|
}
|
|
1654
|
+
function parseExtensionConstraintValue(tagName, text, provenance, options) {
|
|
1655
|
+
const pathResult = extractPathTarget(text);
|
|
1656
|
+
const effectiveText = pathResult ? pathResult.remainingText : text;
|
|
1657
|
+
const path3 = pathResult?.path;
|
|
1658
|
+
const registry = options?.extensionRegistry;
|
|
1659
|
+
if (registry === void 0) {
|
|
1660
|
+
return null;
|
|
1661
|
+
}
|
|
1662
|
+
const directTag = registry.findConstraintTag(tagName);
|
|
1663
|
+
if (directTag !== void 0) {
|
|
1664
|
+
return makeCustomConstraintNode(
|
|
1665
|
+
directTag.extensionId,
|
|
1666
|
+
directTag.registration.constraintName,
|
|
1667
|
+
directTag.registration.parseValue(effectiveText),
|
|
1668
|
+
provenance,
|
|
1669
|
+
path3,
|
|
1670
|
+
registry
|
|
1671
|
+
);
|
|
1672
|
+
}
|
|
1673
|
+
if (!isBuiltinConstraintName(tagName)) {
|
|
1674
|
+
return null;
|
|
1675
|
+
}
|
|
1676
|
+
const broadenedTypeId = getBroadenedCustomTypeId(options?.fieldType);
|
|
1677
|
+
if (broadenedTypeId === void 0) {
|
|
1678
|
+
return null;
|
|
1679
|
+
}
|
|
1680
|
+
const broadened = registry.findBuiltinConstraintBroadening(broadenedTypeId, tagName);
|
|
1681
|
+
if (broadened === void 0) {
|
|
1682
|
+
return null;
|
|
1683
|
+
}
|
|
1684
|
+
return makeCustomConstraintNode(
|
|
1685
|
+
broadened.extensionId,
|
|
1686
|
+
broadened.registration.constraintName,
|
|
1687
|
+
broadened.registration.parseValue(effectiveText),
|
|
1688
|
+
provenance,
|
|
1689
|
+
path3,
|
|
1690
|
+
registry
|
|
1691
|
+
);
|
|
1692
|
+
}
|
|
1693
|
+
function getBroadenedCustomTypeId(fieldType) {
|
|
1694
|
+
if (fieldType?.kind === "custom") {
|
|
1695
|
+
return fieldType.typeId;
|
|
1696
|
+
}
|
|
1697
|
+
if (fieldType?.kind !== "union") {
|
|
1698
|
+
return void 0;
|
|
1699
|
+
}
|
|
1700
|
+
const customMembers = fieldType.members.filter(
|
|
1701
|
+
(member) => member.kind === "custom"
|
|
1702
|
+
);
|
|
1703
|
+
if (customMembers.length !== 1) {
|
|
1704
|
+
return void 0;
|
|
1705
|
+
}
|
|
1706
|
+
const nonCustomMembers = fieldType.members.filter((member) => member.kind !== "custom");
|
|
1707
|
+
const allOtherMembersAreNull = nonCustomMembers.every(
|
|
1708
|
+
(member) => member.kind === "primitive" && member.primitiveKind === "null"
|
|
1709
|
+
);
|
|
1710
|
+
const customMember = customMembers[0];
|
|
1711
|
+
return allOtherMembersAreNull && customMember !== void 0 ? customMember.typeId : void 0;
|
|
1712
|
+
}
|
|
1713
|
+
function makeCustomConstraintNode(extensionId, constraintName, payload, provenance, path3, registry) {
|
|
1714
|
+
const constraintId = `${extensionId}/${constraintName}`;
|
|
1715
|
+
const registration = registry.findConstraint(constraintId);
|
|
1716
|
+
if (registration === void 0) {
|
|
1717
|
+
throw new Error(
|
|
1718
|
+
`Custom TSDoc tag resolved to unregistered constraint "${constraintId}". Register the constraint before using its tag.`
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1721
|
+
return {
|
|
1722
|
+
kind: "constraint",
|
|
1723
|
+
constraintKind: "custom",
|
|
1724
|
+
constraintId,
|
|
1725
|
+
payload,
|
|
1726
|
+
compositionRule: registration.compositionRule,
|
|
1727
|
+
...path3 && { path: path3 },
|
|
1728
|
+
provenance
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1572
1731
|
function parseDefaultValueValue(text, provenance) {
|
|
1573
1732
|
const trimmed = text.trim();
|
|
1574
1733
|
let value;
|
|
@@ -1629,12 +1788,12 @@ function getTagCommentText(tag) {
|
|
|
1629
1788
|
}
|
|
1630
1789
|
|
|
1631
1790
|
// src/analyzer/jsdoc-constraints.ts
|
|
1632
|
-
function extractJSDocConstraintNodes(node, file = "") {
|
|
1633
|
-
const result = parseTSDocTags(node, file);
|
|
1791
|
+
function extractJSDocConstraintNodes(node, file = "", options) {
|
|
1792
|
+
const result = parseTSDocTags(node, file, options);
|
|
1634
1793
|
return [...result.constraints];
|
|
1635
1794
|
}
|
|
1636
|
-
function extractJSDocAnnotationNodes(node, file = "") {
|
|
1637
|
-
const result = parseTSDocTags(node, file);
|
|
1795
|
+
function extractJSDocAnnotationNodes(node, file = "", options) {
|
|
1796
|
+
const result = parseTSDocTags(node, file, options);
|
|
1638
1797
|
return [...result.annotations];
|
|
1639
1798
|
}
|
|
1640
1799
|
function extractDefaultValueAnnotation(initializer, file = "") {
|
|
@@ -1683,18 +1842,38 @@ var RESOLVING_TYPE_PLACEHOLDER = {
|
|
|
1683
1842
|
properties: [],
|
|
1684
1843
|
additionalProperties: true
|
|
1685
1844
|
};
|
|
1686
|
-
function
|
|
1845
|
+
function makeParseOptions(extensionRegistry, fieldType) {
|
|
1846
|
+
if (extensionRegistry === void 0 && fieldType === void 0) {
|
|
1847
|
+
return void 0;
|
|
1848
|
+
}
|
|
1849
|
+
return {
|
|
1850
|
+
...extensionRegistry !== void 0 && { extensionRegistry },
|
|
1851
|
+
...fieldType !== void 0 && { fieldType }
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
1687
1855
|
const name = classDecl.name?.text ?? "AnonymousClass";
|
|
1688
1856
|
const fields = [];
|
|
1689
1857
|
const fieldLayouts = [];
|
|
1690
1858
|
const typeRegistry = {};
|
|
1691
|
-
const annotations = extractJSDocAnnotationNodes(
|
|
1859
|
+
const annotations = extractJSDocAnnotationNodes(
|
|
1860
|
+
classDecl,
|
|
1861
|
+
file,
|
|
1862
|
+
makeParseOptions(extensionRegistry)
|
|
1863
|
+
);
|
|
1692
1864
|
const visiting = /* @__PURE__ */ new Set();
|
|
1693
1865
|
const instanceMethods = [];
|
|
1694
1866
|
const staticMethods = [];
|
|
1695
1867
|
for (const member of classDecl.members) {
|
|
1696
1868
|
if (ts4.isPropertyDeclaration(member)) {
|
|
1697
|
-
const fieldNode = analyzeFieldToIR(
|
|
1869
|
+
const fieldNode = analyzeFieldToIR(
|
|
1870
|
+
member,
|
|
1871
|
+
checker,
|
|
1872
|
+
file,
|
|
1873
|
+
typeRegistry,
|
|
1874
|
+
visiting,
|
|
1875
|
+
extensionRegistry
|
|
1876
|
+
);
|
|
1698
1877
|
if (fieldNode) {
|
|
1699
1878
|
fields.push(fieldNode);
|
|
1700
1879
|
fieldLayouts.push({});
|
|
@@ -1721,15 +1900,26 @@ function analyzeClassToIR(classDecl, checker, file = "") {
|
|
|
1721
1900
|
staticMethods
|
|
1722
1901
|
};
|
|
1723
1902
|
}
|
|
1724
|
-
function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
1903
|
+
function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry) {
|
|
1725
1904
|
const name = interfaceDecl.name.text;
|
|
1726
1905
|
const fields = [];
|
|
1727
1906
|
const typeRegistry = {};
|
|
1728
|
-
const annotations = extractJSDocAnnotationNodes(
|
|
1907
|
+
const annotations = extractJSDocAnnotationNodes(
|
|
1908
|
+
interfaceDecl,
|
|
1909
|
+
file,
|
|
1910
|
+
makeParseOptions(extensionRegistry)
|
|
1911
|
+
);
|
|
1729
1912
|
const visiting = /* @__PURE__ */ new Set();
|
|
1730
1913
|
for (const member of interfaceDecl.members) {
|
|
1731
1914
|
if (ts4.isPropertySignature(member)) {
|
|
1732
|
-
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1915
|
+
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1916
|
+
member,
|
|
1917
|
+
checker,
|
|
1918
|
+
file,
|
|
1919
|
+
typeRegistry,
|
|
1920
|
+
visiting,
|
|
1921
|
+
extensionRegistry
|
|
1922
|
+
);
|
|
1733
1923
|
if (fieldNode) {
|
|
1734
1924
|
fields.push(fieldNode);
|
|
1735
1925
|
}
|
|
@@ -1746,7 +1936,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
|
1746
1936
|
staticMethods: []
|
|
1747
1937
|
};
|
|
1748
1938
|
}
|
|
1749
|
-
function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
1939
|
+
function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
|
|
1750
1940
|
if (!ts4.isTypeLiteralNode(typeAlias.type)) {
|
|
1751
1941
|
const sourceFile = typeAlias.getSourceFile();
|
|
1752
1942
|
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
@@ -1759,11 +1949,22 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
|
1759
1949
|
const name = typeAlias.name.text;
|
|
1760
1950
|
const fields = [];
|
|
1761
1951
|
const typeRegistry = {};
|
|
1762
|
-
const annotations = extractJSDocAnnotationNodes(
|
|
1952
|
+
const annotations = extractJSDocAnnotationNodes(
|
|
1953
|
+
typeAlias,
|
|
1954
|
+
file,
|
|
1955
|
+
makeParseOptions(extensionRegistry)
|
|
1956
|
+
);
|
|
1763
1957
|
const visiting = /* @__PURE__ */ new Set();
|
|
1764
1958
|
for (const member of typeAlias.type.members) {
|
|
1765
1959
|
if (ts4.isPropertySignature(member)) {
|
|
1766
|
-
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1960
|
+
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1961
|
+
member,
|
|
1962
|
+
checker,
|
|
1963
|
+
file,
|
|
1964
|
+
typeRegistry,
|
|
1965
|
+
visiting,
|
|
1966
|
+
extensionRegistry
|
|
1967
|
+
);
|
|
1767
1968
|
if (fieldNode) {
|
|
1768
1969
|
fields.push(fieldNode);
|
|
1769
1970
|
}
|
|
@@ -1782,7 +1983,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
|
1782
1983
|
}
|
|
1783
1984
|
};
|
|
1784
1985
|
}
|
|
1785
|
-
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
1986
|
+
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1786
1987
|
if (!ts4.isIdentifier(prop.name)) {
|
|
1787
1988
|
return null;
|
|
1788
1989
|
}
|
|
@@ -1790,14 +1991,26 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
|
1790
1991
|
const tsType = checker.getTypeAtLocation(prop);
|
|
1791
1992
|
const optional = prop.questionToken !== void 0;
|
|
1792
1993
|
const provenance = provenanceForNode(prop, file);
|
|
1793
|
-
let type = resolveTypeNode(
|
|
1994
|
+
let type = resolveTypeNode(
|
|
1995
|
+
tsType,
|
|
1996
|
+
checker,
|
|
1997
|
+
file,
|
|
1998
|
+
typeRegistry,
|
|
1999
|
+
visiting,
|
|
2000
|
+
prop,
|
|
2001
|
+
extensionRegistry
|
|
2002
|
+
);
|
|
1794
2003
|
const constraints = [];
|
|
1795
2004
|
if (prop.type) {
|
|
1796
|
-
constraints.push(
|
|
2005
|
+
constraints.push(
|
|
2006
|
+
...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
|
|
2007
|
+
);
|
|
1797
2008
|
}
|
|
1798
|
-
constraints.push(...extractJSDocConstraintNodes(prop, file));
|
|
2009
|
+
constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
|
|
1799
2010
|
let annotations = [];
|
|
1800
|
-
annotations.push(
|
|
2011
|
+
annotations.push(
|
|
2012
|
+
...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
2013
|
+
);
|
|
1801
2014
|
const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
|
|
1802
2015
|
if (defaultAnnotation && !annotations.some((a) => a.annotationKind === "defaultValue")) {
|
|
1803
2016
|
annotations.push(defaultAnnotation);
|
|
@@ -1813,7 +2026,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
|
1813
2026
|
provenance
|
|
1814
2027
|
};
|
|
1815
2028
|
}
|
|
1816
|
-
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting) {
|
|
2029
|
+
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1817
2030
|
if (!ts4.isIdentifier(prop.name)) {
|
|
1818
2031
|
return null;
|
|
1819
2032
|
}
|
|
@@ -1821,14 +2034,26 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
1821
2034
|
const tsType = checker.getTypeAtLocation(prop);
|
|
1822
2035
|
const optional = prop.questionToken !== void 0;
|
|
1823
2036
|
const provenance = provenanceForNode(prop, file);
|
|
1824
|
-
let type = resolveTypeNode(
|
|
2037
|
+
let type = resolveTypeNode(
|
|
2038
|
+
tsType,
|
|
2039
|
+
checker,
|
|
2040
|
+
file,
|
|
2041
|
+
typeRegistry,
|
|
2042
|
+
visiting,
|
|
2043
|
+
prop,
|
|
2044
|
+
extensionRegistry
|
|
2045
|
+
);
|
|
1825
2046
|
const constraints = [];
|
|
1826
2047
|
if (prop.type) {
|
|
1827
|
-
constraints.push(
|
|
2048
|
+
constraints.push(
|
|
2049
|
+
...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
|
|
2050
|
+
);
|
|
1828
2051
|
}
|
|
1829
|
-
constraints.push(...extractJSDocConstraintNodes(prop, file));
|
|
2052
|
+
constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
|
|
1830
2053
|
let annotations = [];
|
|
1831
|
-
annotations.push(
|
|
2054
|
+
annotations.push(
|
|
2055
|
+
...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
2056
|
+
);
|
|
1832
2057
|
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
1833
2058
|
return {
|
|
1834
2059
|
kind: "field",
|
|
@@ -1902,7 +2127,66 @@ function parseEnumMemberDisplayName(value) {
|
|
|
1902
2127
|
if (label === "") return null;
|
|
1903
2128
|
return { value: match[1], label };
|
|
1904
2129
|
}
|
|
1905
|
-
function
|
|
2130
|
+
function resolveRegisteredCustomType(sourceNode, extensionRegistry, checker) {
|
|
2131
|
+
if (sourceNode === void 0 || extensionRegistry === void 0) {
|
|
2132
|
+
return null;
|
|
2133
|
+
}
|
|
2134
|
+
const typeNode = extractTypeNodeFromSource(sourceNode);
|
|
2135
|
+
if (typeNode === void 0) {
|
|
2136
|
+
return null;
|
|
2137
|
+
}
|
|
2138
|
+
return resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker);
|
|
2139
|
+
}
|
|
2140
|
+
function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker) {
|
|
2141
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
2142
|
+
return resolveRegisteredCustomTypeFromTypeNode(typeNode.type, extensionRegistry, checker);
|
|
2143
|
+
}
|
|
2144
|
+
const typeName = getTypeNodeRegistrationName(typeNode);
|
|
2145
|
+
if (typeName === null) {
|
|
2146
|
+
return null;
|
|
2147
|
+
}
|
|
2148
|
+
const registration = extensionRegistry.findTypeByName(typeName);
|
|
2149
|
+
if (registration !== void 0) {
|
|
2150
|
+
return {
|
|
2151
|
+
kind: "custom",
|
|
2152
|
+
typeId: `${registration.extensionId}/${registration.registration.typeName}`,
|
|
2153
|
+
payload: null
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
if (ts4.isTypeReferenceNode(typeNode) && ts4.isIdentifier(typeNode.typeName)) {
|
|
2157
|
+
const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts4.isTypeAliasDeclaration);
|
|
2158
|
+
if (aliasDecl !== void 0) {
|
|
2159
|
+
return resolveRegisteredCustomTypeFromTypeNode(aliasDecl.type, extensionRegistry, checker);
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
return null;
|
|
2163
|
+
}
|
|
2164
|
+
function extractTypeNodeFromSource(sourceNode) {
|
|
2165
|
+
if (ts4.isPropertyDeclaration(sourceNode) || ts4.isPropertySignature(sourceNode) || ts4.isParameter(sourceNode) || ts4.isTypeAliasDeclaration(sourceNode)) {
|
|
2166
|
+
return sourceNode.type;
|
|
2167
|
+
}
|
|
2168
|
+
if (ts4.isTypeNode(sourceNode)) {
|
|
2169
|
+
return sourceNode;
|
|
2170
|
+
}
|
|
2171
|
+
return void 0;
|
|
2172
|
+
}
|
|
2173
|
+
function getTypeNodeRegistrationName(typeNode) {
|
|
2174
|
+
if (ts4.isTypeReferenceNode(typeNode)) {
|
|
2175
|
+
return ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : typeNode.typeName.right.text;
|
|
2176
|
+
}
|
|
2177
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
2178
|
+
return getTypeNodeRegistrationName(typeNode.type);
|
|
2179
|
+
}
|
|
2180
|
+
if (typeNode.kind === ts4.SyntaxKind.BigIntKeyword || typeNode.kind === ts4.SyntaxKind.StringKeyword || typeNode.kind === ts4.SyntaxKind.NumberKeyword || typeNode.kind === ts4.SyntaxKind.BooleanKeyword) {
|
|
2181
|
+
return typeNode.getText();
|
|
2182
|
+
}
|
|
2183
|
+
return null;
|
|
2184
|
+
}
|
|
2185
|
+
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
2186
|
+
const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
|
|
2187
|
+
if (customType) {
|
|
2188
|
+
return customType;
|
|
2189
|
+
}
|
|
1906
2190
|
if (type.flags & ts4.TypeFlags.String) {
|
|
1907
2191
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1908
2192
|
}
|
|
@@ -1931,26 +2215,50 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
1931
2215
|
};
|
|
1932
2216
|
}
|
|
1933
2217
|
if (type.isUnion()) {
|
|
1934
|
-
return resolveUnionType(
|
|
2218
|
+
return resolveUnionType(
|
|
2219
|
+
type,
|
|
2220
|
+
checker,
|
|
2221
|
+
file,
|
|
2222
|
+
typeRegistry,
|
|
2223
|
+
visiting,
|
|
2224
|
+
sourceNode,
|
|
2225
|
+
extensionRegistry
|
|
2226
|
+
);
|
|
1935
2227
|
}
|
|
1936
2228
|
if (checker.isArrayType(type)) {
|
|
1937
|
-
return resolveArrayType(
|
|
2229
|
+
return resolveArrayType(
|
|
2230
|
+
type,
|
|
2231
|
+
checker,
|
|
2232
|
+
file,
|
|
2233
|
+
typeRegistry,
|
|
2234
|
+
visiting,
|
|
2235
|
+
sourceNode,
|
|
2236
|
+
extensionRegistry
|
|
2237
|
+
);
|
|
1938
2238
|
}
|
|
1939
2239
|
if (isObjectType(type)) {
|
|
1940
|
-
return resolveObjectType(type, checker, file, typeRegistry, visiting);
|
|
2240
|
+
return resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry);
|
|
1941
2241
|
}
|
|
1942
2242
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1943
2243
|
}
|
|
1944
|
-
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode) {
|
|
2244
|
+
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
1945
2245
|
const typeName = getNamedTypeName(type);
|
|
1946
2246
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
1947
2247
|
if (typeName && typeName in typeRegistry) {
|
|
1948
2248
|
return { kind: "reference", name: typeName, typeArguments: [] };
|
|
1949
2249
|
}
|
|
1950
2250
|
const allTypes = type.types;
|
|
2251
|
+
const unionMemberTypeNodes = extractUnionMemberTypeNodes(sourceNode, checker);
|
|
2252
|
+
const nonNullSourceNodes = unionMemberTypeNodes.filter(
|
|
2253
|
+
(memberTypeNode) => !isNullishTypeNode(resolveAliasedTypeNode(memberTypeNode, checker))
|
|
2254
|
+
);
|
|
1951
2255
|
const nonNullTypes = allTypes.filter(
|
|
1952
|
-
(
|
|
2256
|
+
(memberType) => !(memberType.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
|
|
1953
2257
|
);
|
|
2258
|
+
const nonNullMembers = nonNullTypes.map((memberType, index) => ({
|
|
2259
|
+
memberType,
|
|
2260
|
+
sourceNode: nonNullSourceNodes.length === nonNullTypes.length ? nonNullSourceNodes[index] : void 0
|
|
2261
|
+
}));
|
|
1954
2262
|
const hasNull = allTypes.some((t) => t.flags & ts4.TypeFlags.Null);
|
|
1955
2263
|
const memberDisplayNames = /* @__PURE__ */ new Map();
|
|
1956
2264
|
if (namedDecl) {
|
|
@@ -1967,7 +2275,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
1967
2275
|
if (!typeName) {
|
|
1968
2276
|
return result;
|
|
1969
2277
|
}
|
|
1970
|
-
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2278
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
1971
2279
|
typeRegistry[typeName] = {
|
|
1972
2280
|
name: typeName,
|
|
1973
2281
|
type: result,
|
|
@@ -2015,14 +2323,15 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2015
2323
|
} : enumNode;
|
|
2016
2324
|
return registerNamed(result);
|
|
2017
2325
|
}
|
|
2018
|
-
if (
|
|
2326
|
+
if (nonNullMembers.length === 1 && nonNullMembers[0]) {
|
|
2019
2327
|
const inner = resolveTypeNode(
|
|
2020
|
-
|
|
2328
|
+
nonNullMembers[0].memberType,
|
|
2021
2329
|
checker,
|
|
2022
2330
|
file,
|
|
2023
2331
|
typeRegistry,
|
|
2024
2332
|
visiting,
|
|
2025
|
-
sourceNode
|
|
2333
|
+
nonNullMembers[0].sourceNode ?? sourceNode,
|
|
2334
|
+
extensionRegistry
|
|
2026
2335
|
);
|
|
2027
2336
|
const result = hasNull ? {
|
|
2028
2337
|
kind: "union",
|
|
@@ -2030,21 +2339,38 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2030
2339
|
} : inner;
|
|
2031
2340
|
return registerNamed(result);
|
|
2032
2341
|
}
|
|
2033
|
-
const members =
|
|
2034
|
-
(
|
|
2342
|
+
const members = nonNullMembers.map(
|
|
2343
|
+
({ memberType, sourceNode: memberSourceNode }) => resolveTypeNode(
|
|
2344
|
+
memberType,
|
|
2345
|
+
checker,
|
|
2346
|
+
file,
|
|
2347
|
+
typeRegistry,
|
|
2348
|
+
visiting,
|
|
2349
|
+
memberSourceNode ?? sourceNode,
|
|
2350
|
+
extensionRegistry
|
|
2351
|
+
)
|
|
2035
2352
|
);
|
|
2036
2353
|
if (hasNull) {
|
|
2037
2354
|
members.push({ kind: "primitive", primitiveKind: "null" });
|
|
2038
2355
|
}
|
|
2039
2356
|
return registerNamed({ kind: "union", members });
|
|
2040
2357
|
}
|
|
2041
|
-
function resolveArrayType(type, checker, file, typeRegistry, visiting) {
|
|
2358
|
+
function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
2042
2359
|
const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
|
|
2043
2360
|
const elementType = typeArgs?.[0];
|
|
2044
|
-
const
|
|
2361
|
+
const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
|
|
2362
|
+
const items = elementType ? resolveTypeNode(
|
|
2363
|
+
elementType,
|
|
2364
|
+
checker,
|
|
2365
|
+
file,
|
|
2366
|
+
typeRegistry,
|
|
2367
|
+
visiting,
|
|
2368
|
+
elementSourceNode,
|
|
2369
|
+
extensionRegistry
|
|
2370
|
+
) : { kind: "primitive", primitiveKind: "string" };
|
|
2045
2371
|
return { kind: "array", items };
|
|
2046
2372
|
}
|
|
2047
|
-
function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
|
|
2373
|
+
function tryResolveRecordType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
2048
2374
|
if (type.getProperties().length > 0) {
|
|
2049
2375
|
return null;
|
|
2050
2376
|
}
|
|
@@ -2052,7 +2378,15 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
|
|
|
2052
2378
|
if (!indexInfo) {
|
|
2053
2379
|
return null;
|
|
2054
2380
|
}
|
|
2055
|
-
const valueType = resolveTypeNode(
|
|
2381
|
+
const valueType = resolveTypeNode(
|
|
2382
|
+
indexInfo.type,
|
|
2383
|
+
checker,
|
|
2384
|
+
file,
|
|
2385
|
+
typeRegistry,
|
|
2386
|
+
visiting,
|
|
2387
|
+
void 0,
|
|
2388
|
+
extensionRegistry
|
|
2389
|
+
);
|
|
2056
2390
|
return { kind: "record", valueType };
|
|
2057
2391
|
}
|
|
2058
2392
|
function typeNodeContainsReference(type, targetName) {
|
|
@@ -2080,7 +2414,7 @@ function typeNodeContainsReference(type, targetName) {
|
|
|
2080
2414
|
}
|
|
2081
2415
|
}
|
|
2082
2416
|
}
|
|
2083
|
-
function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
2417
|
+
function resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
2084
2418
|
const typeName = getNamedTypeName(type);
|
|
2085
2419
|
const namedTypeName = typeName ?? void 0;
|
|
2086
2420
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
@@ -2111,7 +2445,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2111
2445
|
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
2112
2446
|
}
|
|
2113
2447
|
}
|
|
2114
|
-
const recordNode = tryResolveRecordType(
|
|
2448
|
+
const recordNode = tryResolveRecordType(
|
|
2449
|
+
type,
|
|
2450
|
+
checker,
|
|
2451
|
+
file,
|
|
2452
|
+
typeRegistry,
|
|
2453
|
+
visiting,
|
|
2454
|
+
extensionRegistry
|
|
2455
|
+
);
|
|
2115
2456
|
if (recordNode) {
|
|
2116
2457
|
visiting.delete(type);
|
|
2117
2458
|
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
@@ -2120,7 +2461,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2120
2461
|
clearNamedTypeRegistration();
|
|
2121
2462
|
return recordNode;
|
|
2122
2463
|
}
|
|
2123
|
-
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2464
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
2124
2465
|
typeRegistry[namedTypeName] = {
|
|
2125
2466
|
name: namedTypeName,
|
|
2126
2467
|
type: recordNode,
|
|
@@ -2132,7 +2473,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2132
2473
|
return recordNode;
|
|
2133
2474
|
}
|
|
2134
2475
|
const properties = [];
|
|
2135
|
-
const fieldInfoMap = getNamedTypeFieldNodeInfoMap(
|
|
2476
|
+
const fieldInfoMap = getNamedTypeFieldNodeInfoMap(
|
|
2477
|
+
type,
|
|
2478
|
+
checker,
|
|
2479
|
+
file,
|
|
2480
|
+
typeRegistry,
|
|
2481
|
+
visiting,
|
|
2482
|
+
extensionRegistry
|
|
2483
|
+
);
|
|
2136
2484
|
for (const prop of type.getProperties()) {
|
|
2137
2485
|
const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
2138
2486
|
if (!declaration) continue;
|
|
@@ -2144,7 +2492,8 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2144
2492
|
file,
|
|
2145
2493
|
typeRegistry,
|
|
2146
2494
|
visiting,
|
|
2147
|
-
declaration
|
|
2495
|
+
declaration,
|
|
2496
|
+
extensionRegistry
|
|
2148
2497
|
);
|
|
2149
2498
|
const fieldNodeInfo = fieldInfoMap?.get(prop.name);
|
|
2150
2499
|
properties.push({
|
|
@@ -2163,7 +2512,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2163
2512
|
additionalProperties: true
|
|
2164
2513
|
};
|
|
2165
2514
|
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
2166
|
-
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2515
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
2167
2516
|
typeRegistry[namedTypeName] = {
|
|
2168
2517
|
name: namedTypeName,
|
|
2169
2518
|
type: objectNode,
|
|
@@ -2174,7 +2523,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2174
2523
|
}
|
|
2175
2524
|
return objectNode;
|
|
2176
2525
|
}
|
|
2177
|
-
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting) {
|
|
2526
|
+
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
2178
2527
|
const symbols = [type.getSymbol(), type.aliasSymbol].filter(
|
|
2179
2528
|
(s) => s?.declarations != null && s.declarations.length > 0
|
|
2180
2529
|
);
|
|
@@ -2186,7 +2535,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2186
2535
|
const map = /* @__PURE__ */ new Map();
|
|
2187
2536
|
for (const member of classDecl.members) {
|
|
2188
2537
|
if (ts4.isPropertyDeclaration(member) && ts4.isIdentifier(member.name)) {
|
|
2189
|
-
const fieldNode = analyzeFieldToIR(
|
|
2538
|
+
const fieldNode = analyzeFieldToIR(
|
|
2539
|
+
member,
|
|
2540
|
+
checker,
|
|
2541
|
+
file,
|
|
2542
|
+
typeRegistry,
|
|
2543
|
+
visiting,
|
|
2544
|
+
extensionRegistry
|
|
2545
|
+
);
|
|
2190
2546
|
if (fieldNode) {
|
|
2191
2547
|
map.set(fieldNode.name, {
|
|
2192
2548
|
constraints: [...fieldNode.constraints],
|
|
@@ -2200,7 +2556,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2200
2556
|
}
|
|
2201
2557
|
const interfaceDecl = declarations.find(ts4.isInterfaceDeclaration);
|
|
2202
2558
|
if (interfaceDecl) {
|
|
2203
|
-
return buildFieldNodeInfoMap(
|
|
2559
|
+
return buildFieldNodeInfoMap(
|
|
2560
|
+
interfaceDecl.members,
|
|
2561
|
+
checker,
|
|
2562
|
+
file,
|
|
2563
|
+
typeRegistry,
|
|
2564
|
+
visiting,
|
|
2565
|
+
extensionRegistry
|
|
2566
|
+
);
|
|
2204
2567
|
}
|
|
2205
2568
|
const typeAliasDecl = declarations.find(ts4.isTypeAliasDeclaration);
|
|
2206
2569
|
if (typeAliasDecl && ts4.isTypeLiteralNode(typeAliasDecl.type)) {
|
|
@@ -2209,17 +2572,68 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2209
2572
|
checker,
|
|
2210
2573
|
file,
|
|
2211
2574
|
typeRegistry,
|
|
2212
|
-
visiting
|
|
2575
|
+
visiting,
|
|
2576
|
+
extensionRegistry
|
|
2213
2577
|
);
|
|
2214
2578
|
}
|
|
2215
2579
|
}
|
|
2216
2580
|
return null;
|
|
2217
2581
|
}
|
|
2218
|
-
function
|
|
2582
|
+
function extractArrayElementTypeNode(sourceNode, checker) {
|
|
2583
|
+
const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
|
|
2584
|
+
if (typeNode === void 0) {
|
|
2585
|
+
return void 0;
|
|
2586
|
+
}
|
|
2587
|
+
const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
|
|
2588
|
+
if (ts4.isArrayTypeNode(resolvedTypeNode)) {
|
|
2589
|
+
return resolvedTypeNode.elementType;
|
|
2590
|
+
}
|
|
2591
|
+
if (ts4.isTypeReferenceNode(resolvedTypeNode) && ts4.isIdentifier(resolvedTypeNode.typeName) && resolvedTypeNode.typeName.text === "Array" && resolvedTypeNode.typeArguments?.[0]) {
|
|
2592
|
+
return resolvedTypeNode.typeArguments[0];
|
|
2593
|
+
}
|
|
2594
|
+
return void 0;
|
|
2595
|
+
}
|
|
2596
|
+
function extractUnionMemberTypeNodes(sourceNode, checker) {
|
|
2597
|
+
const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
|
|
2598
|
+
if (!typeNode) {
|
|
2599
|
+
return [];
|
|
2600
|
+
}
|
|
2601
|
+
const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
|
|
2602
|
+
return ts4.isUnionTypeNode(resolvedTypeNode) ? [...resolvedTypeNode.types] : [];
|
|
2603
|
+
}
|
|
2604
|
+
function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
|
|
2605
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
2606
|
+
return resolveAliasedTypeNode(typeNode.type, checker, visited);
|
|
2607
|
+
}
|
|
2608
|
+
if (!ts4.isTypeReferenceNode(typeNode) || !ts4.isIdentifier(typeNode.typeName)) {
|
|
2609
|
+
return typeNode;
|
|
2610
|
+
}
|
|
2611
|
+
const symbol = checker.getSymbolAtLocation(typeNode.typeName);
|
|
2612
|
+
const aliasDecl = symbol?.declarations?.find(ts4.isTypeAliasDeclaration);
|
|
2613
|
+
if (aliasDecl === void 0 || visited.has(aliasDecl)) {
|
|
2614
|
+
return typeNode;
|
|
2615
|
+
}
|
|
2616
|
+
visited.add(aliasDecl);
|
|
2617
|
+
return resolveAliasedTypeNode(aliasDecl.type, checker, visited);
|
|
2618
|
+
}
|
|
2619
|
+
function isNullishTypeNode(typeNode) {
|
|
2620
|
+
if (typeNode.kind === ts4.SyntaxKind.NullKeyword || typeNode.kind === ts4.SyntaxKind.UndefinedKeyword) {
|
|
2621
|
+
return true;
|
|
2622
|
+
}
|
|
2623
|
+
return ts4.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts4.SyntaxKind.NullKeyword || typeNode.literal.kind === ts4.SyntaxKind.UndefinedKeyword);
|
|
2624
|
+
}
|
|
2625
|
+
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
2219
2626
|
const map = /* @__PURE__ */ new Map();
|
|
2220
2627
|
for (const member of members) {
|
|
2221
2628
|
if (ts4.isPropertySignature(member)) {
|
|
2222
|
-
const fieldNode = analyzeInterfacePropertyToIR(
|
|
2629
|
+
const fieldNode = analyzeInterfacePropertyToIR(
|
|
2630
|
+
member,
|
|
2631
|
+
checker,
|
|
2632
|
+
file,
|
|
2633
|
+
typeRegistry,
|
|
2634
|
+
visiting,
|
|
2635
|
+
extensionRegistry
|
|
2636
|
+
);
|
|
2223
2637
|
if (fieldNode) {
|
|
2224
2638
|
map.set(fieldNode.name, {
|
|
2225
2639
|
constraints: [...fieldNode.constraints],
|
|
@@ -2232,7 +2646,7 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting) {
|
|
|
2232
2646
|
return map;
|
|
2233
2647
|
}
|
|
2234
2648
|
var MAX_ALIAS_CHAIN_DEPTH = 8;
|
|
2235
|
-
function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
|
|
2649
|
+
function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegistry, depth = 0) {
|
|
2236
2650
|
if (!ts4.isTypeReferenceNode(typeNode)) return [];
|
|
2237
2651
|
if (depth >= MAX_ALIAS_CHAIN_DEPTH) {
|
|
2238
2652
|
const aliasName = typeNode.typeName.getText();
|
|
@@ -2245,8 +2659,29 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
|
|
|
2245
2659
|
const aliasDecl = symbol.declarations.find(ts4.isTypeAliasDeclaration);
|
|
2246
2660
|
if (!aliasDecl) return [];
|
|
2247
2661
|
if (ts4.isTypeLiteralNode(aliasDecl.type)) return [];
|
|
2248
|
-
const
|
|
2249
|
-
|
|
2662
|
+
const aliasFieldType = resolveTypeNode(
|
|
2663
|
+
checker.getTypeAtLocation(aliasDecl.type),
|
|
2664
|
+
checker,
|
|
2665
|
+
file,
|
|
2666
|
+
{},
|
|
2667
|
+
/* @__PURE__ */ new Set(),
|
|
2668
|
+
aliasDecl.type,
|
|
2669
|
+
extensionRegistry
|
|
2670
|
+
);
|
|
2671
|
+
const constraints = extractJSDocConstraintNodes(
|
|
2672
|
+
aliasDecl,
|
|
2673
|
+
file,
|
|
2674
|
+
makeParseOptions(extensionRegistry, aliasFieldType)
|
|
2675
|
+
);
|
|
2676
|
+
constraints.push(
|
|
2677
|
+
...extractTypeAliasConstraintNodes(
|
|
2678
|
+
aliasDecl.type,
|
|
2679
|
+
checker,
|
|
2680
|
+
file,
|
|
2681
|
+
extensionRegistry,
|
|
2682
|
+
depth + 1
|
|
2683
|
+
)
|
|
2684
|
+
);
|
|
2250
2685
|
return constraints;
|
|
2251
2686
|
}
|
|
2252
2687
|
function provenanceForNode(node, file) {
|
|
@@ -2342,10 +2777,10 @@ function detectFormSpecReference(typeNode) {
|
|
|
2342
2777
|
}
|
|
2343
2778
|
|
|
2344
2779
|
// src/generators/class-schema.ts
|
|
2345
|
-
function generateClassSchemas(analysis, source) {
|
|
2780
|
+
function generateClassSchemas(analysis, source, options) {
|
|
2346
2781
|
const ir = canonicalizeTSDoc(analysis, source);
|
|
2347
2782
|
return {
|
|
2348
|
-
jsonSchema: generateJsonSchemaFromIR(ir),
|
|
2783
|
+
jsonSchema: generateJsonSchemaFromIR(ir, options),
|
|
2349
2784
|
uiSchema: generateUiSchemaFromIR(ir)
|
|
2350
2785
|
};
|
|
2351
2786
|
}
|
|
@@ -2355,27 +2790,54 @@ function generateSchemasFromClass(options) {
|
|
|
2355
2790
|
if (!classDecl) {
|
|
2356
2791
|
throw new Error(`Class "${options.className}" not found in ${options.filePath}`);
|
|
2357
2792
|
}
|
|
2358
|
-
const analysis = analyzeClassToIR(
|
|
2359
|
-
|
|
2793
|
+
const analysis = analyzeClassToIR(
|
|
2794
|
+
classDecl,
|
|
2795
|
+
ctx.checker,
|
|
2796
|
+
options.filePath,
|
|
2797
|
+
options.extensionRegistry
|
|
2798
|
+
);
|
|
2799
|
+
return generateClassSchemas(
|
|
2800
|
+
analysis,
|
|
2801
|
+
{ file: options.filePath },
|
|
2802
|
+
{
|
|
2803
|
+
extensionRegistry: options.extensionRegistry,
|
|
2804
|
+
vendorPrefix: options.vendorPrefix
|
|
2805
|
+
}
|
|
2806
|
+
);
|
|
2360
2807
|
}
|
|
2361
2808
|
function generateSchemas(options) {
|
|
2362
2809
|
const ctx = createProgramContext(options.filePath);
|
|
2363
2810
|
const source = { file: options.filePath };
|
|
2364
2811
|
const classDecl = findClassByName(ctx.sourceFile, options.typeName);
|
|
2365
2812
|
if (classDecl) {
|
|
2366
|
-
const analysis = analyzeClassToIR(
|
|
2367
|
-
|
|
2813
|
+
const analysis = analyzeClassToIR(
|
|
2814
|
+
classDecl,
|
|
2815
|
+
ctx.checker,
|
|
2816
|
+
options.filePath,
|
|
2817
|
+
options.extensionRegistry
|
|
2818
|
+
);
|
|
2819
|
+
return generateClassSchemas(analysis, source, options);
|
|
2368
2820
|
}
|
|
2369
2821
|
const interfaceDecl = findInterfaceByName(ctx.sourceFile, options.typeName);
|
|
2370
2822
|
if (interfaceDecl) {
|
|
2371
|
-
const analysis = analyzeInterfaceToIR(
|
|
2372
|
-
|
|
2823
|
+
const analysis = analyzeInterfaceToIR(
|
|
2824
|
+
interfaceDecl,
|
|
2825
|
+
ctx.checker,
|
|
2826
|
+
options.filePath,
|
|
2827
|
+
options.extensionRegistry
|
|
2828
|
+
);
|
|
2829
|
+
return generateClassSchemas(analysis, source, options);
|
|
2373
2830
|
}
|
|
2374
2831
|
const typeAlias = findTypeAliasByName(ctx.sourceFile, options.typeName);
|
|
2375
2832
|
if (typeAlias) {
|
|
2376
|
-
const result = analyzeTypeAliasToIR(
|
|
2833
|
+
const result = analyzeTypeAliasToIR(
|
|
2834
|
+
typeAlias,
|
|
2835
|
+
ctx.checker,
|
|
2836
|
+
options.filePath,
|
|
2837
|
+
options.extensionRegistry
|
|
2838
|
+
);
|
|
2377
2839
|
if (result.ok) {
|
|
2378
|
-
return generateClassSchemas(result.analysis, source);
|
|
2840
|
+
return generateClassSchemas(result.analysis, source, options);
|
|
2379
2841
|
}
|
|
2380
2842
|
throw new Error(result.error);
|
|
2381
2843
|
}
|
|
@@ -2387,7 +2849,7 @@ function generateSchemas(options) {
|
|
|
2387
2849
|
// src/generators/mixed-authoring.ts
|
|
2388
2850
|
function buildMixedAuthoringSchemas(options) {
|
|
2389
2851
|
const { filePath, typeName, overlays, ...schemaOptions } = options;
|
|
2390
|
-
const analysis = analyzeNamedType(filePath, typeName);
|
|
2852
|
+
const analysis = analyzeNamedType(filePath, typeName, schemaOptions.extensionRegistry);
|
|
2391
2853
|
const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays);
|
|
2392
2854
|
const ir = canonicalizeTSDoc(composedAnalysis, { file: filePath });
|
|
2393
2855
|
return {
|
|
@@ -2395,20 +2857,20 @@ function buildMixedAuthoringSchemas(options) {
|
|
|
2395
2857
|
uiSchema: generateUiSchemaFromIR(ir)
|
|
2396
2858
|
};
|
|
2397
2859
|
}
|
|
2398
|
-
function analyzeNamedType(filePath, typeName) {
|
|
2860
|
+
function analyzeNamedType(filePath, typeName, extensionRegistry) {
|
|
2399
2861
|
const ctx = createProgramContext(filePath);
|
|
2400
2862
|
const source = { file: filePath };
|
|
2401
2863
|
const classDecl = findClassByName(ctx.sourceFile, typeName);
|
|
2402
2864
|
if (classDecl !== null) {
|
|
2403
|
-
return analyzeClassToIR(classDecl, ctx.checker, source.file);
|
|
2865
|
+
return analyzeClassToIR(classDecl, ctx.checker, source.file, extensionRegistry);
|
|
2404
2866
|
}
|
|
2405
2867
|
const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
|
|
2406
2868
|
if (interfaceDecl !== null) {
|
|
2407
|
-
return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file);
|
|
2869
|
+
return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file, extensionRegistry);
|
|
2408
2870
|
}
|
|
2409
2871
|
const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
|
|
2410
2872
|
if (typeAlias !== null) {
|
|
2411
|
-
const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file);
|
|
2873
|
+
const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file, extensionRegistry);
|
|
2412
2874
|
if (result.ok) {
|
|
2413
2875
|
return result.analysis;
|
|
2414
2876
|
}
|
|
@@ -2487,7 +2949,7 @@ function assertSupportedOverlayField(baseField, overlayField) {
|
|
|
2487
2949
|
`Mixed-authoring overlay for "${baseField.name}" cannot define constraints; keep constraints on the static model`
|
|
2488
2950
|
);
|
|
2489
2951
|
}
|
|
2490
|
-
if (overlayField.required) {
|
|
2952
|
+
if (overlayField.required && !baseField.required) {
|
|
2491
2953
|
throw new Error(
|
|
2492
2954
|
`Mixed-authoring overlay for "${baseField.name}" cannot change requiredness; keep requiredness on the static model`
|
|
2493
2955
|
);
|
|
@@ -2577,7 +3039,7 @@ function mergeAnnotations(baseAnnotations, overlayAnnotations) {
|
|
|
2577
3039
|
const overlayOnly = overlayAnnotations.filter(
|
|
2578
3040
|
(annotation) => !baseKeys.has(annotationKey(annotation))
|
|
2579
3041
|
);
|
|
2580
|
-
return [...
|
|
3042
|
+
return [...baseAnnotations, ...overlayOnly];
|
|
2581
3043
|
}
|
|
2582
3044
|
function annotationKey(annotation) {
|
|
2583
3045
|
return annotation.annotationKind === "custom" ? `${annotation.annotationKind}:${annotation.annotationId}` : annotation.annotationKind;
|