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

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.js CHANGED
@@ -2906,6 +2906,21 @@ function isObjectType(type) {
2906
2906
  function isIntersectionType(type) {
2907
2907
  return !!(type.flags & ts3.TypeFlags.Intersection);
2908
2908
  }
2909
+ function isResolvableObjectLikeAliasTypeNode(typeNode) {
2910
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
2911
+ return isResolvableObjectLikeAliasTypeNode(typeNode.type);
2912
+ }
2913
+ if (ts3.isTypeLiteralNode(typeNode) || ts3.isTypeReferenceNode(typeNode)) {
2914
+ return true;
2915
+ }
2916
+ return ts3.isIntersectionTypeNode(typeNode) && typeNode.types.length > 0 && typeNode.types.every((member) => isResolvableObjectLikeAliasTypeNode(member));
2917
+ }
2918
+ function isSemanticallyPlainObjectLikeType(type, checker) {
2919
+ if (isIntersectionType(type)) {
2920
+ return type.types.length > 0 && type.types.every((member) => isSemanticallyPlainObjectLikeType(member, checker));
2921
+ }
2922
+ 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);
2923
+ }
2909
2924
  function isTypeReference(type) {
2910
2925
  return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
2911
2926
  }
@@ -3163,6 +3178,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3163
3178
  const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
3164
3179
  return {
3165
3180
  ok: false,
3181
+ kind: "not-object-like",
3166
3182
  error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object-like type alias (found ${kindDesc})`
3167
3183
  };
3168
3184
  }
@@ -3177,6 +3193,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3177
3193
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
3178
3194
  return {
3179
3195
  ok: false,
3196
+ kind: "duplicate-properties",
3180
3197
  error: `Type alias "${name}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
3181
3198
  };
3182
3199
  }
@@ -3873,10 +3890,10 @@ function findDuplicateObjectLikeTypeAliasPropertyNames(members) {
3873
3890
  return [...duplicates].sort();
3874
3891
  }
