@clafoutis/cli 1.1.2 → 1.2.0
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/dist/index.js +147 -37
- package/dist/index.js.map +1 -1
- package/dist/templates/tokens/colors/components.dark.json +608 -152
- package/dist/templates/tokens/colors/components.json +608 -152
- package/dist/templates/tokens/colors/semantics.dark.json +220 -55
- package/dist/templates/tokens/colors/semantics.json +220 -55
- package/dist/templates/tokens/dimensions/base.json +0 -2
- package/dist/templates/tokens/typography/base.json +0 -5
- package/package.json +1 -1
- package/src/templates/tokens/colors/components.dark.json +608 -152
- package/src/templates/tokens/colors/components.json +608 -152
- package/src/templates/tokens/colors/semantics.dark.json +220 -55
- package/src/templates/tokens/colors/semantics.json +220 -55
- package/src/templates/tokens/dimensions/base.json +0 -2
- package/src/templates/tokens/typography/base.json +0 -5
package/dist/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as p2 from '@clack/prompts';
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path2 from 'path';
|
|
6
|
+
import { logger } from '@clafoutis/shared';
|
|
4
7
|
import { generate } from '@clafoutis/generators/figma';
|
|
5
8
|
import { generate as generate$1 } from '@clafoutis/generators/tailwind';
|
|
6
|
-
import { logger } from '@clafoutis/shared';
|
|
7
|
-
import path from 'path';
|
|
8
9
|
import StyleDictionary from 'style-dictionary';
|
|
9
10
|
import { register } from 'tsx/esm/api';
|
|
10
11
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
11
12
|
import { Ajv } from 'ajv';
|
|
12
|
-
import
|
|
13
|
-
import fs2 from 'fs';
|
|
13
|
+
import fs2 from 'fs/promises';
|
|
14
14
|
import { spawn } from 'child_process';
|
|
15
15
|
|
|
16
16
|
// src/utils/errors.ts
|
|
@@ -88,7 +88,104 @@ function tokensDirNotFoundError(tokensDir) {
|
|
|
88
88
|
);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
// src/
|
|
91
|
+
// src/commands/format.ts
|
|
92
|
+
function loadTokenFiles(dirPath) {
|
|
93
|
+
const files = [];
|
|
94
|
+
function walk(dir, prefix) {
|
|
95
|
+
if (!fs.existsSync(dir)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
99
|
+
const fullPath = path2.join(dir, entry.name);
|
|
100
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
101
|
+
if (entry.isDirectory()) {
|
|
102
|
+
walk(fullPath, relativePath);
|
|
103
|
+
} else if (entry.name.endsWith(".json")) {
|
|
104
|
+
try {
|
|
105
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
106
|
+
files.push({ relativePath, fullPath, content });
|
|
107
|
+
} catch (err) {
|
|
108
|
+
logger.warn(
|
|
109
|
+
`Failed to read ${relativePath}: ${err instanceof Error ? err.message : String(err)}`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
walk(dirPath, "");
|
|
116
|
+
return files;
|
|
117
|
+
}
|
|
118
|
+
function formatJson(content) {
|
|
119
|
+
const parsed = JSON.parse(content);
|
|
120
|
+
return JSON.stringify(parsed, null, 2) + "\n";
|
|
121
|
+
}
|
|
122
|
+
function formatCommand(options) {
|
|
123
|
+
const tokensDir = options.tokens || "./tokens";
|
|
124
|
+
if (!fs.existsSync(tokensDir)) {
|
|
125
|
+
throw tokensDirNotFoundError(tokensDir);
|
|
126
|
+
}
|
|
127
|
+
logger.info(`Formatting token files in ${tokensDir}...`);
|
|
128
|
+
const files = loadTokenFiles(tokensDir);
|
|
129
|
+
const fileCount = files.length;
|
|
130
|
+
if (fileCount === 0) {
|
|
131
|
+
logger.warn(`No JSON files found in ${tokensDir}`);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
let changedCount = 0;
|
|
135
|
+
const unformattedFiles = [];
|
|
136
|
+
for (const { relativePath, fullPath, content } of files) {
|
|
137
|
+
try {
|
|
138
|
+
const formatted = formatJson(content);
|
|
139
|
+
if (content !== formatted) {
|
|
140
|
+
if (options.check) {
|
|
141
|
+
unformattedFiles.push(relativePath);
|
|
142
|
+
changedCount++;
|
|
143
|
+
} else if (options.dryRun) {
|
|
144
|
+
logger.info(`Would format: ${relativePath}`);
|
|
145
|
+
changedCount++;
|
|
146
|
+
} else {
|
|
147
|
+
fs.writeFileSync(fullPath, formatted, "utf-8");
|
|
148
|
+
logger.info(`Formatted: ${relativePath}`);
|
|
149
|
+
changedCount++;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
} catch (err) {
|
|
153
|
+
logger.error(
|
|
154
|
+
`Failed to format ${relativePath}: ${err instanceof Error ? err.message : String(err)}`
|
|
155
|
+
);
|
|
156
|
+
throw err;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (options.check) {
|
|
160
|
+
if (changedCount > 0) {
|
|
161
|
+
logger.error(
|
|
162
|
+
`${changedCount} of ${fileCount} files are not formatted correctly:`
|
|
163
|
+
);
|
|
164
|
+
for (const file of unformattedFiles) {
|
|
165
|
+
logger.error(` - ${file}`);
|
|
166
|
+
}
|
|
167
|
+
logger.info(
|
|
168
|
+
`Run 'npx clafoutis format --tokens ${tokensDir}' to fix formatting.`
|
|
169
|
+
);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
logger.success(`All ${fileCount} files are correctly formatted`);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (options.dryRun) {
|
|
176
|
+
if (changedCount > 0) {
|
|
177
|
+
logger.info(`Would format ${changedCount} of ${fileCount} files`);
|
|
178
|
+
} else {
|
|
179
|
+
logger.info(`All ${fileCount} files are already formatted`);
|
|
180
|
+
}
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (changedCount > 0) {
|
|
184
|
+
logger.success(`Formatted ${changedCount} of ${fileCount} files`);
|
|
185
|
+
} else {
|
|
186
|
+
logger.success(`All ${fileCount} files are already formatted`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
92
189
|
function validateRepo(value) {
|
|
93
190
|
if (!value) {
|
|
94
191
|
return "Repository is required";
|
|
@@ -163,8 +260,8 @@ function findUnknownFields(config, schemaProperties, prefix = "") {
|
|
|
163
260
|
}
|
|
164
261
|
return unknown;
|
|
165
262
|
}
|
|
166
|
-
function hasField(config,
|
|
167
|
-
const parts =
|
|
263
|
+
function hasField(config, path6) {
|
|
264
|
+
const parts = path6.split(".");
|
|
168
265
|
let current = config;
|
|
169
266
|
for (const part of parts) {
|
|
170
267
|
if (current && typeof current === "object" && part in current) {
|
|
@@ -378,7 +475,7 @@ var log3 = {
|
|
|
378
475
|
};
|
|
379
476
|
async function readConfig(configPath) {
|
|
380
477
|
try {
|
|
381
|
-
const content = await
|
|
478
|
+
const content = await fs2.readFile(configPath, "utf-8");
|
|
382
479
|
return JSON.parse(content);
|
|
383
480
|
} catch {
|
|
384
481
|
return null;
|
|
@@ -386,7 +483,7 @@ async function readConfig(configPath) {
|
|
|
386
483
|
}
|
|
387
484
|
async function readProducerConfig(configPath) {
|
|
388
485
|
try {
|
|
389
|
-
const content = await
|
|
486
|
+
const content = await fs2.readFile(configPath, "utf-8");
|
|
390
487
|
return JSON.parse(content);
|
|
391
488
|
} catch {
|
|
392
489
|
return null;
|
|
@@ -394,7 +491,7 @@ async function readProducerConfig(configPath) {
|
|
|
394
491
|
}
|
|
395
492
|
async function fileExists(filePath) {
|
|
396
493
|
try {
|
|
397
|
-
await
|
|
494
|
+
await fs2.access(filePath);
|
|
398
495
|
return true;
|
|
399
496
|
} catch {
|
|
400
497
|
return false;
|
|
@@ -493,12 +590,12 @@ function getConsumerGitignore() {
|
|
|
493
590
|
`;
|
|
494
591
|
}
|
|
495
592
|
var __filename$1 = fileURLToPath(import.meta.url);
|
|
496
|
-
var __dirname$1 =
|
|
593
|
+
var __dirname$1 = path2.dirname(__filename$1);
|
|
497
594
|
function getTokensDir() {
|
|
498
|
-
const devPath =
|
|
499
|
-
if (
|
|
500
|
-
const distPath =
|
|
501
|
-
if (
|
|
595
|
+
const devPath = path2.resolve(__dirname$1, "tokens");
|
|
596
|
+
if (fs.existsSync(devPath)) return devPath;
|
|
597
|
+
const distPath = path2.resolve(__dirname$1, "templates", "tokens");
|
|
598
|
+
if (fs.existsSync(distPath)) return distPath;
|
|
502
599
|
throw new Error(
|
|
503
600
|
`Starter token templates not found. Searched:
|
|
504
601
|
${devPath}
|
|
@@ -507,15 +604,15 @@ function getTokensDir() {
|
|
|
507
604
|
}
|
|
508
605
|
function walkTokensDir(dir, base = "") {
|
|
509
606
|
const result = [];
|
|
510
|
-
for (const entry of
|
|
511
|
-
const relPath = base ?
|
|
512
|
-
const fullPath =
|
|
607
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
608
|
+
const relPath = base ? path2.join(base, entry.name) : entry.name;
|
|
609
|
+
const fullPath = path2.join(dir, entry.name);
|
|
513
610
|
if (entry.isDirectory()) {
|
|
514
611
|
result.push(...walkTokensDir(fullPath, relPath));
|
|
515
612
|
} else if (entry.name.endsWith(".json")) {
|
|
516
613
|
result.push({
|
|
517
614
|
path: relPath,
|
|
518
|
-
content:
|
|
615
|
+
content: fs.readFileSync(fullPath, "utf-8")
|
|
519
616
|
});
|
|
520
617
|
}
|
|
521
618
|
}
|
|
@@ -554,6 +651,9 @@ jobs:
|
|
|
554
651
|
- name: Install Clafoutis
|
|
555
652
|
run: npm install -D @clafoutis/cli
|
|
556
653
|
|
|
654
|
+
- name: Check token file formatting
|
|
655
|
+
run: npx clafoutis format --tokens tokens --check
|
|
656
|
+
|
|
557
657
|
- name: Generate tokens
|
|
558
658
|
run: npx clafoutis generate
|
|
559
659
|
|
|
@@ -796,7 +896,7 @@ async function createProducerConfig(answers, force, dryRun) {
|
|
|
796
896
|
];
|
|
797
897
|
const starterTokens = getAllStarterTokens();
|
|
798
898
|
for (const token of starterTokens) {
|
|
799
|
-
const tokenPath =
|
|
899
|
+
const tokenPath = path2.join(answers.tokens, token.path);
|
|
800
900
|
if (!force && await fileExists(tokenPath)) {
|
|
801
901
|
continue;
|
|
802
902
|
}
|
|
@@ -854,7 +954,7 @@ async function createConsumerConfig(answers, force, dryRun) {
|
|
|
854
954
|
const gitignorePath = ".gitignore";
|
|
855
955
|
const consumerIgnore = getConsumerGitignore();
|
|
856
956
|
if (await fileExists(gitignorePath)) {
|
|
857
|
-
const existingContent = await
|
|
957
|
+
const existingContent = await fs2.readFile(gitignorePath, "utf-8");
|
|
858
958
|
if (!existingContent.includes(".clafoutis/cache")) {
|
|
859
959
|
filesToCreate.push({
|
|
860
960
|
path: gitignorePath,
|
|
@@ -890,9 +990,9 @@ function showDryRunOutput(files) {
|
|
|
890
990
|
}
|
|
891
991
|
async function writeFiles(files) {
|
|
892
992
|
for (const file of files) {
|
|
893
|
-
const dir =
|
|
894
|
-
await
|
|
895
|
-
await
|
|
993
|
+
const dir = path2.dirname(file.path);
|
|
994
|
+
await fs2.mkdir(dir, { recursive: true });
|
|
995
|
+
await fs2.writeFile(file.path, file.content);
|
|
896
996
|
log3.success(`Created ${file.path}`);
|
|
897
997
|
}
|
|
898
998
|
}
|
|
@@ -904,8 +1004,14 @@ function showNextSteps(mode, answers) {
|
|
|
904
1004
|
log3.message(
|
|
905
1005
|
` 1. Edit ${producerAnswers.tokens}/colors/primitives.json with your design tokens`
|
|
906
1006
|
);
|
|
907
|
-
|
|
908
|
-
|
|
1007
|
+
if (producerAnswers.workflow) {
|
|
1008
|
+
log3.message(
|
|
1009
|
+
" 2. Push to GitHub - the workflow will generate outputs and create a release automatically"
|
|
1010
|
+
);
|
|
1011
|
+
} else {
|
|
1012
|
+
log3.message(" 2. Run: npx clafoutis generate");
|
|
1013
|
+
log3.message(" 3. Push to GitHub to share your design system");
|
|
1014
|
+
}
|
|
909
1015
|
} else {
|
|
910
1016
|
log3.message(" 1. Run: npx clafoutis sync");
|
|
911
1017
|
}
|
|
@@ -913,7 +1019,7 @@ function showNextSteps(mode, answers) {
|
|
|
913
1019
|
|
|
914
1020
|
// src/commands/generate.ts
|
|
915
1021
|
async function loadPlugin(pluginPath) {
|
|
916
|
-
const absolutePath =
|
|
1022
|
+
const absolutePath = path2.resolve(process.cwd(), pluginPath);
|
|
917
1023
|
if (pluginPath.endsWith(".ts")) {
|
|
918
1024
|
register();
|
|
919
1025
|
}
|
|
@@ -959,8 +1065,8 @@ async function generateCommand(options) {
|
|
|
959
1065
|
if (options.output) {
|
|
960
1066
|
config.output = options.output;
|
|
961
1067
|
}
|
|
962
|
-
const tokensDir =
|
|
963
|
-
const outputDir =
|
|
1068
|
+
const tokensDir = path2.resolve(process.cwd(), config.tokens || "./tokens");
|
|
1069
|
+
const outputDir = path2.resolve(process.cwd(), config.output || "./build");
|
|
964
1070
|
if (!await fileExists(tokensDir)) {
|
|
965
1071
|
throw tokensDirNotFoundError(tokensDir);
|
|
966
1072
|
}
|
|
@@ -1009,7 +1115,7 @@ async function generateCommand(options) {
|
|
|
1009
1115
|
}
|
|
1010
1116
|
const context = {
|
|
1011
1117
|
tokensDir,
|
|
1012
|
-
outputDir:
|
|
1118
|
+
outputDir: path2.join(outputDir, name),
|
|
1013
1119
|
config,
|
|
1014
1120
|
StyleDictionary
|
|
1015
1121
|
};
|
|
@@ -1036,7 +1142,7 @@ var CACHE_DIR = ".clafoutis";
|
|
|
1036
1142
|
var CACHE_FILE = `${CACHE_DIR}/cache`;
|
|
1037
1143
|
async function readCache() {
|
|
1038
1144
|
try {
|
|
1039
|
-
return (await
|
|
1145
|
+
return (await fs2.readFile(CACHE_FILE, "utf-8")).trim();
|
|
1040
1146
|
} catch (err) {
|
|
1041
1147
|
if (err instanceof Error && err.code === "ENOENT") {
|
|
1042
1148
|
return null;
|
|
@@ -1045,8 +1151,8 @@ async function readCache() {
|
|
|
1045
1151
|
}
|
|
1046
1152
|
}
|
|
1047
1153
|
async function writeCache(version) {
|
|
1048
|
-
await
|
|
1049
|
-
await
|
|
1154
|
+
await fs2.mkdir(CACHE_DIR, { recursive: true });
|
|
1155
|
+
await fs2.writeFile(CACHE_FILE, version);
|
|
1050
1156
|
}
|
|
1051
1157
|
async function downloadRelease(config) {
|
|
1052
1158
|
const token = process.env.CLAFOUTIS_REPO_TOKEN;
|
|
@@ -1116,9 +1222,9 @@ async function writeOutput(config, files) {
|
|
|
1116
1222
|
for (const [assetName, content] of files) {
|
|
1117
1223
|
const configPath = config.files[assetName];
|
|
1118
1224
|
if (!configPath) continue;
|
|
1119
|
-
const outputPath =
|
|
1120
|
-
await
|
|
1121
|
-
await
|
|
1225
|
+
const outputPath = path2.resolve(process.cwd(), configPath);
|
|
1226
|
+
await fs2.mkdir(path2.dirname(outputPath), { recursive: true });
|
|
1227
|
+
await fs2.writeFile(outputPath, content);
|
|
1122
1228
|
logger.success(`Written: ${outputPath}`);
|
|
1123
1229
|
}
|
|
1124
1230
|
}
|
|
@@ -1163,7 +1269,7 @@ async function syncCommand(options) {
|
|
|
1163
1269
|
}
|
|
1164
1270
|
return;
|
|
1165
1271
|
}
|
|
1166
|
-
const resolveOutputPaths = () => Object.values(config.files).map((p5) =>
|
|
1272
|
+
const resolveOutputPaths = () => Object.values(config.files).map((p5) => path2.resolve(process.cwd(), p5));
|
|
1167
1273
|
if (!isLatest && !options.force && config.version === cachedVersion) {
|
|
1168
1274
|
const outputPaths = resolveOutputPaths();
|
|
1169
1275
|
const existsResults = await Promise.all(
|
|
@@ -1313,6 +1419,10 @@ program.command("init").description("Initialize Clafoutis configuration").option
|
|
|
1313
1419
|
"--files <mapping>",
|
|
1314
1420
|
"File mappings for consumer: asset:dest,asset:dest"
|
|
1315
1421
|
).option("--force", "Overwrite existing configuration").option("--dry-run", "Preview changes without writing files").option("--non-interactive", "Skip prompts, use defaults or flags").action(withErrorHandling(initCommand));
|
|
1422
|
+
program.command("format").description("Format token JSON files for consistent formatting").option("-t, --tokens <path>", "Token directory path", "./tokens").option(
|
|
1423
|
+
"--check",
|
|
1424
|
+
"Check formatting without modifying files (fails if unformatted)"
|
|
1425
|
+
).option("--dry-run", "Preview changes without writing files").action(withErrorHandling(formatCommand));
|
|
1316
1426
|
program.parse();
|
|
1317
1427
|
//# sourceMappingURL=index.js.map
|
|
1318
1428
|
//# sourceMappingURL=index.js.map
|