@contentful/experience-design-system-cli 2.11.4-dev-build-11add11.0 → 2.11.4-dev-build-f3b5300.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
|
@@ -202,28 +202,41 @@ async function retryComponentWithProject(component, ctx, project, warnings) {
|
|
|
202
202
|
}
|
|
203
203
|
if (!members || members.length === 0)
|
|
204
204
|
return false;
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
|
|
205
|
+
// Snippet-only resolution is still useful — it confirms the slot surface
|
|
206
|
+
// and lets us replace the original (which may have had fewer slot entries
|
|
207
|
+
// because the cold AST-only pass missed members declared in heritage).
|
|
208
|
+
// Apply the recovered slots, but DON'T drop `props-type-unresolved` from
|
|
209
|
+
// reasons (the prop side genuinely has no resolvable members).
|
|
210
|
+
const onlySnippets = members.every((m) => m.isSnippet);
|
|
209
211
|
const { props, snippetSlots } = extractFromTypeMembersOnly(members);
|
|
210
212
|
// Merge any template <slot> entries that survived the original pass — we
|
|
211
213
|
// can't easily re-derive those without re-parsing the fragment, but the
|
|
212
214
|
// existing component already has them.
|
|
213
215
|
const templateSlots = component.slots.filter((s) => !snippetSlots.some((ss) => ss.name === s.name));
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
//
|
|
217
|
-
|
|
216
|
+
const finalSlots = mergeSlots(snippetSlots, templateSlots).slots;
|
|
217
|
+
// Defensive dedupe: if a name somehow ended up in both buckets (e.g. when ts-morph
|
|
218
|
+
// returned `any` for a Snippet member and the Snippet detector failed downstream),
|
|
219
|
+
// the slot wins — slot semantics aren't recoverable from a duplicated prop.
|
|
220
|
+
const slotNames = new Set(finalSlots.map((s) => s.name));
|
|
221
|
+
component.props = props.filter((p) => !slotNames.has(p.name));
|
|
222
|
+
component.slots = finalSlots;
|
|
223
|
+
// Recompute confidence. When only Snippets came back, keep the
|
|
224
|
+
// `props-type-unresolved` reason — slot surface is real but the prop side
|
|
225
|
+
// is still legitimately unresolved (the extends-from-node_modules failed).
|
|
226
|
+
// When real props came back, drop the reason: full recovery.
|
|
227
|
+
const remainingReasons = onlySnippets
|
|
228
|
+
? (component.reviewReasons ?? [])
|
|
229
|
+
: (component.reviewReasons ?? []).filter((r) => r !== 'props-type-unresolved');
|
|
218
230
|
const score = computeExtractionScore(component, {
|
|
219
231
|
additionalIssueCount: remainingReasons.length,
|
|
220
232
|
additionalReasons: remainingReasons,
|
|
221
233
|
});
|
|
222
234
|
component.extractionConfidence = score.confidence;
|
|
223
235
|
component.reviewReasons = score.reasons;
|
|
224
|
-
component.needsReview = deriveNeedsReview(score.confidence);
|
|
225
|
-
// Remove the per-component unresolved-type warning
|
|
226
|
-
//
|
|
236
|
+
component.needsReview = deriveNeedsReview(score.confidence) || remainingReasons.includes('props-type-unresolved');
|
|
237
|
+
// Remove the per-component unresolved-type warning. The `props-type-unresolved`
|
|
238
|
+
// reason on the component itself still carries the signal for TUI drill-down;
|
|
239
|
+
// the warnings array is replaced by the collapsed summary upstream.
|
|
227
240
|
const componentName = component.name;
|
|
228
241
|
const idx = warnings.findIndex((w) => w.startsWith(`${componentName}: declared Props type `) && /resolved to /.test(w));
|
|
229
242
|
if (idx >= 0)
|
|
@@ -456,22 +469,26 @@ function extractTypesFromExports(exportsField) {
|
|
|
456
469
|
* When a large fraction of components emit the same `declared Props type ...
|
|
457
470
|
* resolved to ... properties` warning (typical of headless libraries like
|
|
458
471
|
* skeleton-svelte that extend types from external packages we can't reach),
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
472
|
+
* replace ALL of them with a single summary line. Per-component
|
|
473
|
+
* reviewReasons + needsReview flags stay intact, so the user can still
|
|
474
|
+
* drill into any individual component to see the issue — we just don't
|
|
475
|
+
* flood the top-level warnings panel with N near-identical lines.
|
|
462
476
|
*
|
|
463
477
|
* Threshold: 3 or more identical-shape warnings. Below that, the literal
|
|
464
|
-
* per-component lines are
|
|
478
|
+
* per-component lines are short enough to keep as-is.
|
|
465
479
|
*/
|
|
466
480
|
function collapseUnresolvedTypeWarnings(warnings) {
|
|
467
481
|
const isUnresolvedWarning = (w) => /declared Props type .* resolved to/.test(w);
|
|
468
|
-
const
|
|
469
|
-
if (
|
|
482
|
+
const unresolvedCount = warnings.filter(isUnresolvedWarning).length;
|
|
483
|
+
if (unresolvedCount < 3)
|
|
470
484
|
return warnings;
|
|
471
|
-
const
|
|
485
|
+
const others = warnings.filter((w) => !isUnresolvedWarning(w));
|
|
486
|
+
const summary = `Unresolved component types: ${unresolvedCount} components have a declared Props type the parser couldn't fully resolve — ` +
|
|
472
487
|
`most often a cross-package extends pattern (e.g. an interface that extends a type from a node_modules package). ` +
|
|
488
|
+
`Each affected component is flagged with reviewReasons: ['props-type-unresolved'] and needsReview = true; ` +
|
|
489
|
+
`select one in the TUI to drill in. ` +
|
|
473
490
|
`See https://github.com/contentful/experience-design-system-sdk-public/pull/44 for context and partner workarounds.`;
|
|
474
|
-
return [summary, ...
|
|
491
|
+
return [summary, ...others];
|
|
475
492
|
}
|
|
476
493
|
async function extractFromSvelteFile(filePath, source) {
|
|
477
494
|
const warnings = [];
|
|
@@ -872,10 +889,18 @@ async function resolveViaTypeChecker(annotation, instance, moduleScript, filePat
|
|
|
872
889
|
}
|
|
873
890
|
const allowed = extractAllowedValuesFromType(propType);
|
|
874
891
|
const description = readJsDocFromDeclaration(declaration);
|
|
875
|
-
// Snippet detection
|
|
876
|
-
//
|
|
877
|
-
//
|
|
878
|
-
|
|
892
|
+
// Snippet detection — try three signals in order of reliability:
|
|
893
|
+
// 1. The author's declared type-node text on the property's source declaration.
|
|
894
|
+
// This survives even when ts-morph fails to fully resolve a generic
|
|
895
|
+
// instantiation and falls back to `any`/`unknown` (intermittent under
|
|
896
|
+
// partial type-checker state).
|
|
897
|
+
// 2. The resolved type's alias-symbol chain — works when ts-morph DID fully
|
|
898
|
+
// resolve the type (`Snippet<[T]>` expanded to its call signature).
|
|
899
|
+
// 3. Text-match on the rendered type. Last-resort fallback.
|
|
900
|
+
const declaredTypeText = readDeclaredTypeNodeText(declaration);
|
|
901
|
+
const isSnippet = (declaredTypeText !== null && isSnippetTypeText(declaredTypeText, snippetLocals)) ||
|
|
902
|
+
typeRefersToSnippet(propType) ||
|
|
903
|
+
isSnippetTypeText(typeText, snippetLocals);
|
|
879
904
|
members.push({
|
|
880
905
|
name,
|
|
881
906
|
optional,
|
|
@@ -924,6 +949,20 @@ function readJsDocFromDeclaration(decl) {
|
|
|
924
949
|
}
|
|
925
950
|
return undefined;
|
|
926
951
|
}
|
|
952
|
+
/**
|
|
953
|
+
* Read the literal text of the property's declared type annotation as the
|
|
954
|
+
* AUTHOR wrote it (e.g. `Snippet<[Attrs]>`), independent of what the TS
|
|
955
|
+
* checker resolved it to. This is critical because ts-morph's checker can
|
|
956
|
+
* intermittently fall back to `any`/`unknown` for cross-package generic
|
|
957
|
+
* instantiations, in which case the symbol-/alias-based detector has nothing
|
|
958
|
+
* to follow. The author's syntactic annotation is stable in either case.
|
|
959
|
+
*/
|
|
960
|
+
function readDeclaredTypeNodeText(decl) {
|
|
961
|
+
if (Node.isPropertySignature(decl)) {
|
|
962
|
+
return decl.getTypeNode()?.getText() ?? null;
|
|
963
|
+
}
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
927
966
|
function isSnippetTypeText(typeText, snippetLocals) {
|
|
928
967
|
// Direct match against the local Snippet name (handles aliasing).
|
|
929
968
|
for (const local of snippetLocals) {
|
|
@@ -60,5 +60,5 @@ export function AnalyzeView({ result, onExit }) {
|
|
|
60
60
|
.map((e, i) => (_jsx(Text, { color: "red", children: ' ✗ ' + e.component + ': ' + e.error }, i)))] })), result.totalWarnings > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { bold: true, color: "yellow", children: 'Warnings (' + result.totalWarnings + ')' }), _jsx(Text, { dimColor: true, children: '─'.repeat(70) }), _jsx(Text, { children: " " }), result.components
|
|
61
61
|
.filter((c) => c.warnings.length > 0)
|
|
62
62
|
.flatMap((c) => c.warnings.map((w) => ({ component: c.name, warning: w })))
|
|
63
|
-
.map((w, i) => (_jsx(Text, { color: "yellow", children: ' ⚠ ' + w.component + ': ' + w.warning }, i)))] })), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: 'Run: analyze select --session ' + result.sessionId }), _jsx(Text, { children: " " })] }), _jsx(Box, { borderStyle: "single", paddingX: 1, children: _jsx(Text, { dimColor: true, children: "Press Enter or q to exit" }) })] }));
|
|
63
|
+
.map((w, i) => (_jsx(Text, { color: "yellow", children: ' ⚠ ' + (w.warning.startsWith(w.component + ': ') ? w.warning : w.component + ': ' + w.warning) }, i)))] })), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: 'Run: analyze select --session ' + result.sessionId }), _jsx(Text, { children: " " })] }), _jsx(Box, { borderStyle: "single", paddingX: 1, children: _jsx(Text, { dimColor: true, children: "Press Enter or q to exit" }) })] }));
|
|
64
64
|
}
|
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-
|
|
3
|
+
"version": "2.11.4-dev-build-f3b5300.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-
|
|
40
|
+
"@contentful/experience-design-system-types": "2.11.4-dev-build-f3b5300.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@tsconfig/node24": "^24.0.3",
|