@contentful/experience-design-system-cli 2.11.4-dev-build-11add11.0 → 2.11.4-dev-build-7abd382.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.11.4-dev-build-11add11.0",
3
+ "version": "2.11.4-dev-build-7abd382.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -211,8 +211,13 @@ async function retryComponentWithProject(component, ctx, project, warnings) {
211
211
  // can't easily re-derive those without re-parsing the fragment, but the
212
212
  // existing component already has them.
213
213
  const templateSlots = component.slots.filter((s) => !snippetSlots.some((ss) => ss.name === s.name));
214
- component.props = props;
215
- component.slots = mergeSlots(snippetSlots, templateSlots).slots;
214
+ const finalSlots = mergeSlots(snippetSlots, templateSlots).slots;
215
+ // Defensive dedupe: if a name somehow ended up in both buckets (e.g. when ts-morph
216
+ // returned `any` for a Snippet member and the Snippet detector failed downstream),
217
+ // the slot wins — slot semantics aren't recoverable from a duplicated prop.
218
+ const slotNames = new Set(finalSlots.map((s) => s.name));
219
+ component.props = props.filter((p) => !slotNames.has(p.name));
220
+ component.slots = finalSlots;
216
221
  // Drop props-type-unresolved from reasons; recompute confidence.
217
222
  const remainingReasons = (component.reviewReasons ?? []).filter((r) => r !== 'props-type-unresolved');
218
223
  const score = computeExtractionScore(component, {
@@ -872,10 +877,18 @@ async function resolveViaTypeChecker(annotation, instance, moduleScript, filePat
872
877
  }
873
878
  const allowed = extractAllowedValuesFromType(propType);
874
879
  const description = readJsDocFromDeclaration(declaration);
875
- // Snippet detection is alias-based first (works through generic instantiation
876
- // and full type expansion) and falls back to text-matching for the simple
877
- // case where alias info isn't available.
878
- const isSnippet = typeRefersToSnippet(propType) || isSnippetTypeText(typeText, snippetLocals);
880
+ // Snippet detection try three signals in order of reliability:
881
+ // 1. The author's declared type-node text on the property's source declaration.
882
+ // This survives even when ts-morph fails to fully resolve a generic
883
+ // instantiation and falls back to `any`/`unknown` (intermittent under
884
+ // partial type-checker state).
885
+ // 2. The resolved type's alias-symbol chain — works when ts-morph DID fully
886
+ // resolve the type (`Snippet<[T]>` expanded to its call signature).
887
+ // 3. Text-match on the rendered type. Last-resort fallback.
888
+ const declaredTypeText = readDeclaredTypeNodeText(declaration);
889
+ const isSnippet = (declaredTypeText !== null && isSnippetTypeText(declaredTypeText, snippetLocals)) ||
890
+ typeRefersToSnippet(propType) ||
891
+ isSnippetTypeText(typeText, snippetLocals);
879
892
  members.push({
880
893
  name,
881
894
  optional,
@@ -924,6 +937,20 @@ function readJsDocFromDeclaration(decl) {
924
937
  }
925
938
  return undefined;
926
939
  }
940
+ /**
941
+ * Read the literal text of the property's declared type annotation as the
942
+ * AUTHOR wrote it (e.g. `Snippet<[Attrs]>`), independent of what the TS
943
+ * checker resolved it to. This is critical because ts-morph's checker can
944
+ * intermittently fall back to `any`/`unknown` for cross-package generic
945
+ * instantiations, in which case the symbol-/alias-based detector has nothing
946
+ * to follow. The author's syntactic annotation is stable in either case.
947
+ */
948
+ function readDeclaredTypeNodeText(decl) {
949
+ if (Node.isPropertySignature(decl)) {
950
+ return decl.getTypeNode()?.getText() ?? null;
951
+ }
952
+ return null;
953
+ }
927
954
  function isSnippetTypeText(typeText, snippetLocals) {
928
955
  // Direct match against the local Snippet name (handles aliasing).
929
956
  for (const local of snippetLocals) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.11.4-dev-build-11add11.0",
3
+ "version": "2.11.4-dev-build-7abd382.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -37,7 +37,7 @@
37
37
  "svelte": "^5.56.4",
38
38
  "ts-morph": "^27.0.2",
39
39
  "typescript": "^5.9.3",
40
- "@contentful/experience-design-system-types": "2.11.4-dev-build-11add11.0"
40
+ "@contentful/experience-design-system-types": "2.11.4-dev-build-7abd382.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@tsconfig/node24": "^24.0.3",