@formspec/build 0.1.0-alpha.31 → 0.1.0-alpha.33

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
@@ -730,6 +730,7 @@ import * as path from "path";
730
730
  // src/analyzer/class-analyzer.ts
731
731
  import * as ts3 from "typescript";
732
732
  import {
733
+ analyzeMetadataForNodeWithChecker,
733
734
  parseCommentBlock as parseCommentBlock2
734
735
  } from "@formspec/analysis/internal";
735
736
 
@@ -743,6 +744,7 @@ import {
743
744
  extractPathTarget as extractSharedPathTarget,
744
745
  getTagDefinition,
745
746
  hasTypeSemanticCapability,
747
+ normalizeFormSpecTagName,
746
748
  parseConstraintTagValue,
747
749
  parseDefaultValueTagValue,
748
750
  resolveDeclarationPlacement,
@@ -779,7 +781,7 @@ function createFormSpecTSDocConfig(extensionTagNames = []) {
779
781
  })
780
782
  );
781
783
  }
782
- for (const tagName of ["displayName", "format", "placeholder"]) {
784
+ for (const tagName of ["apiName", "displayName", "format", "placeholder"]) {
783
785
  config.addTagDefinition(
784
786
  new TSDocTagDefinition({
785
787
  tagName: "@" + tagName,
@@ -813,6 +815,16 @@ function sharedTagValueOptions(options) {
813
815
  };
814
816
  }
815
817
  var SYNTHETIC_TYPE_FORMAT_FLAGS = ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope;
818
+ function getExtensionTypeNames(registry) {
819
+ if (registry === void 0) {
820
+ return /* @__PURE__ */ new Set();
821
+ }
822
+ return new Set(
823
+ registry.extensions.flatMap(
824
+ (ext) => (ext.types ?? []).flatMap((t) => t.tsTypeNames ?? [t.typeName])
825
+ )
826
+ );
827
+ }
816
828
  function collectImportedNames(sourceFile) {
817
829
  const importedNames = /* @__PURE__ */ new Set();
818
830
  for (const statement of sourceFile.statements) {
@@ -852,6 +864,9 @@ function isNonReferenceIdentifier(node) {
852
864
  return false;
853
865
  }
854
866
  function statementReferencesImportedName(statement, importedNames) {
867
+ if (importedNames.size === 0) {
868
+ return false;
869
+ }
855
870
  let referencesImportedName = false;
856
871
  const visit = (node) => {
857
872
  if (referencesImportedName) {
@@ -866,14 +881,17 @@ function statementReferencesImportedName(statement, importedNames) {
866
881
  visit(statement);
867
882
  return referencesImportedName;
868
883
  }
869
- function buildSupportingDeclarations(sourceFile) {
884
+ function buildSupportingDeclarations(sourceFile, extensionTypeNames) {
870
885
  const importedNames = collectImportedNames(sourceFile);
886
+ const importedNamesToSkip = new Set(
887
+ [...importedNames].filter((name) => !extensionTypeNames.has(name))
888
+ );
871
889
  return sourceFile.statements.filter((statement) => {
872
890
  if (ts.isImportDeclaration(statement)) return false;
873
891
  if (ts.isImportEqualsDeclaration(statement)) return false;
874
892
  if (ts.isExportDeclaration(statement) && statement.moduleSpecifier !== void 0)
875
893
  return false;
876
- if (importedNames.size > 0 && statementReferencesImportedName(statement, importedNames)) {
894
+ if (statementReferencesImportedName(statement, importedNamesToSkip)) {
877
895
  return false;
878
896
  }
879
897
  return true;
@@ -1130,6 +1148,14 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
1130
1148
  extensionId: extension.extensionId,
1131
1149
  ...extension.constraintTags !== void 0 ? {
1132
1150
  constraintTags: extension.constraintTags.map((tag) => ({ tagName: tag.tagName }))
1151
+ } : {},
1152
+ ...extension.metadataSlots !== void 0 ? {
1153
+ metadataSlots: extension.metadataSlots
1154
+ } : {},
1155
+ ...extension.types !== void 0 ? {
1156
+ customTypes: extension.types.map((t) => ({
1157
+ tsTypeNames: t.tsTypeNames ?? [t.typeName]
1158
+ }))
1133
1159
  } : {}
1134
1160
  }))
1135
1161
  } : {}
@@ -1151,7 +1177,10 @@ var parseResultCache = /* @__PURE__ */ new Map();
1151
1177
  function getParser(options) {
1152
1178
  const extensionTagNames = [
1153
1179
  ...options?.extensionRegistry?.extensions.flatMap(
1154
- (extension) => (extension.constraintTags ?? []).map((tag) => tag.tagName)
1180
+ (extension) => (extension.constraintTags ?? []).map((tag) => normalizeFormSpecTagName(tag.tagName))
1181
+ ) ?? [],
1182
+ ...options?.extensionRegistry?.extensions.flatMap(
1183
+ (extension) => (extension.metadataSlots ?? []).map((slot) => normalizeFormSpecTagName(slot.tagName))
1155
1184
  ) ?? []
1156
1185
  ].sort();
1157
1186
  const cacheKey = extensionTagNames.join("|");
@@ -1171,7 +1200,16 @@ function getExtensionRegistryCacheKey(registry) {
1171
1200
  (extension) => JSON.stringify({
1172
1201
  extensionId: extension.extensionId,
1173
1202
  typeNames: extension.types?.map((type) => type.typeName) ?? [],
1174
- constraintTags: extension.constraintTags?.map((tag) => tag.tagName) ?? []
1203
+ constraintTags: extension.constraintTags?.map((tag) => normalizeFormSpecTagName(tag.tagName)) ?? [],
1204
+ metadataSlots: extension.metadataSlots?.map((slot) => ({
1205
+ tagName: normalizeFormSpecTagName(slot.tagName),
1206
+ declarationKinds: [...slot.declarationKinds].sort(),
1207
+ allowBare: slot.allowBare !== false,
1208
+ qualifiers: (slot.qualifiers ?? []).map((qualifier) => ({
1209
+ qualifier: qualifier.qualifier,
1210
+ ...qualifier.sourceQualifier !== void 0 ? { sourceQualifier: qualifier.sourceQualifier } : {}
1211
+ })).sort((left, right) => left.qualifier.localeCompare(right.qualifier))
1212
+ })) ?? []
1175
1213
  })
1176
1214
  ).join("|");
1177
1215
  }
@@ -1206,7 +1244,8 @@ function parseTSDocTags(node, file = "", options) {
1206
1244
  const rawTextTags = [];
1207
1245
  const sourceFile = node.getSourceFile();
1208
1246
  const sourceText = sourceFile.getFullText();
1209
- const supportingDeclarations = buildSupportingDeclarations(sourceFile);
1247
+ const extensionTypeNames = getExtensionTypeNames(options?.extensionRegistry);
1248
+ const supportingDeclarations = buildSupportingDeclarations(sourceFile, extensionTypeNames);
1210
1249
  const commentRanges = ts.getLeadingCommentRanges(sourceText, node.getFullStart());
1211
1250
  const rawTextFallbacks = collectRawTextFallbacks(node, file);
1212
1251
  if (commentRanges) {
@@ -1634,76 +1673,50 @@ function makeParseOptions(extensionRegistry, fieldType, checker, subjectType, ho
1634
1673
  ...hostType !== void 0 && { hostType }
1635
1674
  };
1636
1675
  }
1637
- function makeExplicitScalarMetadata(value) {
1638
- return value === void 0 || value === "" ? void 0 : { value, source: "explicit" };
1639
- }
1640
- function extractExplicitMetadata(node) {
1641
- let apiName;
1642
- let displayName;
1643
- let apiNamePlural;
1644
- let displayNamePlural;
1645
- for (const tag of getLeadingParsedTags(node)) {
1646
- const value = tag.argumentText.trim();
1647
- if (value === "") {
1648
- continue;
1649
- }
1650
- if (tag.normalizedTagName === "apiName") {
1651
- if (tag.target === null) {
1652
- apiName ??= value;
1653
- } else if (tag.target.kind === "variant") {
1654
- if (tag.target.rawText === "singular") {
1655
- apiName ??= value;
1656
- } else if (tag.target.rawText === "plural") {
1657
- apiNamePlural ??= value;
1658
- }
1659
- }
1660
- continue;
1661
- }
1662
- if (tag.normalizedTagName === "displayName") {
1663
- if (tag.target === null) {
1664
- displayName ??= value;
1665
- } else if (tag.target.kind === "variant") {
1666
- if (tag.target.rawText === "singular") {
1667
- displayName ??= value;
1668
- } else if (tag.target.rawText === "plural") {
1669
- displayNamePlural ??= value;
1670
- }
1671
- }
1672
- }
1673
- }
1674
- const resolvedApiName = makeExplicitScalarMetadata(apiName);
1675
- const resolvedDisplayName = makeExplicitScalarMetadata(displayName);
1676
- const resolvedApiNamePlural = makeExplicitScalarMetadata(apiNamePlural);
1677
- const resolvedDisplayNamePlural = makeExplicitScalarMetadata(displayNamePlural);
1678
- const metadata = {
1679
- ...resolvedApiName !== void 0 && { apiName: resolvedApiName },
1680
- ...resolvedDisplayName !== void 0 && { displayName: resolvedDisplayName },
1681
- ...resolvedApiNamePlural !== void 0 && { apiNamePlural: resolvedApiNamePlural },
1682
- ...resolvedDisplayNamePlural !== void 0 && {
1683
- displayNamePlural: resolvedDisplayNamePlural
1684
- }
1676
+ function createAnalyzerMetadataPolicy(input) {
1677
+ return {
1678
+ raw: input,
1679
+ normalized: normalizeMetadataPolicy(input)
1685
1680
  };
1686
- return Object.keys(metadata).length === 0 ? void 0 : metadata;
1687
1681
  }
1688
- function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, buildContext) {
1689
- const explicit = extractExplicitMetadata(node);
1690
- return resolveMetadata(
1691
- {
1692
- ...explicit?.apiName !== void 0 && { apiName: explicit.apiName.value },
1693
- ...explicit?.displayName !== void 0 && { displayName: explicit.displayName.value },
1694
- ...explicit?.apiNamePlural !== void 0 && {
1695
- apiNamePlural: explicit.apiNamePlural.value
1696
- },
1697
- ...explicit?.displayNamePlural !== void 0 && {
1698
- displayNamePlural: explicit.displayNamePlural.value
1699
- }
1700
- },
1701
- getDeclarationMetadataPolicy(metadataPolicy, declarationKind),
1702
- makeMetadataContext("tsdoc", declarationKind, logicalName, buildContext)
1682
+ function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, checker, extensionRegistry, buildContext) {
1683
+ const analysis = analyzeMetadataForNodeWithChecker({
1684
+ checker,
1685
+ node,
1686
+ logicalName,
1687
+ metadata: metadataPolicy.raw,
1688
+ extensions: extensionRegistry?.extensions,
1689
+ ...buildContext !== void 0 && { buildContext }
1690
+ });
1691
+ const resolvedMetadata = analysis?.resolvedMetadata;
1692
+ const declarationPolicy = getDeclarationMetadataPolicy(
1693
+ metadataPolicy.normalized,
1694
+ declarationKind
1703
1695
  );
1696
+ if (resolvedMetadata?.apiName === void 0 && declarationPolicy.apiName.mode === "require-explicit") {
1697
+ throw new Error(
1698
+ `Metadata policy requires explicit apiName for ${declarationKind} "${logicalName}" on the tsdoc surface.`
1699
+ );
1700
+ }
1701
+ if (resolvedMetadata?.displayName === void 0 && declarationPolicy.displayName.mode === "require-explicit") {
1702
+ throw new Error(
1703
+ `Metadata policy requires explicit displayName for ${declarationKind} "${logicalName}" on the tsdoc surface.`
1704
+ );
1705
+ }
1706
+ if (resolvedMetadata?.apiNamePlural === void 0 && declarationPolicy.apiName.pluralization.mode === "require-explicit") {
1707
+ throw new Error(
1708
+ `Metadata policy requires explicit apiNamePlural for ${declarationKind} "${logicalName}" on the tsdoc surface.`
1709
+ );
1710
+ }
1711
+ if (resolvedMetadata?.displayNamePlural === void 0 && declarationPolicy.displayName.pluralization.mode === "require-explicit") {
1712
+ throw new Error(
1713
+ `Metadata policy requires explicit displayNamePlural for ${declarationKind} "${logicalName}" on the tsdoc surface.`
1714
+ );
1715
+ }
1716
+ return resolvedMetadata;
1704
1717
  }
1705
1718
  function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy) {
1706
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
1719
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
1707
1720
  const name = classDecl.name?.text ?? "AnonymousClass";
1708
1721
  const fields = [];
1709
1722
  const fieldLayouts = [];
@@ -1758,12 +1771,20 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, meta
1758
1771
  diagnostics,
1759
1772
  normalizedMetadataPolicy
1760
1773
  );
1761
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, classDecl, {
1774
+ const metadata = resolveNodeMetadata(
1775
+ normalizedMetadataPolicy,
1776
+ "type",
1777
+ name,
1778
+ classDecl,
1762
1779
  checker,
1763
- declaration: classDecl,
1764
- subjectType: classType,
1765
- hostType: classType
1766
- });
1780
+ extensionRegistry,
1781
+ {
1782
+ checker,
1783
+ declaration: classDecl,
1784
+ subjectType: classType,
1785
+ hostType: classType
1786
+ }
1787
+ );
1767
1788
  return {
1768
1789
  name,
1769
1790
  ...metadata !== void 0 && { metadata },
@@ -1777,7 +1798,7 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, meta
1777
1798
  };
1778
1799
  }
1779
1800
  function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy) {
1780
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
1801
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
1781
1802
  const name = interfaceDecl.name.text;
1782
1803
  const fields = [];
1783
1804
  const typeRegistry = {};
@@ -1819,12 +1840,20 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
1819
1840
  normalizedMetadataPolicy
1820
1841
  );
1821
1842
  const fieldLayouts = specializedFields.map(() => ({}));
1822
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, interfaceDecl, {
1843
+ const metadata = resolveNodeMetadata(
1844
+ normalizedMetadataPolicy,
1845
+ "type",
1846
+ name,
1847
+ interfaceDecl,
1823
1848
  checker,
1824
- declaration: interfaceDecl,
1825
- subjectType: interfaceType,
1826
- hostType: interfaceType
1827
- });
1849
+ extensionRegistry,
1850
+ {
1851
+ checker,
1852
+ declaration: interfaceDecl,
1853
+ subjectType: interfaceType,
1854
+ hostType: interfaceType
1855
+ }
1856
+ );
1828
1857
  return {
1829
1858
  name,
1830
1859
  ...metadata !== void 0 && { metadata },
@@ -1848,7 +1877,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
1848
1877
  };
1849
1878
  }
1850
1879
  const typeLiteral = typeAlias.type;
1851
- const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
1880
+ const normalizedMetadataPolicy = createAnalyzerMetadataPolicy(metadataPolicy);
1852
1881
  const name = typeAlias.name.text;
1853
1882
  const fields = [];
1854
1883
  const typeRegistry = {};
@@ -1889,12 +1918,20 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
1889
1918
  diagnostics,
1890
1919
  normalizedMetadataPolicy
1891
1920
  );
1892
- const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, typeAlias, {
1921
+ const metadata = resolveNodeMetadata(
1922
+ normalizedMetadataPolicy,
1923
+ "type",
1924
+ name,
1925
+ typeAlias,
1893
1926
  checker,
1894
- declaration: typeAlias,
1895
- subjectType: aliasType,
1896
- hostType: aliasType
1897
- });
1927
+ extensionRegistry,
1928
+ {
1929
+ checker,
1930
+ declaration: typeAlias,
1931
+ subjectType: aliasType,
1932
+ hostType: aliasType
1933
+ }
1934
+ );
1898
1935
  return {
1899
1936
  ok: true,
1900
1937
  analysis: {
@@ -2116,10 +2153,7 @@ function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker,
2116
2153
  if (resolvedAnchorNode === null) {
2117
2154
  return void 0;
2118
2155
  }
2119
- const propertyType = checker.getTypeOfSymbolAtLocation(
2120
- propertySymbol,
2121
- resolvedAnchorNode
2122
- );
2156
+ const propertyType = checker.getTypeOfSymbolAtLocation(propertySymbol, resolvedAnchorNode);
2123
2157
  if (propertyType.isStringLiteral()) {
2124
2158
  return propertyType.value;
2125
2159
  }
@@ -2150,6 +2184,8 @@ function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
2150
2184
  "type",
2151
2185
  getDiscriminatorLogicalName(boundType, declaration, checker),
2152
2186
  declaration,
2187
+ checker,
2188
+ void 0,
2153
2189
  {
2154
2190
  checker,
2155
2191
  declaration,
@@ -2386,12 +2422,20 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
2386
2422
  annotations.push(defaultAnnotation);
2387
2423
  }
2388
2424
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
2389
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
2425
+ const metadata = resolveNodeMetadata(
2426
+ metadataPolicy,
2427
+ "field",
2428
+ name,
2429
+ prop,
2390
2430
  checker,
2391
- declaration: prop,
2392
- subjectType: tsType,
2393
- hostType
2394
- });
2431
+ extensionRegistry,
2432
+ {
2433
+ checker,
2434
+ declaration: prop,
2435
+ subjectType: tsType,
2436
+ hostType
2437
+ }
2438
+ );
2395
2439
  return {
2396
2440
  kind: "field",
2397
2441
  name,
@@ -2438,12 +2482,20 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
2438
2482
  let annotations = [];
2439
2483
  annotations.push(...docResult.annotations);
2440
2484
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
2441
- const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
2485
+ const metadata = resolveNodeMetadata(
2486
+ metadataPolicy,
2487
+ "field",
2488
+ name,
2489
+ prop,
2442
2490
  checker,
2443
- declaration: prop,
2444
- subjectType: tsType,
2445
- hostType
2446
- });
2491
+ extensionRegistry,
2492
+ {
2493
+ checker,
2494
+ declaration: prop,
2495
+ subjectType: tsType,
2496
+ hostType
2497
+ }
2498
+ );
2447
2499
  return {
2448
2500
  kind: "field",
2449
2501
  name,
@@ -2572,7 +2624,7 @@ function getTypeNodeRegistrationName(typeNode) {
2572
2624
  }
2573
2625
  return null;
2574
2626
  }
2575
- function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2627
+ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2576
2628
  const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
2577
2629
  if (customType) {
2578
2630
  return customType;
@@ -2662,7 +2714,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
2662
2714
  }
2663
2715
  return { kind: "primitive", primitiveKind: "string" };
2664
2716
  }
2665
- function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2717
+ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2666
2718
  if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
2667
2719
  return null;
2668
2720
  }
@@ -2682,11 +2734,19 @@ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiti
2682
2734
  file,
2683
2735
  makeParseOptions(extensionRegistry)
2684
2736
  );
2685
- const metadata = resolveNodeMetadata(metadataPolicy, "type", aliasName, aliasDecl, {
2737
+ const metadata = resolveNodeMetadata(
2738
+ metadataPolicy,
2739
+ "type",
2740
+ aliasName,
2741
+ aliasDecl,
2686
2742
  checker,
2687
- declaration: aliasDecl,
2688
- subjectType: aliasType
2689
- });
2743
+ extensionRegistry,
2744
+ {
2745
+ checker,
2746
+ declaration: aliasDecl,
2747
+ subjectType: aliasType
2748
+ }
2749
+ );
2690
2750
  typeRegistry[aliasName] = {
2691
2751
  name: aliasName,
2692
2752
  ...metadata !== void 0 && { metadata },
@@ -2725,7 +2785,7 @@ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
2725
2785
  const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
2726
2786
  return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
2727
2787
  }
2728
- function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2788
+ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2729
2789
  const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
2730
2790
  if (nestedAliasDecl !== void 0) {
2731
2791
  return resolveAliasedPrimitiveTarget(
@@ -2751,7 +2811,7 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
2751
2811
  diagnostics
2752
2812
  );
2753
2813
  }
2754
- function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2814
+ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2755
2815
  const typeName = getNamedTypeName(type);
2756
2816
  const namedDecl = getNamedTypeDeclaration(type);
2757
2817
  if (typeName && typeName in typeRegistry) {
@@ -2786,11 +2846,19 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2786
2846
  return result;
2787
2847
  }
2788
2848
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
2789
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", typeName, namedDecl, {
2849
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
2850
+ metadataPolicy,
2851
+ "type",
2852
+ typeName,
2853
+ namedDecl,
2790
2854
  checker,
2791
- declaration: namedDecl,
2792
- subjectType: type
2793
- }) : void 0;
2855
+ extensionRegistry,
2856
+ {
2857
+ checker,
2858
+ declaration: namedDecl,
2859
+ subjectType: type
2860
+ }
2861
+ ) : void 0;
2794
2862
  typeRegistry[typeName] = {
2795
2863
  name: typeName,
2796
2864
  ...metadata !== void 0 && { metadata },
@@ -2875,7 +2943,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
2875
2943
  }
