@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/internals.cjs
CHANGED
|
@@ -542,7 +542,7 @@ var LENGTH_CONSTRAINT_MAP = {
|
|
|
542
542
|
maxItems: "maxItems"
|
|
543
543
|
};
|
|
544
544
|
var TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions", "defaultValue"]);
|
|
545
|
-
function createFormSpecTSDocConfig() {
|
|
545
|
+
function createFormSpecTSDocConfig(extensionTagNames = []) {
|
|
546
546
|
const config = new import_tsdoc.TSDocConfiguration();
|
|
547
547
|
for (const tagName of Object.keys(import_core3.BUILTIN_CONSTRAINT_DEFINITIONS)) {
|
|
548
548
|
config.addTagDefinition(
|
|
@@ -562,14 +562,34 @@ function createFormSpecTSDocConfig() {
|
|
|
562
562
|
})
|
|
563
563
|
);
|
|
564
564
|
}
|
|
565
|
+
for (const tagName of extensionTagNames) {
|
|
566
|
+
config.addTagDefinition(
|
|
567
|
+
new import_tsdoc.TSDocTagDefinition({
|
|
568
|
+
tagName: "@" + tagName,
|
|
569
|
+
syntaxKind: import_tsdoc.TSDocTagSyntaxKind.BlockTag,
|
|
570
|
+
allowMultiple: true
|
|
571
|
+
})
|
|
572
|
+
);
|
|
573
|
+
}
|
|
565
574
|
return config;
|
|
566
575
|
}
|
|
567
|
-
var
|
|
568
|
-
function getParser() {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
576
|
+
var parserCache = /* @__PURE__ */ new Map();
|
|
577
|
+
function getParser(options) {
|
|
578
|
+
const extensionTagNames = [
|
|
579
|
+
...options?.extensionRegistry?.extensions.flatMap(
|
|
580
|
+
(extension) => (extension.constraintTags ?? []).map((tag) => tag.tagName)
|
|
581
|
+
) ?? []
|
|
582
|
+
].sort();
|
|
583
|
+
const cacheKey = extensionTagNames.join("|");
|
|
584
|
+
const existing = parserCache.get(cacheKey);
|
|
585
|
+
if (existing) {
|
|
586
|
+
return existing;
|
|
587
|
+
}
|
|
588
|
+
const parser = new import_tsdoc.TSDocParser(createFormSpecTSDocConfig(extensionTagNames));
|
|
589
|
+
parserCache.set(cacheKey, parser);
|
|
590
|
+
return parser;
|
|
591
|
+
}
|
|
592
|
+
function parseTSDocTags(node, file = "", options) {
|
|
573
593
|
const constraints = [];
|
|
574
594
|
const annotations = [];
|
|
575
595
|
let displayName;
|
|
@@ -590,7 +610,7 @@ function parseTSDocTags(node, file = "") {
|
|
|
590
610
|
if (!commentText.startsWith("/**")) {
|
|
591
611
|
continue;
|
|
592
612
|
}
|
|
593
|
-
const parser = getParser();
|
|
613
|
+
const parser = getParser(options);
|
|
594
614
|
const parserContext = parser.parseRange(
|
|
595
615
|
import_tsdoc.TextRange.fromStringRange(sourceText, range.pos, range.end)
|
|
596
616
|
);
|
|
@@ -629,7 +649,7 @@ function parseTSDocTags(node, file = "") {
|
|
|
629
649
|
const expectedType = (0, import_core3.isBuiltinConstraintName)(tagName) ? import_core3.BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
|
|
630
650
|
if (text === "" && expectedType !== "boolean") continue;
|
|
631
651
|
const provenance = provenanceForComment(range, sourceFile, file, tagName);
|
|
632
|
-
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
652
|
+
const constraintNode = parseConstraintValue(tagName, text, provenance, options);
|
|
633
653
|
if (constraintNode) {
|
|
634
654
|
constraints.push(constraintNode);
|
|
635
655
|
}
|
|
@@ -689,7 +709,7 @@ function parseTSDocTags(node, file = "") {
|
|
|
689
709
|
annotations.push(defaultValueNode);
|
|
690
710
|
continue;
|
|
691
711
|
}
|
|
692
|
-
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
712
|
+
const constraintNode = parseConstraintValue(tagName, text, provenance, options);
|
|
693
713
|
if (constraintNode) {
|
|
694
714
|
constraints.push(constraintNode);
|
|
695
715
|
}
|
|
@@ -745,7 +765,11 @@ function extractPlainText(node) {
|
|
|
745
765
|
}
|
|
746
766
|
return result;
|
|
747
767
|
}
|
|
748
|
-
function parseConstraintValue(tagName, text, provenance) {
|
|
768
|
+
function parseConstraintValue(tagName, text, provenance, options) {
|
|
769
|
+
const customConstraint = parseExtensionConstraintValue(tagName, text, provenance, options);
|
|
770
|
+
if (customConstraint) {
|
|
771
|
+
return customConstraint;
|
|
772
|
+
}
|
|
749
773
|
if (!(0, import_core3.isBuiltinConstraintName)(tagName)) {
|
|
750
774
|
return null;
|
|
751
775
|
}
|
|
@@ -850,6 +874,83 @@ function parseConstraintValue(tagName, text, provenance) {
|
|
|
850
874
|
provenance
|
|
851
875
|
};
|
|
852
876
|
}
|
|
877
|
+
function parseExtensionConstraintValue(tagName, text, provenance, options) {
|
|
878
|
+
const pathResult = extractPathTarget(text);
|
|
879
|
+
const effectiveText = pathResult ? pathResult.remainingText : text;
|
|
880
|
+
const path2 = pathResult?.path;
|
|
881
|
+
const registry = options?.extensionRegistry;
|
|
882
|
+
if (registry === void 0) {
|
|
883
|
+
return null;
|
|
884
|
+
}
|
|
885
|
+
const directTag = registry.findConstraintTag(tagName);
|
|
886
|
+
if (directTag !== void 0) {
|
|
887
|
+
return makeCustomConstraintNode(
|
|
888
|
+
directTag.extensionId,
|
|
889
|
+
directTag.registration.constraintName,
|
|
890
|
+
directTag.registration.parseValue(effectiveText),
|
|
891
|
+
provenance,
|
|
892
|
+
path2,
|
|
893
|
+
registry
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
if (!(0, import_core3.isBuiltinConstraintName)(tagName)) {
|
|
897
|
+
return null;
|
|
898
|
+
}
|
|
899
|
+
const broadenedTypeId = getBroadenedCustomTypeId(options?.fieldType);
|
|
900
|
+
if (broadenedTypeId === void 0) {
|
|
901
|
+
return null;
|
|
902
|
+
}
|
|
903
|
+
const broadened = registry.findBuiltinConstraintBroadening(broadenedTypeId, tagName);
|
|
904
|
+
if (broadened === void 0) {
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
907
|
+
return makeCustomConstraintNode(
|
|
908
|
+
broadened.extensionId,
|
|
909
|
+
broadened.registration.constraintName,
|
|
910
|
+
broadened.registration.parseValue(effectiveText),
|
|
911
|
+
provenance,
|
|
912
|
+
path2,
|
|
913
|
+
registry
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
function getBroadenedCustomTypeId(fieldType) {
|
|
917
|
+
if (fieldType?.kind === "custom") {
|
|
918
|
+
return fieldType.typeId;
|
|
919
|
+
}
|
|
920
|
+
if (fieldType?.kind !== "union") {
|
|
921
|
+
return void 0;
|
|
922
|
+
}
|
|
923
|
+
const customMembers = fieldType.members.filter(
|
|
924
|
+
(member) => member.kind === "custom"
|
|
925
|
+
);
|
|
926
|
+
if (customMembers.length !== 1) {
|
|
927
|
+
return void 0;
|
|
928
|
+
}
|
|
929
|
+
const nonCustomMembers = fieldType.members.filter((member) => member.kind !== "custom");
|
|
930
|
+
const allOtherMembersAreNull = nonCustomMembers.every(
|
|
931
|
+
(member) => member.kind === "primitive" && member.primitiveKind === "null"
|
|
932
|
+
);
|
|
933
|
+
const customMember = customMembers[0];
|
|
934
|
+
return allOtherMembersAreNull && customMember !== void 0 ? customMember.typeId : void 0;
|
|
935
|
+
}
|
|
936
|
+
function makeCustomConstraintNode(extensionId, constraintName, payload, provenance, path2, registry) {
|
|
937
|
+
const constraintId = `${extensionId}/${constraintName}`;
|
|
938
|
+
const registration = registry.findConstraint(constraintId);
|
|
939
|
+
if (registration === void 0) {
|
|
940
|
+
throw new Error(
|
|
941
|
+
`Custom TSDoc tag resolved to unregistered constraint "${constraintId}". Register the constraint before using its tag.`
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
return {
|
|
945
|
+
kind: "constraint",
|
|
946
|
+
constraintKind: "custom",
|
|
947
|
+
constraintId,
|
|
948
|
+
payload,
|
|
949
|
+
compositionRule: registration.compositionRule,
|
|
950
|
+
...path2 && { path: path2 },
|
|
951
|
+
provenance
|
|
952
|
+
};
|
|
953
|
+
}
|
|
853
954
|
function parseDefaultValueValue(text, provenance) {
|
|
854
955
|
const trimmed = text.trim();
|
|
855
956
|
let value;
|
|
@@ -910,12 +1011,12 @@ function getTagCommentText(tag) {
|
|
|
910
1011
|
}
|
|
911
1012
|
|
|
912
1013
|
// src/analyzer/jsdoc-constraints.ts
|
|
913
|
-
function extractJSDocConstraintNodes(node, file = "") {
|
|
914
|
-
const result = parseTSDocTags(node, file);
|
|
1014
|
+
function extractJSDocConstraintNodes(node, file = "", options) {
|
|
1015
|
+
const result = parseTSDocTags(node, file, options);
|
|
915
1016
|
return [...result.constraints];
|
|
916
1017
|
}
|
|
917
|
-
function extractJSDocAnnotationNodes(node, file = "") {
|
|
918
|
-
const result = parseTSDocTags(node, file);
|
|
1018
|
+
function extractJSDocAnnotationNodes(node, file = "", options) {
|
|
1019
|
+
const result = parseTSDocTags(node, file, options);
|
|
919
1020
|
return [...result.annotations];
|
|
920
1021
|
}
|
|
921
1022
|
function extractDefaultValueAnnotation(initializer, file = "") {
|
|
@@ -964,18 +1065,38 @@ var RESOLVING_TYPE_PLACEHOLDER = {
|
|
|
964
1065
|
properties: [],
|
|
965
1066
|
additionalProperties: true
|
|
966
1067
|
};
|
|
967
|
-
function
|
|
1068
|
+
function makeParseOptions(extensionRegistry, fieldType) {
|
|
1069
|
+
if (extensionRegistry === void 0 && fieldType === void 0) {
|
|
1070
|
+
return void 0;
|
|
1071
|
+
}
|
|
1072
|
+
return {
|
|
1073
|
+
...extensionRegistry !== void 0 && { extensionRegistry },
|
|
1074
|
+
...fieldType !== void 0 && { fieldType }
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
968
1078
|
const name = classDecl.name?.text ?? "AnonymousClass";
|
|
969
1079
|
const fields = [];
|
|
970
1080
|
const fieldLayouts = [];
|
|
971
1081
|
const typeRegistry = {};
|
|
972
|
-
const annotations = extractJSDocAnnotationNodes(
|
|
1082
|
+
const annotations = extractJSDocAnnotationNodes(
|
|
1083
|
+
classDecl,
|
|
1084
|
+
file,
|
|
1085
|
+
makeParseOptions(extensionRegistry)
|
|
1086
|
+
);
|
|
973
1087
|
const visiting = /* @__PURE__ */ new Set();
|
|
974
1088
|
const instanceMethods = [];
|
|
975
1089
|
const staticMethods = [];
|
|
976
1090
|
for (const member of classDecl.members) {
|
|
977
1091
|
if (ts4.isPropertyDeclaration(member)) {
|
|
978
|
-
const fieldNode = analyzeFieldToIR(
|
|
1092
|
+
const fieldNode = analyzeFieldToIR(
|
|
1093
|
+
member,
|
|
1094
|
+
checker,
|
|
1095
|
+
file,
|
|
1096
|
+
typeRegistry,
|
|
1097
|
+
visiting,
|
|
1098
|
+
extensionRegistry
|
|
1099
|
+
);
|
|
979
1100
|
if (fieldNode) {
|
|
980
1101
|
fields.push(fieldNode);
|
|
981
1102
|
fieldLayouts.push({});
|
|
@@ -1002,15 +1123,26 @@ function analyzeClassToIR(classDecl, checker, file = "") {
|
|
|
1002
1123
|
staticMethods
|
|
1003
1124
|
};
|
|
1004
1125
|
}
|
|
1005
|
-
function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
1126
|
+
function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry) {
|
|
1006
1127
|
const name = interfaceDecl.name.text;
|
|
1007
1128
|
const fields = [];
|
|
1008
1129
|
const typeRegistry = {};
|
|
1009
|
-
const annotations = extractJSDocAnnotationNodes(
|
|
1130
|
+
const annotations = extractJSDocAnnotationNodes(
|
|
1131
|
+
interfaceDecl,
|
|
1132
|
+
file,
|
|
1133
|
+
makeParseOptions(extensionRegistry)
|
|
1134
|
+
);
|
|
1010
1135
|
const visiting = /* @__PURE__ */ new Set();
|
|
1011
1136
|
for (const member of interfaceDecl.members) {
|
|
1012
1137
|
if (ts4.isPropertySignature(member)) {
|
|
1013
|
-
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1138
|
+
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1139
|
+
member,
|
|
1140
|
+
checker,
|
|
1141
|
+
file,
|
|
1142
|
+
typeRegistry,
|
|
1143
|
+
visiting,
|
|
1144
|
+
extensionRegistry
|
|
1145
|
+
);
|
|
1014
1146
|
if (fieldNode) {
|
|
1015
1147
|
fields.push(fieldNode);
|
|
1016
1148
|
}
|
|
@@ -1027,7 +1159,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
|
1027
1159
|
staticMethods: []
|
|
1028
1160
|
};
|
|
1029
1161
|
}
|
|
1030
|
-
function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
1162
|
+
function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
|
|
1031
1163
|
if (!ts4.isTypeLiteralNode(typeAlias.type)) {
|
|
1032
1164
|
const sourceFile = typeAlias.getSourceFile();
|
|
1033
1165
|
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
@@ -1040,11 +1172,22 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
|
1040
1172
|
const name = typeAlias.name.text;
|
|
1041
1173
|
const fields = [];
|
|
1042
1174
|
const typeRegistry = {};
|
|
1043
|
-
const annotations = extractJSDocAnnotationNodes(
|
|
1175
|
+
const annotations = extractJSDocAnnotationNodes(
|
|
1176
|
+
typeAlias,
|
|
1177
|
+
file,
|
|
1178
|
+
makeParseOptions(extensionRegistry)
|
|
1179
|
+
);
|
|
1044
1180
|
const visiting = /* @__PURE__ */ new Set();
|
|
1045
1181
|
for (const member of typeAlias.type.members) {
|
|
1046
1182
|
if (ts4.isPropertySignature(member)) {
|
|
1047
|
-
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1183
|
+
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1184
|
+
member,
|
|
1185
|
+
checker,
|
|
1186
|
+
file,
|
|
1187
|
+
typeRegistry,
|
|
1188
|
+
visiting,
|
|
1189
|
+
extensionRegistry
|
|
1190
|
+
);
|
|
1048
1191
|
if (fieldNode) {
|
|
1049
1192
|
fields.push(fieldNode);
|
|
1050
1193
|
}
|
|
@@ -1063,7 +1206,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
|
1063
1206
|
}
|
|
1064
1207
|
};
|
|
1065
1208
|
}
|
|
1066
|
-
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
1209
|
+
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1067
1210
|
if (!ts4.isIdentifier(prop.name)) {
|
|
1068
1211
|
return null;
|
|
1069
1212
|
}
|
|
@@ -1071,14 +1214,26 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
|
1071
1214
|
const tsType = checker.getTypeAtLocation(prop);
|
|
1072
1215
|
const optional = prop.questionToken !== void 0;
|
|
1073
1216
|
const provenance = provenanceForNode(prop, file);
|
|
1074
|
-
let type = resolveTypeNode(
|
|
1217
|
+
let type = resolveTypeNode(
|
|
1218
|
+
tsType,
|
|
1219
|
+
checker,
|
|
1220
|
+
file,
|
|
1221
|
+
typeRegistry,
|
|
1222
|
+
visiting,
|
|
1223
|
+
prop,
|
|
1224
|
+
extensionRegistry
|
|
1225
|
+
);
|
|
1075
1226
|
const constraints = [];
|
|
1076
1227
|
if (prop.type) {
|
|
1077
|
-
constraints.push(
|
|
1228
|
+
constraints.push(
|
|
1229
|
+
...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
|
|
1230
|
+
);
|
|
1078
1231
|
}
|
|
1079
|
-
constraints.push(...extractJSDocConstraintNodes(prop, file));
|
|
1232
|
+
constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
|
|
1080
1233
|
let annotations = [];
|
|
1081
|
-
annotations.push(
|
|
1234
|
+
annotations.push(
|
|
1235
|
+
...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
1236
|
+
);
|
|
1082
1237
|
const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
|
|
1083
1238
|
if (defaultAnnotation && !annotations.some((a) => a.annotationKind === "defaultValue")) {
|
|
1084
1239
|
annotations.push(defaultAnnotation);
|
|
@@ -1094,7 +1249,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
|
1094
1249
|
provenance
|
|
1095
1250
|
};
|
|
1096
1251
|
}
|
|
1097
|
-
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting) {
|
|
1252
|
+
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1098
1253
|
if (!ts4.isIdentifier(prop.name)) {
|
|
1099
1254
|
return null;
|
|
1100
1255
|
}
|
|
@@ -1102,14 +1257,26 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
1102
1257
|
const tsType = checker.getTypeAtLocation(prop);
|
|
1103
1258
|
const optional = prop.questionToken !== void 0;
|
|
1104
1259
|
const provenance = provenanceForNode(prop, file);
|
|
1105
|
-
let type = resolveTypeNode(
|
|
1260
|
+
let type = resolveTypeNode(
|
|
1261
|
+
tsType,
|
|
1262
|
+
checker,
|
|
1263
|
+
file,
|
|
1264
|
+
typeRegistry,
|
|
1265
|
+
visiting,
|
|
1266
|
+
prop,
|
|
1267
|
+
extensionRegistry
|
|
1268
|
+
);
|
|
1106
1269
|
const constraints = [];
|
|
1107
1270
|
if (prop.type) {
|
|
1108
|
-
constraints.push(
|
|
1271
|
+
constraints.push(
|
|
1272
|
+
...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
|
|
1273
|
+
);
|
|
1109
1274
|
}
|
|
1110
|
-
constraints.push(...extractJSDocConstraintNodes(prop, file));
|
|
1275
|
+
constraints.push(...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type)));
|
|
1111
1276
|
let annotations = [];
|
|
1112
|
-
annotations.push(
|
|
1277
|
+
annotations.push(
|
|
1278
|
+
...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
1279
|
+
);
|
|
1113
1280
|
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
1114
1281
|
return {
|
|
1115
1282
|
kind: "field",
|
|
@@ -1183,7 +1350,66 @@ function parseEnumMemberDisplayName(value) {
|
|
|
1183
1350
|
if (label === "") return null;
|
|
1184
1351
|
return { value: match[1], label };
|
|
1185
1352
|
}
|
|
1186
|
-
function
|
|
1353
|
+
function resolveRegisteredCustomType(sourceNode, extensionRegistry, checker) {
|
|
1354
|
+
if (sourceNode === void 0 || extensionRegistry === void 0) {
|
|
1355
|
+
return null;
|
|
1356
|
+
}
|
|
1357
|
+
const typeNode = extractTypeNodeFromSource(sourceNode);
|
|
1358
|
+
if (typeNode === void 0) {
|
|
1359
|
+
return null;
|
|
1360
|
+
}
|
|
1361
|
+
return resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker);
|
|
1362
|
+
}
|
|
1363
|
+
function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker) {
|
|
1364
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
1365
|
+
return resolveRegisteredCustomTypeFromTypeNode(typeNode.type, extensionRegistry, checker);
|
|
1366
|
+
}
|
|
1367
|
+
const typeName = getTypeNodeRegistrationName(typeNode);
|
|
1368
|
+
if (typeName === null) {
|
|
1369
|
+
return null;
|
|
1370
|
+
}
|
|
1371
|
+
const registration = extensionRegistry.findTypeByName(typeName);
|
|
1372
|
+
if (registration !== void 0) {
|
|
1373
|
+
return {
|
|
1374
|
+
kind: "custom",
|
|
1375
|
+
typeId: `${registration.extensionId}/${registration.registration.typeName}`,
|
|
1376
|
+
payload: null
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
if (ts4.isTypeReferenceNode(typeNode) && ts4.isIdentifier(typeNode.typeName)) {
|
|
1380
|
+
const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts4.isTypeAliasDeclaration);
|
|
1381
|
+
if (aliasDecl !== void 0) {
|
|
1382
|
+
return resolveRegisteredCustomTypeFromTypeNode(aliasDecl.type, extensionRegistry, checker);
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
return null;
|
|
1386
|
+
}
|
|
1387
|
+
function extractTypeNodeFromSource(sourceNode) {
|
|
1388
|
+
if (ts4.isPropertyDeclaration(sourceNode) || ts4.isPropertySignature(sourceNode) || ts4.isParameter(sourceNode) || ts4.isTypeAliasDeclaration(sourceNode)) {
|
|
1389
|
+
return sourceNode.type;
|
|
1390
|
+
}
|
|
1391
|
+
if (ts4.isTypeNode(sourceNode)) {
|
|
1392
|
+
return sourceNode;
|
|
1393
|
+
}
|
|
1394
|
+
return void 0;
|
|
1395
|
+
}
|
|
1396
|
+
function getTypeNodeRegistrationName(typeNode) {
|
|
1397
|
+
if (ts4.isTypeReferenceNode(typeNode)) {
|
|
1398
|
+
return ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : typeNode.typeName.right.text;
|
|
1399
|
+
}
|
|
1400
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
1401
|
+
return getTypeNodeRegistrationName(typeNode.type);
|
|
1402
|
+
}
|
|
1403
|
+
if (typeNode.kind === ts4.SyntaxKind.BigIntKeyword || typeNode.kind === ts4.SyntaxKind.StringKeyword || typeNode.kind === ts4.SyntaxKind.NumberKeyword || typeNode.kind === ts4.SyntaxKind.BooleanKeyword) {
|
|
1404
|
+
return typeNode.getText();
|
|
1405
|
+
}
|
|
1406
|
+
return null;
|
|
1407
|
+
}
|
|
1408
|
+
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
1409
|
+
const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
|
|
1410
|
+
if (customType) {
|
|
1411
|
+
return customType;
|
|
1412
|
+
}
|
|
1187
1413
|
if (type.flags & ts4.TypeFlags.String) {
|
|
1188
1414
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1189
1415
|
}
|
|
@@ -1212,26 +1438,50 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
1212
1438
|
};
|
|
1213
1439
|
}
|
|
1214
1440
|
if (type.isUnion()) {
|
|
1215
|
-
return resolveUnionType(
|
|
1441
|
+
return resolveUnionType(
|
|
1442
|
+
type,
|
|
1443
|
+
checker,
|
|
1444
|
+
file,
|
|
1445
|
+
typeRegistry,
|
|
1446
|
+
visiting,
|
|
1447
|
+
sourceNode,
|
|
1448
|
+
extensionRegistry
|
|
1449
|
+
);
|
|
1216
1450
|
}
|
|
1217
1451
|
if (checker.isArrayType(type)) {
|
|
1218
|
-
return resolveArrayType(
|
|
1452
|
+
return resolveArrayType(
|
|
1453
|
+
type,
|
|
1454
|
+
checker,
|
|
1455
|
+
file,
|
|
1456
|
+
typeRegistry,
|
|
1457
|
+
visiting,
|
|
1458
|
+
sourceNode,
|
|
1459
|
+
extensionRegistry
|
|
1460
|
+
);
|
|
1219
1461
|
}
|
|
1220
1462
|
if (isObjectType(type)) {
|
|
1221
|
-
return resolveObjectType(type, checker, file, typeRegistry, visiting);
|
|
1463
|
+
return resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry);
|
|
1222
1464
|
}
|
|
1223
1465
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1224
1466
|
}
|
|
1225
|
-
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode) {
|
|
1467
|
+
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
1226
1468
|
const typeName = getNamedTypeName(type);
|
|
1227
1469
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
1228
1470
|
if (typeName && typeName in typeRegistry) {
|
|
1229
1471
|
return { kind: "reference", name: typeName, typeArguments: [] };
|
|
1230
1472
|
}
|
|
1231
1473
|
const allTypes = type.types;
|
|
1474
|
+
const unionMemberTypeNodes = extractUnionMemberTypeNodes(sourceNode, checker);
|
|
1475
|
+
const nonNullSourceNodes = unionMemberTypeNodes.filter(
|
|
1476
|
+
(memberTypeNode) => !isNullishTypeNode(resolveAliasedTypeNode(memberTypeNode, checker))
|
|
1477
|
+
);
|
|
1232
1478
|
const nonNullTypes = allTypes.filter(
|
|
1233
|
-
(
|
|
1479
|
+
(memberType) => !(memberType.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
|
|
1234
1480
|
);
|
|
1481
|
+
const nonNullMembers = nonNullTypes.map((memberType, index) => ({
|
|
1482
|
+
memberType,
|
|
1483
|
+
sourceNode: nonNullSourceNodes.length === nonNullTypes.length ? nonNullSourceNodes[index] : void 0
|
|
1484
|
+
}));
|
|
1235
1485
|
const hasNull = allTypes.some((t) => t.flags & ts4.TypeFlags.Null);
|
|
1236
1486
|
const memberDisplayNames = /* @__PURE__ */ new Map();
|
|
1237
1487
|
if (namedDecl) {
|
|
@@ -1248,7 +1498,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
1248
1498
|
if (!typeName) {
|
|
1249
1499
|
return result;
|
|
1250
1500
|
}
|
|
1251
|
-
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
1501
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
1252
1502
|
typeRegistry[typeName] = {
|
|
1253
1503
|
name: typeName,
|
|
1254
1504
|
type: result,
|
|
@@ -1296,14 +1546,15 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
1296
1546
|
} : enumNode;
|
|
1297
1547
|
return registerNamed(result);
|
|
1298
1548
|
}
|
|
1299
|
-
if (
|
|
1549
|
+
if (nonNullMembers.length === 1 && nonNullMembers[0]) {
|
|
1300
1550
|
const inner = resolveTypeNode(
|
|
1301
|
-
|
|
1551
|
+
nonNullMembers[0].memberType,
|
|
1302
1552
|
checker,
|
|
1303
1553
|
file,
|
|
1304
1554
|
typeRegistry,
|
|
1305
1555
|
visiting,
|
|
1306
|
-
sourceNode
|
|
1556
|
+
nonNullMembers[0].sourceNode ?? sourceNode,
|
|
1557
|
+
extensionRegistry
|
|
1307
1558
|
);
|
|
1308
1559
|
const result = hasNull ? {
|
|
1309
1560
|
kind: "union",
|
|
@@ -1311,21 +1562,38 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
1311
1562
|
} : inner;
|
|
1312
1563
|
return registerNamed(result);
|
|
1313
1564
|
}
|
|
1314
|
-
const members =
|
|
1315
|
-
(
|
|
1565
|
+
const members = nonNullMembers.map(
|
|
1566
|
+
({ memberType, sourceNode: memberSourceNode }) => resolveTypeNode(
|
|
1567
|
+
memberType,
|
|
1568
|
+
checker,
|
|
1569
|
+
file,
|
|
1570
|
+
typeRegistry,
|
|
1571
|
+
visiting,
|
|
1572
|
+
memberSourceNode ?? sourceNode,
|
|
1573
|
+
extensionRegistry
|
|
1574
|
+
)
|
|
1316
1575
|
);
|
|
1317
1576
|
if (hasNull) {
|
|
1318
1577
|
members.push({ kind: "primitive", primitiveKind: "null" });
|
|
1319
1578
|
}
|
|
1320
1579
|
return registerNamed({ kind: "union", members });
|
|
1321
1580
|
}
|
|
1322
|
-
function resolveArrayType(type, checker, file, typeRegistry, visiting) {
|
|
1581
|
+
function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
1323
1582
|
const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
|
|
1324
1583
|
const elementType = typeArgs?.[0];
|
|
1325
|
-
const
|
|
1584
|
+
const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
|
|
1585
|
+
const items = elementType ? resolveTypeNode(
|
|
1586
|
+
elementType,
|
|
1587
|
+
checker,
|
|
1588
|
+
file,
|
|
1589
|
+
typeRegistry,
|
|
1590
|
+
visiting,
|
|
1591
|
+
elementSourceNode,
|
|
1592
|
+
extensionRegistry
|
|
1593
|
+
) : { kind: "primitive", primitiveKind: "string" };
|
|
1326
1594
|
return { kind: "array", items };
|
|
1327
1595
|
}
|
|
1328
|
-
function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
|
|
1596
|
+
function tryResolveRecordType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1329
1597
|
if (type.getProperties().length > 0) {
|
|
1330
1598
|
return null;
|
|
1331
1599
|
}
|
|
@@ -1333,7 +1601,15 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
|
|
|
1333
1601
|
if (!indexInfo) {
|
|
1334
1602
|
return null;
|
|
1335
1603
|
}
|
|
1336
|
-
const valueType = resolveTypeNode(
|
|
1604
|
+
const valueType = resolveTypeNode(
|
|
1605
|
+
indexInfo.type,
|
|
1606
|
+
checker,
|
|
1607
|
+
file,
|
|
1608
|
+
typeRegistry,
|
|
1609
|
+
visiting,
|
|
1610
|
+
void 0,
|
|
1611
|
+
extensionRegistry
|
|
1612
|
+
);
|
|
1337
1613
|
return { kind: "record", valueType };
|
|
1338
1614
|
}
|
|
1339
1615
|
function typeNodeContainsReference(type, targetName) {
|
|
@@ -1361,7 +1637,7 @@ function typeNodeContainsReference(type, targetName) {
|
|
|
1361
1637
|
}
|
|
1362
1638
|
}
|
|
1363
1639
|
}
|
|
1364
|
-
function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
1640
|
+
function resolveObjectType(type, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1365
1641
|
const typeName = getNamedTypeName(type);
|
|
1366
1642
|
const namedTypeName = typeName ?? void 0;
|
|
1367
1643
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
@@ -1392,7 +1668,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
1392
1668
|
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
1393
1669
|
}
|
|
1394
1670
|
}
|
|
1395
|
-
const recordNode = tryResolveRecordType(
|
|
1671
|
+
const recordNode = tryResolveRecordType(
|
|
1672
|
+
type,
|
|
1673
|
+
checker,
|
|
1674
|
+
file,
|
|
1675
|
+
typeRegistry,
|
|
1676
|
+
visiting,
|
|
1677
|
+
extensionRegistry
|
|
1678
|
+
);
|
|
1396
1679
|
if (recordNode) {
|
|
1397
1680
|
visiting.delete(type);
|
|
1398
1681
|
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
@@ -1401,7 +1684,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
1401
1684
|
clearNamedTypeRegistration();
|
|
1402
1685
|
return recordNode;
|
|
1403
1686
|
}
|
|
1404
|
-
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
1687
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
1405
1688
|
typeRegistry[namedTypeName] = {
|
|
1406
1689
|
name: namedTypeName,
|
|
1407
1690
|
type: recordNode,
|
|
@@ -1413,7 +1696,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
1413
1696
|
return recordNode;
|
|
1414
1697
|
}
|
|
1415
1698
|
const properties = [];
|
|
1416
|
-
const fieldInfoMap = getNamedTypeFieldNodeInfoMap(
|
|
1699
|
+
const fieldInfoMap = getNamedTypeFieldNodeInfoMap(
|
|
1700
|
+
type,
|
|
1701
|
+
checker,
|
|
1702
|
+
file,
|
|
1703
|
+
typeRegistry,
|
|
1704
|
+
visiting,
|
|
1705
|
+
extensionRegistry
|
|
1706
|
+
);
|
|
1417
1707
|
for (const prop of type.getProperties()) {
|
|
1418
1708
|
const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
1419
1709
|
if (!declaration) continue;
|
|
@@ -1425,7 +1715,8 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
1425
1715
|
file,
|
|
1426
1716
|
typeRegistry,
|
|
1427
1717
|
visiting,
|
|
1428
|
-
declaration
|
|
1718
|
+
declaration,
|
|
1719
|
+
extensionRegistry
|
|
1429
1720
|
);
|
|
1430
1721
|
const fieldNodeInfo = fieldInfoMap?.get(prop.name);
|
|
1431
1722
|
properties.push({
|
|
@@ -1444,7 +1735,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
1444
1735
|
additionalProperties: true
|
|
1445
1736
|
};
|
|
1446
1737
|
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
1447
|
-
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
1738
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
|
|
1448
1739
|
typeRegistry[namedTypeName] = {
|
|
1449
1740
|
name: namedTypeName,
|
|
1450
1741
|
type: objectNode,
|
|
@@ -1455,7 +1746,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
1455
1746
|
}
|
|
1456
1747
|
return objectNode;
|
|
1457
1748
|
}
|
|
1458
|
-
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting) {
|
|
1749
|
+
function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1459
1750
|
const symbols = [type.getSymbol(), type.aliasSymbol].filter(
|
|
1460
1751
|
(s) => s?.declarations != null && s.declarations.length > 0
|
|
1461
1752
|
);
|
|
@@ -1467,7 +1758,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
1467
1758
|
const map = /* @__PURE__ */ new Map();
|
|
1468
1759
|
for (const member of classDecl.members) {
|
|
1469
1760
|
if (ts4.isPropertyDeclaration(member) && ts4.isIdentifier(member.name)) {
|
|
1470
|
-
const fieldNode = analyzeFieldToIR(
|
|
1761
|
+
const fieldNode = analyzeFieldToIR(
|
|
1762
|
+
member,
|
|
1763
|
+
checker,
|
|
1764
|
+
file,
|
|
1765
|
+
typeRegistry,
|
|
1766
|
+
visiting,
|
|
1767
|
+
extensionRegistry
|
|
1768
|
+
);
|
|
1471
1769
|
if (fieldNode) {
|
|
1472
1770
|
map.set(fieldNode.name, {
|
|
1473
1771
|
constraints: [...fieldNode.constraints],
|
|
@@ -1481,7 +1779,14 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
1481
1779
|
}
|
|
1482
1780
|
const interfaceDecl = declarations.find(ts4.isInterfaceDeclaration);
|
|
1483
1781
|
if (interfaceDecl) {
|
|
1484
|
-
return buildFieldNodeInfoMap(
|
|
1782
|
+
return buildFieldNodeInfoMap(
|
|
1783
|
+
interfaceDecl.members,
|
|
1784
|
+
checker,
|
|
1785
|
+
file,
|
|
1786
|
+
typeRegistry,
|
|
1787
|
+
visiting,
|
|
1788
|
+
extensionRegistry
|
|
1789
|
+
);
|
|
1485
1790
|
}
|
|
1486
1791
|
const typeAliasDecl = declarations.find(ts4.isTypeAliasDeclaration);
|
|
1487
1792
|
if (typeAliasDecl && ts4.isTypeLiteralNode(typeAliasDecl.type)) {
|
|
@@ -1490,17 +1795,68 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
1490
1795
|
checker,
|
|
1491
1796
|
file,
|
|
1492
1797
|
typeRegistry,
|
|
1493
|
-
visiting
|
|
1798
|
+
visiting,
|
|
1799
|
+
extensionRegistry
|
|
1494
1800
|
);
|
|
1495
1801
|
}
|
|
1496
1802
|
}
|
|
1497
1803
|
return null;
|
|
1498
1804
|
}
|
|
1499
|
-
function
|
|
1805
|
+
function extractArrayElementTypeNode(sourceNode, checker) {
|
|
1806
|
+
const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
|
|
1807
|
+
if (typeNode === void 0) {
|
|
1808
|
+
return void 0;
|
|
1809
|
+
}
|
|
1810
|
+
const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
|
|
1811
|
+
if (ts4.isArrayTypeNode(resolvedTypeNode)) {
|
|
1812
|
+
return resolvedTypeNode.elementType;
|
|
1813
|
+
}
|
|
1814
|
+
if (ts4.isTypeReferenceNode(resolvedTypeNode) && ts4.isIdentifier(resolvedTypeNode.typeName) && resolvedTypeNode.typeName.text === "Array" && resolvedTypeNode.typeArguments?.[0]) {
|
|
1815
|
+
return resolvedTypeNode.typeArguments[0];
|
|
1816
|
+
}
|
|
1817
|
+
return void 0;
|
|
1818
|
+
}
|
|
1819
|
+
function extractUnionMemberTypeNodes(sourceNode, checker) {
|
|
1820
|
+
const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
|
|
1821
|
+
if (!typeNode) {
|
|
1822
|
+
return [];
|
|
1823
|
+
}
|
|
1824
|
+
const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
|
|
1825
|
+
return ts4.isUnionTypeNode(resolvedTypeNode) ? [...resolvedTypeNode.types] : [];
|
|
1826
|
+
}
|
|
1827
|
+
function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
|
|
1828
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
1829
|
+
return resolveAliasedTypeNode(typeNode.type, checker, visited);
|
|
1830
|
+
}
|
|
1831
|
+
if (!ts4.isTypeReferenceNode(typeNode) || !ts4.isIdentifier(typeNode.typeName)) {
|
|
1832
|
+
return typeNode;
|
|
1833
|
+
}
|
|
1834
|
+
const symbol = checker.getSymbolAtLocation(typeNode.typeName);
|
|
1835
|
+
const aliasDecl = symbol?.declarations?.find(ts4.isTypeAliasDeclaration);
|
|
1836
|
+
if (aliasDecl === void 0 || visited.has(aliasDecl)) {
|
|
1837
|
+
return typeNode;
|
|
1838
|
+
}
|
|
1839
|
+
visited.add(aliasDecl);
|
|
1840
|
+
return resolveAliasedTypeNode(aliasDecl.type, checker, visited);
|
|
1841
|
+
}
|
|
1842
|
+
function isNullishTypeNode(typeNode) {
|
|
1843
|
+
if (typeNode.kind === ts4.SyntaxKind.NullKeyword || typeNode.kind === ts4.SyntaxKind.UndefinedKeyword) {
|
|
1844
|
+
return true;
|
|
1845
|
+
}
|
|
1846
|
+
return ts4.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts4.SyntaxKind.NullKeyword || typeNode.literal.kind === ts4.SyntaxKind.UndefinedKeyword);
|
|
1847
|
+
}
|
|
1848
|
+
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1500
1849
|
const map = /* @__PURE__ */ new Map();
|
|
1501
1850
|
for (const member of members) {
|
|
1502
1851
|
if (ts4.isPropertySignature(member)) {
|
|
1503
|
-
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1852
|
+
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1853
|
+
member,
|
|
1854
|
+
checker,
|
|
1855
|
+
file,
|
|
1856
|
+
typeRegistry,
|
|
1857
|
+
visiting,
|
|
1858
|
+
extensionRegistry
|
|
1859
|
+
);
|
|
1504
1860
|
if (fieldNode) {
|
|
1505
1861
|
map.set(fieldNode.name, {
|
|
1506
1862
|
constraints: [...fieldNode.constraints],
|
|
@@ -1513,7 +1869,7 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting) {
|
|
|
1513
1869
|
return map;
|
|
1514
1870
|
}
|
|
1515
1871
|
var MAX_ALIAS_CHAIN_DEPTH = 8;
|
|
1516
|
-
function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
|
|
1872
|
+
function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegistry, depth = 0) {
|
|
1517
1873
|
if (!ts4.isTypeReferenceNode(typeNode)) return [];
|
|
1518
1874
|
if (depth >= MAX_ALIAS_CHAIN_DEPTH) {
|
|
1519
1875
|
const aliasName = typeNode.typeName.getText();
|
|
@@ -1526,8 +1882,29 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
|
|
|
1526
1882
|
const aliasDecl = symbol.declarations.find(ts4.isTypeAliasDeclaration);
|
|
1527
1883
|
if (!aliasDecl) return [];
|
|
1528
1884
|
if (ts4.isTypeLiteralNode(aliasDecl.type)) return [];
|
|
1529
|
-
const
|
|
1530
|
-
|
|
1885
|
+
const aliasFieldType = resolveTypeNode(
|
|
1886
|
+
checker.getTypeAtLocation(aliasDecl.type),
|
|
1887
|
+
checker,
|
|
1888
|
+
file,
|
|
1889
|
+
{},
|
|
1890
|
+
/* @__PURE__ */ new Set(),
|
|
1891
|
+
aliasDecl.type,
|
|
1892
|
+
extensionRegistry
|
|
1893
|
+
);
|
|
1894
|
+
const constraints = extractJSDocConstraintNodes(
|
|
1895
|
+
aliasDecl,
|
|
1896
|
+
file,
|
|
1897
|
+
makeParseOptions(extensionRegistry, aliasFieldType)
|
|
1898
|
+
);
|
|
1899
|
+
constraints.push(
|
|
1900
|
+
...extractTypeAliasConstraintNodes(
|
|
1901
|
+
aliasDecl.type,
|
|
1902
|
+
checker,
|
|
1903
|
+
file,
|
|
1904
|
+
extensionRegistry,
|
|
1905
|
+
depth + 1
|
|
1906
|
+
)
|
|
1907
|
+
);
|
|
1531
1908
|
return constraints;
|
|
1532
1909
|
}
|
|
1533
1910
|
function provenanceForNode(node, file) {
|
|
@@ -2014,7 +2391,12 @@ function applyCustomConstraint(schema, constraint, ctx) {
|
|
|
2014
2391
|
`Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
|
|
2015
2392
|
);
|
|
2016
2393
|
}
|
|
2017
|
-
|
|
2394
|
+
assignVendorPrefixedExtensionKeywords(
|
|
2395
|
+
schema,
|
|
2396
|
+
registration.toJsonSchema(constraint.payload, ctx.vendorPrefix),
|
|
2397
|
+
ctx.vendorPrefix,
|
|
2398
|
+
`custom constraint "${constraint.constraintId}"`
|
|
2399
|
+
);
|
|
2018
2400
|
}
|
|
2019
2401
|
function applyCustomAnnotation(schema, annotation, ctx) {
|
|
2020
2402
|
const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
|
|
@@ -2026,7 +2408,22 @@ function applyCustomAnnotation(schema, annotation, ctx) {
|
|
|
2026
2408
|
if (registration.toJsonSchema === void 0) {
|
|
2027
2409
|
return;
|
|
2028
2410
|
}
|
|
2029
|
-
|
|
2411
|
+
assignVendorPrefixedExtensionKeywords(
|
|
2412
|
+
schema,
|
|
2413
|
+
registration.toJsonSchema(annotation.value, ctx.vendorPrefix),
|
|
2414
|
+
ctx.vendorPrefix,
|
|
2415
|
+
`custom annotation "${annotation.annotationId}"`
|
|
2416
|
+
);
|
|
2417
|
+
}
|
|
2418
|
+
function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPrefix, source) {
|
|
2419
|
+
for (const [key, value] of Object.entries(extensionSchema)) {
|
|
2420
|
+
if (!key.startsWith(`${vendorPrefix}-`)) {
|
|
2421
|
+
throw new Error(
|
|
2422
|
+
`Cannot apply ${source}: extension hooks may only emit "${vendorPrefix}-*" JSON Schema keywords`
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
schema[key] = value;
|
|
2426
|
+
}
|
|
2030
2427
|
}
|
|
2031
2428
|
|
|
2032
2429
|
// src/ui-schema/schema.ts
|
|
@@ -2253,15 +2650,16 @@ function generateUiSchemaFromIR(ir) {
|
|
|
2253
2650
|
}
|
|
2254
2651
|
|
|
2255
2652
|
// src/generators/class-schema.ts
|
|
2256
|
-
function generateClassSchemas(analysis, source) {
|
|
2653
|
+
function generateClassSchemas(analysis, source, options) {
|
|
2257
2654
|
const ir = canonicalizeTSDoc(analysis, source);
|
|
2258
2655
|
return {
|
|
2259
|
-
jsonSchema: generateJsonSchemaFromIR(ir),
|
|
2656
|
+
jsonSchema: generateJsonSchemaFromIR(ir, options),
|
|
2260
2657
|
uiSchema: generateUiSchemaFromIR(ir)
|
|
2261
2658
|
};
|
|
2262
2659
|
}
|
|
2263
2660
|
|
|
2264
2661
|
// src/validate/constraint-validator.ts
|
|
2662
|
+
var import_core4 = require("@formspec/core");
|
|
2265
2663
|
function addContradiction(ctx, message, primary, related) {
|
|
2266
2664
|
ctx.diagnostics.push({
|
|
2267
2665
|
code: "CONTRADICTING_CONSTRAINTS",
|
|
@@ -2307,6 +2705,13 @@ function addConstraintBroadening(ctx, message, primary, related) {
|
|
|
2307
2705
|
relatedLocations: [related]
|
|
2308
2706
|
});
|
|
2309
2707
|
}
|
|
2708
|
+
function getExtensionIdFromConstraintId(constraintId) {
|
|
2709
|
+
const separator = constraintId.lastIndexOf("/");
|
|
2710
|
+
if (separator <= 0) {
|
|
2711
|
+
return null;
|
|
2712
|
+
}
|
|
2713
|
+
return constraintId.slice(0, separator);
|
|
2714
|
+
}
|
|
2310
2715
|
function findNumeric(constraints, constraintKind) {
|
|
2311
2716
|
return constraints.find((c) => c.constraintKind === constraintKind);
|
|
2312
2717
|
}
|
|
@@ -2477,6 +2882,112 @@ function checkConstraintBroadening(ctx, fieldName, constraints) {
|
|
|
2477
2882
|
strongestByKey.set(key, constraint);
|
|
2478
2883
|
}
|
|
2479
2884
|
}
|
|
2885
|
+
function compareCustomConstraintStrength(current, previous) {
|
|
2886
|
+
const order = current.comparePayloads(current.constraint.payload, previous.constraint.payload);
|
|
2887
|
+
const equalPayloadTiebreaker = order === 0 ? compareSemanticInclusivity(current.role.inclusive, previous.role.inclusive) : order;
|
|
2888
|
+
switch (current.role.bound) {
|
|
2889
|
+
case "lower":
|
|
2890
|
+
return equalPayloadTiebreaker;
|
|
2891
|
+
case "upper":
|
|
2892
|
+
return equalPayloadTiebreaker === 0 ? 0 : -equalPayloadTiebreaker;
|
|
2893
|
+
case "exact":
|
|
2894
|
+
return order === 0 ? 0 : Number.NaN;
|
|
2895
|
+
default: {
|
|
2896
|
+
const _exhaustive = current.role.bound;
|
|
2897
|
+
return _exhaustive;
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
function compareSemanticInclusivity(currentInclusive, previousInclusive) {
|
|
2902
|
+
if (currentInclusive === previousInclusive) {
|
|
2903
|
+
return 0;
|
|
2904
|
+
}
|
|
2905
|
+
return currentInclusive ? -1 : 1;
|
|
2906
|
+
}
|
|
2907
|
+
function customConstraintsContradict(lower, upper) {
|
|
2908
|
+
const order = lower.comparePayloads(lower.constraint.payload, upper.constraint.payload);
|
|
2909
|
+
if (order > 0) {
|
|
2910
|
+
return true;
|
|
2911
|
+
}
|
|
2912
|
+
if (order < 0) {
|
|
2913
|
+
return false;
|
|
2914
|
+
}
|
|
2915
|
+
return !lower.role.inclusive || !upper.role.inclusive;
|
|
2916
|
+
}
|
|
2917
|
+
function describeCustomConstraintTag(constraint) {
|
|
2918
|
+
return constraint.provenance.tagName ?? constraint.constraintId;
|
|
2919
|
+
}
|
|
2920
|
+
function checkCustomConstraintSemantics(ctx, fieldName, constraints) {
|
|
2921
|
+
if (ctx.extensionRegistry === void 0) {
|
|
2922
|
+
return;
|
|
2923
|
+
}
|
|
2924
|
+
const strongestByKey = /* @__PURE__ */ new Map();
|
|
2925
|
+
const lowerByFamily = /* @__PURE__ */ new Map();
|
|
2926
|
+
const upperByFamily = /* @__PURE__ */ new Map();
|
|
2927
|
+
for (const constraint of constraints) {
|
|
2928
|
+
if (constraint.constraintKind !== "custom") {
|
|
2929
|
+
continue;
|
|
2930
|
+
}
|
|
2931
|
+
const registration = ctx.extensionRegistry.findConstraint(constraint.constraintId);
|
|
2932
|
+
if (registration?.comparePayloads === void 0 || registration.semanticRole === void 0) {
|
|
2933
|
+
continue;
|
|
2934
|
+
}
|
|
2935
|
+
const entry = {
|
|
2936
|
+
constraint,
|
|
2937
|
+
comparePayloads: registration.comparePayloads,
|
|
2938
|
+
role: registration.semanticRole
|
|
2939
|
+
};
|
|
2940
|
+
const familyKey = `${registration.semanticRole.family}:${pathKey(constraint)}`;
|
|
2941
|
+
const boundKey = `${familyKey}:${registration.semanticRole.bound}`;
|
|
2942
|
+
const previous = strongestByKey.get(boundKey);
|
|
2943
|
+
if (previous !== void 0) {
|
|
2944
|
+
const strength = compareCustomConstraintStrength(entry, previous);
|
|
2945
|
+
if (Number.isNaN(strength)) {
|
|
2946
|
+
addContradiction(
|
|
2947
|
+
ctx,
|
|
2948
|
+
`Field "${formatPathTargetFieldName(fieldName, constraint.path?.segments ?? [])}": ${describeCustomConstraintTag(constraint)} conflicts with ${describeCustomConstraintTag(previous.constraint)}`,
|
|
2949
|
+
constraint.provenance,
|
|
2950
|
+
previous.constraint.provenance
|
|
2951
|
+
);
|
|
2952
|
+
continue;
|
|
2953
|
+
}
|
|
2954
|
+
if (strength < 0) {
|
|
2955
|
+
addConstraintBroadening(
|
|
2956
|
+
ctx,
|
|
2957
|
+
`Field "${formatPathTargetFieldName(fieldName, constraint.path?.segments ?? [])}": ${describeCustomConstraintTag(constraint)} is broader than earlier ${describeCustomConstraintTag(previous.constraint)}. Constraints can only narrow.`,
|
|
2958
|
+
constraint.provenance,
|
|
2959
|
+
previous.constraint.provenance
|
|
2960
|
+
);
|
|
2961
|
+
continue;
|
|
2962
|
+
}
|
|
2963
|
+
if (strength > 0) {
|
|
2964
|
+
strongestByKey.set(boundKey, entry);
|
|
2965
|
+
}
|
|
2966
|
+
} else {
|
|
2967
|
+
strongestByKey.set(boundKey, entry);
|
|
2968
|
+
}
|
|
2969
|
+
if (registration.semanticRole.bound === "lower") {
|
|
2970
|
+
lowerByFamily.set(familyKey, strongestByKey.get(boundKey) ?? entry);
|
|
2971
|
+
} else if (registration.semanticRole.bound === "upper") {
|
|
2972
|
+
upperByFamily.set(familyKey, strongestByKey.get(boundKey) ?? entry);
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
for (const [familyKey, lower] of lowerByFamily) {
|
|
2976
|
+
const upper = upperByFamily.get(familyKey);
|
|
2977
|
+
if (upper === void 0) {
|
|
2978
|
+
continue;
|
|
2979
|
+
}
|
|
2980
|
+
if (!customConstraintsContradict(lower, upper)) {
|
|
2981
|
+
continue;
|
|
2982
|
+
}
|
|
2983
|
+
addContradiction(
|
|
2984
|
+
ctx,
|
|
2985
|
+
`Field "${formatPathTargetFieldName(fieldName, lower.constraint.path?.segments ?? [])}": ${describeCustomConstraintTag(lower.constraint)} contradicts ${describeCustomConstraintTag(upper.constraint)}`,
|
|
2986
|
+
lower.constraint.provenance,
|
|
2987
|
+
upper.constraint.provenance
|
|
2988
|
+
);
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2480
2991
|
function checkNumericContradictions(ctx, fieldName, constraints) {
|
|
2481
2992
|
const min = findNumeric(constraints, "minimum");
|
|
2482
2993
|
const max = findNumeric(constraints, "maximum");
|
|
@@ -2788,8 +3299,30 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
|
|
|
2788
3299
|
);
|
|
2789
3300
|
return;
|
|
2790
3301
|
}
|
|
2791
|
-
|
|
2792
|
-
if (
|
|
3302
|
+
const normalizedTagName = constraint.provenance.tagName === void 0 ? void 0 : (0, import_core4.normalizeConstraintTagName)(constraint.provenance.tagName.replace(/^@/, ""));
|
|
3303
|
+
if (normalizedTagName !== void 0) {
|
|
3304
|
+
const tagRegistration = ctx.extensionRegistry.findConstraintTag(normalizedTagName);
|
|
3305
|
+
const extensionId = getExtensionIdFromConstraintId(constraint.constraintId);
|
|
3306
|
+
if (extensionId !== null && tagRegistration?.extensionId === extensionId && tagRegistration.registration.constraintName === registration.constraintName && tagRegistration.registration.isApplicableToType?.(type) === false) {
|
|
3307
|
+
addTypeMismatch(
|
|
3308
|
+
ctx,
|
|
3309
|
+
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
|
|
3310
|
+
constraint.provenance
|
|
3311
|
+
);
|
|
3312
|
+
return;
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
if (registration.applicableTypes === null) {
|
|
3316
|
+
if (registration.isApplicableToType?.(type) === false) {
|
|
3317
|
+
addTypeMismatch(
|
|
3318
|
+
ctx,
|
|
3319
|
+
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
|
|
3320
|
+
constraint.provenance
|
|
3321
|
+
);
|
|
3322
|
+
}
|
|
3323
|
+
return;
|
|
3324
|
+
}
|
|
3325
|
+
if (!registration.applicableTypes.includes(type.kind) || registration.isApplicableToType?.(type) === false) {
|
|
2793
3326
|
addTypeMismatch(
|
|
2794
3327
|
ctx,
|
|
2795
3328
|
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
|
|
@@ -2820,6 +3353,7 @@ function validateConstraints(ctx, name, type, constraints) {
|
|
|
2820
3353
|
checkAllowedMembersContradiction(ctx, name, constraints);
|
|
2821
3354
|
checkConstContradictions(ctx, name, constraints);
|
|
2822
3355
|
checkConstraintBroadening(ctx, name, constraints);
|
|
3356
|
+
checkCustomConstraintSemantics(ctx, name, constraints);
|
|
2823
3357
|
checkTypeApplicability(ctx, name, type, constraints);
|
|
2824
3358
|
}
|
|
2825
3359
|
function validateElement(ctx, element) {
|
|
@@ -2861,7 +3395,10 @@ function validateIR(ir, options) {
|
|
|
2861
3395
|
// src/extensions/registry.ts
|
|
2862
3396
|
function createExtensionRegistry(extensions) {
|
|
2863
3397
|
const typeMap = /* @__PURE__ */ new Map();
|
|
3398
|
+
const typeNameMap = /* @__PURE__ */ new Map();
|
|
2864
3399
|
const constraintMap = /* @__PURE__ */ new Map();
|
|
3400
|
+
const constraintTagMap = /* @__PURE__ */ new Map();
|
|
3401
|
+
const builtinBroadeningMap = /* @__PURE__ */ new Map();
|
|
2865
3402
|
const annotationMap = /* @__PURE__ */ new Map();
|
|
2866
3403
|
for (const ext of extensions) {
|
|
2867
3404
|
if (ext.types !== void 0) {
|
|
@@ -2871,6 +3408,27 @@ function createExtensionRegistry(extensions) {
|
|
|
2871
3408
|
throw new Error(`Duplicate custom type ID: "${qualifiedId}"`);
|
|
2872
3409
|
}
|
|
2873
3410
|
typeMap.set(qualifiedId, type);
|
|
3411
|
+
for (const sourceTypeName of type.tsTypeNames ?? [type.typeName]) {
|
|
3412
|
+
if (typeNameMap.has(sourceTypeName)) {
|
|
3413
|
+
throw new Error(`Duplicate custom type source name: "${sourceTypeName}"`);
|
|
3414
|
+
}
|
|
3415
|
+
typeNameMap.set(sourceTypeName, {
|
|
3416
|
+
extensionId: ext.extensionId,
|
|
3417
|
+
registration: type
|
|
3418
|
+
});
|
|
3419
|
+
}
|
|
3420
|
+
if (type.builtinConstraintBroadenings !== void 0) {
|
|
3421
|
+
for (const broadening of type.builtinConstraintBroadenings) {
|
|
3422
|
+
const key = `${qualifiedId}:${broadening.tagName}`;
|
|
3423
|
+
if (builtinBroadeningMap.has(key)) {
|
|
3424
|
+
throw new Error(`Duplicate built-in constraint broadening: "${key}"`);
|
|
3425
|
+
}
|
|
3426
|
+
builtinBroadeningMap.set(key, {
|
|
3427
|
+
extensionId: ext.extensionId,
|
|
3428
|
+
registration: broadening
|
|
3429
|
+
});
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
2874
3432
|
}
|
|
2875
3433
|
}
|
|
2876
3434
|
if (ext.constraints !== void 0) {
|
|
@@ -2882,6 +3440,17 @@ function createExtensionRegistry(extensions) {
|
|
|
2882
3440
|
constraintMap.set(qualifiedId, constraint);
|
|
2883
3441
|
}
|
|
2884
3442
|
}
|
|
3443
|
+
if (ext.constraintTags !== void 0) {
|
|
3444
|
+
for (const tag of ext.constraintTags) {
|
|
3445
|
+
if (constraintTagMap.has(tag.tagName)) {
|
|
3446
|
+
throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
|
|
3447
|
+
}
|
|
3448
|
+
constraintTagMap.set(tag.tagName, {
|
|
3449
|
+
extensionId: ext.extensionId,
|
|
3450
|
+
registration: tag
|
|
3451
|
+
});
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
2885
3454
|
if (ext.annotations !== void 0) {
|
|
2886
3455
|
for (const annotation of ext.annotations) {
|
|
2887
3456
|
const qualifiedId = `${ext.extensionId}/${annotation.annotationName}`;
|
|
@@ -2895,13 +3464,16 @@ function createExtensionRegistry(extensions) {
|
|
|
2895
3464
|
return {
|
|
2896
3465
|
extensions,
|
|
2897
3466
|
findType: (typeId) => typeMap.get(typeId),
|
|
3467
|
+
findTypeByName: (typeName) => typeNameMap.get(typeName),
|
|
2898
3468
|
findConstraint: (constraintId) => constraintMap.get(constraintId),
|
|
3469
|
+
findConstraintTag: (tagName) => constraintTagMap.get(tagName),
|
|
3470
|
+
findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
|
|
2899
3471
|
findAnnotation: (annotationId) => annotationMap.get(annotationId)
|
|
2900
3472
|
};
|
|
2901
3473
|
}
|
|
2902
3474
|
|
|
2903
3475
|
// src/generators/method-schema.ts
|
|
2904
|
-
var
|
|
3476
|
+
var import_core5 = require("@formspec/core");
|
|
2905
3477
|
function typeToJsonSchema(type, checker) {
|
|
2906
3478
|
const typeRegistry = {};
|
|
2907
3479
|
const visiting = /* @__PURE__ */ new Set();
|
|
@@ -2909,7 +3481,7 @@ function typeToJsonSchema(type, checker) {
|
|
|
2909
3481
|
const fieldProvenance = { surface: "tsdoc", file: "", line: 0, column: 0 };
|
|
2910
3482
|
const ir = {
|
|
2911
3483
|
kind: "form-ir",
|
|
2912
|
-
irVersion:
|
|
3484
|
+
irVersion: import_core5.IR_VERSION,
|
|
2913
3485
|
elements: [
|
|
2914
3486
|
{
|
|
2915
3487
|
kind: "field",
|