|
3 | 3 |
|
4 | 4 | // Extracts the Style API documentation surface from a component's *compiled* CSS. |
5 | 5 | // |
6 | | -// Slots are declared explicitly by the author with the `style-api.docs($name, $tokens)` mixin, which |
7 | | -// emits a machine-readable marker comment into the compiled CSS: |
| 6 | +// Slots are declared explicitly by the author with the style-api docs mixins, which emit a |
| 7 | +// machine-readable marker comment into the compiled CSS. Two forms exist: |
8 | 8 | // |
9 | | -// /* awsui:style-api-slot name=<slot> tokens=<t1>, <t2> */ |
| 9 | +// token slot — `@include style-api.docs($name, $tokens)`: |
| 10 | +// /* awsui:style-api-slot name=<slot> tokens=<t1>, <t2> */ |
10 | 11 | // |
11 | | -// This module parses those markers. |
| 12 | +// forward slot — `@include style-api.docs-forward($name, $component, $slot)`: |
| 13 | +// /* awsui:style-api-slot name=<slot> component=<component> slot=<target-slot> */ |
| 14 | +// |
| 15 | +// A forward slot reuses another component's slot (e.g. a nested Button) instead of owning tokens; |
| 16 | +// the docs consumer resolves it to that component's slot, so it never goes stale. This module parses |
| 17 | +// both forms. |
12 | 18 |
|
13 | | -const MARKER = /awsui:style-api-slot\s+name=([\w-]+)\s+tokens=([^*]*)\*\//g; |
| 19 | +const MARKER = /awsui:style-api-slot\s+name=([\w-]+)\s+(?:tokens=([^*]*)|component=([\w-]+)\s+slot=([\w-]+)\s*)\*\//g; |
14 | 20 |
|
15 | 21 | export interface StyleApiDocs { |
16 | 22 | /** |
17 | | - * The component's themeable slots (defined by classNames), each with its own set of style tokens. |
| 23 | + * The component's themeable slots (defined by classNames). Each slot either owns a set of style |
| 24 | + * tokens or forwards to another component's slot. |
18 | 25 | */ |
19 | 26 | slots: StyleApiSlotDocs[]; |
20 | 27 | } |
21 | 28 |
|
22 | | -export interface StyleApiSlotDocs { |
| 29 | +export type StyleApiSlotDocs = StyleApiTokenSlotDocs | StyleApiForwardSlotDocs; |
| 30 | + |
| 31 | +interface StyleApiSlotDocsBase { |
23 | 32 | /** |
24 | | - * The first argument of `style-api.docs(...)` - must match the corresponding classNames slot. |
| 33 | + * The first argument of the docs mixin - must match the corresponding classNames slot. |
25 | 34 | */ |
26 | 35 | name: string; |
| 36 | +} |
| 37 | + |
| 38 | +export interface StyleApiTokenSlotDocs extends StyleApiSlotDocsBase { |
27 | 39 | /** |
28 | 40 | * The public style tokens this slot supports (without "--awsui-style" prefix). |
29 | 41 | */ |
30 | 42 | tokens: string[]; |
31 | 43 | } |
32 | 44 |
|
| 45 | +export interface StyleApiForwardSlotDocs extends StyleApiSlotDocsBase { |
| 46 | + /** |
| 47 | + * The slot this one forwards to. Its tokens are whatever the referenced component's slot documents. |
| 48 | + */ |
| 49 | + forwardsTo: { component: string; slot: string }; |
| 50 | +} |
| 51 | + |
33 | 52 | /** |
34 | | - * Extracts the Style API slot documentation from a component's compiled CSS by reading the |
35 | | - * explicit slot markers emitted by `style-api.docs(...)`. |
| 53 | + * Extracts the Style API slot documentation from a component's compiled CSS by reading the explicit |
| 54 | + * slot markers emitted by the style-api docs mixins. |
36 | 55 | */ |
37 | 56 | export function extractStyleApiDocs(css: string): StyleApiDocs { |
38 | 57 | const slots = new Array<StyleApiSlotDocs>(); |
39 | 58 | const usedSlots = new Set<string>(); |
40 | 59 |
|
41 | 60 | for (const match of css.matchAll(MARKER)) { |
42 | | - const name = match[1]; |
43 | | - const tokens = match[2].split(/[\s,]+/).filter(Boolean); |
44 | | - slots.push({ name, tokens }); |
45 | | - if (!usedSlots.has(name)) { |
46 | | - usedSlots.add(name); |
| 61 | + const [, name, tokens, component, slot] = match; |
| 62 | + if (usedSlots.has(name)) { |
| 63 | + throw new Error(`Found multiple style-api docs annotations with the same name: "${name}"`); |
| 64 | + } |
| 65 | + usedSlots.add(name); |
| 66 | + |
| 67 | + if (tokens !== undefined) { |
| 68 | + slots.push({ name, tokens: tokens.split(/[\s,]+/).filter(Boolean) }); |
47 | 69 | } else { |
48 | | - throw new Error(`Found multiple style-api.docs(...) annotations with the same name: "${name}"`); |
| 70 | + slots.push({ name, forwardsTo: { component, slot } }); |
49 | 71 | } |
50 | 72 | } |
51 | 73 | return { slots }; |
|
0 commit comments