@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.js
CHANGED
|
@@ -20,6 +20,7 @@ function canonicalizeChainDSL(form) {
|
|
|
20
20
|
kind: "form-ir",
|
|
21
21
|
irVersion: IR_VERSION,
|
|
22
22
|
elements: canonicalizeElements(form.elements),
|
|
23
|
+
rootAnnotations: [],
|
|
23
24
|
typeRegistry: {},
|
|
24
25
|
provenance: CHAIN_DSL_PROVENANCE
|
|
25
26
|
};
|
|
@@ -325,6 +326,7 @@ function canonicalizeTSDoc(analysis, source) {
|
|
|
325
326
|
irVersion: IR_VERSION2,
|
|
326
327
|
elements,
|
|
327
328
|
typeRegistry: analysis.typeRegistry,
|
|
329
|
+
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { rootAnnotations: analysis.annotations },
|
|
328
330
|
...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { annotations: analysis.annotations },
|
|
329
331
|
provenance
|
|
330
332
|
};
|
|
@@ -385,85 +387,17 @@ function wrapInConditional(field, layout, provenance) {
|
|
|
385
387
|
}
|
|
386
388
|
|
|
387
389
|
// src/analyzer/program.ts
|
|
388
|
-
import * as
|
|
390
|
+
import * as ts4 from "typescript";
|
|
389
391
|
import * as path from "path";
|
|
390
|
-
function createProgramContext(filePath) {
|
|
391
|
-
const absolutePath = path.resolve(filePath);
|
|
392
|
-
const fileDir = path.dirname(absolutePath);
|
|
393
|
-
const configPath = ts.findConfigFile(fileDir, ts.sys.fileExists.bind(ts.sys), "tsconfig.json");
|
|
394
|
-
let compilerOptions;
|
|
395
|
-
let fileNames;
|
|
396
|
-
if (configPath) {
|
|
397
|
-
const configFile = ts.readConfigFile(configPath, ts.sys.readFile.bind(ts.sys));
|
|
398
|
-
if (configFile.error) {
|
|
399
|
-
throw new Error(
|
|
400
|
-
`Error reading tsconfig.json: ${ts.flattenDiagnosticMessageText(configFile.error.messageText, "\n")}`
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
const parsed = ts.parseJsonConfigFileContent(
|
|
404
|
-
configFile.config,
|
|
405
|
-
ts.sys,
|
|
406
|
-
path.dirname(configPath)
|
|
407
|
-
);
|
|
408
|
-
if (parsed.errors.length > 0) {
|
|
409
|
-
const errorMessages = parsed.errors.map((e) => ts.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n");
|
|
410
|
-
throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
|
|
411
|
-
}
|
|
412
|
-
compilerOptions = parsed.options;
|
|
413
|
-
fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
|
|
414
|
-
} else {
|
|
415
|
-
compilerOptions = {
|
|
416
|
-
target: ts.ScriptTarget.ES2022,
|
|
417
|
-
module: ts.ModuleKind.NodeNext,
|
|
418
|
-
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
419
|
-
strict: true,
|
|
420
|
-
skipLibCheck: true,
|
|
421
|
-
declaration: true
|
|
422
|
-
};
|
|
423
|
-
fileNames = [absolutePath];
|
|
424
|
-
}
|
|
425
|
-
const program = ts.createProgram(fileNames, compilerOptions);
|
|
426
|
-
const sourceFile = program.getSourceFile(absolutePath);
|
|
427
|
-
if (!sourceFile) {
|
|
428
|
-
throw new Error(`Could not find source file: ${absolutePath}`);
|
|
429
|
-
}
|
|
430
|
-
return {
|
|
431
|
-
program,
|
|
432
|
-
checker: program.getTypeChecker(),
|
|
433
|
-
sourceFile
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
function findNodeByName(sourceFile, name, predicate, getName) {
|
|
437
|
-
let result = null;
|
|
438
|
-
function visit(node) {
|
|
439
|
-
if (result) return;
|
|
440
|
-
if (predicate(node) && getName(node) === name) {
|
|
441
|
-
result = node;
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
ts.forEachChild(node, visit);
|
|
445
|
-
}
|
|
446
|
-
visit(sourceFile);
|
|
447
|
-
return result;
|
|
448
|
-
}
|
|
449
|
-
function findClassByName(sourceFile, className) {
|
|
450
|
-
return findNodeByName(sourceFile, className, ts.isClassDeclaration, (n) => n.name?.text);
|
|
451
|
-
}
|
|
452
|
-
function findInterfaceByName(sourceFile, interfaceName) {
|
|
453
|
-
return findNodeByName(sourceFile, interfaceName, ts.isInterfaceDeclaration, (n) => n.name.text);
|
|
454
|
-
}
|
|
455
|
-
function findTypeAliasByName(sourceFile, aliasName) {
|
|
456
|
-
return findNodeByName(sourceFile, aliasName, ts.isTypeAliasDeclaration, (n) => n.name.text);
|
|
457
|
-
}
|
|
458
392
|
|
|
459
393
|
// src/analyzer/class-analyzer.ts
|
|
460
|
-
import * as
|
|
394
|
+
import * as ts3 from "typescript";
|
|
461
395
|
|
|
462
396
|
// src/analyzer/jsdoc-constraints.ts
|
|
463
|
-
import * as
|
|
397
|
+
import * as ts2 from "typescript";
|
|
464
398
|
|
|
465
399
|
// src/analyzer/tsdoc-parser.ts
|
|
466
|
-
import * as
|
|
400
|
+
import * as ts from "typescript";
|
|
467
401
|
import {
|
|
468
402
|
TSDocParser,
|
|
469
403
|
TSDocConfiguration,
|
|
@@ -561,10 +495,10 @@ function parseTSDocTags(node, file = "", options) {
|
|
|
561
495
|
let placeholderProvenance;
|
|
562
496
|
const sourceFile = node.getSourceFile();
|
|
563
497
|
const sourceText = sourceFile.getFullText();
|
|
564
|
-
const commentRanges =
|
|
498
|
+
const commentRanges = ts.getLeadingCommentRanges(sourceText, node.getFullStart());
|
|
565
499
|
if (commentRanges) {
|
|
566
500
|
for (const range of commentRanges) {
|
|
567
|
-
if (range.kind !==
|
|
501
|
+
if (range.kind !== ts.SyntaxKind.MultiLineCommentTrivia) {
|
|
568
502
|
continue;
|
|
569
503
|
}
|
|
570
504
|
const commentText = sourceText.substring(range.pos, range.end);
|
|
@@ -582,26 +516,31 @@ function parseTSDocTags(node, file = "", options) {
|
|
|
582
516
|
const text2 = extractBlockText(block).trim();
|
|
583
517
|
if (text2 === "") continue;
|
|
584
518
|
const provenance2 = provenanceForComment(range, sourceFile, file, tagName);
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
displayName
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
519
|
+
switch (tagName) {
|
|
520
|
+
case "displayName":
|
|
521
|
+
if (!isMemberTargetDisplayName(text2) && displayName === void 0) {
|
|
522
|
+
displayName = text2;
|
|
523
|
+
displayNameProvenance = provenance2;
|
|
524
|
+
}
|
|
525
|
+
break;
|
|
526
|
+
case "format":
|
|
527
|
+
annotations.push({
|
|
528
|
+
kind: "annotation",
|
|
529
|
+
annotationKind: "format",
|
|
530
|
+
value: text2,
|
|
531
|
+
provenance: provenance2
|
|
532
|
+
});
|
|
533
|
+
break;
|
|
534
|
+
case "description":
|
|
599
535
|
description = text2;
|
|
600
536
|
descriptionProvenance = provenance2;
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
537
|
+
break;
|
|
538
|
+
case "placeholder":
|
|
539
|
+
if (placeholder === void 0) {
|
|
540
|
+
placeholder = text2;
|
|
541
|
+
placeholderProvenance = provenance2;
|
|
542
|
+
}
|
|
543
|
+
break;
|
|
605
544
|
}
|
|
606
545
|
continue;
|
|
607
546
|
}
|
|
@@ -631,6 +570,13 @@ function parseTSDocTags(node, file = "", options) {
|
|
|
631
570
|
descriptionProvenance = provenanceForComment(range, sourceFile, file, "remarks");
|
|
632
571
|
}
|
|
633
572
|
}
|
|
573
|
+
if (description === void 0) {
|
|
574
|
+
const summary = extractPlainText(docComment.summarySection).trim();
|
|
575
|
+
if (summary !== "") {
|
|
576
|
+
description = summary;
|
|
577
|
+
descriptionProvenance = provenanceForComment(range, sourceFile, file, "summary");
|
|
578
|
+
}
|
|
579
|
+
}
|
|
634
580
|
}
|
|
635
581
|
}
|
|
636
582
|
if (displayName !== void 0 && displayNameProvenance !== void 0) {
|
|
@@ -657,7 +603,7 @@ function parseTSDocTags(node, file = "", options) {
|
|
|
657
603
|
provenance: placeholderProvenance
|
|
658
604
|
});
|
|
659
605
|
}
|
|
660
|
-
const jsDocTagsAll =
|
|
606
|
+
const jsDocTagsAll = ts.getJSDocTags(node);
|
|
661
607
|
for (const tag of jsDocTagsAll) {
|
|
662
608
|
const tagName = normalizeConstraintTagName(tag.tagName.text);
|
|
663
609
|
if (!TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
|
|
@@ -680,7 +626,7 @@ function parseTSDocTags(node, file = "", options) {
|
|
|
680
626
|
function extractDisplayNameMetadata(node) {
|
|
681
627
|
let displayName;
|
|
682
628
|
const memberDisplayNames = /* @__PURE__ */ new Map();
|
|
683
|
-
for (const tag of
|
|
629
|
+
for (const tag of ts.getJSDocTags(node)) {
|
|
684
630
|
const tagName = normalizeConstraintTagName(tag.tagName.text);
|
|
685
631
|
if (tagName !== "displayName") continue;
|
|
686
632
|
const commentText = getTagCommentText(tag);
|
|
@@ -701,11 +647,11 @@ function extractDisplayNameMetadata(node) {
|
|
|
701
647
|
}
|
|
702
648
|
function extractPathTarget(text) {
|
|
703
649
|
const trimmed = text.trimStart();
|
|
704
|
-
const match = /^:([a-zA-Z_]\w*)
|
|
705
|
-
if (!match?.[1]
|
|
650
|
+
const match = /^:([a-zA-Z_]\w*)(?:\s+([\s\S]*))?$/.exec(trimmed);
|
|
651
|
+
if (!match?.[1]) return null;
|
|
706
652
|
return {
|
|
707
653
|
path: { segments: [match[1]] },
|
|
708
|
-
remainingText: match[2]
|
|
654
|
+
remainingText: match[2] ?? ""
|
|
709
655
|
};
|
|
710
656
|
}
|
|
711
657
|
function extractBlockText(block) {
|
|
@@ -968,7 +914,7 @@ function getTagCommentText(tag) {
|
|
|
968
914
|
if (typeof tag.comment === "string") {
|
|
969
915
|
return tag.comment;
|
|
970
916
|
}
|
|
971
|
-
return
|
|
917
|
+
return ts.getTextOfJSDocComment(tag.comment);
|
|
972
918
|
}
|
|
973
919
|
|
|
974
920
|
// src/analyzer/jsdoc-constraints.ts
|
|
@@ -983,18 +929,18 @@ function extractJSDocAnnotationNodes(node, file = "", options) {
|
|
|
983
929
|
function extractDefaultValueAnnotation(initializer, file = "") {
|
|
984
930
|
if (!initializer) return null;
|
|
985
931
|
let value;
|
|
986
|
-
if (
|
|
932
|
+
if (ts2.isStringLiteral(initializer)) {
|
|
987
933
|
value = initializer.text;
|
|
988
|
-
} else if (
|
|
934
|
+
} else if (ts2.isNumericLiteral(initializer)) {
|
|
989
935
|
value = Number(initializer.text);
|
|
990
|
-
} else if (initializer.kind ===
|
|
936
|
+
} else if (initializer.kind === ts2.SyntaxKind.TrueKeyword) {
|
|
991
937
|
value = true;
|
|
992
|
-
} else if (initializer.kind ===
|
|
938
|
+
} else if (initializer.kind === ts2.SyntaxKind.FalseKeyword) {
|
|
993
939
|
value = false;
|
|
994
|
-
} else if (initializer.kind ===
|
|
940
|
+
} else if (initializer.kind === ts2.SyntaxKind.NullKeyword) {
|
|
995
941
|
value = null;
|
|
996
|
-
} else if (
|
|
997
|
-
if (initializer.operator ===
|
|
942
|
+
} else if (ts2.isPrefixUnaryExpression(initializer)) {
|
|
943
|
+
if (initializer.operator === ts2.SyntaxKind.MinusToken && ts2.isNumericLiteral(initializer.operand)) {
|
|
998
944
|
value = -Number(initializer.operand.text);
|
|
999
945
|
}
|
|
1000
946
|
}
|
|
@@ -1016,10 +962,10 @@ function extractDefaultValueAnnotation(initializer, file = "") {
|
|
|
1016
962
|
|
|
1017
963
|
// src/analyzer/class-analyzer.ts
|
|
1018
964
|
function isObjectType(type) {
|
|
1019
|
-
return !!(type.flags &
|
|
965
|
+
return !!(type.flags & ts3.TypeFlags.Object);
|
|
1020
966
|
}
|
|
1021
967
|
function isTypeReference(type) {
|
|
1022
|
-
return !!(type.flags &
|
|
968
|
+
return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
|
|
1023
969
|
}
|
|
1024
970
|
var RESOLVING_TYPE_PLACEHOLDER = {
|
|
1025
971
|
kind: "object",
|
|
@@ -1049,7 +995,7 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
1049
995
|
const instanceMethods = [];
|
|
1050
996
|
const staticMethods = [];
|
|
1051
997
|
for (const member of classDecl.members) {
|
|
1052
|
-
if (
|
|
998
|
+
if (ts3.isPropertyDeclaration(member)) {
|
|
1053
999
|
const fieldNode = analyzeFieldToIR(
|
|
1054
1000
|
member,
|
|
1055
1001
|
checker,
|
|
@@ -1062,10 +1008,10 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
|
|
|
1062
1008
|
fields.push(fieldNode);
|
|
1063
1009
|
fieldLayouts.push({});
|
|
1064
1010
|
}
|
|
1065
|
-
} else if (
|
|
1011
|
+
} else if (ts3.isMethodDeclaration(member)) {
|
|
1066
1012
|
const methodInfo = analyzeMethod(member, checker);
|
|
1067
1013
|
if (methodInfo) {
|
|
1068
|
-
const isStatic = member.modifiers?.some((m) => m.kind ===
|
|
1014
|
+
const isStatic = member.modifiers?.some((m) => m.kind === ts3.SyntaxKind.StaticKeyword);
|
|
1069
1015
|
if (isStatic) {
|
|
1070
1016
|
staticMethods.push(methodInfo);
|
|
1071
1017
|
} else {
|
|
@@ -1095,7 +1041,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
1095
1041
|
);
|
|
1096
1042
|
const visiting = /* @__PURE__ */ new Set();
|
|
1097
1043
|
for (const member of interfaceDecl.members) {
|
|
1098
|
-
if (
|
|
1044
|
+
if (ts3.isPropertySignature(member)) {
|
|
1099
1045
|
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1100
1046
|
member,
|
|
1101
1047
|
checker,
|
|
@@ -1121,10 +1067,10 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
|
|
|
1121
1067
|
};
|
|
1122
1068
|
}
|
|
1123
1069
|
function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
|
|
1124
|
-
if (!
|
|
1070
|
+
if (!ts3.isTypeLiteralNode(typeAlias.type)) {
|
|
1125
1071
|
const sourceFile = typeAlias.getSourceFile();
|
|
1126
1072
|
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
1127
|
-
const kindDesc =
|
|
1073
|
+
const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
|
|
1128
1074
|
return {
|
|
1129
1075
|
ok: false,
|
|
1130
1076
|
error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object type literal (found ${kindDesc})`
|
|
@@ -1140,7 +1086,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
1140
1086
|
);
|
|
1141
1087
|
const visiting = /* @__PURE__ */ new Set();
|
|
1142
1088
|
for (const member of typeAlias.type.members) {
|
|
1143
|
-
if (
|
|
1089
|
+
if (ts3.isPropertySignature(member)) {
|
|
1144
1090
|
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1145
1091
|
member,
|
|
1146
1092
|
checker,
|
|
@@ -1168,7 +1114,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
|
|
|
1168
1114
|
};
|
|
1169
1115
|
}
|
|
1170
1116
|
function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1171
|
-
if (!
|
|
1117
|
+
if (!ts3.isIdentifier(prop.name)) {
|
|
1172
1118
|
return null;
|
|
1173
1119
|
}
|
|
1174
1120
|
const name = prop.name.text;
|
|
@@ -1185,12 +1131,14 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extension
|
|
|
1185
1131
|
extensionRegistry
|
|
1186
1132
|
);
|
|
1187
1133
|
const constraints = [];
|
|
1188
|
-
if (prop.type) {
|
|
1134
|
+
if (prop.type && !shouldEmitPrimitiveAliasDefinition(prop.type, checker)) {
|
|
1189
1135
|
constraints.push(
|
|
1190
1136
|
...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
|
|
1191
1137
|
);
|
|
1192
1138
|
}
|
|
1193
|
-
constraints.push(
|
|
1139
|
+
constraints.push(
|
|
1140
|
+
...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
1141
|
+
);
|
|
1194
1142
|
let annotations = [];
|
|
1195
1143
|
annotations.push(
|
|
1196
1144
|
...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
@@ -1211,7 +1159,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, extension
|
|
|
1211
1159
|
};
|
|
1212
1160
|
}
|
|
1213
1161
|
function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1214
|
-
if (!
|
|
1162
|
+
if (!ts3.isIdentifier(prop.name)) {
|
|
1215
1163
|
return null;
|
|
1216
1164
|
}
|
|
1217
1165
|
const name = prop.name.text;
|
|
@@ -1228,12 +1176,14 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
1228
1176
|
extensionRegistry
|
|
1229
1177
|
);
|
|
1230
1178
|
const constraints = [];
|
|
1231
|
-
if (prop.type) {
|
|
1179
|
+
if (prop.type && !shouldEmitPrimitiveAliasDefinition(prop.type, checker)) {
|
|
1232
1180
|
constraints.push(
|
|
1233
1181
|
...extractTypeAliasConstraintNodes(prop.type, checker, file, extensionRegistry)
|
|
1234
1182
|
);
|
|
1235
1183
|
}
|
|
1236
|
-
constraints.push(
|
|
1184
|
+
constraints.push(
|
|
1185
|
+
...extractJSDocConstraintNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
1186
|
+
);
|
|
1237
1187
|
let annotations = [];
|
|
1238
1188
|
annotations.push(
|
|
1239
1189
|
...extractJSDocAnnotationNodes(prop, file, makeParseOptions(extensionRegistry, type))
|
|
@@ -1322,7 +1272,7 @@ function resolveRegisteredCustomType(sourceNode, extensionRegistry, checker) {
|
|
|
1322
1272
|
return resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker);
|
|
1323
1273
|
}
|
|
1324
1274
|
function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, checker) {
|
|
1325
|
-
if (
|
|
1275
|
+
if (ts3.isParenthesizedTypeNode(typeNode)) {
|
|
1326
1276
|
return resolveRegisteredCustomTypeFromTypeNode(typeNode.type, extensionRegistry, checker);
|
|
1327
1277
|
}
|
|
1328
1278
|
const typeName = getTypeNodeRegistrationName(typeNode);
|
|
@@ -1337,8 +1287,8 @@ function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, ch
|
|
|
1337
1287
|
payload: null
|
|
1338
1288
|
};
|
|
1339
1289
|
}
|
|
1340
|
-
if (
|
|
1341
|
-
const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(
|
|
1290
|
+
if (ts3.isTypeReferenceNode(typeNode) && ts3.isIdentifier(typeNode.typeName)) {
|
|
1291
|
+
const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
|
|
1342
1292
|
if (aliasDecl !== void 0) {
|
|
1343
1293
|
return resolveRegisteredCustomTypeFromTypeNode(aliasDecl.type, extensionRegistry, checker);
|
|
1344
1294
|
}
|
|
@@ -1346,22 +1296,22 @@ function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, ch
|
|
|
1346
1296
|
return null;
|
|
1347
1297
|
}
|
|
1348
1298
|
function extractTypeNodeFromSource(sourceNode) {
|
|
1349
|
-
if (
|
|
1299
|
+
if (ts3.isPropertyDeclaration(sourceNode) || ts3.isPropertySignature(sourceNode) || ts3.isParameter(sourceNode) || ts3.isTypeAliasDeclaration(sourceNode)) {
|
|
1350
1300
|
return sourceNode.type;
|
|
1351
1301
|
}
|
|
1352
|
-
if (
|
|
1302
|
+
if (ts3.isTypeNode(sourceNode)) {
|
|
1353
1303
|
return sourceNode;
|
|
1354
1304
|
}
|
|
1355
1305
|
return void 0;
|
|
1356
1306
|
}
|
|
1357
1307
|
function getTypeNodeRegistrationName(typeNode) {
|
|
1358
|
-
if (
|
|
1359
|
-
return
|
|
1308
|
+
if (ts3.isTypeReferenceNode(typeNode)) {
|
|
1309
|
+
return ts3.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : typeNode.typeName.right.text;
|
|
1360
1310
|
}
|
|
1361
|
-
if (
|
|
1311
|
+
if (ts3.isParenthesizedTypeNode(typeNode)) {
|
|
1362
1312
|
return getTypeNodeRegistrationName(typeNode.type);
|
|
1363
1313
|
}
|
|
1364
|
-
if (typeNode.kind ===
|
|
1314
|
+
if (typeNode.kind === ts3.SyntaxKind.BigIntKeyword || typeNode.kind === ts3.SyntaxKind.StringKeyword || typeNode.kind === ts3.SyntaxKind.NumberKeyword || typeNode.kind === ts3.SyntaxKind.BooleanKeyword) {
|
|
1365
1315
|
return typeNode.getText();
|
|
1366
1316
|
}
|
|
1367
1317
|
return null;
|
|
@@ -1371,19 +1321,34 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
1371
1321
|
if (customType) {
|
|
1372
1322
|
return customType;
|
|
1373
1323
|
}
|
|
1374
|
-
|
|
1324
|
+
const primitiveAlias = tryResolveNamedPrimitiveAlias(
|
|
1325
|
+
type,
|
|
1326
|
+
checker,
|
|
1327
|
+
file,
|
|
1328
|
+
typeRegistry,
|
|
1329
|
+
visiting,
|
|
1330
|
+
sourceNode,
|
|
1331
|
+
extensionRegistry
|
|
1332
|
+
);
|
|
1333
|
+
if (primitiveAlias) {
|
|
1334
|
+
return primitiveAlias;
|
|
1335
|
+
}
|
|
1336
|
+
if (type.flags & ts3.TypeFlags.String) {
|
|
1375
1337
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1376
1338
|
}
|
|
1377
|
-
if (type.flags &
|
|
1339
|
+
if (type.flags & ts3.TypeFlags.Number) {
|
|
1378
1340
|
return { kind: "primitive", primitiveKind: "number" };
|
|
1379
1341
|
}
|
|
1380
|
-
if (type.flags &
|
|
1342
|
+
if (type.flags & (ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral)) {
|
|
1343
|
+
return { kind: "primitive", primitiveKind: "bigint" };
|
|
1344
|
+
}
|
|
1345
|
+
if (type.flags & ts3.TypeFlags.Boolean) {
|
|
1381
1346
|
return { kind: "primitive", primitiveKind: "boolean" };
|
|
1382
1347
|
}
|
|
1383
|
-
if (type.flags &
|
|
1348
|
+
if (type.flags & ts3.TypeFlags.Null) {
|
|
1384
1349
|
return { kind: "primitive", primitiveKind: "null" };
|
|
1385
1350
|
}
|
|
1386
|
-
if (type.flags &
|
|
1351
|
+
if (type.flags & ts3.TypeFlags.Undefined) {
|
|
1387
1352
|
return { kind: "primitive", primitiveKind: "null" };
|
|
1388
1353
|
}
|
|
1389
1354
|
if (type.isStringLiteral()) {
|
|
@@ -1425,6 +1390,75 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
1425
1390
|
}
|
|
1426
1391
|
return { kind: "primitive", primitiveKind: "string" };
|
|
1427
1392
|
}
|
|
1393
|
+
function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
1394
|
+
if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
|
|
1395
|
+
return null;
|
|
1396
|
+
}
|
|
1397
|
+
const aliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration) ?? getReferencedTypeAliasDeclaration(sourceNode, checker);
|
|
1398
|
+
if (!aliasDecl) {
|
|
1399
|
+
return null;
|
|
1400
|
+
}
|
|
1401
|
+
const aliasName = aliasDecl.name.text;
|
|
1402
|
+
if (!typeRegistry[aliasName]) {
|
|
1403
|
+
const aliasType = checker.getTypeFromTypeNode(aliasDecl.type);
|
|
1404
|
+
const constraints = [
|
|
1405
|
+
...extractJSDocConstraintNodes(aliasDecl, file, makeParseOptions(extensionRegistry)),
|
|
1406
|
+
...extractTypeAliasConstraintNodes(aliasDecl.type, checker, file, extensionRegistry)
|
|
1407
|
+
];
|
|
1408
|
+
const annotations = extractJSDocAnnotationNodes(
|
|
1409
|
+
aliasDecl,
|
|
1410
|
+
file,
|
|
1411
|
+
makeParseOptions(extensionRegistry)
|
|
1412
|
+
);
|
|
1413
|
+
typeRegistry[aliasName] = {
|
|
1414
|
+
name: aliasName,
|
|
1415
|
+
type: resolveAliasedPrimitiveTarget(
|
|
1416
|
+
aliasType,
|
|
1417
|
+
checker,
|
|
1418
|
+
file,
|
|
1419
|
+
typeRegistry,
|
|
1420
|
+
visiting,
|
|
1421
|
+
extensionRegistry
|
|
1422
|
+
),
|
|
1423
|
+
...constraints.length > 0 && { constraints },
|
|
1424
|
+
...annotations.length > 0 && { annotations },
|
|
1425
|
+
provenance: provenanceForDeclaration(aliasDecl, file)
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
return { kind: "reference", name: aliasName, typeArguments: [] };
|
|
1429
|
+
}
|
|
1430
|
+
function getReferencedTypeAliasDeclaration(sourceNode, checker) {
|
|
1431
|
+
const typeNode = sourceNode && (ts3.isPropertyDeclaration(sourceNode) || ts3.isPropertySignature(sourceNode) || ts3.isParameter(sourceNode)) ? sourceNode.type : void 0;
|
|
1432
|
+
if (!typeNode || !ts3.isTypeReferenceNode(typeNode)) {
|
|
1433
|
+
return void 0;
|
|
1434
|
+
}
|
|
1435
|
+
return checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
|
|
1436
|
+
}
|
|
1437
|
+
function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
|
|
1438
|
+
if (!ts3.isTypeReferenceNode(typeNode)) {
|
|
1439
|
+
return false;
|
|
1440
|
+
}
|
|
1441
|
+
const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
|
|
1442
|
+
if (!aliasDecl) {
|
|
1443
|
+
return false;
|
|
1444
|
+
}
|
|
1445
|
+
const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
|
|
1446
|
+
return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
|
|
1447
|
+
}
|
|
1448
|
+
function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1449
|
+
const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
|
|
1450
|
+
if (nestedAliasDecl !== void 0) {
|
|
1451
|
+
return resolveAliasedPrimitiveTarget(
|
|
1452
|
+
checker.getTypeFromTypeNode(nestedAliasDecl.type),
|
|
1453
|
+
checker,
|
|
1454
|
+
file,
|
|
1455
|
+
typeRegistry,
|
|
1456
|
+
visiting,
|
|
1457
|
+
extensionRegistry
|
|
1458
|
+
);
|
|
1459
|
+
}
|
|
1460
|
+
return resolveTypeNode(type, checker, file, typeRegistry, visiting, void 0, extensionRegistry);
|
|
1461
|
+
}
|
|
1428
1462
|
function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry) {
|
|
1429
1463
|
const typeName = getNamedTypeName(type);
|
|
1430
1464
|
const namedDecl = getNamedTypeDeclaration(type);
|
|
@@ -1437,13 +1471,13 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
1437
1471
|
(memberTypeNode) => !isNullishTypeNode(resolveAliasedTypeNode(memberTypeNode, checker))
|
|
1438
1472
|
);
|
|
1439
1473
|
const nonNullTypes = allTypes.filter(
|
|
1440
|
-
(memberType) => !(memberType.flags & (
|
|
1474
|
+
(memberType) => !(memberType.flags & (ts3.TypeFlags.Null | ts3.TypeFlags.Undefined))
|
|
1441
1475
|
);
|
|
1442
1476
|
const nonNullMembers = nonNullTypes.map((memberType, index) => ({
|
|
1443
1477
|
memberType,
|
|
1444
1478
|
sourceNode: nonNullSourceNodes.length === nonNullTypes.length ? nonNullSourceNodes[index] : void 0
|
|
1445
1479
|
}));
|
|
1446
|
-
const hasNull = allTypes.some((t) => t.flags &
|
|
1480
|
+
const hasNull = allTypes.some((t) => t.flags & ts3.TypeFlags.Null);
|
|
1447
1481
|
const memberDisplayNames = /* @__PURE__ */ new Map();
|
|
1448
1482
|
if (namedDecl) {
|
|
1449
1483
|
for (const [value, label] of extractDisplayNameMetadata(namedDecl).memberDisplayNames) {
|
|
@@ -1472,7 +1506,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
|
|
|
1472
1506
|
const displayName = memberDisplayNames.get(String(value));
|
|
1473
1507
|
return displayName !== void 0 ? { value, displayName } : { value };
|
|
1474
1508
|
});
|
|
1475
|
-
const isBooleanUnion2 = nonNullTypes.length === 2 && nonNullTypes.every((t) => t.flags &
|
|
1509
|
+
const isBooleanUnion2 = nonNullTypes.length === 2 && nonNullTypes.every((t) => t.flags & ts3.TypeFlags.BooleanLiteral);
|
|
1476
1510
|
if (isBooleanUnion2) {
|
|
1477
1511
|
const boolNode = { kind: "primitive", primitiveKind: "boolean" };
|
|
1478
1512
|
const result = hasNull ? {
|
|
@@ -1558,7 +1592,7 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, exten
|
|
|
1558
1592
|
if (type.getProperties().length > 0) {
|
|
1559
1593
|
return null;
|
|
1560
1594
|
}
|
|
1561
|
-
const indexInfo = checker.getIndexInfoOfType(type,
|
|
1595
|
+
const indexInfo = checker.getIndexInfoOfType(type, ts3.IndexKind.String);
|
|
1562
1596
|
if (!indexInfo) {
|
|
1563
1597
|
return null;
|
|
1564
1598
|
}
|
|
@@ -1669,7 +1703,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, extensio
|
|
|
1669
1703
|
const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
1670
1704
|
if (!declaration) continue;
|
|
1671
1705
|
const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
|
|
1672
|
-
const optional = !!(prop.flags &
|
|
1706
|
+
const optional = !!(prop.flags & ts3.SymbolFlags.Optional);
|
|
1673
1707
|
const propTypeNode = resolveTypeNode(
|
|
1674
1708
|
propType,
|
|
1675
1709
|
checker,
|
|
@@ -1714,11 +1748,11 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
1714
1748
|
for (const symbol of symbols) {
|
|
1715
1749
|
const declarations = symbol.declarations;
|
|
1716
1750
|
if (!declarations) continue;
|
|
1717
|
-
const classDecl = declarations.find(
|
|
1751
|
+
const classDecl = declarations.find(ts3.isClassDeclaration);
|
|
1718
1752
|
if (classDecl) {
|
|
1719
1753
|
const map = /* @__PURE__ */ new Map();
|
|
1720
1754
|
for (const member of classDecl.members) {
|
|
1721
|
-
if (
|
|
1755
|
+
if (ts3.isPropertyDeclaration(member) && ts3.isIdentifier(member.name)) {
|
|
1722
1756
|
const fieldNode = analyzeFieldToIR(
|
|
1723
1757
|
member,
|
|
1724
1758
|
checker,
|
|
@@ -1738,7 +1772,7 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
1738
1772
|
}
|
|
1739
1773
|
return map;
|
|
1740
1774
|
}
|
|
1741
|
-
const interfaceDecl = declarations.find(
|
|
1775
|
+
const interfaceDecl = declarations.find(ts3.isInterfaceDeclaration);
|
|
1742
1776
|
if (interfaceDecl) {
|
|
1743
1777
|
return buildFieldNodeInfoMap(
|
|
1744
1778
|
interfaceDecl.members,
|
|
@@ -1749,8 +1783,8 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
|
|
|
1749
1783
|
extensionRegistry
|
|
1750
1784
|
);
|
|
1751
1785
|
}
|
|
1752
|
-
const typeAliasDecl = declarations.find(
|
|
1753
|
-
if (typeAliasDecl &&
|
|
1786
|
+
const typeAliasDecl = declarations.find(ts3.isTypeAliasDeclaration);
|
|
1787
|
+
if (typeAliasDecl && ts3.isTypeLiteralNode(typeAliasDecl.type)) {
|
|
1754
1788
|
return buildFieldNodeInfoMap(
|
|
1755
1789
|
typeAliasDecl.type.members,
|
|
1756
1790
|
checker,
|
|
@@ -1769,10 +1803,10 @@ function extractArrayElementTypeNode(sourceNode, checker) {
|
|
|
1769
1803
|
return void 0;
|
|
1770
1804
|
}
|
|
1771
1805
|
const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
|
|
1772
|
-
if (
|
|
1806
|
+
if (ts3.isArrayTypeNode(resolvedTypeNode)) {
|
|
1773
1807
|
return resolvedTypeNode.elementType;
|
|
1774
1808
|
}
|
|
1775
|
-
if (
|
|
1809
|
+
if (ts3.isTypeReferenceNode(resolvedTypeNode) && ts3.isIdentifier(resolvedTypeNode.typeName) && resolvedTypeNode.typeName.text === "Array" && resolvedTypeNode.typeArguments?.[0]) {
|
|
1776
1810
|
return resolvedTypeNode.typeArguments[0];
|
|
1777
1811
|
}
|
|
1778
1812
|
return void 0;
|
|
@@ -1783,17 +1817,17 @@ function extractUnionMemberTypeNodes(sourceNode, checker) {
|
|
|
1783
1817
|
return [];
|
|
1784
1818
|
}
|
|
1785
1819
|
const resolvedTypeNode = resolveAliasedTypeNode(typeNode, checker);
|
|
1786
|
-
return
|
|
1820
|
+
return ts3.isUnionTypeNode(resolvedTypeNode) ? [...resolvedTypeNode.types] : [];
|
|
1787
1821
|
}
|
|
1788
1822
|
function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new Set()) {
|
|
1789
|
-
if (
|
|
1823
|
+
if (ts3.isParenthesizedTypeNode(typeNode)) {
|
|
1790
1824
|
return resolveAliasedTypeNode(typeNode.type, checker, visited);
|
|
1791
1825
|
}
|
|
1792
|
-
if (!
|
|
1826
|
+
if (!ts3.isTypeReferenceNode(typeNode) || !ts3.isIdentifier(typeNode.typeName)) {
|
|
1793
1827
|
return typeNode;
|
|
1794
1828
|
}
|
|
1795
1829
|
const symbol = checker.getSymbolAtLocation(typeNode.typeName);
|
|
1796
|
-
const aliasDecl = symbol?.declarations?.find(
|
|
1830
|
+
const aliasDecl = symbol?.declarations?.find(ts3.isTypeAliasDeclaration);
|
|
1797
1831
|
if (aliasDecl === void 0 || visited.has(aliasDecl)) {
|
|
1798
1832
|
return typeNode;
|
|
1799
1833
|
}
|
|
@@ -1801,15 +1835,15 @@ function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new
|
|
|
1801
1835
|
return resolveAliasedTypeNode(aliasDecl.type, checker, visited);
|
|
1802
1836
|
}
|
|
1803
1837
|
function isNullishTypeNode(typeNode) {
|
|
1804
|
-
if (typeNode.kind ===
|
|
1838
|
+
if (typeNode.kind === ts3.SyntaxKind.NullKeyword || typeNode.kind === ts3.SyntaxKind.UndefinedKeyword) {
|
|
1805
1839
|
return true;
|
|
1806
1840
|
}
|
|
1807
|
-
return
|
|
1841
|
+
return ts3.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts3.SyntaxKind.NullKeyword || typeNode.literal.kind === ts3.SyntaxKind.UndefinedKeyword);
|
|
1808
1842
|
}
|
|
1809
1843
|
function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, extensionRegistry) {
|
|
1810
1844
|
const map = /* @__PURE__ */ new Map();
|
|
1811
1845
|
for (const member of members) {
|
|
1812
|
-
if (
|
|
1846
|
+
if (ts3.isPropertySignature(member)) {
|
|
1813
1847
|
const fieldNode = analyzeInterfacePropertyToIR(
|
|
1814
1848
|
member,
|
|
1815
1849
|
checker,
|
|
@@ -1831,7 +1865,7 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, e
|
|
|
1831
1865
|
}
|
|
1832
1866
|
var MAX_ALIAS_CHAIN_DEPTH = 8;
|
|
1833
1867
|
function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegistry, depth = 0) {
|
|
1834
|
-
if (!
|
|
1868
|
+
if (!ts3.isTypeReferenceNode(typeNode)) return [];
|
|
1835
1869
|
if (depth >= MAX_ALIAS_CHAIN_DEPTH) {
|
|
1836
1870
|
const aliasName = typeNode.typeName.getText();
|
|
1837
1871
|
throw new Error(
|
|
@@ -1840,9 +1874,9 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
|
|
|
1840
1874
|
}
|
|
1841
1875
|
const symbol = checker.getSymbolAtLocation(typeNode.typeName);
|
|
1842
1876
|
if (!symbol?.declarations) return [];
|
|
1843
|
-
const aliasDecl = symbol.declarations.find(
|
|
1877
|
+
const aliasDecl = symbol.declarations.find(ts3.isTypeAliasDeclaration);
|
|
1844
1878
|
if (!aliasDecl) return [];
|
|
1845
|
-
if (
|
|
1879
|
+
if (ts3.isTypeLiteralNode(aliasDecl.type)) return [];
|
|
1846
1880
|
const aliasFieldType = resolveTypeNode(
|
|
1847
1881
|
checker.getTypeAtLocation(aliasDecl.type),
|
|
1848
1882
|
checker,
|
|
@@ -1858,13 +1892,7 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
|
|
|
1858
1892
|
makeParseOptions(extensionRegistry, aliasFieldType)
|
|
1859
1893
|
);
|
|
1860
1894
|
constraints.push(
|
|
1861
|
-
...extractTypeAliasConstraintNodes(
|
|
1862
|
-
aliasDecl.type,
|
|
1863
|
-
checker,
|
|
1864
|
-
file,
|
|
1865
|
-
extensionRegistry,
|
|
1866
|
-
depth + 1
|
|
1867
|
-
)
|
|
1895
|
+
...extractTypeAliasConstraintNodes(aliasDecl.type, checker, file, extensionRegistry, depth + 1)
|
|
1868
1896
|
);
|
|
1869
1897
|
return constraints;
|
|
1870
1898
|
}
|
|
@@ -1891,14 +1919,14 @@ function getNamedTypeName(type) {
|
|
|
1891
1919
|
const symbol = type.getSymbol();
|
|
1892
1920
|
if (symbol?.declarations) {
|
|
1893
1921
|
const decl = symbol.declarations[0];
|
|
1894
|
-
if (decl && (
|
|
1895
|
-
const name =
|
|
1922
|
+
if (decl && (ts3.isClassDeclaration(decl) || ts3.isInterfaceDeclaration(decl) || ts3.isTypeAliasDeclaration(decl))) {
|
|
1923
|
+
const name = ts3.isClassDeclaration(decl) ? decl.name?.text : decl.name.text;
|
|
1896
1924
|
if (name) return name;
|
|
1897
1925
|
}
|
|
1898
1926
|
}
|
|
1899
1927
|
const aliasSymbol = type.aliasSymbol;
|
|
1900
1928
|
if (aliasSymbol?.declarations) {
|
|
1901
|
-
const aliasDecl = aliasSymbol.declarations.find(
|
|
1929
|
+
const aliasDecl = aliasSymbol.declarations.find(ts3.isTypeAliasDeclaration);
|
|
1902
1930
|
if (aliasDecl) {
|
|
1903
1931
|
return aliasDecl.name.text;
|
|
1904
1932
|
}
|
|
@@ -1909,24 +1937,24 @@ function getNamedTypeDeclaration(type) {
|
|
|
1909
1937
|
const symbol = type.getSymbol();
|
|
1910
1938
|
if (symbol?.declarations) {
|
|
1911
1939
|
const decl = symbol.declarations[0];
|
|
1912
|
-
if (decl && (
|
|
1940
|
+
if (decl && (ts3.isClassDeclaration(decl) || ts3.isInterfaceDeclaration(decl) || ts3.isTypeAliasDeclaration(decl))) {
|
|
1913
1941
|
return decl;
|
|
1914
1942
|
}
|
|
1915
1943
|
}
|
|
1916
1944
|
const aliasSymbol = type.aliasSymbol;
|
|
1917
1945
|
if (aliasSymbol?.declarations) {
|
|
1918
|
-
return aliasSymbol.declarations.find(
|
|
1946
|
+
return aliasSymbol.declarations.find(ts3.isTypeAliasDeclaration);
|
|
1919
1947
|
}
|
|
1920
1948
|
return void 0;
|
|
1921
1949
|
}
|
|
1922
1950
|
function analyzeMethod(method, checker) {
|
|
1923
|
-
if (!
|
|
1951
|
+
if (!ts3.isIdentifier(method.name)) {
|
|
1924
1952
|
return null;
|
|
1925
1953
|
}
|
|
1926
1954
|
const name = method.name.text;
|
|
1927
1955
|
const parameters = [];
|
|
1928
1956
|
for (const param of method.parameters) {
|
|
1929
|
-
if (
|
|
1957
|
+
if (ts3.isIdentifier(param.name)) {
|
|
1930
1958
|
const paramInfo = analyzeParameter(param, checker);
|
|
1931
1959
|
parameters.push(paramInfo);
|
|
1932
1960
|
}
|
|
@@ -1937,7 +1965,7 @@ function analyzeMethod(method, checker) {
|
|
|
1937
1965
|
return { name, parameters, returnTypeNode, returnType };
|
|
1938
1966
|
}
|
|
1939
1967
|
function analyzeParameter(param, checker) {
|
|
1940
|
-
const name =
|
|
1968
|
+
const name = ts3.isIdentifier(param.name) ? param.name.text : "param";
|
|
1941
1969
|
const typeNode = param.type;
|
|
1942
1970
|
const type = checker.getTypeAtLocation(param);
|
|
1943
1971
|
const formSpecExportName = detectFormSpecReference(typeNode);
|
|
@@ -1946,20 +1974,90 @@ function analyzeParameter(param, checker) {
|
|
|
1946
1974
|
}
|
|
1947
1975
|
function detectFormSpecReference(typeNode) {
|
|
1948
1976
|
if (!typeNode) return null;
|
|
1949
|
-
if (!
|
|
1950
|
-
const typeName =
|
|
1977
|
+
if (!ts3.isTypeReferenceNode(typeNode)) return null;
|
|
1978
|
+
const typeName = ts3.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : ts3.isQualifiedName(typeNode.typeName) ? typeNode.typeName.right.text : null;
|
|
1951
1979
|
if (typeName !== "InferSchema" && typeName !== "InferFormSchema") return null;
|
|
1952
1980
|
const typeArg = typeNode.typeArguments?.[0];
|
|
1953
|
-
if (!typeArg || !
|
|
1954
|
-
if (
|
|
1981
|
+
if (!typeArg || !ts3.isTypeQueryNode(typeArg)) return null;
|
|
1982
|
+
if (ts3.isIdentifier(typeArg.exprName)) {
|
|
1955
1983
|
return typeArg.exprName.text;
|
|
1956
1984
|
}
|
|
1957
|
-
if (
|
|
1985
|
+
if (ts3.isQualifiedName(typeArg.exprName)) {
|
|
1958
1986
|
return typeArg.exprName.right.text;
|
|
1959
1987
|
}
|
|
1960
1988
|
return null;
|
|
1961
1989
|
}
|
|
1962
1990
|
|
|
1991
|
+
// src/analyzer/program.ts
|
|
1992
|
+
function createProgramContext(filePath) {
|
|
1993
|
+
const absolutePath = path.resolve(filePath);
|
|
1994
|
+
const fileDir = path.dirname(absolutePath);
|
|
1995
|
+
const configPath = ts4.findConfigFile(fileDir, ts4.sys.fileExists.bind(ts4.sys), "tsconfig.json");
|
|
1996
|
+
let compilerOptions;
|
|
1997
|
+
let fileNames;
|
|
1998
|
+
if (configPath) {
|
|
1999
|
+
const configFile = ts4.readConfigFile(configPath, ts4.sys.readFile.bind(ts4.sys));
|
|
2000
|
+
if (configFile.error) {
|
|
2001
|
+
throw new Error(
|
|
2002
|
+
`Error reading tsconfig.json: ${ts4.flattenDiagnosticMessageText(configFile.error.messageText, "\n")}`
|
|
2003
|
+
);
|
|
2004
|
+
}
|
|
2005
|
+
const parsed = ts4.parseJsonConfigFileContent(
|
|
2006
|
+
configFile.config,
|
|
2007
|
+
ts4.sys,
|
|
2008
|
+
path.dirname(configPath)
|
|
2009
|
+
);
|
|
2010
|
+
if (parsed.errors.length > 0) {
|
|
2011
|
+
const errorMessages = parsed.errors.map((e) => ts4.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n");
|
|
2012
|
+
throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
|
|
2013
|
+
}
|
|
2014
|
+
compilerOptions = parsed.options;
|
|
2015
|
+
fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
|
|
2016
|
+
} else {
|
|
2017
|
+
compilerOptions = {
|
|
2018
|
+
target: ts4.ScriptTarget.ES2022,
|
|
2019
|
+
module: ts4.ModuleKind.NodeNext,
|
|
2020
|
+
moduleResolution: ts4.ModuleResolutionKind.NodeNext,
|
|
2021
|
+
strict: true,
|
|
2022
|
+
skipLibCheck: true,
|
|
2023
|
+
declaration: true
|
|
2024
|
+
};
|
|
2025
|
+
fileNames = [absolutePath];
|
|
2026
|
+
}
|
|
2027
|
+
const program = ts4.createProgram(fileNames, compilerOptions);
|
|
2028
|
+
const sourceFile = program.getSourceFile(absolutePath);
|
|
2029
|
+
if (!sourceFile) {
|
|
2030
|
+
throw new Error(`Could not find source file: ${absolutePath}`);
|
|
2031
|
+
}
|
|
2032
|
+
return {
|
|
2033
|
+
program,
|
|
2034
|
+
checker: program.getTypeChecker(),
|
|
2035
|
+
sourceFile
|
|
2036
|
+
};
|
|
2037
|
+
}
|
|
2038
|
+
function findNodeByName(sourceFile, name, predicate, getName) {
|
|
2039
|
+
let result = null;
|
|
2040
|
+
function visit(node) {
|
|
2041
|
+
if (result) return;
|
|
2042
|
+
if (predicate(node) && getName(node) === name) {
|
|
2043
|
+
result = node;
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
ts4.forEachChild(node, visit);
|
|
2047
|
+
}
|
|
2048
|
+
visit(sourceFile);
|
|
2049
|
+
return result;
|
|
2050
|
+
}
|
|
2051
|
+
function findClassByName(sourceFile, className) {
|
|
2052
|
+
return findNodeByName(sourceFile, className, ts4.isClassDeclaration, (n) => n.name?.text);
|
|
2053
|
+
}
|
|
2054
|
+
function findInterfaceByName(sourceFile, interfaceName) {
|
|
2055
|
+
return findNodeByName(sourceFile, interfaceName, ts4.isInterfaceDeclaration, (n) => n.name.text);
|
|
2056
|
+
}
|
|
2057
|
+
function findTypeAliasByName(sourceFile, aliasName) {
|
|
2058
|
+
return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
|
|
2059
|
+
}
|
|
2060
|
+
|
|
1963
2061
|
// src/json-schema/ir-generator.ts
|
|
1964
2062
|
function makeContext(options) {
|
|
1965
2063
|
const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
|
|
@@ -1978,6 +2076,9 @@ function generateJsonSchemaFromIR(ir, options) {
|
|
|
1978
2076
|
const ctx = makeContext(options);
|
|
1979
2077
|
for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
|
|
1980
2078
|
ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
|
|
2079
|
+
if (typeDef.constraints && typeDef.constraints.length > 0) {
|
|
2080
|
+
applyConstraints(ctx.defs[name], typeDef.constraints, ctx);
|
|
2081
|
+
}
|
|
1981
2082
|
if (typeDef.annotations && typeDef.annotations.length > 0) {
|
|
1982
2083
|
applyAnnotations(ctx.defs[name], typeDef.annotations, ctx);
|
|
1983
2084
|
}
|
|
@@ -2146,7 +2247,9 @@ function generateTypeNode(type, ctx) {
|
|
|
2146
2247
|
}
|
|
2147
2248
|
}
|
|
2148
2249
|
function generatePrimitiveType(type) {
|
|
2149
|
-
return {
|
|
2250
|
+
return {
|
|
2251
|
+
type: type.primitiveKind === "integer" || type.primitiveKind === "bigint" ? "integer" : type.primitiveKind
|
|
2252
|
+
};
|
|
2150
2253
|
}
|
|
2151
2254
|
function generateEnumType(type) {
|
|
2152
2255
|
const hasDisplayNames = type.members.some((m) => m.displayName !== void 0);
|
|
@@ -2319,7 +2422,7 @@ function applyAnnotations(schema, annotations, ctx) {
|
|
|
2319
2422
|
case "deprecated":
|
|
2320
2423
|
schema.deprecated = true;
|
|
2321
2424
|
if (annotation.message !== void 0 && annotation.message !== "") {
|
|
2322
|
-
schema[
|
|
2425
|
+
schema[`${ctx.vendorPrefix}-deprecation-description`] = annotation.message;
|
|
2323
2426
|
}
|
|
2324
2427
|
break;
|
|
2325
2428
|
case "placeholder":
|
|
@@ -2610,15 +2713,6 @@ function generateUiSchemaFromIR(ir) {
|
|
|
2610
2713
|
return parseOrThrow(uiSchema, result, "UI Schema");
|
|
2611
2714
|
}
|
|
2612
2715
|
|
|
2613
|
-
// src/generators/class-schema.ts
|
|
2614
|
-
function generateClassSchemas(analysis, source, options) {
|
|
2615
|
-
const ir = canonicalizeTSDoc(analysis, source);
|
|
2616
|
-
return {
|
|
2617
|
-
jsonSchema: generateJsonSchemaFromIR(ir, options),
|
|
2618
|
-
uiSchema: generateUiSchemaFromIR(ir)
|
|
2619
|
-
};
|
|
2620
|
-
}
|
|
2621
|
-
|
|
2622
2716
|
// src/validate/constraint-validator.ts
|
|
2623
2717
|
import { normalizeConstraintTagName as normalizeConstraintTagName2 } from "@formspec/core";
|
|
2624
2718
|
function addContradiction(ctx, message, primary, related) {
|
|
@@ -3096,6 +3190,26 @@ function dereferenceType(ctx, type) {
|
|
|
3096
3190
|
}
|
|
3097
3191
|
return current;
|
|
3098
3192
|
}
|
|
3193
|
+
function collectReferencedTypeConstraints(ctx, type) {
|
|
3194
|
+
const collected = [];
|
|
3195
|
+
let current = type;
|
|
3196
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3197
|
+
while (current.kind === "reference") {
|
|
3198
|
+
if (seen.has(current.name)) {
|
|
3199
|
+
break;
|
|
3200
|
+
}
|
|
3201
|
+
seen.add(current.name);
|
|
3202
|
+
const definition = ctx.typeRegistry[current.name];
|
|
3203
|
+
if (definition === void 0) {
|
|
3204
|
+
break;
|
|
3205
|
+
}
|
|
3206
|
+
if (definition.constraints !== void 0) {
|
|
3207
|
+
collected.push(...definition.constraints);
|
|
3208
|
+
}
|
|
3209
|
+
current = definition.type;
|
|
3210
|
+
}
|
|
3211
|
+
return collected;
|
|
3212
|
+
}
|
|
3099
3213
|
function resolvePathTargetType(ctx, type, segments) {
|
|
3100
3214
|
const effectiveType = dereferenceType(ctx, type);
|
|
3101
3215
|
if (segments.length === 0) {
|
|
@@ -3117,12 +3231,33 @@ function resolvePathTargetType(ctx, type, segments) {
|
|
|
3117
3231
|
}
|
|
3118
3232
|
return { kind: "unresolvable", type: effectiveType };
|
|
3119
3233
|
}
|
|
3234
|
+
function isNullType(type) {
|
|
3235
|
+
return type.kind === "primitive" && type.primitiveKind === "null";
|
|
3236
|
+
}
|
|
3237
|
+
function collectCustomConstraintCandidateTypes(ctx, type) {
|
|
3238
|
+
const effectiveType = dereferenceType(ctx, type);
|
|
3239
|
+
const candidates = [effectiveType];
|
|
3240
|
+
if (effectiveType.kind === "array") {
|
|
3241
|
+
candidates.push(...collectCustomConstraintCandidateTypes(ctx, effectiveType.items));
|
|
3242
|
+
}
|
|
3243
|
+
if (effectiveType.kind === "union") {
|
|
3244
|
+
const memberTypes = effectiveType.members.map((member) => dereferenceType(ctx, member));
|
|
3245
|
+
const nonNullMembers = memberTypes.filter((member) => !isNullType(member));
|
|
3246
|
+
if (nonNullMembers.length === 1 && nonNullMembers.length < memberTypes.length) {
|
|
3247
|
+
const [nullableMember] = nonNullMembers;
|
|
3248
|
+
if (nullableMember !== void 0) {
|
|
3249
|
+
candidates.push(...collectCustomConstraintCandidateTypes(ctx, nullableMember));
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
return candidates;
|
|
3254
|
+
}
|
|
3120
3255
|
function formatPathTargetFieldName(fieldName, path2) {
|
|
3121
3256
|
return path2.length === 0 ? fieldName : `${fieldName}.${path2.join(".")}`;
|
|
3122
3257
|
}
|
|
3123
3258
|
function checkConstraintOnType(ctx, fieldName, type, constraint) {
|
|
3124
3259
|
const effectiveType = dereferenceType(ctx, type);
|
|
3125
|
-
const isNumber = effectiveType.kind === "primitive" &&
|
|
3260
|
+
const isNumber = effectiveType.kind === "primitive" && ["number", "integer", "bigint"].includes(effectiveType.primitiveKind);
|
|
3126
3261
|
const isString = effectiveType.kind === "primitive" && effectiveType.primitiveKind === "string";
|
|
3127
3262
|
const isArray = effectiveType.kind === "array";
|
|
3128
3263
|
const isEnum = effectiveType.kind === "enum";
|
|
@@ -3180,7 +3315,9 @@ function checkConstraintOnType(ctx, fieldName, type, constraint) {
|
|
|
3180
3315
|
break;
|
|
3181
3316
|
}
|
|
3182
3317
|
case "const": {
|
|
3183
|
-
const isPrimitiveConstType = effectiveType.kind === "primitive" && ["string", "number", "boolean", "null"].includes(
|
|
3318
|
+
const isPrimitiveConstType = effectiveType.kind === "primitive" && ["string", "number", "integer", "bigint", "boolean", "null"].includes(
|
|
3319
|
+
effectiveType.primitiveKind
|
|
3320
|
+
) || effectiveType.kind === "enum";
|
|
3184
3321
|
if (!isPrimitiveConstType) {
|
|
3185
3322
|
addTypeMismatch(
|
|
3186
3323
|
ctx,
|
|
@@ -3191,7 +3328,8 @@ function checkConstraintOnType(ctx, fieldName, type, constraint) {
|
|
|
3191
3328
|
}
|
|
3192
3329
|
if (effectiveType.kind === "primitive") {
|
|
3193
3330
|
const valueType = constraint.value === null ? "null" : Array.isArray(constraint.value) ? "array" : typeof constraint.value;
|
|
3194
|
-
|
|
3331
|
+
const expectedValueType = effectiveType.primitiveKind === "integer" || effectiveType.primitiveKind === "bigint" ? "number" : effectiveType.primitiveKind;
|
|
3332
|
+
if (valueType !== expectedValueType) {
|
|
3195
3333
|
addTypeMismatch(
|
|
3196
3334
|
ctx,
|
|
3197
3335
|
`Field "${fieldName}": @const value type "${valueType}" is incompatible with field type "${effectiveType.primitiveKind}"`,
|
|
@@ -3260,11 +3398,14 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
|
|
|
3260
3398
|
);
|
|
3261
3399
|
return;
|
|
3262
3400
|
}
|
|
3401
|
+
const candidateTypes = collectCustomConstraintCandidateTypes(ctx, type);
|
|
3263
3402
|
const normalizedTagName = constraint.provenance.tagName === void 0 ? void 0 : normalizeConstraintTagName2(constraint.provenance.tagName.replace(/^@/, ""));
|
|
3264
3403
|
if (normalizedTagName !== void 0) {
|
|
3265
3404
|
const tagRegistration = ctx.extensionRegistry.findConstraintTag(normalizedTagName);
|
|
3266
3405
|
const extensionId = getExtensionIdFromConstraintId(constraint.constraintId);
|
|
3267
|
-
if (extensionId !== null && tagRegistration?.extensionId === extensionId && tagRegistration.registration.constraintName === registration.constraintName &&
|
|
3406
|
+
if (extensionId !== null && tagRegistration?.extensionId === extensionId && tagRegistration.registration.constraintName === registration.constraintName && !candidateTypes.some(
|
|
3407
|
+
(candidateType) => tagRegistration.registration.isApplicableToType?.(candidateType) !== false
|
|
3408
|
+
)) {
|
|
3268
3409
|
addTypeMismatch(
|
|
3269
3410
|
ctx,
|
|
3270
3411
|
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
|
|
@@ -3274,7 +3415,7 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
|
|
|
3274
3415
|
}
|
|
3275
3416
|
}
|
|
3276
3417
|
if (registration.applicableTypes === null) {
|
|
3277
|
-
if (registration.isApplicableToType?.(
|
|
3418
|
+
if (!candidateTypes.some((candidateType) => registration.isApplicableToType?.(candidateType) !== false)) {
|
|
3278
3419
|
addTypeMismatch(
|
|
3279
3420
|
ctx,
|
|
3280
3421
|
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
|
|
@@ -3283,7 +3424,11 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
|
|
|
3283
3424
|
}
|
|
3284
3425
|
return;
|
|
3285
3426
|
}
|
|
3286
|
-
|
|
3427
|
+
const applicableTypes = registration.applicableTypes;
|
|
3428
|
+
const matchesApplicableType = candidateTypes.some(
|
|
3429
|
+
(candidateType) => applicableTypes.includes(candidateType.kind) && registration.isApplicableToType?.(candidateType) !== false
|
|
3430
|
+
);
|
|
3431
|
+
if (!matchesApplicableType) {
|
|
3287
3432
|
addTypeMismatch(
|
|
3288
3433
|
ctx,
|
|
3289
3434
|
`Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
|
|
@@ -3292,7 +3437,10 @@ function checkCustomConstraint(ctx, fieldName, type, constraint) {
|
|
|
3292
3437
|
}
|
|
3293
3438
|
}
|
|
3294
3439
|
function validateFieldNode(ctx, field) {
|
|
3295
|
-
validateConstraints(ctx, field.name, field.type,
|
|
3440
|
+
validateConstraints(ctx, field.name, field.type, [
|
|
3441
|
+
...collectReferencedTypeConstraints(ctx, field.type),
|
|
3442
|
+
...field.constraints
|
|
3443
|
+
]);
|
|
3296
3444
|
if (field.type.kind === "object") {
|
|
3297
3445
|
for (const prop of field.type.properties) {
|
|
3298
3446
|
validateObjectProperty(ctx, field.name, prop);
|
|
@@ -3301,7 +3449,10 @@ function validateFieldNode(ctx, field) {
|
|
|
3301
3449
|
}
|
|
3302
3450
|
function validateObjectProperty(ctx, parentName, prop) {
|
|
3303
3451
|
const qualifiedName = `${parentName}.${prop.name}`;
|
|
3304
|
-
validateConstraints(ctx, qualifiedName, prop.type,
|
|
3452
|
+
validateConstraints(ctx, qualifiedName, prop.type, [
|
|
3453
|
+
...collectReferencedTypeConstraints(ctx, prop.type),
|
|
3454
|
+
...prop.constraints
|
|
3455
|
+
]);
|
|
3305
3456
|
if (prop.type.kind === "object") {
|
|
3306
3457
|
for (const nestedProp of prop.type.properties) {
|
|
3307
3458
|
validateObjectProperty(ctx, qualifiedName, nestedProp);
|
|
@@ -3353,6 +3504,36 @@ function validateIR(ir, options) {
|
|
|
3353
3504
|
};
|
|
3354
3505
|
}
|
|
3355
3506
|
|
|
3507
|
+
// src/generators/class-schema.ts
|
|
3508
|
+
function generateClassSchemas(analysis, source, options) {
|
|
3509
|
+
const ir = canonicalizeTSDoc(analysis, source);
|
|
3510
|
+
const validationResult = validateIR(ir, {
|
|
3511
|
+
...options?.extensionRegistry !== void 0 && {
|
|
3512
|
+
extensionRegistry: options.extensionRegistry
|
|
3513
|
+
},
|
|
3514
|
+
...options?.vendorPrefix !== void 0 && { vendorPrefix: options.vendorPrefix }
|
|
3515
|
+
});
|
|
3516
|
+
if (!validationResult.valid) {
|
|
3517
|
+
throw new Error(formatValidationError(validationResult.diagnostics));
|
|
3518
|
+
}
|
|
3519
|
+
return {
|
|
3520
|
+
jsonSchema: generateJsonSchemaFromIR(ir, options),
|
|
3521
|
+
uiSchema: generateUiSchemaFromIR(ir)
|
|
3522
|
+
};
|
|
3523
|
+
}
|
|
3524
|
+
function formatValidationError(diagnostics) {
|
|
3525
|
+
const lines = diagnostics.map((diagnostic) => {
|
|
3526
|
+
const primary = formatLocation(diagnostic.primaryLocation);
|
|
3527
|
+
const related = diagnostic.relatedLocations.length > 0 ? ` [related: ${diagnostic.relatedLocations.map(formatLocation).join(", ")}]` : "";
|
|
3528
|
+
return `${diagnostic.code}: ${diagnostic.message} (${primary})${related}`;
|
|
3529
|
+
});
|
|
3530
|
+
return `FormSpec validation failed:
|
|
3531
|
+
${lines.map((line) => `- ${line}`).join("\n")}`;
|
|
3532
|
+
}
|
|
3533
|
+
function formatLocation(location) {
|
|
3534
|
+
return `${location.file}:${String(location.line)}:${String(location.column)}`;
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3356
3537
|
// src/extensions/registry.ts
|
|
3357
3538
|
function createExtensionRegistry(extensions) {
|
|
3358
3539
|
const typeMap = /* @__PURE__ */ new Map();
|
|
@@ -3454,6 +3635,7 @@ function typeToJsonSchema(type, checker) {
|
|
|
3454
3635
|
provenance: fieldProvenance
|
|
3455
3636
|
}
|
|
3456
3637
|
],
|
|
3638
|
+
rootAnnotations: [],
|
|
3457
3639
|
typeRegistry,
|
|
3458
3640
|
provenance: fieldProvenance
|
|
3459
3641
|
};
|