tokvista 1.7.1 → 1.8.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 +13 -0
- package/dist/bin/tokvista.js +134 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,6 +119,18 @@ npx tokvista validate tokens.json
|
|
|
119
119
|
npm run validate-tokens
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
+
### Compare Tokens
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Compare two token files
|
|
126
|
+
npx tokvista diff tokens-v1.json tokens-v2.json
|
|
127
|
+
|
|
128
|
+
# Perfect for:
|
|
129
|
+
# - Version control reviews
|
|
130
|
+
# - Release changelogs
|
|
131
|
+
# - Migration tracking
|
|
132
|
+
```
|
|
133
|
+
|
|
122
134
|
### Interactive Setup
|
|
123
135
|
|
|
124
136
|
```bash
|
|
@@ -149,6 +161,7 @@ Then run `npx tokvista` to use your config.
|
|
|
149
161
|
| `tokvista init` | Interactive config setup |
|
|
150
162
|
| `tokvista export <file> --format <type>` | Export tokens (css, scss, json, tailwind) |
|
|
151
163
|
| `tokvista validate <file>` | Validate token structure and values |
|
|
164
|
+
| `tokvista diff <old> <new>` | Compare two token files |
|
|
152
165
|
| `--config`, `-c` | Config file path |
|
|
153
166
|
| `--port`, `-p` | Server port (default: `3000`) |
|
|
154
167
|
| `--format` | Export format (export only) |
|
package/dist/bin/tokvista.js
CHANGED
|
@@ -935,6 +935,66 @@ function validateTokens(tokens) {
|
|
|
935
935
|
};
|
|
936
936
|
}
|
|
937
937
|
|
|
938
|
+
// src/bin/differ.ts
|
|
939
|
+
function isRecord5(value) {
|
|
940
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
941
|
+
}
|
|
942
|
+
function isTokenLike3(obj) {
|
|
943
|
+
return isRecord5(obj) && "value" in obj;
|
|
944
|
+
}
|
|
945
|
+
function flattenTokens(tokens) {
|
|
946
|
+
const flat = /* @__PURE__ */ new Map();
|
|
947
|
+
function walk(node, path2 = []) {
|
|
948
|
+
if (!isRecord5(node)) return;
|
|
949
|
+
if (path2.length === 0 && Object.keys(node).some((k) => k.includes("/"))) {
|
|
950
|
+
Object.values(node).forEach((val) => walk(val, []));
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
if (isTokenLike3(node)) {
|
|
954
|
+
flat.set(path2.join("."), String(node.value));
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
Object.entries(node).forEach(([key, val]) => {
|
|
958
|
+
walk(val, [...path2, key]);
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
walk(tokens);
|
|
962
|
+
return flat;
|
|
963
|
+
}
|
|
964
|
+
function diffTokens(oldTokens, newTokens) {
|
|
965
|
+
const oldDetection = detectTokenFormat(oldTokens);
|
|
966
|
+
const newDetection = detectTokenFormat(newTokens);
|
|
967
|
+
let normalizedOld = oldTokens;
|
|
968
|
+
let normalizedNew = newTokens;
|
|
969
|
+
if (oldDetection.format !== "token-studio" && oldDetection.format !== "unknown") {
|
|
970
|
+
normalizedOld = normalizeTokenFormat(oldTokens, oldDetection.format);
|
|
971
|
+
}
|
|
972
|
+
if (newDetection.format !== "token-studio" && newDetection.format !== "unknown") {
|
|
973
|
+
normalizedNew = normalizeTokenFormat(newTokens, newDetection.format);
|
|
974
|
+
}
|
|
975
|
+
const oldFlat = flattenTokens(normalizedOld);
|
|
976
|
+
const newFlat = flattenTokens(normalizedNew);
|
|
977
|
+
const added = [];
|
|
978
|
+
const removed = [];
|
|
979
|
+
const modified = [];
|
|
980
|
+
let unchanged = 0;
|
|
981
|
+
newFlat.forEach((newValue, path2) => {
|
|
982
|
+
if (!oldFlat.has(path2)) {
|
|
983
|
+
added.push(path2);
|
|
984
|
+
} else if (oldFlat.get(path2) !== newValue) {
|
|
985
|
+
modified.push({ path: path2, oldValue: oldFlat.get(path2), newValue });
|
|
986
|
+
} else {
|
|
987
|
+
unchanged++;
|
|
988
|
+
}
|
|
989
|
+
});
|
|
990
|
+
oldFlat.forEach((_, path2) => {
|
|
991
|
+
if (!newFlat.has(path2)) {
|
|
992
|
+
removed.push(path2);
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
return { added, removed, modified, unchanged };
|
|
996
|
+
}
|
|
997
|
+
|
|
938
998
|
// src/bin/watcher.ts
|
|
939
999
|
import { watch } from "fs";
|
|
940
1000
|
function watchFile(filePath, onChange) {
|
|
@@ -967,6 +1027,7 @@ Usage:
|
|
|
967
1027
|
tokvista init [--force] [--port 3000] [--no-open] [--no-preview]
|
|
968
1028
|
tokvista export <tokens.json> --format <css|scss|json|tailwind> [--output <file>]
|
|
969
1029
|
tokvista validate <tokens.json>
|
|
1030
|
+
tokvista diff <old.json> <new.json>
|
|
970
1031
|
|
|
971
1032
|
Arguments:
|
|
972
1033
|
tokens.json Path to your tokens file (overrides config.tokens)
|
|
@@ -1096,6 +1157,9 @@ function parseArgs(args) {
|
|
|
1096
1157
|
if (args[0] === "validate") {
|
|
1097
1158
|
return parseValidateArgs(args.slice(1));
|
|
1098
1159
|
}
|
|
1160
|
+
if (args[0] === "diff") {
|
|
1161
|
+
return parseDiffArgs(args.slice(1));
|
|
1162
|
+
}
|
|
1099
1163
|
return parseServeArgs(args);
|
|
1100
1164
|
}
|
|
1101
1165
|
function parseValidateArgs(args) {
|
|
@@ -1117,6 +1181,30 @@ function parseValidateArgs(args) {
|
|
|
1117
1181
|
if (!tokenFileArg) throw new Error("Token file is required for validate");
|
|
1118
1182
|
return { command: "validate", tokenFileArg };
|
|
1119
1183
|
}
|
|
1184
|
+
function parseDiffArgs(args) {
|
|
1185
|
+
let oldFileArg;
|
|
1186
|
+
let newFileArg;
|
|
1187
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1188
|
+
const arg = args[index];
|
|
1189
|
+
if (arg === "-h" || arg === "--help") {
|
|
1190
|
+
printHelp();
|
|
1191
|
+
process.exit(0);
|
|
1192
|
+
}
|
|
1193
|
+
if (arg.startsWith("-")) {
|
|
1194
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
1195
|
+
}
|
|
1196
|
+
if (!oldFileArg) {
|
|
1197
|
+
oldFileArg = arg;
|
|
1198
|
+
} else if (!newFileArg) {
|
|
1199
|
+
newFileArg = arg;
|
|
1200
|
+
} else {
|
|
1201
|
+
throw new Error(`Too many arguments. Expected: tokvista diff <old.json> <new.json>`);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
if (!oldFileArg) throw new Error("Old token file is required");
|
|
1205
|
+
if (!newFileArg) throw new Error("New token file is required");
|
|
1206
|
+
return { command: "diff", oldFileArg, newFileArg };
|
|
1207
|
+
}
|
|
1120
1208
|
function parseExportArgs(args) {
|
|
1121
1209
|
let tokenFileArg;
|
|
1122
1210
|
let format;
|
|
@@ -1825,6 +1913,48 @@ Validating ${resolvedTokenPath}...
|
|
|
1825
1913
|
process.exit(1);
|
|
1826
1914
|
}
|
|
1827
1915
|
}
|
|
1916
|
+
async function runDiffCommand(cwd, options) {
|
|
1917
|
+
const oldPath = path.resolve(cwd, options.oldFileArg);
|
|
1918
|
+
const newPath = path.resolve(cwd, options.newFileArg);
|
|
1919
|
+
if (!existsSync(oldPath)) {
|
|
1920
|
+
throw new Error(`Old token file not found: ${oldPath}`);
|
|
1921
|
+
}
|
|
1922
|
+
if (!existsSync(newPath)) {
|
|
1923
|
+
throw new Error(`New token file not found: ${newPath}`);
|
|
1924
|
+
}
|
|
1925
|
+
const [oldTokens, newTokens] = await Promise.all([
|
|
1926
|
+
readTokens(oldPath),
|
|
1927
|
+
readTokens(newPath)
|
|
1928
|
+
]);
|
|
1929
|
+
const diff = diffTokens(oldTokens, newTokens);
|
|
1930
|
+
console.log(`
|
|
1931
|
+
Comparing tokens:
|
|
1932
|
+
Old: ${oldPath}
|
|
1933
|
+
New: ${newPath}
|
|
1934
|
+
`);
|
|
1935
|
+
if (diff.added.length > 0) {
|
|
1936
|
+
console.log(`\u2705 Added (${diff.added.length}):`);
|
|
1937
|
+
diff.added.forEach((path2) => console.log(` + ${path2}`));
|
|
1938
|
+
console.log("");
|
|
1939
|
+
}
|
|
1940
|
+
if (diff.removed.length > 0) {
|
|
1941
|
+
console.log(`\u274C Removed (${diff.removed.length}):`);
|
|
1942
|
+
diff.removed.forEach((path2) => console.log(` - ${path2}`));
|
|
1943
|
+
console.log("");
|
|
1944
|
+
}
|
|
1945
|
+
if (diff.modified.length > 0) {
|
|
1946
|
+
console.log(`\u{1F504} Modified (${diff.modified.length}):`);
|
|
1947
|
+
diff.modified.forEach(({ path: path2, oldValue, newValue }) => {
|
|
1948
|
+
console.log(` ~ ${path2}`);
|
|
1949
|
+
console.log(` - ${oldValue}`);
|
|
1950
|
+
console.log(` + ${newValue}`);
|
|
1951
|
+
});
|
|
1952
|
+
console.log("");
|
|
1953
|
+
}
|
|
1954
|
+
console.log(`Unchanged: ${diff.unchanged}`);
|
|
1955
|
+
console.log(`Total changes: ${diff.added.length + diff.removed.length + diff.modified.length}
|
|
1956
|
+
`);
|
|
1957
|
+
}
|
|
1828
1958
|
async function main() {
|
|
1829
1959
|
try {
|
|
1830
1960
|
const options = parseArgs(process.argv.slice(2));
|
|
@@ -1844,6 +1974,10 @@ async function main() {
|
|
|
1844
1974
|
await runValidateCommand(cwd, options);
|
|
1845
1975
|
return;
|
|
1846
1976
|
}
|
|
1977
|
+
if (options.command === "diff") {
|
|
1978
|
+
await runDiffCommand(cwd, options);
|
|
1979
|
+
return;
|
|
1980
|
+
}
|
|
1847
1981
|
await runServeCommand(cwd, options);
|
|
1848
1982
|
} catch (error) {
|
|
1849
1983
|
console.error(error.message);
|