2876
2944
  return registerNamed({ kind: "union", members });
2877
2945
  }
2878
- function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2946
+ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2879
2947
  const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
2880
2948
  const elementType = typeArgs?.[0];
2881
2949
  const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
@@ -2892,7 +2960,7 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNod
2892
2960
  ) : { kind: "primitive", primitiveKind: "string" };
2893
2961
  return { kind: "array", items };
2894
2962
  }
2895
- function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2963
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2896
2964
  if (type.getProperties().length > 0) {
2897
2965
  return null;
2898
2966
  }
@@ -2953,7 +3021,7 @@ function shouldEmitResolvedObjectProperty(property, declaration) {
2953
3021
  }
2954
3022
  return true;
2955
3023
  }
2956
- function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3024
+ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = createAnalyzerMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2957
3025
  const collectedDiagnostics = diagnostics ?? [];
2958
3026
  const typeName = getNamedTypeName(type);
2959
3027
  const namedTypeName = typeName ?? void 0;
@@ -3029,11 +3097,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3029
3097
  return recordNode;
3030
3098
  }
3031
3099
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
3032
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
3100
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
3101
+ metadataPolicy,
3102
+ "type",
3103
+ registryTypeName,
3104
+ namedDecl,
3033
3105
  checker,
3034
- declaration: namedDecl,
3035
- subjectType: type
3036
- }) : void 0;
3106
+ extensionRegistry,
3107
+ {
3108
+ checker,
3109
+ declaration: namedDecl,
3110
+ subjectType: type
3111
+ }
3112
+ ) : void 0;
3037
3113
  typeRegistry[registryTypeName] = {
3038
3114
  name: registryTypeName,
3039
3115
  ...metadata !== void 0 && { metadata },
@@ -3078,14 +3154,39 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3078
3154
  collectedDiagnostics
3079
3155
  );
