-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfigma-to-typescript.js
135 lines (115 loc) · 4.39 KB
/
figma-to-typescript.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
const axios = require('axios');
const fs = require('fs');
require('dotenv').config()
const BASE_API_URL = 'https://api.figma.com/v1/files/';
const COMPONENTS_ENDPOINT = '/component_sets?depth=1&organization_id=0&version=1&with_co';
const NODES_ENDPOINT = '/nodes?ids=';
const propTypesToTS = {
'TEXT': 'string',
'NUMBER': 'number',
'BOOLEAN': 'boolean',
// Change this if you are using a different library
'INSTANCE_SWAP': 'ReactNode',
};
/**
* Cleans up a given name by removing digits, special characters, and spaces.
* @param {string} name - The input name to be cleaned.
* @returns {string} - The cleaned name with digits, special characters, and spaces removed.
*/
const cleanName = (name) => {
return name
.replace(/\d/g, '') // Remove any digits
.replace(/\W/g, ' ') // Remove special characters and convert to space
.split(' ') // Split string by space
.join('') // Join the words into one string
.replace(/\s/g, ''); // Remove spaces if any left
}
/**
* Converts component property definitions to TypeScript type definitions.
*
* @param {Object} componentPropertyDefinitions - The component property definitions in the form of an object.
* @returns {string} - A string containing the TypeScript type definitions.
*/
function convertToType(componentPropertyDefinitions) {
const result = [];
Object.keys(componentPropertyDefinitions).forEach(key => {
const props = componentPropertyDefinitions[key];
const cleanKey = cleanName(key);
if (props.type in propTypesToTS) {
result.push(`${cleanKey}: ${propTypesToTS[props.type]}`);
}
if (props.type === 'VARIANT') {
result.push(`${cleanKey}: ${props.variantOptions.map(option => `'${option}'`).join(' | ')}`);
}
});
return result.join(', ');
}
/**
* Asynchronously writes data to a ts file.
*
* @async
* @param {string} filePath - The path to the file.
* @param {string|Buffer} data - The data to write to the file.
* @returns {Promise<void>} A Promise that resolves when the operation is complete or rejects with an error.
* @throws {Error} If an error occurs while writing data to the file.
*/
const writeFile = async (filePath, data) => {
return new Promise((resolve, reject) => {
fs.writeFile(filePath, data, 'utf8', (error) => {
if (error) {
reject(error);
return;
}
resolve();
});
});
}
const getTypes = async (fileKey, accessToken) => {
if (fs.existsSync('types')) {
fs.rmdirSync('types', {recursive: true});
}
fs.mkdirSync('types');
const components = await axios.request({
method: 'get',
maxBodyLength: Infinity,
url: `${BASE_API_URL}${fileKey}${COMPONENTS_ENDPOINT}`,
headers: {
'X-Figma-Token': accessToken
}
});
if (components.data.meta.component_sets.length === 0) {
console.log("No components found in this file")
return;
} else {
console.log("-----------------------")
console.log("Writing files...")
console.log("-----------------------")
}
for (const file of components.data.meta.component_sets) {
const NODE_ID = file.node_id;
const components = await axios.request(
{
method: 'get',
maxBodyLength: Infinity,
url: `${BASE_API_URL}${fileKey}${NODES_ENDPOINT}${NODE_ID}`,
headers: {
'X-Figma-Token': accessToken
}
}
);
const component = components.data.nodes[NODE_ID];
const type = component.document.type;
const name = cleanName(component.document.name);
if (type === 'COMPONENT_SET' && !component.document.name.startsWith(".") && !component.document.name.startsWith("_")) {
console.log(`Writing ${name}.ts...`)
const types = convertToType(component.document.componentPropertyDefinitions);
await writeFile(`types/${name}.ts`, `${types.includes('ReactNode') ? 'import {ReactNode} from "react";\n\n' : ''}export type ${name}Props = {${types}}`);
}
}
return components.data;
}
getTypes(process.env.FIGMA_FILE_KEY, process.env.FIGMA_PERSONAL_ACCESS_TOKEN).then(() => {
console.log("-----------------------")
console.log("Done")
console.log("-----------------------")
});