figma-token 0.1.1 → 0.1.3
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 +30 -14
- package/dist/index.js +126 -8
- package/package.json +10 -12
package/README.md
CHANGED
|
@@ -1,40 +1,56 @@
|
|
|
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
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install
|
|
8
|
+
npm install -D figma-token
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
The Figma Plugin can download a ZIP or all six formats directly. Use this optional 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
|
+
npx figma-token
|
|
16
17
|
```
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
Without an input path, the CLI reads `./tokens.json` from the current project and 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
|
+
npx figma-token --out ./src/tokens
|
|
34
|
+
npx figma-token ./figma-export.json
|
|
24
35
|
```
|
|
25
36
|
|
|
26
|
-
Preview
|
|
37
|
+
Preview all generated files without writing anything:
|
|
27
38
|
|
|
28
39
|
```bash
|
|
29
|
-
figma-token
|
|
40
|
+
npx figma-token --dry-run
|
|
30
41
|
```
|
|
31
42
|
|
|
32
|
-
|
|
43
|
+
Check whether generated files are current:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npx figma-token --check
|
|
47
|
+
npx figma-token --check --out ./src/tokens
|
|
48
|
+
```
|
|
33
49
|
|
|
34
|
-
|
|
50
|
+
`--check` and `--dry-run` cannot be used together. The old `check` subcommand is not supported.
|
|
35
51
|
|
|
36
|
-
|
|
52
|
+
## Advanced Sync
|
|
37
53
|
|
|
38
|
-
|
|
54
|
+
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
55
|
|
|
40
56
|
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,108 @@ 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
|
+
function defaultInputFile(cwd = process.cwd()) {
|
|
231
|
+
return resolve(cwd, "tokens.json");
|
|
232
|
+
}
|
|
233
|
+
var outputDirectory = (output) => resolve(output ?? defaultOutputDirectory());
|
|
234
|
+
async function readPluginTokens(input) {
|
|
235
|
+
const inputPath = resolve(input);
|
|
236
|
+
let inputStat;
|
|
237
|
+
try {
|
|
238
|
+
inputStat = await stat(inputPath);
|
|
239
|
+
} catch (error) {
|
|
240
|
+
if (error.code === "ENOENT") throw new Error(`Input file not found: ${inputPath}`);
|
|
241
|
+
throw new Error(`Cannot read input file: ${inputPath}`);
|
|
242
|
+
}
|
|
243
|
+
if (inputStat.isDirectory()) throw new Error(`Input path is a directory: ${inputPath}`);
|
|
244
|
+
let raw;
|
|
245
|
+
try {
|
|
246
|
+
raw = JSON.parse(await readFile(inputPath, "utf8"));
|
|
247
|
+
} catch (error) {
|
|
248
|
+
if (error instanceof SyntaxError) throw new Error(`Invalid JSON: ${inputPath}`);
|
|
249
|
+
throw new Error(`Cannot read input file: ${inputPath}`);
|
|
250
|
+
}
|
|
251
|
+
if (!isDesignTokenArray(raw)) throw new Error(`Unsupported input: expected Plugin tokens.json: ${inputPath}`);
|
|
252
|
+
return { inputPath, tokens: raw };
|
|
253
|
+
}
|
|
254
|
+
function renderAll(tokens) {
|
|
255
|
+
return {
|
|
256
|
+
"tokens.json": renderTokensJson(tokens),
|
|
257
|
+
"theme.ts": renderTheme(tokens),
|
|
258
|
+
"variables.css": renderCssVariables(tokens),
|
|
259
|
+
"tokens.scss": renderScssVariables(tokens),
|
|
260
|
+
"tailwind.css": renderTailwindTheme(tokens),
|
|
261
|
+
"tokens.dtcg.json": renderDtcgJson(tokens)
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
async function prepare(options) {
|
|
265
|
+
const { inputPath, tokens } = await readPluginTokens(options.input);
|
|
266
|
+
const outputPath = outputDirectory(options.output);
|
|
267
|
+
return { inputPath, outputPath, files: renderAll(tokens) };
|
|
268
|
+
}
|
|
269
|
+
async function writeAll(outputPath, files) {
|
|
270
|
+
try {
|
|
271
|
+
await mkdir(outputPath, { recursive: true });
|
|
272
|
+
} catch {
|
|
273
|
+
throw new Error(`Cannot create output directory: ${outputPath}`);
|
|
274
|
+
}
|
|
275
|
+
for (const filename of tokenFileNames) {
|
|
276
|
+
const path = join(outputPath, filename);
|
|
277
|
+
try {
|
|
278
|
+
await writeFile(path, files[filename]);
|
|
279
|
+
} catch {
|
|
280
|
+
throw new Error(`Cannot write token file: ${path}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
async function exportTokenFiles(options, log = console.log) {
|
|
285
|
+
const result = await prepare(options);
|
|
286
|
+
if (options.dryRun) {
|
|
287
|
+
log(`Input: ${result.inputPath}`);
|
|
288
|
+
log(`Output: ${result.outputPath}`);
|
|
289
|
+
log("Would generate:");
|
|
290
|
+
tokenFileNames.forEach((filename) => log(`- ${filename}`));
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
await writeAll(result.outputPath, result.files);
|
|
294
|
+
log(`Generated token files in: ${result.outputPath}`);
|
|
295
|
+
return result;
|
|
296
|
+
}
|
|
297
|
+
async function checkTokenFiles(options, log = console.log) {
|
|
298
|
+
const result = await prepare(options);
|
|
299
|
+
const outdated = [];
|
|
300
|
+
for (const filename of tokenFileNames) {
|
|
301
|
+
const path = join(result.outputPath, filename);
|
|
302
|
+
let contents;
|
|
303
|
+
try {
|
|
304
|
+
contents = await readFile(path, "utf8");
|
|
305
|
+
} catch (error) {
|
|
306
|
+
if (error.code === "ENOENT") {
|
|
307
|
+
outdated.push(`- ${filename}: missing`);
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
throw new Error(`Cannot read token file: ${path}`);
|
|
311
|
+
}
|
|
312
|
+
if (contents !== result.files[filename]) outdated.push(`- ${filename}: changed`);
|
|
313
|
+
}
|
|
314
|
+
if (outdated.length === 0) {
|
|
315
|
+
log("Token files are up to date.");
|
|
316
|
+
return 0;
|
|
317
|
+
}
|
|
318
|
+
log("Token files are not up to date:");
|
|
319
|
+
outdated.forEach((message) => log(message));
|
|
320
|
+
return 1;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/sync.ts
|
|
324
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
325
|
+
import { dirname } from "path";
|
|
326
|
+
|
|
225
327
|
// src/figma/fetchFigmaVariables.ts
|
|
226
328
|
var FIGMA_API = "https://api.figma.com/v1";
|
|
227
329
|
async function fetchFigmaVariables(fileKey, token) {
|
|
@@ -243,7 +345,7 @@ async function fetchFigmaVariables(fileKey, token) {
|
|
|
243
345
|
// src/sync.ts
|
|
244
346
|
var readJson = async (path) => {
|
|
245
347
|
try {
|
|
246
|
-
return JSON.parse(await
|
|
348
|
+
return JSON.parse(await readFile2(path, "utf8"));
|
|
247
349
|
} catch (error) {
|
|
248
350
|
if (error instanceof SyntaxError) throw new Error(`Invalid JSON: ${path}`);
|
|
249
351
|
throw error;
|
|
@@ -260,8 +362,8 @@ var readSnapshot = async (path) => {
|
|
|
260
362
|
}
|
|
261
363
|
};
|
|
262
364
|
var save = async (path, contents) => {
|
|
263
|
-
await
|
|
264
|
-
await
|
|
365
|
+
await mkdir2(dirname(path), { recursive: true });
|
|
366
|
+
await writeFile2(path, contents);
|
|
265
367
|
};
|
|
266
368
|
var render = (tokens, options) => ({
|
|
267
369
|
"tokens-json": renderTokensJson,
|
|
@@ -300,8 +402,24 @@ var format = (value) => {
|
|
|
300
402
|
if (!formats.includes(value)) throw new InvalidArgumentError(`format\uC740 ${formats.join(", ")} \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);
|
|
301
403
|
return value;
|
|
302
404
|
};
|
|
303
|
-
var program = new Command().name("figma-token").description("Figma
|
|
304
|
-
|
|
405
|
+
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.3").argument("[input]", "Figma Plugin\uC5D0\uC11C \uB2E4\uC6B4\uB85C\uB4DC\uD55C tokens.json (default: ./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").option("--check", "\uD604\uC7AC \uD1A0\uD070 \uD30C\uC77C\uC774 \uCD5C\uC2E0\uC778\uC9C0 \uD655\uC778").action(async (input, options) => {
|
|
406
|
+
if (options.check && options.dryRun) throw new Error("--check\uC640 --dry-run\uC740 \uD568\uAED8 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD558\uB098\uB9CC \uC0AC\uC6A9\uD558\uC138\uC694.");
|
|
407
|
+
const defaultInput = !input;
|
|
408
|
+
const inputPath = input ?? defaultInputFile();
|
|
409
|
+
try {
|
|
410
|
+
if (options.check) {
|
|
411
|
+
process.exitCode = await checkTokenFiles({ input: inputPath, output: options.out ?? defaultOutputDirectory() });
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
await exportTokenFiles({ input: inputPath, output: options.out ?? defaultOutputDirectory(), dryRun: options.dryRun });
|
|
415
|
+
} catch (error) {
|
|
416
|
+
if (defaultInput && error instanceof Error && error.message.startsWith("Input file not found:")) {
|
|
417
|
+
throw new Error("Expected token file: ./tokens.json\nProvide another file with: figma-token <input>");
|
|
418
|
+
}
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
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
423
|
...options,
|
|
306
424
|
figmaToken: options.figmaToken ?? process.env.FIGMA_TOKEN,
|
|
307
425
|
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.3",
|
|
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
|
+
}
|