figma-token 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -13
- package/dist/index.js +112 -8
- package/package.json +10 -12
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# figma-token
|
|
2
2
|
|
|
3
|
-
`figma-token`
|
|
3
|
+
`figma-token` applies a Figma Plugin `tokens.json` export to a project directory.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,33 +8,45 @@
|
|
|
8
8
|
npm install --global figma-token
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
The Figma Plugin can download all six formats directly. Use this CLI when a project needs those files written to a predictable folder and checked for changes.
|
|
12
|
+
|
|
13
|
+
## Apply Plugin Tokens
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
|
-
figma-token
|
|
15
|
-
figma-token sync --help
|
|
16
|
+
figma-token ./figma-tokens.json
|
|
16
17
|
```
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
This creates the following files in `./figma-token-output/`:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
tokens.json
|
|
23
|
+
theme.ts
|
|
24
|
+
variables.css
|
|
25
|
+
tokens.scss
|
|
26
|
+
tailwind.css
|
|
27
|
+
tokens.dtcg.json
|
|
28
|
+
```
|
|
19
29
|
|
|
20
|
-
|
|
30
|
+
Choose a project directory with `--out`:
|
|
21
31
|
|
|
22
32
|
```bash
|
|
23
|
-
figma-token
|
|
33
|
+
figma-token ./figma-tokens.json --out ./src/tokens
|
|
24
34
|
```
|
|
25
35
|
|
|
26
|
-
Preview
|
|
36
|
+
Preview all generated files without writing anything:
|
|
27
37
|
|
|
28
38
|
```bash
|
|
29
|
-
figma-token
|
|
39
|
+
figma-token ./figma-tokens.json --dry-run
|
|
30
40
|
```
|
|
31
41
|
|
|
32
|
-
|
|
42
|
+
Check whether generated files are current:
|
|
33
43
|
|
|
34
|
-
|
|
44
|
+
```bash
|
|
45
|
+
figma-token check ./figma-tokens.json --out ./src/tokens
|
|
46
|
+
```
|
|
35
47
|
|
|
36
|
-
|
|
48
|
+
## Advanced Sync
|
|
37
49
|
|
|
38
|
-
|
|
50
|
+
The hidden `sync` command retains the previous single-format, snapshot, and Figma REST API flow for advanced users. It accepts `--input`, `--output`, `--snapshot`, `--format`, `--export-name`, `--figma-token`, `--file-key`, and `--dry-run`.
|
|
39
51
|
|
|
40
52
|
For Plugin usage and project-wide documentation, see <https://github.com/lee090626/Project-F>.
|
package/dist/index.js
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
import "dotenv/config";
|
|
5
5
|
import { Command, InvalidArgumentError } from "commander";
|
|
6
6
|
|
|
7
|
-
// src/
|
|
8
|
-
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
9
|
-
import {
|
|
7
|
+
// src/exportTokens.ts
|
|
8
|
+
import { mkdir, readFile, stat, writeFile } from "fs/promises";
|
|
9
|
+
import { join, resolve } from "path";
|
|
10
10
|
|
|
11
11
|
// ../core/dist/index.js
|
|
12
12
|
var supportedTypes = ["color", "spacing", "radius", "borderWidth", "size", "fontSize", "opacity"];
|
|
@@ -222,6 +222,105 @@ function renderTheme(tokens, exportName = "theme") {
|
|
|
222
222
|
`;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
+
// src/exportTokens.ts
|
|
226
|
+
var tokenFileNames = ["tokens.json", "theme.ts", "variables.css", "tokens.scss", "tailwind.css", "tokens.dtcg.json"];
|
|
227
|
+
function defaultOutputDirectory(cwd = process.cwd()) {
|
|
228
|
+
return resolve(cwd, "figma-token-output");
|
|
229
|
+
}
|
|
230
|
+
var outputDirectory = (output) => resolve(output ?? defaultOutputDirectory());
|
|
231
|
+
async function readPluginTokens(input) {
|
|
232
|
+
const inputPath = resolve(input);
|
|
233
|
+
let inputStat;
|
|
234
|
+
try {
|
|
235
|
+
inputStat = await stat(inputPath);
|
|
236
|
+
} catch (error) {
|
|
237
|
+
if (error.code === "ENOENT") throw new Error(`Input file not found: ${inputPath}`);
|
|
238
|
+
throw new Error(`Cannot read input file: ${inputPath}`);
|
|
239
|
+
}
|
|
240
|
+
if (inputStat.isDirectory()) throw new Error(`Input path is a directory: ${inputPath}`);
|
|
241
|
+
let raw;
|
|
242
|
+
try {
|
|
243
|
+
raw = JSON.parse(await readFile(inputPath, "utf8"));
|
|
244
|
+
} catch (error) {
|
|
245
|
+
if (error instanceof SyntaxError) throw new Error(`Invalid JSON: ${inputPath}`);
|
|
246
|
+
throw new Error(`Cannot read input file: ${inputPath}`);
|
|
247
|
+
}
|
|
248
|
+
if (!isDesignTokenArray(raw)) throw new Error(`Unsupported input: expected Plugin tokens.json: ${inputPath}`);
|
|
249
|
+
return { inputPath, tokens: raw };
|
|
250
|
+
}
|
|
251
|
+
function renderAll(tokens) {
|
|
252
|
+
return {
|
|
253
|
+
"tokens.json": renderTokensJson(tokens),
|
|
254
|
+
"theme.ts": renderTheme(tokens),
|
|
255
|
+
"variables.css": renderCssVariables(tokens),
|
|
256
|
+
"tokens.scss": renderScssVariables(tokens),
|
|
257
|
+
"tailwind.css": renderTailwindTheme(tokens),
|
|
258
|
+
"tokens.dtcg.json": renderDtcgJson(tokens)
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
async function prepare(options) {
|
|
262
|
+
const { inputPath, tokens } = await readPluginTokens(options.input);
|
|
263
|
+
const outputPath = outputDirectory(options.output);
|
|
264
|
+
return { inputPath, outputPath, files: renderAll(tokens) };
|
|
265
|
+
}
|
|
266
|
+
async function writeAll(outputPath, files) {
|
|
267
|
+
try {
|
|
268
|
+
await mkdir(outputPath, { recursive: true });
|
|
269
|
+
} catch {
|
|
270
|
+
throw new Error(`Cannot create output directory: ${outputPath}`);
|
|
271
|
+
}
|
|
272
|
+
for (const filename of tokenFileNames) {
|
|
273
|
+
const path = join(outputPath, filename);
|
|
274
|
+
try {
|
|
275
|
+
await writeFile(path, files[filename]);
|
|
276
|
+
} catch {
|
|
277
|
+
throw new Error(`Cannot write token file: ${path}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async function exportTokenFiles(options, log = console.log) {
|
|
282
|
+
const result = await prepare(options);
|
|
283
|
+
if (options.dryRun) {
|
|
284
|
+
log(`Input: ${result.inputPath}`);
|
|
285
|
+
log(`Output: ${result.outputPath}`);
|
|
286
|
+
log("Would generate:");
|
|
287
|
+
tokenFileNames.forEach((filename) => log(`- ${filename}`));
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
await writeAll(result.outputPath, result.files);
|
|
291
|
+
log(`Generated token files in: ${result.outputPath}`);
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
async function checkTokenFiles(options, log = console.log) {
|
|
295
|
+
const result = await prepare(options);
|
|
296
|
+
const outdated = [];
|
|
297
|
+
for (const filename of tokenFileNames) {
|
|
298
|
+
const path = join(result.outputPath, filename);
|
|
299
|
+
let contents;
|
|
300
|
+
try {
|
|
301
|
+
contents = await readFile(path, "utf8");
|
|
302
|
+
} catch (error) {
|
|
303
|
+
if (error.code === "ENOENT") {
|
|
304
|
+
outdated.push(`- ${filename}: missing`);
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
throw new Error(`Cannot read token file: ${path}`);
|
|
308
|
+
}
|
|
309
|
+
if (contents !== result.files[filename]) outdated.push(`- ${filename}: changed`);
|
|
310
|
+
}
|
|
311
|
+
if (outdated.length === 0) {
|
|
312
|
+
log("Token files are up to date.");
|
|
313
|
+
return 0;
|
|
314
|
+
}
|
|
315
|
+
log("Token files are not up to date:");
|
|
316
|
+
outdated.forEach((message) => log(message));
|
|
317
|
+
return 1;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/sync.ts
|
|
321
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
322
|
+
import { dirname } from "path";
|
|
323
|
+
|
|
225
324
|
// src/figma/fetchFigmaVariables.ts
|
|
226
325
|
var FIGMA_API = "https://api.figma.com/v1";
|
|
227
326
|
async function fetchFigmaVariables(fileKey, token) {
|
|
@@ -243,7 +342,7 @@ async function fetchFigmaVariables(fileKey, token) {
|
|
|
243
342
|
// src/sync.ts
|
|
244
343
|
var readJson = async (path) => {
|
|
245
344
|
try {
|
|
246
|
-
return JSON.parse(await
|
|
345
|
+
return JSON.parse(await readFile2(path, "utf8"));
|
|
247
346
|
} catch (error) {
|
|
248
347
|
if (error instanceof SyntaxError) throw new Error(`Invalid JSON: ${path}`);
|
|
249
348
|
throw error;
|
|
@@ -260,8 +359,8 @@ var readSnapshot = async (path) => {
|
|
|
260
359
|
}
|
|
261
360
|
};
|
|
262
361
|
var save = async (path, contents) => {
|
|
263
|
-
await
|
|
264
|
-
await
|
|
362
|
+
await mkdir2(dirname(path), { recursive: true });
|
|
363
|
+
await writeFile2(path, contents);
|
|
265
364
|
};
|
|
266
365
|
var render = (tokens, options) => ({
|
|
267
366
|
"tokens-json": renderTokensJson,
|
|
@@ -300,8 +399,13 @@ var format = (value) => {
|
|
|
300
399
|
if (!formats.includes(value)) throw new InvalidArgumentError(`format\uC740 ${formats.join(", ")} \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);
|
|
301
400
|
return value;
|
|
302
401
|
};
|
|
303
|
-
var program = new Command().name("figma-token").description("
|
|
304
|
-
|
|
402
|
+
var program = new Command().name("figma-token").description("Plugin tokens.json\uC744 \uD504\uB85C\uC81D\uD2B8 \uD1A0\uD070 \uD30C\uC77C\uB85C \uC801\uC6A9\uD569\uB2C8\uB2E4.").version("0.1.2").argument("<input>", "Figma Plugin\uC5D0\uC11C \uB2E4\uC6B4\uB85C\uB4DC\uD55C tokens.json").option("--out <directory>", "\uCD9C\uB825 \uD3F4\uB354 (default: ./figma-token-output)").option("--dry-run", "\uD30C\uC77C\uC744 \uC4F0\uC9C0 \uC54A\uACE0 \uC0DD\uC131 \uACB0\uACFC\uB97C \uD655\uC778").action(async (input, options) => {
|
|
403
|
+
await exportTokenFiles({ input, output: options.out ?? defaultOutputDirectory(), dryRun: options.dryRun });
|
|
404
|
+
});
|
|
405
|
+
program.command("check").description("\uD604\uC7AC \uD1A0\uD070 \uD30C\uC77C\uC774 Plugin tokens.json\uACFC \uC77C\uCE58\uD558\uB294\uC9C0 \uD655\uC778\uD569\uB2C8\uB2E4.").argument("<input>", "Figma Plugin\uC5D0\uC11C \uB2E4\uC6B4\uB85C\uB4DC\uD55C tokens.json").option("--out <directory>", "\uCD9C\uB825 \uD3F4\uB354 (default: ./figma-token-output)").action(async (input, options) => {
|
|
406
|
+
process.exitCode = await checkTokenFiles({ input, output: options.out ?? defaultOutputDirectory() });
|
|
407
|
+
});
|
|
408
|
+
program.command("sync", { hidden: true }).option("--input <path>", "\uB85C\uCEEC Figma Variables JSON").option("--output <path>", "\uCD9C\uB825 \uD30C\uC77C", "./tokens.json").option("--snapshot <path>", "snapshot \uD30C\uC77C", ".figma-token/snapshot.json").option("--format <format>", "\uCD9C\uB825 \uD3EC\uB9F7", format, "tokens-json").option("--export-name <name>", "theme.ts export \uC774\uB984", "theme").option("--figma-token <token>", "Figma token").option("--file-key <key>", "Figma file key").option("--dry-run", "\uD30C\uC77C\uC744 \uC4F0\uC9C0 \uC54A\uACE0 diff\uB9CC \uCD9C\uB825", false).action(async (options) => sync({
|
|
305
409
|
...options,
|
|
306
410
|
figmaToken: options.figmaToken ?? process.env.FIGMA_TOKEN,
|
|
307
411
|
fileKey: options.fileKey ?? process.env.FIGMA_FILE_KEY
|
package/package.json
CHANGED
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "figma-token",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Export Figma Variables to design token files.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"bin": {
|
|
8
|
-
"figma-token": "dist/index.js"
|
|
9
|
-
},
|
|
7
|
+
"bin": { "figma-token": "dist/index.js" },
|
|
10
8
|
"repository": {
|
|
11
9
|
"type": "git",
|
|
12
|
-
"url": "https://github.com/lee090626/Project-F.git",
|
|
10
|
+
"url": "git+https://github.com/lee090626/Project-F.git",
|
|
13
11
|
"directory": "packages/cli"
|
|
14
12
|
},
|
|
15
13
|
"files": [
|
|
16
14
|
"dist",
|
|
17
15
|
"README.md"
|
|
18
16
|
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup",
|
|
19
|
+
"test": "vitest run"
|
|
20
|
+
},
|
|
19
21
|
"dependencies": {
|
|
20
22
|
"commander": "^14.0.0",
|
|
21
23
|
"dotenv": "^17.0.0"
|
|
22
24
|
},
|
|
23
25
|
"devDependencies": {
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
},
|
|
27
|
-
"scripts": {
|
|
28
|
-
"build": "tsup",
|
|
29
|
-
"test": "vitest run"
|
|
26
|
+
"@lee090626/core": "workspace:*",
|
|
27
|
+
"tsup": "^8.5.0"
|
|
30
28
|
}
|
|
31
|
-
}
|
|
29
|
+
}
|