Skip to content

Commit 6fbdab5

Browse files
feat(figma): add DTFM support using terrazzo
1 parent df255dc commit 6fbdab5

File tree

6 files changed

+238
-6
lines changed

6 files changed

+238
-6
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export const config = {
2-
extensionPluginKey: 'pro.getds',
2+
extensionPluginKey: 'pro.getds.figma',
33
} as const;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Supported scopes its extractor
2+
3+
- [ ] `ALL_SCOPES`
4+
- [ ] `TEXT_CONTENT`
5+
- [x] `CORNER_RADIUS` via **[float]** extractor
6+
- [x] `WIDTH_HEIGHT` via **[float]** extractor
7+
- [x] `GAP` via **[float]** extractor
8+
- [x] `ALL_FILLS` via **[color]** extractor
9+
- [x] `FRAME_FILL` via **[color]** extractor
10+
- [x] `SHAPE_FILL` via **[color]** extractor
11+
- [x] `TEXT_FILL` via **[color]** extractor
12+
- [x] `STROKE_COLOR` via **[color]** extractor
13+
- [x] `STROKE_FLOAT` via **[float]** extractor
14+
- [x] `EFFECT_FLOAT` via **[float]** extractor
15+
- [x] `EFFECT_COLOR` via **[color]** extractor
16+
- [x] `OPACITY` via **[float]** extractor
17+
- [x] `FONT_FAMILY` via **[string]** extractor
18+
- [x] `FONT_STYLE` via **[string, float]** extractor
19+
- [x] `FONT_WEIGHT` via **[string, float]** extractor
20+
- [x] `FONT_SIZE` via **[float]** extractor
21+
- [x] `LINE_HEIGHT` via **[float]** extractor
22+
- [x] `LETTER_SPACING` via **[float]** extractor
23+
- [x] `PARAGRAPH_SPACING` via **[float]** extractor
24+
- [x] `PARAGRAPH_INDENT` via **[float]** extractor

