@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.js
CHANGED
|
@@ -2377,6 +2377,92 @@ function supportsConstraintCapability(type, checker, capability) {
|
|
|
2377
2377
|
}
|
|
2378
2378
|
return false;
|
|
2379
2379
|
}
|
|
2380
|
+
var MAX_HINT_CANDIDATES = 5;
|
|
2381
|
+
var MAX_HINT_DEPTH = 3;
|
|
2382
|
+
function stripHintNullishUnion(type) {
|
|
2383
|
+
if (!type.isUnion()) {
|
|
2384
|
+
return type;
|
|
2385
|
+
}
|
|
2386
|
+
const nonNullish = type.types.filter(
|
|
2387
|
+
(member) => (member.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) === 0
|
|
2388
|
+
);
|
|
2389
|
+
if (nonNullish.length === 1 && nonNullish[0] !== void 0) {
|
|
2390
|
+
return nonNullish[0];
|
|
2391
|
+
}
|
|
2392
|
+
return type;
|
|
2393
|
+
}
|
|
2394
|
+
function isCallableType(type) {
|
|
2395
|
+
return type.getCallSignatures().length > 0 || type.getConstructSignatures().length > 0;
|
|
2396
|
+
}
|
|
2397
|
+
function isUserEmittableHintProperty(property, declaration) {
|
|
2398
|
+
if (property.name.startsWith("__")) {
|
|
2399
|
+
return false;
|
|
2400
|
+
}
|
|
2401
|
+
if ("name" in declaration && declaration.name !== void 0) {
|
|
2402
|
+
const name = declaration.name;
|
|
2403
|
+
if (ts.isComputedPropertyName(name) || ts.isPrivateIdentifier(name)) {
|
|
2404
|
+
return false;
|
|
2405
|
+
}
|
|
2406
|
+
if (!ts.isIdentifier(name) && !ts.isStringLiteral(name) && !ts.isNumericLiteral(name)) {
|
|
2407
|
+
return false;
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
return true;
|
|
2411
|
+
}
|
|
2412
|
+
function collectObjectSubfieldCandidates(type, checker, capability) {
|
|
2413
|
+
const out = [];
|
|
2414
|
+
const visit = (current, prefix, depth) => {
|
|
2415
|
+
if (depth > MAX_HINT_DEPTH) {
|
|
2416
|
+
return;
|
|
2417
|
+
}
|
|
2418
|
+
const stripped = stripHintNullishUnion(current);
|
|
2419
|
+
if (isCallableType(stripped)) {
|
|
2420
|
+
return;
|
|
2421
|
+
}
|
|
2422
|
+
if (!hasTypeSemanticCapability(stripped, checker, "object-like")) {
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2425
|
+
for (const property of stripped.getProperties()) {
|
|
2426
|
+
const declaration = property.valueDeclaration ?? property.declarations?.[0];
|
|
2427
|
+
if (declaration === void 0) {
|
|
2428
|
+
continue;
|
|
2429
|
+
}
|
|
2430
|
+
if (!isUserEmittableHintProperty(property, declaration)) {
|
|
2431
|
+
continue;
|
|
2432
|
+
}
|
|
2433
|
+
const propertyType = checker.getTypeOfSymbolAtLocation(property, declaration);
|
|
2434
|
+
const path4 = [...prefix, property.name];
|
|
2435
|
+
if (supportsConstraintCapability(propertyType, checker, capability)) {
|
|
2436
|
+
out.push(path4.join("."));
|
|
2437
|
+
continue;
|
|
2438
|
+
}
|
|
2439
|
+
const strippedPropertyType = stripHintNullishUnion(propertyType);
|
|
2440
|
+
if (!isCallableType(strippedPropertyType) && hasTypeSemanticCapability(strippedPropertyType, checker, "object-like")) {
|
|
2441
|
+
visit(strippedPropertyType, path4, depth + 1);
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
};
|
|
2445
|
+
visit(type, [], 0);
|
|
2446
|
+
return out;
|
|
2447
|
+
}
|
|
2448
|
+
function buildPathTargetHint(subjectType, checker, capability, tagName, argumentText) {
|
|
2449
|
+
if (!hasTypeSemanticCapability(subjectType, checker, "object-like")) {
|
|
2450
|
+
return null;
|
|
2451
|
+
}
|
|
2452
|
+
const candidates = collectObjectSubfieldCandidates(subjectType, checker, capability);
|
|
2453
|
+
const primary = candidates[0];
|
|
2454
|
+
if (primary === void 0) {
|
|
2455
|
+
return null;
|
|
2456
|
+
}
|
|
2457
|
+
const argText = argumentText?.trim() ?? "";
|
|
2458
|
+
const renderExample = (path4) => argText === "" ? `@${tagName} :${path4}` : `@${tagName} :${path4} ${argText}`;
|
|
2459
|
+
if (candidates.length === 1) {
|
|
2460
|
+
return `Hint: use a path target to constrain a subfield, e.g. ${renderExample(primary)}`;
|
|
2461
|
+
}
|
|
2462
|
+
const shown = candidates.slice(0, MAX_HINT_CANDIDATES);
|
|
2463
|
+
const overflow = candidates.length > MAX_HINT_CANDIDATES ? ", \u2026" : "";
|
|
2464
|
+
return `Hint: use a path target to constrain a subfield (candidates: ${shown.join(", ")}${overflow}), e.g. ${renderExample(primary)}`;
|
|
2465
|
+
}
|
|
2380
2466
|
function makeDiagnostic(code, message, provenance) {
|
|
2381
2467
|
return {
|
|
2382
2468
|
code,
|
|
@@ -2544,10 +2630,18 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
|
|
|
2544
2630
|
const requiredCapability = definition.capabilities[0];
|
|
2545
2631
|
if (requiredCapability !== void 0 && !supportsConstraintCapability(subjectType, checker, requiredCapability)) {
|
|
2546
2632
|
const actualType = checker.typeToString(subjectType, node, SYNTHETIC_TYPE_FORMAT_FLAGS);
|
|
2633
|
+
const baseMessage = `Target "${node.getText(sourceFile)}": constraint "${tagName}" is only valid on ${capabilityLabel(requiredCapability)} targets, but field type is "${actualType}"`;
|
|
2634
|
+
const hint = buildPathTargetHint(
|
|
2635
|
+
subjectType,
|
|
2636
|
+
checker,
|
|
2637
|
+
requiredCapability,
|
|
2638
|
+
tagName,
|
|
2639
|
+
parsedTag?.argumentText
|
|
2640
|
+
);
|
|
2547
2641
|
return [
|
|
2548
2642
|
makeDiagnostic(
|
|
2549
2643
|
"TYPE_MISMATCH",
|
|
2550
|
-
|
|
2644
|
+
hint === null ? baseMessage : `${baseMessage}. ${hint}`,
|
|
2551
2645
|
provenance
|
|
2552
2646
|
)
|
|
2553
2647
|
];
|
|
@@ -4197,6 +4291,9 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
4197
4291
|
if (type.flags & ts3.TypeFlags.Undefined) {
|
|
4198
4292
|
return { kind: "primitive", primitiveKind: "null" };
|
|
4199
4293
|
}
|
|
4294
|
+
if (type.flags & ts3.TypeFlags.Void) {
|
|
4295
|
+
return { kind: "primitive", primitiveKind: "null" };
|
|
4296
|
+
}
|
|
4200
4297
|
if (type.isStringLiteral()) {
|
|
4201
4298
|
return {
|
|
4202
4299
|
kind: "enum",
|
|
@@ -6379,7 +6476,20 @@ function unwrapPromiseType(checker, type) {
|
|
|
6379
6476
|
if (!("getAwaitedType" in checker) || typeof checker.getAwaitedType !== "function") {
|
|
6380
6477
|
return type;
|
|
6381
6478
|
}
|
|
6382
|
-
|
|
6479
|
+
const awaited = checker.getAwaitedType(type) ?? type;
|
|
6480
|
+
if (awaited === type && looksLikePromiseType(checker, type)) {
|
|
6481
|
+
throw new Error(
|
|
6482
|
+
`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\`.`
|
|
6483
|
+
);
|
|
6484
|
+
}
|
|
6485
|
+
return awaited;
|
|
6486
|
+
}
|
|
6487
|
+
function looksLikePromiseType(checker, type) {
|
|
6488
|
+
const symbolName = type.getSymbol()?.getName();
|
|
6489
|
+
if (symbolName === "Promise" || symbolName === "PromiseLike") {
|
|
6490
|
+
return true;
|
|
6491
|
+
}
|
|
6492
|
+
return /^(?:Promise|PromiseLike)\b/.test(checker.typeToString(type));
|
|
6383
6493
|
}
|
|
6384
6494
|
function unwrapPromiseTypeNode(typeNode) {
|
|
6385
6495
|
if (typeNode === void 0) {
|