@formspec/build 0.1.0-alpha.35 → 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/cli.js CHANGED
@@ -3016,6 +3016,21 @@ function isObjectType(type) {
3016
3016
  function isIntersectionType(type) {
3017
3017
  return !!(type.flags & ts3.TypeFlags.Intersection);
3018
3018
  }
3019
+ function isResolvableObjectLikeAliasTypeNode(typeNode) {
3020
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
3021
+ return isResolvableObjectLikeAliasTypeNode(typeNode.type);
3022
+ }
3023
+ if (ts3.isTypeLiteralNode(typeNode) || ts3.isTypeReferenceNode(typeNode)) {
3024
+ return true;
3025
+ }
3026
+ return ts3.isIntersectionTypeNode(typeNode) && typeNode.types.length > 0 && typeNode.types.every((member) => isResolvableObjectLikeAliasTypeNode(member));
3027
+ }
3028
+ function isSemanticallyPlainObjectLikeType(type, checker) {
3029
+ if (isIntersectionType(type)) {
3030
+ return type.types.length > 0 && type.types.every((member) => isSemanticallyPlainObjectLikeType(member, checker));
3031
+ }
3032
+ 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);
3033
+ }
3019
3034
  function isTypeReference(type) {
3020
3035
  return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
3021
3036
  }
@@ -3268,6 +3283,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3268
3283
  const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
3269
3284
  return {
3270
3285
  ok: false,
3286
+ kind: "not-object-like",
3271
3287
  error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object-like type alias (found ${kindDesc})`
3272
3288
  };
3273
3289
  }
@@ -3282,6 +3298,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
3282
3298
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
3283
3299
  return {
3284
3300
  ok: false,
3301
+ kind: "duplicate-properties",
3285
3302
  error: `Type alias "${name}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
3286
3303
  };
3287
3304
  }
@@ -3978,10 +3995,10 @@ function findDuplicateObjectLikeTypeAliasPropertyNames(members) {
3978
3995
  return [...duplicates].sort();
3979
3996
  }
3980
3997
  function getAnalyzableObjectLikePropertyName(name) {
3981
- if (!ts3.isIdentifier(name)) {
3982
- return null;
3998
+ if (ts3.isIdentifier(name) || ts3.isStringLiteral(name) || ts3.isNumericLiteral(name)) {
3999
+ return name.text;
3983
4000
  }
3984
- return name.text;
4001
+ return null;
3985
4002
  }
3986
4003
  function applyEnumMemberDisplayNames(type, annotations) {
3987
4004
  if (!annotations.some(
@@ -4072,7 +4089,7 @@ function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, ch
4072
4089
  };
4073
4090
  }
4074
4091
  if (ts3.isTypeReferenceNode(typeNode) && ts3.isIdentifier(typeNode.typeName)) {
4075
- const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
4092
+ const aliasDecl = getTypeAliasDeclarationFromTypeReference(typeNode, checker);
4076
4093
  if (aliasDecl !== void 0) {
4077
4094
  return resolveRegisteredCustomTypeFromTypeNode(aliasDecl.type, extensionRegistry, checker);
4078
4095
  }
@@ -4191,6 +4208,19 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
4191
4208
  diagnostics
4192
4209
  );
4193
4210
  }
4211
+ if (resolvedSourceTypeNode !== void 0 && isResolvableObjectLikeAliasTypeNode(resolvedSourceTypeNode) && isSemanticallyPlainObjectLikeType(type, checker)) {
4212
+ return resolveObjectType(
4213
+ type,
4214
+ checker,
4215
+ file,
4216
+ typeRegistry,
4217
+ visiting,
4218
+ sourceNode,
4219
+ metadataPolicy,
4220
+ extensionRegistry,
4221
+ diagnostics
4222
+ );
4223
+ }
4194
4224
  }
