@formspec/build 0.1.0-alpha.36 → 0.1.0-alpha.38

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/cli.js CHANGED
@@ -2237,6 +2237,16 @@ function buildSupportingDeclarations(sourceFile, extensionTypeNames) {
2237
2237
  return true;
2238
2238
  }).map((statement) => statement.getText(sourceFile));
2239
2239
  }
2240
+ function pushUniqueCompilerDiagnostics(target, additions) {
2241
+ for (const diagnostic of additions) {
2242
+ if ((diagnostic.code === "UNSUPPORTED_CUSTOM_TYPE_OVERRIDE" || diagnostic.code === "SYNTHETIC_SETUP_FAILURE") && target.some(
2243
+ (existing) => existing.code === diagnostic.code && existing.message === diagnostic.message
2244
+ )) {
2245
+ continue;
2246
+ }
2247
+ target.push(diagnostic);
2248
+ }
2249
+ }
2240
2250
  function renderSyntheticArgumentExpression(valueKind, argumentText) {
2241
2251
  const trimmed = argumentText.trim();
2242
2252
  if (trimmed === "") {
@@ -2503,6 +2513,16 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
2503
2513
  if (result.diagnostics.length === 0) {
2504
2514
  return [];
2505
2515
  }
2516
+ const setupDiagnostic = result.diagnostics.find((diagnostic) => diagnostic.kind !== "typescript");
2517
+ if (setupDiagnostic !== void 0) {
2518
+ return [
2519
+ makeDiagnostic(
2520
+ setupDiagnostic.kind === "unsupported-custom-type-override" ? "UNSUPPORTED_CUSTOM_TYPE_OVERRIDE" : "SYNTHETIC_SETUP_FAILURE",
2521
+ setupDiagnostic.message,
2522
+ provenance
2523
+ )
2524
+ ];
2525
+ }
2506
2526
  const expectedLabel = definition.valueKind === null ? "compatible argument" : capabilityLabel(definition.valueKind);
2507
2527
  return [
2508
2528
  makeDiagnostic(
@@ -2666,7 +2686,7 @@ function parseTSDocTags(node, file = "", options) {
2666
2686
  options
2667
2687
  );
2668
2688
  if (compilerDiagnostics.length > 0) {
2669
- diagnostics.push(...compilerDiagnostics);
2689
+ pushUniqueCompilerDiagnostics(diagnostics, compilerDiagnostics);
2670
2690
  continue;
2671
2691
  }
2672
2692
  const constraintNode = parseConstraintTagValue(
@@ -2753,7 +2773,7 @@ function parseTSDocTags(node, file = "", options) {
2753
2773
  options
2754
2774
  );
2755
2775
  if (compilerDiagnostics.length > 0) {
2756
- diagnostics.push(...compilerDiagnostics);
2776
+ pushUniqueCompilerDiagnostics(diagnostics, compilerDiagnostics);
2757
2777
  continue;
2758
2778
  }
2759
2779
  const constraintNode = parseConstraintTagValue(
@@ -2787,7 +2807,7 @@ function parseTSDocTags(node, file = "", options) {
2787
2807
  options
2788
2808
  );
2789
2809
  if (compilerDiagnostics.length > 0) {
2790
- diagnostics.push(...compilerDiagnostics);
2810
+ pushUniqueCompilerDiagnostics(diagnostics, compilerDiagnostics);
2791
2811
  continue;
2792
2812
  }
2793
2813
  const constraintNode = parseConstraintTagValue(
@@ -3016,6 +3036,21 @@ function isObjectType(type) {
3016
3036
  function isIntersectionType(type) {
3017
3037
  return !!(type.flags & ts3.TypeFlags.Intersection);
3018
3038
  }
3039
+ function isResolvableObjectLikeAliasTypeNode(typeNode) {
3040
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
3041
+ return isResolvableObjectLikeAliasTypeNode(typeNode.type);
3042
+ }
3043
+ if (ts3.isTypeLiteralNode(typeNode) || ts3.isTypeReferenceNode(typeNode)) {
3044
+ return true;
3045
+ }
3046
+ return ts3.isIntersectionTypeNode(typeNode) && typeNode.types.length > 0 && typeNode.types.every((member) => isResolvableObjectLikeAliasTypeNode(member));
3047
+ }
3048
+ function isSemanticallyPlainObjectLikeType(type, checker) {
3049
+ if (isIntersectionType(type)) {
3050
+ return type.types.length > 0 && type.types.every((member) => isSemanticallyPlainObjectLikeType(member, checker));
3051
+ }
3052
+ return isObjectType(type) && checker.getSignaturesOfType(type, ts3.SignatureKind.Call).length === 0 && checker.getSignaturesOfType(type, ts3.SignatureKind.Construct).length === 0 && !checker.isArrayType(type) && !checker.isTupleType(type);
3053
+ }
3019
3054
  function isTypeReference(type) {
3020
3055
  return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
3021
3056
  }
@@ -3268,6 +3303,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3268
3303
  const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
3269
3304
  return {
3270
3305
  ok: false,
3306
+ kind: "not-object-like",
3271
3307
  error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object-like type alias (found ${kindDesc})`
3272
3308
  };
3273
3309
  }
@@ -3282,6 +3318,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3282
3318
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
3283
3319
  return {
3284
3320
  ok: false,
3321
+ kind: "duplicate-properties",
3285
3322
  error: `Type alias "${name}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
3286
3323
  };
3287
3324
  }
@@ -3978,10 +4015,10 @@ function findDuplicateObjectLikeTypeAliasPropertyNames(members) {
3978
4015
  return [...duplicates].sort();
3979
4016
  }
3980
4017
  function getAnalyzableObjectLikePropertyName(name) {
3981
- if (!ts3.isIdentifier(name)) {
3982
- return null;
4018
+ if (ts3.isIdentifier(name) || ts3.isStringLiteral(name) || ts3.isNumericLiteral(name)) {
4019
+ return name.text;
3983
4020
  }
3984
- return name.text;
4021
+ return null;
3985
4022
  }
3986
4023
  function applyEnumMemberDisplayNames(type, annotations) {
3987
4024
  if (!annotations.some(
@@ -4191,6 +4228,19 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4191
4228
  diagnostics
4192
4229
  );
4193
4230
  }
4231
+ if (resolvedSourceTypeNode !== void 0 && isResolvableObjectLikeAliasTypeNode(resolvedSourceTypeNode) && isSemanticallyPlainObjectLikeType(type, checker)) {
4232
+ return resolveObjectType(
4233
+ type,
4234
+ checker,
4235
+ file,
4236
+ typeRegistry,
4237
+ visiting,
4238
+ sourceNode,
4239
+ metadataPolicy,
4240
+ extensionRegistry,
4241
+ diagnostics
4242
+ );
4243
+ }
4194
4244
  }
4195
4245
  if (isObjectType(type)) {
4196
4246
  return resolveObjectType(
@@ -5114,6 +5164,95 @@ function findInterfaceByName(sourceFile, interfaceName) {
5114
5164
  function findTypeAliasByName(sourceFile, aliasName) {
5115
5165
  return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
5116
5166
  }
5167
+ function getResolvedObjectRootType(rootType, typeRegistry) {
5168
+ if (rootType.kind === "object") {
5169
+ return rootType;
5170
+ }
5171
+ if (rootType.kind !== "reference") {
5172
+ return null;
5173
+ }
5174
+ const definition = typeRegistry[rootType.name];
5175
+ return definition?.type.kind === "object" ? definition.type : null;
5176
+ }
5177
+ function createResolvedObjectAliasAnalysis(name, rootType, typeRegistry, rootInfo, diagnostics) {
5178
+ const resolvedRootType = getResolvedObjectRootType(rootType, typeRegistry);
5179
+ if (resolvedRootType === null) {
5180
+ return null;
5181
+ }
5182
+ const fields = resolvedRootType.properties.map((property) => ({
5183
+ kind: "field",
5184
+ name: property.name,
5185
+ ...property.metadata !== void 0 && { metadata: property.metadata },
5186
+ type: property.type,
5187
+ required: !property.optional,
5188
+ constraints: property.constraints,
5189
+ annotations: property.annotations,
5190
+ provenance: property.provenance
5191
+ }));
5192
+ return {
5193
+ name,
5194
+ ...rootInfo.metadata !== void 0 && { metadata: rootInfo.metadata },
5195
+ fields,
5196
+ fieldLayouts: fields.map(() => ({})),
5197
+ typeRegistry,
5198
+ ...rootInfo.annotations.length > 0 && { annotations: [...rootInfo.annotations] },
5199
+ ...diagnostics.length > 0 && { diagnostics: [...diagnostics] },
5200
+ instanceMethods: [],
5201
+ staticMethods: []
5202
+ };
5203
+ }
5204
+ function containsTypeReferenceInObjectLikeAlias(typeNode) {
5205
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
5206
+ return containsTypeReferenceInObjectLikeAlias(typeNode.type);
5207
+ }
5208
+ if (ts4.isTypeReferenceNode(typeNode)) {
5209
+ return true;
5210
+ }
5211
+ return ts4.isIntersectionTypeNode(typeNode) && typeNode.types.some((member) => containsTypeReferenceInObjectLikeAlias(member));
5212
+ }
5213
+ function collectFallbackAliasMemberPropertyNames(typeNode, checker) {
5214
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
5215
+ return collectFallbackAliasMemberPropertyNames(typeNode.type, checker);
5216
+ }
5217
+ if (ts4.isTypeLiteralNode(typeNode)) {
5218
+ const propertyNames = [];
5219
+ for (const member of typeNode.members) {
5220
+ if (!ts4.isPropertySignature(member)) {
5221
+ continue;
5222
+ }
5223
+ const propertyName = getAnalyzableObjectLikePropertyName(member.name);
5224
+ if (propertyName !== null) {
5225
+ propertyNames.push(propertyName);
5226
+ }
5227
+ }
5228
+ return propertyNames;
5229
+ }
5230
+ if (ts4.isTypeReferenceNode(typeNode)) {
5231
+ return checker.getTypeFromTypeNode(typeNode).getProperties().map((property) => property.getName());
5232
+ }
5233
+ return null;
5234
+ }
5235
+ function findFallbackAliasDuplicatePropertyNames(typeNode, checker) {
5236
+ if (!ts4.isIntersectionTypeNode(typeNode)) {
5237
+ return [];
5238
+ }
5239
+ const seen = /* @__PURE__ */ new Set();
5240
+ const duplicates = /* @__PURE__ */ new Set();
5241
+ for (const member of typeNode.types) {
5242
+ const propertyNames = collectFallbackAliasMemberPropertyNames(member, checker);
5243
+ if (propertyNames === null) {
5244
+ continue;
5245
+ }
5246
+ for (const propertyName of propertyNames) {
5247
+ if (seen.has(propertyName)) {
5248
+ duplicates.add(propertyName);
5249
+ } else {
5250
+ seen.add(propertyName);
5251
+ }
5252
+ }
5253
+ }
5254
+ return [...duplicates].sort();
5255
+ }
5117
5256
  function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
5118
5257
  const ctx = createProgramContext(filePath);
5119
5258
  return analyzeNamedTypeToIRFromProgramContext(
@@ -5162,6 +5301,51 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
5162
5301
  if (result.ok) {
5163
5302
  return result.analysis;
5164
5303
  }
5304
+ const fallbackEligible = result.kind === "not-object-like" && isResolvableObjectLikeAliasTypeNode(typeAlias.type) && containsTypeReferenceInObjectLikeAlias(typeAlias.type);
5305
+ if (!fallbackEligible) {
5306
+ throw new Error(result.error);
5307
+ }
5308
+ const duplicatePropertyNames = findFallbackAliasDuplicatePropertyNames(
5309
+ typeAlias.type,
5310
+ ctx.checker
5311
+ );
5312
+ if (duplicatePropertyNames.length > 0) {
5313
+ const sourceFile = typeAlias.getSourceFile();
5314
+ const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
5315
+ throw new Error(
5316
+ `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
5317
+ );
5318
+ }
5319
+ const rootInfo = analyzeDeclarationRootInfo(
5320
+ typeAlias,
5321
+ ctx.checker,
5322
+ analysisFilePath,
5323
+ extensionRegistry,
5324
+ metadataPolicy
5325
+ );
5326
+ const diagnostics = [...rootInfo.diagnostics];
5327
+ const typeRegistry = {};
5328
+ const rootType = resolveTypeNode(
5329
+ ctx.checker.getTypeAtLocation(typeAlias),
5330
+ ctx.checker,
5331
+ analysisFilePath,
5332
+ typeRegistry,
5333
+ /* @__PURE__ */ new Set(),
5334
+ typeAlias,
5335
+ createAnalyzerMetadataPolicy(metadataPolicy, discriminatorOptions),
5336
+ extensionRegistry,
5337
+ diagnostics
5338
+ );
5339
+ const fallbackAnalysis = createResolvedObjectAliasAnalysis(
5340
+ typeAlias.name.text,
5341
+ rootType,
5342
+ typeRegistry,
5343
+ rootInfo,
5344
+ diagnostics
5345
+ );
5346
+ if (fallbackAnalysis !== null) {
5347
+ return fallbackAnalysis;
5348
+ }
5165
5349
  throw new Error(result.error);
5166
5350
  }
5167
5351
  throw new Error(
@@ -5704,15 +5888,36 @@ function generateSchemasFromParameter(options) {
5704
5888
  }
5705
5889
  function generateSchemasFromReturnType(options) {
5706
5890
  const signature = options.context.checker.getSignatureFromDeclaration(options.declaration);
5707
- const type = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
5891
+ const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
5892
+ const type = unwrapPromiseType(options.context.checker, returnType);
5893
+ const sourceNode = type !== returnType ? unwrapPromiseTypeNode(options.declaration.type) ?? options.declaration.type ?? options.declaration : options.declaration.type ?? options.declaration;
5708
5894
  const fallbackName = options.declaration.name !== void 0 && ts7.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
5709
5895
  return generateSchemasFromResolvedType({
5710
5896
  ...options,
5711
5897
  type,
5712
- sourceNode: options.declaration.type ?? options.declaration,
5898
+ sourceNode,
5713
5899
  name: fallbackName
5714
5900
  });
5715
5901
  }
5902
+ function unwrapPromiseType(checker, type) {
5903
+ if (!("getAwaitedType" in checker) || typeof checker.getAwaitedType !== "function") {
5904
+ return type;
5905
+ }
5906
+ return checker.getAwaitedType(type) ?? type;
5907
+ }
5908
+ function unwrapPromiseTypeNode(typeNode) {
5909
+ if (typeNode === void 0) {
5910
+ return void 0;
5911
+ }
5912
+ if (ts7.isParenthesizedTypeNode(typeNode)) {
5913
+ const unwrapped = unwrapPromiseTypeNode(typeNode.type);
5914
+ return unwrapped ?? typeNode;
5915
+ }
5916
+ return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
5917
+ }
5918
+ function isPromiseTypeReferenceNode(typeNode) {
5919
+ return ts7.isTypeReferenceNode(typeNode) && ts7.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
5920
+ }
5716
5921
  var init_discovered_schema = __esm({
5717
5922
  "src/generators/discovered-schema.ts"() {
5718
5923
  "use strict";