tokvista 1.5.0 → 1.5.1
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 +2 -0
- package/dist/bin/tokvista.js +104 -11
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ Zero configuration. Multiple formats. One command.
|
|
|
24
24
|
- 🔍 **Instant search** - `Cmd+K` / `Ctrl+K` to find any token
|
|
25
25
|
- 🎯 **Zero config** - Works out of the box with any token format
|
|
26
26
|
- ⚡ **Two modes** - CLI for quick preview or React component for apps
|
|
27
|
+
- 🔥 **Live reload** - Auto-refresh on file changes
|
|
27
28
|
|
|
28
29
|
---
|
|
29
30
|
|
|
@@ -120,6 +121,7 @@ Then run `npx tokvista` to use your config.
|
|
|
120
121
|
| `--config`, `-c` | Config file path |
|
|
121
122
|
| `--port`, `-p` | Server port (default: `3000`) |
|
|
122
123
|
| `--no-open` | Don't open browser |
|
|
124
|
+
| `--no-watch` | Disable live reload |
|
|
123
125
|
| `--no-preview` | Skip preview after init |
|
|
124
126
|
| `--force`, `-f` | Overwrite existing config |
|
|
125
127
|
| `--help`, `-h` | Show help |
|
package/dist/bin/tokvista.js
CHANGED
|
@@ -8,6 +8,70 @@ import { createServer } from "http";
|
|
|
8
8
|
import path from "path";
|
|
9
9
|
import { createInterface } from "readline";
|
|
10
10
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
11
|
+
|
|
12
|
+
// src/bin/hotreload.ts
|
|
13
|
+
import { createHash } from "crypto";
|
|
14
|
+
var HotReloadServer = class {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.clients = /* @__PURE__ */ new Set();
|
|
17
|
+
}
|
|
18
|
+
handleUpgrade(request, socket, head) {
|
|
19
|
+
if (request.url !== "/__tokvista_ws") {
|
|
20
|
+
socket.destroy();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const key = request.headers["sec-websocket-key"];
|
|
24
|
+
if (!key) {
|
|
25
|
+
socket.destroy();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const accept = createHash("sha1").update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");
|
|
29
|
+
socket.write(
|
|
30
|
+
`HTTP/1.1 101 Switching Protocols\r
|
|
31
|
+
Upgrade: websocket\r
|
|
32
|
+
Connection: Upgrade\r
|
|
33
|
+
Sec-WebSocket-Accept: ${accept}\r
|
|
34
|
+
\r
|
|
35
|
+
`
|
|
36
|
+
);
|
|
37
|
+
const client = {
|
|
38
|
+
socket,
|
|
39
|
+
send: (data) => {
|
|
40
|
+
const buffer = Buffer.from(data);
|
|
41
|
+
const frame = Buffer.allocUnsafe(2 + buffer.length);
|
|
42
|
+
frame[0] = 129;
|
|
43
|
+
frame[1] = buffer.length;
|
|
44
|
+
buffer.copy(frame, 2);
|
|
45
|
+
socket.write(frame);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
this.clients.add(client);
|
|
49
|
+
socket.on("close", () => this.clients.delete(client));
|
|
50
|
+
}
|
|
51
|
+
reload() {
|
|
52
|
+
for (const client of this.clients) {
|
|
53
|
+
client.send("reload");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
close() {
|
|
57
|
+
for (const client of this.clients) {
|
|
58
|
+
client.socket.destroy();
|
|
59
|
+
}
|
|
60
|
+
this.clients.clear();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// src/bin/watcher.ts
|
|
65
|
+
import { watch } from "fs";
|
|
66
|
+
function watchFile(filePath, onChange) {
|
|
67
|
+
let debounce = null;
|
|
68
|
+
return watch(filePath, () => {
|
|
69
|
+
if (debounce) clearTimeout(debounce);
|
|
70
|
+
debounce = setTimeout(onChange, 100);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/bin/tokvista.ts
|
|
11
75
|
var DEFAULT_PORT = 3e3;
|
|
12
76
|
var MAX_PORT_ATTEMPTS = 25;
|
|
13
77
|
var SHUTDOWN_TIMEOUT_MS = 1500;
|
|
@@ -52,6 +116,7 @@ function parseServeArgs(args) {
|
|
|
52
116
|
let configPathArg;
|
|
53
117
|
let port = DEFAULT_PORT;
|
|
54
118
|
let openBrowser2 = true;
|
|
119
|
+
let watch2 = true;
|
|
55
120
|
for (let index = 0; index < args.length; index += 1) {
|
|
56
121
|
const arg = args[index];
|
|
57
122
|
if (arg === "-h" || arg === "--help") {
|
|
@@ -62,6 +127,10 @@ function parseServeArgs(args) {
|
|
|
62
127
|
openBrowser2 = false;
|
|
63
128
|
continue;
|
|
64
129
|
}
|
|
130
|
+
if (arg === "--no-watch") {
|
|
131
|
+
watch2 = false;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
65
134
|
if (arg === "--port" || arg === "-p") {
|
|
66
135
|
const next = args[index + 1];
|
|
67
136
|
if (!next) {
|
|
@@ -96,7 +165,7 @@ function parseServeArgs(args) {
|
|
|
96
165
|
}
|
|
97
166
|
tokenFileArg = arg;
|
|
98
167
|
}
|
|
99
|
-
return { command: "serve", tokenFileArg, configPathArg, port, openBrowser: openBrowser2 };
|
|
168
|
+
return { command: "serve", tokenFileArg, configPathArg, port, openBrowser: openBrowser2, watch: watch2 };
|
|
100
169
|
}
|
|
101
170
|
function parseInitArgs(args) {
|
|
102
171
|
let force = false;
|
|
@@ -351,7 +420,8 @@ async function runInitCommand(cwd, options) {
|
|
|
351
420
|
tokenFileArg: void 0,
|
|
352
421
|
configPathArg: configPath,
|
|
353
422
|
port: options.port,
|
|
354
|
-
openBrowser: options.openBrowser
|
|
423
|
+
openBrowser: options.openBrowser,
|
|
424
|
+
watch: true
|
|
355
425
|
};
|
|
356
426
|
}
|
|
357
427
|
function serializeForInlineScript(value) {
|
|
@@ -528,12 +598,13 @@ async function buildRuntimeConfig(config, configPath, cwd) {
|
|
|
528
598
|
...typeof config.showSearch === "boolean" ? { showSearch: config.showSearch } : {}
|
|
529
599
|
};
|
|
530
600
|
}
|
|
531
|
-
function buildHtml(tokens, runtimeConfig, css, appBundle) {
|
|
601
|
+
function buildHtml(tokens, runtimeConfig, css, appBundle, enableHotReload) {
|
|
532
602
|
const serializedTokens = serializeForInlineScript(tokens);
|
|
533
603
|
const serializedConfig = serializeForInlineScript(runtimeConfig);
|
|
534
604
|
const safeCss = escapeInlineTag(css, "style");
|
|
535
605
|
const safeAppBundle = escapeInlineTag(appBundle, "script");
|
|
536
606
|
const pageTitle = runtimeConfig.title || "TokVista";
|
|
607
|
+
const hotReloadScript = enableHotReload ? `<script>(function(){const ws=new WebSocket('ws://'+location.host+'/__tokvista_ws');ws.onmessage=()=>location.reload();ws.onerror=()=>setTimeout(()=>location.reload(),1000);})();</script>` : "";
|
|
537
608
|
return `<!doctype html>
|
|
538
609
|
<html lang="en">
|
|
539
610
|
<head>
|
|
@@ -547,6 +618,7 @@ function buildHtml(tokens, runtimeConfig, css, appBundle) {
|
|
|
547
618
|
<script>window.__TOKVISTA_TOKENS__ = ${serializedTokens};</script>
|
|
548
619
|
<script>window.__TOKVISTA_CONFIG__ = ${serializedConfig};</script>
|
|
549
620
|
<script type="module">${safeAppBundle}</script>
|
|
621
|
+
${hotReloadScript}
|
|
550
622
|
</body>
|
|
551
623
|
</html>`;
|
|
552
624
|
}
|
|
@@ -555,7 +627,7 @@ function resolveDistAsset(relativePath) {
|
|
|
555
627
|
const binDir = path.dirname(currentFilePath);
|
|
556
628
|
return path.resolve(binDir, "..", relativePath);
|
|
557
629
|
}
|
|
558
|
-
function handleRequest(request, response,
|
|
630
|
+
function handleRequest(request, response, getHtml) {
|
|
559
631
|
const requestUrl = request.url ?? "/";
|
|
560
632
|
const pathname = requestUrl.split("?")[0];
|
|
561
633
|
if (pathname === "/" || pathname === "") {
|
|
@@ -563,7 +635,7 @@ function handleRequest(request, response, html) {
|
|
|
563
635
|
"content-type": "text/html; charset=utf-8",
|
|
564
636
|
"cache-control": "no-store"
|
|
565
637
|
});
|
|
566
|
-
response.end(
|
|
638
|
+
response.end(getHtml());
|
|
567
639
|
return;
|
|
568
640
|
}
|
|
569
641
|
if (pathname === "/favicon.ico") {
|
|
@@ -577,11 +649,11 @@ function handleRequest(request, response, html) {
|
|
|
577
649
|
function isPortInUse(error) {
|
|
578
650
|
return typeof error === "object" && error !== null && "code" in error && error.code === "EADDRINUSE";
|
|
579
651
|
}
|
|
580
|
-
async function startServer(
|
|
652
|
+
async function startServer(getHtml, preferredPort) {
|
|
581
653
|
for (let attempt = 0; attempt < MAX_PORT_ATTEMPTS; attempt += 1) {
|
|
582
654
|
const port = preferredPort + attempt;
|
|
583
655
|
const server = createServer(
|
|
584
|
-
(request, response) => handleRequest(request, response,
|
|
656
|
+
(request, response) => handleRequest(request, response, getHtml)
|
|
585
657
|
);
|
|
586
658
|
try {
|
|
587
659
|
await new Promise((resolve, reject) => {
|
|
@@ -637,27 +709,48 @@ async function runServeCommand(cwd, options) {
|
|
|
637
709
|
throw new Error(`Token file not found: ${resolvedTokenPath}`);
|
|
638
710
|
}
|
|
639
711
|
const runtimeConfig = await buildRuntimeConfig(config, configPath, cwd);
|
|
640
|
-
const [
|
|
641
|
-
readTokens(resolvedTokenPath),
|
|
712
|
+
const [css, appBundle] = await Promise.all([
|
|
642
713
|
readFile(resolveDistAsset("styles.css"), "utf8"),
|
|
643
714
|
readFile(resolveDistAsset("cli/browser.js"), "utf8")
|
|
644
715
|
]);
|
|
645
|
-
|
|
646
|
-
const
|
|
716
|
+
let cachedTokens = await readTokens(resolvedTokenPath);
|
|
717
|
+
const getHtml = () => buildHtml(cachedTokens, runtimeConfig, css, appBundle, options.watch);
|
|
718
|
+
const { server, port } = await startServer(getHtml, options.port);
|
|
647
719
|
const openSockets = /* @__PURE__ */ new Set();
|
|
648
720
|
let isShuttingDown = false;
|
|
721
|
+
const hotReload = options.watch ? new HotReloadServer() : null;
|
|
649
722
|
server.on("connection", (socket) => {
|
|
650
723
|
openSockets.add(socket);
|
|
651
724
|
socket.on("close", () => {
|
|
652
725
|
openSockets.delete(socket);
|
|
653
726
|
});
|
|
654
727
|
});
|
|
728
|
+
if (hotReload) {
|
|
729
|
+
server.on("upgrade", (req, socket, head) => hotReload.handleUpgrade(req, socket, head));
|
|
730
|
+
}
|
|
655
731
|
const url = `http://localhost:${port}`;
|
|
656
732
|
console.log(`TokVista running at ${url}`);
|
|
657
733
|
console.log(`Using tokens: ${resolvedTokenPath}`);
|
|
658
734
|
if (configPath) {
|
|
659
735
|
console.log(`Using config: ${configPath}`);
|
|
660
736
|
}
|
|
737
|
+
if (options.watch) {
|
|
738
|
+
console.log("Watching for changes...");
|
|
739
|
+
const watcher = watchFile(resolvedTokenPath, async () => {
|
|
740
|
+
try {
|
|
741
|
+
cachedTokens = await readTokens(resolvedTokenPath);
|
|
742
|
+
hotReload == null ? void 0 : hotReload.reload();
|
|
743
|
+
console.log("Tokens reloaded");
|
|
744
|
+
} catch (error) {
|
|
745
|
+
console.error("Failed to reload tokens:", error.message);
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
const cleanup = () => {
|
|
749
|
+
watcher.close();
|
|
750
|
+
hotReload == null ? void 0 : hotReload.close();
|
|
751
|
+
};
|
|
752
|
+
process.on("exit", cleanup);
|
|
753
|
+
}
|
|
661
754
|
if (options.openBrowser) {
|
|
662
755
|
try {
|
|
663
756
|
await openBrowser(url);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokvista",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "Interactive visual documentation for design tokens.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"url": "https://www.linkedin.com/in/nibin-kurian"
|
|
81
81
|
},
|
|
82
82
|
"engines": {
|
|
83
|
-
"node": ">=
|
|
83
|
+
"node": ">=20.0.0"
|
|
84
84
|
},
|
|
85
85
|
"license": "MIT",
|
|
86
86
|
"peerDependencies": {
|