3080
3156
  const fieldNodeInfo = fieldInfoMap?.get(prop.name);
3157
+ const inlineFieldNodeInfo = fieldNodeInfo === void 0 ? ts3.isPropertySignature(declaration) ? analyzeInterfacePropertyToIR(
3158
+ declaration,
3159
+ checker,
3160
+ file,
3161
+ typeRegistry,
3162
+ visiting,
3163
+ collectedDiagnostics,
3164
+ type,
3165
+ metadataPolicy,
3166
+ extensionRegistry
3167
+ ) : ts3.isPropertyDeclaration(declaration) ? analyzeFieldToIR(
3168
+ declaration,
3169
+ checker,
3170
+ file,
3171
+ typeRegistry,
3172
+ visiting,
3173
+ collectedDiagnostics,
3174
+ type,
3175
+ metadataPolicy,
3176
+ extensionRegistry
3177
+ ) : null : null;
3178
+ const resolvedFieldNodeInfo = fieldNodeInfo ?? inlineFieldNodeInfo;
3179
+ const resolvedPropertyType = inlineFieldNodeInfo?.type ?? propTypeNode;
3081
3180
  properties.push({
3082
3181
  name: prop.name,
3083
- ...fieldNodeInfo?.metadata !== void 0 && { metadata: fieldNodeInfo.metadata },
3084
- type: propTypeNode,
3182
+ ...resolvedFieldNodeInfo?.metadata !== void 0 && {
3183
+ metadata: resolvedFieldNodeInfo.metadata
3184
+ },
3185
+ type: resolvedPropertyType,
3085
3186
  optional,
3086
- constraints: fieldNodeInfo?.constraints ?? [],
3087
- annotations: fieldNodeInfo?.annotations ?? [],
3088
- provenance: fieldNodeInfo?.provenance ?? provenanceForFile(file)
3187
+ constraints: resolvedFieldNodeInfo?.constraints ?? [],
3188
+ annotations: resolvedFieldNodeInfo?.annotations ?? [],
3189
+ provenance: resolvedFieldNodeInfo?.provenance ?? provenanceForFile(file)
3089
3190
  });
