@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.
@@ -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(
@@ -2712,7 +2758,7 @@ function resolveRegisteredCustomTypeFromTypeNode(typeNode, extensionRegistry, ch
2712
2758
  };
2713
2759
  }
2714
2760
  if (ts3.isTypeReferenceNode(typeNode) && ts3.isIdentifier(typeNode.typeName)) {
2715
- const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
2761
+ const aliasDecl = getTypeAliasDeclarationFromTypeReference(typeNode, checker);
2716
2762
  if (aliasDecl !== void 0) {
2717
2763
  return resolveRegisteredCustomTypeFromTypeNode(aliasDecl.type, extensionRegistry, checker);
2718
2764
  }
@@ -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(
@@ -2905,13 +2964,13 @@ function getReferencedTypeAliasDeclaration(sourceNode, checker) {
2905
2964
  if (!typeNode || !ts3.isTypeReferenceNode(typeNode)) {
2906
2965
  return void 0;
2907
2966
  }
2908
- return checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
2967
+ return getTypeAliasDeclarationFromTypeReference(typeNode, checker);
2909
2968
  }
2910
2969
  function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
2911
2970
  if (!ts3.isTypeReferenceNode(typeNode)) {
2912
2971
  return false;
2913
2972
  }
2914
- const aliasDecl = checker.getSymbolAtLocation(typeNode.typeName)?.declarations?.find(ts3.isTypeAliasDeclaration);
2973
+ const aliasDecl = getTypeAliasDeclarationFromTypeReference(typeNode, checker);
2915
2974
  if (!aliasDecl) {
2916
2975
  return false;
2917
2976
  }
@@ -3480,8 +3539,7 @@ function resolveAliasedTypeNode(typeNode, checker, visited = /* @__PURE__ */ new
3480
3539
  if (!ts3.isTypeReferenceNode(typeNode) || !ts3.isIdentifier(typeNode.typeName)) {
3481
3540
  return typeNode;
3482
3541
  }
3483
- const symbol = checker.getSymbolAtLocation(typeNode.typeName);
3484
- const aliasDecl = symbol?.declarations?.find(ts3.isTypeAliasDeclaration);
3542
+ const aliasDecl = getTypeAliasDeclarationFromTypeReference(typeNode, checker);
3485
3543
  if (aliasDecl === void 0 || visited.has(aliasDecl)) {
3486
3544
  return typeNode;
3487
3545
  }
@@ -3531,8 +3589,7 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
3531
3589
  );
3532
3590
  }
3533
3591
  const symbol = checker.getSymbolAtLocation(typeNode.typeName);
3534
- if (!symbol?.declarations) return [];
3535
- const aliasDecl = symbol.declarations.find(ts3.isTypeAliasDeclaration);
3592
+ const aliasDecl = getAliasedTypeAliasDeclaration(symbol, checker);
3536
3593
  if (!aliasDecl) return [];
3537
3594
  if (ts3.isTypeLiteralNode(aliasDecl.type)) return [];
3538
3595
  const aliasFieldType = resolveTypeNode(
@@ -3555,6 +3612,18 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
3555
3612
  );
3556
3613
  return constraints;
3557
3614
  }
3615
+ function getAliasedSymbol(symbol, checker) {
3616
+ if (symbol === void 0) {
3617
+ return void 0;
3618
+ }
3619
+ return symbol.flags & ts3.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
3620
+ }
3621
+ function getAliasedTypeAliasDeclaration(symbol, checker) {
3622
+ return getAliasedSymbol(symbol, checker)?.declarations?.find(ts3.isTypeAliasDeclaration);
3623
+ }
3624
+ function getTypeAliasDeclarationFromTypeReference(typeNode, checker) {
3625
+ return getAliasedTypeAliasDeclaration(checker.getSymbolAtLocation(typeNode.typeName), checker);
3626
+ }
3558
3627
  function provenanceForNode(node, file) {
3559
3628
  const sourceFile = node.getSourceFile();
3560
3629
  const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
@@ -3728,6 +3797,95 @@ function findInterfaceByName(sourceFile, interfaceName) {
3728
3797
  function findTypeAliasByName(sourceFile, aliasName) {
3729
3798
  return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
3730
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
+ }
3731
3889
  function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
3732
3890
  const analysisFilePath = path.resolve(filePath);
3733
3891
  const classDecl = findClassByName(ctx.sourceFile, typeName);
@@ -3765,6 +3923,51 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
3765
3923
  if (result.ok) {
3766
3924
  return result.analysis;
3767
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
+ }
3768
3971
  throw new Error(result.error);
3769
3972
  }
3770
3973
  throw new Error(