@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.cjs
CHANGED
|
@@ -362,6 +362,7 @@ function canonicalizeTSDoc(analysis, source) {
|
|
|
362
362
|
irVersion: import_core2.IR_VERSION,
|
|
363
363
|
elements,
|
|
364
364
|
typeRegistry: analysis.typeRegistry,
|
|
365
|
+
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { annotations: analysis.annotations },
|
|
365
366
|
provenance
|
|
366
367
|
};
|
|
367
368
|
}
|
|
@@ -454,6 +455,9 @@ function generateJsonSchemaFromIR(ir, options) {
|
|
|
454
455
|
const ctx = makeContext(options);
|
|
455
456
|
for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
|
|
456
457
|
ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
|
|
458
|
+
if (typeDef.annotations && typeDef.annotations.length > 0) {
|
|
459
|
+
applyAnnotations(ctx.defs[name], typeDef.annotations, ctx);
|
|
460
|
+
}
|
|
457
461
|
}
|
|
458
462
|
const properties = {};
|
|
459
463
|
const required = [];
|
|
@@ -465,6 +469,9 @@ function generateJsonSchemaFromIR(ir, options) {
|
|
|
465
469
|
properties,
|
|
466
470
|
...uniqueRequired.length > 0 && { required: uniqueRequired }
|
|
467
471
|
};
|
|
472
|
+
if (ir.annotations && ir.annotations.length > 0) {
|
|
473
|
+
applyAnnotations(result, ir.annotations, ctx);
|
|
474
|
+
}
|
|
468
475
|
if (Object.keys(ctx.defs).length > 0) {
|
|
469
476
|
result.$defs = ctx.defs;
|
|
470
477
|
}
|
|
@@ -494,22 +501,51 @@ function collectFields(elements, properties, required, ctx) {
|
|
|
494
501
|
}
|
|
495
502
|
function generateFieldSchema(field, ctx) {
|
|
496
503
|
const schema = generateTypeNode(field.type, ctx);
|
|
504
|
+
const itemStringSchema = schema.type === "array" && schema.items?.type === "string" ? schema.items : void 0;
|
|
497
505
|
const directConstraints = [];
|
|
506
|
+
const itemConstraints = [];
|
|
498
507
|
const pathConstraints = [];
|
|
499
508
|
for (const c of field.constraints) {
|
|
500
509
|
if (c.path) {
|
|
501
510
|
pathConstraints.push(c);
|
|
511
|
+
} else if (itemStringSchema !== void 0 && isStringItemConstraint(c)) {
|
|
512
|
+
itemConstraints.push(c);
|
|
502
513
|
} else {
|
|
503
514
|
directConstraints.push(c);
|
|
504
515
|
}
|
|
505
516
|
}
|
|
506
517
|
applyConstraints(schema, directConstraints, ctx);
|
|
507
|
-
|
|
518
|
+
if (itemStringSchema !== void 0) {
|
|
519
|
+
applyConstraints(itemStringSchema, itemConstraints, ctx);
|
|
520
|
+
}
|
|
521
|
+
const rootAnnotations = [];
|
|
522
|
+
const itemAnnotations = [];
|
|
523
|
+
for (const annotation of field.annotations) {
|
|
524
|
+
if (itemStringSchema !== void 0 && annotation.annotationKind === "format") {
|
|
525
|
+
itemAnnotations.push(annotation);
|
|
526
|
+
} else {
|
|
527
|
+
rootAnnotations.push(annotation);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
applyAnnotations(schema, rootAnnotations, ctx);
|
|
531
|
+
if (itemStringSchema !== void 0) {
|
|
532
|
+
applyAnnotations(itemStringSchema, itemAnnotations, ctx);
|
|
533
|
+
}
|
|
508
534
|
if (pathConstraints.length === 0) {
|
|
509
535
|
return schema;
|
|
510
536
|
}
|
|
511
537
|
return applyPathTargetedConstraints(schema, pathConstraints, ctx);
|
|
512
538
|
}
|
|
539
|
+
function isStringItemConstraint(constraint) {
|
|
540
|
+
switch (constraint.constraintKind) {
|
|
541
|
+
case "minLength":
|
|
542
|
+
case "maxLength":
|
|
543
|
+
case "pattern":
|
|
544
|
+
return true;
|
|
545
|
+
default:
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
513
549
|
function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
|
|
514
550
|
if (schema.type === "array" && schema.items) {
|
|
515
551
|
schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx);
|
|
@@ -727,6 +763,9 @@ function applyConstraints(schema, constraints, ctx) {
|
|
|
727
763
|
case "uniqueItems":
|
|
728
764
|
schema.uniqueItems = constraint.value;
|
|
729
765
|
break;
|
|
766
|
+
case "const":
|
|
767
|
+
schema.const = constraint.value;
|
|
768
|
+
break;
|
|
730
769
|
case "allowedMembers":
|
|
731
770
|
break;
|
|
732
771
|
case "custom":
|
|
@@ -751,8 +790,14 @@ function applyAnnotations(schema, annotations, ctx) {
|
|
|
751
790
|
case "defaultValue":
|
|
752
791
|
schema.default = annotation.value;
|
|
753
792
|
break;
|
|
793
|
+
case "format":
|
|
794
|
+
schema.format = annotation.value;
|
|
795
|
+
break;
|
|
754
796
|
case "deprecated":
|
|
755
797
|
schema.deprecated = true;
|
|
798
|
+
if (annotation.message !== void 0 && annotation.message !== "") {
|
|
799
|
+
schema["x-formspec-deprecation-description"] = annotation.message;
|
|
800
|
+
}
|
|
756
801
|
break;
|
|
757
802
|
case "placeholder":
|
|
758
803
|
break;
|
|
@@ -957,25 +1002,31 @@ function createShowRule(fieldName, value) {
|
|
|
957
1002
|
}
|
|
958
1003
|
};
|
|
959
1004
|
}
|
|
1005
|
+
function flattenConditionSchema(scope, schema) {
|
|
1006
|
+
if (schema.allOf === void 0) {
|
|
1007
|
+
if (scope === "#") {
|
|
1008
|
+
return [schema];
|
|
1009
|
+
}
|
|
1010
|
+
const fieldName = scope.replace("#/properties/", "");
|
|
1011
|
+
return [
|
|
1012
|
+
{
|
|
1013
|
+
properties: {
|
|
1014
|
+
[fieldName]: schema
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
];
|
|
1018
|
+
}
|
|
1019
|
+
return schema.allOf.flatMap((member) => flattenConditionSchema(scope, member));
|
|
1020
|
+
}
|
|
960
1021
|
function combineRules(parentRule, childRule) {
|
|
961
|
-
const parentCondition = parentRule.condition;
|
|
962
|
-
const childCondition = childRule.condition;
|
|
963
1022
|
return {
|
|
964
1023
|
effect: "SHOW",
|
|
965
1024
|
condition: {
|
|
966
1025
|
scope: "#",
|
|
967
1026
|
schema: {
|
|
968
1027
|
allOf: [
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
[parentCondition.scope.replace("#/properties/", "")]: parentCondition.schema
|
|
972
|
-
}
|
|
973
|
-
},
|
|
974
|
-
{
|
|
975
|
-
properties: {
|
|
976
|
-
[childCondition.scope.replace("#/properties/", "")]: childCondition.schema
|
|
977
|
-
}
|
|
978
|
-
}
|
|
1028
|
+
...flattenConditionSchema(parentRule.condition.scope, parentRule.condition.schema),
|
|
1029
|
+
...flattenConditionSchema(childRule.condition.scope, childRule.condition.schema)
|
|
979
1030
|
]
|
|
980
1031
|
}
|
|
981
1032
|
}
|
|
@@ -983,10 +1034,14 @@ function combineRules(parentRule, childRule) {
|
|
|
983
1034
|
}
|
|
984
1035
|
function fieldNodeToControl(field, parentRule) {
|
|
985
1036
|
const displayNameAnnotation = field.annotations.find((a) => a.annotationKind === "displayName");
|
|
1037
|
+
const placeholderAnnotation = field.annotations.find((a) => a.annotationKind === "placeholder");
|
|
986
1038
|
const control = {
|
|
987
1039
|
type: "Control",
|
|
988
1040
|
scope: fieldToScope(field.name),
|
|
989
1041
|
...displayNameAnnotation !== void 0 && { label: displayNameAnnotation.value },
|
|
1042
|
+
...placeholderAnnotation !== void 0 && {
|
|
1043
|
+
options: { placeholder: placeholderAnnotation.value }
|
|
1044
|
+
},
|
|
990
1045
|
...parentRule !== void 0 && { rule: parentRule }
|
|
991
1046
|
};
|
|
992
1047
|
return control;
|
|
@@ -1296,7 +1351,7 @@ function createFormSpecTSDocConfig() {
|
|
|
1296
1351
|
})
|
|
1297
1352
|
);
|
|
1298
1353
|
}
|
|
1299
|
-
for (const tagName of ["displayName", "description"]) {
|
|
1354
|
+
for (const tagName of ["displayName", "description", "format", "placeholder"]) {
|
|
1300
1355
|
config.addTagDefinition(
|
|
1301
1356
|
new import_tsdoc.TSDocTagDefinition({
|
|
1302
1357
|
tagName: "@" + tagName,
|
|
@@ -1314,6 +1369,12 @@ function getParser() {
|
|
|
1314
1369
|
function parseTSDocTags(node, file = "") {
|
|
1315
1370
|
const constraints = [];
|
|
1316
1371
|
const annotations = [];
|
|
1372
|
+
let displayName;
|
|
1373
|
+
let description;
|
|
1374
|
+
let placeholder;
|
|
1375
|
+
let displayNameProvenance;
|
|
1376
|
+
let descriptionProvenance;
|
|
1377
|
+
let placeholderProvenance;
|
|
1317
1378
|
const sourceFile = node.getSourceFile();
|
|
1318
1379
|
const sourceText = sourceFile.getFullText();
|
|
1319
1380
|
const commentRanges = ts2.getLeadingCommentRanges(sourceText, node.getFullStart());
|
|
@@ -1333,30 +1394,37 @@ function parseTSDocTags(node, file = "") {
|
|
|
1333
1394
|
const docComment = parserContext.docComment;
|
|
1334
1395
|
for (const block of docComment.customBlocks) {
|
|
1335
1396
|
const tagName = (0, import_core3.normalizeConstraintTagName)(block.blockTag.tagName.substring(1));
|
|
1336
|
-
if (tagName === "displayName" || tagName === "description") {
|
|
1397
|
+
if (tagName === "displayName" || tagName === "description" || tagName === "format" || tagName === "placeholder") {
|
|
1337
1398
|
const text2 = extractBlockText(block).trim();
|
|
1338
1399
|
if (text2 === "") continue;
|
|
1339
1400
|
const provenance2 = provenanceForComment(range, sourceFile, file, tagName);
|
|
1340
1401
|
if (tagName === "displayName") {
|
|
1402
|
+
if (!isMemberTargetDisplayName(text2) && displayName === void 0) {
|
|
1403
|
+
displayName = text2;
|
|
1404
|
+
displayNameProvenance = provenance2;
|
|
1405
|
+
}
|
|
1406
|
+
} else if (tagName === "format") {
|
|
1341
1407
|
annotations.push({
|
|
1342
1408
|
kind: "annotation",
|
|
1343
|
-
annotationKind: "
|
|
1409
|
+
annotationKind: "format",
|
|
1344
1410
|
value: text2,
|
|
1345
1411
|
provenance: provenance2
|
|
1346
1412
|
});
|
|
1347
1413
|
} else {
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1414
|
+
if (tagName === "description" && description === void 0) {
|
|
1415
|
+
description = text2;
|
|
1416
|
+
descriptionProvenance = provenance2;
|
|
1417
|
+
} else if (tagName === "placeholder" && placeholder === void 0) {
|
|
1418
|
+
placeholder = text2;
|
|
1419
|
+
placeholderProvenance = provenance2;
|
|
1420
|
+
}
|
|
1354
1421
|
}
|
|
1355
1422
|
continue;
|
|
1356
1423
|
}
|
|
1357
1424
|
if (TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
|
|
1358
1425
|
const text = extractBlockText(block).trim();
|
|
1359
|
-
|
|
1426
|
+
const expectedType = (0, import_core3.isBuiltinConstraintName)(tagName) ? import_core3.BUILTIN_CONSTRAINT_DEFINITIONS[tagName] : void 0;
|
|
1427
|
+
if (text === "" && expectedType !== "boolean") continue;
|
|
1360
1428
|
const provenance = provenanceForComment(range, sourceFile, file, tagName);
|
|
1361
1429
|
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
1362
1430
|
if (constraintNode) {
|
|
@@ -1364,14 +1432,47 @@ function parseTSDocTags(node, file = "") {
|
|
|
1364
1432
|
}
|
|
1365
1433
|
}
|
|
1366
1434
|
if (docComment.deprecatedBlock !== void 0) {
|
|
1435
|
+
const message = extractBlockText(docComment.deprecatedBlock).trim();
|
|
1367
1436
|
annotations.push({
|
|
1368
1437
|
kind: "annotation",
|
|
1369
1438
|
annotationKind: "deprecated",
|
|
1439
|
+
...message !== "" && { message },
|
|
1370
1440
|
provenance: provenanceForComment(range, sourceFile, file, "deprecated")
|
|
1371
1441
|
});
|
|
1372
1442
|
}
|
|
1443
|
+
if (description === void 0 && docComment.remarksBlock !== void 0) {
|
|
1444
|
+
const remarks = extractBlockText(docComment.remarksBlock).trim();
|
|
1445
|
+
if (remarks !== "") {
|
|
1446
|
+
description = remarks;
|
|
1447
|
+
descriptionProvenance = provenanceForComment(range, sourceFile, file, "remarks");
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1373
1450
|
}
|
|
1374
1451
|
}
|
|
1452
|
+
if (displayName !== void 0 && displayNameProvenance !== void 0) {
|
|
1453
|
+
annotations.push({
|
|
1454
|
+
kind: "annotation",
|
|
1455
|
+
annotationKind: "displayName",
|
|
1456
|
+
value: displayName,
|
|
1457
|
+
provenance: displayNameProvenance
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
if (description !== void 0 && descriptionProvenance !== void 0) {
|
|
1461
|
+
annotations.push({
|
|
1462
|
+
kind: "annotation",
|
|
1463
|
+
annotationKind: "description",
|
|
1464
|
+
value: description,
|
|
1465
|
+
provenance: descriptionProvenance
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1468
|
+
if (placeholder !== void 0 && placeholderProvenance !== void 0) {
|
|
1469
|
+
annotations.push({
|
|
1470
|
+
kind: "annotation",
|
|
1471
|
+
annotationKind: "placeholder",
|
|
1472
|
+
value: placeholder,
|
|
1473
|
+
provenance: placeholderProvenance
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1375
1476
|
const jsDocTagsAll = ts2.getJSDocTags(node);
|
|
1376
1477
|
for (const tag of jsDocTagsAll) {
|
|
1377
1478
|
const tagName = (0, import_core3.normalizeConstraintTagName)(tag.tagName.text);
|
|
@@ -1380,6 +1481,11 @@ function parseTSDocTags(node, file = "") {
|
|
|
1380
1481
|
if (commentText === void 0 || commentText.trim() === "") continue;
|
|
1381
1482
|
const text = commentText.trim();
|
|
1382
1483
|
const provenance = provenanceForJSDocTag(tag, file);
|
|
1484
|
+
if (tagName === "defaultValue") {
|
|
1485
|
+
const defaultValueNode = parseDefaultValueValue(text, provenance);
|
|
1486
|
+
annotations.push(defaultValueNode);
|
|
1487
|
+
continue;
|
|
1488
|
+
}
|
|
1383
1489
|
const constraintNode = parseConstraintValue(tagName, text, provenance);
|
|
1384
1490
|
if (constraintNode) {
|
|
1385
1491
|
constraints.push(constraintNode);
|
|
@@ -1387,6 +1493,28 @@ function parseTSDocTags(node, file = "") {
|
|
|
1387
1493
|
}
|
|
1388
1494
|
return { constraints, annotations };
|
|
1389
1495
|
}
|
|
1496
|
+
function extractDisplayNameMetadata(node) {
|
|
1497
|
+
let displayName;
|
|
1498
|
+
const memberDisplayNames = /* @__PURE__ */ new Map();
|
|
1499
|
+
for (const tag of ts2.getJSDocTags(node)) {
|
|
1500
|
+
const tagName = (0, import_core3.normalizeConstraintTagName)(tag.tagName.text);
|
|
1501
|
+
if (tagName !== "displayName") continue;
|
|
1502
|
+
const commentText = getTagCommentText(tag);
|
|
1503
|
+
if (commentText === void 0) continue;
|
|
1504
|
+
const text = commentText.trim();
|
|
1505
|
+
if (text === "") continue;
|
|
1506
|
+
const memberTarget = parseMemberTargetDisplayName(text);
|
|
1507
|
+
if (memberTarget) {
|
|
1508
|
+
memberDisplayNames.set(memberTarget.target, memberTarget.label);
|
|
1509
|
+
continue;
|
|
1510
|
+
}
|
|
1511
|
+
displayName ??= text;
|
|
1512
|
+
}
|
|
1513
|
+
return {
|
|
1514
|
+
...displayName !== void 0 && { displayName },
|
|
1515
|
+
memberDisplayNames
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1390
1518
|
function extractPathTarget(text) {
|
|
1391
1519
|
const trimmed = text.trimStart();
|
|
1392
1520
|
const match = /^:([a-zA-Z_]\w*)\s+([\s\S]*)$/.exec(trimmed);
|
|
@@ -1449,7 +1577,45 @@ function parseConstraintValue(tagName, text, provenance) {
|
|
|
1449
1577
|
}
|
|
1450
1578
|
return null;
|
|
1451
1579
|
}
|
|
1580
|
+
if (expectedType === "boolean") {
|
|
1581
|
+
const trimmed = effectiveText.trim();
|
|
1582
|
+
if (trimmed !== "" && trimmed !== "true") {
|
|
1583
|
+
return null;
|
|
1584
|
+
}
|
|
1585
|
+
if (tagName === "uniqueItems") {
|
|
1586
|
+
return {
|
|
1587
|
+
kind: "constraint",
|
|
1588
|
+
constraintKind: "uniqueItems",
|
|
1589
|
+
value: true,
|
|
1590
|
+
...path4 && { path: path4 },
|
|
1591
|
+
provenance
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
return null;
|
|
1595
|
+
}
|
|
1452
1596
|
if (expectedType === "json") {
|
|
1597
|
+
if (tagName === "const") {
|
|
1598
|
+
const trimmedText = effectiveText.trim();
|
|
1599
|
+
if (trimmedText === "") return null;
|
|
1600
|
+
try {
|
|
1601
|
+
const parsed2 = JSON.parse(trimmedText);
|
|
1602
|
+
return {
|
|
1603
|
+
kind: "constraint",
|
|
1604
|
+
constraintKind: "const",
|
|
1605
|
+
value: parsed2,
|
|
1606
|
+
...path4 && { path: path4 },
|
|
1607
|
+
provenance
|
|
1608
|
+
};
|
|
1609
|
+
} catch {
|
|
1610
|
+
return {
|
|
1611
|
+
kind: "constraint",
|
|
1612
|
+
constraintKind: "const",
|
|
1613
|
+
value: trimmedText,
|
|
1614
|
+
...path4 && { path: path4 },
|
|
1615
|
+
provenance
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1453
1619
|
const parsed = tryParseJson(effectiveText);
|
|
1454
1620
|
if (!Array.isArray(parsed)) {
|
|
1455
1621
|
return null;
|
|
@@ -1481,6 +1647,34 @@ function parseConstraintValue(tagName, text, provenance) {
|
|
|
1481
1647
|
provenance
|
|
1482
1648
|
};
|
|
1483
1649
|
}
|
|
1650
|
+
function parseDefaultValueValue(text, provenance) {
|
|
1651
|
+
const trimmed = text.trim();
|
|
1652
|
+
let value;
|
|
1653
|
+
if (trimmed === "null") {
|
|
1654
|
+
value = null;
|
|
1655
|
+
} else if (trimmed === "true") {
|
|
1656
|
+
value = true;
|
|
1657
|
+
} else if (trimmed === "false") {
|
|
1658
|
+
value = false;
|
|
1659
|
+
} else {
|
|
1660
|
+
const parsed = tryParseJson(trimmed);
|
|
1661
|
+
value = parsed !== null ? parsed : trimmed;
|
|
1662
|
+
}
|
|
1663
|
+
return {
|
|
1664
|
+
kind: "annotation",
|
|
1665
|
+
annotationKind: "defaultValue",
|
|
1666
|
+
value,
|
|
1667
|
+
provenance
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
function isMemberTargetDisplayName(text) {
|
|
1671
|
+
return parseMemberTargetDisplayName(text) !== null;
|
|
1672
|
+
}
|
|
1673
|
+
function parseMemberTargetDisplayName(text) {
|
|
1674
|
+
const match = /^:([^\s]+)\s+([\s\S]+)$/.exec(text);
|
|
1675
|
+
if (!match?.[1] || !match[2]) return null;
|
|
1676
|
+
return { target: match[1], label: match[2].trim() };
|
|
1677
|
+
}
|
|
1484
1678
|
function provenanceForComment(range, sourceFile, file, tagName) {
|
|
1485
1679
|
const { line, character } = sourceFile.getLineAndCharacterOfPosition(range.pos);
|
|
1486
1680
|
return {
|
|
@@ -1532,7 +1726,7 @@ var init_tsdoc_parser = __esm({
|
|
|
1532
1726
|
minItems: "minItems",
|
|
1533
1727
|
maxItems: "maxItems"
|
|
1534
1728
|
};
|
|
1535
|
-
TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions"]);
|
|
1729
|
+
TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions", "defaultValue"]);
|
|
1536
1730
|
}
|
|
1537
1731
|
});
|
|
1538
1732
|
|
|
@@ -1599,6 +1793,7 @@ function analyzeClassToIR(classDecl, checker, file = "") {
|
|
|
1599
1793
|
const fields = [];
|
|
1600
1794
|
const fieldLayouts = [];
|
|
1601
1795
|
const typeRegistry = {};
|
|
1796
|
+
const annotations = extractJSDocAnnotationNodes(classDecl, file);
|
|
1602
1797
|
const visiting = /* @__PURE__ */ new Set();
|
|
1603
1798
|
const instanceMethods = [];
|
|
1604
1799
|
const staticMethods = [];
|
|
@@ -1621,12 +1816,21 @@ function analyzeClassToIR(classDecl, checker, file = "") {
|
|
|
1621
1816
|
}
|
|
1622
1817
|
}
|
|
1623
1818
|
}
|
|
1624
|
-
return {
|
|
1819
|
+
return {
|
|
1820
|
+
name,
|
|
1821
|
+
fields,
|
|
1822
|
+
fieldLayouts,
|
|
1823
|
+
typeRegistry,
|
|
1824
|
+
...annotations.length > 0 && { annotations },
|
|
1825
|
+
instanceMethods,
|
|
1826
|
+
staticMethods
|
|
1827
|
+
};
|
|
1625
1828
|
}
|
|
1626
1829
|
function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
1627
1830
|
const name = interfaceDecl.name.text;
|
|
1628
1831
|
const fields = [];
|
|
1629
1832
|
const typeRegistry = {};
|
|
1833
|
+
const annotations = extractJSDocAnnotationNodes(interfaceDecl, file);
|
|
1630
1834
|
const visiting = /* @__PURE__ */ new Set();
|
|
1631
1835
|
for (const member of interfaceDecl.members) {
|
|
1632
1836
|
if (ts4.isPropertySignature(member)) {
|
|
@@ -1637,7 +1841,15 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "") {
|
|
|
1637
1841
|
}
|
|
1638
1842
|
}
|
|
1639
1843
|
const fieldLayouts = fields.map(() => ({}));
|
|
1640
|
-
return {
|
|
1844
|
+
return {
|
|
1845
|
+
name,
|
|
1846
|
+
fields,
|
|
1847
|
+
fieldLayouts,
|
|
1848
|
+
typeRegistry,
|
|
1849
|
+
...annotations.length > 0 && { annotations },
|
|
1850
|
+
instanceMethods: [],
|
|
1851
|
+
staticMethods: []
|
|
1852
|
+
};
|
|
1641
1853
|
}
|
|
1642
1854
|
function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
1643
1855
|
if (!ts4.isTypeLiteralNode(typeAlias.type)) {
|
|
@@ -1652,6 +1864,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
|
1652
1864
|
const name = typeAlias.name.text;
|
|
1653
1865
|
const fields = [];
|
|
1654
1866
|
const typeRegistry = {};
|
|
1867
|
+
const annotations = extractJSDocAnnotationNodes(typeAlias, file);
|
|
1655
1868
|
const visiting = /* @__PURE__ */ new Set();
|
|
1656
1869
|
for (const member of typeAlias.type.members) {
|
|
1657
1870
|
if (ts4.isPropertySignature(member)) {
|
|
@@ -1668,6 +1881,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "") {
|
|
|
1668
1881
|
fields,
|
|
1669
1882
|
fieldLayouts: fields.map(() => ({})),
|
|
1670
1883
|
typeRegistry,
|
|
1884
|
+
...annotations.length > 0 && { annotations },
|
|
1671
1885
|
instanceMethods: [],
|
|
1672
1886
|
staticMethods: []
|
|
1673
1887
|
}
|
|
@@ -1681,7 +1895,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
|
1681
1895
|
const tsType = checker.getTypeAtLocation(prop);
|
|
1682
1896
|
const optional = prop.questionToken !== void 0;
|
|
1683
1897
|
const provenance = provenanceForNode(prop, file);
|
|
1684
|
-
let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
|
|
1898
|
+
let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting, prop);
|
|
1685
1899
|
const constraints = [];
|
|
1686
1900
|
if (prop.type) {
|
|
1687
1901
|
constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
|
|
@@ -1690,7 +1904,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
|
1690
1904
|
let annotations = [];
|
|
1691
1905
|
annotations.push(...extractJSDocAnnotationNodes(prop, file));
|
|
1692
1906
|
const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
|
|
1693
|
-
if (defaultAnnotation) {
|
|
1907
|
+
if (defaultAnnotation && !annotations.some((a) => a.annotationKind === "defaultValue")) {
|
|
1694
1908
|
annotations.push(defaultAnnotation);
|
|
1695
1909
|
}
|
|
1696
1910
|
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
@@ -1712,7 +1926,7 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
1712
1926
|
const tsType = checker.getTypeAtLocation(prop);
|
|
1713
1927
|
const optional = prop.questionToken !== void 0;
|
|
1714
1928
|
const provenance = provenanceForNode(prop, file);
|
|
1715
|
-
let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
|
|
1929
|
+
let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting, prop);
|
|
1716
1930
|
const constraints = [];
|
|
1717
1931
|
if (prop.type) {
|
|
1718
1932
|
constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
|
|
@@ -1793,7 +2007,7 @@ function parseEnumMemberDisplayName(value) {
|
|
|
1793
2007
|
if (label === "") return null;
|
|
1794
2008
|
return { value: match[1], label };
|
|
1795
2009
|
}
|
|
1796
|
-
function resolveTypeNode(type, checker, file, typeRegistry, visiting) {
|
|
2010
|
+
function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode) {
|
|
1797
2011
|
if (type.flags & ts4.TypeFlags.String) {
|
|
1798
2012
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1799
2013
|
}
|
|
@@ -1822,7 +2036,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting) {
|
|
|
1822
2036
|
};
|
|
1823
2037
|
}
|
|
1824
2038
|
if (type.isUnion()) {
|
|
1825
|
-
return resolveUnionType(type, checker, file, typeRegistry, visiting);
|
|
2039
|
+
return resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode);
|
|
1826
2040
|
}
|
|
1827
2041
|
if (checker.isArrayType(type)) {
|
|
1828
2042
|
return resolveArrayType(type, checker, file, typeRegistry, visiting);
|
|
@@ -1832,70 +2046,102 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting) {
|
|
|
1832
2046
|
}
|
|
1833
2047
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1834
2048
|
}
|
|
1835
|
-
function resolveUnionType(type, checker, file, typeRegistry, visiting) {
|
|
2049
|
+
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode) {
|
|
2050
|
+
const typeName = getNamedTypeName(type);
|
|
2051
|
+
const namedDecl = getNamedTypeDeclaration(type);
|
|
2052
|
+
if (typeName && typeName in typeRegistry) {
|
|
2053
|
+
return { kind: "reference", name: typeName, typeArguments: [] };
|
|
2054
|
+
}
|
|
1836
2055
|
const allTypes = type.types;
|
|
1837
2056
|
const nonNullTypes = allTypes.filter(
|
|
1838
2057
|
(t) => !(t.flags & (ts4.TypeFlags.Null | ts4.TypeFlags.Undefined))
|
|
1839
2058
|
);
|
|
1840
2059
|
const hasNull = allTypes.some((t) => t.flags & ts4.TypeFlags.Null);
|
|
2060
|
+
const memberDisplayNames = /* @__PURE__ */ new Map();
|
|
2061
|
+
if (namedDecl) {
|
|
2062
|
+
for (const [value, label] of extractDisplayNameMetadata(namedDecl).memberDisplayNames) {
|
|
2063
|
+
memberDisplayNames.set(value, label);
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
if (sourceNode) {
|
|
2067
|
+
for (const [value, label] of extractDisplayNameMetadata(sourceNode).memberDisplayNames) {
|
|
2068
|
+
memberDisplayNames.set(value, label);
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
const registerNamed = (result) => {
|
|
2072
|
+
if (!typeName) {
|
|
2073
|
+
return result;
|
|
2074
|
+
}
|
|
2075
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2076
|
+
typeRegistry[typeName] = {
|
|
2077
|
+
name: typeName,
|
|
2078
|
+
type: result,
|
|
2079
|
+
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2080
|
+
provenance: provenanceForDeclaration(namedDecl ?? sourceNode, file)
|
|
2081
|
+
};
|
|
2082
|
+
return { kind: "reference", name: typeName, typeArguments: [] };
|
|
2083
|
+
};
|
|
2084
|
+
const applyMemberLabels = (members2) => members2.map((value) => {
|
|
2085
|
+
const displayName = memberDisplayNames.get(String(value));
|
|
2086
|
+
return displayName !== void 0 ? { value, displayName } : { value };
|
|
2087
|
+
});
|
|
1841
2088
|
const isBooleanUnion2 = nonNullTypes.length === 2 && nonNullTypes.every((t) => t.flags & ts4.TypeFlags.BooleanLiteral);
|
|
1842
2089
|
if (isBooleanUnion2) {
|
|
1843
2090
|
const boolNode = { kind: "primitive", primitiveKind: "boolean" };
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
}
|
|
1850
|
-
return boolNode;
|
|
2091
|
+
const result = hasNull ? {
|
|
2092
|
+
kind: "union",
|
|
2093
|
+
members: [boolNode, { kind: "primitive", primitiveKind: "null" }]
|
|
2094
|
+
} : boolNode;
|
|
2095
|
+
return registerNamed(result);
|
|
1851
2096
|
}
|
|
1852
2097
|
const allStringLiterals = nonNullTypes.every((t) => t.isStringLiteral());
|
|
1853
2098
|
if (allStringLiterals && nonNullTypes.length > 0) {
|
|
1854
2099
|
const stringTypes = nonNullTypes.filter((t) => t.isStringLiteral());
|
|
1855
2100
|
const enumNode = {
|
|
1856
2101
|
kind: "enum",
|
|
1857
|
-
members: stringTypes.map((t) =>
|
|
2102
|
+
members: applyMemberLabels(stringTypes.map((t) => t.value))
|
|
1858
2103
|
};
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
}
|
|
1865
|
-
return enumNode;
|
|
2104
|
+
const result = hasNull ? {
|
|
2105
|
+
kind: "union",
|
|
2106
|
+
members: [enumNode, { kind: "primitive", primitiveKind: "null" }]
|
|
2107
|
+
} : enumNode;
|
|
2108
|
+
return registerNamed(result);
|
|
1866
2109
|
}
|
|
1867
2110
|
const allNumberLiterals = nonNullTypes.every((t) => t.isNumberLiteral());
|
|
1868
2111
|
if (allNumberLiterals && nonNullTypes.length > 0) {
|
|
1869
2112
|
const numberTypes = nonNullTypes.filter((t) => t.isNumberLiteral());
|
|
1870
2113
|
const enumNode = {
|
|
1871
2114
|
kind: "enum",
|
|
1872
|
-
members: numberTypes.map((t) =>
|
|
2115
|
+
members: applyMemberLabels(numberTypes.map((t) => t.value))
|
|
1873
2116
|
};
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
}
|
|
1880
|
-
return enumNode;
|
|
2117
|
+
const result = hasNull ? {
|
|
2118
|
+
kind: "union",
|
|
2119
|
+
members: [enumNode, { kind: "primitive", primitiveKind: "null" }]
|
|
2120
|
+
} : enumNode;
|
|
2121
|
+
return registerNamed(result);
|
|
1881
2122
|
}
|
|
1882
2123
|
if (nonNullTypes.length === 1 && nonNullTypes[0]) {
|
|
1883
|
-
const inner = resolveTypeNode(
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
2124
|
+
const inner = resolveTypeNode(
|
|
2125
|
+
nonNullTypes[0],
|
|
2126
|
+
checker,
|
|
2127
|
+
file,
|
|
2128
|
+
typeRegistry,
|
|
2129
|
+
visiting,
|
|
2130
|
+
sourceNode
|
|
2131
|
+
);
|
|
2132
|
+
const result = hasNull ? {
|
|
2133
|
+
kind: "union",
|
|
2134
|
+
members: [inner, { kind: "primitive", primitiveKind: "null" }]
|
|
2135
|
+
} : inner;
|
|
2136
|
+
return registerNamed(result);
|
|
1891
2137
|
}
|
|
1892
2138
|
const members = nonNullTypes.map(
|
|
1893
|
-
(t) => resolveTypeNode(t, checker, file, typeRegistry, visiting)
|
|
2139
|
+
(t) => resolveTypeNode(t, checker, file, typeRegistry, visiting, sourceNode)
|
|
1894
2140
|
);
|
|
1895
2141
|
if (hasNull) {
|
|
1896
2142
|
members.push({ kind: "primitive", primitiveKind: "null" });
|
|
1897
2143
|
}
|
|
1898
|
-
return { kind: "union", members };
|
|
2144
|
+
return registerNamed({ kind: "union", members });
|
|
1899
2145
|
}
|
|
1900
2146
|
function resolveArrayType(type, checker, file, typeRegistry, visiting) {
|
|
1901
2147
|
const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
|
|
@@ -1911,30 +2157,84 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
|
|
|
1911
2157
|
if (!indexInfo) {
|
|
1912
2158
|
return null;
|
|
1913
2159
|
}
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
2160
|
+
const valueType = resolveTypeNode(indexInfo.type, checker, file, typeRegistry, visiting);
|
|
2161
|
+
return { kind: "record", valueType };
|
|
2162
|
+
}
|
|
2163
|
+
function typeNodeContainsReference(type, targetName) {
|
|
2164
|
+
switch (type.kind) {
|
|
2165
|
+
case "reference":
|
|
2166
|
+
return type.name === targetName;
|
|
2167
|
+
case "array":
|
|
2168
|
+
return typeNodeContainsReference(type.items, targetName);
|
|
2169
|
+
case "record":
|
|
2170
|
+
return typeNodeContainsReference(type.valueType, targetName);
|
|
2171
|
+
case "union":
|
|
2172
|
+
return type.members.some((member) => typeNodeContainsReference(member, targetName));
|
|
2173
|
+
case "object":
|
|
2174
|
+
return type.properties.some(
|
|
2175
|
+
(property) => typeNodeContainsReference(property.type, targetName)
|
|
2176
|
+
);
|
|
2177
|
+
case "primitive":
|
|
2178
|
+
case "enum":
|
|
2179
|
+
case "dynamic":
|
|
2180
|
+
case "custom":
|
|
2181
|
+
return false;
|
|
2182
|
+
default: {
|
|
2183
|
+
const _exhaustive = type;
|
|
2184
|
+
return _exhaustive;
|
|
2185
|
+
}
|
|
1923
2186
|
}
|
|
1924
2187
|
}
|
|
1925
2188
|
function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
1926
|
-
const
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
2189
|
+
const typeName = getNamedTypeName(type);
|
|
2190
|
+
const namedTypeName = typeName ?? void 0;
|
|
2191
|
+
const namedDecl = getNamedTypeDeclaration(type);
|
|
2192
|
+
const shouldRegisterNamedType = namedTypeName !== void 0 && !(namedTypeName === "Record" && namedDecl?.getSourceFile().fileName !== file);
|
|
2193
|
+
const clearNamedTypeRegistration = () => {
|
|
2194
|
+
if (namedTypeName === void 0 || !shouldRegisterNamedType) {
|
|
2195
|
+
return;
|
|
2196
|
+
}
|
|
2197
|
+
Reflect.deleteProperty(typeRegistry, namedTypeName);
|
|
2198
|
+
};
|
|
1930
2199
|
if (visiting.has(type)) {
|
|
2200
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
2201
|
+
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
2202
|
+
}
|
|
1931
2203
|
return { kind: "object", properties: [], additionalProperties: false };
|
|
1932
2204
|
}
|
|
2205
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType && !typeRegistry[namedTypeName]) {
|
|
2206
|
+
typeRegistry[namedTypeName] = {
|
|
2207
|
+
name: namedTypeName,
|
|
2208
|
+
type: RESOLVING_TYPE_PLACEHOLDER,
|
|
2209
|
+
provenance: provenanceForDeclaration(namedDecl, file)
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
1933
2212
|
visiting.add(type);
|
|
1934
|
-
|
|
1935
|
-
|
|
2213
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType && typeRegistry[namedTypeName]?.type !== void 0) {
|
|
2214
|
+
if (typeRegistry[namedTypeName].type !== RESOLVING_TYPE_PLACEHOLDER) {
|
|
2215
|
+
visiting.delete(type);
|
|
2216
|
+
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
const recordNode = tryResolveRecordType(type, checker, file, typeRegistry, visiting);
|
|
2220
|
+
if (recordNode) {
|
|
1936
2221
|
visiting.delete(type);
|
|
1937
|
-
|
|
2222
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
2223
|
+
const isRecursiveRecord = typeNodeContainsReference(recordNode.valueType, namedTypeName);
|
|
2224
|
+
if (!isRecursiveRecord) {
|
|
2225
|
+
clearNamedTypeRegistration();
|
|
2226
|
+
return recordNode;
|
|
2227
|
+
}
|
|
2228
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2229
|
+
typeRegistry[namedTypeName] = {
|
|
2230
|
+
name: namedTypeName,
|
|
2231
|
+
type: recordNode,
|
|
2232
|
+
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2233
|
+
provenance: provenanceForDeclaration(namedDecl, file)
|
|
2234
|
+
};
|
|
2235
|
+
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
2236
|
+
}
|
|
2237
|
+
return recordNode;
|
|
1938
2238
|
}
|
|
1939
2239
|
const properties = [];
|
|
1940
2240
|
const fieldInfoMap = getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting);
|
|
@@ -1943,7 +2243,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
1943
2243
|
if (!declaration) continue;
|
|
1944
2244
|
const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
|
|
1945
2245
|
const optional = !!(prop.flags & ts4.SymbolFlags.Optional);
|
|
1946
|
-
const propTypeNode = resolveTypeNode(
|
|
2246
|
+
const propTypeNode = resolveTypeNode(
|
|
2247
|
+
propType,
|
|
2248
|
+
checker,
|
|
2249
|
+
file,
|
|
2250
|
+
typeRegistry,
|
|
2251
|
+
visiting,
|
|
2252
|
+
declaration
|
|
2253
|
+
);
|
|
1947
2254
|
const fieldNodeInfo = fieldInfoMap?.get(prop.name);
|
|
1948
2255
|
properties.push({
|
|
1949
2256
|
name: prop.name,
|
|
@@ -1960,13 +2267,15 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
1960
2267
|
properties,
|
|
1961
2268
|
additionalProperties: true
|
|
1962
2269
|
};
|
|
1963
|
-
if (
|
|
1964
|
-
|
|
1965
|
-
|
|
2270
|
+
if (namedTypeName !== void 0 && shouldRegisterNamedType) {
|
|
2271
|
+
const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file) : void 0;
|
|
2272
|
+
typeRegistry[namedTypeName] = {
|
|
2273
|
+
name: namedTypeName,
|
|
1966
2274
|
type: objectNode,
|
|
1967
|
-
|
|
2275
|
+
...annotations !== void 0 && annotations.length > 0 && { annotations },
|
|
2276
|
+
provenance: provenanceForDeclaration(namedDecl, file)
|
|
1968
2277
|
};
|
|
1969
|
-
return { kind: "reference", name:
|
|
2278
|
+
return { kind: "reference", name: namedTypeName, typeArguments: [] };
|
|
1970
2279
|
}
|
|
1971
2280
|
return objectNode;
|
|
1972
2281
|
}
|
|
@@ -2057,6 +2366,12 @@ function provenanceForNode(node, file) {
|
|
|
2057
2366
|
function provenanceForFile(file) {
|
|
2058
2367
|
return { surface: "tsdoc", file, line: 0, column: 0 };
|
|
2059
2368
|
}
|
|
2369
|
+
function provenanceForDeclaration(node, file) {
|
|
2370
|
+
if (!node) {
|
|
2371
|
+
return provenanceForFile(file);
|
|
2372
|
+
}
|
|
2373
|
+
return provenanceForNode(node, file);
|
|
2374
|
+
}
|
|
2060
2375
|
function getNamedTypeName(type) {
|
|
2061
2376
|
const symbol = type.getSymbol();
|
|
2062
2377
|
if (symbol?.declarations) {
|
|
@@ -2075,6 +2390,20 @@ function getNamedTypeName(type) {
|
|
|
2075
2390
|
}
|
|
2076
2391
|
return null;
|
|
2077
2392
|
}
|
|
2393
|
+
function getNamedTypeDeclaration(type) {
|
|
2394
|
+
const symbol = type.getSymbol();
|
|
2395
|
+
if (symbol?.declarations) {
|
|
2396
|
+
const decl = symbol.declarations[0];
|
|
2397
|
+
if (decl && (ts4.isClassDeclaration(decl) || ts4.isInterfaceDeclaration(decl) || ts4.isTypeAliasDeclaration(decl))) {
|
|
2398
|
+
return decl;
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
const aliasSymbol = type.aliasSymbol;
|
|
2402
|
+
if (aliasSymbol?.declarations) {
|
|
2403
|
+
return aliasSymbol.declarations.find(ts4.isTypeAliasDeclaration);
|
|
2404
|
+
}
|
|
2405
|
+
return void 0;
|
|
2406
|
+
}
|
|
2078
2407
|
function analyzeMethod(method, checker) {
|
|
2079
2408
|
if (!ts4.isIdentifier(method.name)) {
|
|
2080
2409
|
return null;
|
|
@@ -2115,12 +2444,18 @@ function detectFormSpecReference(typeNode) {
|
|
|
2115
2444
|
}
|
|
2116
2445
|
return null;
|
|
2117
2446
|
}
|
|
2118
|
-
var ts4, MAX_ALIAS_CHAIN_DEPTH;
|
|
2447
|
+
var ts4, RESOLVING_TYPE_PLACEHOLDER, MAX_ALIAS_CHAIN_DEPTH;
|
|
2119
2448
|
var init_class_analyzer = __esm({
|
|
2120
2449
|
"src/analyzer/class-analyzer.ts"() {
|
|
2121
2450
|
"use strict";
|
|
2122
2451
|
ts4 = __toESM(require("typescript"), 1);
|
|
2123
2452
|
init_jsdoc_constraints();
|
|
2453
|
+
init_tsdoc_parser();
|
|
2454
|
+
RESOLVING_TYPE_PLACEHOLDER = {
|
|
2455
|
+
kind: "object",
|
|
2456
|
+
properties: [],
|
|
2457
|
+
additionalProperties: true
|
|
2458
|
+
};
|
|
2124
2459
|
MAX_ALIAS_CHAIN_DEPTH = 8;
|
|
2125
2460
|
}
|
|
2126
2461
|
});
|
|
@@ -2178,10 +2513,220 @@ var init_class_schema = __esm({
|
|
|
2178
2513
|
}
|
|
2179
2514
|
});
|
|
2180
2515
|
|
|
2516
|
+
// src/generators/mixed-authoring.ts
|
|
2517
|
+
function buildMixedAuthoringSchemas(options) {
|
|
2518
|
+
const { filePath, typeName, overlays, ...schemaOptions } = options;
|
|
2519
|
+
const analysis = analyzeNamedType(filePath, typeName);
|
|
2520
|
+
const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays);
|
|
2521
|
+
const ir = canonicalizeTSDoc(composedAnalysis, { file: filePath });
|
|
2522
|
+
return {
|
|
2523
|
+
jsonSchema: generateJsonSchemaFromIR(ir, schemaOptions),
|
|
2524
|
+
uiSchema: generateUiSchemaFromIR(ir)
|
|
2525
|
+
};
|
|
2526
|
+
}
|
|
2527
|
+
function analyzeNamedType(filePath, typeName) {
|
|
2528
|
+
const ctx = createProgramContext(filePath);
|
|
2529
|
+
const source = { file: filePath };
|
|
2530
|
+
const classDecl = findClassByName(ctx.sourceFile, typeName);
|
|
2531
|
+
if (classDecl !== null) {
|
|
2532
|
+
return analyzeClassToIR(classDecl, ctx.checker, source.file);
|
|
2533
|
+
}
|
|
2534
|
+
const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
|
|
2535
|
+
if (interfaceDecl !== null) {
|
|
2536
|
+
return analyzeInterfaceToIR(interfaceDecl, ctx.checker, source.file);
|
|
2537
|
+
}
|
|
2538
|
+
const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
|
|
2539
|
+
if (typeAlias !== null) {
|
|
2540
|
+
const result = analyzeTypeAliasToIR(typeAlias, ctx.checker, source.file);
|
|
2541
|
+
if (result.ok) {
|
|
2542
|
+
return result.analysis;
|
|
2543
|
+
}
|
|
2544
|
+
throw new Error(result.error);
|
|
2545
|
+
}
|
|
2546
|
+
throw new Error(
|
|
2547
|
+
`Type "${typeName}" not found as a class, interface, or type alias in ${filePath}`
|
|
2548
|
+
);
|
|
2549
|
+
}
|
|
2550
|
+
function composeAnalysisWithOverlays(analysis, overlays) {
|
|
2551
|
+
const overlayIR = canonicalizeChainDSL(overlays);
|
|
2552
|
+
const overlayFields = collectOverlayFields(overlayIR.elements);
|
|
2553
|
+
if (overlayFields.length === 0) {
|
|
2554
|
+
return analysis;
|
|
2555
|
+
}
|
|
2556
|
+
const overlayByName = /* @__PURE__ */ new Map();
|
|
2557
|
+
for (const field of overlayFields) {
|
|
2558
|
+
if (overlayByName.has(field.name)) {
|
|
2559
|
+
throw new Error(`Mixed-authoring overlays define "${field.name}" more than once`);
|
|
2560
|
+
}
|
|
2561
|
+
overlayByName.set(field.name, field);
|
|
2562
|
+
}
|
|
2563
|
+
const mergedFields = [];
|
|
2564
|
+
for (const baseField of analysis.fields) {
|
|
2565
|
+
const overlayField = overlayByName.get(baseField.name);
|
|
2566
|
+
if (overlayField === void 0) {
|
|
2567
|
+
mergedFields.push(baseField);
|
|
2568
|
+
continue;
|
|
2569
|
+
}
|
|
2570
|
+
mergedFields.push(mergeFieldOverlay(baseField, overlayField, analysis.typeRegistry));
|
|
2571
|
+
overlayByName.delete(baseField.name);
|
|
2572
|
+
}
|
|
2573
|
+
if (overlayByName.size > 0) {
|
|
2574
|
+
const unknownFields = [...overlayByName.keys()].sort().join(", ");
|
|
2575
|
+
throw new Error(
|
|
2576
|
+
`Mixed-authoring overlays reference fields that are not present in the static model: ${unknownFields}`
|
|
2577
|
+
);
|
|
2578
|
+
}
|
|
2579
|
+
return {
|
|
2580
|
+
...analysis,
|
|
2581
|
+
fields: mergedFields
|
|
2582
|
+
};
|
|
2583
|
+
}
|
|
2584
|
+
function collectOverlayFields(elements) {
|
|
2585
|
+
const fields = [];
|
|
2586
|
+
for (const element of elements) {
|
|
2587
|
+
switch (element.kind) {
|
|
2588
|
+
case "field":
|
|
2589
|
+
fields.push(element);
|
|
2590
|
+
break;
|
|
2591
|
+
case "group":
|
|
2592
|
+
fields.push(...collectOverlayFields(element.elements));
|
|
2593
|
+
break;
|
|
2594
|
+
case "conditional":
|
|
2595
|
+
fields.push(...collectOverlayFields(element.elements));
|
|
2596
|
+
break;
|
|
2597
|
+
default: {
|
|
2598
|
+
const _exhaustive = element;
|
|
2599
|
+
void _exhaustive;
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
return fields;
|
|
2604
|
+
}
|
|
2605
|
+
function mergeFieldOverlay(baseField, overlayField, typeRegistry) {
|
|
2606
|
+
assertSupportedOverlayField(baseField, overlayField);
|
|
2607
|
+
return {
|
|
2608
|
+
...baseField,
|
|
2609
|
+
type: mergeFieldType(baseField, overlayField, typeRegistry),
|
|
2610
|
+
annotations: mergeAnnotations(baseField.annotations, overlayField.annotations)
|
|
2611
|
+
};
|
|
2612
|
+
}
|
|
2613
|
+
function assertSupportedOverlayField(baseField, overlayField) {
|
|
2614
|
+
if (overlayField.constraints.length > 0) {
|
|
2615
|
+
throw new Error(
|
|
2616
|
+
`Mixed-authoring overlay for "${baseField.name}" cannot define constraints; keep constraints on the static model`
|
|
2617
|
+
);
|
|
2618
|
+
}
|
|
2619
|
+
if (overlayField.required) {
|
|
2620
|
+
throw new Error(
|
|
2621
|
+
`Mixed-authoring overlay for "${baseField.name}" cannot change requiredness; keep requiredness on the static model`
|
|
2622
|
+
);
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
function mergeFieldType(baseField, overlayField, typeRegistry) {
|
|
2626
|
+
const { type: baseType } = baseField;
|
|
2627
|
+
const { type: overlayType } = overlayField;
|
|
2628
|
+
if (overlayType.kind === "object" || overlayType.kind === "array") {
|
|
2629
|
+
throw new Error(
|
|
2630
|
+
`Mixed-authoring overlays do not support nested object or array overlays for "${baseField.name}"`
|
|
2631
|
+
);
|
|
2632
|
+
}
|
|
2633
|
+
if (overlayType.kind === "dynamic") {
|
|
2634
|
+
if (!isCompatibleDynamicOverlay(baseField, overlayField, typeRegistry)) {
|
|
2635
|
+
throw new Error(
|
|
2636
|
+
`Mixed-authoring overlay for "${baseField.name}" is incompatible with the static field type`
|
|
2637
|
+
);
|
|
2638
|
+
}
|
|
2639
|
+
return overlayType;
|
|
2640
|
+
}
|
|
2641
|
+
if (!isSameStaticTypeShape(baseType, overlayType)) {
|
|
2642
|
+
throw new Error(
|
|
2643
|
+
`Mixed-authoring overlay for "${baseField.name}" must preserve the static field type`
|
|
2644
|
+
);
|
|
2645
|
+
}
|
|
2646
|
+
return baseType;
|
|
2647
|
+
}
|
|
2648
|
+
function isCompatibleDynamicOverlay(baseField, overlayField, typeRegistry) {
|
|
2649
|
+
const overlayType = overlayField.type;
|
|
2650
|
+
if (overlayType.kind !== "dynamic") {
|
|
2651
|
+
return false;
|
|
2652
|
+
}
|
|
2653
|
+
const resolvedBaseType = resolveReferenceType(baseField.type, typeRegistry);
|
|
2654
|
+
if (resolvedBaseType === null) {
|
|
2655
|
+
return false;
|
|
2656
|
+
}
|
|
2657
|
+
if (overlayType.dynamicKind === "enum") {
|
|
2658
|
+
return resolvedBaseType.kind === "primitive" ? resolvedBaseType.primitiveKind === "string" : resolvedBaseType.kind === "enum";
|
|
2659
|
+
}
|
|
2660
|
+
return resolvedBaseType.kind === "object" || resolvedBaseType.kind === "record";
|
|
2661
|
+
}
|
|
2662
|
+
function resolveReferenceType(type, typeRegistry, seen = /* @__PURE__ */ new Set()) {
|
|
2663
|
+
if (type.kind !== "reference") {
|
|
2664
|
+
return type;
|
|
2665
|
+
}
|
|
2666
|
+
if (seen.has(type.name)) {
|
|
2667
|
+
return null;
|
|
2668
|
+
}
|
|
2669
|
+
const definition = typeRegistry[type.name];
|
|
2670
|
+
if (definition === void 0) {
|
|
2671
|
+
return null;
|
|
2672
|
+
}
|
|
2673
|
+
seen.add(type.name);
|
|
2674
|
+
return resolveReferenceType(definition.type, typeRegistry, seen);
|
|
2675
|
+
}
|
|
2676
|
+
function isSameStaticTypeShape(baseType, overlayType) {
|
|
2677
|
+
if (baseType.kind !== overlayType.kind) {
|
|
2678
|
+
return false;
|
|
2679
|
+
}
|
|
2680
|
+
switch (baseType.kind) {
|
|
2681
|
+
case "primitive":
|
|
2682
|
+
return overlayType.kind === "primitive" && baseType.primitiveKind === overlayType.primitiveKind;
|
|
2683
|
+
case "enum":
|
|
2684
|
+
return overlayType.kind === "enum";
|
|
2685
|
+
case "dynamic":
|
|
2686
|
+
return overlayType.kind === "dynamic" && baseType.dynamicKind === overlayType.dynamicKind && baseType.sourceKey === overlayType.sourceKey;
|
|
2687
|
+
case "record":
|
|
2688
|
+
return overlayType.kind === "record";
|
|
2689
|
+
case "reference":
|
|
2690
|
+
return overlayType.kind === "reference" && baseType.name === overlayType.name;
|
|
2691
|
+
case "union":
|
|
2692
|
+
return overlayType.kind === "union";
|
|
2693
|
+
case "custom":
|
|
2694
|
+
return overlayType.kind === "custom" && baseType.typeId === overlayType.typeId;
|
|
2695
|
+
case "object":
|
|
2696
|
+
case "array":
|
|
2697
|
+
return true;
|
|
2698
|
+
default: {
|
|
2699
|
+
const _exhaustive = baseType;
|
|
2700
|
+
return _exhaustive;
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
function mergeAnnotations(baseAnnotations, overlayAnnotations) {
|
|
2705
|
+
const baseKeys = new Set(baseAnnotations.map(annotationKey));
|
|
2706
|
+
const overlayOnly = overlayAnnotations.filter(
|
|
2707
|
+
(annotation) => !baseKeys.has(annotationKey(annotation))
|
|
2708
|
+
);
|
|
2709
|
+
return [...overlayOnly, ...baseAnnotations];
|
|
2710
|
+
}
|
|
2711
|
+
function annotationKey(annotation) {
|
|
2712
|
+
return annotation.annotationKind === "custom" ? `${annotation.annotationKind}:${annotation.annotationId}` : annotation.annotationKind;
|
|
2713
|
+
}
|
|
2714
|
+
var init_mixed_authoring = __esm({
|
|
2715
|
+
"src/generators/mixed-authoring.ts"() {
|
|
2716
|
+
"use strict";
|
|
2717
|
+
init_ir_generator();
|
|
2718
|
+
init_ir_generator2();
|
|
2719
|
+
init_canonicalize();
|
|
2720
|
+
init_program();
|
|
2721
|
+
init_class_analyzer();
|
|
2722
|
+
}
|
|
2723
|
+
});
|
|
2724
|
+
|
|
2181
2725
|
// src/index.ts
|
|
2182
2726
|
var index_exports = {};
|
|
2183
2727
|
__export(index_exports, {
|
|
2184
2728
|
buildFormSchemas: () => buildFormSchemas,
|
|
2729
|
+
buildMixedAuthoringSchemas: () => buildMixedAuthoringSchemas,
|
|
2185
2730
|
categorizationSchema: () => categorizationSchema,
|
|
2186
2731
|
categorySchema: () => categorySchema,
|
|
2187
2732
|
controlSchema: () => controlSchema,
|
|
@@ -2247,6 +2792,7 @@ var init_index = __esm({
|
|
|
2247
2792
|
init_ir_generator();
|
|
2248
2793
|
init_generator2();
|
|
2249
2794
|
init_class_schema();
|
|
2795
|
+
init_mixed_authoring();
|
|
2250
2796
|
}
|
|
2251
2797
|
});
|
|
2252
2798
|
|