@formspec/build 0.1.0-alpha.48 → 0.1.0-alpha.49
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/analyzer/class-analyzer.d.ts.map +1 -1
- package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
- package/dist/cli.cjs +113 -3
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +113 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +112 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +112 -2
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +98 -1
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +98 -1
- package/dist/internals.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2409,6 +2409,92 @@ function supportsConstraintCapability(type, checker, capability) {
|
|
|
2409
2409
|
}
|
|
2410
2410
|
return false;
|
|
2411
2411
|
}
|
|
2412
|
+
var MAX_HINT_CANDIDATES = 5;
|
|
2413
|
+
var MAX_HINT_DEPTH = 3;
|
|
2414
|
+
function stripHintNullishUnion(type) {
|
|
2415
|
+
if (!type.isUnion()) {
|
|
2416
|
+
return type;
|
|
2417
|
+
}
|
|
2418
|
+
const nonNullish = type.types.filter(
|
|
2419
|
+
(member) => (member.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) === 0
|
|
2420
|
+
);
|
|
2421
|
+
if (nonNullish.length === 1 && nonNullish[0] !== void 0) {
|
|
2422
|
+
return nonNullish[0];
|
|
2423
|
+
}
|
|
2424
|
+
return type;
|
|
2425
|
+
}
|
|
2426
|
+
function isCallableType(type) {
|
|
2427
|
+
return type.getCallSignatures().length > 0 || type.getConstructSignatures().length > 0;
|
|
2428
|
+
}
|
|
2429
|
+
function isUserEmittableHintProperty(property, declaration) {
|
|
2430
|
+
if (property.name.startsWith("__")) {
|
|
2431
|
+
return false;
|
|
2432
|
+
}
|
|
2433
|
+
if ("name" in declaration && declaration.name !== void 0) {
|
|
2434
|
+
const name = declaration.name;
|
|
2435
|
+
if (ts.isComputedPropertyName(name) || ts.isPrivateIdentifier(name)) {
|
|
2436
|
+
return false;
|
|
2437
|
+
}
|
|
2438
|
+
if (!ts.isIdentifier(name) && !ts.isStringLiteral(name) && !ts.isNumericLiteral(name)) {
|
|
2439
|
+
return false;
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
return true;
|
|
2443
|
+
}
|
|
2444
|
+
function collectObjectSubfieldCandidates(type, checker, capability) {
|
|
2445
|
+
const out = [];
|
|
2446
|
+
const visit = (current, prefix, depth) => {
|
|
2447
|
+
if (depth > MAX_HINT_DEPTH) {
|
|
2448
|
+
return;
|
|
2449
|
+
}
|
|
2450
|
+
const stripped = stripHintNullishUnion(current);
|
|
2451
|
+
if (isCallableType(stripped)) {
|
|
2452
|
+
return;
|
|
2453
|
+
}
|
|
2454
|
+
if (!(0, import_internal2.hasTypeSemanticCapability)(stripped, checker, "object-like")) {
|
|
2455
|
+
return;
|
|
2456
|
+
}
|
|
2457
|
+
for (const property of stripped.getProperties()) {
|
|
2458
|
+
const declaration = property.valueDeclaration ?? property.declarations?.[0];
|
|
2459
|
+
if (declaration === void 0) {
|
|
2460
|
+
continue;
|
|
2461
|
+
}
|
|
2462
|
+
if (!isUserEmittableHintProperty(property, declaration)) {
|
|
2463
|
+
continue;
|
|
2464
|
+
}
|
|
2465
|
+
const propertyType = checker.getTypeOfSymbolAtLocation(property, declaration);
|
|
2466
|
+
const path4 = [...prefix, property.name];
|
|
2467
|
+
if (supportsConstraintCapability(propertyType, checker, capability)) {
|
|
2468
|
+
out.push(path4.join("."));
|
|
2469
|
+
continue;
|
|
2470
|
+
}
|
|
2471
|
+
const strippedPropertyType = stripHintNullishUnion(propertyType);
|
|
2472
|
+
if (!isCallableType(strippedPropertyType) && (0, import_internal2.hasTypeSemanticCapability)(strippedPropertyType, checker, "object-like")) {
|
|
2473
|
+
visit(strippedPropertyType, path4, depth + 1);
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
};
|
|
2477
|
+
visit(type, [], 0);
|
|
2478
|
+
return out;
|
|
2479
|
+
}
|
|
2480
|
+
function buildPathTargetHint(subjectType, checker, capability, tagName, argumentText) {
|
|
2481
|
+
if (!(0, import_internal2.hasTypeSemanticCapability)(subjectType, checker, "object-like")) {
|
|
2482
|
+
return null;
|
|
2483
|
+
}
|
|
2484
|
+
const candidates = collectObjectSubfieldCandidates(subjectType, checker, capability);
|
|
2485
|
+
const primary = candidates[0];
|
|
2486
|
+
if (primary === void 0) {
|
|
2487
|
+
return null;
|
|
2488
|
+
}
|
|
2489
|
+
const argText = argumentText?.trim() ?? "";
|
|
2490
|
+
const renderExample = (path4) => argText === "" ? `@${tagName} :${path4}` : `@${tagName} :${path4} ${argText}`;
|
|
2491
|
+
if (candidates.length === 1) {
|
|
2492
|
+
return `Hint: use a path target to constrain a subfield, e.g. ${renderExample(primary)}`;
|
|
2493
|
+
}
|
|
2494
|
+
const shown = candidates.slice(0, MAX_HINT_CANDIDATES);
|
|
2495
|
+
const overflow = candidates.length > MAX_HINT_CANDIDATES ? ", \u2026" : "";
|
|
2496
|
+
return `Hint: use a path target to constrain a subfield (candidates: ${shown.join(", ")}${overflow}), e.g. ${renderExample(primary)}`;
|
|
2497
|
+
}
|
|
2412
2498
|
function makeDiagnostic(code, message, provenance) {
|
|
2413
2499
|
return {
|
|
2414
2500
|
code,
|
|
@@ -2576,10 +2662,18 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
|
|
|
2576
2662
|
const requiredCapability = definition.capabilities[0];
|
|
2577
2663
|
if (requiredCapability !== void 0 && !supportsConstraintCapability(subjectType, checker, requiredCapability)) {
|
|
2578
2664
|
const actualType = checker.typeToString(subjectType, node, SYNTHETIC_TYPE_FORMAT_FLAGS);
|
|
2665
|
+
const baseMessage = `Target "${node.getText(sourceFile)}": constraint "${tagName}" is only valid on ${capabilityLabel(requiredCapability)} targets, but field type is "${actualType}"`;
|
|
2666
|
+
const hint = buildPathTargetHint(
|
|
2667
|
+
subjectType,
|
|
2668
|
+
checker,
|
|
2669
|
+
requiredCapability,
|
|
2670
|
+
tagName,
|
|
2671
|
+
parsedTag?.argumentText
|
|
2672
|
+
);
|
|
2579
2673
|
return [
|
|
2580
2674
|
makeDiagnostic(
|
|
2581
2675
|
"TYPE_MISMATCH",
|
|
2582
|
-
|
|
2676
|
+
hint === null ? baseMessage : `${baseMessage}. ${hint}`,
|
|
2583
2677
|
provenance
|
|
2584
2678
|
)
|
|
2585
2679
|
];
|
|
@@ -4229,6 +4323,9 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
4229
4323
|
if (type.flags & ts3.TypeFlags.Undefined) {
|
|
4230
4324
|
return { kind: "primitive", primitiveKind: "null" };
|
|
4231
4325
|
}
|
|
4326
|
+
if (type.flags & ts3.TypeFlags.Void) {
|
|
4327
|
+
return { kind: "primitive", primitiveKind: "null" };
|
|
4328
|
+
}
|
|
4232
4329
|
if (type.isStringLiteral()) {
|
|
4233
4330
|
return {
|
|
4234
4331
|
kind: "enum",
|
|
@@ -6411,7 +6508,20 @@ function unwrapPromiseType(checker, type) {
|
|
|
6411
6508
|
if (!("getAwaitedType" in checker) || typeof checker.getAwaitedType !== "function") {
|
|
6412
6509
|
return type;
|
|
6413
6510
|
}
|
|
6414
|
-
|
|
6511
|
+
const awaited = checker.getAwaitedType(type) ?? type;
|
|
6512
|
+
if (awaited === type && looksLikePromiseType(checker, type)) {
|
|
6513
|
+
throw new Error(
|
|
6514
|
+
`FormSpec could not unwrap the awaited type from "${checker.typeToString(type)}". This usually indicates the TypeScript compiler host cannot resolve its default lib files (for example "lib.es2015.promise.d.ts"). Ensure the program is configured with the standard library available to \`ts.createProgram\`.`
|
|
6515
|
+
);
|
|
6516
|
+
}
|
|
6517
|
+
return awaited;
|
|
6518
|
+
}
|
|
6519
|
+
function looksLikePromiseType(checker, type) {
|
|
6520
|
+
const symbolName = type.getSymbol()?.getName();
|
|
6521
|
+
if (symbolName === "Promise" || symbolName === "PromiseLike") {
|
|
6522
|
+
return true;
|
|
6523
|
+
}
|
|
6524
|
+
return /^(?:Promise|PromiseLike)\b/.test(checker.typeToString(type));
|
|
6415
6525
|
}
|
|
6416
6526
|
function unwrapPromiseTypeNode(typeNode) {
|
|
6417
6527
|
if (typeNode === void 0) {
|