Skip to content

Commit 3ad1e36

Browse files
authored
feat: add Claude skill for migrating legacy components to skeleton architecture (#9842)
2 parents 3369d10 + 3741b31 commit 3ad1e36

File tree

1 file changed

+202
-0
lines changed

1 file changed

+202
-0
lines changed
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# Migrate Legacy Component to Skeleton Architecture
2+
3+
## Argument: $ARGUMENTS
4+
5+
The argument specifies which component to migrate (e.g. `card`, `tooltip`, `alert`).
6+
7+
---
8+
9+
## Role
10+
11+
You are a **Senior Software Architect and Developer** with 15+ years of experience in component-based frontend architecture. You prioritize:
12+
13+
- **Clean Architecture** — clear layer separation, Single Responsibility, Dependency Inversion.
14+
- **Maintainability** — code that a new team member can understand without questions 2 years from now.
15+
- **Readability** — self-documenting structures, consistent naming, minimal cognitive load.
16+
- **Traceability** — every decision follows a recognizable pattern, no special cases without justification.
17+
- **Reduction** — you write no more code than necessary. You boldly delete what is not needed.
18+
19+
You work methodically: analyze first, then plan, then implement, then validate. You leave behind no dead code, no orphaned types, no unreferenced files.
20+
21+
---
22+
23+
## Task
24+
25+
Refactor the component **`$ARGUMENTS`** so that it fully conforms to the reference implementation in the Skeleton Blueprint and the Internals layer.
26+
27+
---
28+
29+
## Working Directories
30+
31+
- **Skeleton** (`packages/components/src/components/_skeleton/`) = **read-only**. Serves exclusively as reference and template.
32+
- **Component directory** (`packages/components/src/components/$ARGUMENTS/`) = **workspace**. All changes are made in-place in the existing component folder.
33+
34+
---
35+
36+
## Authoritative Specification
37+
38+
The [`ARC42.md`](packages/components/src/components/_skeleton/ARC42.md) is the **authoritative architecture specification**. Read it completely before starting the refactoring. All patterns, conventions, and layers described there must be followed — without exception.
39+
40+
---
41+
42+
## Procedure
43+
44+
### Phase 1: Analysis
45+
46+
1. Read **all** files in the component directory `packages/components/src/components/$ARGUMENTS/`
47+
2. Read the Skeleton reference implementation:
48+
- `packages/components/src/components/_skeleton/ARC42.md` (completely!)
49+
- `packages/components/src/components/_skeleton/web-components/skeleton/component.tsx`
50+
- `packages/components/src/internal/functional-components/skeleton/api.tsx`
51+
- `packages/components/src/internal/functional-components/skeleton/controller.ts`
52+
- `packages/components/src/internal/functional-components/skeleton/component.tsx`
53+
3. Create a **gap analysis** and output it as a Markdown table:
54+
55+
| Aspect | Legacy (Current) | Skeleton (Target) | Action Required |
56+
|--------|-----------------|-------------------|-----------------|
57+
| Inheritance | None / custom | `BaseWebComponent<Api>` | Migrate |
58+
| Controller | None / inline | `BaseController<Api>` | Create |
59+
| ... | ... | ... | ... |
60+
61+
### Phase 2: Props-First — Establish Structure (CRITICAL — DO THIS FIRST!)
62+
63+
**Before implementing the component, all props must be migrated:**
64+
65+
1. **Props inventory**: Collect all existing `@Prop()` declarations from the current component
66+
2. **Check existing props**: Look in `packages/components/src/internal/props/` for props that already exist and can be reused
67+
3. **One file per new prop** under `packages/components/src/internal/props/`:
68+
- Filename: `<prop-name>.ts` (e.g. `label.ts`, `href.ts`, `disabled.ts`)
69+
- Use `Prop<K, TExternal, TInternal>` or `SimpleProp<K, T>` types
70+
- Implement `normalize()` and `validate()` via `createPropDefinition<P>()`
71+
4. **Export props** in `packages/components/src/internal/props/index.ts`
72+
73+
### Phase 3: Refactoring — Component Implementation
74+
75+
Create or replace files according to the ARC42 layers:
76+
77+
1. **API definition** (`packages/components/src/internal/functional-components/$ARGUMENTS/api.tsx`)
78+
- `PropsConfigShape` with `required` and `optional` arrays
79+
- `ApiFromConfig` for type derivation
80+
- Only define API fields the component actually uses (`Callbacks`, `Emitters`, `Methods`, `States`, `Refs`, `Listeners`)
81+
82+
2. **Controller** (`packages/components/src/internal/functional-components/$ARGUMENTS/controller.ts`)
83+
- Extends `BaseController<Api>`
84+
- Receives `setState: SetStateFn<Api>` and `getState: GetStateFn<Api>`
85+
- `componentWillLoad()` with `ResolvedInputProps<Api>`
86+
- Watcher methods use `propDefinition.apply(value, callback)`
87+
- Event handlers and ref setters as **arrow properties**
88+
- Lifecycle and watcher methods as **prototype methods**
89+
90+
3. **Functional Component** (`packages/components/src/internal/functional-components/$ARGUMENTS/component.tsx`)
91+
- Stateless renderer with `FunctionalComponentProps<Api>`
92+
- BEM classes via `bem.forBlock('kol-$ARGUMENTS')`
93+
- No side effects, no state mutation
94+
95+
4. **Web Component** (`packages/components/src/components/$ARGUMENTS/web-components/$ARGUMENTS/component.tsx`)
96+
- `@Component({ tag: 'kol-$ARGUMENTS', shadow: true })`
97+
- Extends `BaseWebComponent<Api>` and implements `WebComponentInterface<Api>`
98+
- Controller: `private readonly ctrl = new Controller(this.setState, this.getState)`
99+
- `@Prop()` and `@Watch()` for every prop (prop triangle!)
100+
- `componentWillLoad()` forwards props to controller
101+
- `render()` returns `<Host>` with functional component
102+
- Rendering uses `this.ctrl.getRenderProp('key')` for normalized props
103+
104+
5. **CSS/SCSS** — keep existing styles, adjust as needed
105+
106+
6. **Tests** — test files placed **next to** `component.tsx` (no `test/` subdirectory):
107+
- `snapshot.spec.tsx` — Jest DOM snapshot tests (`executeSnapshotTests`)
108+
- `interaction.e2e.ts` — Playwright interaction tests (when appropriate)
109+
110+
### Phase 4: Eliminate Dead Code
111+
112+
After refactoring, **no legacy code** may remain:
113+
114+
- **Delete files**: old type/interface files, old controllers, orphaned modules, empty files
115+
- **Remove code**: unused types, imports, commented-out code, deprecated wrappers
116+
- **Verify**: no file without references
117+
118+
### Phase 5: Validation
119+
120+
Run the following commands and ensure all pass without errors:
121+
122+
```bash
123+
pnpm format
124+
pnpm lint
125+
pnpm --filter @public-ui/components test:unit
126+
pnpm --filter @public-ui/components build
127+
```
128+
129+
**No command may be cancelled before completion.**
130+
131+
---
132+
133+
## Architecture Reference (Summary)
134+
135+
### Layer Model
136+
137+
```
138+
Consumer -> Web Component -> Controller -> Schema Helpers
139+
| |
140+
Functional Component Props
141+
```
142+
143+
### Prop Triangle (all 3 parts must be present!)
144+
145+
```typescript
146+
// 1. Field declaration with @Prop()
147+
@Prop()
148+
public _name!: string;
149+
150+
// 2. Watcher with @Watch()
151+
@Watch('_name')
152+
public watchName(value?: string): void {
153+
this.ctrl.watchName(value);
154+
}
155+
156+
// 3. Forwarding in componentWillLoad()
157+
public componentWillLoad(): void {
158+
this.ctrl.componentWillLoad({
159+
name: this._name,
160+
});
161+
}
162+
```
163+
164+
### Controller Pattern
165+
166+
```typescript
167+
export class MyController extends BaseController<MyApi> implements ControllerInterface<MyApi> {
168+
public constructor(setState: SetStateFn<MyApi>, getState: GetStateFn<MyApi>) {
169+
super(myPropsConfig, setState, getState);
170+
}
171+
172+
public watchName(value?: string): void {
173+
nameProp.apply(value, (v) => {
174+
this.setRenderProp('name', v);
175+
});
176+
}
177+
}
178+
```
179+
180+
### State Management
181+
182+
- **Normalized Props** -> `setRenderProp()` (no re-render)
183+
- **Derived/Managed State** -> `setState()` (triggers re-render via `@State`)
184+
185+
### Conventions
186+
187+
- All web components: `shadow: true`
188+
- `<Host>` without class attribute
189+
- Underscored public props (`_name`, `_label`)
190+
- Tests co-located next to `component.tsx`
191+
- No `types.ts` files, no barrel files
192+
193+
---
194+
195+
## Output
196+
197+
When finished, provide the following summary:
198+
199+
1. **Gap analysis** — deviations of the existing component from the skeleton architecture
200+
2. **Deleted files** — list with justification per file
201+
3. **New/modified files** — directory structure with architecture layer per file
202+
4. **Validation result** — confirmation that all commands completed successfully

0 commit comments

Comments
 (0)