@formspec/build 0.1.0-alpha.17 → 0.1.0-alpha.19
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/README.md +74 -128
- package/dist/__tests__/class-schema.test.d.ts +2 -0
- package/dist/__tests__/class-schema.test.d.ts.map +1 -0
- package/dist/__tests__/date-extension.integration.test.d.ts +2 -0
- package/dist/__tests__/date-extension.integration.test.d.ts.map +1 -0
- package/dist/__tests__/fixtures/class-schema-regressions.d.ts +83 -0
- package/dist/__tests__/fixtures/class-schema-regressions.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-date-extension.d.ts +12 -0
- package/dist/__tests__/fixtures/example-date-extension.d.ts.map +1 -0
- package/dist/__tests__/fixtures/extension-forms.d.ts +7 -0
- package/dist/__tests__/fixtures/extension-forms.d.ts.map +1 -0
- package/dist/__tests__/fixtures/mixed-authoring-shipping-address.d.ts.map +1 -1
- package/dist/__tests__/fixtures/named-primitive-aliases.d.ts +15 -0
- package/dist/__tests__/fixtures/named-primitive-aliases.d.ts.map +1 -0
- package/dist/__tests__/fixtures/nested-array-path-constraints.d.ts +14 -0
- package/dist/__tests__/fixtures/nested-array-path-constraints.d.ts.map +1 -0
- package/dist/__tests__/fixtures/sample-forms.d.ts +10 -0
- package/dist/__tests__/fixtures/sample-forms.d.ts.map +1 -1
- package/dist/__tests__/generate-schemas.test.d.ts +2 -0
- package/dist/__tests__/generate-schemas.test.d.ts.map +1 -0
- package/dist/__tests__/parity/parity.test.d.ts +6 -2
- package/dist/__tests__/parity/parity.test.d.ts.map +1 -1
- package/dist/__tests__/parity/utils.d.ts +9 -4
- package/dist/__tests__/parity/utils.d.ts.map +1 -1
- package/dist/analyzer/class-analyzer.d.ts.map +1 -1
- package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -1
- package/dist/analyzer/program.d.ts +15 -0
- package/dist/analyzer/program.d.ts.map +1 -1
- package/dist/analyzer/tsdoc-parser.d.ts +5 -0
- package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
- package/dist/browser.cjs +73 -10
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +73 -10
- package/dist/browser.js.map +1 -1
- package/dist/canonicalize/chain-dsl-canonicalizer.d.ts.map +1 -1
- package/dist/canonicalize/tsdoc-canonicalizer.d.ts.map +1 -1
- package/dist/cli.cjs +1147 -252
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1142 -248
- package/dist/cli.js.map +1 -1
- package/dist/extensions/registry.d.ts.map +1 -1
- package/dist/generators/class-schema.d.ts.map +1 -1
- package/dist/generators/method-schema.d.ts.map +1 -1
- package/dist/generators/mixed-authoring.d.ts.map +1 -1
- package/dist/index.cjs +1121 -239
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1121 -239
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +377 -195
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +377 -195
- package/dist/internals.js.map +1 -1
- package/dist/json-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/internals.cjs
CHANGED
|
@@ -71,6 +71,7 @@ function canonicalizeChainDSL(form) {
|
|
|
71
71
|
kind: "form-ir",
|
|
72
72
|
irVersion: import_core.IR_VERSION,
|
|
73
73
|
elements: canonicalizeElements(form.elements),
|
|
74
|
+
rootAnnotations: [],
|
|
74
75
|
typeRegistry: {},
|
|
75
76
|
provenance: CHAIN_DSL_PROVENANCE
|
|
76
77
|
};
|
|
@@ -376,6 +377,7 @@ function canonicalizeTSDoc(analysis, source) {
|
|
|
376
377
|
irVersion: import_core2.IR_VERSION,
|
|
377
378
|
elements,
|
|
378
379
|
typeRegistry: analysis.typeRegistry,
|
|
380
|
+
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { rootAnnotations: analysis.annotations },
|
|
379
381
|
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { annotations: analysis.annotations },
|
|
380
382
|
provenance
|
|
381
383
|
};
|
|
@@ -436,85 +438,17 @@ function wrapInConditional(field, layout, provenance) {
|
|
|
436
438
|
}
|
|
437
439
|
|
|
438
440
|
// src/analyzer/program.ts
|
|
439
|
-
var
|
|
441
|
+
var ts4 = __toESM(require("typescript"), 1);
|
|
440
442
|
var path = __toESM(require("path"), 1);
|
|
441
|
-
function createProgramContext(filePath) {
|
|
442
|
-
const absolutePath = path.resolve(filePath);
|
|
443
|
-
const fileDir = path.dirname(absolutePath);
|
|
444
|
-
const configPath = ts.findConfigFile(fileDir, ts.sys.fileExists.bind(ts.sys), "tsconfig.json");
|
|
445
|
-
let compilerOptions;
|
|
446
|
-
let fileNames;
|
|
447
|
-
if (configPath) {
|
|
448
|
-
const configFile = ts.readConfigFile(configPath, ts.sys.readFile.bind(ts.sys));
|
|
449
|
-
if (configFile.error) {
|
|
450
|
-
throw new Error(
|
|
451
|
-
`Error reading tsconfig.json: ${ts.flattenDiagnosticMessageText(configFile.error.messageText, "\n")}`
|
|
452
|
-
);
|
|
453
|
-
}
|
|
454
|
-
const parsed = ts.parseJsonConfigFileContent(
|
|
455
|
-
configFile.config,
|
|
456
|
-
ts.sys,
|
|
457
|
-
path.dirname(configPath)
|
|
458
|
-
);
|
|
459
|
-
if (parsed.errors.length > 0) {
|
|
460
|
-
const errorMessages = parsed.errors.map((e) => ts.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n");
|
|
461
|
-
throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
|
|
462
|
-
}
|
|
463
|
-
compilerOptions = parsed.options;
|
|
464
|
-
fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
|
|
465
|
-
} else {
|
|
466
|
-
compilerOptions = {
|
|
467
|
-
target: ts.ScriptTarget.ES2022,
|
|
468
|
-
module: ts.ModuleKind.NodeNext,
|
|
469
|
-
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
470
|
-
strict: true,
|
|
471
|
-
skipLibCheck: true,
|
|
472
|
-
declaration: true
|
|
473
|
-
};
|
|
474
|
-
fileNames = [absolutePath];
|
|
475
|
-
}
|
|
476
|
-
const program = ts.createProgram(fileNames, compilerOptions);
|
|
477
|
-
const sourceFile = program.getSourceFile(absolutePath);
|
|
478
|
-
if (!sourceFile) {
|
|
479
|
-
throw new Error(`Could not find source file: ${absolutePath}`);
|
|
480
|
-
}
|
|
481
|
-
return {
|
|
482
|
-
program,
|
|
483
|
-
checker: program.getTypeChecker(),
|
|
484
|
-
sourceFile
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
function findNodeByName(sourceFile, name, predicate, getName) {
|
|
488
|
-
let result = null;
|
|
489
|
-
function visit(node) {
|
|
490
|
-
if (result) return;
|
|
491
|
-
if (predicate(node) && getName(node) === name) {
|
|
492
|
-
result = node;
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
ts.forEachChild(node, visit);
|
|
496
|
-
}
|
|
497
|
-
visit(sourceFile);
|
|
498
|
-
return result;
|
|
499
|
-
}
|
|
500
|
-
function findClassByName(sourceFile, className) {
|
|
501
|
-
return findNodeByName(sourceFile, className, ts.isClassDeclaration, (n) => n.name?.text);
|
|
502
|
-
}
|
|
503
|
-
function findInterfaceByName(sourceFile, interfaceName) {
|
|
504
|
-
return findNodeByName(sourceFile, interfaceName, ts.isInterfaceDeclaration, (n) => n.name.text);
|
|
505
|
-
}
|
|
506
|
-
function findTypeAliasByName(sourceFile, aliasName) {
|
|
507
|
-
return findNodeByName(sourceFile, aliasName, ts.isTypeAliasDeclaration, (n) => n.name.text);
|
|
508
|
-
}
|
|
509
443
|
|
|
510
444
|
// src/analyzer/class-analyzer.ts
|
|
511
|
-
var
|
|
445
|
+
var ts3 = __toESM(require("typescript"), 1);
|
|
512
446
|
|
|
513
447
|
// src/analyzer/jsdoc-constraints.ts
|
|
514
|
-
var
|
|
448
|
+
var ts2 = __toESM(require("typescript"), 1);
|
|
515
449
|
|
|
516
450
|
// src/analyzer/tsdoc-parser.ts
|
|
517
|
-
var
|
|
451
|
+
var ts = __toESM(require("typescript"), 1);
|
|
518
452
|
var import_tsdoc = require("@microsoft/tsdoc");
|
|
519
453
|
var import_core3 = require("@formspec/core");
|
|
520
454
|
|
|
@@ -600,10 +534,10 @@ function parseTSDocTags(node, file = "", options) {
|
|
|
600
534
|
let placeholderProvenance;
|
|
601
535
|
const sourceFile = node.getSourceFile();
|
|
602
536
|
const sourceText = sourceFile.getFullText();
|
|
603
|
-
const commentRanges =
|
|
537
|
+
const commentRanges = ts.getLeadingCommentRanges(sourceText, node.getFullStart());
|
|
604
538
|
if (commentRanges) {
|
|
605
539
|
for (const range of commentRanges) {
|
|
606
|
-
if (range.kind !==
|
|
540
|
+
if (range.kind !== ts.SyntaxKind.MultiLineCommentTrivia) {
|
|
607
541
|
continue;
|
|
608
542
|
}
|
|
609
543
|
const commentText = sourceText.substring(range.pos, range.end);
|
|
@@ -621,26 +555,31 @@ function parseTSDocTags(node, file = "", options) {
|
|
|
621
555
|
const text2 = extractBlockText(block).trim();
|
|
622
556
|
if (text2 === "") continue;
|
|
623
557
|
const provenance2 = provenanceForComment(range, sourceFile, file, tagName);
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
displayName
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
558
|
+
switch (tagName) {
|
|
559
|
+
case "displayName":
|
|
560
|
+
if (!isMemberTargetDisplayName(text2) && displayName === void 0) {
|
|
561
|
+
displayName = text2;
|
|
562
|
+
displayNameProvenance = provenance2;
|
|
563
|
+
}
|
|
564
|
+
break;
|
|
565
|
+
case "format":
|
|
566
|
+
annotations.push({
|
|
567
|
+
kind: "annotation",
|
|
568
|
+
annotationKind: "format",
|
|
569
|
+
value: text2,
|
|
570
|
+
provenance: provenance2
|
|
571
|
+
});
|
|
572
|
+
break;
|
|
573
|
+
case "description":
|
|
638
574
|
description = text2;
|
|
639
575
|
descriptionProvenance = provenance2;
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
576
|
+
break;
|
|
577
|
+
case "placeholder":
|
|
578
|
+
if (placeholder === void 0) {
|
|
579
|
+
placeholder = text2;
|
|
580
|
+
placeholderProvenance = provenance2;
|
|
581
|
+
}
|
|
582
|
+
break;
|
|
644
583
|
}
|
|
645
584
|
continue;
|
|
646
585
|
}
|
|
@@ -670,6 +609,13 @@ function parseTSDocTags(node, file = "", options) {
|
|
|
670
609
|
descriptionProvenance = provenanceForComment(range, sourceFile, file, "remarks");
|
|
671
610
|
}
|
|
672
611
|
}
|
|
612
|
+
if (description === void 0) {
|
|
613
|
+
const summary = extractPlainText(docComment.summarySection).trim();
|
|
614
|
+
if (summary !== "") {
|
|
615
|
+
description = summary;
|
|
616
|
+
descriptionProvenance = provenanceForComment(range, sourceFile, file, "summary");
|
|
617
|
+
}
|
|
618
|
+
}
|
|
673
619
|
}
|
|
674
620
|
}
|
|
675
621
|
if (displayName !== void 0 && displayNameProvenance !== void 0) {
|
|
@@ -696,7 +642,7 @@ function parseTSDocTags(node, file = "", options) {
|
|
|
696
642
|
provenance: placeholderProvenance
|
|
697
643
|
});
|
|
698
644
|
}
|
|
699
|
-
const jsDocTagsAll =
|
|
645
|
+
const jsDocTagsAll = ts.getJSDocTags(node);
|
|
700
646
|
for (const tag of jsDocTagsAll) {
|
|
701
647
|
const tagName = (0, import_core3.normalizeConstraintTagName)(tag.tagName.text);
|
|
702
648
|
if (!TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
|
|
@@ -719,7 +665,7 @@ function parseTSDocTags(node, file = "", options) {
|
|
|
719
665
|
function extractDisplayNameMetadata(node) {
|
|
720
666
|
let displayName;
|
|
721
667
|
const memberDisplayNames = /* @__PURE__ */ new Map();
|
|
722
|
-
for (const tag of
|
|
668
|
+
for (const tag of ts.getJSDocTags(node)) {
|
|
723
669
|
const tagName = (0, import_core3.normalizeConstraintTagName)(tag.tagName.text);
|
|
724
670
|
if (tagName !== "displayName") continue;
|
|
725
671
|
const commentText = getTagCommentText(tag);
|
|
@@ -740,11 +686,11 @@ function extractDisplayNameMetadata(node) {
|
|
|
740
686
|
}
|
|
741
687
|
function extractPathTarget(text) {
|
|
742
688
|
const trimmed = text.trimStart();
|
|
743
|
-
const match = /^:([a-zA-Z_]\w*)
|
|
744
|
-
if (!match?.[1]
|
|
689
|
+
const match = /^:([a-zA-Z_]\w*)(?:\s+([\s\S]*))?$/.exec(trimmed);
|
|
690
|
+
if (!match?.[1]) return null;
|
|
745
691
|
return {
|
|
746
692
|
path: { segments: [match[1]] },
|
|
747
|
-
remainingText: match[2]
|
|
693
|
+
remainingText: match[2] ?? ""
|
|
748
694
|
};
|
|
749
695
|
}
|
|
750
696
|
function extractBlockText(block) {
|
|
@@ -1007,7 +953,7 @@ function getTagCommentText(tag) {
|
|
|
1007
953
|
if (typeof tag.comment === "string") {
|
|
1008
954
|
return tag.comment;
|
|
1009
955
|
}
|
|
1010
|
-
return
|
|
956
|
+
return ts.getTextOfJSDocComment(tag.comment);
|
|
1011
957
|
}
|
|
1012
958
|
|
|
1013
959
|
// src/analyzer/jsdoc-constraints.ts
|
|
@@ -1022,18 +968,18 @@ function extractJSDocAnnotationNodes(node, file = "", options) {
|
|
|
1022
968
|
function extractDefaultValueAnnotation(initializer, file = "") {
|
|
1023
969
|
if (!initializer) return null;
|
|
1024
970
|
let value;
|
|
1025
|
-
if (
|
|
971
|
+
if (ts2.isStringLiteral(initializer)) {
|
|
1026
972
|
value = initializer.text;
|
|
1027
|
-
} else if (
|
|
973
|
+
} else if (ts2.isNumericLiteral(initializer)) {
|
|
1028
974
|
value = Number(initializer.text);
|
|
1029
|
-
} else if (initializer.kind ===
|
|
975
|
+
} else if (initializer.kind === ts2.SyntaxKind.TrueKeyword) {
|
|
1030
976
|
value = true;
|
|
1031
|
-
} else if (initializer.kind ===
|
|
977
|
+
} else if (initializer.kind === ts2.SyntaxKind.FalseKeyword) {
|
|
1032
978
|
value = false;
|
|
1033
|
-
} else if (initializer.kind ===
|
|
979
|
+
} else if (initializer.kind === ts2.SyntaxKind.NullKeyword) {
|
|
1034
980
|
value = null;
|
|
1035
|
-
} else if (
|
|
1036
|
-
if (initializer.operator ===
|
|
981
|
+
} else if (ts2.isPrefixUnaryExpression(initializer)) {
|
|
982
|
+
if (initializer.operator === ts2.SyntaxKind.MinusToken && ts2.isNumericLiteral(initializer.operand)) {
|
|
1037
983
|
value = -Number(initializer.operand.text);
|
|
1038
984
|
}
|
|
1039
985
|
}
|
|
@@ -1055,10 +1001,10 @@ function extractDefaultValueAnnotation(initializer, file = "") {
|
|
|
1055
1001
|
|
|
1056
1002
|
// src/analyzer/class-analyzer.ts
|
|
1057
1003
|
function isObjectType(type) {
|
|
1058
|
-
return !!(type.flags &
|
|
1004
|
+
return !!(type.flags & ts3.TypeFlags.Object);
|
|
1059
1005
|
}
|
|
1060
1006
|
function isTypeReference(type) {
|
|
1061
|
-
return !!(type.flags &
|
|
1007
|
+
return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
|
|
1062
1008
|
}
|
|
1063
1009
|
var RESOLVING_TYPE_PLACEHOLDER = {
|
|
1064
1010
|
kind: "object",
|
|
@@ -1088,7 +1034,7 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
1088
1034
|
const instanceMethods = [];
|
|
1089
1035
|
const staticMethods = [];
|
|
1090
1036
|
for (const member of classDecl.members) {
|
|
1091
|
-
if (
|
|
1037
|
+
if (ts3.isPropertyDeclaration(member)) {
|
|
1092
1038
|
const fieldNode = analyzeFieldToIR(
|
|
1093
1039
|
member,
|
|
1094
1040
|
checker,
|
|
@@ -1101,10 +1047,10 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
1101
1047
|
fields.push(fieldNode);
|
|
1102
1048
|
fieldLayouts.push({});
|
|
1103
1049
|
}
|
|
1104
|
-
} else if (
|
|
1050
|
+
} else if (ts3.isMethodDeclaration(member)) {
|
|
1105
1051
|
const methodInfo = analyzeMethod(member, checker);
|
|
1106
1052
|
if (methodInfo) {
|
|
1107
|
-
const isStatic = member.modifiers?.some((m) => m.kind ===
|
|
1053
|
+
const isStatic = member.modifiers?.some((m) => m.kind === ts3.SyntaxKind.StaticKeyword);
|
|
1108
1054
|
if (isStatic) {
|
|
1109
1055
|
staticMethods.push(methodInfo);
|
|
1110
1056
|
} else {
|
|
@@ -1134,7 +1080,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
1134
1080
|
);
|
|
1135
1081
|
const visiting = /* @__PURE__ */ new Set();
|
|
1136
1082
|
for (const member of interfaceDecl.members) {
|
|
1137
|
-
if (
|
|
1083
|
+
if (ts3.isPropertySignature(member)) {
|
|
1138
1084
|
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1139
1085
|
member,
|
|
1140
1086
|
checker,
|
|
@@ -1160,10 +1106,10 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
1160
1106
|
};
|
|
1161
1107
|
}
|
|
1162
1108
|
function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
|
|
1163
|
-
if (!
|
|
1109
|
+
if (!ts3.isTypeLiteralNode(typeAlias.type)) {
|
|
1164
1110
|
const sourceFile = typeAlias.getSourceFile();
|
|
1165
1111
|
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
1166
|
-
const kindDesc =
|
|
1112
|
+
const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
|
|
1167
1113
|
return {
|
|
1168
1114
|
ok: false,
|
|
1169
1115
|
error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object type literal (found ${kindDesc})`
|
|
@@ -1179,7 +1125,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
1179
1125
|
);
|
|
1180
1126
|
const visiting = /* @__PURE__ */ new Set();
|
|
1181
1127
|
for (const member of typeAlias.type.members) {
|
|
1182
|
-
if (
|
|
1128
|
+
if (ts3.isPropertySignature(member)) {
|
|
1183
1129
|
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1184
1130
|
member,
|
|
1185
1131
|
checker,
|
|
@@ -1207,7 +1153,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
1207
1153
|
};
|
|
1208
1154
|
}
|
|
1209
1155
|
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1210
|
-
if (!
|
|
1156
|
+
if (!ts3.isIdentifier(prop.name)) {
|
|
1211
1157
|
return null;
|
|
1212
1158
|
}
|
|
1213
1159
|
const name = prop.name.text;
|
|
@@ -1224,12 +1170,14 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extension
|
|
|
1224
1170
|
extensionRegistry
|
|
1225
1171
|
);
|
|
1226
1172
|
const constraints = [];
|
|
1227
|
-
if (prop.type) {
|
|
1173
|
+
if (prop.type && !shouldEmitPrimitiveAliasDefinition(prop.type, checker)) {
|
|
1228
1174
|
constraints.push(
|
|
1229
1175
|
...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
|
|
1230
1176
|
);
|
|
1231
1177
|
}
|
|
1232
|
-
constraints.push(
|
|
1178
|
+
constraints.push(
|
|
1179
|
+
...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
1180
|
+
);
|
|
1233
1181
|
let annotations = [];
|
|
1234
1182
|
annotations.push(
|
|
1235
1183
|
...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
@@ -1250,7 +1198,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extension
|
|
|
1250
1198
|
};
|
|
1251
1199
|
}
|
|
1252
1200
|
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1253
|
-
if (!
|
|
1201
|
+
if (!ts3.isIdentifier(prop.name)) {
|
|
1254
1202
|
return null;
|
|
1255
1203
|
}
|
|
1256
1204
|
const name = prop.name.text;
|
|
@@ -1267,12 +1215,14 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
1267
1215
|
extensionRegistry
|
|
1268
1216
|
);
|
|
1269
1217
|
const constraints = [];
|
|
1270
|
-
if (prop.type) {
|
|
1218
|
+
if (prop.type && !shouldEmitPrimitiveAliasDefinition(prop.type, checker)) {
|
|
1271
1219
|
constraints.push(
|
|
1272
1220
|
...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
|
|
1273
1221
|
);
|
|
1274
1222
|
}
|
|
1275
|
-
constraints.push(
|
|
1223
|
+
constraints.push(
|
|
1224
|
+
...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
1225
|
+
);
|
|
1276
1226
|
let annotations = [];
|
|
1277
1227
|
annotations.push(
|
|
1278
1228
|
...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
@@ -1361,7 +1311,7 @@ function resolveRegisteredCustomType(sourceNode, extensionRegistry, checker) {
|
|
|
1361
1311
|
return resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker);
|
|
1362
1312
|
}
|
|
1363
1313
|
function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker) {
|
|
1364
|
-
if (
|
|
1314
|
+
if (ts3.isParenthesizedTypeNode(typeNode)) {
|
|
1365
1315
|
return resolveRegisteredCustomTypeFromTypeNode(typeNode.type, extensionRegistry, checker);
|
|
1366
1316
|
}
|
|
1367
1317
|
const typeName = getTypeNodeRegistrationName(typeNode);
|
|
@@ -1376,8 +1326,8 @@ function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, ch
|
|
|
1376
1326
|
payload: null
|
|
1377
1327
|
};
|
|
1378
1328
|
}
|
|
1379
|
-
if (
|
|
1380
|
-
const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(
|
|
1329
|
+
if (ts3.isTypeReferenceNode(typeNode) && ts3.isIdentifier(typeNode.typeName)) {
|
|
1330
|
+
const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
|
|
1381
1331
|
if (aliasDecl !== void 0) {
|
|
1382
1332
|
return resolveRegisteredCustomTypeFromTypeNode(aliasDecl.type, extensionRegistry, checker);
|
|
1383
1333
|
}
|
|
@@ -1385,22 +1335,22 @@ function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, ch
|
|
|
1385
1335
|
return null;
|
|
1386
1336
|
}
|
|
1387
1337
|
function extractTypeNodeFromSource(sourceNode) {
|
|
1388
|
-
if (
|
|
1338
|
+
if (ts3.isPropertyDeclaration(sourceNode) || ts3.isPropertySignature(sourceNode) || ts3.isParameter(sourceNode) || ts3.isTypeAliasDeclaration(sourceNode)) {
|
|
1389
1339
|
return sourceNode.type;
|
|
1390
1340
|
}
|
|
1391
|
-
if (
|
|
1341
|
+
if (ts3.isTypeNode(sourceNode)) {
|
|
1392
1342
|
return sourceNode;
|
|
1393
1343
|
}
|
|
1394
1344
|
return void 0;
|
|
1395
1345
|
}
|
|
1396
1346
|
function getTypeNodeRegistrationName(typeNode) {
|
|
1397
|
-
if (
|
|
1398
|
-
return
|
|
1347
|
+
if (ts3.isTypeReferenceNode(typeNode)) {
|
|
1348
|
+
return ts3.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : typeNode.typeName.right.text;
|
|
1399
1349
|
}
|
|
1400
|
-
if (
|
|
1350
|
+
if (ts3.isParenthesizedTypeNode(typeNode)) {
|
|
1401
1351
|
return getTypeNodeRegistrationName(typeNode.type);
|
|
1402
1352
|
}
|
|
1403
|
-
if (typeNode.kind ===
|
|
1353
|
+
if (typeNode.kind === ts3.SyntaxKind.BigIntKeyword || typeNode.kind === ts3.SyntaxKind.StringKeyword || typeNode.kind === ts3.SyntaxKind.NumberKeyword || typeNode.kind === ts3.SyntaxKind.BooleanKeyword) {
|
|
1404
1354
|
return typeNode.getText();
|
|
1405
1355
|
}
|
|
1406
1356
|
return null;
|
|
@@ -1410,19 +1360,34 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
1410
1360
|
if (customType) {
|
|
1411
1361
|
return customType;
|
|
1412
1362
|
}
|
|
1413
|
-
|
|
1363
|
+
const primitiveAlias = tryResolveNamedPrimitiveAlias(
|
|
1364
|
+
type,
|
|
1365
|
+
checker,
|
|
1366
|
+
file,
|
|
1367
|
+
typeRegistry,
|
|
1368
|
+
visiting,
|
|
1369
|
+
sourceNode,
|
|
1370
|
+
extensionRegistry
|
|
1371
|
+
);
|
|
1372
|
+
if (primitiveAlias) {
|
|
1373
|
+
return primitiveAlias;
|
|
1374
|
+
}
|
|
1375
|
+
if (type.flags & ts3.TypeFlags.String) {
|
|
1414
1376
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1415
1377
|
}
|
|
1416
|
-
if (type.flags &
|
|
1378
|
+
if (type.flags & ts3.TypeFlags.Number) {
|
|
1417
1379
|
return { kind: "primitive", primitiveKind: "number" };
|
|
1418
1380
|
}
|
|
1419
|
-
if (type.flags &
|
|
1381
|
+
if (type.flags & (ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral)) {
|
|
1382
|
+
return { kind: "primitive", primitiveKind: "bigint" };
|
|
1383
|
+
}
|
|
1384
|
+
if (type.flags & ts3.TypeFlags.Boolean) {
|
|
1420
1385
|
return { kind: "primitive", primitiveKind: "boolean" };
|
|
1421
1386
|
}
|
|
1422
|
-
if (type.flags &
|
|
1387
|
+
if (type.flags & ts3.TypeFlags.Null) {
|
|
1423
1388
|
return { kind: "primitive", primitiveKind: "null" };
|
|
1424
1389
|
}
|
|
1425
|
-
if (type.flags &
|
|
1390
|
+
if (type.flags & ts3.TypeFlags.Undefined) {
|
|
1426
1391
|
return { kind: "primitive", primitiveKind: "null" };
|
|
1427
1392
|
}
|
|
1428
1393
|
if (type.isStringLiteral()) {
|
|
@@ -1464,6 +1429,75 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
1464
1429
|
}
|
|
1465
1430
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1466
1431
|
}
|
|
1432
|
+
function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
1433
|
+
if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
|
|
1434
|
+
return null;
|
|
1435
|
+
}
|
|
1436
|
+
const aliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration) ?? getReferencedTypeAliasDeclaration(sourceNode, checker);
|
|
1437
|
+
if (!aliasDecl) {
|
|
1438
|
+
return null;
|
|
1439
|
+
}
|
|
1440
|
+
const aliasName = aliasDecl.name.text;
|
|
1441
|
+
if (!typeRegistry[aliasName]) {
|
|
1442
|
+
const aliasType = checker.getTypeFromTypeNode(aliasDecl.type);
|
|
1443
|
+
const constraints = [
|
|
1444
|
+
...extractJSDocConstraintNodes(aliasDecl, file, makeParseOptions(extensionRegistry)),
|
|
1445
|
+
...extractTypeAliasConstraintNodes(aliasDecl.type, checker, file, extensionRegistry)
|
|
1446
|
+
];
|
|
1447
|
+
const annotations = extractJSDocAnnotationNodes(
|
|
1448
|
+
aliasDecl,
|
|
1449
|
+
file,
|
|
1450
|
+
makeParseOptions(extensionRegistry)
|
|
1451
|
+
);
|
|
1452
|
+
typeRegistry[aliasName] = {
|
|
1453
|
+
name: aliasName,
|
|
1454
|
+
type: resolveAliasedPrimitiveTarget(
|
|
1455
|
+
aliasType,
|
|
1456
|
+
checker,
|
|
1457
|
+
file,
|
|
1458
|
+
typeRegistry,
|
|
1459
|
+
visiting,
|
|
1460
|
+
extensionRegistry
|
|
1461
|
+
),
|
|
1462
|
+
...constraints.length > 0 && { constraints },
|
|
1463
|
+
...annotations.length > 0 && { annotations },
|
|
1464
|
+
provenance: provenanceForDeclaration(aliasDecl, file)
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
return { kind: "reference", name: aliasName, typeArguments: [] };
|
|
1468
|
+
}
|
|
1469
|
+
function getReferencedTypeAliasDeclaration(sourceNode, checker) {
|
|
1470
|
+
const typeNode = sourceNode && (ts3.isPropertyDeclaration(sourceNode) || ts3.isPropertySignature(sourceNode) || ts3.isParameter(sourceNode)) ? sourceNode.type : void 0;
|
|
1471
|
+
if (!typeNode || !ts3.isTypeReferenceNode(typeNode)) {
|
|
1472
|
+
return void 0;
|
|
1473
|
+
}
|
|
1474
|
+
return checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
|
|
1475
|
+
}
|
|
1476
|
+
function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
|
|
1477
|
+
if (!ts3.isTypeReferenceNode(typeNode)) {
|
|
1478
|
+
return false;
|
|
1479
|
+
}
|
|
1480
|
+
const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
|
|
1481
|
+
if (!aliasDecl) {
|
|
1482
|
+
return false;
|
|
1483
|
+
}
|
|
1484
|
+
const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
|
|
1485
|
+
return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
|
|
1486
|
+
}
|
|
1487
|
+
function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1488
|
+
const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
|
|
1489
|
+
if (nestedAliasDecl !== void 0) {
|
|
1490
|
+
return resolveAliasedPrimitiveTarget(
|
|
1491
|
+
checker.getTypeFromTypeNode(nestedAliasDecl.type),
|
|
1492
|
+
checker,
|
|
1493
|
+
file,
|
|
1494
|
+
typeRegistry,
|
|
1495
|
+
visiting,
|
|
1496
|
+
extensionRegistry
|
|
1497
|
+
);
|
|
1498
|
+
}
|
|
1499
|
+
return resolveTypeNode(type, checker, file, typeRegistry, visiting, void 0, extensionRegistry);
|
|
1500
|
+
}
|
|
1467
1501
|
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
1468
1502
|
const typeName = getNamedTypeName(type);
|
|
1469
1503
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
@@ -1476,13 +1510,13 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
1476
1510
|
(memberTypeNode) => !isNullishTypeNode(resolveAliasedTypeNode(memberTypeNode, checker))
|
|
1477
1511
|
);
|
|
1478
1512
|
const nonNullTypes = allTypes.filter(
|
|
1479
|
-
(memberType) => !(memberType.flags & (
|
|
1513
|
+
(memberType) => !(memberType.flags & (ts3.TypeFlags.Null | ts3.TypeFlags.Undefined))
|
|
1480
1514
|
);
|
|
1481
1515
|
const nonNullMembers = nonNullTypes.map((memberType, index) => ({
|
|
1482
1516
|
memberType,
|
|
1483
1517
|
sourceNode: nonNullSourceNodes.length === nonNullTypes.length ? nonNullSourceNodes[index] : void 0
|
|
1484
1518
|
}));
|
|
1485
|
-
const hasNull = allTypes.some((t) => t.flags &
|
|
1519
|
+
const hasNull = allTypes.some((t) => t.flags & ts3.TypeFlags.Null);
|
|
1486
1520
|
const memberDisplayNames = /* @__PURE__ */ new Map();
|
|
1487
1521
|
if (namedDecl) {
|
|
1488
1522
|
for (const [value, label] of extractDisplayNameMetadata(namedDecl).memberDisplayNames) {
|
|
@@ -1511,7 +1545,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
1511
1545
|
const displayName = memberDisplayNames.get(String(value));
|
|
1512
1546
|
return displayName !== void 0 ? { value, displayName } : { value };
|
|
1513
1547
|
});
|
|
1514
|
-
const isBooleanUnion2 = nonNullTypes.length === 2 && nonNullTypes.every((t) => t.flags &
|
|
1548
|
+
const isBooleanUnion2 = nonNullTypes.length === 2 && nonNullTypes.every((t) => t.flags & ts3.TypeFlags.BooleanLiteral);
|
|
1515
1549
|
if (isBooleanUnion2) {
|
|
1516
1550
|
const boolNode = { kind: "primitive", primitiveKind: "boolean" };
|
|
1517
1551
|
const result = hasNull ? {
|
|
@@ -1597,7 +1631,7 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, exten
|
|
|
1597
1631
|
if (type.getProperties().length > 0) {
|
|
1598
1632
|
return null;
|
|
1599
1633
|
}
|
|
1600
|
-
const indexInfo = checker.getIndexInfoOfType(type,
|
|
1634
|
+
const indexInfo = checker.getIndexInfoOfType(type, ts3.IndexKind.String);
|
|
1601
1635
|
if (!indexInfo) {
|
|
1602
1636
|
return null;
|
|
1603
1637
|
}
|
|
@@ -1708,7 +1742,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensio
|
|
|
1708
1742
|
const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
1709
1743
|
if (!declaration) continue;
|
|
1710
1744
|
const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
|
|
1711
|
-
const optional = !!(prop.flags &
|
|
1745
|
+
const optional = !!(prop.flags & ts3.SymbolFlags.Optional);
|
|
1712
1746
|
const propTypeNode = resolveTypeNode(
|
|
1713
1747
|
propType,
|
|
1714
1748
|
checker,
|
|
@@ -1753,11 +1787,11 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
1753
1787
|
for (const symbol of symbols) {
|
|
1754
1788
|
const declarations = symbol.declarations;
|
|
1755
1789
|
if (!declarations) continue;
|
|
1756
|
-
const classDecl = declarations.find(
|
|
1790
|
+
const classDecl = declarations.find(ts3.isClassDeclaration);
|
|
1757
1791
|
if (classDecl) {
|
|
1758
1792
|
const map = /* @__PURE__ */ new Map();
|
|
1759
1793
|
for (const member of classDecl.members) {
|
|
1760
|
-
if (
|
|
1794
|
+
if (ts3.isPropertyDeclaration(member) && ts3.isIdentifier(member.name)) {
|
|
1761
1795
|
const fieldNode = analyzeFieldToIR(
|
|
1762
1796
|
member,
|
|
1763
1797
|
checker,
|
|
@@ -1777,7 +1811,7 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
1777
1811
|
}
|
|
1778
1812
|
return map;
|
|
1779
1813
|
}
|
|
1780
|
-
const interfaceDecl = declarations.find(
|
|
1814
|
+
const interfaceDecl = declarations.find(ts3.isInterfaceDeclaration);
|
|
1781
1815
|
if (interfaceDecl) {
|
|
1782
1816
|
return buildFieldNodeInfoMap(
|
|
1783
1817
|
interfaceDecl.members,
|
|
@@ -1788,8 +1822,8 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
1788
1822
|
extensionRegistry
|
|
1789
1823
|
);
|
|
1790
1824
|
}
|
|
1791
|
-
const typeAliasDecl = declarations.find(
|
|
1792
|
-
if (typeAliasDecl &&
|
|
1825
|
+
const typeAliasDecl = declarations.find(ts3.isTypeAliasDeclaration);
|
|
1826
|
+
if (typeAliasDecl && ts3.isTypeLiteralNode(typeAliasDecl.type)) {
|
|
1793
1827
|
return buildFieldNodeInfoMap(
|
|
1794
1828
|
typeAliasDecl.type.members,
|
|
1795
1829
|
checker,
|
|
@@ -1808,10 +1842,10 @@ function extractArrayElementTypeNode(sourceNode, checker) {
|
|
|
1808
1842
|
return void 0;
|
|
1809
1843
|
}
|
|
1810
1844
|
const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
|
|
1811
|
-
if (
|
|
1845
|
+
if (ts3.isArrayTypeNode(resolvedTypeNode)) {
|
|
1812
1846
|
return resolvedTypeNode.elementType;
|
|
1813
1847
|
}
|
|
1814
|
-
if (
|
|
1848
|
+
if (ts3.isTypeReferenceNode(resolvedTypeNode) && ts3.isIdentifier(resolvedTypeNode.typeName) && resolvedTypeNode.typeName.text === "Array" && resolvedTypeNode.typeArguments?.[0]) {
|
|
1815
1849
|
return resolvedTypeNode.typeArguments[0];
|
|
1816
1850
|
}
|
|
1817
1851
|
return void 0;
|
|
@@ -1822,17 +1856,17 @@ function extractUnionMemberTypeNodes(sourceNode, checker) {
|
|
|
1822
1856
|
return [];
|
|
1823
1857
|
}
|
|
1824
1858
|
const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
|
|
1825
|
-
return
|
|
1859
|
+
return ts3.isUnionTypeNode(resolvedTypeNode) ? [...resolvedTypeNode.types] : [];
|
|
1826
1860
|
}
|
|
1827
1861
|
function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
|
|
1828
|
-
if (
|
|
1862
|
+
if (ts3.isParenthesizedTypeNode(typeNode)) {
|
|
1829
1863
|
return resolveAliasedTypeNode(typeNode.type, checker, visited);
|
|
1830
1864
|
}
|
|
1831
|
-
if (!
|
|
1865
|
+
if (!ts3.isTypeReferenceNode(typeNode) || !ts3.isIdentifier(typeNode.typeName)) {
|
|
1832
1866
|
return typeNode;
|
|
1833
1867
|
}
|
|
1834
1868
|
const symbol = checker.getSymbolAtLocation(typeNode.typeName);
|
|
1835
|
-
const aliasDecl = symbol?.declarations?.find(
|
|
1869
|
+
const aliasDecl = symbol?.declarations?.find(ts3.isTypeAliasDeclaration);
|
|
1836
1870
|
if (aliasDecl === void 0 || visited.has(aliasDecl)) {
|
|
1837
1871
|
return typeNode;
|
|
1838
1872
|
}
|
|
@@ -1840,15 +1874,15 @@ function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new
|
|
|
1840
1874
|
return resolveAliasedTypeNode(aliasDecl.type, checker, visited);
|
|
1841
1875
|
}
|
|
1842
1876
|
function isNullishTypeNode(typeNode) {
|
|
1843
|
-
if (typeNode.kind ===
|
|
1877
|
+
if (typeNode.kind === ts3.SyntaxKind.NullKeyword || typeNode.kind === ts3.SyntaxKind.UndefinedKeyword) {
|
|
1844
1878
|
return true;
|
|
1845
1879
|
}
|
|
1846
|
-
return
|
|
1880
|
+
return ts3.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts3.SyntaxKind.NullKeyword || typeNode.literal.kind === ts3.SyntaxKind.UndefinedKeyword);
|
|
1847
1881
|
}
|
|
1848
1882
|
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1849
1883
|
const map = /* @__PURE__ */ new Map();
|
|
1850
1884
|
for (const member of members) {
|
|
1851
|
-
if (
|
|
1885
|
+
if (ts3.isPropertySignature(member)) {
|
|
1852
1886
|
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1853
1887
|
member,
|
|
1854
1888
|
checker,
|
|
@@ -1870,7 +1904,7 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, e
|
|
|
1870
1904
|
}
|
|
1871
1905
|
var MAX_ALIAS_CHAIN_DEPTH = 8;
|
|
1872
1906
|
function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegistry, depth = 0) {
|
|
1873
|
-
if (!
|
|
1907
|
+
if (!ts3.isTypeReferenceNode(typeNode)) return [];
|
|
1874
1908
|
if (depth >= MAX_ALIAS_CHAIN_DEPTH) {
|
|
1875
1909
|
const aliasName = typeNode.typeName.getText();
|
|
1876
1910
|
throw new Error(
|
|
@@ -1879,9 +1913,9 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
|
|
|
1879
1913
|
}
|
|
1880
1914
|
const symbol = checker.getSymbolAtLocation(typeNode.typeName);
|
|
1881
1915
|
if (!symbol?.declarations) return [];
|
|
1882
|
-
const aliasDecl = symbol.declarations.find(
|
|
1916
|
+
const aliasDecl = symbol.declarations.find(ts3.isTypeAliasDeclaration);
|
|
1883
1917
|
if (!aliasDecl) return [];
|
|
1884
|
-
if (
|
|
1918
|
+
if (ts3.isTypeLiteralNode(aliasDecl.type)) return [];
|
|
1885
1919
|
const aliasFieldType = resolveTypeNode(
|
|
1886
1920
|
checker.getTypeAtLocation(aliasDecl.type),
|
|
1887
1921
|
checker,
|
|
@@ -1897,13 +1931,7 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
|
|
|
1897
1931
|
makeParseOptions(extensionRegistry, aliasFieldType)
|
|
1898
1932
|
);
|
|
1899
1933
|
constraints.push(
|
|
1900
|
-
...extractTypeAliasConstraintNodes(
|
|
1901
|
-
aliasDecl.type,
|
|
1902
|
-
checker,
|
|
1903
|
-
file,
|
|
1904
|
-
extensionRegistry,
|
|
1905
|
-
depth + 1
|
|
1906
|
-
)
|
|
1934
|
+
...extractTypeAliasConstraintNodes(aliasDecl.type, checker, file, extensionRegistry, depth + 1)
|
|
1907
1935
|
);
|
|
1908
1936
|
return constraints;
|
|
1909
1937
|
}
|
|
@@ -1930,14 +1958,14 @@ function getNamedTypeName(type) {
|
|
|
1930
1958
|
const symbol = type.getSymbol();
|
|
1931
1959
|
if (symbol?.declarations) {
|
|
1932
1960
|
const decl = symbol.declarations[0];
|
|
1933
|
-
if (decl && (
|
|
1934
|
-
const name =
|
|
1961
|
+
if (decl && (ts3.isClassDeclaration(decl) || ts3.isInterfaceDeclaration(decl) || ts3.isTypeAliasDeclaration(decl))) {
|
|
1962
|
+
const name = ts3.isClassDeclaration(decl) ? decl.name?.text : decl.name.text;
|
|
1935
1963
|
if (name) return name;
|
|
1936
1964
|
}
|
|
1937
1965
|
}
|
|
1938
1966
|
const aliasSymbol = type.aliasSymbol;
|
|
1939
1967
|
if (aliasSymbol?.declarations) {
|
|
1940
|
-
const aliasDecl = aliasSymbol.declarations.find(
|
|
1968
|
+
const aliasDecl = aliasSymbol.declarations.find(ts3.isTypeAliasDeclaration);
|
|
1941
1969
|
if (aliasDecl) {
|
|
1942
1970
|
return aliasDecl.name.text;
|
|
1943
1971
|
}
|
|
@@ -1948,24 +1976,24 @@ function getNamedTypeDeclaration(type) {
|
|
|
1948
1976
|
const symbol = type.getSymbol();
|
|
1949
1977
|
if (symbol?.declarations) {
|
|
1950
1978
|
const decl = symbol.declarations[0];
|
|
1951
|
-
if (decl && (
|
|
1979
|
+
if (decl && (ts3.isClassDeclaration(decl) || ts3.isInterfaceDeclaration(decl) || ts3.isTypeAliasDeclaration(decl))) {
|
|
1952
1980
|
return decl;
|
|
1953
1981
|
}
|
|
1954
1982
|
}
|
|
1955
1983
|
const aliasSymbol = type.aliasSymbol;
|
|
1956
1984
|
if (aliasSymbol?.declarations) {
|
|
1957
|
-
return aliasSymbol.declarations.find(
|
|
1985
|
+
return aliasSymbol.declarations.find(ts3.isTypeAliasDeclaration);
|
|
1958
1986
|
}
|
|
1959
1987
|
return void 0;
|
|
1960
1988
|
}
|
|
1961
1989
|
function analyzeMethod(method, checker) {
|
|
1962
|
-
if (!
|
|
1990
|
+
if (!ts3.isIdentifier(method.name)) {
|
|
1963
1991
|
return null;
|
|
1964
1992
|
}
|
|
1965
1993
|
const name = method.name.text;
|
|
1966
1994
|
const parameters = [];
|
|
1967
1995
|
for (const param of method.parameters) {
|
|
1968
|
-
if (
|
|
1996
|
+
if (ts3.isIdentifier(param.name)) {
|
|
1969
1997
|
const paramInfo = analyzeParameter(param, checker);
|
|
1970
1998
|
parameters.push(paramInfo);
|
|
1971
1999
|
}
|
|
@@ -1976,7 +2004,7 @@ function analyzeMethod(method, checker) {
|
|
|
1976
2004
|
return { name, parameters, returnTypeNode, returnType };
|
|
1977
2005
|
}
|
|
1978
2006
|
function analyzeParameter(param, checker) {
|
|
1979
|
-
const name =
|
|
2007
|
+
const name = ts3.isIdentifier(param.name) ? param.name.text : "param";
|
|
1980
2008
|
const typeNode = param.type;
|
|
1981
2009
|
const type = checker.getTypeAtLocation(param);
|
|
1982
2010
|
const formSpecExportName = detectFormSpecReference(typeNode);
|
|
@@ -1985,20 +2013,90 @@ function analyzeParameter(param, checker) {
|
|
|
1985
2013
|
}
|
|
1986
2014
|
function detectFormSpecReference(typeNode) {
|
|
1987
2015
|
if (!typeNode) return null;
|
|
1988
|
-
if (!
|
|
1989
|
-
const typeName =
|
|
2016
|
+
if (!ts3.isTypeReferenceNode(typeNode)) return null;
|
|
2017
|
+
const typeName = ts3.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : ts3.isQualifiedName(typeNode.typeName) ? typeNode.typeName.right.text : null;
|
|
1990
2018
|
if (typeName !== "InferSchema" && typeName !== "InferFormSchema") return null;
|
|
1991
2019
|
const typeArg = typeNode.typeArguments?.[0];
|
|
1992
|
-
if (!typeArg || !
|
|
1993
|
-
if (
|
|
2020
|
+
if (!typeArg || !ts3.isTypeQueryNode(typeArg)) return null;
|
|
2021
|
+
if (ts3.isIdentifier(typeArg.exprName)) {
|
|
1994
2022
|
return typeArg.exprName.text;
|
|
1995
2023
|
}
|
|
1996
|
-
if (
|
|
2024
|
+
if (ts3.isQualifiedName(typeArg.exprName)) {
|
|
1997
2025
|
return typeArg.exprName.right.text;
|
|
1998
2026
|
}
|
|
1999
2027
|
return null;
|
|
2000
2028
|
}
|
|
2001
2029
|
|
|
2030
|
+
// src/analyzer/program.ts
|
|
2031
|
+
function createProgramContext(filePath) {
|
|
2032
|
+
const absolutePath = path.resolve(filePath);
|
|
2033
|
+
const fileDir = path.dirname(absolutePath);
|
|
2034
|
+
const configPath = ts4.findConfigFile(fileDir, ts4.sys.fileExists.bind(ts4.sys), "tsconfig.json");
|
|
2035
|
+
let compilerOptions;
|
|
2036
|
+
let fileNames;
|
|
2037
|
+
if (configPath) {
|
|
2038
|
+
const configFile = ts4.readConfigFile(configPath, ts4.sys.readFile.bind(ts4.sys));
|
|
2039
|
+
if (configFile.error) {
|
|
2040
|
+
throw new Error(
|
|
2041
|
+
`Error reading tsconfig.json: ${ts4.flattenDiagnosticMessageText(configFile.error.messageText, "\n")}`
|
|
2042
|
+
);
|
|
2043
|
+
}
|
|
2044
|
+
const parsed = ts4.parseJsonConfigFileContent(
|
|
2045
|
+
configFile.config,
|
|
2046
|
+
ts4.sys,
|
|
2047
|
+
path.dirname(configPath)
|
|
2048
|
+
);
|
|
2049
|
+
if (parsed.errors.length > 0) {
|
|
2050
|
+
const errorMessages = parsed.errors.map((e) => ts4.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n");
|
|
2051
|
+
throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
|
|
2052
|
+
}
|
|
2053
|
+
compilerOptions = parsed.options;
|
|
2054
|
+
fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
|
|
2055
|
+
} else {
|
|
2056
|
+
compilerOptions = {
|
|
2057
|
+
target: ts4.ScriptTarget.ES2022,
|
|
2058
|
+
module: ts4.ModuleKind.NodeNext,
|
|
2059
|
+
moduleResolution: ts4.ModuleResolutionKind.NodeNext,
|
|
2060
|
+
strict: true,
|
|
2061
|
+
skipLibCheck: true,
|
|
2062
|
+
declaration: true
|
|
2063
|
+
};
|
|
2064
|
+
fileNames = [absolutePath];
|
|
2065
|
+
}
|
|
2066
|
+
const program = ts4.createProgram(fileNames, compilerOptions);
|
|
2067
|
+
const sourceFile = program.getSourceFile(absolutePath);
|
|
2068
|
+
if (!sourceFile) {
|
|
2069
|
+
throw new Error(`Could not find source file: ${absolutePath}`);
|
|
2070
|
+
}
|
|
2071
|
+
return {
|
|
2072
|
+
program,
|
|
2073
|
+
checker: program.getTypeChecker(),
|
|
2074
|
+
sourceFile
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
function findNodeByName(sourceFile, name, predicate, getName) {
|
|
2078
|
+
let result = null;
|
|
2079
|
+
function visit(node) {
|
|
2080
|
+
if (result) return;
|
|
2081
|
+
if (predicate(node) && getName(node) === name) {
|
|
2082
|
+
result = node;
|
|
2083
|
+
return;
|
|
2084
|
+
}
|
|
2085
|
+
ts4.forEachChild(node, visit);
|
|
2086
|
+
}
|
|
2087
|
+
visit(sourceFile);
|
|
2088
|
+
return result;
|
|
2089
|
+
}
|
|
2090
|
+
function findClassByName(sourceFile, className) {
|
|
2091
|
+
return findNodeByName(sourceFile, className, ts4.isClassDeclaration, (n) => n.name?.text);
|
|
2092
|
+
}
|
|
2093
|
+
function findInterfaceByName(sourceFile, interfaceName) {
|
|
2094
|
+
return findNodeByName(sourceFile, interfaceName, ts4.isInterfaceDeclaration, (n) => n.name.text);
|
|
2095
|
+
}
|
|
2096
|
+
function findTypeAliasByName(sourceFile, aliasName) {
|
|
2097
|
+
return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2002
2100
|
// src/json-schema/ir-generator.ts
|
|
2003
2101
|
function makeContext(options) {
|
|
2004
2102
|
const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
|
|
@@ -2017,6 +2115,9 @@ function generateJsonSchemaFromIR(ir, options) {
|
|
|
2017
2115
|
const ctx = makeContext(options);
|
|
2018
2116
|
for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
|
|
2019
2117
|
ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
|
|
2118
|
+
if (typeDef.constraints && typeDef.constraints.length > 0) {
|
|
2119
|
+
applyConstraints(ctx.defs[name], typeDef.constraints, ctx);
|
|
2120
|
+
}
|
|
2020
2121
|
if (typeDef.annotations && typeDef.annotations.length > 0) {
|
|
2021
2122
|
applyAnnotations(ctx.defs[name], typeDef.annotations, ctx);
|
|
2022
2123
|
}
|
|
@@ -2185,7 +2286,9 @@ function generateTypeNode(type, ctx) {
|
|
|
2185
2286
|
}
|
|
2186
2287
|
}
|
|
2187
2288
|
function generatePrimitiveType(type) {
|
|
2188
|
-
return {
|
|
2289
|
+
return {
|
|
2290
|
+
type: type.primitiveKind === "integer" || type.primitiveKind === "bigint" ? "integer" : type.primitiveKind
|
|
2291
|
+
};
|
|
2189
2292
|
}
|
|
2190
2293
|
function generateEnumType(type) {
|
|
2191
2294
|
const hasDisplayNames = type.members.some((m) => m.displayName !== void 0);
|
|
@@ -2358,7 +2461,7 @@ function applyAnnotations(schema, annotations, ctx) {
|
|
|
2358
2461
|
case "deprecated":
|
|
2359
2462
|
schema.deprecated = true;
|
|
2360
2463
|
if (annotation.message !== void 0 && annotation.message !== "") {
|
|
2361
|
-
schema[
|
|
2464
|
+
schema[`${ctx.vendorPrefix}-deprecation-description`] = annotation.message;
|
|
2362
2465
|
}
|
|
2363
2466
|
break;
|
|
2364
2467
|
case "placeholder":
|
|
@@ -2649,15 +2752,6 @@ function generateUiSchemaFromIR(ir) {
|
|
|
2649
2752
|
return parseOrThrow(uiSchema, result, "UI Schema");
|
|
2650
2753
|
}
|
|
2651
2754
|
|
|
2652
|
-
// src/generators/class-schema.ts
|
|
2653
|
-
function generateClassSchemas(analysis, source, options) {
|
|
2654
|
-
const ir = canonicalizeTSDoc(analysis, source);
|
|
2655
|
-
return {
|
|
2656
|
-
jsonSchema: generateJsonSchemaFromIR(ir, options),
|
|
2657
|
-
uiSchema: generateUiSchemaFromIR(ir)
|
|
2658
|
-
};
|
|
2659
|
-
}
|
|
2660
|
-
|
|
2661
2755
|
// src/validate/constraint-validator.ts
|
|
2662
2756
|
var import_core4 = require("@formspec/core");
|
|
2663
2757
|
function addContradiction(ctx, message, primary, related) {
|
|
@@ -3135,6 +3229,26 @@ function dereferenceType(ctx, type) {
|
|
|
3135
3229
|
}
|
|
3136
3230
|
return current;
|
|
3137
3231
|
}
|
|
3232
|
+
function collectReferencedTypeConstraints(ctx, type) {
|
|
3233
|
+
const collected = [];
|
|
3234
|
+
let current = type;
|
|
3235
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3236
|
+
while (current.kind === "reference") {
|
|
3237
|
+
if (seen.has(current.name)) {
|
|
3238
|
+
break;
|
|
3239
|
+
}
|
|
3240
|
+
seen.add(current.name);
|
|
3241
|
+
const definition = ctx.typeRegistry[current.name];
|
|
3242
|
+
if (definition === void 0) {
|
|
3243
|
+
break;
|
|
3244
|
+
}
|
|
3245
|
+
if (definition.constraints !== void 0) {
|
|
3246
|
+
collected.push(...definition.constraints);
|
|
3247
|
+
}
|
|
3248
|
+
current = definition.type;
|
|
3249
|
+
}
|
|
3250
|
+
return collected;
|
|
3251
|
+
}
|
|
3138
3252
|
function resolvePathTargetType(ctx, type, segments) {
|
|
3139
3253
|
const effectiveType = dereferenceType(ctx, type);
|
|
3140
3254
|
if (segments.length === 0) {
|
|
@@ -3156,12 +3270,33 @@ function resolvePathTargetType(ctx, type, segments) {
|
|
|
3156
3270
|
}
|
|
3157
3271
|
return { kind: "unresolvable", type: effectiveType };
|
|
3158
3272
|
}
|
|
3273
|
+
function isNullType(type) {
|
|
3274
|
+
return type.kind === "primitive" && type.primitiveKind === "null";
|
|
3275
|
+
}
|
|
3276
|
+
function collectCustomConstraintCandidateTypes(ctx, type) {
|
|
3277
|
+
const effectiveType = dereferenceType(ctx, type);
|
|
3278
|
+
const candidates = [effectiveType];
|
|
3279
|
+
if (effectiveType.kind === "array") {
|
|
3280
|
+
candidates.push(...collectCustomConstraintCandidateTypes(ctx, effectiveType.items));
|
|
3281
|
+
}
|
|
3282
|
+
if (effectiveType.kind === "union") {
|
|
3283
|
+
const memberTypes = effectiveType.members.map((member) => dereferenceType(ctx, member));
|
|
3284
|
+
const nonNullMembers = memberTypes.filter((member) => !isNullType(member));
|
|
3285
|
+
if (nonNullMembers.length === 1 && nonNullMembers.length < memberTypes.length) {
|
|
3286
|
+
const [nullableMember] = nonNullMembers;
|
|
3287
|
+
if (nullableMember !== void 0) {
|
|
3288
|
+
candidates.push(...collectCustomConstraintCandidateTypes(ctx, nullableMember));
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
return candidates;
|
|
3293
|
+
}
|
|
3159
3294
|
function formatPathTargetFieldName(fieldName, path2) {
|
|
3160
3295
|
return path2.length === 0 ? fieldName : `${fieldName}.${path2.join(".")}`;
|
|
3161
3296
|
}
|
|
3162
3297
|
function checkConstraintOnType(ctx, fieldName, type, constraint) {
|
|
3163
3298
|
const effectiveType = dereferenceType(ctx, type);
|
|
3164
|
-
const isNumber = effectiveType.kind === "primitive" &&
|
|
3299
|
+
const isNumber = effectiveType.kind === "primitive" && ["number", "integer", "bigint"].includes(effectiveType.primitiveKind);
|
|
3165
3300
|
const isString = effectiveType.kind === "primitive" && effectiveType.primitiveKind === "string";
|
|
3166
3301
|
const isArray = effectiveType.kind === "array";
|
|
3167
3302
|
const isEnum = effectiveType.kind === "enum";
|
|
@@ -3219,7 +3354,9 @@ function checkConstraintOnType(ctx, fieldName, type, constraint) {
|
|
|
3219
3354
|
break;
|
|
3220
3355
|
}
|
|
3221
3356
|
case "const": {
|
|
3222
|
-
const isPrimitiveConstType = effectiveType.kind === "primitive" && ["string", "number", "boolean", "null"].includes(
|
|
3357
|
+
const isPrimitiveConstType = effectiveType.kind === "primitive" && ["string", "number", "integer", "bigint", "boolean", "null"].includes(
|
|
3358
|
+
effectiveType.primitiveKind
|
|
3359
|
+
) || effectiveType.kind === "enum";
|
|
3223
3360
|
if (!isPrimitiveConstType) {
|
|
3224
3361
|
addTypeMismatch(
|
|
3225
3362
|
ctx,
|
|
@@ -3230,7 +3367,8 @@ function checkConstraintOnType(ctx, fieldName, type, constraint) {
|
|
|
3230
3367
|
}
|
|
3231
3368
|
if (effectiveType.kind === "primitive") {
|
|
3232
3369
|
const valueType = constraint.value === null ? "null" : Array.isArray(constraint.value) ? "array" : typeof constraint.value;
|
|
3233
|
-
|
|
3370
|
+
const expectedValueType = effectiveType.primitiveKind === "integer" || effectiveType.primitiveKind === "bigint" ? "number" : effectiveType.primitiveKind;
|
|
3371
|
+
if (valueType !== expectedValueType) {
|
|
3234
3372
|
addTypeMismatch(
|
|
3235
3373
|
ctx,
|
|
3236
3374
|
`Field "${fieldName}": @const value type "${valueType}" is incompatible with field type "${effectiveType.primitiveKind}"`,
|
|
@@ -3299,11 +3437,14 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
|
|
|
3299
3437
|
);
|
|
3300
3438
|
return;
|
|
3301
3439
|
}
|
|
3440
|
+
const candidateTypes = collectCustomConstraintCandidateTypes(ctx, type);
|
|
3302
3441
|
const normalizedTagName = constraint.provenance.tagName === void 0 ? void 0 : (0, import_core4.normalizeConstraintTagName)(constraint.provenance.tagName.replace(/^@/, ""));
|
|
3303
3442
|
if (normalizedTagName !== void 0) {
|
|
3304
3443
|
const tagRegistration = ctx.extensionRegistry.findConstraintTag(normalizedTagName);
|
|
3305
3444
|
const extensionId = getExtensionIdFromConstraintId(constraint.constraintId);
|
|
3306
|
-
if (extensionId !== null && tagRegistration?.extensionId === extensionId && tagRegistration.registration.constraintName === registration.constraintName &&
|
|
3445
|
+
if (extensionId !== null && tagRegistration?.extensionId === extensionId && tagRegistration.registration.constraintName === registration.constraintName && !candidateTypes.some(
|
|
3446
|
+
(candidateType) => tagRegistration.registration.isApplicableToType?.(candidateType) !== false
|
|
3447
|
+
)) {
|
|
3307
3448
|
addTypeMismatch(
|
|
3308
3449
|
ctx,
|
|
3309
3450
|
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
|
|
@@ -3313,7 +3454,7 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
|
|
|
3313
3454
|
}
|
|
3314
3455
|
}
|
|
3315
3456
|
if (registration.applicableTypes === null) {
|
|
3316
|
-
if (registration.isApplicableToType?.(
|
|
3457
|
+
if (!candidateTypes.some((candidateType) => registration.isApplicableToType?.(candidateType) !== false)) {
|
|
3317
3458
|
addTypeMismatch(
|
|
3318
3459
|
ctx,
|
|
3319
3460
|
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
|
|
@@ -3322,7 +3463,11 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
|
|
|
3322
3463
|
}
|
|
3323
3464
|
return;
|
|
3324
3465
|
}
|
|
3325
|
-
|
|
3466
|
+
const applicableTypes = registration.applicableTypes;
|
|
3467
|
+
const matchesApplicableType = candidateTypes.some(
|
|
3468
|
+
(candidateType) => applicableTypes.includes(candidateType.kind) && registration.isApplicableToType?.(candidateType) !== false
|
|
3469
|
+
);
|
|
3470
|
+
if (!matchesApplicableType) {
|
|
3326
3471
|
addTypeMismatch(
|
|
3327
3472
|
ctx,
|
|
3328
3473
|
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
|
|
@@ -3331,7 +3476,10 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
|
|
|
3331
3476
|
}
|
|
3332
3477
|
}
|
|
3333
3478
|
function validateFieldNode(ctx, field) {
|
|
3334
|
-
validateConstraints(ctx, field.name, field.type,
|
|
3479
|
+
validateConstraints(ctx, field.name, field.type, [
|
|
3480
|
+
...collectReferencedTypeConstraints(ctx, field.type),
|
|
3481
|
+
...field.constraints
|
|
3482
|
+
]);
|
|
3335
3483
|
if (field.type.kind === "object") {
|
|
3336
3484
|
for (const prop of field.type.properties) {
|
|
3337
3485
|
validateObjectProperty(ctx, field.name, prop);
|
|
@@ -3340,7 +3488,10 @@ function validateFieldNode(ctx, field) {
|
|
|
3340
3488
|
}
|
|
3341
3489
|
function validateObjectProperty(ctx, parentName, prop) {
|
|
3342
3490
|
const qualifiedName = `${parentName}.${prop.name}`;
|
|
3343
|
-
validateConstraints(ctx, qualifiedName, prop.type,
|
|
3491
|
+
validateConstraints(ctx, qualifiedName, prop.type, [
|
|
3492
|
+
...collectReferencedTypeConstraints(ctx, prop.type),
|
|
3493
|
+
...prop.constraints
|
|
3494
|
+
]);
|
|
3344
3495
|
if (prop.type.kind === "object") {
|
|
3345
3496
|
for (const nestedProp of prop.type.properties) {
|
|
3346
3497
|
validateObjectProperty(ctx, qualifiedName, nestedProp);
|
|
@@ -3392,6 +3543,36 @@ function validateIR(ir, options) {
|
|
|
3392
3543
|
};
|
|
3393
3544
|
}
|
|
3394
3545
|
|
|
3546
|
+
// src/generators/class-schema.ts
|
|
3547
|
+
function generateClassSchemas(analysis, source, options) {
|
|
3548
|
+
const ir = canonicalizeTSDoc(analysis, source);
|
|
3549
|
+
const validationResult = validateIR(ir, {
|
|
3550
|
+
...options?.extensionRegistry !== void 0 && {
|
|
3551
|
+
extensionRegistry: options.extensionRegistry
|
|
3552
|
+
},
|
|
3553
|
+
...options?.vendorPrefix !== void 0 && { vendorPrefix: options.vendorPrefix }
|
|
3554
|
+
});
|
|
3555
|
+
if (!validationResult.valid) {
|
|
3556
|
+
throw new Error(formatValidationError(validationResult.diagnostics));
|
|
3557
|
+
}
|
|
3558
|
+
return {
|
|
3559
|
+
jsonSchema: generateJsonSchemaFromIR(ir, options),
|
|
3560
|
+
uiSchema: generateUiSchemaFromIR(ir)
|
|
3561
|
+
};
|
|
3562
|
+
}
|
|
3563
|
+
function formatValidationError(diagnostics) {
|
|
3564
|
+
const lines = diagnostics.map((diagnostic) => {
|
|
3565
|
+
const primary = formatLocation(diagnostic.primaryLocation);
|
|
3566
|
+
const related = diagnostic.relatedLocations.length > 0 ? ` [related: ${diagnostic.relatedLocations.map(formatLocation).join(", ")}]` : "";
|
|
3567
|
+
return `${diagnostic.code}: ${diagnostic.message} (${primary})${related}`;
|
|
3568
|
+
});
|
|
3569
|
+
return `FormSpec validation failed:
|
|
3570
|
+
${lines.map((line) => `- ${line}`).join("\n")}`;
|
|
3571
|
+
}
|
|
3572
|
+
function formatLocation(location) {
|
|
3573
|
+
return `${location.file}:${String(location.line)}:${String(location.column)}`;
|
|
3574
|
+
}
|
|
3575
|
+
|
|
3395
3576
|
// src/extensions/registry.ts
|
|
3396
3577
|
function createExtensionRegistry(extensions) {
|
|
3397
3578
|
const typeMap = /* @__PURE__ */ new Map();
|
|
@@ -3493,6 +3674,7 @@ function typeToJsonSchema(type, checker) {
|
|
|
3493
3674
|
provenance: fieldProvenance
|
|
3494
3675
|
}
|
|
3495
3676
|
],
|
|
3677
|
+
rootAnnotations: [],
|
|
3496
3678
|
typeRegistry,
|
|
3497
3679
|
provenance: fieldProvenance
|
|
3498
3680
|
};
|