@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/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
- `Target "${node.getText(sourceFile)}": constraint "${tagName}" is only valid on ${capabilityLabel(requiredCapability)} targets, but field type is "${actualType}"`,
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
- return checker.getAwaitedType(type) ?? type;
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) {