@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.
@@ -921,6 +921,16 @@ function buildSupportingDeclarations(sourceFile, extensionTypeNames) {
921
921
  return true;
922
922
  }).map((statement) => statement.getText(sourceFile));
923
923
  }
924
+ function pushUniqueCompilerDiagnostics(target, additions) {
925
+ for (const diagnostic of additions) {
926
+ if ((diagnostic.code === "UNSUPPORTED_CUSTOM_TYPE_OVERRIDE" || diagnostic.code === "SYNTHETIC_SETUP_FAILURE") && target.some(
927
+ (existing) => existing.code === diagnostic.code && existing.message === diagnostic.message
928
+ )) {
929
+ continue;
930
+ }
931
+ target.push(diagnostic);
932
+ }
933
+ }
924
934
  function renderSyntheticArgumentExpression(valueKind, argumentText) {
925
935
  const trimmed = argumentText.trim();
926
936
  if (trimmed === "") {
@@ -1187,6 +1197,16 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
1187
1197
  if (result.diagnostics.length === 0) {
1188
1198
  return [];
1189
1199
  }
1200
+ const setupDiagnostic = result.diagnostics.find((diagnostic) => diagnostic.kind !== "typescript");
1201
+ if (setupDiagnostic !== void 0) {
1202
+ return [
1203
+ makeDiagnostic(
1204
+ setupDiagnostic.kind === "unsupported-custom-type-override" ? "UNSUPPORTED_CUSTOM_TYPE_OVERRIDE" : "SYNTHETIC_SETUP_FAILURE",
1205
+ setupDiagnostic.message,
1206
+ provenance
1207
+ )
1208
+ ];
1209
+ }
1190
1210
  const expectedLabel = definition.valueKind === null ? "compatible argument" : capabilityLabel(definition.valueKind);
1191
1211
  return [
1192
1212
  makeDiagnostic(
@@ -1352,7 +1372,7 @@ function parseTSDocTags(node, file = "", options) {
1352
1372
  options
1353
1373
  );
1354
1374
  if (compilerDiagnostics.length > 0) {
1355
- diagnostics.push(...compilerDiagnostics);
1375
+ pushUniqueCompilerDiagnostics(diagnostics, compilerDiagnostics);
1356
1376
  continue;
1357
1377
  }
1358
1378
  const constraintNode = (0, import_internal.parseConstraintTagValue)(
@@ -1439,7 +1459,7 @@ function parseTSDocTags(node, file = "", options) {
1439
1459
  options
1440
1460
  );
1441
1461
  if (compilerDiagnostics.length > 0) {
1442
- diagnostics.push(...compilerDiagnostics);
1462
+ pushUniqueCompilerDiagnostics(diagnostics, compilerDiagnostics);
1443
1463
  continue;
1444
1464
  }
1445
1465
  const constraintNode = (0, import_internal.parseConstraintTagValue)(
@@ -1473,7 +1493,7 @@ function parseTSDocTags(node, file = "", options) {
1473
1493
  options
1474
1494
  );
1475
1495
  if (compilerDiagnostics.length > 0) {
1476
- diagnostics.push(...compilerDiagnostics);
1496
+ pushUniqueCompilerDiagnostics(diagnostics, compilerDiagnostics);
1477
1497
  continue;
1478
1498
  }
1479
1499
  const constraintNode = (0, import_internal.parseConstraintTagValue)(
@@ -1680,6 +1700,21 @@ function isObjectType(type) {
1680
1700
  function isIntersectionType(type) {
1681
1701
  return !!(type.flags & ts3.TypeFlags.Intersection);
1682
1702
  }
1703
+ function isResolvableObjectLikeAliasTypeNode(typeNode) {
1704
+ if (ts3.isParenthesizedTypeNode(typeNode)) {
1705
+ return isResolvableObjectLikeAliasTypeNode(typeNode.type);
1706
+ }
1707
+ if (ts3.isTypeLiteralNode(typeNode) || ts3.isTypeReferenceNode(typeNode)) {
1708
+ return true;
1709
+ }
1710
+ return ts3.isIntersectionTypeNode(typeNode) && typeNode.types.length > 0 && typeNode.types.every((member) => isResolvableObjectLikeAliasTypeNode(member));
1711
+ }
1712
+ function isSemanticallyPlainObjectLikeType(type, checker) {
1713
+ if (isIntersectionType(type)) {
1714
+ return type.types.length > 0 && type.types.every((member) => isSemanticallyPlainObjectLikeType(member, checker));
1715
+ }
1716
+ 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);
1717
+ }
1683
1718
  function isTypeReference(type) {
1684
1719
  return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
1685
1720
  }
@@ -1743,6 +1778,35 @@ function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node,
1743
1778
  }
1744
1779
  return resolvedMetadata;
1745
1780
  }
1781
+ function analyzeDeclarationRootInfo(declaration, checker, file = "", extensionRegistry, metadataPolicy) {
1782
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
1783
+ const declarationType = checker.getTypeAtLocation(declaration);
1784
+ const logicalName = ts3.isClassDeclaration(declaration) ? declaration.name?.text ?? "AnonymousClass" : declaration.name.text;
1785
+ const docResult = extractJSDocParseResult(
1786
+ declaration,
1787
+ file,
1788
+ makeParseOptions(extensionRegistry, void 0, checker, declarationType, declarationType)
1789
+ );
1790
+ const metadata = resolveNodeMetadata(
1791
+ normalizedMetadataPolicy,
1792
+ "type",
1793
+ logicalName,
1794
+ declaration,
1795
+ checker,
1796
+ extensionRegistry,
1797
+ {
1798
+ checker,
1799
+ declaration,
1800
+ subjectType: declarationType,
1801
+ hostType: declarationType
1802
+ }
1803
+ );
1804
+ return {
1805
+ ...metadata !== void 0 && { metadata },
1806
+ annotations: docResult.annotations,
1807
+ diagnostics: docResult.diagnostics
1808
+ };
1809
+ }
1746
1810
  function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy, discriminatorOptions) {
1747
1811
  const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(
1748
1812
  metadataPolicy,
@@ -1908,6 +1972,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
1908
1972
  const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
1909
1973
  return {
1910
1974
  ok: false,
1975
+ kind: "not-object-like",
1911
1976
  error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object-like type alias (found ${kindDesc})`
1912
1977
  };
1913
1978
  }
@@ -1922,6 +1987,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
1922
1987
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
1923
1988
  return {
1924
1989
  ok: false,
1990
+ kind: "duplicate-properties",
1925
1991
  error: `Type alias "${name}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
1926
1992
  };
1927
1993
  }
@@ -2618,10 +2684,10 @@ function findDuplicateObjectLikeTypeAliasPropertyNames(members) {
2618
2684
  return [...duplicates].sort();
2619
2685
  }
2620
2686
  function getAnalyzableObjectLikePropertyName(name) {
2621
- if (!ts3.isIdentifier(name)) {
2622
- return null;
2687
+ if (ts3.isIdentifier(name) || ts3.isStringLiteral(name) || ts3.isNumericLiteral(name)) {
2688
+ return name.text;
2623
2689
  }
2624
- return name.text;
2690
+ return null;
2625
2691
  }
2626
2692
  function applyEnumMemberDisplayNames(type, annotations) {
2627
2693
  if (!annotations.some(
@@ -2831,6 +2897,19 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
2831
2897
  diagnostics
2832
2898
  );
2833
2899
  }
2900
+ if (resolvedSourceTypeNode !== void 0 && isResolvableObjectLikeAliasTypeNode(resolvedSourceTypeNode) && isSemanticallyPlainObjectLikeType(type, checker)) {
2901
+ return resolveObjectType(
2902
+ type,
2903
+ checker,
2904
+ file,
2905
+ typeRegistry,
2906
+ visiting,
2907
+ sourceNode,
2908
+ metadataPolicy,
2909
+ extensionRegistry,
2910
+ diagnostics
2911
+ );
2912
+ }
2834
2913
  }
2835
2914
  if (isObjectType(type)) {
2836
2915
  return resolveObjectType(
@@ -3738,6 +3817,95 @@ function findInterfaceByName(sourceFile, interfaceName) {
3738
3817
  function findTypeAliasByName(sourceFile, aliasName) {
3739
3818
  return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
3740
3819
  }
3820
+ function getResolvedObjectRootType(rootType, typeRegistry) {
3821
+ if (rootType.kind === "object") {
3822
+ return rootType;
3823
+ }
3824
+ if (rootType.kind !== "reference") {
3825
+ return null;
3826
+ }
3827
+ const definition = typeRegistry[rootType.name];
3828
+ return definition?.type.kind === "object" ? definition.type : null;
3829
+ }
3830
+ function createResolvedObjectAliasAnalysis(name, rootType, typeRegistry, rootInfo, diagnostics) {
3831
+ const resolvedRootType = getResolvedObjectRootType(rootType, typeRegistry);
3832
+ if (resolvedRootType === null) {
3833
+ return null;
3834
+ }
3835
+ const fields = resolvedRootType.properties.map((property) => ({
3836
+ kind: "field",
3837
+ name: property.name,
3838
+ ...property.metadata !== void 0 && { metadata: property.metadata },
3839
+ type: property.type,
3840
+ required: !property.optional,
3841
+ constraints: property.constraints,
3842
+ annotations: property.annotations,
3843
+ provenance: property.provenance
3844
+ }));
3845
+ return {
3846
+ name,
3847
+ ...rootInfo.metadata !== void 0 && { metadata: rootInfo.metadata },
3848
+ fields,
3849
+ fieldLayouts: fields.map(() => ({})),
3850
+ typeRegistry,
3851
+ ...rootInfo.annotations.length > 0 && { annotations: [...rootInfo.annotations] },
3852
+ ...diagnostics.length > 0 && { diagnostics: [...diagnostics] },
3853
+ instanceMethods: [],
3854
+ staticMethods: []
3855
+ };
3856
+ }
3857
+ function containsTypeReferenceInObjectLikeAlias(typeNode) {
3858
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
3859
+ return containsTypeReferenceInObjectLikeAlias(typeNode.type);
3860
+ }
3861
+ if (ts4.isTypeReferenceNode(typeNode)) {
3862
+ return true;
3863
+ }
3864
+ return ts4.isIntersectionTypeNode(typeNode) && typeNode.types.some((member) => containsTypeReferenceInObjectLikeAlias(member));
3865
+ }
3866
+ function collectFallbackAliasMemberPropertyNames(typeNode, checker) {
3867
+ if (ts4.isParenthesizedTypeNode(typeNode)) {
3868
+ return collectFallbackAliasMemberPropertyNames(typeNode.type, checker);
3869
+ }
3870
+ if (ts4.isTypeLiteralNode(typeNode)) {
3871
+ const propertyNames = [];
3872
+ for (const member of typeNode.members) {
3873
+ if (!ts4.isPropertySignature(member)) {
3874
+ continue;
3875
+ }
3876
+ const propertyName = getAnalyzableObjectLikePropertyName(member.name);
3877
+ if (propertyName !== null) {
3878
+ propertyNames.push(propertyName);
3879
+ }
3880
+ }
3881
+ return propertyNames;
3882
+ }
3883
+ if (ts4.isTypeReferenceNode(typeNode)) {
3884
+ return checker.getTypeFromTypeNode(typeNode).getProperties().map((property) => property.getName());
3885
+ }
3886
+ return null;
3887
+ }
3888
+ function findFallbackAliasDuplicatePropertyNames(typeNode, checker) {
3889
+ if (!ts4.isIntersectionTypeNode(typeNode)) {
3890
+ return [];
3891
+ }
3892
+ const seen = /* @__PURE__ */ new Set();
3893
+ const duplicates = /* @__PURE__ */ new Set();
3894
+ for (const member of typeNode.types) {
3895
+ const propertyNames = collectFallbackAliasMemberPropertyNames(member, checker);
3896
+ if (propertyNames === null) {
3897
+ continue;
3898
+ }
3899
+ for (const propertyName of propertyNames) {
3900
+ if (seen.has(propertyName)) {
3901
+ duplicates.add(propertyName);
3902
+ } else {
3903
+ seen.add(propertyName);
3904
+ }
3905
+ }
3906
+ }
3907
+ return [...duplicates].sort();
3908
+ }
3741
3909
  function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
3742
3910
  const analysisFilePath = path.resolve(filePath);
3743
3911
  const classDecl = findClassByName(ctx.sourceFile, typeName);
@@ -3775,6 +3943,51 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
3775
3943
  if (result.ok) {
3776
3944
  return result.analysis;
3777
3945
  }
3946
+ const fallbackEligible = result.kind === "not-object-like" && isResolvableObjectLikeAliasTypeNode(typeAlias.type) && containsTypeReferenceInObjectLikeAlias(typeAlias.type);
3947
+ if (!fallbackEligible) {
3948
+ throw new Error(result.error);
3949
+ }
3950
+ const duplicatePropertyNames = findFallbackAliasDuplicatePropertyNames(
3951
+ typeAlias.type,
3952
+ ctx.checker
3953
+ );
3954
+ if (duplicatePropertyNames.length > 0) {
3955
+ const sourceFile = typeAlias.getSourceFile();
3956
+ const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
3957
+ throw new Error(
3958
+ `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
3959
+ );
3960
+ }
3961
+ const rootInfo = analyzeDeclarationRootInfo(
3962
+ typeAlias,
3963
+ ctx.checker,
3964
+ analysisFilePath,
3965
+ extensionRegistry,
3966
+ metadataPolicy
3967
+ );
3968
+ const diagnostics = [...rootInfo.diagnostics];
3969
+ const typeRegistry = {};
3970
+ const rootType = resolveTypeNode(
3971
+ ctx.checker.getTypeAtLocation(typeAlias),
3972
+ ctx.checker,
3973
+ analysisFilePath,
3974
+ typeRegistry,
3975
+ /* @__PURE__ */ new Set(),
3976
+ typeAlias,
3977
+ createAnalyzerMetadataPolicy(metadataPolicy, discriminatorOptions),
3978
+ extensionRegistry,
3979
+ diagnostics
3980
+ );
3981
+ const fallbackAnalysis = createResolvedObjectAliasAnalysis(
3982
+ typeAlias.name.text,
3983
+ rootType,
3984
+ typeRegistry,
3985
+ rootInfo,
3986
+ diagnostics
3987
+ );
3988
+ if (fallbackAnalysis !== null) {
3989
+ return fallbackAnalysis;
3990
+ }
3778
3991
  throw new Error(result.error);
3779
3992
  }
3780
3993
  throw new Error(