Skip to content

Commit 4d62a1f

Browse files
authored
fix(site): support satisfies in api-docs data attrs extraction (#517)
1 parent bdb9d7b commit 4d62a1f

File tree

2 files changed

+73
-9
lines changed

2 files changed

+73
-9
lines changed

site/scripts/api-docs-builder/src/data-attrs-handler.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,26 @@
11
import * as ts from 'typescript';
22
import type { DataAttrsExtraction } from './types.js';
33

4+
function unwrapObjectLiteral(node: ts.Expression): ts.ObjectLiteralExpression | undefined {
5+
if (ts.isObjectLiteralExpression(node)) {
6+
return node;
7+
}
8+
9+
if (ts.isParenthesizedExpression(node)) {
10+
return unwrapObjectLiteral(node.expression);
11+
}
12+
13+
if (ts.isAsExpression(node)) {
14+
return unwrapObjectLiteral(node.expression);
15+
}
16+
17+
if (ts.isSatisfiesExpression(node)) {
18+
return unwrapObjectLiteral(node.expression);
19+
}
20+
21+
return undefined;
22+
}
23+
424
/**
525
* Extract data attributes from a data-attrs file.
626
*
@@ -37,15 +57,7 @@ export function extractDataAttrs(
3757
continue;
3858
}
3959

40-
// Handle `as const` assertions
41-
let objLiteral: ts.ObjectLiteralExpression | undefined;
42-
43-
if (ts.isObjectLiteralExpression(decl.initializer)) {
44-
objLiteral = decl.initializer;
45-
} else if (ts.isAsExpression(decl.initializer) && ts.isObjectLiteralExpression(decl.initializer.expression)) {
46-
objLiteral = decl.initializer.expression;
47-
}
48-
60+
const objLiteral = unwrapObjectLiteral(decl.initializer);
4961
if (!objLiteral) continue;
5062

5163
// Extract properties with their JSDoc comments

site/scripts/api-docs-builder/src/tests/data-attrs-handler.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,58 @@ describe('extractDataAttrs', () => {
6363
expect(result!.attrs).toHaveLength(1);
6464
});
6565

66+
it('extracts from {Name}DataAttrs with as const satisfies', () => {
67+
const code = `
68+
type StateAttrMap<State> = { [Key in keyof State]?: string };
69+
interface MockComponentState {
70+
active: boolean;
71+
}
72+
73+
export const MockComponentDataAttrs = {
74+
active: 'data-active',
75+
} as const satisfies StateAttrMap<MockComponentState>;
76+
`;
77+
const program = createTestProgram(code);
78+
const result = extractDataAttrs('test.ts', program, 'MockComponent');
79+
80+
expect(result).not.toBeNull();
81+
expect(result!.attrs).toHaveLength(1);
82+
expect(result!.attrs[0]!.name).toBe('data-active');
83+
});
84+
85+
it('extracts from {Name}DataAttrs with satisfies expression', () => {
86+
const code = `
87+
export const MockComponentDataAttrs = ({
88+
active: 'data-active',
89+
}) satisfies Record<string, string>;
90+
`;
91+
const program = createTestProgram(code);
92+
const result = extractDataAttrs('test.ts', program, 'MockComponent');
93+
94+
expect(result).not.toBeNull();
95+
expect(result!.attrs).toHaveLength(1);
96+
expect(result!.attrs[0]!.name).toBe('data-active');
97+
});
98+
99+
it('extracts JSDoc comments for properties wrapped with satisfies', () => {
100+
const code = `
101+
type StateAttrMap<State> = { [Key in keyof State]?: string };
102+
interface MockComponentState {
103+
active: boolean;
104+
}
105+
106+
export const MockComponentDataAttrs = {
107+
/** Present when the component is active. */
108+
active: 'data-active',
109+
} as const satisfies StateAttrMap<MockComponentState>;
110+
`;
111+
const program = createTestProgram(code);
112+
const result = extractDataAttrs('test.ts', program, 'MockComponent');
113+
114+
expect(result).not.toBeNull();
115+
expect(result!.attrs[0]!.description).toBe('Present when the component is active.');
116+
});
117+
66118
it('returns null when constant not found', () => {
67119
const code = `
68120
export const OtherConstant = {

0 commit comments

Comments
 (0)