4195
4225
  if (isObjectType(type)) {
4196
4226
  return resolveObjectType(
@@ -4265,13 +4295,13 @@ function getReferencedTypeAliasDeclaration(sourceNode, checker) {
4265
4295
  if (!typeNode || !ts3.isTypeReferenceNode(typeNode)) {
4266
4296
  return void 0;
4267
4297
  }
4268
- return checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
4298
+ return getTypeAliasDeclarationFromTypeReference(typeNode, checker);
4269
4299
  }
4270
4300
  function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
4271
4301
  if (!ts3.isTypeReferenceNode(typeNode)) {
4272
4302
  return false;
4273
4303
  }
4274
- const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
4304
+ const aliasDecl = getTypeAliasDeclarationFromTypeReference(typeNode, checker);
4275
4305
  if (!aliasDecl) {
4276
4306
  return false;
4277
4307
  }
@@ -4840,8 +4870,7 @@ function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new
4840
4870
  if (!ts3.isTypeReferenceNode(typeNode) || !ts3.isIdentifier(typeNode.typeName)) {
4841
4871
  return typeNode;
4842
4872
  }
4843
- const symbol = checker.getSymbolAtLocation(typeNode.typeName);
4844
- const aliasDecl = symbol?.declarations?.find(ts3.isTypeAliasDeclaration);
4873
+ const aliasDecl = getTypeAliasDeclarationFromTypeReference(typeNode, checker);
4845
4874
  if (aliasDecl === void 0 || visited.has(aliasDecl)) {
4846
4875
  return typeNode;
4847
4876
  }
@@ -4890,8 +4919,7 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
4890
4919
  );
4891
4920
  }
4892
4921
  const symbol = checker.getSymbolAtLocation(typeNode.typeName);
4893
- if (!symbol?.declarations) return [];
4894
- const aliasDecl = symbol.declarations.find(ts3.isTypeAliasDeclaration);
4922
+ const aliasDecl = getAliasedTypeAliasDeclaration(symbol, checker);
4895
4923
  if (!aliasDecl) return [];
4896
4924
  if (ts3.isTypeLiteralNode(aliasDecl.type)) return [];
4897
4925
  const aliasFieldType = resolveTypeNode(
@@ -4914,6 +4942,18 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
4914
4942
  );
4915
4943
  return constraints;
4916
4944
  }
