@formspec/build 0.1.0-alpha.15 → 0.1.0-alpha.16
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/edge-cases.d.ts +11 -0
- package/dist/__tests__/fixtures/edge-cases.d.ts.map +1 -1
- package/dist/__tests__/fixtures/mixed-authoring-shipping-address.d.ts +30 -0
- package/dist/__tests__/fixtures/mixed-authoring-shipping-address.d.ts.map +1 -0
- package/dist/__tests__/mixed-authoring.test.d.ts +2 -0
- package/dist/__tests__/mixed-authoring.test.d.ts.map +1 -0
- package/dist/__tests__/parity/utils.d.ts +5 -3
- package/dist/__tests__/parity/utils.d.ts.map +1 -1
- package/dist/analyzer/class-analyzer.d.ts +4 -2
- package/dist/analyzer/class-analyzer.d.ts.map +1 -1
- package/dist/analyzer/tsdoc-parser.d.ts +20 -2
- package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
- package/dist/browser.cjs +172 -17
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +172 -17
- package/dist/browser.js.map +1 -1
- package/dist/build.d.ts +39 -1
- package/dist/canonicalize/tsdoc-canonicalizer.d.ts.map +1 -1
- package/dist/cli.cjs +634 -88
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +634 -88
- package/dist/cli.js.map +1 -1
- package/dist/generators/mixed-authoring.d.ts +45 -0
- package/dist/generators/mixed-authoring.d.ts.map +1 -0
- package/dist/index.cjs +622 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +621 -87
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +526 -91
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +526 -91
- package/dist/internals.js.map +1 -1
- package/dist/json-schema/ir-generator.d.ts +3 -2
- package/dist/json-schema/ir-generator.d.ts.map +1 -1
- package/dist/ui-schema/ir-generator.d.ts.map +1 -1
- package/dist/validate/constraint-validator.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/__tests__/jsdoc-constraints.test.d.ts +0 -9
- package/dist/__tests__/jsdoc-constraints.test.d.ts.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -342,6 +342,7 @@ function canonicalizeTSDoc(analysis, source) {
|
|
|
342
342
|
irVersion: IR_VERSION2,
|
|
343
343
|
elements,
|
|
344
344
|
typeRegistry: analysis.typeRegistry,
|
|
345
|
+
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { annotations: analysis.annotations },
|
|
345
346
|
provenance
|
|
346
347
|
};
|
|
347
348
|
}
|
|
@@ -432,6 +433,9 @@ function generateJsonSchemaFromIR(ir, options) {
|
|
|
432
433
|
const ctx = makeContext(options);
|
|
433
434
|
for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
|
|
434
435
|
ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
|
|
436
|
+
if (typeDef.annotations && typeDef.annotations.length > 0) {
|
|
437
|
+
applyAnnotations(ctx.defs[name], typeDef.annotations, ctx);
|
|
438
|
+
}
|
|
435
439
|
}
|
|
436
440
|
const properties = {};
|
|
437
441
|
const required = [];
|
|
@@ -443,6 +447,9 @@ function generateJsonSchemaFromIR(ir, options) {
|
|
|
443
447
|
properties,
|
|
444
448
|
...uniqueRequired.length > 0 && { required: uniqueRequired }
|
|
445
449
|
};
|
|
450
|
+
if (ir.annotations && ir.annotations.length > 0) {
|
|
451
|
+
applyAnnotations(result, ir.annotations, ctx);
|
|
452
|
+
}
|
|
446
453
|
if (Object.keys(ctx.defs).length > 0) {
|
|
447
454
|
result.$defs = ctx.defs;
|
|
448
455
|
}
|
|
@@ -472,22 +479,51 @@ function collectFields(elements, properties, required, ctx) {
|
|
|
472
479
|
}
|
|
473
480
|
function generateFieldSchema(field, ctx) {
|
|
474
481
|
const schema = generateTypeNode(field.type, ctx);
|
|
482
|
+
const itemStringSchema = schema.type === "array" && schema.items?.type === "string" ? schema.items : void 0;
|
|
475
483
|
const directConstraints = [];
|
|
484
|
+
const itemConstraints = [];
|
|
476
485
|
const pathConstraints = [];
|
|
477
486
|
for (const c of field.constraints) {
|
|
478
487
|
if (c.path) {
|
|
479
488
|
pathConstraints.push(c);
|
|
489
|
+
} else if (itemStringSchema !== void 0 && isStringItemConstraint(c)) {
|
|
490
|
+
itemConstraints.push(c);
|
|
480
491
|
} else {
|
|
481
492
|
directConstraints.push(c);
|
|
482
493
|
}
|
|
483
494
|
}
|
|
484
495
|
applyConstraints(schema, directConstraints, ctx);
|
|
485
|
-
|
|
496
|
+
if (itemStringSchema !== void 0) {
|
|
497
|
+
applyConstraints(itemStringSchema, itemConstraints, ctx);
|
|
498
|
+
}
|
|
499
|
+
const rootAnnotations = [];
|
|
500
|
+
const itemAnnotations = [];
|
|
501
|
+
for (const annotation of field.annotations) {
|
|
502
|
+
if (itemStringSchema !== void 0 && annotation.annotationKind === "format") {
|
|
503
|
+
itemAnnotations.push(annotation);
|
|
504
|
+
} else {
|
|
505
|
+
rootAnnotations.push(annotation);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
applyAnnotations(schema, rootAnnotations, ctx);
|
|
509
|
+
if (itemStringSchema !== void 0) {
|
|
510
|
+
applyAnnotations(itemStringSchema, itemAnnotations, ctx);
|
|
511
|
+
}
|
|
486
512
|
if (pathConstraints.length === 0) {
|
|
487
513
|
return schema;
|
|
488
514
|
}
|
|
489
515
|
return applyPathTargetedConstraints(schema, pathConstraints, ctx);
|
|
490
516
|
}
|
|
517
|
+
function isStringItemConstraint(constraint) {
|
|
518
|
+
switch (constraint.constraintKind) {
|
|
519
|
+
case "minLength":
|
|
520
|
+
case "maxLength":
|
|
521
|
+
case "pattern":
|
|
522
|
+
return true;
|
|
523
|
+
default:
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
491
527
|
function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
|
|
492
528
|
if (schema.type === "array" && schema.items) {
|
|
493
529
|
schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx);
|
|
@@ -705,6 +741,9 @@ function applyConstraints(schema, constraints, ctx) {
|
|
|
705
741
|
case "uniqueItems":
|
|
706
742
|
schema.uniqueItems = constraint.value;
|
|
707
743
|
break;
|
|
744
|
+
case "const":
|
|
745
|
+
schema.const = constraint.value;
|
|
746
|
+
break;
|
|
708
747
|
case "allowedMembers":
|
|
709
748
|
break;
|
|
710
749
|
case "custom":
|
|
@@ -729,8 +768,14 @@ function applyAnnotations(schema, annotations, ctx) {
|
|
|
729
768
|
case "defaultValue":
|
|
730
769
|
schema.default = annotation.value;
|
|
731
770
|
break;
|
|
771
|
+
case "format":
|
|
772
|
+
schema.format = annotation.value;
|
|
773
|
+
break;
|
|
732
774
|
case "deprecated":
|
|
733
775
|
schema.deprecated = true;
|
|
776
|
+
if (annotation.message !== void 0 && annotation.message !== "") {
|
|
777
|
+
schema["x-formspec-deprecation-description"] = annotation.message;
|
|
778
|
+
}
|
|
734
779
|
break;
|
|
735
780
|
case "placeholder":
|
|
736
781
|
break;
|
|
@@ -936,25 +981,31 @@ function createShowRule(fieldName, value) {
|
|
|
936
981
|
}
|
|
937
982
|
};
|
|
938
983
|
}
|
|
984
|
+
function flattenConditionSchema(scope, schema) {
|
|
985
|
+
if (schema.allOf === void 0) {
|
|
986
|
+
if (scope === "#") {
|
|
987
|
+
return [schema];
|
|
988
|
+
}
|
|
989
|
+
const fieldName = scope.replace("#/properties/", "");
|
|
990
|
+
return [
|
|
991
|
+
{
|
|
992
|
+
properties: {
|
|
993
|
+
[fieldName]: schema
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
];
|
|
997
|
+
}
|
|
998
|
+
return schema.allOf.flatMap((member) => flattenConditionSchema(scope, member));
|
|
999
|
+
}
|
|
939
1000
|
function combineRules(parentRule, childRule) {
|
|
940
|
-
const parentCondition = parentRule.condition;
|
|
941
|
-
const childCondition = childRule.condition;
|
|
942
1001
|
return {
|
|
943
1002
|
effect: "SHOW",
|
|
944
1003
|
condition: {
|
|
945
1004
|
scope: "#",
|
|
946
1005
|
schema: {
|
|
947
1006
|
allOf: [
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
[parentCondition.scope.replace("#/properties/", "")]: parentCondition.schema
|
|
951
|
-
}
|
|
952
|
-
},
|
|
953
|
-
{
|
|
954
|
-
properties: {
|
|
955
|
-
[childCondition.scope.replace("#/properties/", "")]: childCondition.schema
|
|
956
|
-
}
|
|
957
|
-
}
|
|
1007
|
+
...flattenConditionSchema(parentRule.condition.scope, parentRule.condition.schema),
|
|
1008
|
+
...flattenConditionSchema(childRule.condition.scope, childRule.condition.schema)
|
|
958
1009
|
]
|
|
959
1010
|
}
|
|
960
1011
|
}
|
|
@@ -962,10 +1013,14 @@ function combineRules(parentRule, childRule) {
|
|
|
962
1013
|
}
|
|
963
1014
|
function fieldNodeToControl(field, parentRule) {
|
|
964
1015
|
const displayNameAnnotation = field.annotations.find((a) => a.annotationKind === "displayName");
|
|
1016
|
+
const placeholderAnnotation = field.annotations.find((a) => a.annotationKind === "placeholder");
|
|
965
1017
|
const control = {
|
|
966
1018
|
type: "Control",
|
|
967
1019
|
scope: fieldToScope(field.name),
|
|
968
1020
|
...displayNameAnnotation !== void 0 && { label: displayNameAnnotation.value },
|
|
1021
|
+
...placeholderAnnotation !== void 0 && {
|
|
1022
|
+
options: { placeholder: placeholderAnnotation.value }
|
|
1023
|
+
},
|
|
969
1024
|
...parentRule !== void 0 && { rule: parentRule }
|
|
970
1025
|
};
|
|
971
1026
|
return control;
|
|
@@ -1287,7 +1342,7 @@ function createFormSpecTSDocConfig() {
|
|
|
1287
1342
|
})
|
|
1288
1343
|
);
|
|
1289
1344
|
}
|
|
1290
|
-
for (const tagName of ["displayName", "description"]) {
|
|
1345
|
+
for (const tagName of ["displayName", "description", "format", "placeholder"]) {
|
|
1291
1346
|
config.addTagDefinition(
|
|
1292
1347
|
new TSDocTagDefinition({
|
|
1293
1348
|
tagName: "@" + tagName,
|
|
@@ -1305,6 +1360,12 @@ function getParser() {
|
|
|
1305
1360
|
function parseTSDocTags(node, file = "") {
|
|
1306
1361
|
const constraints = [];
|
|
1307
1362
|
const annotations = [];
|
|
1363
|
+
let displayName;
|
|
1364
|
+
let description;
|
|
1365
|
+
let placeholder;
|
|
1366
|
+
let displayNameProvenance;
|
|
1367
|
+
let descriptionProvenance;
|
|
1368
|
+
let placeholderProvenance;
|
|
1308
1369
|
const sourceFile = node.getSourceFile();
|
|
1309
1370
|
const sourceText = sourceFile.getFullText();
|
|
1310
1371
|
const commentRanges = ts2.getLeadingCommentRanges(sourceText, node.getFullStart());
|
|
@@ -1324,30 +1385,37 @@ function parseTSDocTags(node, file = "") {
|
|
|
1324
1385
|
const docComment = parserContext.docComment;
|
|
1325
1386
|
for (const block of docComment.customBlocks) {
|
|
1326
1387
|
const tagName = normalizeConstraintTagName(block.blockTag.tagName.substring(1));
|
|
1327
|
-
if (tagName === "displayName" || tagName === "description") {
|
|
1388
|
+
if (tagName === "displayName" || tagName === "description" || tagName === "format" || tagName === "placeholder") {
|
|
1328
1389
|
const text2 = extractBlockText(block).trim();
|
|
1329
1390
|
if (text2 === "") continue;
|
|
1330
1391
|
const provenance2 = provenanceForComment(range, sourceFile, file, tagName);
|
|
1331
1392
|
if (tagName === "displayName") {
|
|
1393
|
+
if (!isMemberTargetDisplayName(text2) && displayName === void 0) {
|
|
1394
|
+
displayName = text2;
|
|
1395
|
+
displayNameProvenance = provenance2;
|
|
1396
|
+
}
|
|
1397
|
+
} else if (tagName === "format") {
|
|
1332
1398
|
annotations.push({
|
|
1333
1399
|
kind: "annotation",
|
|
1334
|
-
annotationKind: "
|
|
1400
|
+
annotationKind: "format",
|
|
1335
1401
|
value: text2,
|
|
1336
1402
|
provenance: provenance2
|
|
1337
1403
|
});
|
|
1338
1404
|
} else {
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1405
|
+
if (tagName === "description" && description === void 0) {
|
|
1406
|
+
description = text2;
|
|
1407
|
+
descriptionProvenance = provenance2;
|
|
1408
|
+
} else if (tagName === "placeholder" && placeholder === void 0) {
|
|
1409
|
+
placeholder = text2;
|
|
1410
|
+
placeholderProvenance = provenance2;
|
|
1411
|
+
}
|
|
1345
1412
|
}
|
|
1346
1413
|
continue;
|
|
1347
1414
|
}
|
|
1348
1415
|
if (TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
|
|
1349
1416
|
const text = extractBlockText(block).trim();
|
|
1350
|
-
|
|
1417
|
+
const expectedType = isBuiltinConstraintName(tagName) ? BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
|
|
1418
|
+
if (text === "" && expectedType !== "boolean") continue;
|
|
1351
1419
|
const provenance = provenanceForComment(range, sourceFile, file, tagName);
|
|
1352
1420
|
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
1353
1421
|
if (constraintNode) {
|
|
@@ -1355,14 +1423,47 @@ function parseTSDocTags(node, file = "") {
|
|
|
1355
1423
|
}
|
|
1356
1424
|
}
|
|
1357
1425
|
if (docComment.deprecatedBlock !== void 0) {
|
|
1426
|
+
const message = extractBlockText(docComment.deprecatedBlock).trim();
|
|
1358
1427
|
annotations.push({
|
|
1359
1428
|
kind: "annotation",
|
|
1360
1429
|
annotationKind: "deprecated",
|
|
1430
|
+
...message !== "" && { message },
|
|
1361
1431
|
provenance: provenanceForComment(range, sourceFile, file, "deprecated")
|
|
1362
1432
|
});
|
|
1363
1433
|
}
|
|
1434
|
+
if (description === void 0 && docComment.remarksBlock !== void 0) {
|
|
1435
|
+
const remarks = extractBlockText(docComment.remarksBlock).trim();
|
|
1436
|
+
if (remarks !== "") {
|
|
1437
|
+
description = remarks;
|
|
1438
|
+
descriptionProvenance = provenanceForComment(range, sourceFile, file, "remarks");
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1364
1441
|
}
|
|
1365
1442
|
}
|
|
1443
|
+
if (displayName !== void 0 && displayNameProvenance !== void 0) {
|
|
1444
|
+
annotations.push({
|
|
1445
|
+
kind: "annotation",
|
|
1446
|
+
annotationKind: "displayName",
|
|
1447
|
+
value: displayName,
|
|
1448
|
+
provenance: displayNameProvenance
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
if (description !== void 0 && descriptionProvenance !== void 0) {
|
|
1452
|
+
annotations.push({
|
|
1453
|
+
kind: "annotation",
|
|
1454
|
+
annotationKind: "description",
|
|
1455
|
+
value: description,
|
|
1456
|
+
provenance: descriptionProvenance
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
if (placeholder !== void 0 && placeholderProvenance !== void 0) {
|
|
1460
|
+
annotations.push({
|
|
1461
|
+
kind: "annotation",
|
|
1462
|
+
annotationKind: "placeholder",
|
|
1463
|
+
value: placeholder,
|
|
1464
|
+
provenance: placeholderProvenance
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1366
1467
|
const jsDocTagsAll = ts2.getJSDocTags(node);
|
|
1367
1468
|
for (const tag of jsDocTagsAll) {
|
|
1368
1469
|
const tagName = normalizeConstraintTagName(tag.tagName.text);
|
|
@@ -1371,6 +1472,11 @@ function parseTSDocTags(node, file = "") {
|
|
|
1371
1472
|
if (commentText === void 0 || commentText.trim() === "") continue;
|
|
1372
1473
|
const text = commentText.trim();
|
|
1373
1474
|
const provenance = provenanceForJSDocTag(tag, file);
|
|
1475
|
+
if (tagName === "defaultValue") {
|
|
1476
|
+
const defaultValueNode = parseDefaultValueValue(text, provenance);
|
|
1477
|
+
annotations.push(defaultValueNode);
|
|
1478
|
+
continue;
|
|
1479
|
+
}
|
|
1374
1480
|
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
1375
1481
|
if (constraintNode) {
|
|
1376
1482
|
constraints.push(constraintNode);
|
|
@@ -1378,6 +1484,28 @@ function parseTSDocTags(node, file = "") {
|
|
|
1378
1484
|
}
|
|
1379
1485
|
return { constraints, annotations };
|
|
1380
1486
|
}
|
|
1487
|
+
function extractDisplayNameMetadata(node) {
|
|
1488
|
+
let displayName;
|
|
1489
|
+
const memberDisplayNames = /* @__PURE__ */ new Map();
|
|
1490
|
+
for (const tag of ts2.getJSDocTags(node)) {
|
|
1491
|
+
const tagName = normalizeConstraintTagName(tag.tagName.text);
|
|
1492
|
+
if (tagName !== "displayName") continue;
|
|
1493
|
+
const commentText = getTagCommentText(tag);
|
|
1494
|
+
if (commentText === void 0) continue;
|
|
1495
|
+
const text = commentText.trim();
|
|
1496
|
+
if (text === "") continue;
|
|
1497
|
+
const memberTarget = parseMemberTargetDisplayName(text);
|
|
1498
|
+
if (memberTarget) {
|
|
1499
|
+
memberDisplayNames.set(memberTarget.target, memberTarget.label);
|
|
1500
|
+
continue;
|
|
1501
|
+
}
|
|
1502
|
+
displayName ??= text;
|
|
1503
|
+
}
|
|
1504
|
+
return {
|
|
1505
|
+
...displayName !== void 0 && { displayName },
|
|
1506
|
+
memberDisplayNames
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1381
1509
|
function extractPathTarget(text) {
|
|
1382
1510
|
const trimmed = text.trimStart();
|
|
1383
1511
|
const match = /^:([a-zA-Z_]\w*)\s+([\s\S]*)$/.exec(trimmed);
|
|
@@ -1440,7 +1568,45 @@ function parseConstraintValue(tagName, text, provenance) {
|
|
|
1440
1568
|
}
|
|
1441
1569
|
return null;
|
|
1442
1570
|
}
|
|
1571
|
+
if (expectedType === "boolean") {
|
|
1572
|
+
const trimmed = effectiveText.trim();
|
|
1573
|
+
if (trimmed !== "" && trimmed !== "true") {
|
|
1574
|
+
return null;
|
|
1575
|
+
}
|
|
1576
|
+
if (tagName === "uniqueItems") {
|
|
1577
|
+
return {
|
|
1578
|
+
kind: "constraint",
|
|
1579
|
+
constraintKind: "uniqueItems",
|
|
1580
|
+
value: true,
|
|
1581
|
+
...path4 && { path: path4 },
|
|
1582
|
+
provenance
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
return null;
|
|
1586
|
+
}
|
|
1443
1587
|
if (expectedType === "json") {
|
|
1588
|
+
if (tagName === "const") {
|
|
1589
|
+
const trimmedText = effectiveText.trim();
|
|
1590
|
+
if (trimmedText === "") return null;
|
|
1591
|
+
try {
|
|
1592
|
+
const parsed2 = JSON.parse(trimmedText);
|
|
1593
|
+
return {
|
|
1594
|
+
kind: "constraint",
|
|
1595
|
+
constraintKind: "const",
|
|
1596
|
+
value: parsed2,
|
|
1597
|
+
...path4 && { path: path4 },
|
|
1598
|
+
provenance
|
|
1599
|
+
};
|
|
1600
|
+
} catch {
|
|
1601
|
+
return {
|
|
1602
|
+
kind: "constraint",
|
|
1603
|
+
constraintKind: "const",
|
|
1604
|
+
value: trimmedText,
|
|
1605
|
+
...path4 && { path: path4 },
|
|
1606
|
+
provenance
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1444
1610
|
const parsed = tryParseJson(effectiveText);
|
|
1445
1611
|
if (!Array.isArray(parsed)) {
|
|
1446
1612
|
return null;
|
|
@@ -1472,6 +1638,34 @@ function parseConstraintValue(tagName, text, provenance) {
|
|
|
1472
1638
|
provenance
|
|
1473
1639
|
};
|
|
1474
1640
|
}
|
|
1641
|
+
function parseDefaultValueValue(text, provenance) {
|
|
1642
|
+
const trimmed = text.trim();
|
|
1643
|
+
let value;
|
|
1644
|
+
if (trimmed === "null") {
|
|
1645
|
+
value = null;
|
|
1646
|
+
} else if (trimmed === "true") {
|
|
1647
|
+
value = true;
|
|
1648
|
+
} else if (trimmed === "false") {
|
|
1649
|
+
value = false;
|
|
1650
|
+
} else {
|
|
1651
|
+
const parsed = tryParseJson(trimmed);
|
|
1652
|
+
value = parsed !== null ? parsed : trimmed;
|
|
1653
|
+
}
|
|
1654
|
+
return {
|
|
1655
|
+
kind: "annotation",
|
|
1656
|
+
annotationKind: "defaultValue",
|
|
1657
|
+
value,
|
|
1658
|
+
provenance
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
function isMemberTargetDisplayName(text) {
|
|
1662
|
+
return parseMemberTargetDisplayName(text) !== null;
|
|
1663
|
+
}
|
|
1664
|
+
function parseMemberTargetDisplayName(text) {
|
|
1665
|
+
const match = /^:([^\s]+)\s+([\s\S]+)$/.exec(text);
|
|
1666
|
+
if (!match?.[1] || !match[2]) return null;
|
|
1667
|
+
return { target: match[1], label: match[2].trim() };
|
|
1668
|
+
}
|
|
1475
1669
|
function provenanceForComment(range, sourceFile, file, tagName) {
|
|
1476
1670
|
const { line, character } = sourceFile.getLineAndCharacterOfPosition(range.pos);
|
|
1477
1671
|
return {
|
|
@@ -1520,7 +1714,7 @@ var init_tsdoc_parser = __esm({
|
|
|
1520
1714
|
minItems: "minItems",
|
|
1521
1715
|
maxItems: "maxItems"
|
|
1522
1716
|
};
|
|
1523
|
-
TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions"]);
|
|
1717
|
+
TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions", "defaultValue"]);
|
|
1524
1718
|
}
|
|
1525
1719
|
});
|
|
1526
1720
|
|
|
@@ -1587,6 +1781,7 @@ function analyzeClassToIR(classDecl, checker, file = "") {
|
|
|
1587
1781
|
const fields = [];
|
|
1588
1782
|
const fieldLayouts = [];
|
|
1589
1783
|
const typeRegistry = {};
|
|
1784
|
+
const annotations = extractJSDocAnnotationNodes(classDecl, file);
|
|
1590
1785
|
const visiting = /* @__PURE__ */ new Set();
|
|
1591
1786
|
const instanceMethods = [];
|
|
1592
1787
|
const staticMethods = [];
|
|
@@ -1609,12 +1804,21 @@ function analyzeClassToIR(classDecl, checker, file = "") {
|
|
|
1609
1804
|
}
|
|
1610
1805
|
}
|
|
1611
1806
|
}
|
|
1612
|
-
return {
|
|
1807
|
+
return {
|
|
1808
|
+
name,
|
|
1809
|
+
fields,
|
|
1810
|
+
fieldLayouts,
|
|
1811
|
+
typeRegistry,
|
|
1812
|
+
...annotations.length > 0 && { annotations },
|
|
1813
|
+
instanceMethods,
|
|
1814
|
+
staticMethods
|
|
1815
|
+
};
|
|
1613
1816
|
}
|
|
1614
1817
|
function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
1615
1818
|
const name = interfaceDecl.name.text;
|
|
1616
1819
|
const fields = [];
|
|
1617
1820
|
const typeRegistry = {};
|
|
1821
|
+
const annotations = extractJSDocAnnotationNodes(interfaceDecl, file);
|
|
1618
1822
|
const visiting = /* @__PURE__ */ new Set();
|
|
1619
1823
|
for (const member of interfaceDecl.members) {
|
|
1620
1824
|
if (ts4.isPropertySignature(member)) {
|
|
@@ -1625,7 +1829,15 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
|
1625
1829
|
}
|
|
1626
1830
|
}
|
|
1627
1831
|
const fieldLayouts = fields.map(() => ({}));
|
|
1628
|
-
return {
|
|
1832
|
+
return {
|
|
1833
|
+
name,
|
|
1834
|
+
fields,
|
|
1835
|
+
fieldLayouts,
|
|
1836
|
+
typeRegistry,
|
|
1837
|
+
...annotations.length > 0 && { annotations },
|
|
1838
|
+
instanceMethods: [],
|
|
1839
|
+
staticMethods: []
|
|
1840
|
+
};
|
|
1629
1841
|
}
|
|
1630
1842
|
function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
1631
1843
|
if (!ts4.isTypeLiteralNode(typeAlias.type)) {
|
|
@@ -1640,6 +1852,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
|
1640
1852
|
const name = typeAlias.name.text;
|
|
1641
1853
|
const fields = [];
|
|
1642
1854
|
const typeRegistry = {};
|
|
1855
|
+
const annotations = extractJSDocAnnotationNodes(typeAlias, file);
|
|
1643
1856
|
const visiting = /* @__PURE__ */ new Set();
|
|
1644
1857
|
for (const member of typeAlias.type.members) {
|
|
1645
1858
|
if (ts4.isPropertySignature(member)) {
|
|
@@ -1656,6 +1869,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
|
1656
1869
|
fields,
|
|
1657
1870
|
fieldLayouts: fields.map(() => ({})),
|
|
1658
1871
|
typeRegistry,
|
|
1872
|
+
...annotations.length > 0 && { annotations },
|
|
1659
1873
|
instanceMethods: [],
|
|
1660
1874
|
staticMethods: []
|
|
1661
1875
|
}
|
|
@@ -1669,7 +1883,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
|
1669
1883
|
const tsType = checker.getTypeAtLocation(prop);
|
|
1670
1884
|
const optional = prop.questionToken !== void 0;
|
|
1671
1885
|
const provenance = provenanceForNode(prop, file);
|
|
1672
|
-
let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
|
|
1886
|
+
let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting, prop);
|
|
1673
1887
|
const constraints = [];
|
|
1674
1888
|
if (prop.type) {
|
|
1675
1889
|
constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
|
|
@@ -1678,7 +1892,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
|
1678
1892
|
let annotations = [];
|
|
1679
1893
|
annotations.push(...extractJSDocAnnotationNodes(prop, file));
|
|
1680
1894
|
const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
|
|
1681
|
-
if (defaultAnnotation) {
|
|
1895
|
+
if (defaultAnnotation && !annotations.some((a) => a.annotationKind === "defaultValue")) {
|
|
1682
1896
|
annotations.push(defaultAnnotation);
|
|
1683
1897
|
}
|
|
1684
1898
|
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
@@ -1700,7 +1914,7 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
1700
1914
|
const tsType = checker.getTypeAtLocation(prop);
|
|
1701
1915
|
const optional = prop.questionToken !== void 0;
|
|
1702
1916
|
const provenance = provenanceForNode(prop, file);
|
|
1703
|
-
let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
|
|
1917
|
+
let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting, prop);
|
|
1704
1918
|
const constraints = [];
|
|
1705
1919
|
if (prop.type) {
|
|
1706
1920
|
constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
|
|
@@ -1781,7 +1995,7 @@ function parseEnumMemberDisplayName(value) {
|
|
|
1781
1995
|
if (label === "") return null;
|
|
1782
1996
|
return { value: match[1], label };
|
|
1783
1997
|
}
|
|
1784
|
-
function resolveTypeNode(type, checker, file, typeRegistry, visiting) {
|
|
1998
|
+
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode) {
|
|
1785
1999
|
if (type.flags & ts4.TypeFlags.String) {
|
|
1786
2000
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1787
2001
|
}
|
|
@@ -1810,7 +2024,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting) {
|
|
|
1810
2024
|
};
|
|
1811
2025
|
}
|
|
1812
2026
|
if (type.isUnion()) {
|
|
1813
|
-
return resolveUnionType(type, checker, file, typeRegistry, visiting);
|
|
2027
|
+
return resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode);
|
|
1814
2028
|
}
|
|
1815
2029
|
if (checker.isArrayType(type)) {
|
|
1816
2030
|
return resolveArrayType(type, checker, file, typeRegistry, visiting);
|
|
@@ -1820,70 +2034,102 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting) {
|
|
|
1820
2034
|
}
|
|
1821
2035
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1822
2036
|
}
|
|
1823
|
-
function resolveUnionType(type, checker, file, typeRegistry, visiting) {
|
|
2037
|
+
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode) {
|
|
2038
|
+
const typeName = getNamedTypeName(type);
|
|
2039
|
+
const namedDecl = getNamedTypeDeclaration(type);
|
|
2040
|
+
if (typeName && typeName in typeRegistry) {
|
|
2041
|
+
return { kind: "reference", name: typeName, typeArguments: [] };
|
|
2042
|
+
}
|
|
1824
2043
|
const allTypes = type.types;
|
|
1825
2044
|
const nonNullTypes = allTypes.filter(
|
|
1826
2045
|
(t) => !(t.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
|
|
1827
2046
|
);
|
|
1828
2047
|
const hasNull = allTypes.some((t) => t.flags & ts4.TypeFlags.Null);
|
|
2048
|
+
const memberDisplayNames = /* @__PURE__ */ new Map();
|
|
2049
|
+
if (namedDecl) {
|
|
2050
|
+
for (const [value, label] of extractDisplayNameMetadata(namedDecl).memberDisplayNames) {
|
|
2051
|
+
memberDisplayNames.set(value, label);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
if (sourceNode) {
|
|
2055
|
+
for (const [value, label] of extractDisplayNameMetadata(sourceNode).memberDisplayNames) {
|
|
2056
|
+
memberDisplayNames.set(value, label);
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
const registerNamed = (result) => {
|
|
2060
|
+
if (!typeName) {
|
|
2061
|
+
return result;
|
|
2062
|
+
}
|
|
2063
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2064
|
+
typeRegistry[typeName] = {
|
|
2065
|
+
name: typeName,
|
|
2066
|
+
type: result,
|
|
2067
|
+
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2068
|
+
provenance: provenanceForDeclaration(namedDecl ?? sourceNode, file)
|
|
2069
|
+
};
|
|
2070
|
+
return { kind: "reference", name: typeName, typeArguments: [] };
|
|
2071
|
+
};
|
|
2072
|
+
const applyMemberLabels = (members2) => members2.map((value) => {
|
|
2073
|
+
const displayName = memberDisplayNames.get(String(value));
|
|
2074
|
+
return displayName !== void 0 ? { value, displayName } : { value };
|
|
2075
|
+
});
|
|
1829
2076
|
const isBooleanUnion2 = nonNullTypes.length === 2 && nonNullTypes.every((t) => t.flags & ts4.TypeFlags.BooleanLiteral);
|
|
1830
2077
|
if (isBooleanUnion2) {
|
|
1831
2078
|
const boolNode = { kind: "primitive", primitiveKind: "boolean" };
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
}
|
|
1838
|
-
return boolNode;
|
|
2079
|
+
const result = hasNull ? {
|
|
2080
|
+
kind: "union",
|
|
2081
|
+
members: [boolNode, { kind: "primitive", primitiveKind: "null" }]
|
|
2082
|
+
} : boolNode;
|
|
2083
|
+
return registerNamed(result);
|
|
1839
2084
|
}
|
|
1840
2085
|
const allStringLiterals = nonNullTypes.every((t) => t.isStringLiteral());
|
|
1841
2086
|
if (allStringLiterals && nonNullTypes.length > 0) {
|
|
1842
2087
|
const stringTypes = nonNullTypes.filter((t) => t.isStringLiteral());
|
|
1843
2088
|
const enumNode = {
|
|
1844
2089
|
kind: "enum",
|
|
1845
|
-
members: stringTypes.map((t) =>
|
|
2090
|
+
members: applyMemberLabels(stringTypes.map((t) => t.value))
|
|
1846
2091
|
};
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
}
|
|
1853
|
-
return enumNode;
|
|
2092
|
+
const result = hasNull ? {
|
|
2093
|
+
kind: "union",
|
|
2094
|
+
members: [enumNode, { kind: "primitive", primitiveKind: "null" }]
|
|
2095
|
+
} : enumNode;
|
|
2096
|
+
return registerNamed(result);
|
|
1854
2097
|
}
|
|
1855
2098
|
const allNumberLiterals = nonNullTypes.every((t) => t.isNumberLiteral());
|
|
1856
2099
|
if (allNumberLiterals && nonNullTypes.length > 0) {
|
|
1857
2100
|
const numberTypes = nonNullTypes.filter((t) => t.isNumberLiteral());
|
|
1858
2101
|
const enumNode = {
|
|
1859
2102
|
kind: "enum",
|
|
1860
|
-
members: numberTypes.map((t) =>
|
|
2103
|
+
members: applyMemberLabels(numberTypes.map((t) => t.value))
|
|
1861
2104
|
};
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
}
|
|
1868
|
-
return enumNode;
|
|
2105
|
+
const result = hasNull ? {
|
|
2106
|
+
kind: "union",
|
|
2107
|
+
members: [enumNode, { kind: "primitive", primitiveKind: "null" }]
|
|
2108
|
+
} : enumNode;
|
|
2109
|
+
return registerNamed(result);
|
|
1869
2110
|
}
|
|
1870
2111
|
if (nonNullTypes.length === 1 && nonNullTypes[0]) {
|
|
1871
|
-
const inner = resolveTypeNode(
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
2112
|
+
const inner = resolveTypeNode(
|
|
2113
|
+
nonNullTypes[0],
|
|
2114
|
+
checker,
|
|
2115
|
+
file,
|
|
2116
|
+
typeRegistry,
|
|
2117
|
+
visiting,
|
|
2118
|
+
sourceNode
|
|
2119
|
+
);
|
|
2120
|
+
const result = hasNull ? {
|
|
2121
|
+
kind: "union",
|
|
2122
|
+
members: [inner, { kind: "primitive", primitiveKind: "null" }]
|
|
2123
|
+
} : inner;
|
|
2124
|
+
return registerNamed(result);
|
|
1879
2125
|
}
|
|
1880
2126
|
const members = nonNullTypes.map(
|
|
1881
|
-
(t) => resolveTypeNode(t, checker, file, typeRegistry, visiting)
|
|
2127
|
+
(t) => resolveTypeNode(t, checker, file, typeRegistry, visiting, sourceNode)
|
|
1882
2128
|
);
|
|
1883
2129
|
if (hasNull) {
|
|
1884
2130
|
members.push({ kind: "primitive", primitiveKind: "null" });
|
|
1885
2131
|
}
|
|
1886
|
-
return { kind: "union", members };
|
|
2132
|
+
return registerNamed({ kind: "union", members });
|
|
1887
2133
|
}
|
|
1888
2134
|
function resolveArrayType(type, checker, file, typeRegistry, visiting) {
|
|
1889
2135
|
const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
|
|
@@ -1899,30 +2145,84 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
|
|
|
1899
2145
|
if (!indexInfo) {
|
|
1900
2146
|
return null;
|
|
1901
2147
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
2148
|
+
const valueType = resolveTypeNode(indexInfo.type, checker, file, typeRegistry, visiting);
|
|
2149
|
+
return { kind: "record", valueType };
|
|
2150
|
+
}
|
|
2151
|
+
function typeNodeContainsReference(type, targetName) {
|
|
2152
|
+
switch (type.kind) {
|
|
2153
|
+
case "reference":
|
|
2154
|
+
return type.name === targetName;
|
|
2155
|
+
case "array":
|
|
2156
|
+
return typeNodeContainsReference(type.items, targetName);
|
|
2157
|
+
case "record":
|
|
2158
|
+
return typeNodeContainsReference(type.valueType, targetName);
|
|
2159
|
+
case "union":
|
|
2160
|
+
return type.members.some((member) => typeNodeContainsReference(member, targetName));
|
|
2161
|
+
case "object":
|
|
2162
|
+
return type.properties.some(
|
|
2163
|
+
(property) => typeNodeContainsReference(property.type, targetName)
|
|
2164
|
+
);
|
|
2165
|
+
case "primitive":
|
|
2166
|
+
case "enum":
|
|
2167
|
+
case "dynamic":
|
|
2168
|
+
case "custom":
|
|
2169
|
+
return false;
|
|
2170
|
+
default: {
|
|
2171
|
+
const _exhaustive = type;
|
|
2172
|
+
return _exhaustive;
|
|
2173
|
+
}
|
|
1911
2174
|
}
|
|
1912
2175
|
}
|
|
1913
2176
|
function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
1914
|
-
const
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
2177
|
+
const typeName = getNamedTypeName(type);
|
|
2178
|
+
const namedTypeName = typeName ?? void 0;
|
|
2179
|
+
const namedDecl = getNamedTypeDeclaration(type);
|
|
2180
|
+
const shouldRegisterNamedType = namedTypeName !== void 0 && !(namedTypeName === "Record" && namedDecl?.getSourceFile().fileName !== file);
|
|
2181
|
+
const clearNamedTypeRegistration = () => {
|
|
2182
|
+
if (namedTypeName === void 0 || !shouldRegisterNamedType) {
|
|
2183
|
+
return;
|
|
2184
|
+
}
|
|
2185
|
+
Reflect.deleteProperty(typeRegistry, namedTypeName);
|
|
2186
|
+
};
|
|
1918
2187
|
if (visiting.has(type)) {
|
|
2188
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
2189
|
+
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
2190
|
+
}
|
|
1919
2191
|
return { kind: "object", properties: [], additionalProperties: false };
|
|
1920
2192
|
}
|
|
2193
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType && !typeRegistry[namedTypeName]) {
|
|
2194
|
+
typeRegistry[namedTypeName] = {
|
|
2195
|
+
name: namedTypeName,
|
|
2196
|
+
type: RESOLVING_TYPE_PLACEHOLDER,
|
|
2197
|
+
provenance: provenanceForDeclaration(namedDecl, file)
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
1921
2200
|
visiting.add(type);
|
|
1922
|
-
|
|
1923
|
-
|
|
2201
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType && typeRegistry[namedTypeName]?.type !== void 0) {
|
|
2202
|
+
if (typeRegistry[namedTypeName].type !== RESOLVING_TYPE_PLACEHOLDER) {
|
|
2203
|
+
visiting.delete(type);
|
|
2204
|
+
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
const recordNode = tryResolveRecordType(type, checker, file, typeRegistry, visiting);
|
|
2208
|
+
if (recordNode) {
|
|
1924
2209
|
visiting.delete(type);
|
|
1925
|
-
|
|
2210
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
2211
|
+
const isRecursiveRecord = typeNodeContainsReference(recordNode.valueType, namedTypeName);
|
|
2212
|
+
if (!isRecursiveRecord) {
|
|
2213
|
+
clearNamedTypeRegistration();
|
|
2214
|
+
return recordNode;
|
|
2215
|
+
}
|
|
2216
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2217
|
+
typeRegistry[namedTypeName] = {
|
|
2218
|
+
name: namedTypeName,
|
|
2219
|
+
type: recordNode,
|
|
2220
|
+
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2221
|
+
provenance: provenanceForDeclaration(namedDecl, file)
|
|
2222
|
+
};
|
|
2223
|
+
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
2224
|
+
}
|
|
2225
|
+
return recordNode;
|
|
1926
2226
|
}
|
|
1927
2227
|
const properties = [];
|
|
1928
2228
|
const fieldInfoMap = getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting);
|
|
@@ -1931,7 +2231,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
1931
2231
|
if (!declaration) continue;
|
|
1932
2232
|
const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
|
|
1933
2233
|
const optional = !!(prop.flags & ts4.SymbolFlags.Optional);
|
|
1934
|
-
const propTypeNode = resolveTypeNode(
|
|
2234
|
+
const propTypeNode = resolveTypeNode(
|
|
2235
|
+
propType,
|
|
2236
|
+
checker,
|
|
2237
|
+
file,
|
|
2238
|
+
typeRegistry,
|
|
2239
|
+
visiting,
|
|
2240
|
+
declaration
|
|
2241
|
+
);
|
|
1935
2242
|
const fieldNodeInfo = fieldInfoMap?.get(prop.name);
|
|
1936
2243
|
properties.push({
|
|
1937
2244
|
name: prop.name,
|
|
@@ -1948,13 +2255,15 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
1948
2255
|
properties,
|
|
1949
2256
|
additionalProperties: true
|
|
1950
2257
|
};
|
|
1951
|
-
if (
|
|
1952
|
-
|
|
1953
|
-
|
|
2258
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
2259
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2260
|
+
typeRegistry[namedTypeName] = {
|
|
2261
|
+
name: namedTypeName,
|
|
1954
2262
|
type: objectNode,
|
|
1955
|
-
|
|
2263
|
+
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2264
|
+
provenance: provenanceForDeclaration(namedDecl, file)
|
|
1956
2265
|
};
|
|
1957
|
-
return { kind: "reference", name:
|
|
2266
|
+
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
1958
2267
|
}
|
|
1959
2268
|
return objectNode;
|
|
1960
2269
|
}
|
|
@@ -2045,6 +2354,12 @@ function provenanceForNode(node, file) {
|
|
|
2045
2354
|
function provenanceForFile(file) {
|
|
2046
2355
|
return { surface: "tsdoc", file, line: 0, column: 0 };
|
|
2047
2356
|
}
|
|
2357
|
+
function provenanceForDeclaration(node, file) {
|
|
2358
|
+
if (!node) {
|
|
2359
|
+
return provenanceForFile(file);
|
|
2360
|
+
}
|
|
2361
|
+
return provenanceForNode(node, file);
|
|
2362
|
+
}
|
|
2048
2363
|
function getNamedTypeName(type) {
|
|
2049
2364
|
const symbol = type.getSymbol();
|
|
2050
2365
|
if (symbol?.declarations) {
|
|
@@ -2063,6 +2378,20 @@ function getNamedTypeName(type) {
|
|
|
2063
2378
|
}
|
|
2064
2379
|
return null;
|
|
2065
2380
|
}
|
|
2381
|
+
function getNamedTypeDeclaration(type) {
|
|
2382
|
+
const symbol = type.getSymbol();
|
|
2383
|
+
if (symbol?.declarations) {
|
|
2384
|
+
const decl = symbol.declarations[0];
|
|
2385
|
+
if (decl && (ts4.isClassDeclaration(decl) || ts4.isInterfaceDeclaration(decl) || ts4.isTypeAliasDeclaration(decl))) {
|
|
2386
|
+
return decl;
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
const aliasSymbol = type.aliasSymbol;
|
|
2390
|
+
if (aliasSymbol?.declarations) {
|
|
2391
|
+
return aliasSymbol.declarations.find(ts4.isTypeAliasDeclaration);
|
|
2392
|
+
}
|
|
2393
|
+
return void 0;
|
|
2394
|
+
}
|
|
2066
2395
|
function analyzeMethod(method, checker) {
|
|
2067
2396
|
if (!ts4.isIdentifier(method.name)) {
|
|
2068
2397
|
return null;
|
|
@@ -2103,11 +2432,17 @@ function detectFormSpecReference(typeNode) {
|
|
|
2103
2432
|
}
|
|
2104
2433
|
return null;
|
|
2105
2434
|
}
|
|
2106
|
-
var MAX_ALIAS_CHAIN_DEPTH;
|
|
2435
|
+
var RESOLVING_TYPE_PLACEHOLDER, MAX_ALIAS_CHAIN_DEPTH;
|
|
2107
2436
|
var init_class_analyzer = __esm({
|
|
2108
2437
|
"src/analyzer/class-analyzer.ts"() {
|
|
2109
2438
|
"use strict";
|
|
2110
2439
|
init_jsdoc_constraints();
|
|
2440
|
+
init_tsdoc_parser();
|
|
2441
|
+
RESOLVING_TYPE_PLACEHOLDER = {
|
|
2442
|
+
kind: "object",
|
|
2443
|
+
properties: [],
|
|
2444
|
+
additionalProperties: true
|
|
2445
|
+
};
|
|
2111
2446
|
MAX_ALIAS_CHAIN_DEPTH = 8;
|
|
2112
2447
|
}
|
|
2113
2448
|
});
|
|
@@ -2165,10 +2500,220 @@ var init_class_schema = __esm({
|
|
|
2165
2500
|
}
|
|
2166
2501
|
});
|
|
2167
2502
|
|
|
2503
|
+
// src/generators/mixed-authoring.ts
|
|
2504
|
+
function buildMixedAuthoringSchemas(options) {
|
|
2505
|
+
const { filePath, typeName, overlays, ...schemaOptions } = options;
|
|
2506
|
+
const analysis = analyzeNamedType(filePath, typeName);
|
|
2507
|
+
const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays);
|
|
2508
|
+
const ir = canonicalizeTSDoc(composedAnalysis, { file: filePath });
|
|
2509
|
+
return {
|
|
2510
|
+
jsonSchema: generateJsonSchemaFromIR(ir, schemaOptions),
|
|
2511
|
+
uiSchema: generateUiSchemaFromIR(ir)
|
|
2512
|
+
};
|
|
2513
|
+
}
|
|
2514
|
+
function analyzeNamedType(filePath, typeName) {
|
|
2515
|
+
const ctx = createProgramContext(filePath);
|
|
2516
|
+
const source = { file: filePath };
|
|
2517
|
+
const classDecl = findClassByName(ctx.sourceFile, typeName);
|
|
2518
|
+
if (classDecl !== null) {
|
|
2519
|
+
return analyzeClassToIR(classDecl, ctx.checker, source.file);
|
|
2520
|
+
}
|
|
2521
|
+
const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
|
|
2522
|
+
if (interfaceDecl !== null) {
|
|
2523
|
+
return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file);
|
|
2524
|
+
}
|
|
2525
|
+
const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
|
|
2526
|
+
if (typeAlias !== null) {
|
|
2527
|
+
const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file);
|
|
2528
|
+
if (result.ok) {
|
|
2529
|
+
return result.analysis;
|
|
2530
|
+
}
|
|
2531
|
+
throw new Error(result.error);
|
|
2532
|
+
}
|
|
2533
|
+
throw new Error(
|
|
2534
|
+
`Type "${typeName}" not found as a class, interface, or type alias in ${filePath}`
|
|
2535
|
+
);
|
|
2536
|
+
}
|
|
2537
|
+
function composeAnalysisWithOverlays(analysis, overlays) {
|
|
2538
|
+
const overlayIR = canonicalizeChainDSL(overlays);
|
|
2539
|
+
const overlayFields = collectOverlayFields(overlayIR.elements);
|
|
2540
|
+
if (overlayFields.length === 0) {
|
|
2541
|
+
return analysis;
|
|
2542
|
+
}
|
|
2543
|
+
const overlayByName = /* @__PURE__ */ new Map();
|
|
2544
|
+
for (const field of overlayFields) {
|
|
2545
|
+
if (overlayByName.has(field.name)) {
|
|
2546
|
+
throw new Error(`Mixed-authoring overlays define "${field.name}" more than once`);
|
|
2547
|
+
}
|
|
2548
|
+
overlayByName.set(field.name, field);
|
|
2549
|
+
}
|
|
2550
|
+
const mergedFields = [];
|
|
2551
|
+
for (const baseField of analysis.fields) {
|
|
2552
|
+
const overlayField = overlayByName.get(baseField.name);
|
|
2553
|
+
if (overlayField === void 0) {
|
|
2554
|
+
mergedFields.push(baseField);
|
|
2555
|
+
continue;
|
|
2556
|
+
}
|
|
2557
|
+
mergedFields.push(mergeFieldOverlay(baseField, overlayField, analysis.typeRegistry));
|
|
2558
|
+
overlayByName.delete(baseField.name);
|
|
2559
|
+
}
|
|
2560
|
+
if (overlayByName.size > 0) {
|
|
2561
|
+
const unknownFields = [...overlayByName.keys()].sort().join(", ");
|
|
2562
|
+
throw new Error(
|
|
2563
|
+
`Mixed-authoring overlays reference fields that are not present in the static model: ${unknownFields}`
|
|
2564
|
+
);
|
|
2565
|
+
}
|
|
2566
|
+
return {
|
|
2567
|
+
...analysis,
|
|
2568
|
+
fields: mergedFields
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2571
|
+
function collectOverlayFields(elements) {
|
|
2572
|
+
const fields = [];
|
|
2573
|
+
for (const element of elements) {
|
|
2574
|
+
switch (element.kind) {
|
|
2575
|
+
case "field":
|
|
2576
|
+
fields.push(element);
|
|
2577
|
+
break;
|
|
2578
|
+
case "group":
|
|
2579
|
+
fields.push(...collectOverlayFields(element.elements));
|
|
2580
|
+
break;
|
|
2581
|
+
case "conditional":
|
|
2582
|
+
fields.push(...collectOverlayFields(element.elements));
|
|
2583
|
+
break;
|
|
2584
|
+
default: {
|
|
2585
|
+
const _exhaustive = element;
|
|
2586
|
+
void _exhaustive;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
return fields;
|
|
2591
|
+
}
|
|
2592
|
+
function mergeFieldOverlay(baseField, overlayField, typeRegistry) {
|
|
2593
|
+
assertSupportedOverlayField(baseField, overlayField);
|
|
2594
|
+
return {
|
|
2595
|
+
...baseField,
|
|
2596
|
+
type: mergeFieldType(baseField, overlayField, typeRegistry),
|
|
2597
|
+
annotations: mergeAnnotations(baseField.annotations, overlayField.annotations)
|
|
2598
|
+
};
|
|
2599
|
+
}
|
|
2600
|
+
function assertSupportedOverlayField(baseField, overlayField) {
|
|
2601
|
+
if (overlayField.constraints.length > 0) {
|
|
2602
|
+
throw new Error(
|
|
2603
|
+
`Mixed-authoring overlay for "${baseField.name}" cannot define constraints; keep constraints on the static model`
|
|
2604
|
+
);
|
|
2605
|
+
}
|
|
2606
|
+
if (overlayField.required) {
|
|
2607
|
+
throw new Error(
|
|
2608
|
+
`Mixed-authoring overlay for "${baseField.name}" cannot change requiredness; keep requiredness on the static model`
|
|
2609
|
+
);
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
function mergeFieldType(baseField, overlayField, typeRegistry) {
|
|
2613
|
+
const { type: baseType } = baseField;
|
|
2614
|
+
const { type: overlayType } = overlayField;
|
|
2615
|
+
if (overlayType.kind === "object" || overlayType.kind === "array") {
|
|
2616
|
+
throw new Error(
|
|
2617
|
+
`Mixed-authoring overlays do not support nested object or array overlays for "${baseField.name}"`
|
|
2618
|
+
);
|
|
2619
|
+
}
|
|
2620
|
+
if (overlayType.kind === "dynamic") {
|
|
2621
|
+
if (!isCompatibleDynamicOverlay(baseField, overlayField, typeRegistry)) {
|
|
2622
|
+
throw new Error(
|
|
2623
|
+
`Mixed-authoring overlay for "${baseField.name}" is incompatible with the static field type`
|
|
2624
|
+
);
|
|
2625
|
+
}
|
|
2626
|
+
return overlayType;
|
|
2627
|
+
}
|
|
2628
|
+
if (!isSameStaticTypeShape(baseType, overlayType)) {
|
|
2629
|
+
throw new Error(
|
|
2630
|
+
`Mixed-authoring overlay for "${baseField.name}" must preserve the static field type`
|
|
2631
|
+
);
|
|
2632
|
+
}
|
|
2633
|
+
return baseType;
|
|
2634
|
+
}
|
|
2635
|
+
function isCompatibleDynamicOverlay(baseField, overlayField, typeRegistry) {
|
|
2636
|
+
const overlayType = overlayField.type;
|
|
2637
|
+
if (overlayType.kind !== "dynamic") {
|
|
2638
|
+
return false;
|
|
2639
|
+
}
|
|
2640
|
+
const resolvedBaseType = resolveReferenceType(baseField.type, typeRegistry);
|
|
2641
|
+
if (resolvedBaseType === null) {
|
|
2642
|
+
return false;
|
|
2643
|
+
}
|
|
2644
|
+
if (overlayType.dynamicKind === "enum") {
|
|
2645
|
+
return resolvedBaseType.kind === "primitive" ? resolvedBaseType.primitiveKind === "string" : resolvedBaseType.kind === "enum";
|
|
2646
|
+
}
|
|
2647
|
+
return resolvedBaseType.kind === "object" || resolvedBaseType.kind === "record";
|
|
2648
|
+
}
|
|
2649
|
+
function resolveReferenceType(type, typeRegistry, seen = /* @__PURE__ */ new Set()) {
|
|
2650
|
+
if (type.kind !== "reference") {
|
|
2651
|
+
return type;
|
|
2652
|
+
}
|
|
2653
|
+
if (seen.has(type.name)) {
|
|
2654
|
+
return null;
|
|
2655
|
+
}
|
|
2656
|
+
const definition = typeRegistry[type.name];
|
|
2657
|
+
if (definition === void 0) {
|
|
2658
|
+
return null;
|
|
2659
|
+
}
|
|
2660
|
+
seen.add(type.name);
|
|
2661
|
+
return resolveReferenceType(definition.type, typeRegistry, seen);
|
|
2662
|
+
}
|
|
2663
|
+
function isSameStaticTypeShape(baseType, overlayType) {
|
|
2664
|
+
if (baseType.kind !== overlayType.kind) {
|
|
2665
|
+
return false;
|
|
2666
|
+
}
|
|
2667
|
+
switch (baseType.kind) {
|
|
2668
|
+
case "primitive":
|
|
2669
|
+
return overlayType.kind === "primitive" && baseType.primitiveKind === overlayType.primitiveKind;
|
|
2670
|
+
case "enum":
|
|
2671
|
+
return overlayType.kind === "enum";
|
|
2672
|
+
case "dynamic":
|
|
2673
|
+
return overlayType.kind === "dynamic" && baseType.dynamicKind === overlayType.dynamicKind && baseType.sourceKey === overlayType.sourceKey;
|
|
2674
|
+
case "record":
|
|
2675
|
+
return overlayType.kind === "record";
|
|
2676
|
+
case "reference":
|
|
2677
|
+
return overlayType.kind === "reference" && baseType.name === overlayType.name;
|
|
2678
|
+
case "union":
|
|
2679
|
+
return overlayType.kind === "union";
|
|
2680
|
+
case "custom":
|
|
2681
|
+
return overlayType.kind === "custom" && baseType.typeId === overlayType.typeId;
|
|
2682
|
+
case "object":
|
|
2683
|
+
case "array":
|
|
2684
|
+
return true;
|
|
2685
|
+
default: {
|
|
2686
|
+
const _exhaustive = baseType;
|
|
2687
|
+
return _exhaustive;
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
function mergeAnnotations(baseAnnotations, overlayAnnotations) {
|
|
2692
|
+
const baseKeys = new Set(baseAnnotations.map(annotationKey));
|
|
2693
|
+
const overlayOnly = overlayAnnotations.filter(
|
|
2694
|
+
(annotation) => !baseKeys.has(annotationKey(annotation))
|
|
2695
|
+
);
|
|
2696
|
+
return [...overlayOnly, ...baseAnnotations];
|
|
2697
|
+
}
|
|
2698
|
+
function annotationKey(annotation) {
|
|
2699
|
+
return annotation.annotationKind === "custom" ? `${annotation.annotationKind}:${annotation.annotationId}` : annotation.annotationKind;
|
|
2700
|
+
}
|
|
2701
|
+
var init_mixed_authoring = __esm({
|
|
2702
|
+
"src/generators/mixed-authoring.ts"() {
|
|
2703
|
+
"use strict";
|
|
2704
|
+
init_ir_generator();
|
|
2705
|
+
init_ir_generator2();
|
|
2706
|
+
init_canonicalize();
|
|
2707
|
+
init_program();
|
|
2708
|
+
init_class_analyzer();
|
|
2709
|
+
}
|
|
2710
|
+
});
|
|
2711
|
+
|
|
2168
2712
|
// src/index.ts
|
|
2169
2713
|
var index_exports = {};
|
|
2170
2714
|
__export(index_exports, {
|
|
2171
2715
|
buildFormSchemas: () => buildFormSchemas,
|
|
2716
|
+
buildMixedAuthoringSchemas: () => buildMixedAuthoringSchemas,
|
|
2172
2717
|
categorizationSchema: () => categorizationSchema,
|
|
2173
2718
|
categorySchema: () => categorySchema,
|
|
2174
2719
|
controlSchema: () => controlSchema,
|
|
@@ -2233,6 +2778,7 @@ var init_index = __esm({
|
|
|
2233
2778
|
init_ir_generator();
|
|
2234
2779
|
init_generator2();
|
|
2235
2780
|
init_class_schema();
|
|
2781
|
+
init_mixed_authoring();
|
|
2236
2782
|
}
|
|
2237
2783
|
});
|
|
2238
2784
|
|