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.
Files changed (3) hide show
  1. package/README.md +30 -14
  2. package/dist/index.js +126 -8
  3. package/package.json +10 -12
package/README.md CHANGED
@@ -1,40 +1,56 @@
1
1
  # figma-token
2
2
 
3
- `figma-token` exports Figma Variables to design token files.
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 --global figma-token
8
+ npm install -D figma-token
9
9
  ```
10
10
 
11
- ## Help
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 --help
15
- figma-token sync --help
16
+ npx figma-token
16
17
  ```
17
18
 
18
- ## Basic usage
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
- Export a local Figma Variables JSON file:
30
+ Choose a project directory with `--out`:
21
31
 
22
32
  ```bash
23
- figma-token sync --input ./figma-variables.json --format theme-ts --output ./theme.ts
33
+ npx figma-token --out ./src/tokens
34
+ npx figma-token ./figma-export.json
24
35
  ```
25
36
 
26
- Preview token changes without writing output or snapshot files:
37
+ Preview all generated files without writing anything:
27
38
 
28
39
  ```bash
29
- figma-token sync --input ./figma-variables.json --dry-run
40
+ npx figma-token --dry-run
30
41
  ```
31
42
 
32
- `--input` accepts a Figma Variables JSON response or a normalized `tokens.json` array. Without `--input`, provide `--figma-token` and `--file-key`, or set `FIGMA_TOKEN` and `FIGMA_FILE_KEY`.
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
- ## Options
50
+ `--check` and `--dry-run` cannot be used together. The old `check` subcommand is not supported.
35
51
 
36
- `sync` supports `--input`, `--output`, `--snapshot`, `--format`, `--export-name`, `--figma-token`, `--file-key`, and `--dry-run`.
52
+ ## Advanced Sync
37
53
 
38
- Supported formats are `tokens-json`, `theme-ts`, `variables-css`, `tokens-scss`, `tailwind-css`, and `tokens-dtcg-json`.
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/sync.ts
8
- import { mkdir, readFile, writeFile } from "fs/promises";
9
- import { dirname } from "path";
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 readFile(path, "utf8"));
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 mkdir(dirname(path), { recursive: true });
264
- await writeFile(path, contents);
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 Variables\uB97C \uB85C\uCEEC \uB514\uC790\uC778 \uD1A0\uD070\uC73C\uB85C \uB3D9\uAE30\uD654\uD569\uB2C8\uB2E4.");
304
- program.command("sync").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({
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.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
- "tsup": "^8.5.0",
25
- "@lee090626/core": "0.1.0"
26
- },
27
- "scripts": {
28
- "build": "tsup",
29
- "test": "vitest run"
26
+ "@lee090626/core": "workspace:*",
27
+ "tsup": "^8.5.0"
30
28
  }
31
- }
29
+ }