4945
+ function getAliasedSymbol(symbol, checker) {
4946
+ if (symbol === void 0) {
4947
+ return void 0;
4948
+ }
4949
+ return symbol.flags & ts3.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
4950
+ }
4951
+ function getAliasedTypeAliasDeclaration(symbol, checker) {
4952
+ return getAliasedSymbol(symbol, checker)?.declarations?.find(ts3.isTypeAliasDeclaration);
4953
+ }
4954
+ function getTypeAliasDeclarationFromTypeReference(typeNode, checker) {
4955
+ return getAliasedTypeAliasDeclaration(checker.getSymbolAtLocation(typeNode.typeName), checker);
4956
+ }
4917
4957
  function provenanceForNode(node, file) {
4918
4958
  const sourceFile = node.getSourceFile();
4919
4959
  const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
@@ -5104,6 +5144,95 @@ function findInterfaceByName(sourceFile, interfaceName) {
5104
5144
  function findTypeAliasByName(sourceFile, aliasName) {
5105
5145
  return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
5106
5146
  }
5147
+ function getResolvedObjectRootType(rootType, typeRegistry) {
5148
+ if (rootType.kind === "object") {
5149
+ return rootType;
5150
+ }
5151
+ if (rootType.kind !== "reference") {
5152
+ return null;
5153
+ }
5154
+ const definition = typeRegistry[rootType.name];
5155
+ return definition?.type.kind === "object" ? definition.type : null;
5156
+ }
5157
+ function createResolvedObjectAliasAnalysis(name, rootType, typeRegistry, rootInfo, diagnostics) {
5158
+ const resolvedRootType = getResolvedObjectRootType(rootType, typeRegistry);
5159
+ if (resolvedRootType === null) {
5160
+ return null;
5161
+ }
5162
+ const fields = resolvedRootType.properties.map((property) => ({
5163
+ kind: "field",
5164
+ name: property.name,
5165
+ ...property.metadata !== void 0 && { metadata: property.metadata },
5166
+ type: property.type,
5167
+ required: !property.optional,
5168
+ constraints: property.constraints,
5169
+ annotations: property.annotations,
5170
+ provenance: property.provenance
5171
+ }));
5172
+ return {
5173
+ name,
5174
+ ...rootInfo.metadata !== void 0 && { metadata: rootInfo.metadata },
5175
+ fields,
5176
+ fieldLayouts: fields.map(() => ({})),
5177
+ typeRegistry,
5178
+ ...rootInfo.annotations.length > 0 && { annotations: [...rootInfo.annotations] },
5179
+ ...diagnostics.length > 0 && { diagnostics: [...diagnostics] },
5180
+ instanceMethods: [],
5181
+ staticMethods: []
5182
+ };
5183
+ }
5184
+ function containsTypeReferenceInObjectLikeAlias(typeNode) {
5185
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
5186
+ return containsTypeReferenceInObjectLikeAlias(typeNode.type);
5187
+ }
5188
+ if (ts4.isTypeReferenceNode(typeNode)) {
5189
+ return true;
5190
+ }
5191
+ return ts4.isIntersectionTypeNode(typeNode) && typeNode.types.some((member) => containsTypeReferenceInObjectLikeAlias(member));
5192
+ }
5193
+ function collectFallbackAliasMemberPropertyNames(typeNode, checker) {
5194
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
5195
+ return collectFallbackAliasMemberPropertyNames(typeNode.type, checker);
5196
+ }
5197
+ if (ts4.isTypeLiteralNode(typeNode)) {
5198
+ const propertyNames = [];
5199
+ for (const member of typeNode.members) {
5200
+ if (!ts4.isPropertySignature(member)) {
5201
+ continue;
5202
+ }
5203
+ const propertyName = getAnalyzableObjectLikePropertyName(member.name);
5204
+ if (propertyName !== null) {
5205
+ propertyNames.push(propertyName);
5206
+ }
5207
+ }
5208
+ return propertyNames;
5209
+ }
5210
+ if (ts4.isTypeReferenceNode(typeNode)) {
5211
+ return checker.getTypeFromTypeNode(typeNode).getProperties().map((property) => property.getName());
5212
+ }
5213
+ return null;
5214
+ }
5215
+ function findFallbackAliasDuplicatePropertyNames(typeNode, checker) {
5216
+ if (!ts4.isIntersectionTypeNode(typeNode)) {
5217
+ return [];
5218
+ }
5219
+ const seen = /* @__PURE__ */ new Set();
5220
+ const duplicates = /* @__PURE__ */ new Set();
5221
+ for (const member of typeNode.types) {
5222
+ const propertyNames = collectFallbackAliasMemberPropertyNames(member, checker);
5223
+ if (propertyNames === null) {
5224
+ continue;
5225
+ }
5226
+ for (const propertyName of propertyNames) {
5227
+ if (seen.has(propertyName)) {
5228
+ duplicates.add(propertyName);
5229
+ } else {
5230
+ seen.add(propertyName);
5231
+ }
5232
+ }
5233
+ }
5234
+ return [...duplicates].sort();
5235
+ }
5107
5236
  function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
5108
5237
  const ctx = createProgramContext(filePath);
