@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.
@@ -1680,6 +1680,21 @@ function isObjectType(type) {
1680
1680
  function isIntersectionType(type) {
1681
1681
  return !!(type.flags & ts3.TypeFlags.Intersection);
1682
1682
  }
1683
+ function isResolvableObjectLikeAliasTypeNode(typeNode) {
1684
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
1685
+ return isResolvableObjectLikeAliasTypeNode(typeNode.type);
1686
+ }
1687
+ if (ts3.isTypeLiteralNode(typeNode) || ts3.isTypeReferenceNode(typeNode)) {
1688
+ return true;
1689
+ }
1690
+ return ts3.isIntersectionTypeNode(typeNode) && typeNode.types.length > 0 && typeNode.types.every((member) => isResolvableObjectLikeAliasTypeNode(member));
1691
+ }
1692
+ function isSemanticallyPlainObjectLikeType(type, checker) {
1693
+ if (isIntersectionType(type)) {
1694
+ return type.types.length > 0 && type.types.every((member) => isSemanticallyPlainObjectLikeType(member, checker));
1695
+ }
1696
+ 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);
1697
+ }
1683
1698
  function isTypeReference(type) {
1684
1699
  return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
1685
1700
  }
@@ -1743,6 +1758,35 @@ function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node,
1743
1758
  }
1744
1759
  return resolvedMetadata;
1745
1760
  }
1761
+ function analyzeDeclarationRootInfo(declaration, checker, file = "", extensionRegistry, metadataPolicy) {
1762
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
1763
+ const declarationType = checker.getTypeAtLocation(declaration);
1764
+ const logicalName = ts3.isClassDeclaration(declaration) ? declaration.name?.text ?? "AnonymousClass" : declaration.name.text;
1765
+ const docResult = extractJSDocParseResult(
1766
+ declaration,
1767
+ file,
1768
+ makeParseOptions(extensionRegistry, void 0, checker, declarationType, declarationType)
1769
+ );
1770
+ const metadata = resolveNodeMetadata(
1771
+ normalizedMetadataPolicy,
1772
+ "type",
1773
+ logicalName,
1774
+ declaration,
1775
+ checker,
1776
+ extensionRegistry,
1777
+ {
1778
+ checker,
1779
+ declaration,
1780
+ subjectType: declarationType,
1781
+ hostType: declarationType
1782
+ }
1783
+ );
1784
+ return {
1785
+ ...metadata !== void 0 && { metadata },
1786
+ annotations: docResult.annotations,
1787
+ diagnostics: docResult.diagnostics
1788
+ };
1789
+ }
1746
1790
  function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
1747
1791
  const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
1748
1792
  metadataPolicy,
@@ -1908,6 +1952,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
1908
1952
  const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
1909
1953
  return {
1910
1954
  ok: false,
1955
+ kind: "not-object-like",
1911
1956
  error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object-like type alias (found ${kindDesc})`
1912
1957
  };
1913
1958
  }
@@ -1922,6 +1967,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
1922
1967
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
1923
1968
  return {
1924
1969
  ok: false,
1970
+ kind: "duplicate-properties",
1925
1971
  error: `Type alias "${name}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
1926
1972
  };
1927
1973
  }
@@ -2618,10 +2664,10 @@ function findDuplicateObjectLikeTypeAliasPropertyNames(members) {
2618
2664
  return [...duplicates].sort();
2619
2665
  }
