@vertz/ui-server 0.2.38 → 0.2.41
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/bun-dev-server.js +356 -45
- package/dist/index.d.ts +38 -3
- package/dist/index.js +153 -9
- package/dist/node-handler.d.ts +7 -1
- package/dist/node-handler.js +2 -2
- package/dist/shared/{chunk-2ymwre0k.js → chunk-15kac8x8.js} +194 -24
- package/dist/shared/{chunk-d1a3ygv6.js → chunk-gqvkycnj.js} +5 -3
- package/dist/shared/{chunk-595trxsn.js → chunk-j18fewkf.js} +19 -4
- package/dist/ssr/index.d.ts +7 -1
- package/dist/ssr/index.js +2 -2
- package/package.json +6 -6
package/dist/bun-dev-server.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
|
|
10
10
|
// src/bun-dev-server.ts
|
|
11
11
|
import { execSync } from "child_process";
|
|
12
|
-
import { existsSync as
|
|
12
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync3, watch as watch2, writeFileSync as writeFileSync3 } from "fs";
|
|
13
13
|
import { dirname, normalize, resolve } from "path";
|
|
14
14
|
|
|
15
15
|
// src/debug-logger.ts
|
|
@@ -288,7 +288,7 @@ function runWithScopedFetch(interceptor, fn) {
|
|
|
288
288
|
// src/font-metrics.ts
|
|
289
289
|
import { access as fsAccess } from "fs/promises";
|
|
290
290
|
import { readFile } from "fs/promises";
|
|
291
|
-
import { join as join2 } from "path";
|
|
291
|
+
import { isAbsolute, join as join2 } from "path";
|
|
292
292
|
import { fromBuffer } from "@capsizecss/unpack";
|
|
293
293
|
var SYSTEM_FONT_METRICS = {
|
|
294
294
|
Arial: {
|
|
@@ -356,6 +356,12 @@ function getPrimarySrcPath(descriptor) {
|
|
|
356
356
|
return null;
|
|
357
357
|
}
|
|
358
358
|
async function resolveFilePath(urlPath, rootDir) {
|
|
359
|
+
if (isAbsolute(urlPath)) {
|
|
360
|
+
try {
|
|
361
|
+
await fsAccess(urlPath);
|
|
362
|
+
return urlPath;
|
|
363
|
+
} catch {}
|
|
364
|
+
}
|
|
359
365
|
const cleaned = urlPath.startsWith("/") ? urlPath.slice(1) : urlPath;
|
|
360
366
|
const direct = join2(rootDir, cleaned);
|
|
361
367
|
try {
|
|
@@ -395,6 +401,184 @@ async function extractFontMetrics(fonts, rootDir) {
|
|
|
395
401
|
return result;
|
|
396
402
|
}
|
|
397
403
|
|
|
404
|
+
// src/google-fonts-resolver.ts
|
|
405
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync as writeFileSync2 } from "fs";
|
|
406
|
+
import { createHash } from "crypto";
|
|
407
|
+
import { join as join3, relative } from "path";
|
|
408
|
+
var GOOGLE_FONTS_CSS2_URL = "https://fonts.googleapis.com/css2";
|
|
409
|
+
var MODERN_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
|
410
|
+
var MIN_WOFF2_SIZE = 100;
|
|
411
|
+
function buildGoogleFontsUrl(family, meta) {
|
|
412
|
+
const encodedFamily = family.replace(/ /g, "+");
|
|
413
|
+
const weightSpec = formatWeightSpec(meta.weight);
|
|
414
|
+
const hasItalic = meta.style.includes("italic");
|
|
415
|
+
let familyParam;
|
|
416
|
+
if (hasItalic && meta.style.includes("normal")) {
|
|
417
|
+
familyParam = `${encodedFamily}:ital,wght@0,${weightSpec};1,${weightSpec}`;
|
|
418
|
+
} else if (hasItalic) {
|
|
419
|
+
familyParam = `${encodedFamily}:ital,wght@1,${weightSpec}`;
|
|
420
|
+
} else {
|
|
421
|
+
familyParam = `${encodedFamily}:wght@${weightSpec}`;
|
|
422
|
+
}
|
|
423
|
+
return `${GOOGLE_FONTS_CSS2_URL}?family=${familyParam}&display=${meta.display}`;
|
|
424
|
+
}
|
|
425
|
+
function formatWeightSpec(weight) {
|
|
426
|
+
if (Array.isArray(weight)) {
|
|
427
|
+
return weight.join(";");
|
|
428
|
+
}
|
|
429
|
+
return String(weight);
|
|
430
|
+
}
|
|
431
|
+
function parseWoff2UrlsBySubset(css) {
|
|
432
|
+
const entries = [];
|
|
433
|
+
const blockRegex = /(?:\/\*\s*([\w-]+)\s*\*\/\s*)?@font-face\s*\{[^}]*url\(([^)]+\.woff2)\)[^}]*\}/g;
|
|
434
|
+
let match;
|
|
435
|
+
while ((match = blockRegex.exec(css)) !== null) {
|
|
436
|
+
entries.push({
|
|
437
|
+
subset: match[1] ?? "unknown",
|
|
438
|
+
url: match[2]
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
return entries;
|
|
442
|
+
}
|
|
443
|
+
function selectSubsetUrl(entries, requestedSubsets) {
|
|
444
|
+
for (const subset of requestedSubsets) {
|
|
445
|
+
const entry = entries.find((e) => e.subset === subset);
|
|
446
|
+
if (entry)
|
|
447
|
+
return entry.url;
|
|
448
|
+
}
|
|
449
|
+
const latin = entries.find((e) => e.subset === "latin");
|
|
450
|
+
if (latin)
|
|
451
|
+
return latin.url;
|
|
452
|
+
return entries[0].url;
|
|
453
|
+
}
|
|
454
|
+
function computeOptionsHash(meta) {
|
|
455
|
+
const data = JSON.stringify({
|
|
456
|
+
family: meta.family,
|
|
457
|
+
weight: meta.weight,
|
|
458
|
+
style: meta.style,
|
|
459
|
+
subsets: meta.subsets,
|
|
460
|
+
display: meta.display
|
|
461
|
+
});
|
|
462
|
+
return createHash("sha256").update(data).digest("hex").slice(0, 12);
|
|
463
|
+
}
|
|
464
|
+
function readManifest(cacheDir) {
|
|
465
|
+
const manifestPath = join3(cacheDir, "manifest.json");
|
|
466
|
+
if (existsSync(manifestPath)) {
|
|
467
|
+
try {
|
|
468
|
+
return JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
469
|
+
} catch {
|
|
470
|
+
return { entries: {} };
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return { entries: {} };
|
|
474
|
+
}
|
|
475
|
+
function writeManifest(cacheDir, manifest) {
|
|
476
|
+
const manifestPath = join3(cacheDir, "manifest.json");
|
|
477
|
+
writeFileSync2(manifestPath, JSON.stringify(manifest, null, 2));
|
|
478
|
+
}
|
|
479
|
+
function isCacheValid(cacheDir, entry) {
|
|
480
|
+
for (let i = 0;i < entry.files.length; i++) {
|
|
481
|
+
const filePath = join3(cacheDir, entry.files[i]);
|
|
482
|
+
if (!existsSync(filePath))
|
|
483
|
+
return false;
|
|
484
|
+
const stat = Bun.file(filePath).size;
|
|
485
|
+
if (stat < MIN_WOFF2_SIZE)
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
async function downloadFile(url, destPath) {
|
|
491
|
+
const response = await fetch(url);
|
|
492
|
+
if (!response.ok) {
|
|
493
|
+
throw new Error(`Failed to download ${url}: ${response.status} ${response.statusText}`);
|
|
494
|
+
}
|
|
495
|
+
const buffer = await response.arrayBuffer();
|
|
496
|
+
const tmpPath = destPath + ".tmp";
|
|
497
|
+
writeFileSync2(tmpPath, Buffer.from(buffer));
|
|
498
|
+
renameSync(tmpPath, destPath);
|
|
499
|
+
return buffer.byteLength;
|
|
500
|
+
}
|
|
501
|
+
async function resolveGoogleFonts(fonts, cacheDir, projectRoot) {
|
|
502
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
503
|
+
const manifest = readManifest(cacheDir);
|
|
504
|
+
const result = {};
|
|
505
|
+
const googleEntries = [];
|
|
506
|
+
for (const [key, descriptor] of Object.entries(fonts)) {
|
|
507
|
+
if (descriptor.__google) {
|
|
508
|
+
googleEntries.push({ key, descriptor });
|
|
509
|
+
} else {
|
|
510
|
+
result[key] = descriptor;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
await Promise.all(googleEntries.map(async ({ key, descriptor }) => {
|
|
514
|
+
const meta = descriptor.__google;
|
|
515
|
+
const hash = computeOptionsHash(meta);
|
|
516
|
+
const cached = manifest.entries[hash];
|
|
517
|
+
if (cached && isCacheValid(cacheDir, cached)) {
|
|
518
|
+
const srcPath = toSrcPath(cacheDir, cached.files[0], projectRoot);
|
|
519
|
+
result[key] = createResolvedDescriptor(descriptor, srcPath);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
try {
|
|
523
|
+
const url = buildGoogleFontsUrl(meta.family, meta);
|
|
524
|
+
const response = await fetch(url, {
|
|
525
|
+
headers: { "User-Agent": MODERN_USER_AGENT }
|
|
526
|
+
});
|
|
527
|
+
if (!response.ok) {
|
|
528
|
+
console.error(`[vertz] Error: Google Fonts returned ${response.status} for family "${meta.family}".` + `
|
|
529
|
+
Check https://fonts.google.com for the correct name.`);
|
|
530
|
+
result[key] = descriptor;
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
const css = await response.text();
|
|
534
|
+
const subsetEntries = parseWoff2UrlsBySubset(css);
|
|
535
|
+
if (subsetEntries.length === 0) {
|
|
536
|
+
console.error(`[vertz] Error: No .woff2 URLs found in Google Fonts response for "${meta.family}".`);
|
|
537
|
+
result[key] = descriptor;
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
const selectedUrl = selectSubsetUrl(subsetEntries, meta.subsets);
|
|
541
|
+
const familySlug = meta.family.toLowerCase().replace(/\s+/g, "-");
|
|
542
|
+
const fileName = `${familySlug}-${hash}.woff2`;
|
|
543
|
+
const destPath = join3(cacheDir, fileName);
|
|
544
|
+
const size = await downloadFile(selectedUrl, destPath);
|
|
545
|
+
manifest.entries[hash] = { hash, files: [fileName], sizes: [size] };
|
|
546
|
+
const totalKB = Math.round(size / 1024);
|
|
547
|
+
console.log(`[vertz] Fetching Google Font: ${meta.family}... done (1 file, ${totalKB}KB)`);
|
|
548
|
+
const srcPath = toSrcPath(cacheDir, fileName, projectRoot);
|
|
549
|
+
result[key] = createResolvedDescriptor(descriptor, srcPath);
|
|
550
|
+
} catch (error) {
|
|
551
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
552
|
+
console.error(`[vertz] Error resolving Google Font "${meta.family}": ${msg}`);
|
|
553
|
+
result[key] = descriptor;
|
|
554
|
+
}
|
|
555
|
+
}));
|
|
556
|
+
writeManifest(cacheDir, manifest);
|
|
557
|
+
return result;
|
|
558
|
+
}
|
|
559
|
+
function toSrcPath(cacheDir, fileName, projectRoot) {
|
|
560
|
+
const absPath = join3(cacheDir, fileName);
|
|
561
|
+
if (projectRoot) {
|
|
562
|
+
return "/" + relative(projectRoot, absPath);
|
|
563
|
+
}
|
|
564
|
+
return absPath;
|
|
565
|
+
}
|
|
566
|
+
function createResolvedDescriptor(original, srcPath) {
|
|
567
|
+
return {
|
|
568
|
+
__brand: "FontDescriptor",
|
|
569
|
+
family: original.family,
|
|
570
|
+
weight: original.weight,
|
|
571
|
+
style: original.style,
|
|
572
|
+
display: original.display,
|
|
573
|
+
src: srcPath,
|
|
574
|
+
fallback: original.fallback,
|
|
575
|
+
subsets: original.subsets,
|
|
576
|
+
unicodeRange: original.unicodeRange,
|
|
577
|
+
adjustFontFallback: original.adjustFontFallback,
|
|
578
|
+
__google: original.__google
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
398
582
|
// src/ready-gate.ts
|
|
399
583
|
function createReadyGate(options) {
|
|
400
584
|
let ready = false;
|
|
@@ -448,7 +632,7 @@ function createReadyGate(options) {
|
|
|
448
632
|
}
|
|
449
633
|
|
|
450
634
|
// src/source-map-resolver.ts
|
|
451
|
-
import { readFileSync } from "fs";
|
|
635
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
452
636
|
import { resolve as resolvePath } from "path";
|
|
453
637
|
import { originalPositionFor, TraceMap } from "@jridgewell/trace-mapping";
|
|
454
638
|
function extractInlineSourceMap(jsContent) {
|
|
@@ -475,7 +659,7 @@ function resolvePosition(sourceMapJSON, line, column) {
|
|
|
475
659
|
}
|
|
476
660
|
function readLineText(filePath, line) {
|
|
477
661
|
try {
|
|
478
|
-
const content =
|
|
662
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
479
663
|
const lines = content.split(`
|
|
480
664
|
`);
|
|
481
665
|
const idx = line - 1;
|
|
@@ -948,6 +1132,91 @@ function createPrefetchManifestManager(options) {
|
|
|
948
1132
|
import { compileTheme } from "@vertz/ui";
|
|
949
1133
|
import { EntityStore, MemoryCache, QueryEnvelopeStore } from "@vertz/ui/internals";
|
|
950
1134
|
|
|
1135
|
+
// src/css-filter.ts
|
|
1136
|
+
function extractClassNamesFromHTML(html) {
|
|
1137
|
+
const classes = new Set;
|
|
1138
|
+
const attrRegex = /\bclass(?:Name)?="([^"]*)"/g;
|
|
1139
|
+
let match;
|
|
1140
|
+
while ((match = attrRegex.exec(html)) !== null) {
|
|
1141
|
+
const value = match[1];
|
|
1142
|
+
for (const cls of value.split(/\s+/)) {
|
|
1143
|
+
if (cls)
|
|
1144
|
+
classes.add(cls);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
return classes;
|
|
1148
|
+
}
|
|
1149
|
+
function extractClassSelectorsFromCSS(css) {
|
|
1150
|
+
const selectors = new Set;
|
|
1151
|
+
const selectorRegex = /\.([\w-]+)/g;
|
|
1152
|
+
let match;
|
|
1153
|
+
while ((match = selectorRegex.exec(css)) !== null) {
|
|
1154
|
+
selectors.add(match[1]);
|
|
1155
|
+
}
|
|
1156
|
+
return selectors;
|
|
1157
|
+
}
|
|
1158
|
+
function isKeyframesBlock(css) {
|
|
1159
|
+
return /^\s*@keyframes\s/.test(css);
|
|
1160
|
+
}
|
|
1161
|
+
function hasOnlyClassSelectors(css) {
|
|
1162
|
+
let stripped = css.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
1163
|
+
stripped = stripped.replace(/@keyframes\s+[\w-]+\s*\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/g, "");
|
|
1164
|
+
stripped = stripped.replace(/@[\w-]+\s*\([^)]*\)\s*\{/g, "").replace(/^\s*\}/gm, "");
|
|
1165
|
+
const ruleRegex = /([^{}]+)\{/g;
|
|
1166
|
+
let match;
|
|
1167
|
+
let foundAnySelector = false;
|
|
1168
|
+
while ((match = ruleRegex.exec(stripped)) !== null) {
|
|
1169
|
+
const selector = match[1].trim();
|
|
1170
|
+
if (!selector)
|
|
1171
|
+
continue;
|
|
1172
|
+
foundAnySelector = true;
|
|
1173
|
+
if (!selector.startsWith(".")) {
|
|
1174
|
+
return false;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
return foundAnySelector;
|
|
1178
|
+
}
|
|
1179
|
+
function filterCSSByHTML(html, cssStrings) {
|
|
1180
|
+
if (cssStrings.length === 0 || !html)
|
|
1181
|
+
return [];
|
|
1182
|
+
const htmlClasses = extractClassNamesFromHTML(html);
|
|
1183
|
+
const kept = [];
|
|
1184
|
+
const pendingKeyframes = [];
|
|
1185
|
+
for (const css of cssStrings) {
|
|
1186
|
+
if (isKeyframesBlock(css)) {
|
|
1187
|
+
const nameMatch = /@keyframes\s+([\w-]+)/.exec(css);
|
|
1188
|
+
if (nameMatch) {
|
|
1189
|
+
pendingKeyframes.push({ css, name: nameMatch[1] });
|
|
1190
|
+
}
|
|
1191
|
+
continue;
|
|
1192
|
+
}
|
|
1193
|
+
if (!hasOnlyClassSelectors(css)) {
|
|
1194
|
+
kept.push(css);
|
|
1195
|
+
continue;
|
|
1196
|
+
}
|
|
1197
|
+
const cssClasses = extractClassSelectorsFromCSS(css);
|
|
1198
|
+
let used = false;
|
|
1199
|
+
for (const cls of cssClasses) {
|
|
1200
|
+
if (htmlClasses.has(cls)) {
|
|
1201
|
+
used = true;
|
|
1202
|
+
break;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
if (used)
|
|
1206
|
+
kept.push(css);
|
|
1207
|
+
}
|
|
1208
|
+
if (pendingKeyframes.length > 0) {
|
|
1209
|
+
const survivingCss = kept.join(`
|
|
1210
|
+
`);
|
|
1211
|
+
for (const kf of pendingKeyframes) {
|
|
1212
|
+
if (survivingCss.includes(kf.name)) {
|
|
1213
|
+
kept.push(kf.css);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
return kept;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
951
1220
|
// src/dom-shim/index.ts
|
|
952
1221
|
import { setAdapter } from "@vertz/ui/internals";
|
|
953
1222
|
|
|
@@ -1791,12 +2060,14 @@ function safeSerialize(data) {
|
|
|
1791
2060
|
|
|
1792
2061
|
// src/ssr-render.ts
|
|
1793
2062
|
var compiledThemeCache = new WeakMap;
|
|
2063
|
+
var compiledThemeWithMetricsCache = new WeakMap;
|
|
1794
2064
|
function compileThemeCached(theme, fallbackMetrics) {
|
|
1795
|
-
const
|
|
2065
|
+
const cache = fallbackMetrics ? compiledThemeWithMetricsCache : compiledThemeCache;
|
|
2066
|
+
const cached = cache.get(theme);
|
|
1796
2067
|
if (cached)
|
|
1797
2068
|
return cached;
|
|
1798
2069
|
const compiled = compileTheme(theme, { fallbackMetrics });
|
|
1799
|
-
|
|
2070
|
+
cache.set(theme, compiled);
|
|
1800
2071
|
return compiled;
|
|
1801
2072
|
}
|
|
1802
2073
|
function createRequestContext(url) {
|
|
@@ -1832,7 +2103,7 @@ function resolveAppFactory(module) {
|
|
|
1832
2103
|
}
|
|
1833
2104
|
return createApp;
|
|
1834
2105
|
}
|
|
1835
|
-
function collectCSS(themeCss, module) {
|
|
2106
|
+
function collectCSS(themeCss, module, renderedHtml) {
|
|
1836
2107
|
const alreadyIncluded = new Set;
|
|
1837
2108
|
if (themeCss)
|
|
1838
2109
|
alreadyIncluded.add(themeCss);
|
|
@@ -1841,8 +2112,13 @@ function collectCSS(themeCss, module) {
|
|
|
1841
2112
|
alreadyIncluded.add(s);
|
|
1842
2113
|
}
|
|
1843
2114
|
const ssrCtx = ssrStorage.getStore();
|
|
1844
|
-
const
|
|
1845
|
-
const
|
|
2115
|
+
const tracker = ssrCtx?.cssTracker;
|
|
2116
|
+
const useTracker = tracker && tracker.size > 0;
|
|
2117
|
+
const rawComponentCss = useTracker ? Array.from(tracker) : module.getInjectedCSS?.() ?? [];
|
|
2118
|
+
let componentCss = rawComponentCss.filter((s) => !alreadyIncluded.has(s));
|
|
2119
|
+
if (!useTracker && componentCss.length > 0 && renderedHtml) {
|
|
2120
|
+
componentCss = filterCSSByHTML(renderedHtml, componentCss);
|
|
2121
|
+
}
|
|
1846
2122
|
const themeTag = themeCss ? `<style data-vertz-css>${themeCss}</style>` : "";
|
|
1847
2123
|
const globalTag = module.styles && module.styles.length > 0 ? `<style data-vertz-css>${module.styles.join(`
|
|
1848
2124
|
`)}</style>` : "";
|
|
@@ -1923,7 +2199,7 @@ async function ssrRenderToString(module, url, options) {
|
|
|
1923
2199
|
const vnode = toVNode(app);
|
|
1924
2200
|
const stream = renderToStream(vnode);
|
|
1925
2201
|
const html = await streamToString(stream);
|
|
1926
|
-
const css = collectCSS(themeCss, module);
|
|
2202
|
+
const css = collectCSS(themeCss, module, html);
|
|
1927
2203
|
const ssrData = resolvedQueries.length > 0 ? resolvedQueries.map(({ key, data }) => ({ key, data })) : [];
|
|
1928
2204
|
return {
|
|
1929
2205
|
html,
|
|
@@ -2226,7 +2502,7 @@ async function ssrRenderSinglePass(module, url, options) {
|
|
|
2226
2502
|
const vnode = toVNode(app);
|
|
2227
2503
|
const stream = renderToStream(vnode);
|
|
2228
2504
|
const html = await streamToString(stream);
|
|
2229
|
-
const css = collectCSS2(themeCss, module);
|
|
2505
|
+
const css = collectCSS2(themeCss, module, html);
|
|
2230
2506
|
const ssrData = discoveredData.resolvedQueries.map(({ key, data }) => ({
|
|
2231
2507
|
key,
|
|
2232
2508
|
data
|
|
@@ -2378,7 +2654,7 @@ async function renderWithPrefetchedData(module, normalizedUrl, prefetchedData, o
|
|
|
2378
2654
|
matchedRoutePatterns: renderCtx.matchedRoutePatterns
|
|
2379
2655
|
};
|
|
2380
2656
|
}
|
|
2381
|
-
const css = collectCSS2(themeCss, module);
|
|
2657
|
+
const css = collectCSS2(themeCss, module, html);
|
|
2382
2658
|
const ssrData = data.resolvedQueries.map(({ key, data: d }) => ({
|
|
2383
2659
|
key,
|
|
2384
2660
|
data: d
|
|
@@ -2451,7 +2727,7 @@ function extractMethodFromKey(key) {
|
|
|
2451
2727
|
const segments = cleanPath.split("/").filter(Boolean);
|
|
2452
2728
|
return segments.length > 1 ? "get" : "list";
|
|
2453
2729
|
}
|
|
2454
|
-
function collectCSS2(themeCss, module) {
|
|
2730
|
+
function collectCSS2(themeCss, module, renderedHtml) {
|
|
2455
2731
|
const alreadyIncluded = new Set;
|
|
2456
2732
|
if (themeCss)
|
|
2457
2733
|
alreadyIncluded.add(themeCss);
|
|
@@ -2460,8 +2736,13 @@ function collectCSS2(themeCss, module) {
|
|
|
2460
2736
|
alreadyIncluded.add(s);
|
|
2461
2737
|
}
|
|
2462
2738
|
const ssrCtx = ssrStorage.getStore();
|
|
2463
|
-
const
|
|
2464
|
-
const
|
|
2739
|
+
const tracker = ssrCtx?.cssTracker;
|
|
2740
|
+
const useTracker = tracker && tracker.size > 0;
|
|
2741
|
+
const rawComponentCss = useTracker ? Array.from(tracker) : module.getInjectedCSS?.() ?? [];
|
|
2742
|
+
let componentCss = rawComponentCss.filter((s) => !alreadyIncluded.has(s));
|
|
2743
|
+
if (!useTracker && componentCss.length > 0 && renderedHtml) {
|
|
2744
|
+
componentCss = filterCSSByHTML(renderedHtml, componentCss);
|
|
2745
|
+
}
|
|
2465
2746
|
const themeTag = themeCss ? `<style data-vertz-css>${themeCss}</style>` : "";
|
|
2466
2747
|
const globalTag = module.styles && module.styles.length > 0 ? `<style data-vertz-css>${module.styles.join(`
|
|
2467
2748
|
`)}</style>` : "";
|
|
@@ -2472,21 +2753,21 @@ function collectCSS2(themeCss, module) {
|
|
|
2472
2753
|
}
|
|
2473
2754
|
|
|
2474
2755
|
// src/upstream-watcher.ts
|
|
2475
|
-
import { existsSync, lstatSync, readdirSync, realpathSync, watch } from "fs";
|
|
2476
|
-
import { join as
|
|
2756
|
+
import { existsSync as existsSync2, lstatSync, readdirSync, realpathSync, watch } from "fs";
|
|
2757
|
+
import { join as join4 } from "path";
|
|
2477
2758
|
function resolveWorkspacePackages(projectRoot, filter) {
|
|
2478
2759
|
const results = [];
|
|
2479
2760
|
const packageNames = filter === true ? discoverVertzPackages(projectRoot) : parsePackageNames(filter);
|
|
2480
2761
|
for (const { scope, name } of packageNames) {
|
|
2481
|
-
const nmPath =
|
|
2482
|
-
if (!
|
|
2762
|
+
const nmPath = join4(projectRoot, "node_modules", scope, name);
|
|
2763
|
+
if (!existsSync2(nmPath))
|
|
2483
2764
|
continue;
|
|
2484
2765
|
const stat = lstatSync(nmPath);
|
|
2485
2766
|
if (!stat.isSymbolicLink())
|
|
2486
2767
|
continue;
|
|
2487
2768
|
const realPath = realpathSync(nmPath);
|
|
2488
|
-
const distPath =
|
|
2489
|
-
if (!
|
|
2769
|
+
const distPath = join4(realPath, "dist");
|
|
2770
|
+
if (!existsSync2(distPath))
|
|
2490
2771
|
continue;
|
|
2491
2772
|
const fullName = scope ? `${scope}/${name}` : name;
|
|
2492
2773
|
results.push({ name: fullName, distPath });
|
|
@@ -2494,8 +2775,8 @@ function resolveWorkspacePackages(projectRoot, filter) {
|
|
|
2494
2775
|
return results;
|
|
2495
2776
|
}
|
|
2496
2777
|
function discoverVertzPackages(projectRoot) {
|
|
2497
|
-
const scopeDir =
|
|
2498
|
-
if (!
|
|
2778
|
+
const scopeDir = join4(projectRoot, "node_modules", "@vertz");
|
|
2779
|
+
if (!existsSync2(scopeDir))
|
|
2499
2780
|
return [];
|
|
2500
2781
|
return readdirSync(scopeDir).filter((entry) => !entry.startsWith(".")).map((entry) => ({ scope: "@vertz", name: entry }));
|
|
2501
2782
|
}
|
|
@@ -2553,7 +2834,7 @@ function createUpstreamWatcher(options) {
|
|
|
2553
2834
|
// src/bun-dev-server.ts
|
|
2554
2835
|
function detectFaviconTag(projectRoot) {
|
|
2555
2836
|
const faviconPath = resolve(projectRoot, "public", "favicon.svg");
|
|
2556
|
-
return
|
|
2837
|
+
return existsSync3(faviconPath) ? '<link rel="icon" type="image/svg+xml" href="/favicon.svg">' : "";
|
|
2557
2838
|
}
|
|
2558
2839
|
var STALE_GRAPH_PATTERNS = [
|
|
2559
2840
|
/Export named ['"].*['"] not found in module/i,
|
|
@@ -2982,7 +3263,7 @@ function createBunDevServer(options) {
|
|
|
2982
3263
|
installFetchProxy();
|
|
2983
3264
|
}
|
|
2984
3265
|
const devDir = resolve(projectRoot, ".vertz", "dev");
|
|
2985
|
-
|
|
3266
|
+
mkdirSync2(devDir, { recursive: true });
|
|
2986
3267
|
const logger = createDebugLogger(devDir);
|
|
2987
3268
|
const diagnostics = new DiagnosticsCollector;
|
|
2988
3269
|
let aotManifestManager = null;
|
|
@@ -3206,7 +3487,7 @@ function createBunDevServer(options) {
|
|
|
3206
3487
|
if (!openapi)
|
|
3207
3488
|
return null;
|
|
3208
3489
|
try {
|
|
3209
|
-
const specContent =
|
|
3490
|
+
const specContent = readFileSync3(openapi.specPath, "utf-8");
|
|
3210
3491
|
return JSON.parse(specContent);
|
|
3211
3492
|
} catch (err) {
|
|
3212
3493
|
console.error("[Server] Error reading OpenAPI spec:", err);
|
|
@@ -3214,7 +3495,7 @@ function createBunDevServer(options) {
|
|
|
3214
3495
|
}
|
|
3215
3496
|
};
|
|
3216
3497
|
const setupOpenAPIWatcher = () => {
|
|
3217
|
-
if (!openapi || !
|
|
3498
|
+
if (!openapi || !existsSync3(openapi.specPath))
|
|
3218
3499
|
return;
|
|
3219
3500
|
cachedSpec = loadOpenAPISpec();
|
|
3220
3501
|
if (cachedSpec === null)
|
|
@@ -3240,7 +3521,7 @@ function createBunDevServer(options) {
|
|
|
3240
3521
|
headers: { "Content-Type": "application/json" }
|
|
3241
3522
|
});
|
|
3242
3523
|
}
|
|
3243
|
-
if (openapi &&
|
|
3524
|
+
if (openapi && existsSync3(openapi.specPath)) {
|
|
3244
3525
|
cachedSpec = loadOpenAPISpec();
|
|
3245
3526
|
if (cachedSpec) {
|
|
3246
3527
|
return new Response(JSON.stringify(cachedSpec), {
|
|
@@ -3330,10 +3611,10 @@ function createBunDevServer(options) {
|
|
|
3330
3611
|
let ssrMod;
|
|
3331
3612
|
try {
|
|
3332
3613
|
if (isRestarting) {
|
|
3333
|
-
|
|
3614
|
+
mkdirSync2(devDir, { recursive: true });
|
|
3334
3615
|
const ssrBootPath = resolve(devDir, "ssr-reload-entry.ts");
|
|
3335
3616
|
const ts = Date.now();
|
|
3336
|
-
|
|
3617
|
+
writeFileSync3(ssrBootPath, `export * from '${entryPath}';
|
|
3337
3618
|
`);
|
|
3338
3619
|
ssrMod = await import(`${ssrBootPath}?t=${ts}`);
|
|
3339
3620
|
} else {
|
|
@@ -3358,6 +3639,18 @@ function createBunDevServer(options) {
|
|
|
3358
3639
|
process.exit(1);
|
|
3359
3640
|
}
|
|
3360
3641
|
}
|
|
3642
|
+
const fontCacheDir = resolve(projectRoot, ".vertz", "fonts");
|
|
3643
|
+
if (ssrMod.theme?.fonts) {
|
|
3644
|
+
try {
|
|
3645
|
+
const resolvedFonts = await resolveGoogleFonts(ssrMod.theme.fonts, fontCacheDir, projectRoot);
|
|
3646
|
+
ssrMod = {
|
|
3647
|
+
...ssrMod,
|
|
3648
|
+
theme: { ...ssrMod.theme, fonts: resolvedFonts }
|
|
3649
|
+
};
|
|
3650
|
+
} catch (e) {
|
|
3651
|
+
console.warn("[Server] Failed to resolve Google Fonts:", e);
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3361
3654
|
let fontFallbackMetrics;
|
|
3362
3655
|
if (ssrMod.theme?.fonts) {
|
|
3363
3656
|
try {
|
|
@@ -3369,13 +3662,13 @@ function createBunDevServer(options) {
|
|
|
3369
3662
|
let prefetchManager = null;
|
|
3370
3663
|
const srcDir = resolve(projectRoot, "src");
|
|
3371
3664
|
const routerCandidates = [resolve(srcDir, "router.tsx"), resolve(srcDir, "router.ts")];
|
|
3372
|
-
const routerPath = routerCandidates.find((p) =>
|
|
3665
|
+
const routerPath = routerCandidates.find((p) => existsSync3(p));
|
|
3373
3666
|
if (routerPath) {
|
|
3374
3667
|
prefetchManager = createPrefetchManifestManager({
|
|
3375
3668
|
routerPath,
|
|
3376
3669
|
readFile: (path) => {
|
|
3377
3670
|
try {
|
|
3378
|
-
return
|
|
3671
|
+
return readFileSync3(path, "utf-8");
|
|
3379
3672
|
} catch {
|
|
3380
3673
|
return;
|
|
3381
3674
|
}
|
|
@@ -3387,12 +3680,12 @@ function createBunDevServer(options) {
|
|
|
3387
3680
|
const base = resolve(dir, specifier);
|
|
3388
3681
|
for (const ext of [".tsx", ".ts", ".jsx", ".js"]) {
|
|
3389
3682
|
const candidate = `${base}${ext}`;
|
|
3390
|
-
if (
|
|
3683
|
+
if (existsSync3(candidate))
|
|
3391
3684
|
return candidate;
|
|
3392
3685
|
}
|
|
3393
3686
|
for (const ext of [".tsx", ".ts"]) {
|
|
3394
3687
|
const candidate = resolve(base, `index${ext}`);
|
|
3395
|
-
if (
|
|
3688
|
+
if (existsSync3(candidate))
|
|
3396
3689
|
return candidate;
|
|
3397
3690
|
}
|
|
3398
3691
|
return;
|
|
@@ -3416,7 +3709,7 @@ function createBunDevServer(options) {
|
|
|
3416
3709
|
aotManifestManager = createAotManifestManager({
|
|
3417
3710
|
readFile: (path) => {
|
|
3418
3711
|
try {
|
|
3419
|
-
return
|
|
3712
|
+
return readFileSync3(path, "utf-8");
|
|
3420
3713
|
} catch {
|
|
3421
3714
|
return;
|
|
3422
3715
|
}
|
|
@@ -3443,9 +3736,9 @@ function createBunDevServer(options) {
|
|
|
3443
3736
|
console.warn("[Server] Failed to build AOT manifest:", e instanceof Error ? e.message : e);
|
|
3444
3737
|
aotManifestManager = null;
|
|
3445
3738
|
}
|
|
3446
|
-
|
|
3739
|
+
mkdirSync2(devDir, { recursive: true });
|
|
3447
3740
|
const frInitPath = resolve(devDir, "fast-refresh-init.ts");
|
|
3448
|
-
|
|
3741
|
+
writeFileSync3(frInitPath, `import '@vertz/ui-server/fast-refresh-runtime';
|
|
3449
3742
|
if (import.meta.hot) import.meta.hot.accept();
|
|
3450
3743
|
`);
|
|
3451
3744
|
const hmrShellHtml = `<!doctype html>
|
|
@@ -3457,7 +3750,7 @@ if (import.meta.hot) import.meta.hot.accept();
|
|
|
3457
3750
|
<script type="module" src="${clientSrc}"></script>
|
|
3458
3751
|
</body></html>`;
|
|
3459
3752
|
const hmrShellPath = resolve(devDir, "hmr-shell.html");
|
|
3460
|
-
|
|
3753
|
+
writeFileSync3(hmrShellPath, hmrShellHtml);
|
|
3461
3754
|
const hmrShellModule = __require(hmrShellPath);
|
|
3462
3755
|
setupOpenAPIWatcher();
|
|
3463
3756
|
let bundledScriptUrl = null;
|
|
@@ -3924,7 +4217,7 @@ data: {}
|
|
|
3924
4217
|
return false;
|
|
3925
4218
|
}
|
|
3926
4219
|
stopped = false;
|
|
3927
|
-
if (
|
|
4220
|
+
if (existsSync3(srcDir)) {
|
|
3928
4221
|
srcWatcherRef = watch2(srcDir, { recursive: true }, (_event, filename) => {
|
|
3929
4222
|
if (!filename)
|
|
3930
4223
|
return;
|
|
@@ -4058,12 +4351,21 @@ data: {}
|
|
|
4058
4351
|
const cacheCleared = clearSSRRequireCache();
|
|
4059
4352
|
logger.log("watcher", "cache-cleared", { entries: cacheCleared });
|
|
4060
4353
|
const ssrWrapperPath = resolve(devDir, "ssr-reload-entry.ts");
|
|
4061
|
-
|
|
4062
|
-
|
|
4354
|
+
mkdirSync2(devDir, { recursive: true });
|
|
4355
|
+
writeFileSync3(ssrWrapperPath, `export * from '${entryPath}';
|
|
4063
4356
|
`);
|
|
4064
4357
|
const ssrReloadStart = performance.now();
|
|
4065
4358
|
try {
|
|
4066
|
-
|
|
4359
|
+
let freshMod = await import(`${ssrWrapperPath}?t=${Date.now()}`);
|
|
4360
|
+
if (freshMod.theme?.fonts) {
|
|
4361
|
+
try {
|
|
4362
|
+
const resolvedFonts = await resolveGoogleFonts(freshMod.theme.fonts, fontCacheDir, projectRoot);
|
|
4363
|
+
freshMod = {
|
|
4364
|
+
...freshMod,
|
|
4365
|
+
theme: { ...freshMod.theme, fonts: resolvedFonts }
|
|
4366
|
+
};
|
|
4367
|
+
} catch {}
|
|
4368
|
+
}
|
|
4067
4369
|
ssrMod = freshMod;
|
|
4068
4370
|
ssrFallback = false;
|
|
4069
4371
|
if (freshMod.theme?.fonts) {
|
|
@@ -4088,11 +4390,20 @@ data: {}
|
|
|
4088
4390
|
if (stopped)
|
|
4089
4391
|
return;
|
|
4090
4392
|
clearSSRRequireCache();
|
|
4091
|
-
|
|
4092
|
-
|
|
4393
|
+
mkdirSync2(devDir, { recursive: true });
|
|
4394
|
+
writeFileSync3(ssrWrapperPath, `export * from '${entryPath}';
|
|
4093
4395
|
`);
|
|
4094
4396
|
try {
|
|
4095
|
-
|
|
4397
|
+
let freshMod = await import(`${ssrWrapperPath}?t=${Date.now()}`);
|
|
4398
|
+
if (freshMod.theme?.fonts) {
|
|
4399
|
+
try {
|
|
4400
|
+
const resolvedFonts = await resolveGoogleFonts(freshMod.theme.fonts, fontCacheDir, projectRoot);
|
|
4401
|
+
freshMod = {
|
|
4402
|
+
...freshMod,
|
|
4403
|
+
theme: { ...freshMod.theme, fonts: resolvedFonts }
|
|
4404
|
+
};
|
|
4405
|
+
} catch {}
|
|
4406
|
+
}
|
|
4096
4407
|
ssrMod = freshMod;
|
|
4097
4408
|
ssrFallback = false;
|
|
4098
4409
|
if (freshMod.theme?.fonts) {
|