5109
5238
  return analyzeNamedTypeToIRFromProgramContext(
@@ -5152,6 +5281,51 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
5152
5281
  if (result.ok) {
5153
5282
  return result.analysis;
5154
5283
  }
5284
+ const fallbackEligible = result.kind === "not-object-like" && isResolvableObjectLikeAliasTypeNode(typeAlias.type) && containsTypeReferenceInObjectLikeAlias(typeAlias.type);
5285
+ if (!fallbackEligible) {
5286
+ throw new Error(result.error);
5287
+ }
5288
+ const duplicatePropertyNames = findFallbackAliasDuplicatePropertyNames(
5289
+ typeAlias.type,
5290
+ ctx.checker
5291
+ );
5292
+ if (duplicatePropertyNames.length > 0) {
5293
+ const sourceFile = typeAlias.getSourceFile();
5294
+ const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
5295
+ throw new Error(
5296
+ `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
5297
+ );
5298
+ }
5299
+ const rootInfo = analyzeDeclarationRootInfo(
5300
+ typeAlias,
5301
+ ctx.checker,
5302
+ analysisFilePath,
5303
+ extensionRegistry,
5304
+ metadataPolicy
5305
+ );
5306
+ const diagnostics = [...rootInfo.diagnostics];
5307
+ const typeRegistry = {};
5308
+ const rootType = resolveTypeNode(
5309
+ ctx.checker.getTypeAtLocation(typeAlias),
5310
+ ctx.checker,
5311
+ analysisFilePath,
5312
+ typeRegistry,
5313
+ /* @__PURE__ */ new Set(),
5314
+ typeAlias,
5315
+ createAnalyzerMetadataPolicy(metadataPolicy, discriminatorOptions),
5316
+ extensionRegistry,
5317
+ diagnostics
5318
+ );
5319
+ const fallbackAnalysis = createResolvedObjectAliasAnalysis(
5320
+ typeAlias.name.text,
5321
+ rootType,
5322
+ typeRegistry,
5323
+ rootInfo,
5324
+ diagnostics
5325
+ );
5326
+ if (fallbackAnalysis !== null) {
5327
+ return fallbackAnalysis;
5328
+ }
5155
5329
  throw new Error(result.error);
5156
5330
  }
5157
5331
  throw new Error(
@@ -5694,15 +5868,36 @@ function generateSchemasFromParameter(options) {
5694
5868
  }
5695
5869
  function generateSchemasFromReturnType(options) {
5696
5870
  const signature = options.context.checker.getSignatureFromDeclaration(options.declaration);
5697
- const type = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
5871
+ const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
5872
+ const type = unwrapPromiseType(options.context.checker, returnType);
5873
+ const sourceNode = type !== returnType ? unwrapPromiseTypeNode(options.declaration.type) ?? options.declaration.type ?? options.declaration : options.declaration.type ?? options.declaration;
5698
5874
  const fallbackName = options.declaration.name !== void 0 && ts7.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
5699
5875
  return generateSchemasFromResolvedType({
5700
5876
  ...options,
5701
5877
  type,
5702
- sourceNode: options.declaration.type ?? options.declaration,
5878
+ sourceNode,
5703
5879
  name: fallbackName
5704
5880
  });
5705
5881
  }
5882
+ function unwrapPromiseType(checker, type) {
5883
+ if (!("getAwaitedType" in checker) || typeof checker.getAwaitedType !== "function") {
5884
+ return type;
5885
+ }
5886
+ return checker.getAwaitedType(type) ?? type;
5887
+ }
5888
+ function unwrapPromiseTypeNode(typeNode) {
5889
+ if (typeNode === void 0) {
5890
+ return void 0;
5891
+ }
5892
+ if (ts7.isParenthesizedTypeNode(typeNode)) {
5893
+ const unwrapped = unwrapPromiseTypeNode(typeNode.type);
5894
+ return unwrapped ?? typeNode;
5895
+ }
5896
+ return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
5897
+ }
5898
+ function isPromiseTypeReferenceNode(typeNode) {
5899
+ return ts7.isTypeReferenceNode(typeNode) && ts7.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
5900
+ }
5706
5901
  var init_discovered_schema = __esm({
5707
5902
  "src/generators/discovered-schema.ts"() {
5708
5903
  "use strict";