tokvista 1.8.0 → 1.9.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/README.md +17 -0
- package/dist/bin/tokvista.js +174 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -131,6 +131,22 @@ npx tokvista diff tokens-v1.json tokens-v2.json
|
|
|
131
131
|
# - Migration tracking
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
+
### Convert Formats
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# Convert to W3C DTCG format
|
|
138
|
+
npx tokvista convert tokens.json --to w3c --output tokens-w3c.json
|
|
139
|
+
|
|
140
|
+
# Convert to Style Dictionary
|
|
141
|
+
npx tokvista convert tokens.json --to style-dictionary --output tokens-sd.json
|
|
142
|
+
|
|
143
|
+
# Convert to Supernova array format
|
|
144
|
+
npx tokvista convert tokens.json --to supernova --output tokens-sn.json
|
|
145
|
+
|
|
146
|
+
# Print to stdout
|
|
147
|
+
npx tokvista convert tokens.json --to w3c
|
|
148
|
+
```
|
|
149
|
+
|
|
134
150
|
### Interactive Setup
|
|
135
151
|
|
|
136
152
|
```bash
|
|
@@ -162,6 +178,7 @@ Then run `npx tokvista` to use your config.
|
|
|
162
178
|
| `tokvista export <file> --format <type>` | Export tokens (css, scss, json, tailwind) |
|
|
163
179
|
| `tokvista validate <file>` | Validate token structure and values |
|
|
164
180
|
| `tokvista diff <old> <new>` | Compare two token files |
|
|
181
|
+
| `tokvista convert <file> --to <format>` | Convert between token formats |
|
|
165
182
|
| `--config`, `-c` | Config file path |
|
|
166
183
|
| `--port`, `-p` | Server port (default: `3000`) |
|
|
167
184
|
| `--format` | Export format (export only) |
|
package/dist/bin/tokvista.js
CHANGED
|
@@ -995,6 +995,99 @@ function diffTokens(oldTokens, newTokens) {
|
|
|
995
995
|
return { added, removed, modified, unchanged };
|
|
996
996
|
}
|
|
997
997
|
|
|
998
|
+
// src/bin/converter.ts
|
|
999
|
+
function isRecord6(value) {
|
|
1000
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1001
|
+
}
|
|
1002
|
+
function isTokenLike4(obj) {
|
|
1003
|
+
return isRecord6(obj) && "value" in obj;
|
|
1004
|
+
}
|
|
1005
|
+
function toW3C(tokens) {
|
|
1006
|
+
function walk(node) {
|
|
1007
|
+
if (!isRecord6(node)) return node;
|
|
1008
|
+
if (Object.keys(node).some((k) => k.includes("/"))) {
|
|
1009
|
+
const result2 = {};
|
|
1010
|
+
Object.entries(node).forEach(([key, val]) => {
|
|
1011
|
+
Object.assign(result2, walk(val));
|
|
1012
|
+
});
|
|
1013
|
+
return result2;
|
|
1014
|
+
}
|
|
1015
|
+
if (isTokenLike4(node)) {
|
|
1016
|
+
return {
|
|
1017
|
+
$value: node.value,
|
|
1018
|
+
...node.type ? { $type: node.type } : {}
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
const result = {};
|
|
1022
|
+
Object.entries(node).forEach(([key, val]) => {
|
|
1023
|
+
result[key] = walk(val);
|
|
1024
|
+
});
|
|
1025
|
+
return result;
|
|
1026
|
+
}
|
|
1027
|
+
return walk(tokens);
|
|
1028
|
+
}
|
|
1029
|
+
function toStyleDictionary(tokens) {
|
|
1030
|
+
function walk(node) {
|
|
1031
|
+
if (!isRecord6(node)) return node;
|
|
1032
|
+
if (Object.keys(node).some((k) => k.includes("/"))) {
|
|
1033
|
+
const result2 = {};
|
|
1034
|
+
Object.entries(node).forEach(([key, val]) => {
|
|
1035
|
+
Object.assign(result2, walk(val));
|
|
1036
|
+
});
|
|
1037
|
+
return result2;
|
|
1038
|
+
}
|
|
1039
|
+
if (isTokenLike4(node)) {
|
|
1040
|
+
return node;
|
|
1041
|
+
}
|
|
1042
|
+
const result = {};
|
|
1043
|
+
Object.entries(node).forEach(([key, val]) => {
|
|
1044
|
+
result[key] = walk(val);
|
|
1045
|
+
});
|
|
1046
|
+
return result;
|
|
1047
|
+
}
|
|
1048
|
+
return walk(tokens);
|
|
1049
|
+
}
|
|
1050
|
+
function toSupernova(tokens) {
|
|
1051
|
+
const result = [];
|
|
1052
|
+
let idCounter = 1;
|
|
1053
|
+
function walk(node, path2 = []) {
|
|
1054
|
+
if (!isRecord6(node)) return;
|
|
1055
|
+
if (path2.length === 0 && Object.keys(node).some((k) => k.includes("/"))) {
|
|
1056
|
+
Object.values(node).forEach((val) => walk(val, []));
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
if (isTokenLike4(node)) {
|
|
1060
|
+
result.push({
|
|
1061
|
+
id: String(idCounter++),
|
|
1062
|
+
name: path2[path2.length - 1] || "token",
|
|
1063
|
+
tokenType: node.type || "unknown",
|
|
1064
|
+
value: node.value
|
|
1065
|
+
});
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
Object.entries(node).forEach(([key, val]) => {
|
|
1069
|
+
walk(val, [...path2, key]);
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
walk(tokens);
|
|
1073
|
+
return result;
|
|
1074
|
+
}
|
|
1075
|
+
function convertTokenFormat(tokens, targetFormat) {
|
|
1076
|
+
if (targetFormat === "token-studio") {
|
|
1077
|
+
return tokens;
|
|
1078
|
+
}
|
|
1079
|
+
switch (targetFormat) {
|
|
1080
|
+
case "w3c":
|
|
1081
|
+
return toW3C(tokens);
|
|
1082
|
+
case "style-dictionary":
|
|
1083
|
+
return toStyleDictionary(tokens);
|
|
1084
|
+
case "supernova":
|
|
1085
|
+
return toSupernova(tokens);
|
|
1086
|
+
default:
|
|
1087
|
+
throw new Error(`Unsupported target format: ${targetFormat}`);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
998
1091
|
// src/bin/watcher.ts
|
|
999
1092
|
import { watch } from "fs";
|
|
1000
1093
|
function watchFile(filePath, onChange) {
|
|
@@ -1028,6 +1121,7 @@ Usage:
|
|
|
1028
1121
|
tokvista export <tokens.json> --format <css|scss|json|tailwind> [--output <file>]
|
|
1029
1122
|
tokvista validate <tokens.json>
|
|
1030
1123
|
tokvista diff <old.json> <new.json>
|
|
1124
|
+
tokvista convert <tokens.json> --to <w3c|style-dictionary|supernova> [--output <file>]
|
|
1031
1125
|
|
|
1032
1126
|
Arguments:
|
|
1033
1127
|
tokens.json Path to your tokens file (overrides config.tokens)
|
|
@@ -1160,6 +1254,9 @@ function parseArgs(args) {
|
|
|
1160
1254
|
if (args[0] === "diff") {
|
|
1161
1255
|
return parseDiffArgs(args.slice(1));
|
|
1162
1256
|
}
|
|
1257
|
+
if (args[0] === "convert") {
|
|
1258
|
+
return parseConvertArgs(args.slice(1));
|
|
1259
|
+
}
|
|
1163
1260
|
return parseServeArgs(args);
|
|
1164
1261
|
}
|
|
1165
1262
|
function parseValidateArgs(args) {
|
|
@@ -1205,6 +1302,57 @@ function parseDiffArgs(args) {
|
|
|
1205
1302
|
if (!newFileArg) throw new Error("New token file is required");
|
|
1206
1303
|
return { command: "diff", oldFileArg, newFileArg };
|
|
1207
1304
|
}
|
|
1305
|
+
function parseConvertArgs(args) {
|
|
1306
|
+
let tokenFileArg;
|
|
1307
|
+
let to;
|
|
1308
|
+
let output;
|
|
1309
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1310
|
+
const arg = args[index];
|
|
1311
|
+
if (arg === "-h" || arg === "--help") {
|
|
1312
|
+
printHelp();
|
|
1313
|
+
process.exit(0);
|
|
1314
|
+
}
|
|
1315
|
+
if (arg === "--to") {
|
|
1316
|
+
const next = args[index + 1];
|
|
1317
|
+
if (!next) throw new Error("Missing value for --to");
|
|
1318
|
+
if (!["w3c", "style-dictionary", "supernova", "token-studio"].includes(next)) {
|
|
1319
|
+
throw new Error("Format must be: w3c, style-dictionary, supernova, or token-studio");
|
|
1320
|
+
}
|
|
1321
|
+
to = next;
|
|
1322
|
+
index += 1;
|
|
1323
|
+
continue;
|
|
1324
|
+
}
|
|
1325
|
+
if (arg === "--output" || arg === "-o") {
|
|
1326
|
+
const next = args[index + 1];
|
|
1327
|
+
if (!next) throw new Error("Missing value for --output");
|
|
1328
|
+
output = next;
|
|
1329
|
+
index += 1;
|
|
1330
|
+
continue;
|
|
1331
|
+
}
|
|
1332
|
+
if (arg.startsWith("--to=")) {
|
|
1333
|
+
const val = arg.slice("--to=".length);
|
|
1334
|
+
if (!["w3c", "style-dictionary", "supernova", "token-studio"].includes(val)) {
|
|
1335
|
+
throw new Error("Format must be: w3c, style-dictionary, supernova, or token-studio");
|
|
1336
|
+
}
|
|
1337
|
+
to = val;
|
|
1338
|
+
continue;
|
|
1339
|
+
}
|
|
1340
|
+
if (arg.startsWith("--output=")) {
|
|
1341
|
+
output = arg.slice("--output=".length);
|
|
1342
|
+
continue;
|
|
1343
|
+
}
|
|
1344
|
+
if (arg.startsWith("-")) {
|
|
1345
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
1346
|
+
}
|
|
1347
|
+
if (tokenFileArg) {
|
|
1348
|
+
throw new Error(`Only one token file is supported. Unexpected value: "${arg}"`);
|
|
1349
|
+
}
|
|
1350
|
+
tokenFileArg = arg;
|
|
1351
|
+
}
|
|
1352
|
+
if (!tokenFileArg) throw new Error("Token file is required for convert");
|
|
1353
|
+
if (!to) throw new Error("--to is required (w3c, style-dictionary, supernova, or token-studio)");
|
|
1354
|
+
return { command: "convert", tokenFileArg, to, output };
|
|
1355
|
+
}
|
|
1208
1356
|
function parseExportArgs(args) {
|
|
1209
1357
|
let tokenFileArg;
|
|
1210
1358
|
let format;
|
|
@@ -1955,6 +2103,28 @@ Comparing tokens:
|
|
|
1955
2103
|
console.log(`Total changes: ${diff.added.length + diff.removed.length + diff.modified.length}
|
|
1956
2104
|
`);
|
|
1957
2105
|
}
|
|
2106
|
+
async function runConvertCommand(cwd, options) {
|
|
2107
|
+
const resolvedTokenPath = path.resolve(cwd, options.tokenFileArg);
|
|
2108
|
+
if (!existsSync(resolvedTokenPath)) {
|
|
2109
|
+
throw new Error(`Token file not found: ${resolvedTokenPath}`);
|
|
2110
|
+
}
|
|
2111
|
+
const tokens = await readTokens(resolvedTokenPath);
|
|
2112
|
+
const detection = detectTokenFormat(tokens);
|
|
2113
|
+
let normalizedTokens = tokens;
|
|
2114
|
+
if (detection.format !== "token-studio" && detection.format !== "unknown") {
|
|
2115
|
+
normalizedTokens = normalizeTokenFormat(tokens, detection.format);
|
|
2116
|
+
}
|
|
2117
|
+
const converted = convertTokenFormat(normalizedTokens, options.to);
|
|
2118
|
+
const output = JSON.stringify(converted, null, 2);
|
|
2119
|
+
if (options.output) {
|
|
2120
|
+
const outputPath = path.resolve(cwd, options.output);
|
|
2121
|
+
await writeFile(outputPath, output, "utf8");
|
|
2122
|
+
console.log(`Converted ${detection.format} \u2192 ${options.to}`);
|
|
2123
|
+
console.log(`Output: ${outputPath}`);
|
|
2124
|
+
} else {
|
|
2125
|
+
console.log(output);
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
1958
2128
|
async function main() {
|
|
1959
2129
|
try {
|
|
1960
2130
|
const options = parseArgs(process.argv.slice(2));
|
|
@@ -1978,6 +2148,10 @@ async function main() {
|
|
|
1978
2148
|
await runDiffCommand(cwd, options);
|
|
1979
2149
|
return;
|
|
1980
2150
|
}
|
|
2151
|
+
if (options.command === "convert") {
|
|
2152
|
+
await runConvertCommand(cwd, options);
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
1981
2155
|
await runServeCommand(cwd, options);
|
|
1982
2156
|
} catch (error) {
|
|
1983
2157
|
console.error(error.message);
|