@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/internals.js CHANGED
@@ -897,6 +897,16 @@ function buildSupportingDeclarations(sourceFile, extensionTypeNames) {
897
897
  return true;
898
898
  }).map((statement) => statement.getText(sourceFile));
899
899
  }
900
+ function pushUniqueCompilerDiagnostics(target, additions) {
901
+ for (const diagnostic of additions) {
902
+ if ((diagnostic.code === "UNSUPPORTED_CUSTOM_TYPE_OVERRIDE" || diagnostic.code === "SYNTHETIC_SETUP_FAILURE") && target.some(
903
+ (existing) => existing.code === diagnostic.code && existing.message === diagnostic.message
904
+ )) {
905
+ continue;
906
+ }
907
+ target.push(diagnostic);
908
+ }
909
+ }
900
910
  function renderSyntheticArgumentExpression(valueKind, argumentText) {
901
911
  const trimmed = argumentText.trim();
902
912
  if (trimmed === "") {
@@ -1163,6 +1173,16 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
1163
1173
  if (result.diagnostics.length === 0) {
1164
1174
  return [];
1165
1175
  }
1176
+ const setupDiagnostic = result.diagnostics.find((diagnostic) => diagnostic.kind !== "typescript");
1177
+ if (setupDiagnostic !== void 0) {
1178
+ return [
1179
+ makeDiagnostic(
1180
+ setupDiagnostic.kind === "unsupported-custom-type-override" ? "UNSUPPORTED_CUSTOM_TYPE_OVERRIDE" : "SYNTHETIC_SETUP_FAILURE",
1181
+ setupDiagnostic.message,
1182
+ provenance
1183
+ )
1184
+ ];
1185
+ }
1166
1186
  const expectedLabel = definition.valueKind === null ? "compatible argument" : capabilityLabel(definition.valueKind);
1167
1187
  return [
1168
1188
  makeDiagnostic(
@@ -1328,7 +1348,7 @@ function parseTSDocTags(node, file = "", options) {
1328
1348
  options
1329
1349
  );
1330
1350
  if (compilerDiagnostics.length > 0) {
1331
- diagnostics.push(...compilerDiagnostics);
1351
+ pushUniqueCompilerDiagnostics(diagnostics, compilerDiagnostics);
1332
1352
  continue;
1333
1353
  }
1334
1354
  const constraintNode = parseConstraintTagValue(
@@ -1415,7 +1435,7 @@ function parseTSDocTags(node, file = "", options) {
1415
1435
  options
1416
1436
  );
1417
1437
  if (compilerDiagnostics.length > 0) {
1418
- diagnostics.push(...compilerDiagnostics);
1438
+ pushUniqueCompilerDiagnostics(diagnostics, compilerDiagnostics);
1419
1439
  continue;
1420
1440
  }
1421
1441
  const constraintNode = parseConstraintTagValue(
@@ -1449,7 +1469,7 @@ function parseTSDocTags(node, file = "", options) {
1449
1469
  options
1450
1470
  );
1451
1471
  if (compilerDiagnostics.length > 0) {
1452
- diagnostics.push(...compilerDiagnostics);
1472
+ pushUniqueCompilerDiagnostics(diagnostics, compilerDiagnostics);
1453
1473
  continue;
1454
1474
  }
1455
1475
  const constraintNode = parseConstraintTagValue(
@@ -1656,6 +1676,21 @@ function isObjectType(type) {
1656
1676
  function isIntersectionType(type) {
1657
1677
  return !!(type.flags & ts3.TypeFlags.Intersection);
1658
1678
  }
1679
+ function isResolvableObjectLikeAliasTypeNode(typeNode) {
1680
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
1681
+ return isResolvableObjectLikeAliasTypeNode(typeNode.type);
1682
+ }
1683
+ if (ts3.isTypeLiteralNode(typeNode) || ts3.isTypeReferenceNode(typeNode)) {
1684
+ return true;
1685
+ }
1686
+ return ts3.isIntersectionTypeNode(typeNode) && typeNode.types.length > 0 && typeNode.types.every((member) => isResolvableObjectLikeAliasTypeNode(member));
1687
+ }
1688
+ function isSemanticallyPlainObjectLikeType(type, checker) {
1689
+ if (isIntersectionType(type)) {
1690
+ return type.types.length > 0 && type.types.every((member) => isSemanticallyPlainObjectLikeType(member, checker));
1691
+ }
1692
+ 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);
1693
+ }
1659
1694
  function isTypeReference(type) {
1660
1695
  return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
1661
1696
  }
@@ -1719,6 +1754,35 @@ function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node,
1719
1754
  }
1720
1755
  return resolvedMetadata;
1721
1756
  }
1757
+ function analyzeDeclarationRootInfo(declaration, checker, file = "", extensionRegistry, metadataPolicy) {
1758
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
1759
+ const declarationType = checker.getTypeAtLocation(declaration);
1760
+ const logicalName = ts3.isClassDeclaration(declaration) ? declaration.name?.text ?? "AnonymousClass" : declaration.name.text;
1761
+ const docResult = extractJSDocParseResult(
1762
+ declaration,
1763
+ file,
1764
+ makeParseOptions(extensionRegistry, void 0, checker, declarationType, declarationType)
1765
+ );
1766
+ const metadata = resolveNodeMetadata(
1767
+ normalizedMetadataPolicy,
1768
+ "type",
1769
+ logicalName,
1770
+ declaration,
1771
+ checker,
1772
+ extensionRegistry,
1773
+ {
1774
+ checker,
1775
+ declaration,
1776
+ subjectType: declarationType,
1777
+ hostType: declarationType
1778
+ }
1779
+ );
1780
+ return {
1781
+ ...metadata !== void 0 && { metadata },
1782
+ annotations: docResult.annotations,
1783
+ diagnostics: docResult.diagnostics
1784
+ };
1785
+ }
1722
1786
  function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
1723
1787
  const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
1724
1788
  metadataPolicy,
@@ -1884,6 +1948,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
1884
1948
  const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
1885
1949
  return {
1886
1950
  ok: false,
1951
+ kind: "not-object-like",
1887
1952
  error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object-like type alias (found ${kindDesc})`
1888
1953
  };
1889
1954
  }
@@ -1898,6 +1963,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
1898
1963
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
1899
1964
  return {
1900
1965
  ok: false,
1966
+ kind: "duplicate-properties",
1901
1967
  error: `Type alias "${name}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
1902
1968
  };
1903
1969
  }
@@ -2594,10 +2660,10 @@ function findDuplicateObjectLikeTypeAliasPropertyNames(members) {
2594
2660
  return [...duplicates].sort();
2595
2661
  }
2596
2662
  function getAnalyzableObjectLikePropertyName(name) {
2597
- if (!ts3.isIdentifier(name)) {
2598
- return null;
2663
+ if (ts3.isIdentifier(name) || ts3.isStringLiteral(name) || ts3.isNumericLiteral(name)) {
2664
+ return name.text;
2599
2665
  }
2600
- return name.text;
2666
+ return null;
2601
2667
  }
2602
2668
  function applyEnumMemberDisplayNames(type, annotations) {
2603
2669
  if (!annotations.some(
@@ -2807,6 +2873,19 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
2807
2873
  diagnostics
2808
2874
  );
2809
2875
  }
2876
+ if (resolvedSourceTypeNode !== void 0 && isResolvableObjectLikeAliasTypeNode(resolvedSourceTypeNode) && isSemanticallyPlainObjectLikeType(type, checker)) {
2877
+ return resolveObjectType(
2878
+ type,
2879
+ checker,
2880
+ file,
2881
+ typeRegistry,
2882
+ visiting,
2883
+ sourceNode,
2884
+ metadataPolicy,
2885
+ extensionRegistry,
2886
+ diagnostics
2887
+ );
2888
+ }
2810
2889
  }
2811
2890
  if (isObjectType(type)) {
2812
2891
  return resolveObjectType(
@@ -3714,6 +3793,95 @@ function findInterfaceByName(sourceFile, interfaceName) {
3714
3793
  function findTypeAliasByName(sourceFile, aliasName) {
3715
3794
  return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
3716
3795
  }
3796
+ function getResolvedObjectRootType(rootType, typeRegistry) {
3797
+ if (rootType.kind === "object") {
3798
+ return rootType;
3799
+ }
3800
+ if (rootType.kind !== "reference") {
3801
+ return null;
3802
+ }
3803
+ const definition = typeRegistry[rootType.name];
3804
+ return definition?.type.kind === "object" ? definition.type : null;
3805
+ }
3806
+ function createResolvedObjectAliasAnalysis(name, rootType, typeRegistry, rootInfo, diagnostics) {
3807
+ const resolvedRootType = getResolvedObjectRootType(rootType, typeRegistry);
3808
+ if (resolvedRootType === null) {
3809
+ return null;
3810
+ }
3811
+ const fields = resolvedRootType.properties.map((property) => ({
3812
+ kind: "field",
3813
+ name: property.name,
3814
+ ...property.metadata !== void 0 && { metadata: property.metadata },
3815
+ type: property.type,
3816
+ required: !property.optional,
3817
+ constraints: property.constraints,
3818
+ annotations: property.annotations,
3819
+ provenance: property.provenance
3820
+ }));
3821
+ return {
3822
+ name,
3823
+ ...rootInfo.metadata !== void 0 && { metadata: rootInfo.metadata },
3824
+ fields,
3825
+ fieldLayouts: fields.map(() => ({})),
3826
+ typeRegistry,
3827
+ ...rootInfo.annotations.length > 0 && { annotations: [...rootInfo.annotations] },
3828
+ ...diagnostics.length > 0 && { diagnostics: [...diagnostics] },
3829
+ instanceMethods: [],
3830
+ staticMethods: []
3831
+ };
3832
+ }
3833
+ function containsTypeReferenceInObjectLikeAlias(typeNode) {
3834
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
3835
+ return containsTypeReferenceInObjectLikeAlias(typeNode.type);
3836
+ }
3837
+ if (ts4.isTypeReferenceNode(typeNode)) {
3838
+ return true;
3839
+ }
3840
+ return ts4.isIntersectionTypeNode(typeNode) && typeNode.types.some((member) => containsTypeReferenceInObjectLikeAlias(member));
3841
+ }
3842
+ function collectFallbackAliasMemberPropertyNames(typeNode, checker) {
3843
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
3844
+ return collectFallbackAliasMemberPropertyNames(typeNode.type, checker);
3845
+ }
3846
+ if (ts4.isTypeLiteralNode(typeNode)) {
3847
+ const propertyNames = [];
3848
+ for (const member of typeNode.members) {
3849
+ if (!ts4.isPropertySignature(member)) {
3850
+ continue;
3851
+ }
3852
+ const propertyName = getAnalyzableObjectLikePropertyName(member.name);
3853
+ if (propertyName !== null) {
3854
+ propertyNames.push(propertyName);
3855
+ }
3856
+ }
3857
+ return propertyNames;
3858
+ }
3859
+ if (ts4.isTypeReferenceNode(typeNode)) {
3860
+ return checker.getTypeFromTypeNode(typeNode).getProperties().map((property) => property.getName());
3861
+ }
3862
+ return null;
3863
+ }
3864
+ function findFallbackAliasDuplicatePropertyNames(typeNode, checker) {
3865
+ if (!ts4.isIntersectionTypeNode(typeNode)) {
3866
+ return [];
3867
+ }
3868
+ const seen = /* @__PURE__ */ new Set();
3869
+ const duplicates = /* @__PURE__ */ new Set();
3870
+ for (const member of typeNode.types) {
3871
+ const propertyNames = collectFallbackAliasMemberPropertyNames(member, checker);
3872
+ if (propertyNames === null) {
3873
+ continue;
3874
+ }
3875
+ for (const propertyName of propertyNames) {
3876
+ if (seen.has(propertyName)) {
3877
+ duplicates.add(propertyName);
3878
+ } else {
3879
+ seen.add(propertyName);
3880
+ }
3881
+ }
3882
+ }
3883
+ return [...duplicates].sort();
3884
+ }
3717
3885
  function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
3718
3886
  const analysisFilePath = path.resolve(filePath);
3719
3887
  const classDecl = findClassByName(ctx.sourceFile, typeName);
@@ -3751,6 +3919,51 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
3751
3919
  if (result.ok) {
3752
3920
  return result.analysis;
3753
3921
  }
3922
+ const fallbackEligible = result.kind === "not-object-like" && isResolvableObjectLikeAliasTypeNode(typeAlias.type) && containsTypeReferenceInObjectLikeAlias(typeAlias.type);
3923
+ if (!fallbackEligible) {
3924
+ throw new Error(result.error);
3925
+ }
3926
+ const duplicatePropertyNames = findFallbackAliasDuplicatePropertyNames(
3927
+ typeAlias.type,
3928
+ ctx.checker
3929
+ );
3930
+ if (duplicatePropertyNames.length > 0) {
3931
+ const sourceFile = typeAlias.getSourceFile();
3932
+ const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
3933
+ throw new Error(
3934
+ `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
3935
+ );
3936
+ }
3937
+ const rootInfo = analyzeDeclarationRootInfo(
3938
+ typeAlias,
3939
+ ctx.checker,
3940
+ analysisFilePath,
3941
+ extensionRegistry,
3942
+ metadataPolicy
3943
+ );
3944
+ const diagnostics = [...rootInfo.diagnostics];
3945
+ const typeRegistry = {};
3946
+ const rootType = resolveTypeNode(
3947
+ ctx.checker.getTypeAtLocation(typeAlias),
3948
+ ctx.checker,
3949
+ analysisFilePath,
3950
+ typeRegistry,
3951
+ /* @__PURE__ */ new Set(),
3952
+ typeAlias,
3953
+ createAnalyzerMetadataPolicy(metadataPolicy, discriminatorOptions),
3954
+ extensionRegistry,
3955
+ diagnostics
3956
+ );
3957
+ const fallbackAnalysis = createResolvedObjectAliasAnalysis(
3958
+ typeAlias.name.text,
3959
+ rootType,
3960
+ typeRegistry,
3961
+ rootInfo,
3962
+ diagnostics
3963
+ );
3964
+ if (fallbackAnalysis !== null) {
3965
+ return fallbackAnalysis;
3966
+ }
3754
3967
  throw new Error(result.error);
3755
3968
  }
3756
3969
  throw new Error(