@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/cli.js
CHANGED
|
@@ -807,7 +807,12 @@ function applyCustomConstraint(schema, constraint, ctx) {
|
|
|
807
807
|
`Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
|
|
808
808
|
);
|
|
809
809
|
}
|
|
810
|
-
|
|
810
|
+
assignVendorPrefixedExtensionKeywords(
|
|
811
|
+
schema,
|
|
812
|
+
registration.toJsonSchema(constraint.payload, ctx.vendorPrefix),
|
|
813
|
+
ctx.vendorPrefix,
|
|
814
|
+
`custom constraint "${constraint.constraintId}"`
|
|
815
|
+
);
|
|
811
816
|
}
|
|
812
817
|
function applyCustomAnnotation(schema, annotation, ctx) {
|
|
813
818
|
const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
|
|
@@ -819,7 +824,22 @@ function applyCustomAnnotation(schema, annotation, ctx) {
|
|
|
819
824
|
if (registration.toJsonSchema === void 0) {
|
|
820
825
|
return;
|
|
821
826
|
}
|
|
822
|
-
|
|
827
|
+
assignVendorPrefixedExtensionKeywords(
|
|
828
|
+
schema,
|
|
829
|
+
registration.toJsonSchema(annotation.value, ctx.vendorPrefix),
|
|
830
|
+
ctx.vendorPrefix,
|
|
831
|
+
`custom annotation "${annotation.annotationId}"`
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPrefix, source) {
|
|
835
|
+
for (const [key, value] of Object.entries(extensionSchema)) {
|
|
836
|
+
if (!key.startsWith(`${vendorPrefix}-`)) {
|
|
837
|
+
throw new Error(
|
|
838
|
+
`Cannot apply ${source}: extension hooks may only emit "${vendorPrefix}-*" JSON Schema keywords`
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
schema[key] = value;
|
|
842
|
+
}
|
|
823
843
|
}
|
|
824
844
|
var init_ir_generator = __esm({
|
|
825
845
|
"src/json-schema/ir-generator.ts"() {
|
|
@@ -1104,7 +1124,10 @@ var init_types = __esm({
|
|
|
1104
1124
|
// src/extensions/registry.ts
|
|
1105
1125
|
function createExtensionRegistry(extensions) {
|
|
1106
1126
|
const typeMap = /* @__PURE__ */ new Map();
|
|
1127
|
+
const typeNameMap = /* @__PURE__ */ new Map();
|
|
1107
1128
|
const constraintMap = /* @__PURE__ */ new Map();
|
|
1129
|
+
const constraintTagMap = /* @__PURE__ */ new Map();
|
|
1130
|
+
const builtinBroadeningMap = /* @__PURE__ */ new Map();
|
|
1108
1131
|
const annotationMap = /* @__PURE__ */ new Map();
|
|
1109
1132
|
for (const ext of extensions) {
|
|
1110
1133
|
if (ext.types !== void 0) {
|
|
@@ -1114,6 +1137,27 @@ function createExtensionRegistry(extensions) {
|
|
|
1114
1137
|
throw new Error(`Duplicate custom type ID: "${qualifiedId}"`);
|
|
1115
1138
|
}
|
|
1116
1139
|
typeMap.set(qualifiedId, type);
|
|
1140
|
+
for (const sourceTypeName of type.tsTypeNames ?? [type.typeName]) {
|
|
1141
|
+
if (typeNameMap.has(sourceTypeName)) {
|
|
1142
|
+
throw new Error(`Duplicate custom type source name: "${sourceTypeName}"`);
|
|
1143
|
+
}
|
|
1144
|
+
typeNameMap.set(sourceTypeName, {
|
|
1145
|
+
extensionId: ext.extensionId,
|
|
1146
|
+
registration: type
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
if (type.builtinConstraintBroadenings !== void 0) {
|
|
1150
|
+
for (const broadening of type.builtinConstraintBroadenings) {
|
|
1151
|
+
const key = `${qualifiedId}:${broadening.tagName}`;
|
|
1152
|
+
if (builtinBroadeningMap.has(key)) {
|
|
1153
|
+
throw new Error(`Duplicate built-in constraint broadening: "${key}"`);
|
|
1154
|
+
}
|
|
1155
|
+
builtinBroadeningMap.set(key, {
|
|
1156
|
+
extensionId: ext.extensionId,
|
|
1157
|
+
registration: broadening
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1117
1161
|
}
|
|
1118
1162
|
}
|
|
1119
1163
|
if (ext.constraints !== void 0) {
|
|
@@ -1125,6 +1169,17 @@ function createExtensionRegistry(extensions) {
|
|
|
1125
1169
|
constraintMap.set(qualifiedId, constraint);
|
|
1126
1170
|
}
|
|
1127
1171
|
}
|
|
1172
|
+
if (ext.constraintTags !== void 0) {
|
|
1173
|
+
for (const tag of ext.constraintTags) {
|
|
1174
|
+
if (constraintTagMap.has(tag.tagName)) {
|
|
1175
|
+
throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
|
|
1176
|
+
}
|
|
1177
|
+
constraintTagMap.set(tag.tagName, {
|
|
1178
|
+
extensionId: ext.extensionId,
|
|
1179
|
+
registration: tag
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1128
1183
|
if (ext.annotations !== void 0) {
|
|
1129
1184
|
for (const annotation of ext.annotations) {
|
|
1130
1185
|
const qualifiedId = `${ext.extensionId}/${annotation.annotationName}`;
|
|
@@ -1138,7 +1193,10 @@ function createExtensionRegistry(extensions) {
|
|
|
1138
1193
|
return {
|
|
1139
1194
|
extensions,
|
|
1140
1195
|
findType: (typeId) => typeMap.get(typeId),
|
|
1196
|
+
findTypeByName: (typeName) => typeNameMap.get(typeName),
|
|
1141
1197
|
findConstraint: (constraintId) => constraintMap.get(constraintId),
|
|
1198
|
+
findConstraintTag: (tagName) => constraintTagMap.get(tagName),
|
|
1199
|
+
findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
|
|
1142
1200
|
findAnnotation: (annotationId) => annotationMap.get(annotationId)
|
|
1143
1201
|
};
|
|
1144
1202
|
}
|
|
@@ -1331,7 +1389,7 @@ import {
|
|
|
1331
1389
|
normalizeConstraintTagName,
|
|
1332
1390
|
isBuiltinConstraintName
|
|
1333
1391
|
} from "@formspec/core";
|
|
1334
|
-
function createFormSpecTSDocConfig() {
|
|
1392
|
+
function createFormSpecTSDocConfig(extensionTagNames = []) {
|
|
1335
1393
|
const config = new TSDocConfiguration();
|
|
1336
1394
|
for (const tagName of Object.keys(BUILTIN_CONSTRAINT_DEFINITIONS)) {
|
|
1337
1395
|
config.addTagDefinition(
|
|
@@ -1351,13 +1409,33 @@ function createFormSpecTSDocConfig() {
|
|
|
1351
1409
|
})
|
|
1352
1410
|
);
|
|
1353
1411
|
}
|
|
1412
|
+
for (const tagName of extensionTagNames) {
|
|
1413
|
+
config.addTagDefinition(
|
|
1414
|
+
new TSDocTagDefinition({
|
|
1415
|
+
tagName: "@" + tagName,
|
|
1416
|
+
syntaxKind: TSDocTagSyntaxKind.BlockTag,
|
|
1417
|
+
allowMultiple: true
|
|
1418
|
+
})
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1354
1421
|
return config;
|
|
1355
1422
|
}
|
|
1356
|
-
function getParser() {
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1423
|
+
function getParser(options) {
|
|
1424
|
+
const extensionTagNames = [
|
|
1425
|
+
...options?.extensionRegistry?.extensions.flatMap(
|
|
1426
|
+
(extension) => (extension.constraintTags ?? []).map((tag) => tag.tagName)
|
|
1427
|
+
) ?? []
|
|
1428
|
+
].sort();
|
|
1429
|
+
const cacheKey = extensionTagNames.join("|");
|
|
1430
|
+
const existing = parserCache.get(cacheKey);
|
|
1431
|
+
if (existing) {
|
|
1432
|
+
return existing;
|
|
1433
|
+
}
|
|
1434
|
+
const parser = new TSDocParser(createFormSpecTSDocConfig(extensionTagNames));
|
|
1435
|
+
parserCache.set(cacheKey, parser);
|
|
1436
|
+
return parser;
|
|
1437
|
+
}
|
|
1438
|
+
function parseTSDocTags(node, file = "", options) {
|
|
1361
1439
|
const constraints = [];
|
|
1362
1440
|
const annotations = [];
|
|
1363
1441
|
let displayName;
|
|
@@ -1378,7 +1456,7 @@ function parseTSDocTags(node, file = "") {
|
|
|
1378
1456
|
if (!commentText.startsWith("/**")) {
|
|
1379
1457
|
continue;
|
|
1380
1458
|
}
|
|
1381
|
-
const parser = getParser();
|
|
1459
|
+
const parser = getParser(options);
|
|
1382
1460
|
const parserContext = parser.parseRange(
|
|
1383
1461
|
TextRange.fromStringRange(sourceText, range.pos, range.end)
|
|
1384
1462
|
);
|
|
@@ -1417,7 +1495,7 @@ function parseTSDocTags(node, file = "") {
|
|
|
1417
1495
|
const expectedType = isBuiltinConstraintName(tagName) ? BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
|
|
1418
1496
|
if (text === "" && expectedType !== "boolean") continue;
|
|
1419
1497
|
const provenance = provenanceForComment(range, sourceFile, file, tagName);
|
|
1420
|
-
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
1498
|
+
const constraintNode = parseConstraintValue(tagName, text, provenance, options);
|
|
1421
1499
|
if (constraintNode) {
|
|
1422
1500
|
constraints.push(constraintNode);
|
|
1423
1501
|
}
|
|
@@ -1477,7 +1555,7 @@ function parseTSDocTags(node, file = "") {
|
|
|
1477
1555
|
annotations.push(defaultValueNode);
|
|
1478
1556
|
continue;
|
|
1479
1557
|
}
|
|
1480
|
-
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
1558
|
+
const constraintNode = parseConstraintValue(tagName, text, provenance, options);
|
|
1481
1559
|
if (constraintNode) {
|
|
1482
1560
|
constraints.push(constraintNode);
|
|
1483
1561
|
}
|
|
@@ -1533,7 +1611,11 @@ function extractPlainText(node) {
|
|
|
1533
1611
|
}
|
|
1534
1612
|
return result;
|
|
1535
1613
|
}
|
|
1536
|
-
function parseConstraintValue(tagName, text, provenance) {
|
|
1614
|
+
function parseConstraintValue(tagName, text, provenance, options) {
|
|
1615
|
+
const customConstraint = parseExtensionConstraintValue(tagName, text, provenance, options);
|
|
1616
|
+
if (customConstraint) {
|
|
1617
|
+
return customConstraint;
|
|
1618
|
+
}
|
|
1537
1619
|
if (!isBuiltinConstraintName(tagName)) {
|
|
1538
1620
|
return null;
|
|
1539
1621
|
}
|
|
@@ -1638,6 +1720,83 @@ function parseConstraintValue(tagName, text, provenance) {
|
|
|
1638
1720
|
provenance
|
|
1639
1721
|
};
|
|
1640
1722
|
}
|
|
1723
|
+
function parseExtensionConstraintValue(tagName, text, provenance, options) {
|
|
1724
|
+
const pathResult = extractPathTarget(text);
|
|
1725
|
+
const effectiveText = pathResult ? pathResult.remainingText : text;
|
|
1726
|
+
const path4 = pathResult?.path;
|
|
1727
|
+
const registry = options?.extensionRegistry;
|
|
1728
|
+
if (registry === void 0) {
|
|
1729
|
+
return null;
|
|
1730
|
+
}
|
|
1731
|
+
const directTag = registry.findConstraintTag(tagName);
|
|
1732
|
+
if (directTag !== void 0) {
|
|
1733
|
+
return makeCustomConstraintNode(
|
|
1734
|
+
directTag.extensionId,
|
|
1735
|
+
directTag.registration.constraintName,
|
|
1736
|
+
directTag.registration.parseValue(effectiveText),
|
|
1737
|
+
provenance,
|
|
1738
|
+
path4,
|
|
1739
|
+
registry
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
if (!isBuiltinConstraintName(tagName)) {
|
|
1743
|
+
return null;
|
|
1744
|
+
}
|
|
1745
|
+
const broadenedTypeId = getBroadenedCustomTypeId(options?.fieldType);
|
|
1746
|
+
if (broadenedTypeId === void 0) {
|
|
1747
|
+
return null;
|
|
1748
|
+
}
|
|
1749
|
+
const broadened = registry.findBuiltinConstraintBroadening(broadenedTypeId, tagName);
|
|
1750
|
+
if (broadened === void 0) {
|
|
1751
|
+
return null;
|
|
1752
|
+
}
|
|
1753
|
+
return makeCustomConstraintNode(
|
|
1754
|
+
broadened.extensionId,
|
|
1755
|
+
broadened.registration.constraintName,
|
|
1756
|
+
broadened.registration.parseValue(effectiveText),
|
|
1757
|
+
provenance,
|
|
1758
|
+
path4,
|
|
1759
|
+
registry
|
|
1760
|
+
);
|
|
1761
|
+
}
|
|
1762
|
+
function getBroadenedCustomTypeId(fieldType) {
|
|
1763
|
+
if (fieldType?.kind === "custom") {
|
|
1764
|
+
return fieldType.typeId;
|
|
1765
|
+
}
|
|
1766
|
+
if (fieldType?.kind !== "union") {
|
|
1767
|
+
return void 0;
|
|
1768
|
+
}
|
|
1769
|
+
const customMembers = fieldType.members.filter(
|
|
1770
|
+
(member) => member.kind === "custom"
|
|
1771
|
+
);
|
|
1772
|
+
if (customMembers.length !== 1) {
|
|
1773
|
+
return void 0;
|
|
1774
|
+
}
|
|
1775
|
+
const nonCustomMembers = fieldType.members.filter((member) => member.kind !== "custom");
|
|
1776
|
+
const allOtherMembersAreNull = nonCustomMembers.every(
|
|
1777
|
+
(member) => member.kind === "primitive" && member.primitiveKind === "null"
|
|
1778
|
+
);
|
|
1779
|
+
const customMember = customMembers[0];
|
|
1780
|
+
return allOtherMembersAreNull && customMember !== void 0 ? customMember.typeId : void 0;
|
|
1781
|
+
}
|
|
1782
|
+
function makeCustomConstraintNode(extensionId, constraintName, payload, provenance, path4, registry) {
|
|
1783
|
+
const constraintId = `${extensionId}/${constraintName}`;
|
|
1784
|
+
const registration = registry.findConstraint(constraintId);
|
|
1785
|
+
if (registration === void 0) {
|
|
1786
|
+
throw new Error(
|
|
1787
|
+
`Custom TSDoc tag resolved to unregistered constraint "${constraintId}". Register the constraint before using its tag.`
|
|
1788
|
+
);
|
|
1789
|
+
}
|
|
1790
|
+
return {
|
|
1791
|
+
kind: "constraint",
|
|
1792
|
+
constraintKind: "custom",
|
|
1793
|
+
constraintId,
|
|
1794
|
+
payload,
|
|
1795
|
+
compositionRule: registration.compositionRule,
|
|
1796
|
+
...path4 && { path: path4 },
|
|
1797
|
+
provenance
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1641
1800
|
function parseDefaultValueValue(text, provenance) {
|
|
1642
1801
|
const trimmed = text.trim();
|
|
1643
1802
|
let value;
|
|
@@ -1696,7 +1855,7 @@ function getTagCommentText(tag) {
|
|
|
1696
1855
|
}
|
|
1697
1856
|
return ts2.getTextOfJSDocComment(tag.comment);
|
|
1698
1857
|
}
|
|
1699
|
-
var NUMERIC_CONSTRAINT_MAP, LENGTH_CONSTRAINT_MAP, TAGS_REQUIRING_RAW_TEXT,
|
|
1858
|
+
var NUMERIC_CONSTRAINT_MAP, LENGTH_CONSTRAINT_MAP, TAGS_REQUIRING_RAW_TEXT, parserCache;
|
|
1700
1859
|
var init_tsdoc_parser = __esm({
|
|
1701
1860
|
"src/analyzer/tsdoc-parser.ts"() {
|
|
1702
1861
|
"use strict";
|
|
@@ -1715,17 +1874,18 @@ var init_tsdoc_parser = __esm({
|
|
|
1715
1874
|
maxItems: "maxItems"
|
|
1716
1875
|
};
|
|
1717
1876
|
TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions", "defaultValue"]);
|
|
1877
|
+
parserCache = /* @__PURE__ */ new Map();
|
|
1718
1878
|
}
|
|
1719
1879
|
});
|
|
1720
1880
|
|
|
1721
1881
|
// src/analyzer/jsdoc-constraints.ts
|
|
1722
1882
|
import * as ts3 from "typescript";
|
|
1723
|
-
function extractJSDocConstraintNodes(node, file = "") {
|
|
1724
|
-
const result = parseTSDocTags(node, file);
|
|
1883
|
+
function extractJSDocConstraintNodes(node, file = "", options) {
|
|
1884
|
+
const result = parseTSDocTags(node, file, options);
|
|
1725
1885
|
return [...result.constraints];
|
|
1726
1886
|
}
|
|
1727
|
-
function extractJSDocAnnotationNodes(node, file = "") {
|
|
1728
|
-
const result = parseTSDocTags(node, file);
|
|
1887
|
+
function extractJSDocAnnotationNodes(node, file = "", options) {
|
|
1888
|
+
const result = parseTSDocTags(node, file, options);
|
|
1729
1889
|
return [...result.annotations];
|
|
1730
1890
|
}
|
|
1731
1891
|
function extractDefaultValueAnnotation(initializer, file = "") {
|
|
@@ -1776,18 +1936,38 @@ function isObjectType(type) {
|
|
|
1776
1936
|
function isTypeReference(type) {
|
|
1777
1937
|
return !!(type.flags & ts4.TypeFlags.Object) && !!(type.objectFlags & ts4.ObjectFlags.Reference);
|
|
1778
1938
|
}
|
|
1779
|
-
function
|
|
1939
|
+
function makeParseOptions(extensionRegistry, fieldType) {
|
|
1940
|
+
if (extensionRegistry === void 0 && fieldType === void 0) {
|
|
1941
|
+
return void 0;
|
|
1942
|
+
}
|
|
1943
|
+
return {
|
|
1944
|
+
...extensionRegistry !== void 0 && { extensionRegistry },
|
|
1945
|
+
...fieldType !== void 0 && { fieldType }
|
|
1946
|
+
};
|
|
1947
|
+
}
|
|
1948
|
+
function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
1780
1949
|
const name = classDecl.name?.text ?? "AnonymousClass";
|
|
1781
1950
|
const fields = [];
|
|
1782
1951
|
const fieldLayouts = [];
|
|
1783
1952
|
const typeRegistry = {};
|
|
1784
|
-
const annotations = extractJSDocAnnotationNodes(
|
|
1953
|
+
const annotations = extractJSDocAnnotationNodes(
|
|
1954
|
+
classDecl,
|
|
1955
|
+
file,
|
|
1956
|
+
makeParseOptions(extensionRegistry)
|
|
1957
|
+
);
|
|
1785
1958
|
const visiting = /* @__PURE__ */ new Set();
|
|
1786
1959
|
const instanceMethods = [];
|
|
1787
1960
|
const staticMethods = [];
|
|
1788
1961
|
for (const member of classDecl.members) {
|
|
1789
1962
|
if (ts4.isPropertyDeclaration(member)) {
|
|
1790
|
-
const fieldNode = analyzeFieldToIR(
|
|
1963
|
+
const fieldNode = analyzeFieldToIR(
|
|
1964
|
+
member,
|
|
1965
|
+
checker,
|
|
1966
|
+
file,
|
|
1967
|
+
typeRegistry,
|
|
1968
|
+
visiting,
|
|
1969
|
+
extensionRegistry
|
|
1970
|
+
);
|
|
1791
1971
|
if (fieldNode) {
|
|
1792
1972
|
fields.push(fieldNode);
|
|
1793
1973
|
fieldLayouts.push({});
|
|
@@ -1814,15 +1994,26 @@ function analyzeClassToIR(classDecl, checker, file = "") {
|
|
|
1814
1994
|
staticMethods
|
|
1815
1995
|
};
|
|
1816
1996
|
}
|
|
1817
|
-
function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
1997
|
+
function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry) {
|
|
1818
1998
|
const name = interfaceDecl.name.text;
|
|
1819
1999
|
const fields = [];
|
|
1820
2000
|
const typeRegistry = {};
|
|
1821
|
-
const annotations = extractJSDocAnnotationNodes(
|
|
2001
|
+
const annotations = extractJSDocAnnotationNodes(
|
|
2002
|
+
interfaceDecl,
|
|
2003
|
+
file,
|
|
2004
|
+
makeParseOptions(extensionRegistry)
|
|
2005
|
+
);
|
|
1822
2006
|
const visiting = /* @__PURE__ */ new Set();
|
|
1823
2007
|
for (const member of interfaceDecl.members) {
|
|
1824
2008
|
if (ts4.isPropertySignature(member)) {
|
|
1825
|
-
const fieldNode = analyzeInterfacePropertyToIR(
|
|
2009
|
+
const fieldNode = analyzeInterfacePropertyToIR(
|
|
2010
|
+
member,
|
|
2011
|
+
checker,
|
|
2012
|
+
file,
|
|
2013
|
+
typeRegistry,
|
|
2014
|
+
visiting,
|
|
2015
|
+
extensionRegistry
|
|
2016
|
+
);
|
|
1826
2017
|
if (fieldNode) {
|
|
1827
2018
|
fields.push(fieldNode);
|
|
1828
2019
|
}
|
|
@@ -1839,7 +2030,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
|
1839
2030
|
staticMethods: []
|
|
1840
2031
|
};
|
|
1841
2032
|
}
|
|
1842
|
-
function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
2033
|
+
function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
|
|
1843
2034
|
if (!ts4.isTypeLiteralNode(typeAlias.type)) {
|
|
1844
2035
|
const sourceFile = typeAlias.getSourceFile();
|
|
1845
2036
|
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
@@ -1852,11 +2043,22 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
|
1852
2043
|
const name = typeAlias.name.text;
|
|
1853
2044
|
const fields = [];
|
|
1854
2045
|
const typeRegistry = {};
|
|
1855
|
-
const annotations = extractJSDocAnnotationNodes(
|
|
2046
|
+
const annotations = extractJSDocAnnotationNodes(
|
|
2047
|
+
typeAlias,
|
|
2048
|
+
file,
|
|
2049
|
+
makeParseOptions(extensionRegistry)
|
|
2050
|
+
);
|
|
1856
2051
|
const visiting = /* @__PURE__ */ new Set();
|
|
1857
2052
|
for (const member of typeAlias.type.members) {
|
|
1858
2053
|
if (ts4.isPropertySignature(member)) {
|
|
1859
|
-
const fieldNode = analyzeInterfacePropertyToIR(
|
|
2054
|
+
const fieldNode = analyzeInterfacePropertyToIR(
|
|
2055
|
+
member,
|
|
2056
|
+
checker,
|
|
2057
|
+
file,
|
|
2058
|
+
typeRegistry,
|
|
2059
|
+
visiting,
|
|
2060
|
+
extensionRegistry
|
|
2061
|
+
);
|
|
1860
2062
|
if (fieldNode) {
|
|
1861
2063
|
fields.push(fieldNode);
|
|
1862
2064
|
}
|
|
@@ -1875,7 +2077,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
|
1875
2077
|
}
|
|
1876
2078
|
};
|
|
1877
2079
|
}
|
|
1878
|
-
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
2080
|
+
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1879
2081
|
if (!ts4.isIdentifier(prop.name)) {
|
|
1880
2082
|
return null;
|
|
1881
2083
|
}
|
|
@@ -1883,14 +2085,26 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
|
1883
2085
|
const tsType = checker.getTypeAtLocation(prop);
|
|
1884
2086
|
const optional = prop.questionToken !== void 0;
|
|
1885
2087
|
const provenance = provenanceForNode(prop, file);
|
|
1886
|
-
let type = resolveTypeNode(
|
|
2088
|
+
let type = resolveTypeNode(
|
|
2089
|
+
tsType,
|
|
2090
|
+
checker,
|
|
2091
|
+
file,
|
|
2092
|
+
typeRegistry,
|
|
2093
|
+
visiting,
|
|
2094
|
+
prop,
|
|
2095
|
+
extensionRegistry
|
|
2096
|
+
);
|
|
1887
2097
|
const constraints = [];
|
|
1888
2098
|
if (prop.type) {
|
|
1889
|
-
constraints.push(
|
|
2099
|
+
constraints.push(
|
|
2100
|
+
...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
|
|
2101
|
+
);
|
|
1890
2102
|
}
|
|
1891
|
-
constraints.push(...extractJSDocConstraintNodes(prop, file));
|
|
2103
|
+
constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
|
|
1892
2104
|
let annotations = [];
|
|
1893
|
-
annotations.push(
|
|
2105
|
+
annotations.push(
|
|
2106
|
+
...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
2107
|
+
);
|
|
1894
2108
|
const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
|
|
1895
2109
|
if (defaultAnnotation && !annotations.some((a) => a.annotationKind === "defaultValue")) {
|
|
1896
2110
|
annotations.push(defaultAnnotation);
|
|
@@ -1906,7 +2120,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
|
1906
2120
|
provenance
|
|
1907
2121
|
};
|
|
1908
2122
|
}
|
|
1909
|
-
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting) {
|
|
2123
|
+
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1910
2124
|
if (!ts4.isIdentifier(prop.name)) {
|
|
1911
2125
|
return null;
|
|
1912
2126
|
}
|
|
@@ -1914,14 +2128,26 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
1914
2128
|
const tsType = checker.getTypeAtLocation(prop);
|
|
1915
2129
|
const optional = prop.questionToken !== void 0;
|
|
1916
2130
|
const provenance = provenanceForNode(prop, file);
|
|
1917
|
-
let type = resolveTypeNode(
|
|
2131
|
+
let type = resolveTypeNode(
|
|
2132
|
+
tsType,
|
|
2133
|
+
checker,
|
|
2134
|
+
file,
|
|
2135
|
+
typeRegistry,
|
|
2136
|
+
visiting,
|
|
2137
|
+
prop,
|
|
2138
|
+
extensionRegistry
|
|
2139
|
+
);
|
|
1918
2140
|
const constraints = [];
|
|
1919
2141
|
if (prop.type) {
|
|
1920
|
-
constraints.push(
|
|
2142
|
+
constraints.push(
|
|
2143
|
+
...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
|
|
2144
|
+
);
|
|
1921
2145
|
}
|
|
1922
|
-
constraints.push(...extractJSDocConstraintNodes(prop, file));
|
|
2146
|
+
constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
|
|
1923
2147
|
let annotations = [];
|
|
1924
|
-
annotations.push(
|
|
2148
|
+
annotations.push(
|
|
2149
|
+
...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
2150
|
+
);
|
|
1925
2151
|
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
1926
2152
|
return {
|
|
1927
2153
|
kind: "field",
|
|
@@ -1995,7 +2221,66 @@ function parseEnumMemberDisplayName(value) {
|
|
|
1995
2221
|
if (label === "") return null;
|
|
1996
2222
|
return { value: match[1], label };
|
|
1997
2223
|
}
|
|
1998
|
-
function
|
|
2224
|
+
function resolveRegisteredCustomType(sourceNode, extensionRegistry, checker) {
|
|
2225
|
+
if (sourceNode === void 0 || extensionRegistry === void 0) {
|
|
2226
|
+
return null;
|
|
2227
|
+
}
|
|
2228
|
+
const typeNode = extractTypeNodeFromSource(sourceNode);
|
|
2229
|
+
if (typeNode === void 0) {
|
|
2230
|
+
return null;
|
|
2231
|
+
}
|
|
2232
|
+
return resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker);
|
|
2233
|
+
}
|
|
2234
|
+
function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker) {
|
|
2235
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
2236
|
+
return resolveRegisteredCustomTypeFromTypeNode(typeNode.type, extensionRegistry, checker);
|
|
2237
|
+
}
|
|
2238
|
+
const typeName = getTypeNodeRegistrationName(typeNode);
|
|
2239
|
+
if (typeName === null) {
|
|
2240
|
+
return null;
|
|
2241
|
+
}
|
|
2242
|
+
const registration = extensionRegistry.findTypeByName(typeName);
|
|
2243
|
+
if (registration !== void 0) {
|
|
2244
|
+
return {
|
|
2245
|
+
kind: "custom",
|
|
2246
|
+
typeId: `${registration.extensionId}/${registration.registration.typeName}`,
|
|
2247
|
+
payload: null
|
|
2248
|
+
};
|
|
2249
|
+
}
|
|
2250
|
+
if (ts4.isTypeReferenceNode(typeNode) && ts4.isIdentifier(typeNode.typeName)) {
|
|
2251
|
+
const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts4.isTypeAliasDeclaration);
|
|
2252
|
+
if (aliasDecl !== void 0) {
|
|
2253
|
+
return resolveRegisteredCustomTypeFromTypeNode(aliasDecl.type, extensionRegistry, checker);
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
return null;
|
|
2257
|
+
}
|
|
2258
|
+
function extractTypeNodeFromSource(sourceNode) {
|
|
2259
|
+
if (ts4.isPropertyDeclaration(sourceNode) || ts4.isPropertySignature(sourceNode) || ts4.isParameter(sourceNode) || ts4.isTypeAliasDeclaration(sourceNode)) {
|
|
2260
|
+
return sourceNode.type;
|
|
2261
|
+
}
|
|
2262
|
+
if (ts4.isTypeNode(sourceNode)) {
|
|
2263
|
+
return sourceNode;
|
|
2264
|
+
}
|
|
2265
|
+
return void 0;
|
|
2266
|
+
}
|
|
2267
|
+
function getTypeNodeRegistrationName(typeNode) {
|
|
2268
|
+
if (ts4.isTypeReferenceNode(typeNode)) {
|
|
2269
|
+
return ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : typeNode.typeName.right.text;
|
|
2270
|
+
}
|
|
2271
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
2272
|
+
return getTypeNodeRegistrationName(typeNode.type);
|
|
2273
|
+
}
|
|
2274
|
+
if (typeNode.kind === ts4.SyntaxKind.BigIntKeyword || typeNode.kind === ts4.SyntaxKind.StringKeyword || typeNode.kind === ts4.SyntaxKind.NumberKeyword || typeNode.kind === ts4.SyntaxKind.BooleanKeyword) {
|
|
2275
|
+
return typeNode.getText();
|
|
2276
|
+
}
|
|
2277
|
+
return null;
|
|
2278
|
+
}
|
|
2279
|
+
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
2280
|
+
const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
|
|
2281
|
+
if (customType) {
|
|
2282
|
+
return customType;
|
|
2283
|
+
}
|
|
1999
2284
|
if (type.flags & ts4.TypeFlags.String) {
|
|
2000
2285
|
return { kind: "primitive", primitiveKind: "string" };
|
|
2001
2286
|
}
|
|
@@ -2024,26 +2309,50 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2024
2309
|
};
|
|
2025
2310
|
}
|
|
2026
2311
|
if (type.isUnion()) {
|
|
2027
|
-
return resolveUnionType(
|
|
2312
|
+
return resolveUnionType(
|
|
2313
|
+
type,
|
|
2314
|
+
checker,
|
|
2315
|
+
file,
|
|
2316
|
+
typeRegistry,
|
|
2317
|
+
visiting,
|
|
2318
|
+
sourceNode,
|
|
2319
|
+
extensionRegistry
|
|
2320
|
+
);
|
|
2028
2321
|
}
|
|
2029
2322
|
if (checker.isArrayType(type)) {
|
|
2030
|
-
return resolveArrayType(
|
|
2323
|
+
return resolveArrayType(
|
|
2324
|
+
type,
|
|
2325
|
+
checker,
|
|
2326
|
+
file,
|
|
2327
|
+
typeRegistry,
|
|
2328
|
+
visiting,
|
|
2329
|
+
sourceNode,
|
|
2330
|
+
extensionRegistry
|
|
2331
|
+
);
|
|
2031
2332
|
}
|
|
2032
2333
|
if (isObjectType(type)) {
|
|
2033
|
-
return resolveObjectType(type, checker, file, typeRegistry, visiting);
|
|
2334
|
+
return resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry);
|
|
2034
2335
|
}
|
|
2035
2336
|
return { kind: "primitive", primitiveKind: "string" };
|
|
2036
2337
|
}
|
|
2037
|
-
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode) {
|
|
2338
|
+
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
2038
2339
|
const typeName = getNamedTypeName(type);
|
|
2039
2340
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
2040
2341
|
if (typeName && typeName in typeRegistry) {
|
|
2041
2342
|
return { kind: "reference", name: typeName, typeArguments: [] };
|
|
2042
2343
|
}
|
|
2043
2344
|
const allTypes = type.types;
|
|
2345
|
+
const unionMemberTypeNodes = extractUnionMemberTypeNodes(sourceNode, checker);
|
|
2346
|
+
const nonNullSourceNodes = unionMemberTypeNodes.filter(
|
|
2347
|
+
(memberTypeNode) => !isNullishTypeNode(resolveAliasedTypeNode(memberTypeNode, checker))
|
|
2348
|
+
);
|
|
2044
2349
|
const nonNullTypes = allTypes.filter(
|
|
2045
|
-
(
|
|
2350
|
+
(memberType) => !(memberType.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
|
|
2046
2351
|
);
|
|
2352
|
+
const nonNullMembers = nonNullTypes.map((memberType, index) => ({
|
|
2353
|
+
memberType,
|
|
2354
|
+
sourceNode: nonNullSourceNodes.length === nonNullTypes.length ? nonNullSourceNodes[index] : void 0
|
|
2355
|
+
}));
|
|
2047
2356
|
const hasNull = allTypes.some((t) => t.flags & ts4.TypeFlags.Null);
|
|
2048
2357
|
const memberDisplayNames = /* @__PURE__ */ new Map();
|
|
2049
2358
|
if (namedDecl) {
|
|
@@ -2060,7 +2369,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2060
2369
|
if (!typeName) {
|
|
2061
2370
|
return result;
|
|
2062
2371
|
}
|
|
2063
|
-
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2372
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
2064
2373
|
typeRegistry[typeName] = {
|
|
2065
2374
|
name: typeName,
|
|
2066
2375
|
type: result,
|
|
@@ -2108,14 +2417,15 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2108
2417
|
} : enumNode;
|
|
2109
2418
|
return registerNamed(result);
|
|
2110
2419
|
}
|
|
2111
|
-
if (
|
|
2420
|
+
if (nonNullMembers.length === 1 && nonNullMembers[0]) {
|
|
2112
2421
|
const inner = resolveTypeNode(
|
|
2113
|
-
|
|
2422
|
+
nonNullMembers[0].memberType,
|
|
2114
2423
|
checker,
|
|
2115
2424
|
file,
|
|
2116
2425
|
typeRegistry,
|
|
2117
2426
|
visiting,
|
|
2118
|
-
sourceNode
|
|
2427
|
+
nonNullMembers[0].sourceNode ?? sourceNode,
|
|
2428
|
+
extensionRegistry
|
|
2119
2429
|
);
|
|
2120
2430
|
const result = hasNull ? {
|
|
2121
2431
|
kind: "union",
|
|
@@ -2123,21 +2433,38 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
2123
2433
|
} : inner;
|
|
2124
2434
|
return registerNamed(result);
|
|
2125
2435
|
}
|
|
2126
|
-
const members =
|
|
2127
|
-
(
|
|
2436
|
+
const members = nonNullMembers.map(
|
|
2437
|
+
({ memberType, sourceNode: memberSourceNode }) => resolveTypeNode(
|
|
2438
|
+
memberType,
|
|
2439
|
+
checker,
|
|
2440
|
+
file,
|
|
2441
|
+
typeRegistry,
|
|
2442
|
+
visiting,
|
|
2443
|
+
memberSourceNode ?? sourceNode,
|
|
2444
|
+
extensionRegistry
|
|
2445
|
+
)
|
|
2128
2446
|
);
|
|
2129
2447
|
if (hasNull) {
|
|
2130
2448
|
members.push({ kind: "primitive", primitiveKind: "null" });
|
|
2131
2449
|
}
|
|
2132
2450
|
return registerNamed({ kind: "union", members });
|
|
2133
2451
|
}
|
|
2134
|
-
function resolveArrayType(type, checker, file, typeRegistry, visiting) {
|
|
2452
|
+
function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
2135
2453
|
const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
|
|
2136
2454
|
const elementType = typeArgs?.[0];
|
|
2137
|
-
const
|
|
2455
|
+
const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
|
|
2456
|
+
const items = elementType ? resolveTypeNode(
|
|
2457
|
+
elementType,
|
|
2458
|
+
checker,
|
|
2459
|
+
file,
|
|
2460
|
+
typeRegistry,
|
|
2461
|
+
visiting,
|
|
2462
|
+
elementSourceNode,
|
|
2463
|
+
extensionRegistry
|
|
2464
|
+
) : { kind: "primitive", primitiveKind: "string" };
|
|
2138
2465
|
return { kind: "array", items };
|
|
2139
2466
|
}
|
|
2140
|
-
function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
|
|
2467
|
+
function tryResolveRecordType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
2141
2468
|
if (type.getProperties().length > 0) {
|
|
2142
2469
|
return null;
|
|
2143
2470
|
}
|
|
@@ -2145,7 +2472,15 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
|
|
|
2145
2472
|
if (!indexInfo) {
|
|
2146
2473
|
return null;
|
|
2147
2474
|
}
|
|
2148
|
-
const valueType = resolveTypeNode(
|
|
2475
|
+
const valueType = resolveTypeNode(
|
|
2476
|
+
indexInfo.type,
|
|
2477
|
+
checker,
|
|
2478
|
+
file,
|
|
2479
|
+
typeRegistry,
|
|
2480
|
+
visiting,
|
|
2481
|
+
void 0,
|
|
2482
|
+
extensionRegistry
|
|
2483
|
+
);
|
|
2149
2484
|
return { kind: "record", valueType };
|
|
2150
2485
|
}
|
|
2151
2486
|
function typeNodeContainsReference(type, targetName) {
|
|
@@ -2173,7 +2508,7 @@ function typeNodeContainsReference(type, targetName) {
|
|
|
2173
2508
|
}
|
|
2174
2509
|
}
|
|
2175
2510
|
}
|
|
2176
|
-
function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
2511
|
+
function resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
2177
2512
|
const typeName = getNamedTypeName(type);
|
|
2178
2513
|
const namedTypeName = typeName ?? void 0;
|
|
2179
2514
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
@@ -2204,7 +2539,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2204
2539
|
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
2205
2540
|
}
|
|
2206
2541
|
}
|
|
2207
|
-
const recordNode = tryResolveRecordType(
|
|
2542
|
+
const recordNode = tryResolveRecordType(
|
|
2543
|
+
type,
|
|
2544
|
+
checker,
|
|
2545
|
+
file,
|
|
2546
|
+
typeRegistry,
|
|
2547
|
+
visiting,
|
|
2548
|
+
extensionRegistry
|
|
2549
|
+
);
|
|
2208
2550
|
if (recordNode) {
|
|
2209
2551
|
visiting.delete(type);
|
|
2210
2552
|
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
@@ -2213,7 +2555,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2213
2555
|
clearNamedTypeRegistration();
|
|
2214
2556
|
return recordNode;
|
|
2215
2557
|
}
|
|
2216
|
-
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2558
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
2217
2559
|
typeRegistry[namedTypeName] = {
|
|
2218
2560
|
name: namedTypeName,
|
|
2219
2561
|
type: recordNode,
|
|
@@ -2225,7 +2567,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2225
2567
|
return recordNode;
|
|
2226
2568
|
}
|
|
2227
2569
|
const properties = [];
|
|
2228
|
-
const fieldInfoMap = getNamedTypeFieldNodeInfoMap(
|
|
2570
|
+
const fieldInfoMap = getNamedTypeFieldNodeInfoMap(
|
|
2571
|
+
type,
|
|
2572
|
+
checker,
|
|
2573
|
+
file,
|
|
2574
|
+
typeRegistry,
|
|
2575
|
+
visiting,
|
|
2576
|
+
extensionRegistry
|
|
2577
|
+
);
|
|
2229
2578
|
for (const prop of type.getProperties()) {
|
|
2230
2579
|
const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
2231
2580
|
if (!declaration) continue;
|
|
@@ -2237,7 +2586,8 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2237
2586
|
file,
|
|
2238
2587
|
typeRegistry,
|
|
2239
2588
|
visiting,
|
|
2240
|
-
declaration
|
|
2589
|
+
declaration,
|
|
2590
|
+
extensionRegistry
|
|
2241
2591
|
);
|
|
2242
2592
|
const fieldNodeInfo = fieldInfoMap?.get(prop.name);
|
|
2243
2593
|
properties.push({
|
|
@@ -2256,7 +2606,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2256
2606
|
additionalProperties: true
|
|
2257
2607
|
};
|
|
2258
2608
|
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
2259
|
-
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2609
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
2260
2610
|
typeRegistry[namedTypeName] = {
|
|
2261
2611
|
name: namedTypeName,
|
|
2262
2612
|
type: objectNode,
|
|
@@ -2267,7 +2617,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
2267
2617
|
}
|
|
2268
2618
|
return objectNode;
|
|
2269
2619
|
}
|
|
2270
|
-
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting) {
|
|
2620
|
+
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
2271
2621
|
const symbols = [type.getSymbol(), type.aliasSymbol].filter(
|
|
2272
2622
|
(s) => s?.declarations != null && s.declarations.length > 0
|
|
2273
2623
|
);
|
|
@@ -2279,7 +2629,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2279
2629
|
const map = /* @__PURE__ */ new Map();
|
|
2280
2630
|
for (const member of classDecl.members) {
|
|
2281
2631
|
if (ts4.isPropertyDeclaration(member) && ts4.isIdentifier(member.name)) {
|
|
2282
|
-
const fieldNode = analyzeFieldToIR(
|
|
2632
|
+
const fieldNode = analyzeFieldToIR(
|
|
2633
|
+
member,
|
|
2634
|
+
checker,
|
|
2635
|
+
file,
|
|
2636
|
+
typeRegistry,
|
|
2637
|
+
visiting,
|
|
2638
|
+
extensionRegistry
|
|
2639
|
+
);
|
|
2283
2640
|
if (fieldNode) {
|
|
2284
2641
|
map.set(fieldNode.name, {
|
|
2285
2642
|
constraints: [...fieldNode.constraints],
|
|
@@ -2293,7 +2650,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2293
2650
|
}
|
|
2294
2651
|
const interfaceDecl = declarations.find(ts4.isInterfaceDeclaration);
|
|
2295
2652
|
if (interfaceDecl) {
|
|
2296
|
-
return buildFieldNodeInfoMap(
|
|
2653
|
+
return buildFieldNodeInfoMap(
|
|
2654
|
+
interfaceDecl.members,
|
|
2655
|
+
checker,
|
|
2656
|
+
file,
|
|
2657
|
+
typeRegistry,
|
|
2658
|
+
visiting,
|
|
2659
|
+
extensionRegistry
|
|
2660
|
+
);
|
|
2297
2661
|
}
|
|
2298
2662
|
const typeAliasDecl = declarations.find(ts4.isTypeAliasDeclaration);
|
|
2299
2663
|
if (typeAliasDecl && ts4.isTypeLiteralNode(typeAliasDecl.type)) {
|
|
@@ -2302,17 +2666,68 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
2302
2666
|
checker,
|
|
2303
2667
|
file,
|
|
2304
2668
|
typeRegistry,
|
|
2305
|
-
visiting
|
|
2669
|
+
visiting,
|
|
2670
|
+
extensionRegistry
|
|
2306
2671
|
);
|
|
2307
2672
|
}
|
|
2308
2673
|
}
|
|
2309
2674
|
return null;
|
|
2310
2675
|
}
|
|
2311
|
-
function
|
|
2676
|
+
function extractArrayElementTypeNode(sourceNode, checker) {
|
|
2677
|
+
const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
|
|
2678
|
+
if (typeNode === void 0) {
|
|
2679
|
+
return void 0;
|
|
2680
|
+
}
|
|
2681
|
+
const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
|
|
2682
|
+
if (ts4.isArrayTypeNode(resolvedTypeNode)) {
|
|
2683
|
+
return resolvedTypeNode.elementType;
|
|
2684
|
+
}
|
|
2685
|
+
if (ts4.isTypeReferenceNode(resolvedTypeNode) && ts4.isIdentifier(resolvedTypeNode.typeName) && resolvedTypeNode.typeName.text === "Array" && resolvedTypeNode.typeArguments?.[0]) {
|
|
2686
|
+
return resolvedTypeNode.typeArguments[0];
|
|
2687
|
+
}
|
|
2688
|
+
return void 0;
|
|
2689
|
+
}
|
|
2690
|
+
function extractUnionMemberTypeNodes(sourceNode, checker) {
|
|
2691
|
+
const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
|
|
2692
|
+
if (!typeNode) {
|
|
2693
|
+
return [];
|
|
2694
|
+
}
|
|
2695
|
+
const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
|
|
2696
|
+
return ts4.isUnionTypeNode(resolvedTypeNode) ? [...resolvedTypeNode.types] : [];
|
|
2697
|
+
}
|
|
2698
|
+
function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
|
|
2699
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
2700
|
+
return resolveAliasedTypeNode(typeNode.type, checker, visited);
|
|
2701
|
+
}
|
|
2702
|
+
if (!ts4.isTypeReferenceNode(typeNode) || !ts4.isIdentifier(typeNode.typeName)) {
|
|
2703
|
+
return typeNode;
|
|
2704
|
+
}
|
|
2705
|
+
const symbol = checker.getSymbolAtLocation(typeNode.typeName);
|
|
2706
|
+
const aliasDecl = symbol?.declarations?.find(ts4.isTypeAliasDeclaration);
|
|
2707
|
+
if (aliasDecl === void 0 || visited.has(aliasDecl)) {
|
|
2708
|
+
return typeNode;
|
|
2709
|
+
}
|
|
2710
|
+
visited.add(aliasDecl);
|
|
2711
|
+
return resolveAliasedTypeNode(aliasDecl.type, checker, visited);
|
|
2712
|
+
}
|
|
2713
|
+
function isNullishTypeNode(typeNode) {
|
|
2714
|
+
if (typeNode.kind === ts4.SyntaxKind.NullKeyword || typeNode.kind === ts4.SyntaxKind.UndefinedKeyword) {
|
|
2715
|
+
return true;
|
|
2716
|
+
}
|
|
2717
|
+
return ts4.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts4.SyntaxKind.NullKeyword || typeNode.literal.kind === ts4.SyntaxKind.UndefinedKeyword);
|
|
2718
|
+
}
|
|
2719
|
+
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
2312
2720
|
const map = /* @__PURE__ */ new Map();
|
|
2313
2721
|
for (const member of members) {
|
|
2314
2722
|
if (ts4.isPropertySignature(member)) {
|
|
2315
|
-
const fieldNode = analyzeInterfacePropertyToIR(
|
|
2723
|
+
const fieldNode = analyzeInterfacePropertyToIR(
|
|
2724
|
+
member,
|
|
2725
|
+
checker,
|
|
2726
|
+
file,
|
|
2727
|
+
typeRegistry,
|
|
2728
|
+
visiting,
|
|
2729
|
+
extensionRegistry
|
|
2730
|
+
);
|
|
2316
2731
|
if (fieldNode) {
|
|
2317
2732
|
map.set(fieldNode.name, {
|
|
2318
2733
|
constraints: [...fieldNode.constraints],
|
|
@@ -2324,7 +2739,7 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting) {
|
|
|
2324
2739
|
}
|
|
2325
2740
|
return map;
|
|
2326
2741
|
}
|
|
2327
|
-
function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
|
|
2742
|
+
function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegistry, depth = 0) {
|
|
2328
2743
|
if (!ts4.isTypeReferenceNode(typeNode)) return [];
|
|
2329
2744
|
if (depth >= MAX_ALIAS_CHAIN_DEPTH) {
|
|
2330
2745
|
const aliasName = typeNode.typeName.getText();
|
|
@@ -2337,8 +2752,29 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
|
|
|
2337
2752
|
const aliasDecl = symbol.declarations.find(ts4.isTypeAliasDeclaration);
|
|
2338
2753
|
if (!aliasDecl) return [];
|
|
2339
2754
|
if (ts4.isTypeLiteralNode(aliasDecl.type)) return [];
|
|
2340
|
-
const
|
|
2341
|
-
|
|
2755
|
+
const aliasFieldType = resolveTypeNode(
|
|
2756
|
+
checker.getTypeAtLocation(aliasDecl.type),
|
|
2757
|
+
checker,
|
|
2758
|
+
file,
|
|
2759
|
+
{},
|
|
2760
|
+
/* @__PURE__ */ new Set(),
|
|
2761
|
+
aliasDecl.type,
|
|
2762
|
+
extensionRegistry
|
|
2763
|
+
);
|
|
2764
|
+
const constraints = extractJSDocConstraintNodes(
|
|
2765
|
+
aliasDecl,
|
|
2766
|
+
file,
|
|
2767
|
+
makeParseOptions(extensionRegistry, aliasFieldType)
|
|
2768
|
+
);
|
|
2769
|
+
constraints.push(
|
|
2770
|
+
...extractTypeAliasConstraintNodes(
|
|
2771
|
+
aliasDecl.type,
|
|
2772
|
+
checker,
|
|
2773
|
+
file,
|
|
2774
|
+
extensionRegistry,
|
|
2775
|
+
depth + 1
|
|
2776
|
+
)
|
|
2777
|
+
);
|
|
2342
2778
|
return constraints;
|
|
2343
2779
|
}
|
|
2344
2780
|
function provenanceForNode(node, file) {
|
|
@@ -2448,10 +2884,10 @@ var init_class_analyzer = __esm({
|
|
|
2448
2884
|
});
|
|
2449
2885
|
|
|
2450
2886
|
// src/generators/class-schema.ts
|
|
2451
|
-
function generateClassSchemas(analysis, source) {
|
|
2887
|
+
function generateClassSchemas(analysis, source, options) {
|
|
2452
2888
|
const ir = canonicalizeTSDoc(analysis, source);
|
|
2453
2889
|
return {
|
|
2454
|
-
jsonSchema: generateJsonSchemaFromIR(ir),
|
|
2890
|
+
jsonSchema: generateJsonSchemaFromIR(ir, options),
|
|
2455
2891
|
uiSchema: generateUiSchemaFromIR(ir)
|
|
2456
2892
|
};
|
|
2457
2893
|
}
|
|
@@ -2461,27 +2897,54 @@ function generateSchemasFromClass(options) {
|
|
|
2461
2897
|
if (!classDecl) {
|
|
2462
2898
|
throw new Error(`Class "${options.className}" not found in ${options.filePath}`);
|
|
2463
2899
|
}
|
|
2464
|
-
const analysis = analyzeClassToIR(
|
|
2465
|
-
|
|
2900
|
+
const analysis = analyzeClassToIR(
|
|
2901
|
+
classDecl,
|
|
2902
|
+
ctx.checker,
|
|
2903
|
+
options.filePath,
|
|
2904
|
+
options.extensionRegistry
|
|
2905
|
+
);
|
|
2906
|
+
return generateClassSchemas(
|
|
2907
|
+
analysis,
|
|
2908
|
+
{ file: options.filePath },
|
|
2909
|
+
{
|
|
2910
|
+
extensionRegistry: options.extensionRegistry,
|
|
2911
|
+
vendorPrefix: options.vendorPrefix
|
|
2912
|
+
}
|
|
2913
|
+
);
|
|
2466
2914
|
}
|
|
2467
2915
|
function generateSchemas(options) {
|
|
2468
2916
|
const ctx = createProgramContext(options.filePath);
|
|
2469
2917
|
const source = { file: options.filePath };
|
|
2470
2918
|
const classDecl = findClassByName(ctx.sourceFile, options.typeName);
|
|
2471
2919
|
if (classDecl) {
|
|
2472
|
-
const analysis = analyzeClassToIR(
|
|
2473
|
-
|
|
2920
|
+
const analysis = analyzeClassToIR(
|
|
2921
|
+
classDecl,
|
|
2922
|
+
ctx.checker,
|
|
2923
|
+
options.filePath,
|
|
2924
|
+
options.extensionRegistry
|
|
2925
|
+
);
|
|
2926
|
+
return generateClassSchemas(analysis, source, options);
|
|
2474
2927
|
}
|
|
2475
2928
|
const interfaceDecl = findInterfaceByName(ctx.sourceFile, options.typeName);
|
|
2476
2929
|
if (interfaceDecl) {
|
|
2477
|
-
const analysis = analyzeInterfaceToIR(
|
|
2478
|
-
|
|
2930
|
+
const analysis = analyzeInterfaceToIR(
|
|
2931
|
+
interfaceDecl,
|
|
2932
|
+
ctx.checker,
|
|
2933
|
+
options.filePath,
|
|
2934
|
+
options.extensionRegistry
|
|
2935
|
+
);
|
|
2936
|
+
return generateClassSchemas(analysis, source, options);
|
|
2479
2937
|
}
|
|
2480
2938
|
const typeAlias = findTypeAliasByName(ctx.sourceFile, options.typeName);
|
|
2481
2939
|
if (typeAlias) {
|
|
2482
|
-
const result = analyzeTypeAliasToIR(
|
|
2940
|
+
const result = analyzeTypeAliasToIR(
|
|
2941
|
+
typeAlias,
|
|
2942
|
+
ctx.checker,
|
|
2943
|
+
options.filePath,
|
|
2944
|
+
options.extensionRegistry
|
|
2945
|
+
);
|
|
2483
2946
|
if (result.ok) {
|
|
2484
|
-
return generateClassSchemas(result.analysis, source);
|
|
2947
|
+
return generateClassSchemas(result.analysis, source, options);
|
|
2485
2948
|
}
|
|
2486
2949
|
throw new Error(result.error);
|
|
2487
2950
|
}
|
|
@@ -2503,7 +2966,7 @@ var init_class_schema = __esm({
|
|
|
2503
2966
|
// src/generators/mixed-authoring.ts
|
|
2504
2967
|
function buildMixedAuthoringSchemas(options) {
|
|
2505
2968
|
const { filePath, typeName, overlays, ...schemaOptions } = options;
|
|
2506
|
-
const analysis = analyzeNamedType(filePath, typeName);
|
|
2969
|
+
const analysis = analyzeNamedType(filePath, typeName, schemaOptions.extensionRegistry);
|
|
2507
2970
|
const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays);
|
|
2508
2971
|
const ir = canonicalizeTSDoc(composedAnalysis, { file: filePath });
|
|
2509
2972
|
return {
|
|
@@ -2511,20 +2974,20 @@ function buildMixedAuthoringSchemas(options) {
|
|
|
2511
2974
|
uiSchema: generateUiSchemaFromIR(ir)
|
|
2512
2975
|
};
|
|
2513
2976
|
}
|
|
2514
|
-
function analyzeNamedType(filePath, typeName) {
|
|
2977
|
+
function analyzeNamedType(filePath, typeName, extensionRegistry) {
|
|
2515
2978
|
const ctx = createProgramContext(filePath);
|
|
2516
2979
|
const source = { file: filePath };
|
|
2517
2980
|
const classDecl = findClassByName(ctx.sourceFile, typeName);
|
|
2518
2981
|
if (classDecl !== null) {
|
|
2519
|
-
return analyzeClassToIR(classDecl, ctx.checker, source.file);
|
|
2982
|
+
return analyzeClassToIR(classDecl, ctx.checker, source.file, extensionRegistry);
|
|
2520
2983
|
}
|
|
2521
2984
|
const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
|
|
2522
2985
|
if (interfaceDecl !== null) {
|
|
2523
|
-
return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file);
|
|
2986
|
+
return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file, extensionRegistry);
|
|
2524
2987
|
}
|
|
2525
2988
|
const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
|
|
2526
2989
|
if (typeAlias !== null) {
|
|
2527
|
-
const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file);
|
|
2990
|
+
const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file, extensionRegistry);
|
|
2528
2991
|
if (result.ok) {
|
|
2529
2992
|
return result.analysis;
|
|
2530
2993
|
}
|
|
@@ -2603,7 +3066,7 @@ function assertSupportedOverlayField(baseField, overlayField) {
|
|
|
2603
3066
|
`Mixed-authoring overlay for "${baseField.name}" cannot define constraints; keep constraints on the static model`
|
|
2604
3067
|
);
|
|
2605
3068
|
}
|
|
2606
|
-
if (overlayField.required) {
|
|
3069
|
+
if (overlayField.required && !baseField.required) {
|
|
2607
3070
|
throw new Error(
|
|
2608
3071
|
`Mixed-authoring overlay for "${baseField.name}" cannot change requiredness; keep requiredness on the static model`
|
|
2609
3072
|
);
|
|
@@ -2693,7 +3156,7 @@ function mergeAnnotations(baseAnnotations, overlayAnnotations) {
|
|
|
2693
3156
|
const overlayOnly = overlayAnnotations.filter(
|
|
2694
3157
|
(annotation) => !baseKeys.has(annotationKey(annotation))
|
|
2695
3158
|
);
|
|
2696
|
-
return [...
|
|
3159
|
+
return [...baseAnnotations, ...overlayOnly];
|
|
2697
3160
|
}
|
|
2698
3161
|
function annotationKey(annotation) {
|
|
2699
3162
|
return annotation.annotationKind === "custom" ? `${annotation.annotationKind}:${annotation.annotationId}` : annotation.annotationKind;
|