@formspec/build 0.1.0-alpha.13 → 0.1.0-alpha.14
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 +20 -20
- package/dist/__tests__/alias-chain-propagation.test.d.ts +9 -0
- package/dist/__tests__/alias-chain-propagation.test.d.ts.map +1 -0
- package/dist/__tests__/fixtures/alias-chains.d.ts +37 -0
- package/dist/__tests__/fixtures/alias-chains.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-a-builtins.d.ts +7 -7
- package/dist/__tests__/fixtures/example-interface-types.d.ts +17 -17
- package/dist/__tests__/json-utils.test.d.ts +5 -0
- package/dist/__tests__/json-utils.test.d.ts.map +1 -0
- package/dist/__tests__/path-target-parser.test.d.ts +9 -0
- package/dist/__tests__/path-target-parser.test.d.ts.map +1 -0
- package/dist/analyzer/class-analyzer.d.ts.map +1 -1
- package/dist/analyzer/jsdoc-constraints.d.ts +2 -2
- package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -1
- package/dist/analyzer/json-utils.d.ts +22 -0
- package/dist/analyzer/json-utils.d.ts.map +1 -0
- package/dist/analyzer/tsdoc-parser.d.ts +18 -4
- package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
- package/dist/browser.cjs +76 -7
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +76 -7
- package/dist/browser.js.map +1 -1
- package/dist/build.d.ts +1 -0
- package/dist/cli.cjs +140 -41
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +145 -41
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +134 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +139 -41
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +147 -46
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +152 -47
- package/dist/internals.js.map +1 -1
- package/dist/json-schema/ir-generator.d.ts +1 -0
- 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/index.cjs
CHANGED
|
@@ -490,8 +490,70 @@ function collectFields(elements, properties, required, ctx) {
|
|
|
490
490
|
}
|
|
491
491
|
function generateFieldSchema(field, ctx) {
|
|
492
492
|
const schema = generateTypeNode(field.type, ctx);
|
|
493
|
-
|
|
493
|
+
const directConstraints = [];
|
|
494
|
+
const pathConstraints = [];
|
|
495
|
+
for (const c of field.constraints) {
|
|
496
|
+
if (c.path) {
|
|
497
|
+
pathConstraints.push(c);
|
|
498
|
+
} else {
|
|
499
|
+
directConstraints.push(c);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
applyConstraints(schema, directConstraints);
|
|
494
503
|
applyAnnotations(schema, field.annotations);
|
|
504
|
+
if (pathConstraints.length === 0) {
|
|
505
|
+
return schema;
|
|
506
|
+
}
|
|
507
|
+
return applyPathTargetedConstraints(schema, pathConstraints);
|
|
508
|
+
}
|
|
509
|
+
function applyPathTargetedConstraints(schema, pathConstraints) {
|
|
510
|
+
if (schema.type === "array" && schema.items) {
|
|
511
|
+
schema.items = applyPathTargetedConstraints(schema.items, pathConstraints);
|
|
512
|
+
return schema;
|
|
513
|
+
}
|
|
514
|
+
const byTarget = /* @__PURE__ */ new Map();
|
|
515
|
+
for (const c of pathConstraints) {
|
|
516
|
+
const target = c.path?.segments[0];
|
|
517
|
+
if (!target) continue;
|
|
518
|
+
const group = byTarget.get(target) ?? [];
|
|
519
|
+
group.push(c);
|
|
520
|
+
byTarget.set(target, group);
|
|
521
|
+
}
|
|
522
|
+
const propertyOverrides = {};
|
|
523
|
+
for (const [target, constraints] of byTarget) {
|
|
524
|
+
const subSchema = {};
|
|
525
|
+
applyConstraints(subSchema, constraints);
|
|
526
|
+
propertyOverrides[target] = subSchema;
|
|
527
|
+
}
|
|
528
|
+
if (schema.$ref) {
|
|
529
|
+
const { $ref, ...rest } = schema;
|
|
530
|
+
const refPart = { $ref };
|
|
531
|
+
const overridePart = {
|
|
532
|
+
properties: propertyOverrides,
|
|
533
|
+
...rest
|
|
534
|
+
};
|
|
535
|
+
return { allOf: [refPart, overridePart] };
|
|
536
|
+
}
|
|
537
|
+
if (schema.type === "object" && schema.properties) {
|
|
538
|
+
const missingOverrides = {};
|
|
539
|
+
for (const [target, overrideSchema] of Object.entries(propertyOverrides)) {
|
|
540
|
+
if (schema.properties[target]) {
|
|
541
|
+
Object.assign(schema.properties[target], overrideSchema);
|
|
542
|
+
} else {
|
|
543
|
+
missingOverrides[target] = overrideSchema;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
if (Object.keys(missingOverrides).length === 0) {
|
|
547
|
+
return schema;
|
|
548
|
+
}
|
|
549
|
+
return {
|
|
550
|
+
allOf: [schema, { properties: missingOverrides }]
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
if (schema.allOf) {
|
|
554
|
+
schema.allOf = [...schema.allOf, { properties: propertyOverrides }];
|
|
555
|
+
return schema;
|
|
556
|
+
}
|
|
495
557
|
return schema;
|
|
496
558
|
}
|
|
497
559
|
function generateTypeNode(type, ctx) {
|
|
@@ -1066,20 +1128,31 @@ var import_core4 = require("@formspec/core");
|
|
|
1066
1128
|
var ts2 = __toESM(require("typescript"), 1);
|
|
1067
1129
|
var import_tsdoc = require("@microsoft/tsdoc");
|
|
1068
1130
|
var import_core3 = require("@formspec/core");
|
|
1131
|
+
|
|
1132
|
+
// src/analyzer/json-utils.ts
|
|
1133
|
+
function tryParseJson(text) {
|
|
1134
|
+
try {
|
|
1135
|
+
return JSON.parse(text);
|
|
1136
|
+
} catch {
|
|
1137
|
+
return null;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// src/analyzer/tsdoc-parser.ts
|
|
1069
1142
|
var NUMERIC_CONSTRAINT_MAP = {
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1143
|
+
minimum: "minimum",
|
|
1144
|
+
maximum: "maximum",
|
|
1145
|
+
exclusiveMinimum: "exclusiveMinimum",
|
|
1146
|
+
exclusiveMaximum: "exclusiveMaximum",
|
|
1147
|
+
multipleOf: "multipleOf"
|
|
1074
1148
|
};
|
|
1075
1149
|
var LENGTH_CONSTRAINT_MAP = {
|
|
1076
|
-
|
|
1077
|
-
|
|
1150
|
+
minLength: "minLength",
|
|
1151
|
+
maxLength: "maxLength",
|
|
1152
|
+
minItems: "minItems",
|
|
1153
|
+
maxItems: "maxItems"
|
|
1078
1154
|
};
|
|
1079
|
-
var TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["
|
|
1080
|
-
function isBuiltinConstraintName(tagName) {
|
|
1081
|
-
return tagName in import_core3.BUILTIN_CONSTRAINT_DEFINITIONS;
|
|
1082
|
-
}
|
|
1155
|
+
var TAGS_REQUIRING_RAW_TEXT = /* @__PURE__ */ new Set(["pattern", "enumOptions"]);
|
|
1083
1156
|
function createFormSpecTSDocConfig() {
|
|
1084
1157
|
const config = new import_tsdoc.TSDocConfiguration();
|
|
1085
1158
|
for (const tagName of Object.keys(import_core3.BUILTIN_CONSTRAINT_DEFINITIONS)) {
|
|
@@ -1119,7 +1192,7 @@ function parseTSDocTags(node, file = "") {
|
|
|
1119
1192
|
);
|
|
1120
1193
|
const docComment = parserContext.docComment;
|
|
1121
1194
|
for (const block of docComment.customBlocks) {
|
|
1122
|
-
const tagName = block.blockTag.tagName.substring(1);
|
|
1195
|
+
const tagName = (0, import_core3.normalizeConstraintTagName)(block.blockTag.tagName.substring(1));
|
|
1123
1196
|
if (TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
|
|
1124
1197
|
const text = extractBlockText(block).trim();
|
|
1125
1198
|
if (text === "") continue;
|
|
@@ -1140,7 +1213,7 @@ function parseTSDocTags(node, file = "") {
|
|
|
1140
1213
|
}
|
|
1141
1214
|
const jsDocTagsAll = ts2.getJSDocTags(node);
|
|
1142
1215
|
for (const tag of jsDocTagsAll) {
|
|
1143
|
-
const tagName = tag.tagName.text;
|
|
1216
|
+
const tagName = (0, import_core3.normalizeConstraintTagName)(tag.tagName.text);
|
|
1144
1217
|
if (!TAGS_REQUIRING_RAW_TEXT.has(tagName)) continue;
|
|
1145
1218
|
const commentText = getTagCommentText(tag);
|
|
1146
1219
|
if (commentText === void 0 || commentText.trim() === "") continue;
|
|
@@ -1188,6 +1261,15 @@ function parseTSDocTags(node, file = "") {
|
|
|
1188
1261
|
}
|
|
1189
1262
|
return { constraints, annotations };
|
|
1190
1263
|
}
|
|
1264
|
+
function extractPathTarget(text) {
|
|
1265
|
+
const trimmed = text.trimStart();
|
|
1266
|
+
const match = /^:([a-zA-Z_]\w*)\s+([\s\S]*)$/.exec(trimmed);
|
|
1267
|
+
if (!match?.[1] || !match[2]) return null;
|
|
1268
|
+
return {
|
|
1269
|
+
path: { segments: [match[1]] },
|
|
1270
|
+
remainingText: match[2]
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1191
1273
|
function extractBlockText(block) {
|
|
1192
1274
|
return extractPlainText(block.content);
|
|
1193
1275
|
}
|
|
@@ -1207,12 +1289,15 @@ function extractPlainText(node) {
|
|
|
1207
1289
|
return result;
|
|
1208
1290
|
}
|
|
1209
1291
|
function parseConstraintValue(tagName, text, provenance) {
|
|
1210
|
-
if (!isBuiltinConstraintName(tagName)) {
|
|
1292
|
+
if (!(0, import_core3.isBuiltinConstraintName)(tagName)) {
|
|
1211
1293
|
return null;
|
|
1212
1294
|
}
|
|
1295
|
+
const pathResult = extractPathTarget(text);
|
|
1296
|
+
const effectiveText = pathResult ? pathResult.remainingText : text;
|
|
1297
|
+
const path3 = pathResult?.path;
|
|
1213
1298
|
const expectedType = import_core3.BUILTIN_CONSTRAINT_DEFINITIONS[tagName];
|
|
1214
1299
|
if (expectedType === "number") {
|
|
1215
|
-
const value = Number(
|
|
1300
|
+
const value = Number(effectiveText);
|
|
1216
1301
|
if (Number.isNaN(value)) {
|
|
1217
1302
|
return null;
|
|
1218
1303
|
}
|
|
@@ -1222,6 +1307,7 @@ function parseConstraintValue(tagName, text, provenance) {
|
|
|
1222
1307
|
kind: "constraint",
|
|
1223
1308
|
constraintKind: numericKind,
|
|
1224
1309
|
value,
|
|
1310
|
+
...path3 && { path: path3 },
|
|
1225
1311
|
provenance
|
|
1226
1312
|
};
|
|
1227
1313
|
}
|
|
@@ -1231,42 +1317,41 @@ function parseConstraintValue(tagName, text, provenance) {
|
|
|
1231
1317
|
kind: "constraint",
|
|
1232
1318
|
constraintKind: lengthKind,
|
|
1233
1319
|
value,
|
|
1320
|
+
...path3 && { path: path3 },
|
|
1234
1321
|
provenance
|
|
1235
1322
|
};
|
|
1236
1323
|
}
|
|
1237
1324
|
return null;
|
|
1238
1325
|
}
|
|
1239
1326
|
if (expectedType === "json") {
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
members.push(id);
|
|
1253
|
-
}
|
|
1327
|
+
const parsed = tryParseJson(effectiveText);
|
|
1328
|
+
if (!Array.isArray(parsed)) {
|
|
1329
|
+
return null;
|
|
1330
|
+
}
|
|
1331
|
+
const members = [];
|
|
1332
|
+
for (const item of parsed) {
|
|
1333
|
+
if (typeof item === "string" || typeof item === "number") {
|
|
1334
|
+
members.push(item);
|
|
1335
|
+
} else if (typeof item === "object" && item !== null && "id" in item) {
|
|
1336
|
+
const id = item["id"];
|
|
1337
|
+
if (typeof id === "string" || typeof id === "number") {
|
|
1338
|
+
members.push(id);
|
|
1254
1339
|
}
|
|
1255
1340
|
}
|
|
1256
|
-
return {
|
|
1257
|
-
kind: "constraint",
|
|
1258
|
-
constraintKind: "allowedMembers",
|
|
1259
|
-
members,
|
|
1260
|
-
provenance
|
|
1261
|
-
};
|
|
1262
|
-
} catch {
|
|
1263
|
-
return null;
|
|
1264
1341
|
}
|
|
1342
|
+
return {
|
|
1343
|
+
kind: "constraint",
|
|
1344
|
+
constraintKind: "allowedMembers",
|
|
1345
|
+
members,
|
|
1346
|
+
...path3 && { path: path3 },
|
|
1347
|
+
provenance
|
|
1348
|
+
};
|
|
1265
1349
|
}
|
|
1266
1350
|
return {
|
|
1267
1351
|
kind: "constraint",
|
|
1268
1352
|
constraintKind: "pattern",
|
|
1269
|
-
pattern:
|
|
1353
|
+
pattern: effectiveText,
|
|
1354
|
+
...path3 && { path: path3 },
|
|
1270
1355
|
provenance
|
|
1271
1356
|
};
|
|
1272
1357
|
}
|
|
@@ -1697,14 +1782,23 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting) {
|
|
|
1697
1782
|
}
|
|
1698
1783
|
return map;
|
|
1699
1784
|
}
|
|
1700
|
-
|
|
1785
|
+
var MAX_ALIAS_CHAIN_DEPTH = 8;
|
|
1786
|
+
function extractTypeAliasConstraintNodes(typeNode, checker, file, depth = 0) {
|
|
1701
1787
|
if (!ts4.isTypeReferenceNode(typeNode)) return [];
|
|
1788
|
+
if (depth >= MAX_ALIAS_CHAIN_DEPTH) {
|
|
1789
|
+
const aliasName = typeNode.typeName.getText();
|
|
1790
|
+
throw new Error(
|
|
1791
|
+
`Type alias chain exceeds maximum depth of ${String(MAX_ALIAS_CHAIN_DEPTH)} at alias "${aliasName}" in ${file}. Simplify the alias chain or check for circular references.`
|
|
1792
|
+
);
|
|
1793
|
+
}
|
|
1702
1794
|
const symbol = checker.getSymbolAtLocation(typeNode.typeName);
|
|
1703
1795
|
if (!symbol?.declarations) return [];
|
|
1704
1796
|
const aliasDecl = symbol.declarations.find(ts4.isTypeAliasDeclaration);
|
|
1705
1797
|
if (!aliasDecl) return [];
|
|
1706
1798
|
if (ts4.isTypeLiteralNode(aliasDecl.type)) return [];
|
|
1707
|
-
|
|
1799
|
+
const constraints = extractJSDocConstraintNodes(aliasDecl, file);
|
|
1800
|
+
constraints.push(...extractTypeAliasConstraintNodes(aliasDecl.type, checker, file, depth + 1));
|
|
1801
|
+
return constraints;
|
|
1708
1802
|
}
|
|
1709
1803
|
function provenanceForNode(node, file) {
|
|
1710
1804
|
const sourceFile = node.getSourceFile();
|