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.
Files changed (3) hide show
  1. package/README.md +25 -13
  2. package/dist/index.js +112 -8
  3. package/package.json +10 -12
package/README.md CHANGED
@@ -1,6 +1,6 @@
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
 
@@ -8,33 +8,45 @@
8
8
  npm install --global figma-token
9
9
  ```
10
10
 
11
- ## Help
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 --help
15
- figma-token sync --help
16
+ figma-token ./figma-tokens.json
16
17
  ```
17
18
 
18
- ## Basic usage
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
- 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
+ figma-token ./figma-tokens.json --out ./src/tokens
24
34
  ```
25
35
 
26
- Preview token changes without writing output or snapshot files:
36
+ Preview all generated files without writing anything:
27
37
 
28
38
  ```bash
29
- figma-token sync --input ./figma-variables.json --dry-run
39
+ figma-token ./figma-tokens.json --dry-run
30
40
  ```
31
41
 
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`.
42
+ Check whether generated files are current:
33
43
 
34
- ## Options
44
+ ```bash
45
+ figma-token check ./figma-tokens.json --out ./src/tokens
46
+ ```
35
47
 
36
- `sync` supports `--input`, `--output`, `--snapshot`, `--format`, `--export-name`, `--figma-token`, `--file-key`, and `--dry-run`.
48
+ ## Advanced Sync
37
49
 
38
- Supported formats are `tokens-json`, `theme-ts`, `variables-css`, `tokens-scss`, `tailwind-css`, and `tokens-dtcg-json`.
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/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,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 readFile(path, "utf8"));
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 mkdir(dirname(path), { recursive: true });
264
- await writeFile(path, contents);
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("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({
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.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
- "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
+ }