Skip to content

Commit 49c952d

Browse files
authored
feat: update figma v3 plugin mapping (#1125)
1 parent c0484bb commit 49c952d

File tree

98 files changed

+2369
-537
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+2369
-537
lines changed
Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
---
2+
name: figma-v3-migration-plugin-dev
3+
description: Figma V3 Migration Plugin development specialist. Use when developing Figma V3 Migration Plugin. Focuses on Figma V3 Migration Plugin development.
4+
allowed-tools: Read, Write, Edit, MultiEdit, Bash, Glob, Grep
5+
---
6+
7+
## 개요
8+
9+
Figma V3 Migration 플러그인은 SEED Design System V2 컴포넌트를 V3로 마이그레이션하는 Figma 플러그인입니다.
10+
11+
### 디렉토리 구조
12+
13+
```
14+
tools/figma-v3-migration/
15+
├── src/main/
16+
│ ├── mapping/ # 매핑 정의 파일들
17+
│ │ ├── types.ts # 타입 정의
18+
│ │ ├── index.ts # 모든 매핑 export
19+
│ │ ├── buttons.ts # 버튼 컴포넌트 매핑
20+
│ │ ├── action-sheet.ts # Action Sheet 매핑
21+
│ │ └── [component].ts # 기타 컴포넌트별 매핑
22+
│ ├── data/
23+
│ │ └── __generated__/ # 자동 생성된 메타데이터
24+
│ │ ├── v2-component-sets/ # V2 컴포넌트 메타데이터
25+
│ │ └── v3-component-sets/ # V3 컴포넌트 메타데이터
26+
│ └── services/ # Figma API 서비스
27+
├── figma-extractor.config.ts # 추출 설정
28+
└── package.json
29+
```
30+
31+
## 환경 설정
32+
33+
### 1. 환경변수 설정
34+
35+
```bash
36+
export FIGMA_PERSONAL_ACCESS_TOKEN="your-figma-token"
37+
```
38+
39+
Figma Personal Access Token은 Figma Settings > Account > Personal access tokens에서 생성할 수 있습니다.
40+
41+
### 2. 의존성 설치
42+
43+
```bash
44+
cd tools/figma-v3-migration
45+
bun install
46+
```
47+
48+
## 매핑 최신화 프로세스
49+
50+
### 1. 메타데이터 추출
51+
52+
```bash
53+
cd tools/figma-v3-migration
54+
bun extract
55+
```
56+
57+
이 명령어는 Figma API에서 V3 컴포넌트 메타데이터를 추출하여 `src/main/data/__generated__/v3-component-sets/` 디렉토리에 `.d.ts` 파일로 저장합니다.
58+
59+
### 2. 변경된 파일 확인
60+
61+
```bash
62+
git status
63+
git diff src/main/data/__generated__/
64+
```
65+
66+
### 3. 매핑 파일 업데이트 워크플로우
67+
68+
1. **변경된 Generated 파일 분석**: 새로 추가되거나 변경된 컴포넌트 확인
69+
2. **관련 매핑 파일 수정**: `src/main/mapping/` 디렉토리의 해당 컴포넌트 매핑 업데이트
70+
3. **index.ts 업데이트**: 새 매핑 추가 시 export 목록에 추가
71+
4. **타입 체크**: `bun run typecheck:main`으로 매핑 파일 타입 에러 확인
72+
73+
## 매핑 파일 작성 가이드
74+
75+
### 기본 구조
76+
77+
매핑 파일은 `ComponentMapping<OldComponentName, NewComponentName>` 타입을 사용합니다.
78+
79+
```typescript
80+
import type { ComponentMapping, NewComponentProperties } from "./types";
81+
82+
export const exampleMapping: ComponentMapping<"Old Component Name", "New Component Name"> = {
83+
oldComponent: "Old Component Name", // V2 컴포넌트 이름 (Generated 파일의 name과 일치)
84+
newComponent: "New Component Name", // V3 컴포넌트 이름
85+
variantMap: {
86+
// Variant 값 매핑
87+
},
88+
calculateProperties(oldProperties) {
89+
// 프로퍼티 변환 로직
90+
const newProperties: NewComponentProperties<"New Component Name"> = {};
91+
return newProperties;
92+
},
93+
};
94+
```
95+
96+
### 실제 예제: buttons.ts
97+
98+
```typescript
99+
// tools/figma-v3-migration/src/main/mapping/buttons.ts
100+
101+
import type { ComponentMapping, NewComponentProperties } from "./types";
102+
103+
export const boxButtonMapping: ComponentMapping<"✅ Box Button v2", "🟢 Action Button"> = {
104+
oldComponent: "✅ Box Button v2",
105+
newComponent: "🟢 Action Button",
106+
107+
// 1. variantMap: Variant 값 매핑
108+
variantMap: {
109+
// 형식: "VariantName:OldValue": "VariantName:NewValue"
110+
"Size:XSmall": "Size:Small",
111+
"Size:Small": "Size:Small",
112+
"Size:Medium": "Size:Medium",
113+
"Size:Large": "Size:Large",
114+
"Size:XLarge": "Size:Large",
115+
"State:Enabled": "State:Enabled",
116+
"State:Disabled": "State:Disabled",
117+
"State:Loading": "State:Loading",
118+
"State:Pressed": "State:Pressed",
119+
"Variant:Primary": "Variant:Neutral Solid",
120+
"Variant:Primary low": "Variant:Neutral Weak",
121+
"Variant:Secondary": "Variant:Neutral Weak",
122+
"Variant:Danger": "Variant:Critical Solid",
123+
},
124+
125+
// 2. calculateProperties: 프로퍼티 변환 로직
126+
calculateProperties(oldProperties) {
127+
const newProperties: NewComponentProperties<"🟢 Action Button"> = {
128+
// TEXT 프로퍼티 매핑: "PropertyName#NodeId"
129+
"Label#5987:61": oldProperties["Label#28272:77"].value,
130+
};
131+
132+
// BOOLEAN 프로퍼티 읽기
133+
const prefixIcon = oldProperties["Prefix icon#28272:78"].value;
134+
const suffixIcon = oldProperties["Suffix icon#28272:76"].value;
135+
136+
// 조건부 로직으로 Layout 설정
137+
if (prefixIcon && suffixIcon) {
138+
newProperties.Layout = "Icon Last";
139+
newProperties["Prefix Icon#5987:305"] = oldProperties["↳Icons#28292:0"].value;
140+
} else if (prefixIcon) {
141+
newProperties.Layout = "Icon First";
142+
newProperties["Prefix Icon#5987:305"] = oldProperties["↳Icons#28292:0"].value;
143+
} else if (suffixIcon) {
144+
newProperties.Layout = "Icon Last";
145+
} else {
146+
newProperties.Layout = "Text Only";
147+
}
148+
149+
return newProperties;
150+
},
151+
};
152+
```
153+
154+
### 중첩 컴포넌트 처리: action-sheet.ts
155+
156+
부모 컴포넌트 내부에 자식 컴포넌트가 있는 경우 `childrenMappings`를 사용합니다.
157+
158+
```typescript
159+
// tools/figma-v3-migration/src/main/mapping/action-sheet.ts
160+
161+
import type { ComponentMapping, NewComponentProperties } from "./types";
162+
163+
// 자식 컴포넌트 매핑 정의
164+
const itemMenuGroupMapping: ComponentMapping<"Action button group", ".Item / Menu Group"> = {
165+
oldComponent: "Action button group",
166+
newComponent: ".Item / Menu Group",
167+
variantMap: {},
168+
calculateProperties(oldProperties) {
169+
const newProperties: NewComponentProperties<".Item / Menu Group"> = {
170+
"Action Count":
171+
oldProperties["Action count"].value === "8 (Max)"
172+
? "8"
173+
: oldProperties["Action count"].value,
174+
};
175+
return newProperties;
176+
},
177+
};
178+
179+
const itemMenuItemMapping: ComponentMapping<"Action button", ".Item / Menu Item"> = {
180+
oldComponent: "Action button",
181+
newComponent: ".Item / Menu Item",
182+
variantMap: {
183+
"State:Default": "State:Enabled",
184+
"State:Pressed": "State:Pressed",
185+
"Type:Destructive": "Tone:Critical",
186+
"Type:Enabled": "Tone:Neutral",
187+
"Prefix icon:True": "Layout:Text with Icon",
188+
"Prefix icon:False": "Layout:\bText Only",
189+
},
190+
calculateProperties(oldProperties) {
191+
const newProperties: NewComponentProperties<".Item / Menu Item"> = {
192+
"Label#55905:8": oldProperties["🅃 Action label#55905:8"].value,
193+
};
194+
195+
const hasPrefixIcon = oldProperties["Prefix icon"].value === "True";
196+
if (hasPrefixIcon) {
197+
newProperties["Show Prefix Icon#17043:5"] = true;
198+
newProperties["Prefix Icon#55948:0"] = oldProperties["Icon#55948:0"].value;
199+
}
200+
201+
return newProperties;
202+
},
203+
};
204+
205+
// 부모 컴포넌트 매핑 (childrenMappings 포함)
206+
export const actionSheetMapping: ComponentMapping<"✅ Action Sheet v2", "🟢 Menu Sheet"> = {
207+
oldComponent: "✅ Action Sheet v2",
208+
newComponent: "🟢 Menu Sheet",
209+
variantMap: {},
210+
calculateProperties(oldProperties) {
211+
const newProperties: NewComponentProperties<"🟢 Menu Sheet"> = {
212+
Layout: "Text Only",
213+
"Show Safe Area#25531:15": true,
214+
"Menu Group Count": "1",
215+
};
216+
217+
const hasTitle = oldProperties.Title.value === "True";
218+
if (hasTitle) {
219+
newProperties["Show Header#17043:12"] = true;
220+
}
221+
222+
return newProperties;
223+
},
224+
// 자식 컴포넌트 매핑 배열
225+
childrenMappings: [itemMenuGroupMapping, itemMenuItemMapping],
226+
};
227+
```
228+
229+
### 새 매핑 추가 시 체크리스트
230+
231+
1. **Generated 파일 확인**
232+
- V2: `src/main/data/__generated__/v2-component-sets/[component].d.ts`
233+
- V3: `src/main/data/__generated__/v3-component-sets/[component].d.ts`
234+
235+
2. **매핑 파일 생성/수정**
236+
- `src/main/mapping/[component].ts` 파일 생성 또는 기존 파일에 추가
237+
238+
3. **index.ts 업데이트**
239+
```typescript
240+
// src/main/mapping/index.ts
241+
import { newComponentMapping } from "./new-component";
242+
243+
export default [
244+
// ... 기존 매핑들
245+
newComponentMapping,
246+
] as const;
247+
```
248+
249+
4. **타입 체크**
250+
```bash
251+
cd tools/figma-v3-migration
252+
bun run typecheck:main # 매핑 파일 타입 체크
253+
bun run typecheck # 전체 타입 체크 (main + ui)
254+
```
255+
256+
## 타입 시스템
257+
258+
### Generated 메타데이터 구조
259+
260+
```typescript
261+
// src/main/data/__generated__/v3-component-sets/action-button.d.ts
262+
export declare const metadata: {
263+
"name": "🟢 Action Button",
264+
"key": "450ede9d0bf42fc6ef14345c77e6e407d6d5ee89",
265+
"componentPropertyDefinitions": {
266+
"Label#5987:61": {
267+
"type": "TEXT",
268+
"defaultValue": "라벨"
269+
},
270+
"Size": {
271+
"type": "VARIANT",
272+
"defaultValue": "XSmall",
273+
"variantOptions": ["XSmall", "Small", "Medium", "Large"]
274+
},
275+
"Layout": {
276+
"type": "VARIANT",
277+
"defaultValue": "Text Only",
278+
"variantOptions": ["Text Only", "Icon First", "Icon Last", "Icon Only"]
279+
},
280+
"Prefix Icon#5987:305": {
281+
"type": "INSTANCE_SWAP",
282+
"defaultValue": "37665:153410",
283+
"preferredValues": []
284+
}
285+
}
286+
};
287+
```
288+
289+
### 프로퍼티 타입별 처리
290+
291+
| 타입 | 설명 | 값 형식 |
292+
|------|------|---------|
293+
| `VARIANT` | 선택 가능한 옵션들 | `variantOptions` 중 하나 |
294+
| `TEXT` | 텍스트 입력 | `string` |
295+
| `BOOLEAN` | 참/거짓 | `boolean` |
296+
| `INSTANCE_SWAP` | 컴포넌트 교체 | 컴포넌트 key (`string`) |
297+
298+
### 타입 안전성
299+
300+
`ComponentMapping` 타입은 Generated 메타데이터를 기반으로 타입 검증을 수행합니다:
301+
302+
- **컴포넌트 이름**: V2/V3 Generated 파일의 `name`과 일치해야 함
303+
- **프로퍼티 키**: `"PropertyName#NodeId"` 형식으로 정확히 일치해야 함
304+
- **Variant 값**: `variantOptions`에 정의된 값만 사용 가능
305+
306+
잘못된 프로퍼티 이름이나 값을 사용하면 TypeScript 컴파일 에러가 발생합니다.
307+
308+
## 트러블슈팅
309+
310+
### 일반적인 에러
311+
312+
#### 1. "Property does not exist" 타입 에러
313+
314+
**원인**: 프로퍼티 이름이 Generated 파일과 일치하지 않음
315+
316+
**해결**: Generated `.d.ts` 파일에서 정확한 프로퍼티 이름 확인
317+
```bash
318+
cat src/main/data/__generated__/v3-component-sets/[component].d.ts
319+
```
320+
321+
#### 2. "Type is not assignable" 에러
322+
323+
**원인**: Variant 값이 `variantOptions`에 없는 값
324+
325+
**해결**: Generated 파일의 `variantOptions` 확인 후 올바른 값 사용
326+
327+
#### 3. Extract 명령어 실패
328+
329+
**원인**: `FIGMA_PERSONAL_ACCESS_TOKEN` 미설정 또는 만료
330+
331+
**해결**:
332+
```bash
333+
export FIGMA_PERSONAL_ACCESS_TOKEN="new-token"
334+
bun extract
335+
```
336+
337+
### 디버깅 팁
338+
339+
1. **프로퍼티 이름 확인**
340+
```bash
341+
# V2 컴포넌트 프로퍼티 확인
342+
cat src/main/data/__generated__/v2-component-sets/[component].d.ts
343+
344+
# V3 컴포넌트 프로퍼티 확인
345+
cat src/main/data/__generated__/v3-component-sets/[component].d.ts
346+
```
347+
348+
2. **기존 매핑 패턴 참고**
349+
```bash
350+
# 비슷한 컴포넌트의 매핑 확인
351+
cat src/main/mapping/buttons.ts
352+
cat src/main/mapping/checkbox.ts
353+
```
354+
355+
3. **타입 에러 확인**
356+
```bash
357+
cd tools/figma-v3-migration
358+
bun run typecheck:main # 매핑 파일만 체크
359+
bun run typecheck # 전체 체크
360+
```

tools/figma-v3-migration/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
"dev": "concurrently \"bun run watch:ui\" \"bun run watch:main\"",
1515
"lint": "biome check .",
1616
"preview": "vite preview",
17-
"extract": "bun figma-extractor src/main/data/__generated__/v3-component-sets component-sets"
17+
"extract": "bun figma-extractor src/main/data/__generated__/v3-component-sets component-sets",
18+
"typecheck": "bun run typecheck:main && bun run typecheck:ui",
19+
"typecheck:main": "tsc --noEmit -p tsconfig.main.json",
20+
"typecheck:ui": "tsc --noEmit -p tsconfig.ui.json"
1821
},
1922
"dependencies": {
2023
"@figmazing/event": "0.0.1",

0 commit comments

Comments
 (0)