3875
3892
  function getAnalyzableObjectLikePropertyName(name) {
3876
- if (!ts3.isIdentifier(name)) {
3877
- return null;
3893
+ if (ts3.isIdentifier(name) || ts3.isStringLiteral(name) || ts3.isNumericLiteral(name)) {
3894
+ return name.text;
3878
3895
  }
3879
- return name.text;
3896
+ return null;
3880
3897
  }
3881
3898
  function applyEnumMemberDisplayNames(type, annotations) {
3882
3899
  if (!annotations.some(
@@ -4086,6 +4103,19 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4086
4103
  diagnostics
4087
4104
  );
4088
4105
  }
4106
+ if (resolvedSourceTypeNode !== void 0 && isResolvableObjectLikeAliasTypeNode(resolvedSourceTypeNode) && isSemanticallyPlainObjectLikeType(type, checker)) {
4107
+ return resolveObjectType(
4108
+ type,
4109
+ checker,
4110
+ file,
4111
+ typeRegistry,
4112
+ visiting,
4113
+ sourceNode,
4114
+ metadataPolicy,
4115
+ extensionRegistry,
4116
+ diagnostics
4117
+ );
4118
+ }
4089
4119
  }
4090
4120
  if (isObjectType(type)) {
4091
4121
  return resolveObjectType(
@@ -4993,6 +5023,95 @@ function findInterfaceByName(sourceFile, interfaceName) {
4993
5023
  function findTypeAliasByName(sourceFile, aliasName) {
4994
5024
  return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
4995
5025
  }
5026
+ function getResolvedObjectRootType(rootType, typeRegistry) {
5027
+ if (rootType.kind === "object") {
5028
+ return rootType;
5029
+ }
5030
+ if (rootType.kind !== "reference") {
5031
+ return null;
5032
+ }
5033
+ const definition = typeRegistry[rootType.name];
5034
+ return definition?.type.kind === "object" ? definition.type : null;
5035
+ }
5036
+ function createResolvedObjectAliasAnalysis(name, rootType, typeRegistry, rootInfo, diagnostics) {
5037
+ const resolvedRootType = getResolvedObjectRootType(rootType, typeRegistry);
5038
+ if (resolvedRootType === null) {
5039
+ return null;
5040
+ }
5041
+ const fields = resolvedRootType.properties.map((property) => ({
5042
+ kind: "field",
5043
+ name: property.name,
5044
+ ...property.metadata !== void 0 && { metadata: property.metadata },
5045
+ type: property.type,
5046
+ required: !property.optional,
5047
+ constraints: property.constraints,
5048
+ annotations: property.annotations,
5049
+ provenance: property.provenance
5050
+ }));
5051
+ return {
5052
+ name,
5053
+ ...rootInfo.metadata !== void 0 && { metadata: rootInfo.metadata },
5054
+ fields,
5055
+ fieldLayouts: fields.map(() => ({})),
5056
+ typeRegistry,
5057
+ ...rootInfo.annotations.length > 0 && { annotations: [...rootInfo.annotations] },
5058
+ ...diagnostics.length > 0 && { diagnostics: [...diagnostics] },
5059
+ instanceMethods: [],
5060
+ staticMethods: []
5061
+ };
5062
+ }
5063
+ function containsTypeReferenceInObjectLikeAlias(typeNode) {
5064
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
5065
+ return containsTypeReferenceInObjectLikeAlias(typeNode.type);
5066
+ }
5067
+ if (ts4.isTypeReferenceNode(typeNode)) {
5068
+ return true;
5069
+ }
5070
+ return ts4.isIntersectionTypeNode(typeNode) && typeNode.types.some((member) => containsTypeReferenceInObjectLikeAlias(member));
5071
+ }
5072
+ function collectFallbackAliasMemberPropertyNames(typeNode, checker) {
5073
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
5074
+ return collectFallbackAliasMemberPropertyNames(typeNode.type, checker);
5075
+ }
5076
+ if (ts4.isTypeLiteralNode(typeNode)) {
5077
+ const propertyNames = [];
5078
+ for (const member of typeNode.members) {
5079
+ if (!ts4.isPropertySignature(member)) {
5080
+ continue;
5081
+ }
5082
+ const propertyName = getAnalyzableObjectLikePropertyName(member.name);
5083
+ if (propertyName !== null) {
5084
+ propertyNames.push(propertyName);
5085
+ }
5086
+ }
5087
+ return propertyNames;
5088
+ }
5089
+ if (ts4.isTypeReferenceNode(typeNode)) {
5090
+ return checker.getTypeFromTypeNode(typeNode).getProperties().map((property) => property.getName());
5091
+ }
5092
+ return null;
5093
+ }
5094
+ function findFallbackAliasDuplicatePropertyNames(typeNode, checker) {
5095
+ if (!ts4.isIntersectionTypeNode(typeNode)) {
5096
+ return [];
5097
+ }
5098
+ const seen = /* @__PURE__ */ new Set();
5099
+ const duplicates = /* @__PURE__ */ new Set();
5100
+ for (const member of typeNode.types) {
5101
+ const propertyNames = collectFallbackAliasMemberPropertyNames(member, checker);
5102
+ if (propertyNames === null) {
5103
+ continue;
5104
+ }
5105
+ for (const propertyName of propertyNames) {
5106
+ if (seen.has(propertyName)) {
5107
+ duplicates.add(propertyName);
5108
+ } else {
5109
+ seen.add(propertyName);
5110
+ }
5111
+ }
5112
+ }
5113
+ return [...duplicates].sort();
5114
+ }
4996
5115
  function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
4997
5116
  const ctx = createProgramContext(filePath);
4998
5117
  return analyzeNamedTypeToIRFromProgramContext(
@@ -5041,6 +5160,51 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
5041
5160
  if (result.ok) {
5042
5161
  return result.analysis;
5043
5162
  }
5163
+ const fallbackEligible = result.kind === "not-object-like" && isResolvableObjectLikeAliasTypeNode(typeAlias.type) && containsTypeReferenceInObjectLikeAlias(typeAlias.type);
5164
+ if (!fallbackEligible) {
5165
+ throw new Error(result.error);
5166
+ }
5167
+ const duplicatePropertyNames = findFallbackAliasDuplicatePropertyNames(
5168
+ typeAlias.type,
5169
+ ctx.checker
5170
+ );
5171
+ if (duplicatePropertyNames.length > 0) {
5172
+ const sourceFile = typeAlias.getSourceFile();
5173
+ const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
5174
+ throw new Error(
5175
+ `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
5176
+ );
5177
+ }
5178
+ const rootInfo = analyzeDeclarationRootInfo(
5179
+ typeAlias,
5180
+ ctx.checker,
5181
+ analysisFilePath,
5182
+ extensionRegistry,
5183
+ metadataPolicy
5184
+ );
5185
+ const diagnostics = [...rootInfo.diagnostics];
5186
+ const typeRegistry = {};
5187
+ const rootType = resolveTypeNode(
5188
+ ctx.checker.getTypeAtLocation(typeAlias),
5189
+ ctx.checker,
5190
+ analysisFilePath,
5191
+ typeRegistry,
5192
+ /* @__PURE__ */ new Set(),
5193
+ typeAlias,
5194
+ createAnalyzerMetadataPolicy(metadataPolicy, discriminatorOptions),
5195
+ extensionRegistry,
5196
+ diagnostics
5197
+ );
5198
+ const fallbackAnalysis = createResolvedObjectAliasAnalysis(
5199
+ typeAlias.name.text,
5200
+ rootType,
5201
+ typeRegistry,
5202
+ rootInfo,
5203
+ diagnostics
5204
+ );
5205
+ if (fallbackAnalysis !== null) {
5206
+ return fallbackAnalysis;
5207
+ }
5044
5208
  throw new Error(result.error);
5045
5209
  }
5046
5210
  throw new Error(
@@ -5546,15 +5710,36 @@ function generateSchemasFromParameter(options) {
5546
5710
  }
5547
5711
  function generateSchemasFromReturnType(options) {
5548
5712
  const signature = options.context.checker.getSignatureFromDeclaration(options.declaration);
5549
- const type = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
5713
+ const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
5714
+ const type = unwrapPromiseType(options.context.checker, returnType);
5715
+ const sourceNode = type !== returnType ? unwrapPromiseTypeNode(options.declaration.type) ?? options.declaration.type ?? options.declaration : options.declaration.type ?? options.declaration;
5550
5716
  const fallbackName = options.declaration.name !== void 0 && ts7.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
5551
5717
  return generateSchemasFromResolvedType({
5552
5718
  ...options,
5553
5719
  type,
5554
- sourceNode: options.declaration.type ?? options.declaration,
5720
+ sourceNode,
5555
5721
  name: fallbackName
5556
5722
  });
5557
5723
  }
5724
+ function unwrapPromiseType(checker, type) {
5725
+ if (!("getAwaitedType" in checker) || typeof checker.getAwaitedType !== "function") {
5726
+ return type;
5727
+ }
5728
+ return checker.getAwaitedType(type) ?? type;
5729
+ }
5730
+ function unwrapPromiseTypeNode(typeNode) {
5731
+ if (typeNode === void 0) {
5732
+ return void 0;
5733
+ }
5734
+ if (ts7.isParenthesizedTypeNode(typeNode)) {
5735
+ const unwrapped = unwrapPromiseTypeNode(typeNode.type);
5736
+ return unwrapped ?? typeNode;
5737
+ }
5738
+ return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
5739
+ }
5740
+ function isPromiseTypeReferenceNode(typeNode) {
5741
+ return ts7.isTypeReferenceNode(typeNode) && ts7.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
5742
+ }
5558
5743
 
5559
5744
  // src/generators/mixed-authoring.ts
5560
5745
  function buildMixedAuthoringSchemas(options) {