@formspec/build 0.1.0-alpha.14 → 0.1.0-alpha.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/extension-runtime.integration.test.d.ts +2 -0
- package/dist/__tests__/extension-runtime.integration.test.d.ts.map +1 -0
- package/dist/__tests__/fixtures/edge-cases.d.ts +11 -0
- package/dist/__tests__/fixtures/edge-cases.d.ts.map +1 -1
- package/dist/__tests__/fixtures/example-a-builtins.d.ts +6 -6
- package/dist/__tests__/fixtures/example-interface-types.d.ts +26 -26
- package/dist/__tests__/fixtures/example-interface-types.d.ts.map +1 -1
- package/dist/__tests__/jsdoc-constraints.test.d.ts +4 -5
- package/dist/__tests__/jsdoc-constraints.test.d.ts.map +1 -1
- package/dist/__tests__/parity/fixtures/plan-status/chain-dsl.d.ts +19 -0
- package/dist/__tests__/parity/fixtures/plan-status/chain-dsl.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/plan-status/expected-ir.d.ts +6 -0
- package/dist/__tests__/parity/fixtures/plan-status/expected-ir.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/plan-status/tsdoc.d.ts +17 -0
- package/dist/__tests__/parity/fixtures/plan-status/tsdoc.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/usd-cents/chain-dsl.d.ts +9 -0
- package/dist/__tests__/parity/fixtures/usd-cents/chain-dsl.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/usd-cents/expected-ir.d.ts +6 -0
- package/dist/__tests__/parity/fixtures/usd-cents/expected-ir.d.ts.map +1 -0
- package/dist/__tests__/parity/fixtures/usd-cents/tsdoc.d.ts +19 -0
- package/dist/__tests__/parity/fixtures/usd-cents/tsdoc.d.ts.map +1 -0
- package/dist/__tests__/parity/utils.d.ts +6 -1
- package/dist/__tests__/parity/utils.d.ts.map +1 -1
- package/dist/analyzer/class-analyzer.d.ts +1 -1
- package/dist/analyzer/class-analyzer.d.ts.map +1 -1
- package/dist/analyzer/jsdoc-constraints.d.ts +7 -51
- package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -1
- package/dist/analyzer/tsdoc-parser.d.ts +6 -8
- package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
- package/dist/browser.cjs +387 -98
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +15 -2
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +385 -98
- package/dist/browser.js.map +1 -1
- package/dist/build.d.ts +131 -5
- package/dist/canonicalize/tsdoc-canonicalizer.d.ts +3 -3
- package/dist/cli.cjs +272 -69
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +271 -72
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +257 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +20 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +255 -71
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +461 -137
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +459 -139
- package/dist/internals.js.map +1 -1
- package/dist/json-schema/generator.d.ts +8 -2
- package/dist/json-schema/generator.d.ts.map +1 -1
- package/dist/json-schema/ir-generator.d.ts +24 -2
- package/dist/json-schema/ir-generator.d.ts.map +1 -1
- package/dist/json-schema/types.d.ts +1 -1
- package/dist/json-schema/types.d.ts.map +1 -1
- package/dist/validate/constraint-validator.d.ts +3 -7
- package/dist/validate/constraint-validator.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -34,7 +34,9 @@ __export(index_exports, {
|
|
|
34
34
|
categorizationSchema: () => categorizationSchema,
|
|
35
35
|
categorySchema: () => categorySchema,
|
|
36
36
|
controlSchema: () => controlSchema,
|
|
37
|
+
createExtensionRegistry: () => createExtensionRegistry,
|
|
37
38
|
generateJsonSchema: () => generateJsonSchema,
|
|
39
|
+
generateJsonSchemaFromIR: () => generateJsonSchemaFromIR,
|
|
38
40
|
generateSchemas: () => generateSchemas,
|
|
39
41
|
generateSchemasFromClass: () => generateSchemasFromClass,
|
|
40
42
|
generateUiSchema: () => generateUiSchema,
|
|
@@ -236,7 +238,7 @@ function canonicalizeArrayField(field) {
|
|
|
236
238
|
const itemsType = {
|
|
237
239
|
kind: "object",
|
|
238
240
|
properties: itemProperties,
|
|
239
|
-
additionalProperties:
|
|
241
|
+
additionalProperties: true
|
|
240
242
|
};
|
|
241
243
|
const type = { kind: "array", items: itemsType };
|
|
242
244
|
const constraints = [];
|
|
@@ -271,7 +273,7 @@ function canonicalizeObjectField(field) {
|
|
|
271
273
|
const type = {
|
|
272
274
|
kind: "object",
|
|
273
275
|
properties,
|
|
274
|
-
additionalProperties:
|
|
276
|
+
additionalProperties: true
|
|
275
277
|
};
|
|
276
278
|
return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
|
|
277
279
|
}
|
|
@@ -443,11 +445,21 @@ function wrapInConditional(field, layout, provenance) {
|
|
|
443
445
|
}
|
|
444
446
|
|
|
445
447
|
// src/json-schema/ir-generator.ts
|
|
446
|
-
function makeContext() {
|
|
447
|
-
|
|
448
|
+
function makeContext(options) {
|
|
449
|
+
const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
|
|
450
|
+
if (!vendorPrefix.startsWith("x-")) {
|
|
451
|
+
throw new Error(
|
|
452
|
+
`Invalid vendorPrefix "${vendorPrefix}". Extension JSON Schema keywords must start with "x-".`
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
return {
|
|
456
|
+
defs: {},
|
|
457
|
+
extensionRegistry: options?.extensionRegistry,
|
|
458
|
+
vendorPrefix
|
|
459
|
+
};
|
|
448
460
|
}
|
|
449
|
-
function generateJsonSchemaFromIR(ir) {
|
|
450
|
-
const ctx = makeContext();
|
|
461
|
+
function generateJsonSchemaFromIR(ir, options) {
|
|
462
|
+
const ctx = makeContext(options);
|
|
451
463
|
for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
|
|
452
464
|
ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
|
|
453
465
|
}
|
|
@@ -499,16 +511,16 @@ function generateFieldSchema(field, ctx) {
|
|
|
499
511
|
directConstraints.push(c);
|
|
500
512
|
}
|
|
501
513
|
}
|
|
502
|
-
applyConstraints(schema, directConstraints);
|
|
503
|
-
applyAnnotations(schema, field.annotations);
|
|
514
|
+
applyConstraints(schema, directConstraints, ctx);
|
|
515
|
+
applyAnnotations(schema, field.annotations, ctx);
|
|
504
516
|
if (pathConstraints.length === 0) {
|
|
505
517
|
return schema;
|
|
506
518
|
}
|
|
507
|
-
return applyPathTargetedConstraints(schema, pathConstraints);
|
|
519
|
+
return applyPathTargetedConstraints(schema, pathConstraints, ctx);
|
|
508
520
|
}
|
|
509
|
-
function applyPathTargetedConstraints(schema, pathConstraints) {
|
|
521
|
+
function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
|
|
510
522
|
if (schema.type === "array" && schema.items) {
|
|
511
|
-
schema.items = applyPathTargetedConstraints(schema.items, pathConstraints);
|
|
523
|
+
schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx);
|
|
512
524
|
return schema;
|
|
513
525
|
}
|
|
514
526
|
const byTarget = /* @__PURE__ */ new Map();
|
|
@@ -522,7 +534,7 @@ function applyPathTargetedConstraints(schema, pathConstraints) {
|
|
|
522
534
|
const propertyOverrides = {};
|
|
523
535
|
for (const [target, constraints] of byTarget) {
|
|
524
536
|
const subSchema = {};
|
|
525
|
-
applyConstraints(subSchema, constraints);
|
|
537
|
+
applyConstraints(subSchema, constraints, ctx);
|
|
526
538
|
propertyOverrides[target] = subSchema;
|
|
527
539
|
}
|
|
528
540
|
if (schema.$ref) {
|
|
@@ -566,6 +578,8 @@ function generateTypeNode(type, ctx) {
|
|
|
566
578
|
return generateArrayType(type, ctx);
|
|
567
579
|
case "object":
|
|
568
580
|
return generateObjectType(type, ctx);
|
|
581
|
+
case "record":
|
|
582
|
+
return generateRecordType(type, ctx);
|
|
569
583
|
case "union":
|
|
570
584
|
return generateUnionType(type, ctx);
|
|
571
585
|
case "reference":
|
|
@@ -573,7 +587,7 @@ function generateTypeNode(type, ctx) {
|
|
|
573
587
|
case "dynamic":
|
|
574
588
|
return generateDynamicType(type);
|
|
575
589
|
case "custom":
|
|
576
|
-
return generateCustomType(type);
|
|
590
|
+
return generateCustomType(type, ctx);
|
|
577
591
|
default: {
|
|
578
592
|
const _exhaustive = type;
|
|
579
593
|
return _exhaustive;
|
|
@@ -622,16 +636,27 @@ function generateObjectType(type, ctx) {
|
|
|
622
636
|
}
|
|
623
637
|
return schema;
|
|
624
638
|
}
|
|
639
|
+
function generateRecordType(type, ctx) {
|
|
640
|
+
return {
|
|
641
|
+
type: "object",
|
|
642
|
+
additionalProperties: generateTypeNode(type.valueType, ctx)
|
|
643
|
+
};
|
|
644
|
+
}
|
|
625
645
|
function generatePropertySchema(prop, ctx) {
|
|
626
646
|
const schema = generateTypeNode(prop.type, ctx);
|
|
627
|
-
applyConstraints(schema, prop.constraints);
|
|
628
|
-
applyAnnotations(schema, prop.annotations);
|
|
647
|
+
applyConstraints(schema, prop.constraints, ctx);
|
|
648
|
+
applyAnnotations(schema, prop.annotations, ctx);
|
|
629
649
|
return schema;
|
|
630
650
|
}
|
|
631
651
|
function generateUnionType(type, ctx) {
|
|
632
652
|
if (isBooleanUnion(type)) {
|
|
633
653
|
return { type: "boolean" };
|
|
634
654
|
}
|
|
655
|
+
if (isNullableUnion(type)) {
|
|
656
|
+
return {
|
|
657
|
+
oneOf: type.members.map((m) => generateTypeNode(m, ctx))
|
|
658
|
+
};
|
|
659
|
+
}
|
|
635
660
|
return {
|
|
636
661
|
anyOf: type.members.map((m) => generateTypeNode(m, ctx))
|
|
637
662
|
};
|
|
@@ -641,6 +666,13 @@ function isBooleanUnion(type) {
|
|
|
641
666
|
const kinds = type.members.map((m) => m.kind);
|
|
642
667
|
return kinds.every((k) => k === "primitive") && type.members.every((m) => m.kind === "primitive" && m.primitiveKind === "boolean");
|
|
643
668
|
}
|
|
669
|
+
function isNullableUnion(type) {
|
|
670
|
+
if (type.members.length !== 2) return false;
|
|
671
|
+
const nullCount = type.members.filter(
|
|
672
|
+
(m) => m.kind === "primitive" && m.primitiveKind === "null"
|
|
673
|
+
).length;
|
|
674
|
+
return nullCount === 1;
|
|
675
|
+
}
|
|
644
676
|
function generateReferenceType(type) {
|
|
645
677
|
return { $ref: `#/$defs/${type.name}` };
|
|
646
678
|
}
|
|
@@ -661,10 +693,7 @@ function generateDynamicType(type) {
|
|
|
661
693
|
"x-formspec-schemaSource": type.sourceKey
|
|
662
694
|
};
|
|
663
695
|
}
|
|
664
|
-
function
|
|
665
|
-
return { type: "object" };
|
|
666
|
-
}
|
|
667
|
-
function applyConstraints(schema, constraints) {
|
|
696
|
+
function applyConstraints(schema, constraints, ctx) {
|
|
668
697
|
for (const constraint of constraints) {
|
|
669
698
|
switch (constraint.constraintKind) {
|
|
670
699
|
case "minimum":
|
|
@@ -709,6 +738,7 @@ function applyConstraints(schema, constraints) {
|
|
|
709
738
|
case "allowedMembers":
|
|
710
739
|
break;
|
|
711
740
|
case "custom":
|
|
741
|
+
applyCustomConstraint(schema, constraint, ctx);
|
|
712
742
|
break;
|
|
713
743
|
default: {
|
|
714
744
|
const _exhaustive = constraint;
|
|
@@ -717,7 +747,7 @@ function applyConstraints(schema, constraints) {
|
|
|
717
747
|
}
|
|
718
748
|
}
|
|
719
749
|
}
|
|
720
|
-
function applyAnnotations(schema, annotations) {
|
|
750
|
+
function applyAnnotations(schema, annotations, ctx) {
|
|
721
751
|
for (const annotation of annotations) {
|
|
722
752
|
switch (annotation.annotationKind) {
|
|
723
753
|
case "displayName":
|
|
@@ -737,6 +767,7 @@ function applyAnnotations(schema, annotations) {
|
|
|
737
767
|
case "formatHint":
|
|
738
768
|
break;
|
|
739
769
|
case "custom":
|
|
770
|
+
applyCustomAnnotation(schema, annotation, ctx);
|
|
740
771
|
break;
|
|
741
772
|
default: {
|
|
742
773
|
const _exhaustive = annotation;
|
|
@@ -745,11 +776,41 @@ function applyAnnotations(schema, annotations) {
|
|
|
745
776
|
}
|
|
746
777
|
}
|
|
747
778
|
}
|
|
779
|
+
function generateCustomType(type, ctx) {
|
|
780
|
+
const registration = ctx.extensionRegistry?.findType(type.typeId);
|
|
781
|
+
if (registration === void 0) {
|
|
782
|
+
throw new Error(
|
|
783
|
+
`Cannot generate JSON Schema for custom type "${type.typeId}" without a matching extension registration`
|
|
784
|
+
);
|
|
785
|
+
}
|
|
786
|
+
return registration.toJsonSchema(type.payload, ctx.vendorPrefix);
|
|
787
|
+
}
|
|
788
|
+
function applyCustomConstraint(schema, constraint, ctx) {
|
|
789
|
+
const registration = ctx.extensionRegistry?.findConstraint(constraint.constraintId);
|
|
790
|
+
if (registration === void 0) {
|
|
791
|
+
throw new Error(
|
|
792
|
+
`Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
Object.assign(schema, registration.toJsonSchema(constraint.payload, ctx.vendorPrefix));
|
|
796
|
+
}
|
|
797
|
+
function applyCustomAnnotation(schema, annotation, ctx) {
|
|
798
|
+
const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
|
|
799
|
+
if (registration === void 0) {
|
|
800
|
+
throw new Error(
|
|
801
|
+
`Cannot generate JSON Schema for custom annotation "${annotation.annotationId}" without a matching extension registration`
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
if (registration.toJsonSchema === void 0) {
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
Object.assign(schema, registration.toJsonSchema(annotation.value, ctx.vendorPrefix));
|
|
808
|
+
}
|
|
748
809
|
|
|
749
810
|
// src/json-schema/generator.ts
|
|
750
|
-
function generateJsonSchema(form) {
|
|
811
|
+
function generateJsonSchema(form, options) {
|
|
751
812
|
const ir = canonicalizeChainDSL(form);
|
|
752
|
-
return generateJsonSchemaFromIR(ir);
|
|
813
|
+
return generateJsonSchemaFromIR(ir, options);
|
|
753
814
|
}
|
|
754
815
|
|
|
755
816
|
// src/ui-schema/schema.ts
|
|
@@ -983,6 +1044,48 @@ function getSchemaExtension(schema, key) {
|
|
|
983
1044
|
return schema[key];
|
|
984
1045
|
}
|
|
985
1046
|
|
|
1047
|
+
// src/extensions/registry.ts
|
|
1048
|
+
function createExtensionRegistry(extensions) {
|
|
1049
|
+
const typeMap = /* @__PURE__ */ new Map();
|
|
1050
|
+
const constraintMap = /* @__PURE__ */ new Map();
|
|
1051
|
+
const annotationMap = /* @__PURE__ */ new Map();
|
|
1052
|
+
for (const ext of extensions) {
|
|
1053
|
+
if (ext.types !== void 0) {
|
|
1054
|
+
for (const type of ext.types) {
|
|
1055
|
+
const qualifiedId = `${ext.extensionId}/${type.typeName}`;
|
|
1056
|
+
if (typeMap.has(qualifiedId)) {
|
|
1057
|
+
throw new Error(`Duplicate custom type ID: "${qualifiedId}"`);
|
|
1058
|
+
}
|
|
1059
|
+
typeMap.set(qualifiedId, type);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
if (ext.constraints !== void 0) {
|
|
1063
|
+
for (const constraint of ext.constraints) {
|
|
1064
|
+
const qualifiedId = `${ext.extensionId}/${constraint.constraintName}`;
|
|
1065
|
+
if (constraintMap.has(qualifiedId)) {
|
|
1066
|
+
throw new Error(`Duplicate custom constraint ID: "${qualifiedId}"`);
|
|
1067
|
+
}
|
|
1068
|
+
constraintMap.set(qualifiedId, constraint);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
if (ext.annotations !== void 0) {
|
|
1072
|
+
for (const annotation of ext.annotations) {
|
|
1073
|
+
const qualifiedId = `${ext.extensionId}/${annotation.annotationName}`;
|
|
1074
|
+
if (annotationMap.has(qualifiedId)) {
|
|
1075
|
+
throw new Error(`Duplicate custom annotation ID: "${qualifiedId}"`);
|
|
1076
|
+
}
|
|
1077
|
+
annotationMap.set(qualifiedId, annotation);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return {
|
|
1082
|
+
extensions,
|
|
1083
|
+
findType: (typeId) => typeMap.get(typeId),
|
|
1084
|
+
findConstraint: (constraintId) => constraintMap.get(constraintId),
|
|
1085
|
+
findAnnotation: (annotationId) => annotationMap.get(annotationId)
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
|
|
986
1089
|
// src/json-schema/schema.ts
|
|
987
1090
|
var import_zod3 = require("zod");
|
|
988
1091
|
var jsonSchemaTypeSchema = import_zod3.z.enum([
|
|
@@ -1122,7 +1225,6 @@ var ts4 = __toESM(require("typescript"), 1);
|
|
|
1122
1225
|
|
|
1123
1226
|
// src/analyzer/jsdoc-constraints.ts
|
|
1124
1227
|
var ts3 = __toESM(require("typescript"), 1);
|
|
1125
|
-
var import_core4 = require("@formspec/core");
|
|
1126
1228
|
|
|
1127
1229
|
// src/analyzer/tsdoc-parser.ts
|
|
1128
1230
|
var ts2 = __toESM(require("typescript"), 1);
|
|
@@ -1164,6 +1266,15 @@ function createFormSpecTSDocConfig() {
|
|
|
1164
1266
|
})
|
|
1165
1267
|
);
|
|
1166
1268
|
}
|
|
1269
|
+
for (const tagName of ["displayName", "description"]) {
|
|
1270
|
+
config.addTagDefinition(
|
|
1271
|
+
new import_tsdoc.TSDocTagDefinition({
|
|
1272
|
+
tagName: "@" + tagName,
|
|
1273
|
+
syntaxKind: import_tsdoc.TSDocTagSyntaxKind.BlockTag,
|
|
1274
|
+
allowMultiple: true
|
|
1275
|
+
})
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1167
1278
|
return config;
|
|
1168
1279
|
}
|
|
1169
1280
|
var sharedParser;
|
|
@@ -1193,6 +1304,27 @@ function parseTSDocTags(node, file = "") {
|
|
|
1193
1304
|
const docComment = parserContext.docComment;
|
|
1194
1305
|
for (const block of docComment.customBlocks) {
|
|
1195
1306
|
const tagName = (0, import_core3.normalizeConstraintTagName)(block.blockTag.tagName.substring(1));
|
|
1307
|
+
if (tagName === "displayName" || tagName === "description") {
|
|
1308
|
+
const text2 = extractBlockText(block).trim();
|
|
1309
|
+
if (text2 === "") continue;
|
|
1310
|
+
const provenance2 = provenanceForComment(range, sourceFile, file, tagName);
|
|
1311
|
+
if (tagName === "displayName") {
|
|
1312
|
+
annotations.push({
|
|
1313
|
+
kind: "annotation",
|
|
1314
|
+
annotationKind: "displayName",
|
|
1315
|
+
value: text2,
|
|
1316
|
+
provenance: provenance2
|
|
1317
|
+
});
|
|
1318
|
+
} else {
|
|
1319
|
+
annotations.push({
|
|
1320
|
+
kind: "annotation",
|
|
1321
|
+
annotationKind: "description",
|
|
1322
|
+
value: text2,
|
|
1323
|
+
provenance: provenance2
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
continue;
|
|
1327
|
+
}
|
|
1196
1328
|
if (TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
|
|
1197
1329
|
const text = extractBlockText(block).trim();
|
|
1198
1330
|
if (text === "") continue;
|
|
@@ -1224,41 +1356,6 @@ function parseTSDocTags(node, file = "") {
|
|
|
1224
1356
|
constraints.push(constraintNode);
|
|
1225
1357
|
}
|
|
1226
1358
|
}
|
|
1227
|
-
let displayName;
|
|
1228
|
-
let description;
|
|
1229
|
-
let displayNameTag;
|
|
1230
|
-
let descriptionTag;
|
|
1231
|
-
for (const tag of jsDocTagsAll) {
|
|
1232
|
-
const tagName = tag.tagName.text;
|
|
1233
|
-
const commentText = getTagCommentText(tag);
|
|
1234
|
-
if (commentText === void 0 || commentText.trim() === "") {
|
|
1235
|
-
continue;
|
|
1236
|
-
}
|
|
1237
|
-
const trimmed = commentText.trim();
|
|
1238
|
-
if (tagName === "Field_displayName") {
|
|
1239
|
-
displayName = trimmed;
|
|
1240
|
-
displayNameTag = tag;
|
|
1241
|
-
} else if (tagName === "Field_description") {
|
|
1242
|
-
description = trimmed;
|
|
1243
|
-
descriptionTag = tag;
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
if (displayName !== void 0 && displayNameTag) {
|
|
1247
|
-
annotations.push({
|
|
1248
|
-
kind: "annotation",
|
|
1249
|
-
annotationKind: "displayName",
|
|
1250
|
-
value: displayName,
|
|
1251
|
-
provenance: provenanceForJSDocTag(displayNameTag, file)
|
|
1252
|
-
});
|
|
1253
|
-
}
|
|
1254
|
-
if (description !== void 0 && descriptionTag) {
|
|
1255
|
-
annotations.push({
|
|
1256
|
-
kind: "annotation",
|
|
1257
|
-
annotationKind: "description",
|
|
1258
|
-
value: description,
|
|
1259
|
-
provenance: provenanceForJSDocTag(descriptionTag, file)
|
|
1260
|
-
});
|
|
1261
|
-
}
|
|
1262
1359
|
return { constraints, annotations };
|
|
1263
1360
|
}
|
|
1264
1361
|
function extractPathTarget(text) {
|
|
@@ -1523,18 +1620,19 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting) {
|
|
|
1523
1620
|
const tsType = checker.getTypeAtLocation(prop);
|
|
1524
1621
|
const optional = prop.questionToken !== void 0;
|
|
1525
1622
|
const provenance = provenanceForNode(prop, file);
|
|
1526
|
-
|
|
1623
|
+
let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
|
|
1527
1624
|
const constraints = [];
|
|
1528
1625
|
if (prop.type) {
|
|
1529
1626
|
constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
|
|
1530
1627
|
}
|
|
1531
1628
|
constraints.push(...extractJSDocConstraintNodes(prop, file));
|
|
1532
|
-
|
|
1629
|
+
let annotations = [];
|
|
1533
1630
|
annotations.push(...extractJSDocAnnotationNodes(prop, file));
|
|
1534
1631
|
const defaultAnnotation = extractDefaultValueAnnotation(prop.initializer, file);
|
|
1535
1632
|
if (defaultAnnotation) {
|
|
1536
1633
|
annotations.push(defaultAnnotation);
|
|
1537
1634
|
}
|
|
1635
|
+
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
1538
1636
|
return {
|
|
1539
1637
|
kind: "field",
|
|
1540
1638
|
name,
|
|
@@ -1553,14 +1651,15 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
1553
1651
|
const tsType = checker.getTypeAtLocation(prop);
|
|
1554
1652
|
const optional = prop.questionToken !== void 0;
|
|
1555
1653
|
const provenance = provenanceForNode(prop, file);
|
|
1556
|
-
|
|
1654
|
+
let type = resolveTypeNode(tsType, checker, file, typeRegistry, visiting);
|
|
1557
1655
|
const constraints = [];
|
|
1558
1656
|
if (prop.type) {
|
|
1559
1657
|
constraints.push(...extractTypeAliasConstraintNodes(prop.type, checker, file));
|
|
1560
1658
|
}
|
|
1561
1659
|
constraints.push(...extractJSDocConstraintNodes(prop, file));
|
|
1562
|
-
|
|
1660
|
+
let annotations = [];
|
|
1563
1661
|
annotations.push(...extractJSDocAnnotationNodes(prop, file));
|
|
1662
|
+
({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
|
|
1564
1663
|
return {
|
|
1565
1664
|
kind: "field",
|
|
1566
1665
|
name,
|
|
@@ -1571,6 +1670,68 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
|
|
|
1571
1670
|
provenance
|
|
1572
1671
|
};
|
|
1573
1672
|
}
|
|
1673
|
+
function applyEnumMemberDisplayNames(type, annotations) {
|
|
1674
|
+
if (!annotations.some(
|
|
1675
|
+
(annotation) => annotation.annotationKind === "displayName" && annotation.value.trim().startsWith(":")
|
|
1676
|
+
)) {
|
|
1677
|
+
return { type, annotations: [...annotations] };
|
|
1678
|
+
}
|
|
1679
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
1680
|
+
const nextType = rewriteEnumDisplayNames(type, annotations, consumed);
|
|
1681
|
+
if (consumed.size === 0) {
|
|
1682
|
+
return { type, annotations: [...annotations] };
|
|
1683
|
+
}
|
|
1684
|
+
return {
|
|
1685
|
+
type: nextType,
|
|
1686
|
+
annotations: annotations.filter((annotation) => !consumed.has(annotation))
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
function rewriteEnumDisplayNames(type, annotations, consumed) {
|
|
1690
|
+
switch (type.kind) {
|
|
1691
|
+
case "enum":
|
|
1692
|
+
return applyEnumMemberDisplayNamesToEnum(type, annotations, consumed);
|
|
1693
|
+
case "union": {
|
|
1694
|
+
return {
|
|
1695
|
+
...type,
|
|
1696
|
+
members: type.members.map(
|
|
1697
|
+
(member) => rewriteEnumDisplayNames(member, annotations, consumed)
|
|
1698
|
+
)
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
default:
|
|
1702
|
+
return type;
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
function applyEnumMemberDisplayNamesToEnum(type, annotations, consumed) {
|
|
1706
|
+
const displayNames = /* @__PURE__ */ new Map();
|
|
1707
|
+
for (const annotation of annotations) {
|
|
1708
|
+
if (annotation.annotationKind !== "displayName") continue;
|
|
1709
|
+
const parsed = parseEnumMemberDisplayName(annotation.value);
|
|
1710
|
+
if (!parsed) continue;
|
|
1711
|
+
consumed.add(annotation);
|
|
1712
|
+
const member = type.members.find((m) => String(m.value) === parsed.value);
|
|
1713
|
+
if (!member) continue;
|
|
1714
|
+
displayNames.set(String(member.value), parsed.label);
|
|
1715
|
+
}
|
|
1716
|
+
if (displayNames.size === 0) {
|
|
1717
|
+
return type;
|
|
1718
|
+
}
|
|
1719
|
+
return {
|
|
1720
|
+
...type,
|
|
1721
|
+
members: type.members.map((member) => {
|
|
1722
|
+
const displayName = displayNames.get(String(member.value));
|
|
1723
|
+
return displayName !== void 0 ? { ...member, displayName } : member;
|
|
1724
|
+
})
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
function parseEnumMemberDisplayName(value) {
|
|
1728
|
+
const trimmed = value.trim();
|
|
1729
|
+
const match = /^:([^\s]+)\s+([\s\S]+)$/.exec(trimmed);
|
|
1730
|
+
if (!match?.[1] || !match[2]) return null;
|
|
1731
|
+
const label = match[2].trim();
|
|
1732
|
+
if (label === "") return null;
|
|
1733
|
+
return { value: match[1], label };
|
|
1734
|
+
}
|
|
1574
1735
|
function resolveTypeNode(type, checker, file, typeRegistry, visiting) {
|
|
1575
1736
|
if (type.flags & ts4.TypeFlags.String) {
|
|
1576
1737
|
return { kind: "primitive", primitiveKind: "string" };
|
|
@@ -1681,7 +1842,30 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting) {
|
|
|
1681
1842
|
const items = elementType ? resolveTypeNode(elementType, checker, file, typeRegistry, visiting) : { kind: "primitive", primitiveKind: "string" };
|
|
1682
1843
|
return { kind: "array", items };
|
|
1683
1844
|
}
|
|
1845
|
+
function tryResolveRecordType(type, checker, file, typeRegistry, visiting) {
|
|
1846
|
+
if (type.getProperties().length > 0) {
|
|
1847
|
+
return null;
|
|
1848
|
+
}
|
|
1849
|
+
const indexInfo = checker.getIndexInfoOfType(type, ts4.IndexKind.String);
|
|
1850
|
+
if (!indexInfo) {
|
|
1851
|
+
return null;
|
|
1852
|
+
}
|
|
1853
|
+
if (visiting.has(type)) {
|
|
1854
|
+
return null;
|
|
1855
|
+
}
|
|
1856
|
+
visiting.add(type);
|
|
1857
|
+
try {
|
|
1858
|
+
const valueType = resolveTypeNode(indexInfo.type, checker, file, typeRegistry, visiting);
|
|
1859
|
+
return { kind: "record", valueType };
|
|
1860
|
+
} finally {
|
|
1861
|
+
visiting.delete(type);
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1684
1864
|
function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
1865
|
+
const recordNode = tryResolveRecordType(type, checker, file, typeRegistry, visiting);
|
|
1866
|
+
if (recordNode) {
|
|
1867
|
+
return recordNode;
|
|
1868
|
+
}
|
|
1685
1869
|
if (visiting.has(type)) {
|
|
1686
1870
|
return { kind: "object", properties: [], additionalProperties: false };
|
|
1687
1871
|
}
|
|
@@ -1713,7 +1897,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting) {
|
|
|
1713
1897
|
const objectNode = {
|
|
1714
1898
|
kind: "object",
|
|
1715
1899
|
properties,
|
|
1716
|
-
additionalProperties:
|
|
1900
|
+
additionalProperties: true
|
|
1717
1901
|
};
|
|
1718
1902
|
if (typeName) {
|
|
1719
1903
|
typeRegistry[typeName] = {
|
|
@@ -1916,15 +2100,19 @@ function generateSchemas(options) {
|
|
|
1916
2100
|
}
|
|
1917
2101
|
|
|
1918
2102
|
// src/index.ts
|
|
1919
|
-
function buildFormSchemas(form) {
|
|
2103
|
+
function buildFormSchemas(form, options) {
|
|
1920
2104
|
return {
|
|
1921
|
-
jsonSchema: generateJsonSchema(form),
|
|
2105
|
+
jsonSchema: generateJsonSchema(form, options),
|
|
1922
2106
|
uiSchema: generateUiSchema(form)
|
|
1923
2107
|
};
|
|
1924
2108
|
}
|
|
1925
2109
|
function writeSchemas(form, options) {
|
|
1926
|
-
const { outDir, name = "schema", indent = 2 } = options;
|
|
1927
|
-
const
|
|
2110
|
+
const { outDir, name = "schema", indent = 2, extensionRegistry, vendorPrefix } = options;
|
|
2111
|
+
const buildOptions = extensionRegistry === void 0 && vendorPrefix === void 0 ? void 0 : {
|
|
2112
|
+
extensionRegistry,
|
|
2113
|
+
vendorPrefix
|
|
2114
|
+
};
|
|
2115
|
+
const { jsonSchema, uiSchema: uiSchema2 } = buildFormSchemas(form, buildOptions);
|
|
1928
2116
|
if (!fs.existsSync(outDir)) {
|
|
1929
2117
|
fs.mkdirSync(outDir, { recursive: true });
|
|
1930
2118
|
}
|
|
@@ -1940,7 +2128,9 @@ function writeSchemas(form, options) {
|
|
|
1940
2128
|
categorizationSchema,
|
|
1941
2129
|
categorySchema,
|
|
1942
2130
|
controlSchema,
|
|
2131
|
+
createExtensionRegistry,
|
|
1943
2132
|
generateJsonSchema,
|
|
2133
|
+
generateJsonSchemaFromIR,
|
|
1944
2134
|
generateSchemas,
|
|
1945
2135
|
generateSchemasFromClass,
|
|
1946
2136
|
generateUiSchema,
|