@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.cjs
CHANGED
|
@@ -838,7 +838,12 @@ function applyCustomConstraint(schema, constraint, ctx) {
|
|
|
838
838
|
`Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
|
|
839
839
|
);
|
|
840
840
|
}
|
|
841
|
-
|
|
841
|
+
assignVendorPrefixedExtensionKeywords(
|
|
842
|
+
schema,
|
|
843
|
+
registration.toJsonSchema(constraint.payload, ctx.vendorPrefix),
|
|
844
|
+
ctx.vendorPrefix,
|
|
845
|
+
`custom constraint "${constraint.constraintId}"`
|
|
846
|
+
);
|
|
842
847
|
}
|
|
843
848
|
function applyCustomAnnotation(schema, annotation, ctx) {
|
|
844
849
|
const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
|
|
@@ -850,7 +855,22 @@ function applyCustomAnnotation(schema, annotation, ctx) {
|
|
|
850
855
|
if (registration.toJsonSchema === void 0) {
|
|
851
856
|
return;
|
|
852
857
|
}
|
|
853
|
-
|
|
858
|
+
assignVendorPrefixedExtensionKeywords(
|
|
859
|
+
schema,
|
|
860
|
+
registration.toJsonSchema(annotation.value, ctx.vendorPrefix),
|
|
861
|
+
ctx.vendorPrefix,
|
|
862
|
+
`custom annotation "${annotation.annotationId}"`
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPrefix, source) {
|
|
866
|
+
for (const [key, value] of Object.entries(extensionSchema)) {
|
|
867
|
+
if (!key.startsWith(`${vendorPrefix}-`)) {
|
|
868
|
+
throw new Error(
|
|
869
|
+
`Cannot apply ${source}: extension hooks may only emit "${vendorPrefix}-*" JSON Schema keywords`
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
schema[key] = value;
|
|
873
|
+
}
|
|
854
874
|
}
|
|
855
875
|
|
|
856
876
|
// src/json-schema/generator.ts
|
|
@@ -1103,7 +1123,10 @@ function getSchemaExtension(schema, key) {
|
|
|
1103
1123
|
// src/extensions/registry.ts
|
|
1104
1124
|
function createExtensionRegistry(extensions) {
|
|
1105
1125
|
const typeMap = /* @__PURE__ */ new Map();
|
|
1126
|
+
const typeNameMap = /* @__PURE__ */ new Map();
|
|
1106
1127
|
const constraintMap = /* @__PURE__ */ new Map();
|
|
1128
|
+
const constraintTagMap = /* @__PURE__ */ new Map();
|
|
1129
|
+
const builtinBroadeningMap = /* @__PURE__ */ new Map();
|
|
1107
1130
|
const annotationMap = /* @__PURE__ */ new Map();
|
|
1108
1131
|
for (const ext of extensions) {
|
|
1109
1132
|
if (ext.types !== void 0) {
|
|
@@ -1113,6 +1136,27 @@ function createExtensionRegistry(extensions) {
|
|
|
1113
1136
|
throw new Error(`Duplicate custom type ID: "${qualifiedId}"`);
|
|
1114
1137
|
}
|
|
1115
1138
|
typeMap.set(qualifiedId, type);
|
|
1139
|
+
for (const sourceTypeName of type.tsTypeNames ?? [type.typeName]) {
|
|
1140
|
+
if (typeNameMap.has(sourceTypeName)) {
|
|
1141
|
+
throw new Error(`Duplicate custom type source name: "${sourceTypeName}"`);
|
|
1142
|
+
}
|
|
1143
|
+
typeNameMap.set(sourceTypeName, {
|
|
1144
|
+
extensionId: ext.extensionId,
|
|
1145
|
+
registration: type
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
if (type.builtinConstraintBroadenings !== void 0) {
|
|
1149
|
+
for (const broadening of type.builtinConstraintBroadenings) {
|
|
1150
|
+
const key = `${qualifiedId}:${broadening.tagName}`;
|
|
1151
|
+
if (builtinBroadeningMap.has(key)) {
|
|
1152
|
+
throw new Error(`Duplicate built-in constraint broadening: "${key}"`);
|
|
1153
|
+
}
|
|
1154
|
+
builtinBroadeningMap.set(key, {
|
|
1155
|
+
extensionId: ext.extensionId,
|
|
1156
|
+
registration: broadening
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1116
1160
|
}
|
|
1117
1161
|
}
|
|
1118
1162
|
if (ext.constraints !== void 0) {
|
|
@@ -1124,6 +1168,17 @@ function createExtensionRegistry(extensions) {
|
|
|
1124
1168
|
constraintMap.set(qualifiedId, constraint);
|
|
1125
1169
|
}
|
|
1126
1170
|
}
|
|
1171
|
+
if (ext.constraintTags !== void 0) {
|
|
1172
|
+
for (const tag of ext.constraintTags) {
|
|
1173
|
+
if (constraintTagMap.has(tag.tagName)) {
|
|
1174
|
+
throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
|
|
1175
|
+
}
|
|
1176
|
+
constraintTagMap.set(tag.tagName, {
|
|
1177
|
+
extensionId: ext.extensionId,
|
|
1178
|
+
registration: tag
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1127
1182
|
if (ext.annotations !== void 0) {
|
|
1128
1183
|
for (const annotation of ext.annotations) {
|
|
1129
1184
|
const qualifiedId = `${ext.extensionId}/${annotation.annotationName}`;
|
|
@@ -1137,7 +1192,10 @@ function createExtensionRegistry(extensions) {
|
|
|
1137
1192
|
return {
|
|
1138
1193
|
extensions,
|
|
1139
1194
|
findType: (typeId) => typeMap.get(typeId),
|
|
1195
|
+
findTypeByName: (typeName) => typeNameMap.get(typeName),
|
|
1140
1196
|
findConstraint: (constraintId) => constraintMap.get(constraintId),
|
|
1197
|
+
findConstraintTag: (tagName) => constraintTagMap.get(tagName),
|
|
1198
|
+
findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
|
|
1141
1199
|
findAnnotation: (annotationId) => annotationMap.get(annotationId)
|
|
1142
1200
|
};
|
|
1143
1201
|
}
|
|
@@ -1311,7 +1369,7 @@ var LENGTH_CONSTRAINT_MAP = {
|
|
|
1311
1369
|
maxItems: "maxItems"
|
|
1312
1370
|
};
|
|
1313
1371
|
var TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions", "defaultValue"]);
|
|
1314
|
-
function createFormSpecTSDocConfig() {
|
|
1372
|
+
function createFormSpecTSDocConfig(extensionTagNames = []) {
|
|
1315
1373
|
const config = new import_tsdoc.TSDocConfiguration();
|
|
1316
1374
|
for (const tagName of Object.keys(import_core3.BUILTIN_CONSTRAINT_DEFINITIONS)) {
|
|
1317
1375
|
config.addTagDefinition(
|
|
@@ -1331,14 +1389,34 @@ function createFormSpecTSDocConfig() {
|
|
|
1331
1389
|
})
|
|
1332
1390
|
);
|
|
1333
1391
|
}
|
|
1392
|
+
for (const tagName of extensionTagNames) {
|
|
1393
|
+
config.addTagDefinition(
|
|
1394
|
+
new import_tsdoc.TSDocTagDefinition({
|
|
1395
|
+
tagName: "@" + tagName,
|
|
1396
|
+
syntaxKind: import_tsdoc.TSDocTagSyntaxKind.BlockTag,
|
|
1397
|
+
allowMultiple: true
|
|
1398
|
+
})
|
|
1399
|
+
);
|
|
1400
|
+
}
|
|
1334
1401
|
return config;
|
|
1335
1402
|
}
|
|
1336
|
-
var
|
|
1337
|
-
function getParser() {
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1403
|
+
var parserCache = /* @__PURE__ */ new Map();
|
|
1404
|
+
function getParser(options) {
|
|
1405
|
+
const extensionTagNames = [
|
|
1406
|
+
...options?.extensionRegistry?.extensions.flatMap(
|
|
1407
|
+
(extension) => (extension.constraintTags ?? []).map((tag) => tag.tagName)
|
|
1408
|
+
) ?? []
|
|
1409
|
+
].sort();
|
|
1410
|
+
const cacheKey = extensionTagNames.join("|");
|
|
1411
|
+
const existing = parserCache.get(cacheKey);
|
|
1412
|
+
if (existing) {
|
|
1413
|
+
return existing;
|
|
1414
|
+
}
|
|
1415
|
+
const parser = new import_tsdoc.TSDocParser(createFormSpecTSDocConfig(extensionTagNames));
|
|
1416
|
+
parserCache.set(cacheKey, parser);
|
|
1417
|
+
return parser;
|
|
1418
|
+
}
|
|
1419
|
+
function parseTSDocTags(node, file = "", options) {
|
|
1342
1420
|
const constraints = [];
|
|
1343
1421
|
const annotations = [];
|
|
1344
1422
|
let displayName;
|
|
@@ -1359,7 +1437,7 @@ function parseTSDocTags(node, file = "") {
|
|
|
1359
1437
|
if (!commentText.startsWith("/**")) {
|
|
1360
1438
|
continue;
|
|
1361
1439
|
}
|
|
1362
|
-
const parser = getParser();
|
|
1440
|
+
const parser = getParser(options);
|
|
1363
1441
|
const parserContext = parser.parseRange(
|
|
1364
1442
|
import_tsdoc.TextRange.fromStringRange(sourceText, range.pos, range.end)
|
|
1365
1443
|
);
|
|
@@ -1398,7 +1476,7 @@ function parseTSDocTags(node, file = "") {
|
|
|
1398
1476
|
const expectedType = (0, import_core3.isBuiltinConstraintName)(tagName) ? import_core3.BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
|
|
1399
1477
|
if (text === "" && expectedType !== "boolean") continue;
|
|
1400
1478
|
const provenance = provenanceForComment(range, sourceFile, file, tagName);
|
|
1401
|
-
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
1479
|
+
const constraintNode = parseConstraintValue(tagName, text, provenance, options);
|
|
1402
1480
|
if (constraintNode) {
|
|
1403
1481
|
constraints.push(constraintNode);
|
|
1404
1482
|
}
|
|
@@ -1458,7 +1536,7 @@ function parseTSDocTags(node, file = "") {
|
|
|
1458
1536
|
annotations.push(defaultValueNode);
|
|
1459
1537
|
continue;
|
|
1460
1538
|
}
|
|
1461
|
-
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
1539
|
+
const constraintNode = parseConstraintValue(tagName, text, provenance, options);
|
|
1462
1540
|
if (constraintNode) {
|
|
1463
1541
|
constraints.push(constraintNode);
|
|
1464
1542
|
}
|
|
@@ -1514,7 +1592,11 @@ function extractPlainText(node) {
|
|
|
1514
1592
|
}
|
|
1515
1593
|
return result;
|
|
1516
1594
|
}
|
|
1517
|
-
function parseConstraintValue(tagName, text, provenance) {
|
|
1595
|
+
function parseConstraintValue(tagName, text, provenance, options) {
|
|
1596
|
+
const customConstraint = parseExtensionConstraintValue(tagName, text, provenance, options);
|
|
1597
|
+
if (customConstraint) {
|
|
1598
|
+
return customConstraint;
|
|
1599
|
+
}
|
|
1518
1600
|
if (!(0, import_core3.isBuiltinConstraintName)(tagName)) {
|
|
1519
1601
|
return null;
|
|
1520
1602
|
}
|
|
@@ -1619,6 +1701,83 @@ function parseConstraintValue(tagName, text, provenance) {
|
|
|
1619
1701
|
provenance
|
|
1620
1702
|
};
|
|
1621
1703
|
}
|
|
1704
|
+
function parseExtensionConstraintValue(tagName, text, provenance, options) {
|
|
1705
|
+
const pathResult = extractPathTarget(text);
|
|
1706
|
+
const effectiveText = pathResult ? pathResult.remainingText : text;
|
|
1707
|
+
const path3 = pathResult?.path;
|
|
1708
|
+
const registry = options?.extensionRegistry;
|
|
1709
|
+
if (registry === void 0) {
|
|
1710
|
+
return null;
|
|
1711
|
+
}
|
|
1712
|
+
const directTag = registry.findConstraintTag(tagName);
|
|
1713
|
+
if (directTag !== void 0) {
|
|
1714
|
+
return makeCustomConstraintNode(
|
|
1715
|
+
directTag.extensionId,
|
|
1716
|
+
directTag.registration.constraintName,
|
|
1717
|
+
directTag.registration.parseValue(effectiveText),
|
|
1718
|
+
provenance,
|
|
1719
|
+
path3,
|
|
1720
|
+
registry
|
|
1721
|
+
);
|
|
1722
|
+
}
|
|
1723
|
+
if (!(0, import_core3.isBuiltinConstraintName)(tagName)) {
|
|
1724
|
+
return null;
|
|
1725
|
+
}
|
|
1726
|
+
const broadenedTypeId = getBroadenedCustomTypeId(options?.fieldType);
|
|
1727
|
+
if (broadenedTypeId === void 0) {
|
|
1728
|
+
return null;
|
|
1729
|
+
}
|
|
1730
|
+
const broadened = registry.findBuiltinConstraintBroadening(broadenedTypeId, tagName);
|
|
1731
|
+
if (broadened === void 0) {
|
|
1732
|
+
return null;
|
|
1733
|
+
}
|
|
1734
|
+
return makeCustomConstraintNode(
|
|
1735
|
+
broadened.extensionId,
|
|
1736
|
+
broadened.registration.constraintName,
|
|
1737
|
+
broadened.registration.parseValue(effectiveText),
|
|
1738
|
+
provenance,
|
|
1739
|
+
path3,
|
|
1740
|
+
registry
|
|
1741
|
+
);
|
|
1742
|
+
}
|
|
1743
|
+
function getBroadenedCustomTypeId(fieldType) {
|
|
1744
|
+
if (fieldType?.kind === "custom") {
|
|
1745
|
+
return fieldType.typeId;
|
|
1746
|
+
}
|
|
1747
|
+
if (fieldType?.kind !== "union") {
|
|
1748
|
+
return void 0;
|
|
1749
|
+
}
|
|
1750
|
+
const customMembers = fieldType.members.filter(
|
|
1751
|
+
(member) => member.kind === "custom"
|
|
1752
|
+
);
|
|
1753
|
+
if (customMembers.length !== 1) {
|
|
1754
|
+
return void 0;
|
|
1755
|
+
}
|
|
1756
|
+
const nonCustomMembers = fieldType.members.filter((member) => member.kind !== "custom");
|
|
1757
|
+
const allOtherMembersAreNull = nonCustomMembers.every(
|
|
1758
|
+
(member) => member.kind === "primitive" && member.primitiveKind === "null"
|
|
1759
|
+
);
|
|
1760
|
+
const customMember = customMembers[0];
|
|
1761
|
+
return allOtherMembersAreNull && customMember !== void 0 ? customMember.typeId : void 0;
|
|
1762
|
+
}
|
|
1763
|
+
function makeCustomConstraintNode(extensionId, constraintName, payload, provenance, path3, registry) {
|
|
1764
|
+
const constraintId = `${extensionId}/${constraintName}`;
|
|
1765
|
+
const registration = registry.findConstraint(constraintId);
|
|
1766
|
+
if (registration === void 0) {
|
|
1767
|
+
throw new Error(
|
|
1768
|
+
`Custom TSDoc tag resolved to unregistered constraint "${constraintId}". Register the constraint before using its tag.`
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
return {
|
|
1772
|
+
kind: "constraint",
|
|
1773
|
+
constraintKind: "custom",
|
|
1774
|
+
constraintId,
|
|
1775
|
+
payload,
|
|
1776
|
+
compositionRule: registration.compositionRule,
|
|
1777
|
+
...path3 && { path: path3 },
|
|
1778
|
+
provenance
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1622
1781
|
function parseDefaultValueValue(text, provenance) {
|
|
1623
1782
|
const trimmed = text.trim();
|
|
1624
1783
|
let value;
|
|
@@ -1679,12 +1838,12 @@ function getTagCommentText(tag) {
|
|
|
1679
1838
|
}
|
|
1680
1839
|
|
|
1681
1840
|
// src/analyzer/jsdoc-constraints.ts
|
|
1682
|
-
function extractJSDocConstraintNodes(node, file = "") {
|
|
1683
|
-
const result = parseTSDocTags(node, file);
|
|
1841
|
+
function extractJSDocConstraintNodes(node, file = "", options) {
|
|
1842
|
+
const result = parseTSDocTags(node, file, options);
|
|
1684
1843
|
return [...result.constraints];
|
|
1685
1844
|
}
|
|
1686
|
-
function extractJSDocAnnotationNodes(node, file = "") {
|
|
1687
|
-
const result = parseTSDocTags(node, file);
|
|
1845
|
+
function extractJSDocAnnotationNodes(node, file = "", options) {
|
|
1846
|
+
const result = parseTSDocTags(node, file, options);
|
|
1688
1847
|
return [...result.annotations];
|
|
1689
1848
|
}
|
|
1690
1849
|
function extractDefaultValueAnnotation(initializer, file = "") {
|
|
@@ -1733,18 +1892,38 @@ var RESOLVING_TYPE_PLACEHOLDER = {
|
|
|
1733
1892
|
properties: [],
|
|
1734
1893
|
additionalProperties: true
|
|
1735
1894
|
};
|
|
1736
|
-
function
|
|
1895
|
+
function makeParseOptions(extensionRegistry, fieldType) {
|
|
1896
|
+
if (extensionRegistry === void 0 && fieldType === void 0) {
|
|
1897
|
+
return void 0;
|
|
1898
|
+
}
|
|
1899
|
+
return {
|
|
1900
|
+
...extensionRegistry !== void 0 && { extensionRegistry },
|
|
1901
|
+
...fieldType !== void 0 && { fieldType }
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
1737
1905
|
const name = classDecl.name?.text ?? "AnonymousClass";
|
|
1738
1906
|
const fields = [];
|
|
1739
1907
|
const fieldLayouts = [];
|
|
1740
1908
|
const typeRegistry = {};
|
|
1741
|
-
const annotations = extractJSDocAnnotationNodes(
|
|
1909
|
+
const annotations = extractJSDocAnnotationNodes(
|
|
1910
|
+
classDecl,
|
|
1911
|
+
file,
|
|
1912
|
+
makeParseOptions(extensionRegistry)
|
|
1913
|
+
);
|
|
1742
1914
|
const visiting = /* @__PURE__ */ new Set();
|
|
1743
1915
|
const instanceMethods = [];
|
|
1744
1916
|
const staticMethods = [];
|
|
1745
1917
|
for (const member of classDecl.members) {
|
|
1746
1918
|
if (ts4.isPropertyDeclaration(member)) {
|
|
1747
|
-
const fieldNode = analyzeFieldToIR(
|
|
1919
|
+
const fieldNode = analyzeFieldToIR(
|
|
1920
|
+
member,
|
|
1921
|
+
checker,
|
|
1922
|
+
file,
|
|
1923
|
+
typeRegistry,
|
|
1924
|
+
visiting,
|
|
1925
|
+
extensionRegistry
|
|
1926
|
+
);
|
|
1748
1927
|
if (fieldNode) {
|
|
1749
1928
|
fields.push(fieldNode);
|
|
1750
1929
|
fieldLayouts.push({});
|
|
@@ -1771,15 +1950,26 @@ function analyzeClassToIR(classDecl, checker, file = "") {
|
|
|
1771
1950
|
staticMethods
|
|
1772
1951
|
};
|
|
1773
1952
|
}
|
|
1774
|
-
function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
1953
|
+
function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry) {
|
|
1775
1954
|
const name = interfaceDecl.name.text;
|
|
1776
1955
|
const fields = [];
|
|
1777
1956
|
const typeRegistry = {};
|
|
1778
|
-
const annotations = extractJSDocAnnotationNodes(
|
|
1957
|
+
const annotations = extractJSDocAnnotationNodes(
|
|
1958
|
+
interfaceDecl,
|
|
1959
|
+
file,
|
|
1960
|
+
makeParseOptions(extensionRegistry)
|
|
1961
|
+
);
|
|
1779
1962
|
const visiting = /* @__PURE__ */ new Set();
|
|
1780
1963
|
for (const member of interfaceDecl.members) {
|
|
1781
1964
|
if (ts4.isPropertySignature(member)) {
|
|
1782
|
-
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1965
|
+
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1966
|
+
member,
|
|
1967
|
+
checker,
|
|
1968
|
+
file,
|
|
1969
|
+
typeRegistry,
|
|
1970
|
+
visiting,
|
|
1971
|
+
extensionRegistry
|
|
1972
|
+
);
|
|
1783
1973
|
if (fieldNode) {
|
|
1784
1974
|
fields.push(fieldNode);
|
|
1785
1975
|
}
|
|
@@ -1796,7 +1986,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
|
1796
1986
|
staticMethods: []
|
|
1797
1987
|
};
|
|
1798
1988
|
}
|
|
1799
|
-
function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
1989
|
+
function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
|
|
1800
1990
|
if (!ts4.isTypeLiteralNode(typeAlias.type)) {
|
|
1801
1991
|
const sourceFile = typeAlias.getSourceFile();
|
|
1802
1992
|
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
@@ -1809,11 +1999,22 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
|
1809
1999
|
const name = typeAlias.name.text;
|
|
1810
2000
|
const fields = [];
|
|
1811
2001
|
const typeRegistry = {};
|
|
1812
|
-
const annotations = extractJSDocAnnotationNodes(
|
|
2002
|
+
const annotations = extractJSDocAnnotationNodes(
|
|
2003
|
+
typeAlias,
|
|
2004
|
+
file,
|
|
2005
|
+
makeParseOptions(extensionRegistry)
|
|
2006
|
+
);
|
|
1813
2007
|
const visiting = /* @__PURE__ */ new Set();
|
|
1814
2008
|
for (const member of typeAlias.type.members) {
|
|
1815
2009
|
if (ts4.isPropertySignature(member)) {
|
|
1816
|
-
const fieldNode = analyzeInterfacePropertyToIR(
|
|
2010
|
+
const fieldNode = analyzeInterfacePropertyToIR(
|
|
2011
|
+
member,
|
|
2012
|
+
checker,
|
|
2013
|
+
file,
|
|
2014
|
+
typeRegistry,
|
|
2015
|
+
visiting,
|
|
2016
|
+
extensionRegistry
|
|
2017
|
+
);
|
|
1817
2018
|
if (fieldNode) {
|
|
1818
2019
|
fields.push(fieldNode);
|
|
1819
2020
|
}
|
|
@@ -1832,7 +2033,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
|
1832
2033
|
}
|
|
1833
2034
|
};
|
|
1834
2035
|
}
|
|
1835
|
-
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
2036
|
+
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1836
2037
|
if (!ts4.isIdentifier(prop.name)) {
|
|
1837
2038
|
return null;
|
|
1838
2039
|
}
|
|
@@ -1840,14 +2041,26 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
|
1840
2041
|
const tsType = checker.getTypeAtLocation(prop);
|
|
1841
2042
|
const optional = prop.questionToken !== void 0;
|
|
1842
2043
|
const provenance = provenanceForNode(prop, file);
|
|
1843
|
-
let type = resolveTypeNode(
|
|
2044
|
+
let type = resolveTypeNode(
|
|
2045
|
+
tsType,
|
|
2046
|
+
checker,
|
|
2047
|
+
file,
|
|
2048
|
+
typeRegistry,
|
|
2049
|
+
visiting,
|
|
2050
|
+
prop,
|
|
2051
|
+
extensionRegistry
|
|
2052
|
+
);
|
|
1844
2053
|
const constraints = [];
|
|
1845
2054
|
if (prop.type) {
|
|
1846
|
-
constraints.push(
|
|
2055
|
+
constraints.push(
|
|
2056
|
+
...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
|
|
2057
|
+
);
|
|
1847
2058
|
}
|
|
1848
|
-
constraints.push(...extractJSDocConstraintNodes(prop, file));
|
|
2059
|
+
constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
|
|
1849
2060
|
let annotations = [];
|
|
1850
|
-
annotations.push(
|
|
2061
|
+
annotations.push(
|
|
2062
|
+
...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
2063
|
+
);
|
|
1851
2064
|
const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
|
|
1852
2065
|
if (defaultAnnotation && !annotations.some((a) => a.annotationKind === "defaultValue")) {
|
|
1853
2066
|
annotations.push(defaultAnnotation);
|
|
@@ -1863,7 +2076,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
|
1863
2076
|
provenance
|
|
1864
2077
|
};
|
|
1865
2078
|
}
|
|
1866
|
-
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting) {
|
|
2079
|
+
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1867
2080
|
if (!ts4.isIdentifier(prop.name)) {
|
|
1868
2081
|
return null;
|
|
1869
2082
|
}
|
|
@@ -1871,14 +2084,26 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
1871
2084
|
const tsType = checker.getTypeAtLocation(prop);
|
|
1872
2085
|
const optional = prop.questionToken !== void 0;
|
|
1873
2086
|
const provenance = provenanceForNode(prop, file);
|
|
1874
|
-
let type = resolveTypeNode(
|
|
2087
|
+
let type = resolveTypeNode(
|
|
2088
|
+
tsType,
|
|
2089
|
+
checker,
|
|
2090
|
+
file,
|
|
2091
|
+
typeRegistry,
|
|
2092
|
+
visiting,
|
|
2093
|
+
prop,
|
|
2094
|
+
extensionRegistry
|
|
2095
|
+
);
|
|
1875
2096
|
const constraints = [];
|
|
1876
2097
|
if (prop.type) {
|
|
1877
|
-
constraints.push(
|
|
2098
|
+
constraints.push(
|
|
2099
|
+
...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
|
|
2100
|
+
);
|
|
1878
2101
|
}
|
|
1879
|
-
constraints.push(...extractJSDocConstraintNodes(prop, file));
|
|
2102
|
+
constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
|
|
1880
2103
|
let annotations = [];
|
|
1881
|
-
annotations.push(
|
|
2104
|
+
annotations.push(
|
|
2105
|
+
...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
2106
|
+
);
|
|
1882
2107
|
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
1883
2108
|
return {
|
|
1884
2109
|
kind: "field",
|
|
@@ -1952,7 +2177,66 @@ function parseEnumMemberDisplayName(value) {
|
|
|
1952
2177
|
if (label === "") return null;
|
|
1953
2178
|
return { value: match[1], label };
|
|
1954
2179
|
}
|
|
1955
|
-
function
|
|
2180
|
+
function resolveRegisteredCustomType(sourceNode, extensionRegistry, checker) {
|
|
2181
|
+
if (sourceNode === void 0 || extensionRegistry === void 0) {
|
|
2182
|
+
return null;
|
|
2183
|
+
}
|
|
2184
|
+
const typeNode = extractTypeNodeFromSource(sourceNode);
|
|
2185
|
+
if (typeNode === void 0) {
|
|
2186
|
+
return null;
|
|
2187
|
+
}
|
|
2188
|
+
return resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker);
|
|
2189
|
+
}
|
|
2190
|
+
function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker) {
|
|
2191
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
2192
|
+
return resolveRegisteredCustomTypeFromTypeNode(typeNode.type, extensionRegistry, checker);
|
|
2193
|
+
}
|
|
2194
|
+
const typeName = getTypeNodeRegistrationName(typeNode);
|
|
2195
|
+
if (typeName === null) {
|
|
2196
|
+
return null;
|
|
2197
|
+
}
|
|
2198
|
+
const registration = extensionRegistry.findTypeByName(typeName);
|
|
2199
|
+
if (registration !== void 0) {
|
|
2200
|
+
return {
|
|
2201
|
+
kind: "custom",
|
|
2202
|
+
typeId: `${registration.extensionId}/${registration.registration.typeName}`,
|
|
2203
|
+
payload: null
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
if (ts4.isTypeReferenceNode(typeNode) && ts4.isIdentifier(typeNode.typeName)) {
|
|
2207
|
+
const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts4.isTypeAliasDeclaration);
|
|
2208
|
+
if (aliasDecl !== void 0) {
|
|
2209
|
+
return resolveRegisteredCustomTypeFromTypeNode(aliasDecl.type, extensionRegistry, checker);
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
return null;
|
|
2213
|
+
}
|
|
2214
|
+
function extractTypeNodeFromSource(sourceNode) {
|
|
2215
|
+
if (ts4.isPropertyDeclaration(sourceNode) || ts4.isPropertySignature(sourceNode) || ts4.isParameter(sourceNode) || ts4.isTypeAliasDeclaration(sourceNode)) {
|
|
2216
|
+
return sourceNode.type;
|
|
2217
|
+
}
|
|
2218
|
+
if (ts4.isTypeNode(sourceNode)) {
|
|
2219
|
+
return sourceNode;
|
|
2220
|
+
}
|
|
2221
|
+
return void 0;
|
|
2222
|
+
}
|
|
2223
|
+
function getTypeNodeRegistrationName(typeNode) {
|
|
2224
|
+
if (ts4.isTypeReferenceNode(typeNode)) {
|
|
2225
|
+
return ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : typeNode.typeName.right.text;
|
|
2226
|
+
}
|
|
2227
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
2228
|
+
return getTypeNodeRegistrationName(typeNode.type);
|
|
2229
|
+
}
|
|
2230
|
+
if (typeNode.kind === ts4.SyntaxKind.BigIntKeyword || typeNode.kind === ts4.SyntaxKind.StringKeyword || typeNode.kind === ts4.SyntaxKind.NumberKeyword || typeNode.kind === ts4.SyntaxKind.BooleanKeyword) {
|
|
2231
|
+
return typeNode.getText();
|
|
2232
|
+
}
|
|
2233
|
+
return null;
|
|
2234
|
+
}
|
|
2235
|
+
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
2236
|
+
const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
|
|
2237
|
+
if (customType) {
|
|
2238
|
+
return customType;
|
|
2239
|
+
}
|
|
1956
2240
|
if (type.flags & ts4.TypeFlags.String) {
|
|
1957
2241
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1958
2242
|
}
|
|
@@ -1981,26 +2265,50 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
1981
2265
|
};
|
|
1982
2266
|
}
|
|
1983
2267
|
if (type.isUnion()) {
|
|
1984
|
-
return resolveUnionType(
|
|
2268
|
+
return resolveUnionType(
|
|
2269
|
+
type,
|
|
2270
|
+
checker,
|
|
2271
|
+
file,
|
|
2272
|
+
typeRegistry,
|
|
2273
|
+
visiting,
|
|
2274
|
+
sourceNode,
|
|
2275
|
+
extensionRegistry
|
|
2276
|
+
);
|
|
1985
2277
|
}
|
|
1986
2278
|
if (checker.isArrayType(type)) {
|
|
1987
|
-
return resolveArrayType(
|
|
2279
|
+
return resolveArrayType(
|
|
2280
|
+
type,
|
|
2281
|
+
checker,
|
|
2282
|
+
file,
|
|
2283
|
+
typeRegistry,
|
|
2284
|
+
visiting,
|
|
2285
|
+
sourceNode,
|
|
2286
|
+
extensionRegistry
|
|
2287
|
+
);
|
|
1988
2288
|
}
|
|
1989
2289
|
if (isObjectType(type)) {
|
|
1990
|
-
return resolveObjectType(type, checker, file, typeRegistry, visiting);
|
|
2290
|
+
return resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry);
|
|
1991
2291
|
}
|
|
1992
2292
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1993
2293
|
}
|
|
1994
|
-
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode) {
|
|
2294
|
+
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
1995
2295
|
const typeName = getNamedTypeName(type);
|
|
1996
2296
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
1997
2297
|
if (typeName && typeName in typeRegistry) {
|
|
1998
2298
|
return { kind: "reference", name: typeName, typeArguments: [] };
|
|
1999
2299
|
}
|
|
2000
2300
|
const allTypes = type.types;
|
|
2301
|
+
const unionMemberTypeNodes = extractUnionMemberTypeNodes(sourceNode, checker);
|
|
2302
|
+
const nonNullSourceNodes = unionMemberTypeNodes.filter(
|
|
2303
|
+
(memberTypeNode) => !isNullishTypeNode(resolveAliasedTypeNode(memberTypeNode, checker))
|
|
2304
|
+
);
|
|
2001
2305
|
const nonNullTypes = allTypes.filter(
|
|
2002
|
-
(
|
|
2306
|
+
(memberType) => !(memberType.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
|
|
2003
2307
|
);
|
|
2308
|
+
const nonNullMembers = nonNullTypes.map((memberType, index) => ({
|
|
2309
|
+
memberType,
|
|
2310
|
+
sourceNode: nonNullSourceNodes.length === nonNullTypes.length ? nonNullSourceNodes[index] : void 0
|
|
2311
|
+
}));
|
|
2004
2312
|
const hasNull = allTypes.some((t) => t.flags & ts4.TypeFlags.Null);
|
|
2005
2313
|
const memberDisplayNames = /* @__PURE__ */ new Map();
|
|
2006
2314
|
if (namedDecl) {
|
|
@@ -2017,7 +2325,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2017
2325
|
if (!typeName) {
|
|
2018
2326
|
return result;
|
|
2019
2327
|
}
|
|
2020
|
-
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2328
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
2021
2329
|
typeRegistry[typeName] = {
|
|
2022
2330
|
name: typeName,
|
|
2023
2331
|
type: result,
|
|
@@ -2065,14 +2373,15 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2065
2373
|
} : enumNode;
|
|
2066
2374
|
return registerNamed(result);
|
|
2067
2375
|
}
|
|
2068
|
-
if (
|
|
2376
|
+
if (nonNullMembers.length === 1 && nonNullMembers[0]) {
|
|
2069
2377
|
const inner = resolveTypeNode(
|
|
2070
|
-
|
|
2378
|
+
nonNullMembers[0].memberType,
|
|
2071
2379
|
checker,
|
|
2072
2380
|
file,
|
|
2073
2381
|
typeRegistry,
|
|
2074
2382
|
visiting,
|
|
2075
|
-
sourceNode
|
|
2383
|
+
nonNullMembers[0].sourceNode ?? sourceNode,
|
|
2384
|
+
extensionRegistry
|
|
2076
2385
|
);
|
|
2077
2386
|
const result = hasNull ? {
|
|
2078
2387
|
kind: "union",
|
|
@@ -2080,21 +2389,38 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2080
2389
|
} : inner;
|
|
2081
2390
|
return registerNamed(result);
|
|
2082
2391
|
}
|
|
2083
|
-
const members =
|
|
2084
|
-
(
|
|
2392
|
+
const members = nonNullMembers.map(
|
|
2393
|
+
({ memberType, sourceNode: memberSourceNode }) => resolveTypeNode(
|
|
2394
|
+
memberType,
|
|
2395
|
+
checker,
|
|
2396
|
+
file,
|
|
2397
|
+
typeRegistry,
|
|
2398
|
+
visiting,
|
|
2399
|
+
memberSourceNode ?? sourceNode,
|
|
2400
|
+
extensionRegistry
|
|
2401
|
+
)
|
|
2085
2402
|
);
|
|
2086
2403
|
if (hasNull) {
|
|
2087
2404
|
members.push({ kind: "primitive", primitiveKind: "null" });
|
|
2088
2405
|
}
|
|
2089
2406
|
return registerNamed({ kind: "union", members });
|
|
2090
2407
|
}
|
|
2091
|
-
function resolveArrayType(type, checker, file, typeRegistry, visiting) {
|
|
2408
|
+
function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
2092
2409
|
const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
|
|
2093
2410
|
const elementType = typeArgs?.[0];
|
|
2094
|
-
const
|
|
2411
|
+
const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
|
|
2412
|
+
const items = elementType ? resolveTypeNode(
|
|
2413
|
+
elementType,
|
|
2414
|
+
checker,
|
|
2415
|
+
file,
|
|
2416
|
+
typeRegistry,
|
|
2417
|
+
visiting,
|
|
2418
|
+
elementSourceNode,
|
|
2419
|
+
extensionRegistry
|
|
2420
|
+
) : { kind: "primitive", primitiveKind: "string" };
|
|
2095
2421
|
return { kind: "array", items };
|
|
2096
2422
|
}
|
|
2097
|
-
function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
|
|
2423
|
+
function tryResolveRecordType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
2098
2424
|
if (type.getProperties().length > 0) {
|
|
2099
2425
|
return null;
|
|
2100
2426
|
}
|
|
@@ -2102,7 +2428,15 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
|
|
|
2102
2428
|
if (!indexInfo) {
|
|
2103
2429
|
return null;
|
|
2104
2430
|
}
|
|
2105
|
-
const valueType = resolveTypeNode(
|
|
2431
|
+
const valueType = resolveTypeNode(
|
|
2432
|
+
indexInfo.type,
|
|
2433
|
+
checker,
|
|
2434
|
+
file,
|
|
2435
|
+
typeRegistry,
|
|
2436
|
+
visiting,
|
|
2437
|
+
void 0,
|
|
2438
|
+
extensionRegistry
|
|
2439
|
+
);
|
|
2106
2440
|
return { kind: "record", valueType };
|
|
2107
2441
|
}
|
|
2108
2442
|
function typeNodeContainsReference(type, targetName) {
|
|
@@ -2130,7 +2464,7 @@ function typeNodeContainsReference(type, targetName) {
|
|
|
2130
2464
|
}
|
|
2131
2465
|
}
|
|
2132
2466
|
}
|
|
2133
|
-
function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
2467
|
+
function resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
2134
2468
|
const typeName = getNamedTypeName(type);
|
|
2135
2469
|
const namedTypeName = typeName ?? void 0;
|
|
2136
2470
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
@@ -2161,7 +2495,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2161
2495
|
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
2162
2496
|
}
|
|
2163
2497
|
}
|
|
2164
|
-
const recordNode = tryResolveRecordType(
|
|
2498
|
+
const recordNode = tryResolveRecordType(
|
|
2499
|
+
type,
|
|
2500
|
+
checker,
|
|
2501
|
+
file,
|
|
2502
|
+
typeRegistry,
|
|
2503
|
+
visiting,
|
|
2504
|
+
extensionRegistry
|
|
2505
|
+
);
|
|
2165
2506
|
if (recordNode) {
|
|
2166
2507
|
visiting.delete(type);
|
|
2167
2508
|
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
@@ -2170,7 +2511,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2170
2511
|
clearNamedTypeRegistration();
|
|
2171
2512
|
return recordNode;
|
|
2172
2513
|
}
|
|
2173
|
-
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2514
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
2174
2515
|
typeRegistry[namedTypeName] = {
|
|
2175
2516
|
name: namedTypeName,
|
|
2176
2517
|
type: recordNode,
|
|
@@ -2182,7 +2523,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2182
2523
|
return recordNode;
|
|
2183
2524
|
}
|
|
2184
2525
|
const properties = [];
|
|
2185
|
-
const fieldInfoMap = getNamedTypeFieldNodeInfoMap(
|
|
2526
|
+
const fieldInfoMap = getNamedTypeFieldNodeInfoMap(
|
|
2527
|
+
type,
|
|
2528
|
+
checker,
|
|
2529
|
+
file,
|
|
2530
|
+
typeRegistry,
|
|
2531
|
+
visiting,
|
|
2532
|
+
extensionRegistry
|
|
2533
|
+
);
|
|
2186
2534
|
for (const prop of type.getProperties()) {
|
|
2187
2535
|
const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
2188
2536
|
if (!declaration) continue;
|
|
@@ -2194,7 +2542,8 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2194
2542
|
file,
|
|
2195
2543
|
typeRegistry,
|
|
2196
2544
|
visiting,
|
|
2197
|
-
declaration
|
|
2545
|
+
declaration,
|
|
2546
|
+
extensionRegistry
|
|
2198
2547
|
);
|
|
2199
2548
|
const fieldNodeInfo = fieldInfoMap?.get(prop.name);
|
|
2200
2549
|
properties.push({
|
|
@@ -2213,7 +2562,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2213
2562
|
additionalProperties: true
|
|
2214
2563
|
};
|
|
2215
2564
|
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
2216
|
-
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2565
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
2217
2566
|
typeRegistry[namedTypeName] = {
|
|
2218
2567
|
name: namedTypeName,
|
|
2219
2568
|
type: objectNode,
|
|
@@ -2224,7 +2573,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2224
2573
|
}
|
|
2225
2574
|
return objectNode;
|
|
2226
2575
|
}
|
|
2227
|
-
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting) {
|
|
2576
|
+
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
2228
2577
|
const symbols = [type.getSymbol(), type.aliasSymbol].filter(
|
|
2229
2578
|
(s) => s?.declarations != null && s.declarations.length > 0
|
|
2230
2579
|
);
|
|
@@ -2236,7 +2585,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2236
2585
|
const map = /* @__PURE__ */ new Map();
|
|
2237
2586
|
for (const member of classDecl.members) {
|
|
2238
2587
|
if (ts4.isPropertyDeclaration(member) && ts4.isIdentifier(member.name)) {
|
|
2239
|
-
const fieldNode = analyzeFieldToIR(
|
|
2588
|
+
const fieldNode = analyzeFieldToIR(
|
|
2589
|
+
member,
|
|
2590
|
+
checker,
|
|
2591
|
+
file,
|
|
2592
|
+
typeRegistry,
|
|
2593
|
+
visiting,
|
|
2594
|
+
extensionRegistry
|
|
2595
|
+
);
|
|
2240
2596
|
if (fieldNode) {
|
|
2241
2597
|
map.set(fieldNode.name, {
|
|
2242
2598
|
constraints: [...fieldNode.constraints],
|
|
@@ -2250,7 +2606,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2250
2606
|
}
|
|
2251
2607
|
const interfaceDecl = declarations.find(ts4.isInterfaceDeclaration);
|
|
2252
2608
|
if (interfaceDecl) {
|
|
2253
|
-
return buildFieldNodeInfoMap(
|
|
2609
|
+
return buildFieldNodeInfoMap(
|
|
2610
|
+
interfaceDecl.members,
|
|
2611
|
+
checker,
|
|
2612
|
+
file,
|
|
2613
|
+
typeRegistry,
|
|
2614
|
+
visiting,
|
|
2615
|
+
extensionRegistry
|
|
2616
|
+
);
|
|
2254
2617
|
}
|
|
2255
2618
|
const typeAliasDecl = declarations.find(ts4.isTypeAliasDeclaration);
|
|
2256
2619
|
if (typeAliasDecl && ts4.isTypeLiteralNode(typeAliasDecl.type)) {
|
|
@@ -2259,17 +2622,68 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2259
2622
|
checker,
|
|
2260
2623
|
file,
|
|
2261
2624
|
typeRegistry,
|
|
2262
|
-
visiting
|
|
2625
|
+
visiting,
|
|
2626
|
+
extensionRegistry
|
|
2263
2627
|
);
|
|
2264
2628
|
}
|
|
2265
2629
|
}
|
|
2266
2630
|
return null;
|
|
2267
2631
|
}
|
|
2268
|
-
function
|
|
2632
|
+
function extractArrayElementTypeNode(sourceNode, checker) {
|
|
2633
|
+
const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
|
|
2634
|
+
if (typeNode === void 0) {
|
|
2635
|
+
return void 0;
|
|
2636
|
+
}
|
|
2637
|
+
const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
|
|
2638
|
+
if (ts4.isArrayTypeNode(resolvedTypeNode)) {
|
|
2639
|
+
return resolvedTypeNode.elementType;
|
|
2640
|
+
}
|
|
2641
|
+
if (ts4.isTypeReferenceNode(resolvedTypeNode) && ts4.isIdentifier(resolvedTypeNode.typeName) && resolvedTypeNode.typeName.text === "Array" && resolvedTypeNode.typeArguments?.[0]) {
|
|
2642
|
+
return resolvedTypeNode.typeArguments[0];
|
|
2643
|
+
}
|
|
2644
|
+
return void 0;
|
|
2645
|
+
}
|
|
2646
|
+
function extractUnionMemberTypeNodes(sourceNode, checker) {
|
|
2647
|
+
const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
|
|
2648
|
+
if (!typeNode) {
|
|
2649
|
+
return [];
|
|
2650
|
+
}
|
|
2651
|
+
const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
|
|
2652
|
+
return ts4.isUnionTypeNode(resolvedTypeNode) ? [...resolvedTypeNode.types] : [];
|
|
2653
|
+
}
|
|
2654
|
+
function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
|
|
2655
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
2656
|
+
return resolveAliasedTypeNode(typeNode.type, checker, visited);
|
|
2657
|
+
}
|
|
2658
|
+
if (!ts4.isTypeReferenceNode(typeNode) || !ts4.isIdentifier(typeNode.typeName)) {
|
|
2659
|
+
return typeNode;
|
|
2660
|
+
}
|
|
2661
|
+
const symbol = checker.getSymbolAtLocation(typeNode.typeName);
|
|
2662
|
+
const aliasDecl = symbol?.declarations?.find(ts4.isTypeAliasDeclaration);
|
|
2663
|
+
if (aliasDecl === void 0 || visited.has(aliasDecl)) {
|
|
2664
|
+
return typeNode;
|
|
2665
|
+
}
|
|
2666
|
+
visited.add(aliasDecl);
|
|
2667
|
+
return resolveAliasedTypeNode(aliasDecl.type, checker, visited);
|
|
2668
|
+
}
|
|
2669
|
+
function isNullishTypeNode(typeNode) {
|
|
2670
|
+
if (typeNode.kind === ts4.SyntaxKind.NullKeyword || typeNode.kind === ts4.SyntaxKind.UndefinedKeyword) {
|
|
2671
|
+
return true;
|
|
2672
|
+
}
|
|
2673
|
+
return ts4.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts4.SyntaxKind.NullKeyword || typeNode.literal.kind === ts4.SyntaxKind.UndefinedKeyword);
|
|
2674
|
+
}
|
|
2675
|
+
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
2269
2676
|
const map = /* @__PURE__ */ new Map();
|
|
2270
2677
|
for (const member of members) {
|
|
2271
2678
|
if (ts4.isPropertySignature(member)) {
|
|
2272
|
-
const fieldNode = analyzeInterfacePropertyToIR(
|
|
2679
|
+
const fieldNode = analyzeInterfacePropertyToIR(
|
|
2680
|
+
member,
|
|
2681
|
+
checker,
|
|
2682
|
+
file,
|
|
2683
|
+
typeRegistry,
|
|
2684
|
+
visiting,
|
|
2685
|
+
extensionRegistry
|
|
2686
|
+
);
|
|
2273
2687
|
if (fieldNode) {
|
|
2274
2688
|
map.set(fieldNode.name, {
|
|
2275
2689
|
constraints: [...fieldNode.constraints],
|
|
@@ -2282,7 +2696,7 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting) {
|
|
|
2282
2696
|
return map;
|
|
2283
2697
|
}
|
|
2284
2698
|
var MAX_ALIAS_CHAIN_DEPTH = 8;
|
|
2285
|
-
function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
|
|
2699
|
+
function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegistry, depth = 0) {
|
|
2286
2700
|
if (!ts4.isTypeReferenceNode(typeNode)) return [];
|
|
2287
2701
|
if (depth >= MAX_ALIAS_CHAIN_DEPTH) {
|
|
2288
2702
|
const aliasName = typeNode.typeName.getText();
|
|
@@ -2295,8 +2709,29 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
|
|
|
2295
2709
|
const aliasDecl = symbol.declarations.find(ts4.isTypeAliasDeclaration);
|
|
2296
2710
|
if (!aliasDecl) return [];
|
|
2297
2711
|
if (ts4.isTypeLiteralNode(aliasDecl.type)) return [];
|
|
2298
|
-
const
|
|
2299
|
-
|
|
2712
|
+
const aliasFieldType = resolveTypeNode(
|
|
2713
|
+
checker.getTypeAtLocation(aliasDecl.type),
|
|
2714
|
+
checker,
|
|
2715
|
+
file,
|
|
2716
|
+
{},
|
|
2717
|
+
/* @__PURE__ */ new Set(),
|
|
2718
|
+
aliasDecl.type,
|
|
2719
|
+
extensionRegistry
|
|
2720
|
+
);
|
|
2721
|
+
const constraints = extractJSDocConstraintNodes(
|
|
2722
|
+
aliasDecl,
|
|
2723
|
+
file,
|
|
2724
|
+
makeParseOptions(extensionRegistry, aliasFieldType)
|
|
2725
|
+
);
|
|
2726
|
+
constraints.push(
|
|
2727
|
+
...extractTypeAliasConstraintNodes(
|
|
2728
|
+
aliasDecl.type,
|
|
2729
|
+
checker,
|
|
2730
|
+
file,
|
|
2731
|
+
extensionRegistry,
|
|
2732
|
+
depth + 1
|
|
2733
|
+
)
|
|
2734
|
+
);
|
|
2300
2735
|
return constraints;
|
|
2301
2736
|
}
|
|
2302
2737
|
function provenanceForNode(node, file) {
|
|
@@ -2392,10 +2827,10 @@ function detectFormSpecReference(typeNode) {
|
|
|
2392
2827
|
}
|
|
2393
2828
|
|
|
2394
2829
|
// src/generators/class-schema.ts
|
|
2395
|
-
function generateClassSchemas(analysis, source) {
|
|
2830
|
+
function generateClassSchemas(analysis, source, options) {
|
|
2396
2831
|
const ir = canonicalizeTSDoc(analysis, source);
|
|
2397
2832
|
return {
|
|
2398
|
-
jsonSchema: generateJsonSchemaFromIR(ir),
|
|
2833
|
+
jsonSchema: generateJsonSchemaFromIR(ir, options),
|
|
2399
2834
|
uiSchema: generateUiSchemaFromIR(ir)
|
|
2400
2835
|
};
|
|
2401
2836
|
}
|
|
@@ -2405,27 +2840,54 @@ function generateSchemasFromClass(options) {
|
|
|
2405
2840
|
if (!classDecl) {
|
|
2406
2841
|
throw new Error(`Class "${options.className}" not found in ${options.filePath}`);
|
|
2407
2842
|
}
|
|
2408
|
-
const analysis = analyzeClassToIR(
|
|
2409
|
-
|
|
2843
|
+
const analysis = analyzeClassToIR(
|
|
2844
|
+
classDecl,
|
|
2845
|
+
ctx.checker,
|
|
2846
|
+
options.filePath,
|
|
2847
|
+
options.extensionRegistry
|
|
2848
|
+
);
|
|
2849
|
+
return generateClassSchemas(
|
|
2850
|
+
analysis,
|
|
2851
|
+
{ file: options.filePath },
|
|
2852
|
+
{
|
|
2853
|
+
extensionRegistry: options.extensionRegistry,
|
|
2854
|
+
vendorPrefix: options.vendorPrefix
|
|
2855
|
+
}
|
|
2856
|
+
);
|
|
2410
2857
|
}
|
|
2411
2858
|
function generateSchemas(options) {
|
|
2412
2859
|
const ctx = createProgramContext(options.filePath);
|
|
2413
2860
|
const source = { file: options.filePath };
|
|
2414
2861
|
const classDecl = findClassByName(ctx.sourceFile, options.typeName);
|
|
2415
2862
|
if (classDecl) {
|
|
2416
|
-
const analysis = analyzeClassToIR(
|
|
2417
|
-
|
|
2863
|
+
const analysis = analyzeClassToIR(
|
|
2864
|
+
classDecl,
|
|
2865
|
+
ctx.checker,
|
|
2866
|
+
options.filePath,
|
|
2867
|
+
options.extensionRegistry
|
|
2868
|
+
);
|
|
2869
|
+
return generateClassSchemas(analysis, source, options);
|
|
2418
2870
|
}
|
|
2419
2871
|
const interfaceDecl = findInterfaceByName(ctx.sourceFile, options.typeName);
|
|
2420
2872
|
if (interfaceDecl) {
|
|
2421
|
-
const analysis = analyzeInterfaceToIR(
|
|
2422
|
-
|
|
2873
|
+
const analysis = analyzeInterfaceToIR(
|
|
2874
|
+
interfaceDecl,
|
|
2875
|
+
ctx.checker,
|
|
2876
|
+
options.filePath,
|
|
2877
|
+
options.extensionRegistry
|
|
2878
|
+
);
|
|
2879
|
+
return generateClassSchemas(analysis, source, options);
|
|
2423
2880
|
}
|
|
2424
2881
|
const typeAlias = findTypeAliasByName(ctx.sourceFile, options.typeName);
|
|
2425
2882
|
if (typeAlias) {
|
|
2426
|
-
const result = analyzeTypeAliasToIR(
|
|
2883
|
+
const result = analyzeTypeAliasToIR(
|
|
2884
|
+
typeAlias,
|
|
2885
|
+
ctx.checker,
|
|
2886
|
+
options.filePath,
|
|
2887
|
+
options.extensionRegistry
|
|
2888
|
+
);
|
|
2427
2889
|
if (result.ok) {
|
|
2428
|
-
return generateClassSchemas(result.analysis, source);
|
|
2890
|
+
return generateClassSchemas(result.analysis, source, options);
|
|
2429
2891
|
}
|
|
2430
2892
|
throw new Error(result.error);
|
|
2431
2893
|
}
|
|
@@ -2437,7 +2899,7 @@ function generateSchemas(options) {
|
|
|
2437
2899
|
// src/generators/mixed-authoring.ts
|
|
2438
2900
|
function buildMixedAuthoringSchemas(options) {
|
|
2439
2901
|
const { filePath, typeName, overlays, ...schemaOptions } = options;
|
|
2440
|
-
const analysis = analyzeNamedType(filePath, typeName);
|
|
2902
|
+
const analysis = analyzeNamedType(filePath, typeName, schemaOptions.extensionRegistry);
|
|
2441
2903
|
const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays);
|
|
2442
2904
|
const ir = canonicalizeTSDoc(composedAnalysis, { file: filePath });
|
|
2443
2905
|
return {
|
|
@@ -2445,20 +2907,20 @@ function buildMixedAuthoringSchemas(options) {
|
|
|
2445
2907
|
uiSchema: generateUiSchemaFromIR(ir)
|
|
2446
2908
|
};
|
|
2447
2909
|
}
|
|
2448
|
-
function analyzeNamedType(filePath, typeName) {
|
|
2910
|
+
function analyzeNamedType(filePath, typeName, extensionRegistry) {
|
|
2449
2911
|
const ctx = createProgramContext(filePath);
|
|
2450
2912
|
const source = { file: filePath };
|
|
2451
2913
|
const classDecl = findClassByName(ctx.sourceFile, typeName);
|
|
2452
2914
|
if (classDecl !== null) {
|
|
2453
|
-
return analyzeClassToIR(classDecl, ctx.checker, source.file);
|
|
2915
|
+
return analyzeClassToIR(classDecl, ctx.checker, source.file, extensionRegistry);
|
|
2454
2916
|
}
|
|
2455
2917
|
const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
|
|
2456
2918
|
if (interfaceDecl !== null) {
|
|
2457
|
-
return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file);
|
|
2919
|
+
return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file, extensionRegistry);
|
|
2458
2920
|
}
|
|
2459
2921
|
const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
|
|
2460
2922
|
if (typeAlias !== null) {
|
|
2461
|
-
const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file);
|
|
2923
|
+
const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file, extensionRegistry);
|
|
2462
2924
|
if (result.ok) {
|
|
2463
2925
|
return result.analysis;
|
|
2464
2926
|
}
|
|
@@ -2537,7 +2999,7 @@ function assertSupportedOverlayField(baseField, overlayField) {
|
|
|
2537
2999
|
`Mixed-authoring overlay for "${baseField.name}" cannot define constraints; keep constraints on the static model`
|
|
2538
3000
|
);
|
|
2539
3001
|
}
|
|
2540
|
-
if (overlayField.required) {
|
|
3002
|
+
if (overlayField.required && !baseField.required) {
|
|
2541
3003
|
throw new Error(
|
|
2542
3004
|
`Mixed-authoring overlay for "${baseField.name}" cannot change requiredness; keep requiredness on the static model`
|
|
2543
3005
|
);
|
|
@@ -2627,7 +3089,7 @@ function mergeAnnotations(baseAnnotations, overlayAnnotations) {
|
|
|
2627
3089
|
const overlayOnly = overlayAnnotations.filter(
|
|
2628
3090
|
(annotation) => !baseKeys.has(annotationKey(annotation))
|
|
2629
3091
|
);
|
|
2630
|
-
return [...
|
|
3092
|
+
return [...baseAnnotations, ...overlayOnly];
|
|
2631
3093
|
}
|
|
2632
3094
|
function annotationKey(annotation) {
|
|
2633
3095
|
return annotation.annotationKind === "custom" ? `${annotation.annotationKind}:${annotation.annotationId}` : annotation.annotationKind;
|