@ryndesign/preview 0.1.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/LICENSE +21 -0
- package/client/index.html +12 -0
- package/client/src/App.tsx +261 -0
- package/client/src/components/CodeViewer.tsx +88 -0
- package/client/src/components/ComponentBrowser.tsx +54 -0
- package/client/src/components/PreviewPanel.tsx +188 -0
- package/client/src/components/ThemeSwitcher.tsx +27 -0
- package/client/src/components/TokenEditor.tsx +113 -0
- package/client/src/hooks/useTokens.ts +14 -0
- package/client/src/hooks/useWebSocket.ts +116 -0
- package/client/src/index.tsx +6 -0
- package/dist/index.cjs +691 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +660 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// ../../node_modules/.pnpm/@rolldown+pluginutils@1.0.0-rc.7/node_modules/@rolldown/pluginutils/dist/utils.js
|
|
12
|
+
var init_utils = __esm({
|
|
13
|
+
"../../node_modules/.pnpm/@rolldown+pluginutils@1.0.0-rc.7/node_modules/@rolldown/pluginutils/dist/utils.js"() {
|
|
14
|
+
"use strict";
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// ../../node_modules/.pnpm/@rolldown+pluginutils@1.0.0-rc.7/node_modules/@rolldown/pluginutils/dist/filter/composable-filters.js
|
|
19
|
+
var init_composable_filters = __esm({
|
|
20
|
+
"../../node_modules/.pnpm/@rolldown+pluginutils@1.0.0-rc.7/node_modules/@rolldown/pluginutils/dist/filter/composable-filters.js"() {
|
|
21
|
+
"use strict";
|
|
22
|
+
init_utils();
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// ../../node_modules/.pnpm/@rolldown+pluginutils@1.0.0-rc.7/node_modules/@rolldown/pluginutils/dist/filter/filter-vite-plugins.js
|
|
27
|
+
var init_filter_vite_plugins = __esm({
|
|
28
|
+
"../../node_modules/.pnpm/@rolldown+pluginutils@1.0.0-rc.7/node_modules/@rolldown/pluginutils/dist/filter/filter-vite-plugins.js"() {
|
|
29
|
+
"use strict";
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// ../../node_modules/.pnpm/@rolldown+pluginutils@1.0.0-rc.7/node_modules/@rolldown/pluginutils/dist/filter/simple-filters.js
|
|
34
|
+
function exactRegex(str, flags) {
|
|
35
|
+
return new RegExp(`^${escapeRegex(str)}$`, flags);
|
|
36
|
+
}
|
|
37
|
+
function escapeRegex(str) {
|
|
38
|
+
return str.replace(escapeRegexRE, "\\$&");
|
|
39
|
+
}
|
|
40
|
+
function makeIdFiltersToMatchWithQuery(input) {
|
|
41
|
+
if (!Array.isArray(input)) {
|
|
42
|
+
return makeIdFilterToMatchWithQuery(
|
|
43
|
+
// Array.isArray cannot narrow the type
|
|
44
|
+
// https://github.com/microsoft/TypeScript/issues/17002
|
|
45
|
+
input
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return input.map((i) => makeIdFilterToMatchWithQuery(i));
|
|
49
|
+
}
|
|
50
|
+
function makeIdFilterToMatchWithQuery(input) {
|
|
51
|
+
if (typeof input === "string") {
|
|
52
|
+
return `${input}{?*,}`;
|
|
53
|
+
}
|
|
54
|
+
return makeRegexIdFilterToMatchWithQuery(input);
|
|
55
|
+
}
|
|
56
|
+
function makeRegexIdFilterToMatchWithQuery(input) {
|
|
57
|
+
return new RegExp(
|
|
58
|
+
// replace `$` with `(?:\?.*)?$` (ignore `\$`)
|
|
59
|
+
input.source.replace(/(?<!\\)\$/g, "(?:\\?.*)?$"),
|
|
60
|
+
input.flags
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
var escapeRegexRE;
|
|
64
|
+
var init_simple_filters = __esm({
|
|
65
|
+
"../../node_modules/.pnpm/@rolldown+pluginutils@1.0.0-rc.7/node_modules/@rolldown/pluginutils/dist/filter/simple-filters.js"() {
|
|
66
|
+
"use strict";
|
|
67
|
+
escapeRegexRE = /[-/\\^$*+?.()|[\]{}]/g;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// ../../node_modules/.pnpm/@rolldown+pluginutils@1.0.0-rc.7/node_modules/@rolldown/pluginutils/dist/filter/index.js
|
|
72
|
+
var init_filter = __esm({
|
|
73
|
+
"../../node_modules/.pnpm/@rolldown+pluginutils@1.0.0-rc.7/node_modules/@rolldown/pluginutils/dist/filter/index.js"() {
|
|
74
|
+
"use strict";
|
|
75
|
+
init_composable_filters();
|
|
76
|
+
init_filter_vite_plugins();
|
|
77
|
+
init_simple_filters();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// ../../node_modules/.pnpm/@rolldown+pluginutils@1.0.0-rc.7/node_modules/@rolldown/pluginutils/dist/index.js
|
|
82
|
+
var init_dist = __esm({
|
|
83
|
+
"../../node_modules/.pnpm/@rolldown+pluginutils@1.0.0-rc.7/node_modules/@rolldown/pluginutils/dist/index.js"() {
|
|
84
|
+
"use strict";
|
|
85
|
+
init_filter();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// ../../node_modules/.pnpm/@vitejs+plugin-react@6.0.1_vite@6.4.1_@types+node@25.5.0_jiti@2.6.1_/node_modules/@vitejs/plugin-react/dist/index.js
|
|
90
|
+
var dist_exports = {};
|
|
91
|
+
__export(dist_exports, {
|
|
92
|
+
default: () => viteReact,
|
|
93
|
+
"module.exports": () => viteReactForCjs,
|
|
94
|
+
reactCompilerPreset: () => reactCompilerPreset
|
|
95
|
+
});
|
|
96
|
+
import { readFileSync } from "fs";
|
|
97
|
+
import { dirname, join } from "path";
|
|
98
|
+
import { fileURLToPath } from "url";
|
|
99
|
+
import { reactRefreshWrapperPlugin } from "vite/internal";
|
|
100
|
+
function virtualPreamblePlugin({ name, isEnabled }) {
|
|
101
|
+
return {
|
|
102
|
+
name: "vite:react-virtual-preamble",
|
|
103
|
+
resolveId: {
|
|
104
|
+
order: "pre",
|
|
105
|
+
filter: { id: exactRegex(name) },
|
|
106
|
+
handler(source) {
|
|
107
|
+
if (source === name) return "\0" + source;
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
load: {
|
|
111
|
+
filter: { id: exactRegex("\0" + name) },
|
|
112
|
+
handler(id) {
|
|
113
|
+
if (id === "\0" + name) {
|
|
114
|
+
if (isEnabled()) return preambleCode.replace("__BASE__", "/");
|
|
115
|
+
return "";
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function viteReact(opts = {}) {
|
|
122
|
+
const include = opts.include ?? defaultIncludeRE;
|
|
123
|
+
const exclude = opts.exclude ?? defaultExcludeRE;
|
|
124
|
+
const jsxImportSource = opts.jsxImportSource ?? "react";
|
|
125
|
+
const jsxImportRuntime = `${jsxImportSource}/jsx-runtime`;
|
|
126
|
+
const jsxImportDevRuntime = `${jsxImportSource}/jsx-dev-runtime`;
|
|
127
|
+
let runningInVite = false;
|
|
128
|
+
let isProduction = true;
|
|
129
|
+
let skipFastRefresh = true;
|
|
130
|
+
let base;
|
|
131
|
+
let isBundledDev = false;
|
|
132
|
+
const viteBabel = {
|
|
133
|
+
name: "vite:react-babel",
|
|
134
|
+
enforce: "pre",
|
|
135
|
+
config(_userConfig, { command }) {
|
|
136
|
+
if (opts.jsxRuntime === "classic") return { oxc: {
|
|
137
|
+
jsx: {
|
|
138
|
+
runtime: "classic",
|
|
139
|
+
refresh: command === "serve"
|
|
140
|
+
},
|
|
141
|
+
jsxRefreshInclude: makeIdFiltersToMatchWithQuery(include),
|
|
142
|
+
jsxRefreshExclude: makeIdFiltersToMatchWithQuery(exclude)
|
|
143
|
+
} };
|
|
144
|
+
else return {
|
|
145
|
+
oxc: {
|
|
146
|
+
jsx: {
|
|
147
|
+
runtime: "automatic",
|
|
148
|
+
importSource: opts.jsxImportSource,
|
|
149
|
+
refresh: command === "serve"
|
|
150
|
+
},
|
|
151
|
+
jsxRefreshInclude: makeIdFiltersToMatchWithQuery(include),
|
|
152
|
+
jsxRefreshExclude: makeIdFiltersToMatchWithQuery(exclude)
|
|
153
|
+
},
|
|
154
|
+
optimizeDeps: { rolldownOptions: { transform: { jsx: { runtime: "automatic" } } } }
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
configResolved(config) {
|
|
158
|
+
runningInVite = true;
|
|
159
|
+
base = config.base;
|
|
160
|
+
if (config.experimental.bundledDev) isBundledDev = true;
|
|
161
|
+
isProduction = config.isProduction;
|
|
162
|
+
skipFastRefresh = isProduction || config.command === "build" || config.server.hmr === false;
|
|
163
|
+
},
|
|
164
|
+
options(options) {
|
|
165
|
+
if (!runningInVite) {
|
|
166
|
+
options.transform ??= {};
|
|
167
|
+
options.transform.jsx = {
|
|
168
|
+
runtime: opts.jsxRuntime,
|
|
169
|
+
importSource: opts.jsxImportSource
|
|
170
|
+
};
|
|
171
|
+
return options;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
const viteRefreshWrapper = {
|
|
176
|
+
name: "vite:react:refresh-wrapper",
|
|
177
|
+
apply: "serve",
|
|
178
|
+
async applyToEnvironment(env) {
|
|
179
|
+
if (env.config.consumer !== "client" || skipFastRefresh) return false;
|
|
180
|
+
return reactRefreshWrapperPlugin({
|
|
181
|
+
cwd: process.cwd(),
|
|
182
|
+
include: makeIdFiltersToMatchWithQuery(include),
|
|
183
|
+
exclude: makeIdFiltersToMatchWithQuery(exclude),
|
|
184
|
+
jsxImportSource,
|
|
185
|
+
reactRefreshHost: opts.reactRefreshHost ?? ""
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
const viteConfigPost = {
|
|
190
|
+
name: "vite:react:config-post",
|
|
191
|
+
enforce: "post",
|
|
192
|
+
config(userConfig) {
|
|
193
|
+
if (userConfig.server?.hmr === false) return { oxc: { jsx: { refresh: false } } };
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
const viteReactRefreshBundledDevMode = {
|
|
197
|
+
name: "vite:react-refresh-fbm",
|
|
198
|
+
enforce: "pre",
|
|
199
|
+
transformIndexHtml: {
|
|
200
|
+
handler() {
|
|
201
|
+
if (!skipFastRefresh && isBundledDev) return [{
|
|
202
|
+
tag: "script",
|
|
203
|
+
attrs: { type: "module" },
|
|
204
|
+
children: getPreambleCode(base)
|
|
205
|
+
}];
|
|
206
|
+
},
|
|
207
|
+
order: "pre"
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
const dependencies = [
|
|
211
|
+
"react",
|
|
212
|
+
"react-dom",
|
|
213
|
+
jsxImportDevRuntime,
|
|
214
|
+
jsxImportRuntime
|
|
215
|
+
];
|
|
216
|
+
return [
|
|
217
|
+
viteBabel,
|
|
218
|
+
viteRefreshWrapper,
|
|
219
|
+
viteConfigPost,
|
|
220
|
+
viteReactRefreshBundledDevMode,
|
|
221
|
+
{
|
|
222
|
+
name: "vite:react-refresh",
|
|
223
|
+
enforce: "pre",
|
|
224
|
+
config: (userConfig) => ({
|
|
225
|
+
build: silenceUseClientWarning(userConfig),
|
|
226
|
+
optimizeDeps: { include: dependencies }
|
|
227
|
+
}),
|
|
228
|
+
resolveId: {
|
|
229
|
+
filter: { id: exactRegex(runtimePublicPath) },
|
|
230
|
+
handler(id) {
|
|
231
|
+
if (id === "/@react-refresh") return id;
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
load: {
|
|
235
|
+
filter: { id: exactRegex(runtimePublicPath) },
|
|
236
|
+
handler(id) {
|
|
237
|
+
if (id === "/@react-refresh") return readFileSync(refreshRuntimePath, "utf-8").replace(/__README_URL__/g, "https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react");
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
transformIndexHtml() {
|
|
241
|
+
if (!skipFastRefresh && !isBundledDev) return [{
|
|
242
|
+
tag: "script",
|
|
243
|
+
attrs: { type: "module" },
|
|
244
|
+
children: getPreambleCode(base)
|
|
245
|
+
}];
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
virtualPreamblePlugin({
|
|
249
|
+
name: "@vitejs/plugin-react/preamble",
|
|
250
|
+
isEnabled: () => !skipFastRefresh && !isBundledDev
|
|
251
|
+
})
|
|
252
|
+
];
|
|
253
|
+
}
|
|
254
|
+
function viteReactForCjs(options) {
|
|
255
|
+
return viteReact.call(this, options);
|
|
256
|
+
}
|
|
257
|
+
var runtimePublicPath, preambleCode, getPreambleCode, silenceUseClientWarning, reactCompilerPreset, refreshRuntimePath, defaultIncludeRE, defaultExcludeRE;
|
|
258
|
+
var init_dist2 = __esm({
|
|
259
|
+
"../../node_modules/.pnpm/@vitejs+plugin-react@6.0.1_vite@6.4.1_@types+node@25.5.0_jiti@2.6.1_/node_modules/@vitejs/plugin-react/dist/index.js"() {
|
|
260
|
+
"use strict";
|
|
261
|
+
init_dist();
|
|
262
|
+
runtimePublicPath = "/@react-refresh";
|
|
263
|
+
preambleCode = `import { injectIntoGlobalHook } from "__BASE__${runtimePublicPath.slice(1)}";
|
|
264
|
+
injectIntoGlobalHook(window);
|
|
265
|
+
window.$RefreshReg$ = () => {};
|
|
266
|
+
window.$RefreshSig$ = () => (type) => type;`;
|
|
267
|
+
getPreambleCode = (base) => preambleCode.replace("__BASE__", base);
|
|
268
|
+
silenceUseClientWarning = (userConfig) => ({ rollupOptions: { onwarn(warning, defaultHandler) {
|
|
269
|
+
if (warning.code === "MODULE_LEVEL_DIRECTIVE" && (warning.message.includes("use client") || warning.message.includes("use server"))) return;
|
|
270
|
+
if (warning.code === "SOURCEMAP_ERROR" && warning.message.includes("resolve original location") && warning.pos === 0) return;
|
|
271
|
+
if (userConfig.build?.rollupOptions?.onwarn) userConfig.build.rollupOptions.onwarn(warning, defaultHandler);
|
|
272
|
+
else defaultHandler(warning);
|
|
273
|
+
} } });
|
|
274
|
+
reactCompilerPreset = (options = {}) => ({
|
|
275
|
+
preset: () => ({ plugins: [["babel-plugin-react-compiler", options]] }),
|
|
276
|
+
rolldown: {
|
|
277
|
+
filter: { code: options.compilationMode === "annotation" ? /['"]use memo['"]/ : /\b[A-Z]|\buse/ },
|
|
278
|
+
applyToEnvironmentHook: (env) => env.config.consumer === "client",
|
|
279
|
+
optimizeDeps: { include: options.target === "17" || options.target === "18" ? ["react-compiler-runtime"] : ["react/compiler-runtime"] }
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
refreshRuntimePath = join(dirname(fileURLToPath(import.meta.url)), "refresh-runtime.js");
|
|
283
|
+
defaultIncludeRE = /\.[tj]sx?$/;
|
|
284
|
+
defaultExcludeRE = /\/node_modules\//;
|
|
285
|
+
viteReact.preambleCode = preambleCode;
|
|
286
|
+
Object.assign(viteReactForCjs, {
|
|
287
|
+
default: viteReactForCjs,
|
|
288
|
+
reactCompilerPreset
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// src/server/index.ts
|
|
294
|
+
import { createServer as createViteServer } from "vite";
|
|
295
|
+
import { WebSocketServer } from "ws";
|
|
296
|
+
import http from "http";
|
|
297
|
+
import path2 from "path";
|
|
298
|
+
|
|
299
|
+
// src/server/api-routes.ts
|
|
300
|
+
function setupApiRoutes(builder) {
|
|
301
|
+
return (req, res) => {
|
|
302
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
303
|
+
const pathname = url.pathname;
|
|
304
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
305
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS");
|
|
306
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
307
|
+
if (req.method === "OPTIONS") {
|
|
308
|
+
res.writeHead(204);
|
|
309
|
+
res.end();
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
try {
|
|
313
|
+
if (pathname === "/api/tokens" && req.method === "GET") {
|
|
314
|
+
json(res, builder.getTokenSet());
|
|
315
|
+
} else if (pathname === "/api/themes" && req.method === "GET") {
|
|
316
|
+
json(res, builder.getThemes());
|
|
317
|
+
} else if (pathname === "/api/components" && req.method === "GET") {
|
|
318
|
+
json(res, builder.getComponents());
|
|
319
|
+
} else if (pathname === "/api/snippets" && req.method === "GET") {
|
|
320
|
+
const platform = url.searchParams.get("platform") ?? "react";
|
|
321
|
+
const component = url.searchParams.get("component") ?? void 0;
|
|
322
|
+
const type = url.searchParams.get("type") ?? void 0;
|
|
323
|
+
builder.generateSnippets(platform, component, type).then((code) => {
|
|
324
|
+
json(res, { code });
|
|
325
|
+
}).catch((err) => {
|
|
326
|
+
res.writeHead(500);
|
|
327
|
+
json(res, { error: err.message });
|
|
328
|
+
});
|
|
329
|
+
return;
|
|
330
|
+
} else if (pathname === "/api/tokens" && req.method === "PUT") {
|
|
331
|
+
let body = "";
|
|
332
|
+
req.on("data", (chunk) => {
|
|
333
|
+
body += chunk;
|
|
334
|
+
});
|
|
335
|
+
req.on("end", async () => {
|
|
336
|
+
const { path: path3, value, theme } = JSON.parse(body);
|
|
337
|
+
await builder.updateToken(path3, value, theme);
|
|
338
|
+
json(res, { success: true });
|
|
339
|
+
});
|
|
340
|
+
} else if (pathname === "/api/generated" && req.method === "GET") {
|
|
341
|
+
const platform = url.searchParams.get("platform");
|
|
342
|
+
json(res, builder.getGeneratedFiles(platform ?? void 0));
|
|
343
|
+
} else {
|
|
344
|
+
res.writeHead(404);
|
|
345
|
+
json(res, { error: "Not found" });
|
|
346
|
+
}
|
|
347
|
+
} catch (err) {
|
|
348
|
+
res.writeHead(500);
|
|
349
|
+
json(res, { error: err.message });
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
function json(res, data) {
|
|
354
|
+
if (!res.headersSent) {
|
|
355
|
+
res.setHeader("Content-Type", "application/json");
|
|
356
|
+
}
|
|
357
|
+
res.end(JSON.stringify(data));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// src/server/ws-handler.ts
|
|
361
|
+
function setupWsHandler(wss, builder) {
|
|
362
|
+
wss.on("connection", (ws) => {
|
|
363
|
+
console.log("Preview client connected");
|
|
364
|
+
ws.send(JSON.stringify({
|
|
365
|
+
type: "init",
|
|
366
|
+
tokenSet: builder.getTokenSet(),
|
|
367
|
+
themes: builder.getThemes(),
|
|
368
|
+
components: builder.getComponents()
|
|
369
|
+
}));
|
|
370
|
+
ws.on("message", async (data) => {
|
|
371
|
+
try {
|
|
372
|
+
const message = JSON.parse(data.toString());
|
|
373
|
+
switch (message.type) {
|
|
374
|
+
case "token-update": {
|
|
375
|
+
const update = message;
|
|
376
|
+
await builder.updateToken(update.path, update.value, update.theme);
|
|
377
|
+
broadcast(wss, {
|
|
378
|
+
type: "rebuild-complete",
|
|
379
|
+
changedTokens: [update.path],
|
|
380
|
+
timestamp: Date.now()
|
|
381
|
+
});
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
case "theme-change": {
|
|
385
|
+
const themeMsg = message;
|
|
386
|
+
broadcast(wss, {
|
|
387
|
+
type: "theme-switched",
|
|
388
|
+
theme: themeMsg.theme,
|
|
389
|
+
tokens: builder.getThemeTokens(themeMsg.theme)
|
|
390
|
+
});
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
case "request-state": {
|
|
394
|
+
ws.send(JSON.stringify({
|
|
395
|
+
type: "full-state",
|
|
396
|
+
tokenSet: builder.getTokenSet(),
|
|
397
|
+
themes: builder.getThemes(),
|
|
398
|
+
components: builder.getComponents()
|
|
399
|
+
}));
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
} catch (err) {
|
|
404
|
+
ws.send(JSON.stringify({
|
|
405
|
+
type: "error",
|
|
406
|
+
message: err.message
|
|
407
|
+
}));
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
ws.on("close", () => {
|
|
411
|
+
console.log("Preview client disconnected");
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
function broadcast(wss, message) {
|
|
416
|
+
const data = JSON.stringify(message);
|
|
417
|
+
for (const client of wss.clients) {
|
|
418
|
+
if (client.readyState === 1) {
|
|
419
|
+
client.send(data);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// src/server/watcher.ts
|
|
425
|
+
import { watch } from "chokidar";
|
|
426
|
+
function setupWatcher(builder, wss, cwd) {
|
|
427
|
+
const watcher = watch(
|
|
428
|
+
["tokens/**/*.tokens.json", "components/**/*.component.json"],
|
|
429
|
+
{
|
|
430
|
+
cwd,
|
|
431
|
+
ignoreInitial: true,
|
|
432
|
+
awaitWriteFinish: {
|
|
433
|
+
stabilityThreshold: 300,
|
|
434
|
+
pollInterval: 50
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
);
|
|
438
|
+
let debounceTimer = null;
|
|
439
|
+
const handleChange = (changedPath) => {
|
|
440
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
441
|
+
debounceTimer = setTimeout(async () => {
|
|
442
|
+
console.log(`File changed: ${changedPath}`);
|
|
443
|
+
try {
|
|
444
|
+
await builder.rebuild();
|
|
445
|
+
const data = JSON.stringify({
|
|
446
|
+
type: "rebuild-complete",
|
|
447
|
+
changedTokens: [],
|
|
448
|
+
timestamp: Date.now(),
|
|
449
|
+
tokenSet: builder.getTokenSet(),
|
|
450
|
+
components: builder.getComponents()
|
|
451
|
+
});
|
|
452
|
+
for (const client of wss.clients) {
|
|
453
|
+
if (client.readyState === 1) {
|
|
454
|
+
client.send(data);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
} catch (err) {
|
|
458
|
+
console.error("Rebuild failed:", err.message);
|
|
459
|
+
}
|
|
460
|
+
}, 300);
|
|
461
|
+
};
|
|
462
|
+
watcher.on("change", handleChange);
|
|
463
|
+
watcher.on("add", handleChange);
|
|
464
|
+
watcher.on("unlink", handleChange);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// src/server/incremental-build.ts
|
|
468
|
+
import { buildTokenSet, loadComponents, resolveComponent } from "@ryndesign/core";
|
|
469
|
+
import fs from "fs/promises";
|
|
470
|
+
import path from "path";
|
|
471
|
+
var IncrementalBuilder = class {
|
|
472
|
+
tokenSet = null;
|
|
473
|
+
components = [];
|
|
474
|
+
generatedFiles = /* @__PURE__ */ new Map();
|
|
475
|
+
generators = [];
|
|
476
|
+
cwd;
|
|
477
|
+
configPath;
|
|
478
|
+
constructor(cwd, configPath) {
|
|
479
|
+
this.cwd = cwd;
|
|
480
|
+
this.configPath = configPath;
|
|
481
|
+
}
|
|
482
|
+
async initialBuild() {
|
|
483
|
+
await this.rebuild();
|
|
484
|
+
}
|
|
485
|
+
async rebuild() {
|
|
486
|
+
const configFile = this.configPath ?? "ryndesign.config.ts";
|
|
487
|
+
const tokens = ["tokens/**/*.tokens.json"];
|
|
488
|
+
const componentPatterns = ["components/**/*.component.json"];
|
|
489
|
+
let themes = void 0;
|
|
490
|
+
try {
|
|
491
|
+
const configPath = path.resolve(this.cwd, configFile);
|
|
492
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
493
|
+
if (content.includes("dark")) {
|
|
494
|
+
themes = {
|
|
495
|
+
default: "light",
|
|
496
|
+
dark: { file: path.resolve(this.cwd, "tokens/dark.tokens.json") }
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
} catch {
|
|
500
|
+
}
|
|
501
|
+
this.tokenSet = await buildTokenSet({
|
|
502
|
+
tokens,
|
|
503
|
+
basePath: this.cwd,
|
|
504
|
+
themes
|
|
505
|
+
});
|
|
506
|
+
try {
|
|
507
|
+
const componentDefs = await loadComponents(componentPatterns, this.cwd);
|
|
508
|
+
this.components = componentDefs.map((def) => resolveComponent(def, this.tokenSet));
|
|
509
|
+
} catch {
|
|
510
|
+
this.components = [];
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
getTokenSet() {
|
|
514
|
+
return this.tokenSet;
|
|
515
|
+
}
|
|
516
|
+
getThemes() {
|
|
517
|
+
if (!this.tokenSet) return {};
|
|
518
|
+
return {
|
|
519
|
+
default: this.tokenSet.themes.default,
|
|
520
|
+
available: Object.keys(this.tokenSet.themes.themes),
|
|
521
|
+
themes: this.tokenSet.themes.themes
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
getThemeTokens(theme) {
|
|
525
|
+
if (!this.tokenSet) return {};
|
|
526
|
+
const themeData = this.tokenSet.themes.themes[theme];
|
|
527
|
+
if (!themeData) return {};
|
|
528
|
+
return {
|
|
529
|
+
name: themeData.name,
|
|
530
|
+
tokens: themeData.tokens
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
getComponents() {
|
|
534
|
+
return this.components;
|
|
535
|
+
}
|
|
536
|
+
setGenerators(generators) {
|
|
537
|
+
this.generators = generators;
|
|
538
|
+
}
|
|
539
|
+
async generateSnippets(platform, componentName, type) {
|
|
540
|
+
if (!this.tokenSet) return "";
|
|
541
|
+
const generator = this.generators.find((g) => g.name === platform);
|
|
542
|
+
if (!generator) return `// Generator for "${platform}" not available`;
|
|
543
|
+
const { createGeneratorHelpers } = await import("@ryndesign/core");
|
|
544
|
+
const ctx = {
|
|
545
|
+
tokenSet: this.tokenSet,
|
|
546
|
+
config: { outDir: "generated" },
|
|
547
|
+
outputDir: path.resolve(this.cwd, "generated"),
|
|
548
|
+
helpers: createGeneratorHelpers(),
|
|
549
|
+
components: this.components
|
|
550
|
+
};
|
|
551
|
+
try {
|
|
552
|
+
if (type === "tokens") {
|
|
553
|
+
const files = await generator.generateTokens(ctx);
|
|
554
|
+
return files.map((f) => `// ${f.path}
|
|
555
|
+
${f.content}`).join("\n\n");
|
|
556
|
+
}
|
|
557
|
+
if (componentName) {
|
|
558
|
+
const comp = this.components.find((c) => c.definition.name === componentName);
|
|
559
|
+
if (comp) {
|
|
560
|
+
const files = await generator.generateComponent(comp, ctx);
|
|
561
|
+
return files.map((f) => `// ${f.path}
|
|
562
|
+
${f.content}`).join("\n\n");
|
|
563
|
+
}
|
|
564
|
+
return `// Component "${componentName}" not found`;
|
|
565
|
+
}
|
|
566
|
+
return "// Specify a component or type=tokens";
|
|
567
|
+
} catch (err) {
|
|
568
|
+
return `// Error: ${err.message}`;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
getGeneratedFiles(platform) {
|
|
572
|
+
if (!platform) {
|
|
573
|
+
return Array.from(this.generatedFiles.values()).flat();
|
|
574
|
+
}
|
|
575
|
+
return this.generatedFiles.get(platform) ?? [];
|
|
576
|
+
}
|
|
577
|
+
async updateToken(tokenPath, value, theme) {
|
|
578
|
+
const filePath = theme ? path.resolve(this.cwd, `tokens/${theme}.tokens.json`) : path.resolve(this.cwd, "tokens/semantic.tokens.json");
|
|
579
|
+
try {
|
|
580
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
581
|
+
const tree = JSON.parse(content);
|
|
582
|
+
const parts = tokenPath.split(".");
|
|
583
|
+
let current = tree;
|
|
584
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
585
|
+
if (!current[parts[i]]) current[parts[i]] = {};
|
|
586
|
+
current = current[parts[i]];
|
|
587
|
+
}
|
|
588
|
+
const lastKey = parts[parts.length - 1];
|
|
589
|
+
if (current[lastKey] && typeof current[lastKey] === "object" && "$value" in current[lastKey]) {
|
|
590
|
+
current[lastKey].$value = value;
|
|
591
|
+
} else {
|
|
592
|
+
current[lastKey] = { $type: "color", $value: value };
|
|
593
|
+
}
|
|
594
|
+
await fs.writeFile(filePath, JSON.stringify(tree, null, 2), "utf-8");
|
|
595
|
+
await this.rebuild();
|
|
596
|
+
} catch (err) {
|
|
597
|
+
console.error(`Failed to update token: ${err.message}`);
|
|
598
|
+
throw err;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
// src/server/index.ts
|
|
604
|
+
async function startPreviewServer(options = {}) {
|
|
605
|
+
const port = options.port ?? 4400;
|
|
606
|
+
const cwd = process.cwd();
|
|
607
|
+
const builder = new IncrementalBuilder(cwd, options.configPath);
|
|
608
|
+
await builder.initialBuild();
|
|
609
|
+
const server = http.createServer();
|
|
610
|
+
const wss = new WebSocketServer({ server });
|
|
611
|
+
setupWsHandler(wss, builder);
|
|
612
|
+
let reactPlugin;
|
|
613
|
+
try {
|
|
614
|
+
const mod = await Promise.resolve().then(() => (init_dist2(), dist_exports));
|
|
615
|
+
reactPlugin = (mod.default ?? mod)();
|
|
616
|
+
} catch {
|
|
617
|
+
}
|
|
618
|
+
const vite = await createViteServer({
|
|
619
|
+
root: path2.resolve(__dirname, "../client"),
|
|
620
|
+
server: {
|
|
621
|
+
middlewareMode: true,
|
|
622
|
+
hmr: { server }
|
|
623
|
+
},
|
|
624
|
+
plugins: reactPlugin ? [reactPlugin] : [],
|
|
625
|
+
appType: "spa"
|
|
626
|
+
});
|
|
627
|
+
const apiHandler = setupApiRoutes(builder);
|
|
628
|
+
server.on("request", (req, res) => {
|
|
629
|
+
if (req.url?.startsWith("/api/")) {
|
|
630
|
+
apiHandler(req, res);
|
|
631
|
+
} else {
|
|
632
|
+
vite.middlewares(req, res);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
setupWatcher(builder, wss, cwd);
|
|
636
|
+
server.listen(port, () => {
|
|
637
|
+
console.log(`
|
|
638
|
+
\u{1F3A8} RynDesign Preview`);
|
|
639
|
+
console.log(` \u279C Local: http://localhost:${port}/`);
|
|
640
|
+
console.log(` \u279C WS: ws://localhost:${port}/
|
|
641
|
+
`);
|
|
642
|
+
if (options.open) {
|
|
643
|
+
import("child_process").then((cp) => {
|
|
644
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
645
|
+
cp.exec(`${cmd} http://localhost:${port}/`);
|
|
646
|
+
}).catch(() => {
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
process.on("SIGINT", () => {
|
|
651
|
+
wss.close();
|
|
652
|
+
vite.close();
|
|
653
|
+
server.close();
|
|
654
|
+
process.exit(0);
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
export {
|
|
658
|
+
startPreviewServer
|
|
659
|
+
};
|
|
660
|
+
//# sourceMappingURL=index.js.map
|