packages/figma-widget/src/widget/modules/design-tokens/extractors/color.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ export async function extractColor(
3939

4040
const token = {
4141
$type: 'color',
42-
$description: variable.description,
4342
$value: colorOrAlias,
43+
$description: variable.description,
4444
$extensions: {
4545
[config.extensionPluginKey]: {
4646
scopes: variable.scopes,

packages/figma-widget/src/widget/modules/design-tokens/extractors/extract-mode-variable.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type {
44
GroupProperties,
55
JSONTokenTree,
66
} from 'design-tokens-format-module';
7+
import { extractString } from './string';
78

89
export async function extractModeVariable(
910
variable: Variable,
@@ -14,8 +15,9 @@ export async function extractModeVariable(
1415
return extractColor(variable, modeId);
1516
case 'FLOAT':
1617
return extractFloat(variable, modeId);
17-
case 'BOOLEAN':
1818
case 'STRING':
19+
return extractString(variable, modeId);
20+
case 'BOOLEAN':
1921
default:
2022
return {
2123
$description: 'Not implemented',

packages/figma-widget/src/widget/modules/design-tokens/extractors/float.ts

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import type { Dimension, JSONTokenTree } from 'design-tokens-format-module';
1+
import type {
2+
AliasValue,
3+
Dimension,
4+
FontWeight,
5+
JSONTokenTree,
6+
Number,
7+
} from 'design-tokens-format-module';
28
import { extractAlias } from './extract-alias';
39
import { config } from '../../../config';
410
import { tokenizeVariable } from '../utils/tokenize-variable';
@@ -19,6 +25,94 @@ export async function extractFloat(
1925
throw new Error('Unexpected float type');
2026
}
2127

28+
if (
29+
variable.scopes.includes('FONT_WEIGHT') ||
30+
variable.scopes.includes('FONT_STYLE')
31+
) {
32+
let $value: number | AliasValue;
33+
34+
if (typeof value === 'object' && 'id' in value) {
35+
$value = await extractAlias(value.id, modeId);
36+
} else {
37+
$value = value;
38+
}
39+
40+
const token = {
41+
$type: 'fontWeight',
42+
$value,
43+
$description: variable.description,
44+
$extensions: {
45+
[config.extensionPluginKey]: {
46+
scopes: variable.scopes,
47+
},
48+
},
49+
} satisfies FontWeight.Token;
50+
51+
return tokenizeVariable(variable.name)(token);
52+
}
53+
54+
if (
55+
variable.scopes.includes('LINE_HEIGHT') ||
56+
variable.scopes.includes('LETTER_SPACING') ||
57+
variable.scopes.includes('PARAGRAPH_SPACING') ||
58+
variable.scopes.includes('PARAGRAPH_INDENT') ||
59+
variable.scopes.includes('OPACITY') ||
60+
variable.scopes.includes('CORNER_RADIUS') ||
61+
variable.scopes.includes('EFFECT_FLOAT') ||
62+
variable.scopes.includes('STROKE_FLOAT')
63+
) {
64+
let $value: number | AliasValue;
65+
66+
if (typeof value === 'object' && 'id' in value) {
67+
$value = await extractAlias(value.id, modeId);
68+
} else {
69+
$value = value;
70+
}
71+
72+
const token = {
73+
$type: 'number',
74+
$value,
75+
$description: variable.description,
76+
$extensions: {
77+
[config.extensionPluginKey]: {
78+
scopes: variable.scopes,
79+
},
80+
},
81+
} satisfies Number.Token;
82+
83+
return tokenizeVariable(variable.name)(token);
84+
}
85+
86+
if (
87+
variable.scopes.includes('FONT_SIZE') ||
88+
variable.scopes.includes('GAP') ||
89+
variable.scopes.includes('WIDTH_HEIGHT') ||
90+
variable.scopes.includes('ALL_SCOPES')
91+
) {
92+
let $value: Dimension.RawValue | AliasValue;
93+
94+
if (typeof value === 'object' && 'id' in value) {
95+
$value = await extractAlias(value.id, modeId);
96+
} else {
97+
$value = `${value}px`;
98+
}
99+
100+
const token = {
101+
$type: 'dimension',
102+
$value,
103+
$description: variable.description,
104+
$extensions: {
105+
[config.extensionPluginKey]: {
106+
scopes: variable.scopes,
107+
},
108+
},
109+
} satisfies Dimension.Token;
110+
111+
return tokenizeVariable(variable.name)(token);
112+
}
113+
114+
// Any other scope by default will be a dimension
115+
22116
let floatOrAlias: Dimension.Value | undefined;
23117

24118
if (typeof value === 'object' && 'id' in value) {
@@ -27,11 +121,10 @@ export async function extractFloat(
27121
floatOrAlias = `${value}px`;
28122
}
29123

30-
// All scopes
31-
32124
const token = {
33125
$type: 'dimension',
34126
$value: floatOrAlias,
127+
$description: variable.description,
35128
$extensions: {
36129
[config.extensionPluginKey]: {
37130
scopes: variable.scopes,
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import type {
2+
AliasValue,
3+
FontFamily,
4+
FontWeight,
5+
fontWeightValues,
6+
JSONTokenTree,
7+
} from 'design-tokens-format-module';
8+
import { extractAlias } from './extract-alias';
9+
import { config } from '../../../config';
10+
import { tokenizeVariable } from '../utils/tokenize-variable';
11+
12+
export async function extractString(
13+
variable: Variable,
14+
modeId: string
15+
): Promise<JSONTokenTree> {
16+
const value = variable.valuesByMode[modeId];
17+
18+
if (
19+
typeof value === 'number' ||
20+
typeof value === 'boolean' ||
21+
(typeof value === 'object' && 'b' in value)
22+
) {
23+
// number | boolean | RGB | RGBA | VariableAlias
24+
// We should not receive these types under string. If that happens, something went wrong during the extraction from Figma.
25+
throw new Error('Unexpected string type');
26+
}
27+
28+
let stringOrAlias;
29+
30+
if (typeof value === 'object' && 'id' in value) {
31+
stringOrAlias = await extractAlias(value.id, modeId);
32+
} else {
33+
stringOrAlias = value;
34+
}
35+
36+
if (!stringOrAlias) {
37+
// We should not receive these types under string. If that happens, something went wrong during the extraction from Figma.
38+
throw new Error('Unexpected string value');
39+
}
40+
41+
if (
42+
variable.scopes.includes('FONT_FAMILY') &&
43+
typeof stringOrAlias === 'string'
44+
) {
45+
let $value: string;
46+
47+
if (typeof value === 'object' && 'id' in value) {
48+
$value = await extractAlias(value.id, modeId);
49+
} else {
50+
$value = value;
51+
}
52+
53+
return tokenizeVariable(variable.name)({
54+
$type: 'fontFamily',
55+
$value,
56+
$description: variable.description,
57+
$extensions: {
58+
[config.extensionPluginKey]: {
59+
[config.extensionFigmaOriginKey]: {
60+
scopes: variable.scopes,
61+
},
62+
},
63+
},
64+
} satisfies FontFamily.Token);
65+
}
66+
67+
if (
68+
variable.scopes.includes('FONT_WEIGHT') ||
69+
variable.scopes.includes('FONT_STYLE')
70+
) {
71+
let $value: (typeof fontWeightValues)[number] | AliasValue;
72+
73+
if (typeof value === 'object' && 'id' in value) {
74+
$value = await extractAlias(value.id, modeId);
75+
} else {
76+
$value = value as (typeof fontWeightValues)[number];
77+
}
78+
79+
return tokenizeVariable(variable.name)({
80+
$type: 'fontWeight',
81+
$value,
82+
$description: variable.description,
83+
$extensions: {
84+
[config.extensionPluginKey]: {
85+
scopes: variable.scopes,
86+
},
87+
},
88+
} satisfies FontWeight.Token);
89+
}
90+
91+
// Any other scope by default will be a dimension
92+
93+
let floatOrAlias: Dimension.Value | undefined;
94+
95+
if (typeof value === 'object' && 'id' in value) {
96+
floatOrAlias = await extractAlias(value.id, modeId);
97+
} else {
98+
floatOrAlias = `${value}px`;
99+
}
100+
101+
const token = {
102+
$type: 'dimension',
103+
$value: floatOrAlias,
104+
$description: variable.description,
105+
$extensions: {
106+
[config.extensionPluginKey]: {
107+
scopes: variable.scopes,
108+
},
109+
},
110+
} satisfies Dimension.Token;
111+
112+
return tokenizeVariable(variable.name)(token);
113+
}

0 commit comments

Comments
 (0)