2620
2666
  function getAnalyzableObjectLikePropertyName(name) {
2621
- if (!ts3.isIdentifier(name)) {
2622
- return null;
2667
+ if (ts3.isIdentifier(name) || ts3.isStringLiteral(name) || ts3.isNumericLiteral(name)) {
2668
+ return name.text;
2623
2669
  }
2624
- return name.text;
2670
+ return null;
2625
2671
  }
2626
2672
  function applyEnumMemberDisplayNames(type, annotations) {
2627
2673
  if (!annotations.some(
@@ -2831,6 +2877,19 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
2831
2877
  diagnostics
2832
2878
  );
2833
2879
  }
2880
+ if (resolvedSourceTypeNode !== void 0 && isResolvableObjectLikeAliasTypeNode(resolvedSourceTypeNode) && isSemanticallyPlainObjectLikeType(type, checker)) {
2881
+ return resolveObjectType(
2882
+ type,
2883
+ checker,
2884
+ file,
2885
+ typeRegistry,
2886
+ visiting,
2887
+ sourceNode,
2888
+ metadataPolicy,
2889
+ extensionRegistry,
2890
+ diagnostics
2891
+ );
2892
+ }
2834
2893
  }
2835
2894
  if (isObjectType(type)) {
2836
2895
  return resolveObjectType(
@@ -3738,6 +3797,95 @@ function findInterfaceByName(sourceFile, interfaceName) {
3738
3797
  function findTypeAliasByName(sourceFile, aliasName) {
3739
3798
  return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
3740
3799
  }
3800
+ function getResolvedObjectRootType(rootType, typeRegistry) {
3801
+ if (rootType.kind === "object") {
3802
+ return rootType;
3803
+ }
3804
+ if (rootType.kind !== "reference") {
3805
+ return null;
3806
+ }
3807
+ const definition = typeRegistry[rootType.name];
3808
+ return definition?.type.kind === "object" ? definition.type : null;
3809
+ }
3810
+ function createResolvedObjectAliasAnalysis(name, rootType, typeRegistry, rootInfo, diagnostics) {
3811
+ const resolvedRootType = getResolvedObjectRootType(rootType, typeRegistry);
3812
+ if (resolvedRootType === null) {
3813
+ return null;
3814
+ }
3815
+ const fields = resolvedRootType.properties.map((property) => ({
3816
+ kind: "field",
3817
+ name: property.name,
3818
+ ...property.metadata !== void 0 && { metadata: property.metadata },
3819
+ type: property.type,
3820
+ required: !property.optional,
3821
+ constraints: property.constraints,
3822
+ annotations: property.annotations,
3823
+ provenance: property.provenance
3824
+ }));
3825
+ return {
3826
+ name,
3827
+ ...rootInfo.metadata !== void 0 && { metadata: rootInfo.metadata },
3828
+ fields,
3829
+ fieldLayouts: fields.map(() => ({})),
3830
+ typeRegistry,
3831
+ ...rootInfo.annotations.length > 0 && { annotations: [...rootInfo.annotations] },
3832
+ ...diagnostics.length > 0 && { diagnostics: [...diagnostics] },
3833
+ instanceMethods: [],
3834
+ staticMethods: []
3835
+ };
3836
+ }
3837
+ function containsTypeReferenceInObjectLikeAlias(typeNode) {
3838
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
3839
+ return containsTypeReferenceInObjectLikeAlias(typeNode.type);
3840
+ }
3841
+ if (ts4.isTypeReferenceNode(typeNode)) {
3842
+ return true;
3843
+ }
3844
+ return ts4.isIntersectionTypeNode(typeNode) && typeNode.types.some((member) => containsTypeReferenceInObjectLikeAlias(member));
3845
+ }
3846
+ function collectFallbackAliasMemberPropertyNames(typeNode, checker) {
3847
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
3848
+ return collectFallbackAliasMemberPropertyNames(typeNode.type, checker);
3849
+ }
3850
+ if (ts4.isTypeLiteralNode(typeNode)) {
3851
+ const propertyNames = [];
3852
+ for (const member of typeNode.members) {
3853
+ if (!ts4.isPropertySignature(member)) {
3854
+ continue;
3855
+ }
3856
+ const propertyName = getAnalyzableObjectLikePropertyName(member.name);
3857
+ if (propertyName !== null) {
3858
+ propertyNames.push(propertyName);
3859
+ }
3860
+ }
3861
+ return propertyNames;
3862
+ }
3863
+ if (ts4.isTypeReferenceNode(typeNode)) {
3864
+ return checker.getTypeFromTypeNode(typeNode).getProperties().map((property) => property.getName());
3865
+ }
3866
+ return null;
3867
+ }
3868
+ function findFallbackAliasDuplicatePropertyNames(typeNode, checker) {
3869
+ if (!ts4.isIntersectionTypeNode(typeNode)) {
3870
+ return [];
3871
+ }
3872
+ const seen = /* @__PURE__ */ new Set();
3873
+ const duplicates = /* @__PURE__ */ new Set();
3874
+ for (const member of typeNode.types) {
3875
+ const propertyNames = collectFallbackAliasMemberPropertyNames(member, checker);
3876
+ if (propertyNames === null) {
3877
+ continue;
3878
+ }
3879
+ for (const propertyName of propertyNames) {
3880
+ if (seen.has(propertyName)) {
3881
+ duplicates.add(propertyName);
3882
+ } else {
3883
+ seen.add(propertyName);
3884
+ }
3885
+ }
3886
+ }
3887
+ return [...duplicates].sort();
3888
+ }
3741
3889
  function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
3742
3890
  const analysisFilePath = path.resolve(filePath);
3743
3891
  const classDecl = findClassByName(ctx.sourceFile, typeName);
@@ -3775,6 +3923,51 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
3775
3923
  if (result.ok) {
3776
3924
  return result.analysis;
3777
3925
  }
3926
+ const fallbackEligible = result.kind === "not-object-like" && isResolvableObjectLikeAliasTypeNode(typeAlias.type) && containsTypeReferenceInObjectLikeAlias(typeAlias.type);
3927
+ if (!fallbackEligible) {
3928
+ throw new Error(result.error);
3929
+ }
3930
+ const duplicatePropertyNames = findFallbackAliasDuplicatePropertyNames(
3931
+ typeAlias.type,
3932
+ ctx.checker
3933
+ );
3934
+ if (duplicatePropertyNames.length > 0) {
3935
+ const sourceFile = typeAlias.getSourceFile();
3936
+ const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
3937
+ throw new Error(
3938
+ `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
3939
+ );
3940
+ }
3941
+ const rootInfo = analyzeDeclarationRootInfo(
3942
+ typeAlias,
3943
+ ctx.checker,
3944
+ analysisFilePath,
3945
+ extensionRegistry,
3946
+ metadataPolicy
3947
+ );
3948
+ const diagnostics = [...rootInfo.diagnostics];
3949
+ const typeRegistry = {};
3950
+ const rootType = resolveTypeNode(
3951
+ ctx.checker.getTypeAtLocation(typeAlias),
3952
+ ctx.checker,
3953
+ analysisFilePath,
3954
+ typeRegistry,
3955
+ /* @__PURE__ */ new Set(),
3956
+ typeAlias,
3957
+ createAnalyzerMetadataPolicy(metadataPolicy, discriminatorOptions),
3958
+ extensionRegistry,
3959
+ diagnostics
3960
+ );
3961
+ const fallbackAnalysis = createResolvedObjectAliasAnalysis(
3962
+ typeAlias.name.text,
3963
+ rootType,
3964
+ typeRegistry,
3965
+ rootInfo,
3966
+ diagnostics
3967
+ );
3968
+ if (fallbackAnalysis !== null) {
3969
+ return fallbackAnalysis;
3970
+ }
3778
3971
  throw new Error(result.error);
3779
3972
  }
3780
3973
  throw new Error(