3090
3191
  }
3091
3192
  visiting.delete(type);
@@ -3104,11 +3205,19 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3104
3205
  };
3105
3206
  if (registryTypeName !== void 0 && shouldRegisterNamedType) {
3106
3207
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
3107
- const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
3208
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(
3209
+ metadataPolicy,
3210
+ "type",
3211
+ registryTypeName,
3212
+ namedDecl,
3108
3213
  checker,
3109
- declaration: namedDecl,
3110
- subjectType: type
3111
- }) : void 0;
3214
+ extensionRegistry,
3215
+ {
3216
+ checker,
3217
+ declaration: namedDecl,
3218
+ subjectType: type
3219
+ }
3220
+ ) : void 0;
3112
3221
  typeRegistry[registryTypeName] = {
3113
3222
  name: registryTypeName,
3114
3223
  ...metadata !== void 0 && { metadata },
@@ -4613,13 +4722,35 @@ function formatLocation(location) {
4613
4722
  }
4614
4723
 
4615
4724
  // src/extensions/registry.ts
4725
+ import {
4726
+ BUILTIN_CONSTRAINT_DEFINITIONS as BUILTIN_CONSTRAINT_DEFINITIONS2,
4727
+ normalizeConstraintTagName as normalizeConstraintTagName2
4728
+ } from "@formspec/core/internals";
4729
+ import {
4730
+ getTagDefinition as getTagDefinition2,
4731
+ normalizeFormSpecTagName as normalizeFormSpecTagName2
4732
+ } from "@formspec/analysis/internal";
4733
+ var BUILTIN_METADATA_TAGS = /* @__PURE__ */ new Set(["apiName", "displayName"]);
4734
+ function buildConstraintTagSources(extensions) {
4735
+ return extensions.map((extension) => ({
4736
+ extensionId: extension.extensionId,
4737
+ ...extension.constraintTags !== void 0 ? {
4738
+ constraintTags: extension.constraintTags.map((tag) => ({
4739
+ tagName: normalizeFormSpecTagName2(tag.tagName)
4740
+ }))
4741
+ } : {}
4742
+ }));
4743
+ }
4616
4744
  function createExtensionRegistry(extensions) {
4745
+ const reservedTagSources = buildConstraintTagSources(extensions);
4617
4746
  const typeMap = /* @__PURE__ */ new Map();
4618
4747
  const typeNameMap = /* @__PURE__ */ new Map();
4619
4748
  const constraintMap = /* @__PURE__ */ new Map();
4620
4749
  const constraintTagMap = /* @__PURE__ */ new Map();
4621
4750
  const builtinBroadeningMap = /* @__PURE__ */ new Map();
4622
4751
  const annotationMap = /* @__PURE__ */ new Map();
4752
+ const metadataSlotMap = /* @__PURE__ */ new Map();
4753
+ const metadataTagMap = /* @__PURE__ */ new Map();
4623
4754
  for (const ext of extensions) {
4624
4755
  if (ext.types !== void 0) {
4625
4756
  for (const type of ext.types) {
@@ -4662,10 +4793,11 @@ function createExtensionRegistry(extensions) {
4662
4793
  }
4663
4794
  if (ext.constraintTags !== void 0) {
4664
4795
  for (const tag of ext.constraintTags) {
4665
- if (constraintTagMap.has(tag.tagName)) {
4666
- throw new Error(`Duplicate custom constraint tag: "@${tag.tagName}"`);
4796
+ const canonicalTagName = normalizeFormSpecTagName2(tag.tagName);
4797
+ if (constraintTagMap.has(canonicalTagName)) {
4798
+ throw new Error(`Duplicate custom constraint tag: "@${canonicalTagName}"`);
4667
4799
  }
4668
- constraintTagMap.set(tag.tagName, {
4800
+ constraintTagMap.set(canonicalTagName, {
4669
4801
  extensionId: ext.extensionId,
4670
4802
  registration: tag
4671
4803
  });
@@ -4680,13 +4812,54 @@ function createExtensionRegistry(extensions) {
4680
4812
  annotationMap.set(qualifiedId, annotation);
4681
4813
  }
4682
4814
  }
4815
+ if (ext.metadataSlots !== void 0) {
4816
+ for (const slot of ext.metadataSlots) {
4817
+ if (metadataSlotMap.has(slot.slotId)) {
4818
+ throw new Error(`Duplicate metadata slot ID: "${slot.slotId}"`);
4819
+ }
4820
+ metadataSlotMap.set(slot.slotId, true);
4821
+ const canonicalTagName = normalizeFormSpecTagName2(slot.tagName);
4822
+ if (slot.allowBare === false && (slot.qualifiers?.length ?? 0) === 0) {
4823
+ throw new Error(
4824
+ `Metadata tag "@${canonicalTagName}" must allow bare usage or declare at least one qualifier.`
4825
+ );
4826
+ }
4827
+ if (metadataTagMap.has(canonicalTagName)) {
4828
+ throw new Error(`Duplicate metadata tag: "@${canonicalTagName}"`);
4829
+ }
4830
+ if (BUILTIN_METADATA_TAGS.has(canonicalTagName)) {
4831
+ throw new Error(
4832
+ `Metadata tag "@${canonicalTagName}" conflicts with built-in metadata tags.`
4833
+ );
4834
+ }
4835
+ if (constraintTagMap.has(canonicalTagName)) {
4836
+ throw new Error(
4837
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${canonicalTagName}".`
4838
+ );
4839
+ }
4840
+ if (Object.hasOwn(BUILTIN_CONSTRAINT_DEFINITIONS2, normalizeConstraintTagName2(canonicalTagName))) {
4841
+ throw new Error(
4842
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${normalizeConstraintTagName2(canonicalTagName)}".`
4843
+ );
4844
+ }
4845
+ const existingTag = getTagDefinition2(canonicalTagName, reservedTagSources);
4846
+ if (existingTag !== null) {
4847
+ throw BUILTIN_METADATA_TAGS.has(existingTag.canonicalName) ? new Error(
4848
+ `Metadata tag "@${canonicalTagName}" conflicts with built-in metadata tags.`
4849
+ ) : new Error(
4850
+ `Metadata tag "@${canonicalTagName}" conflicts with existing FormSpec tag "@${existingTag.canonicalName}".`
4851
+ );
4852
+ }
4853
+ metadataTagMap.set(canonicalTagName, true);
4854
+ }
4855
+ }
4683
4856
  }
4684
4857
  return {
4685
4858
  extensions,
4686
4859
  findType: (typeId) => typeMap.get(typeId),
4687
4860
  findTypeByName: (typeName) => typeNameMap.get(typeName),
4688
4861
  findConstraint: (constraintId) => constraintMap.get(constraintId),
4689
- findConstraintTag: (tagName) => constraintTagMap.get(tagName),
4862
+ findConstraintTag: (tagName) => constraintTagMap.get(normalizeFormSpecTagName2(tagName)),
4690
4863
  findBuiltinConstraintBroadening: (typeId, tagName) => builtinBroadeningMap.get(`${typeId}:${tagName}`),
4691
4864
  findAnnotation: (annotationId) => annotationMap.get(annotationId)
4692
4865
  };