@vertz/ui-server 0.2.25 → 0.2.26
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 +244 -2
- package/dist/bun-plugin/index.d.ts +1 -1
- package/dist/index.d.ts +333 -85
- package/dist/index.js +655 -153
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -35,6 +35,62 @@ import {
|
|
|
35
35
|
toVNode
|
|
36
36
|
} from "./shared/chunk-gcwqkynf.js";
|
|
37
37
|
|
|
38
|
+
// src/aot-manifest-build.ts
|
|
39
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
40
|
+
import { join } from "node:path";
|
|
41
|
+
import { compileForSSRAot } from "@vertz/ui-compiler";
|
|
42
|
+
function generateAotBuildManifest(srcDir) {
|
|
43
|
+
const components = {};
|
|
44
|
+
const classificationLog = [];
|
|
45
|
+
const tsxFiles = collectTsxFiles(srcDir);
|
|
46
|
+
for (const filePath of tsxFiles) {
|
|
47
|
+
try {
|
|
48
|
+
const source = readFileSync(filePath, "utf-8");
|
|
49
|
+
const result = compileForSSRAot(source, { filename: filePath });
|
|
50
|
+
for (const comp of result.components) {
|
|
51
|
+
components[comp.name] = {
|
|
52
|
+
tier: comp.tier,
|
|
53
|
+
holes: comp.holes
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
} catch (e) {
|
|
57
|
+
classificationLog.push(`⚠ ${filePath}: ${e instanceof Error ? e.message : "compilation failed"}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
let aotCount = 0;
|
|
61
|
+
let runtimeCount = 0;
|
|
62
|
+
for (const [name, entry] of Object.entries(components)) {
|
|
63
|
+
let line = `${name}: ${entry.tier}`;
|
|
64
|
+
if (entry.holes.length > 0) {
|
|
65
|
+
const holeLabel = entry.holes.length === 1 ? "hole" : "holes";
|
|
66
|
+
line += `, ${entry.holes.length} ${holeLabel} (${entry.holes.join(", ")})`;
|
|
67
|
+
}
|
|
68
|
+
classificationLog.push(line);
|
|
69
|
+
if (entry.tier === "runtime-fallback") {
|
|
70
|
+
runtimeCount++;
|
|
71
|
+
} else {
|
|
72
|
+
aotCount++;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const total = aotCount + runtimeCount;
|
|
76
|
+
if (total > 0) {
|
|
77
|
+
const pct = Math.round(aotCount / total * 100);
|
|
78
|
+
classificationLog.push(`Coverage: ${aotCount}/${total} components (${pct}%)`);
|
|
79
|
+
}
|
|
80
|
+
return { components, classificationLog };
|
|
81
|
+
}
|
|
82
|
+
function collectTsxFiles(dir) {
|
|
83
|
+
const files = [];
|
|
84
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
85
|
+
const fullPath = join(dir, entry.name);
|
|
86
|
+
if (entry.isDirectory()) {
|
|
87
|
+
files.push(...collectTsxFiles(fullPath));
|
|
88
|
+
} else if (entry.name.endsWith(".tsx")) {
|
|
89
|
+
files.push(fullPath);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return files;
|
|
93
|
+
}
|
|
38
94
|
// src/asset-pipeline.ts
|
|
39
95
|
function renderAssetTags(assets) {
|
|
40
96
|
if (assets.length === 0)
|
|
@@ -63,7 +119,7 @@ function inlineCriticalCss(css) {
|
|
|
63
119
|
// src/font-metrics.ts
|
|
64
120
|
import { access as fsAccess } from "node:fs/promises";
|
|
65
121
|
import { readFile } from "node:fs/promises";
|
|
66
|
-
import { join } from "node:path";
|
|
122
|
+
import { join as join2 } from "node:path";
|
|
67
123
|
import { fromBuffer } from "@capsizecss/unpack";
|
|
68
124
|
var SYSTEM_FONT_METRICS = {
|
|
69
125
|
Arial: {
|
|
@@ -132,12 +188,12 @@ function getPrimarySrcPath(descriptor) {
|
|
|
132
188
|
}
|
|
133
189
|
async function resolveFilePath(urlPath, rootDir) {
|
|
134
190
|
const cleaned = urlPath.startsWith("/") ? urlPath.slice(1) : urlPath;
|
|
135
|
-
const direct =
|
|
191
|
+
const direct = join2(rootDir, cleaned);
|
|
136
192
|
try {
|
|
137
193
|
await fsAccess(direct);
|
|
138
194
|
return direct;
|
|
139
195
|
} catch {
|
|
140
|
-
return
|
|
196
|
+
return join2(rootDir, "public", cleaned);
|
|
141
197
|
}
|
|
142
198
|
}
|
|
143
199
|
async function extractFontMetrics(fonts, rootDir) {
|
|
@@ -467,144 +523,168 @@ function evaluateAccessRule(rule, session) {
|
|
|
467
523
|
return false;
|
|
468
524
|
}
|
|
469
525
|
}
|
|
470
|
-
// src/ssr-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
const { routerPath, readFile: readFile2, resolveImport } = options;
|
|
509
|
-
let currentManifest = null;
|
|
510
|
-
let currentSSRManifest;
|
|
511
|
-
let rebuildCount = 0;
|
|
512
|
-
let lastRebuildMs = null;
|
|
513
|
-
let lastRebuildAt = null;
|
|
514
|
-
let fileToRouteIndices = new Map;
|
|
515
|
-
function buildFileIndex(routes) {
|
|
516
|
-
const index = new Map;
|
|
517
|
-
for (let i = 0;i < routes.length; i++) {
|
|
518
|
-
const file = routes[i]?.file;
|
|
519
|
-
if (file) {
|
|
520
|
-
const existing = index.get(file) ?? [];
|
|
521
|
-
existing.push(i);
|
|
522
|
-
index.set(file, existing);
|
|
526
|
+
// src/ssr-aot-diagnostics.ts
|
|
527
|
+
var MAX_DIVERGENCES = 20;
|
|
528
|
+
|
|
529
|
+
class AotDiagnostics {
|
|
530
|
+
_components = new Map;
|
|
531
|
+
_divergences = [];
|
|
532
|
+
recordCompilation(components) {
|
|
533
|
+
for (const comp of components) {
|
|
534
|
+
this._components.set(comp.name, {
|
|
535
|
+
tier: comp.tier,
|
|
536
|
+
holes: comp.holes
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
recordDivergence(component, aotHtml, domHtml) {
|
|
541
|
+
this._divergences.push({
|
|
542
|
+
component,
|
|
543
|
+
aotHtml,
|
|
544
|
+
domHtml,
|
|
545
|
+
timestamp: new Date().toISOString()
|
|
546
|
+
});
|
|
547
|
+
if (this._divergences.length > MAX_DIVERGENCES) {
|
|
548
|
+
this._divergences = this._divergences.slice(this._divergences.length - MAX_DIVERGENCES);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
clear() {
|
|
552
|
+
this._components.clear();
|
|
553
|
+
this._divergences = [];
|
|
554
|
+
}
|
|
555
|
+
clearComponents() {
|
|
556
|
+
this._components.clear();
|
|
557
|
+
}
|
|
558
|
+
getClassificationLog() {
|
|
559
|
+
const lines = [];
|
|
560
|
+
for (const [name, comp] of this._components) {
|
|
561
|
+
let line = `${name}: ${comp.tier}`;
|
|
562
|
+
if (comp.holes.length > 0) {
|
|
563
|
+
line += `, ${comp.holes.length} hole${comp.holes.length > 1 ? "s" : ""} (${comp.holes.join(", ")})`;
|
|
523
564
|
}
|
|
565
|
+
lines.push(line);
|
|
524
566
|
}
|
|
525
|
-
|
|
567
|
+
const snapshot = this.getSnapshot();
|
|
568
|
+
const { total, aot, percentage } = snapshot.coverage;
|
|
569
|
+
if (total > 0) {
|
|
570
|
+
lines.push(`Coverage: ${aot}/${total} components (${percentage}%)`);
|
|
571
|
+
}
|
|
572
|
+
return lines;
|
|
526
573
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
if (
|
|
532
|
-
|
|
574
|
+
getSnapshot() {
|
|
575
|
+
let aot = 0;
|
|
576
|
+
let runtime = 0;
|
|
577
|
+
for (const comp of this._components.values()) {
|
|
578
|
+
if (comp.tier === "runtime-fallback") {
|
|
579
|
+
runtime++;
|
|
533
580
|
} else {
|
|
534
|
-
|
|
581
|
+
aot++;
|
|
535
582
|
}
|
|
536
583
|
}
|
|
584
|
+
const total = aot + runtime;
|
|
537
585
|
return {
|
|
538
|
-
|
|
539
|
-
|
|
586
|
+
components: Object.fromEntries(this._components),
|
|
587
|
+
coverage: {
|
|
588
|
+
total,
|
|
589
|
+
aot,
|
|
590
|
+
runtime,
|
|
591
|
+
percentage: total === 0 ? 0 : Math.round(aot / total * 100)
|
|
592
|
+
},
|
|
593
|
+
divergences: [...this._divergences]
|
|
540
594
|
};
|
|
541
595
|
}
|
|
542
|
-
|
|
596
|
+
}
|
|
597
|
+
// src/ssr-aot-manifest-dev.ts
|
|
598
|
+
import { compileForSSRAot as compileForSSRAot2 } from "@vertz/ui-compiler";
|
|
599
|
+
function createAotManifestManager(options) {
|
|
600
|
+
const { readFile: readFile2, listFiles } = options;
|
|
601
|
+
let currentManifest = null;
|
|
602
|
+
const diagnostics = new AotDiagnostics;
|
|
603
|
+
let rebuildCount = 0;
|
|
604
|
+
let lastRebuildMs = null;
|
|
605
|
+
let lastRebuildAt = null;
|
|
606
|
+
function compileFile(filePath, source) {
|
|
607
|
+
try {
|
|
608
|
+
const result = compileForSSRAot2(source, { filename: filePath });
|
|
609
|
+
return result.components.map((comp) => ({
|
|
610
|
+
name: comp.name,
|
|
611
|
+
entry: {
|
|
612
|
+
tier: comp.tier,
|
|
613
|
+
holes: comp.holes,
|
|
614
|
+
file: filePath
|
|
615
|
+
}
|
|
616
|
+
}));
|
|
617
|
+
} catch {
|
|
618
|
+
return [];
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
function updateDiagnostics(manifest, isFullBuild) {
|
|
622
|
+
if (isFullBuild) {
|
|
623
|
+
diagnostics.clear();
|
|
624
|
+
} else {
|
|
625
|
+
diagnostics.clearComponents();
|
|
626
|
+
}
|
|
627
|
+
const entries = Object.entries(manifest.components).map(([name, entry]) => ({
|
|
628
|
+
name,
|
|
629
|
+
tier: entry.tier,
|
|
630
|
+
holes: entry.holes
|
|
631
|
+
}));
|
|
632
|
+
diagnostics.recordCompilation(entries);
|
|
633
|
+
}
|
|
634
|
+
function fullBuild() {
|
|
543
635
|
const start = performance.now();
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
636
|
+
const files = listFiles();
|
|
637
|
+
const components = {};
|
|
638
|
+
for (const filePath of files) {
|
|
639
|
+
if (!filePath.endsWith(".tsx"))
|
|
640
|
+
continue;
|
|
641
|
+
const source = readFile2(filePath);
|
|
642
|
+
if (!source)
|
|
643
|
+
continue;
|
|
644
|
+
const entries = compileFile(filePath, source);
|
|
645
|
+
for (const { name, entry } of entries) {
|
|
646
|
+
components[name] = entry;
|
|
647
|
+
}
|
|
547
648
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
resolveImport
|
|
554
|
-
});
|
|
555
|
-
currentManifest = manifest;
|
|
556
|
-
currentSSRManifest = toSSRManifest(manifest);
|
|
557
|
-
fileToRouteIndices = buildFileIndex(manifest.routes);
|
|
558
|
-
rebuildCount++;
|
|
559
|
-
lastRebuildMs = Math.round(performance.now() - start);
|
|
560
|
-
lastRebuildAt = new Date().toISOString();
|
|
561
|
-
} catch {}
|
|
649
|
+
currentManifest = { components };
|
|
650
|
+
updateDiagnostics(currentManifest, true);
|
|
651
|
+
rebuildCount++;
|
|
652
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
653
|
+
lastRebuildAt = new Date().toISOString();
|
|
562
654
|
}
|
|
563
655
|
function incrementalUpdate(filePath, sourceText) {
|
|
564
656
|
if (!currentManifest)
|
|
565
657
|
return;
|
|
566
|
-
const indices = fileToRouteIndices.get(filePath);
|
|
567
|
-
if (!indices || indices.length === 0)
|
|
568
|
-
return;
|
|
569
658
|
const start = performance.now();
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
const existing = newRoutes[idx];
|
|
575
|
-
if (existing) {
|
|
576
|
-
newRoutes[idx] = {
|
|
577
|
-
...existing,
|
|
578
|
-
queries: analysis.queries,
|
|
579
|
-
params: analysis.params
|
|
580
|
-
};
|
|
581
|
-
}
|
|
659
|
+
const newComponents = { ...currentManifest.components };
|
|
660
|
+
for (const [name, entry] of Object.entries(newComponents)) {
|
|
661
|
+
if (entry.file === filePath) {
|
|
662
|
+
delete newComponents[name];
|
|
582
663
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
664
|
+
}
|
|
665
|
+
if (sourceText.trim()) {
|
|
666
|
+
const entries = compileFile(filePath, sourceText);
|
|
667
|
+
for (const { name, entry } of entries) {
|
|
668
|
+
newComponents[name] = entry;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
currentManifest = { components: newComponents };
|
|
672
|
+
updateDiagnostics(currentManifest, false);
|
|
673
|
+
rebuildCount++;
|
|
674
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
675
|
+
lastRebuildAt = new Date().toISOString();
|
|
594
676
|
}
|
|
595
677
|
return {
|
|
596
678
|
build() {
|
|
597
679
|
fullBuild();
|
|
598
680
|
},
|
|
599
681
|
onFileChange(filePath, sourceText) {
|
|
600
|
-
if (filePath
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
incrementalUpdate(filePath, sourceText);
|
|
604
|
-
}
|
|
682
|
+
if (!filePath.endsWith(".tsx"))
|
|
683
|
+
return;
|
|
684
|
+
incrementalUpdate(filePath, sourceText);
|
|
605
685
|
},
|
|
606
|
-
|
|
607
|
-
return
|
|
686
|
+
getManifest() {
|
|
687
|
+
return currentManifest;
|
|
608
688
|
},
|
|
609
689
|
getSnapshot() {
|
|
610
690
|
return {
|
|
@@ -613,9 +693,53 @@ function createPrefetchManifestManager(options) {
|
|
|
613
693
|
lastRebuildMs,
|
|
614
694
|
lastRebuildAt
|
|
615
695
|
};
|
|
696
|
+
},
|
|
697
|
+
getDiagnostics() {
|
|
698
|
+
return diagnostics;
|
|
616
699
|
}
|
|
617
700
|
};
|
|
618
701
|
}
|
|
702
|
+
// src/ssr-aot-pipeline.ts
|
|
703
|
+
import { compileTheme as compileTheme3 } from "@vertz/ui";
|
|
704
|
+
|
|
705
|
+
// src/ssr-route-matcher.ts
|
|
706
|
+
function matchUrlToPatterns(url, patterns) {
|
|
707
|
+
const path = (url.split("?")[0] ?? "").split("#")[0] ?? "";
|
|
708
|
+
const matches = [];
|
|
709
|
+
for (const pattern of patterns) {
|
|
710
|
+
const result = matchPattern(path, pattern);
|
|
711
|
+
if (result) {
|
|
712
|
+
matches.push(result);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
matches.sort((a, b) => {
|
|
716
|
+
const aSegments = a.pattern.split("/").length;
|
|
717
|
+
const bSegments = b.pattern.split("/").length;
|
|
718
|
+
return aSegments - bSegments;
|
|
719
|
+
});
|
|
720
|
+
return matches;
|
|
721
|
+
}
|
|
722
|
+
function matchPattern(path, pattern) {
|
|
723
|
+
const pathSegments = path.split("/").filter(Boolean);
|
|
724
|
+
const patternSegments = pattern.split("/").filter(Boolean);
|
|
725
|
+
if (patternSegments.length > pathSegments.length)
|
|
726
|
+
return;
|
|
727
|
+
const params = {};
|
|
728
|
+
for (let i = 0;i < patternSegments.length; i++) {
|
|
729
|
+
const seg = patternSegments[i];
|
|
730
|
+
const val = pathSegments[i];
|
|
731
|
+
if (seg.startsWith(":")) {
|
|
732
|
+
params[seg.slice(1)] = val;
|
|
733
|
+
} else if (seg !== val) {
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return { pattern, params };
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// src/ssr-single-pass.ts
|
|
741
|
+
import { compileTheme as compileTheme2 } from "@vertz/ui";
|
|
742
|
+
|
|
619
743
|
// src/ssr-manifest-prefetch.ts
|
|
620
744
|
function reconstructDescriptors(queries, routeParams, apiClient) {
|
|
621
745
|
if (!apiClient)
|
|
@@ -704,42 +828,8 @@ function resolveQueryBindings(bindings, routeParams) {
|
|
|
704
828
|
resolved.limit = bindings.limit;
|
|
705
829
|
return resolved;
|
|
706
830
|
}
|
|
707
|
-
|
|
708
|
-
function matchUrlToPatterns(url, patterns) {
|
|
709
|
-
const path = (url.split("?")[0] ?? "").split("#")[0] ?? "";
|
|
710
|
-
const matches = [];
|
|
711
|
-
for (const pattern of patterns) {
|
|
712
|
-
const result = matchPattern(path, pattern);
|
|
713
|
-
if (result) {
|
|
714
|
-
matches.push(result);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
matches.sort((a, b) => {
|
|
718
|
-
const aSegments = a.pattern.split("/").length;
|
|
719
|
-
const bSegments = b.pattern.split("/").length;
|
|
720
|
-
return aSegments - bSegments;
|
|
721
|
-
});
|
|
722
|
-
return matches;
|
|
723
|
-
}
|
|
724
|
-
function matchPattern(path, pattern) {
|
|
725
|
-
const pathSegments = path.split("/").filter(Boolean);
|
|
726
|
-
const patternSegments = pattern.split("/").filter(Boolean);
|
|
727
|
-
if (patternSegments.length > pathSegments.length)
|
|
728
|
-
return;
|
|
729
|
-
const params = {};
|
|
730
|
-
for (let i = 0;i < patternSegments.length; i++) {
|
|
731
|
-
const seg = patternSegments[i];
|
|
732
|
-
const val = pathSegments[i];
|
|
733
|
-
if (seg.startsWith(":")) {
|
|
734
|
-
params[seg.slice(1)] = val;
|
|
735
|
-
} else if (seg !== val) {
|
|
736
|
-
return;
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
return { pattern, params };
|
|
740
|
-
}
|
|
831
|
+
|
|
741
832
|
// src/ssr-single-pass.ts
|
|
742
|
-
import { compileTheme as compileTheme2 } from "@vertz/ui";
|
|
743
833
|
async function ssrRenderSinglePass(module, url, options) {
|
|
744
834
|
if (options?.prefetch === false) {
|
|
745
835
|
return ssrRenderToString(module, url, options);
|
|
@@ -1031,6 +1121,408 @@ function collectCSS(themeCss, module) {
|
|
|
1031
1121
|
return [themeTag, globalTag, componentTag].filter(Boolean).join(`
|
|
1032
1122
|
`);
|
|
1033
1123
|
}
|
|
1124
|
+
|
|
1125
|
+
// src/ssr-aot-pipeline.ts
|
|
1126
|
+
function createHoles(holeNames, module, url, queryCache, ssrAuth) {
|
|
1127
|
+
if (holeNames.length === 0)
|
|
1128
|
+
return {};
|
|
1129
|
+
const holes = {};
|
|
1130
|
+
for (const name of holeNames) {
|
|
1131
|
+
holes[name] = () => {
|
|
1132
|
+
const holeCtx = createRequestContext(url);
|
|
1133
|
+
for (const [key, data] of queryCache) {
|
|
1134
|
+
holeCtx.queryCache.set(key, data);
|
|
1135
|
+
}
|
|
1136
|
+
if (ssrAuth) {
|
|
1137
|
+
holeCtx.ssrAuth = ssrAuth;
|
|
1138
|
+
}
|
|
1139
|
+
holeCtx.resolvedComponents = new Map;
|
|
1140
|
+
return ssrStorage.run(holeCtx, () => {
|
|
1141
|
+
ensureDomShim2();
|
|
1142
|
+
const factory = resolveHoleComponent(module, name);
|
|
1143
|
+
if (!factory) {
|
|
1144
|
+
return `<!-- AOT hole: ${name} not found -->`;
|
|
1145
|
+
}
|
|
1146
|
+
const node = factory();
|
|
1147
|
+
const vnode = toVNode(node);
|
|
1148
|
+
return serializeToHtml(vnode);
|
|
1149
|
+
});
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
return holes;
|
|
1153
|
+
}
|
|
1154
|
+
function resolveHoleComponent(module, name) {
|
|
1155
|
+
const moduleRecord = module;
|
|
1156
|
+
const exported = moduleRecord[name];
|
|
1157
|
+
if (typeof exported === "function") {
|
|
1158
|
+
return exported;
|
|
1159
|
+
}
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
async function ssrRenderAot(module, url, options) {
|
|
1163
|
+
const { aotManifest, manifest } = options;
|
|
1164
|
+
const ssrTimeout = options.ssrTimeout ?? 300;
|
|
1165
|
+
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
1166
|
+
const fallbackOptions = {
|
|
1167
|
+
ssrTimeout,
|
|
1168
|
+
fallbackMetrics: options.fallbackMetrics,
|
|
1169
|
+
ssrAuth: options.ssrAuth,
|
|
1170
|
+
manifest,
|
|
1171
|
+
prefetchSession: options.prefetchSession
|
|
1172
|
+
};
|
|
1173
|
+
const aotPatterns = Object.keys(aotManifest.routes);
|
|
1174
|
+
const matches = matchUrlToPatterns(normalizedUrl, aotPatterns);
|
|
1175
|
+
if (matches.length === 0) {
|
|
1176
|
+
return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1177
|
+
}
|
|
1178
|
+
const match = matches[matches.length - 1];
|
|
1179
|
+
if (!match) {
|
|
1180
|
+
return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1181
|
+
}
|
|
1182
|
+
const aotEntry = aotManifest.routes[match.pattern];
|
|
1183
|
+
if (!aotEntry) {
|
|
1184
|
+
return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1185
|
+
}
|
|
1186
|
+
const queryCache = new Map;
|
|
1187
|
+
try {
|
|
1188
|
+
setGlobalSSRTimeout(ssrTimeout);
|
|
1189
|
+
const holes = createHoles(aotEntry.holes, module, normalizedUrl, queryCache, options.ssrAuth);
|
|
1190
|
+
const ctx = {
|
|
1191
|
+
holes,
|
|
1192
|
+
getData: (key) => queryCache.get(key),
|
|
1193
|
+
session: options.prefetchSession,
|
|
1194
|
+
params: match.params
|
|
1195
|
+
};
|
|
1196
|
+
const data = {};
|
|
1197
|
+
for (const [key, value] of queryCache) {
|
|
1198
|
+
data[key] = value;
|
|
1199
|
+
}
|
|
1200
|
+
const html = aotEntry.render(data, ctx);
|
|
1201
|
+
if (options.diagnostics && isAotDebugEnabled()) {
|
|
1202
|
+
try {
|
|
1203
|
+
const domResult = await ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1204
|
+
if (domResult.html !== html) {
|
|
1205
|
+
options.diagnostics.recordDivergence(match.pattern, html, domResult.html);
|
|
1206
|
+
}
|
|
1207
|
+
} catch {}
|
|
1208
|
+
}
|
|
1209
|
+
const css = collectCSSFromModule(module, options.fallbackMetrics);
|
|
1210
|
+
const ssrData = [];
|
|
1211
|
+
for (const [key, data2] of queryCache) {
|
|
1212
|
+
ssrData.push({ key, data: JSON.parse(JSON.stringify(data2)) });
|
|
1213
|
+
}
|
|
1214
|
+
return {
|
|
1215
|
+
html,
|
|
1216
|
+
css: css.cssString,
|
|
1217
|
+
ssrData,
|
|
1218
|
+
headTags: css.preloadTags
|
|
1219
|
+
};
|
|
1220
|
+
} finally {
|
|
1221
|
+
clearGlobalSSRTimeout();
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
function isAotDebugEnabled() {
|
|
1225
|
+
const env = process.env.VERTZ_DEBUG;
|
|
1226
|
+
if (!env)
|
|
1227
|
+
return false;
|
|
1228
|
+
return env === "1" || env.split(",").includes("aot");
|
|
1229
|
+
}
|
|
1230
|
+
var domShimInstalled2 = false;
|
|
1231
|
+
function ensureDomShim2() {
|
|
1232
|
+
if (domShimInstalled2 && typeof document !== "undefined")
|
|
1233
|
+
return;
|
|
1234
|
+
domShimInstalled2 = true;
|
|
1235
|
+
installDomShim();
|
|
1236
|
+
}
|
|
1237
|
+
function collectCSSFromModule(module, fallbackMetrics) {
|
|
1238
|
+
let themeCss = "";
|
|
1239
|
+
let preloadTags = "";
|
|
1240
|
+
if (module.theme) {
|
|
1241
|
+
try {
|
|
1242
|
+
const compiled = compileTheme3(module.theme, { fallbackMetrics });
|
|
1243
|
+
themeCss = compiled.css;
|
|
1244
|
+
preloadTags = compiled.preloadTags;
|
|
1245
|
+
} catch (e) {
|
|
1246
|
+
console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
const alreadyIncluded = new Set;
|
|
1250
|
+
if (themeCss)
|
|
1251
|
+
alreadyIncluded.add(themeCss);
|
|
1252
|
+
if (module.styles) {
|
|
1253
|
+
for (const s of module.styles)
|
|
1254
|
+
alreadyIncluded.add(s);
|
|
1255
|
+
}
|
|
1256
|
+
const componentCss = module.getInjectedCSS ? module.getInjectedCSS().filter((s) => !alreadyIncluded.has(s)) : [];
|
|
1257
|
+
const themeTag = themeCss ? `<style data-vertz-css>${themeCss}</style>` : "";
|
|
1258
|
+
const globalTag = module.styles && module.styles.length > 0 ? `<style data-vertz-css>${module.styles.join(`
|
|
1259
|
+
`)}</style>` : "";
|
|
1260
|
+
const componentTag = componentCss.length > 0 ? `<style data-vertz-css>${componentCss.join(`
|
|
1261
|
+
`)}</style>` : "";
|
|
1262
|
+
const cssString = [themeTag, globalTag, componentTag].filter(Boolean).join(`
|
|
1263
|
+
`);
|
|
1264
|
+
return { cssString, preloadTags };
|
|
1265
|
+
}
|
|
1266
|
+
// src/ssr-aot-runtime.ts
|
|
1267
|
+
var UNITLESS_PROPERTIES = new Set([
|
|
1268
|
+
"animationIterationCount",
|
|
1269
|
+
"aspectRatio",
|
|
1270
|
+
"borderImageOutset",
|
|
1271
|
+
"borderImageSlice",
|
|
1272
|
+
"borderImageWidth",
|
|
1273
|
+
"boxFlex",
|
|
1274
|
+
"boxFlexGroup",
|
|
1275
|
+
"boxOrdinalGroup",
|
|
1276
|
+
"columnCount",
|
|
1277
|
+
"columns",
|
|
1278
|
+
"flex",
|
|
1279
|
+
"flexGrow",
|
|
1280
|
+
"flexPositive",
|
|
1281
|
+
"flexShrink",
|
|
1282
|
+
"flexNegative",
|
|
1283
|
+
"flexOrder",
|
|
1284
|
+
"fontWeight",
|
|
1285
|
+
"gridArea",
|
|
1286
|
+
"gridColumn",
|
|
1287
|
+
"gridColumnEnd",
|
|
1288
|
+
"gridColumnSpan",
|
|
1289
|
+
"gridColumnStart",
|
|
1290
|
+
"gridRow",
|
|
1291
|
+
"gridRowEnd",
|
|
1292
|
+
"gridRowSpan",
|
|
1293
|
+
"gridRowStart",
|
|
1294
|
+
"lineClamp",
|
|
1295
|
+
"lineHeight",
|
|
1296
|
+
"opacity",
|
|
1297
|
+
"order",
|
|
1298
|
+
"orphans",
|
|
1299
|
+
"scale",
|
|
1300
|
+
"tabSize",
|
|
1301
|
+
"widows",
|
|
1302
|
+
"zIndex",
|
|
1303
|
+
"zoom"
|
|
1304
|
+
]);
|
|
1305
|
+
function __esc(value) {
|
|
1306
|
+
if (value == null || value === false)
|
|
1307
|
+
return "";
|
|
1308
|
+
if (value === true)
|
|
1309
|
+
return "true";
|
|
1310
|
+
if (typeof value === "number")
|
|
1311
|
+
return String(value);
|
|
1312
|
+
if (Array.isArray(value))
|
|
1313
|
+
return value.map(__esc).join("");
|
|
1314
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1315
|
+
}
|
|
1316
|
+
function __esc_attr(value) {
|
|
1317
|
+
const str = typeof value === "string" ? value : String(value);
|
|
1318
|
+
return str.replace(/&/g, "&").replace(/"/g, """);
|
|
1319
|
+
}
|
|
1320
|
+
function __ssr_spread(props) {
|
|
1321
|
+
const parts = [];
|
|
1322
|
+
for (const key in props) {
|
|
1323
|
+
const value = props[key];
|
|
1324
|
+
if (value == null || value === false)
|
|
1325
|
+
continue;
|
|
1326
|
+
const third = key.charAt(2);
|
|
1327
|
+
if (key.length > 2 && key.charAt(0) === "o" && key.charAt(1) === "n" && third >= "A" && third <= "Z") {
|
|
1328
|
+
continue;
|
|
1329
|
+
}
|
|
1330
|
+
if (typeof value === "function")
|
|
1331
|
+
continue;
|
|
1332
|
+
if (key === "key" || key === "ref" || key === "children")
|
|
1333
|
+
continue;
|
|
1334
|
+
const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
|
|
1335
|
+
if (key === "style" && typeof value === "object") {
|
|
1336
|
+
const css = __ssr_style_object(value);
|
|
1337
|
+
if (css) {
|
|
1338
|
+
parts.push(` style="${__esc_attr(css)}"`);
|
|
1339
|
+
}
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
if (value === true) {
|
|
1343
|
+
parts.push(` ${attrName}`);
|
|
1344
|
+
continue;
|
|
1345
|
+
}
|
|
1346
|
+
parts.push(` ${attrName}="${__esc_attr(value)}"`);
|
|
1347
|
+
}
|
|
1348
|
+
return parts.join("");
|
|
1349
|
+
}
|
|
1350
|
+
function camelToKebab(prop) {
|
|
1351
|
+
if (prop.startsWith("--"))
|
|
1352
|
+
return prop;
|
|
1353
|
+
if (prop.startsWith("ms")) {
|
|
1354
|
+
return `-${prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}`;
|
|
1355
|
+
}
|
|
1356
|
+
const first = prop.charAt(0);
|
|
1357
|
+
if (first >= "A" && first <= "Z") {
|
|
1358
|
+
return `-${first.toLowerCase()}${prop.slice(1).replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}`;
|
|
1359
|
+
}
|
|
1360
|
+
return prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
1361
|
+
}
|
|
1362
|
+
function __ssr_style_object(style) {
|
|
1363
|
+
const parts = [];
|
|
1364
|
+
for (const prop in style) {
|
|
1365
|
+
const value = style[prop];
|
|
1366
|
+
if (value == null || value === "")
|
|
1367
|
+
continue;
|
|
1368
|
+
const cssProp = camelToKebab(prop);
|
|
1369
|
+
if (typeof value === "number" && value !== 0 && !UNITLESS_PROPERTIES.has(prop)) {
|
|
1370
|
+
parts.push(`${cssProp}: ${value}px`);
|
|
1371
|
+
} else {
|
|
1372
|
+
parts.push(`${cssProp}: ${value}`);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
return parts.join("; ");
|
|
1376
|
+
}
|
|
1377
|
+
// src/ssr-html.ts
|
|
1378
|
+
function generateSSRHtml(options) {
|
|
1379
|
+
const {
|
|
1380
|
+
appHtml,
|
|
1381
|
+
css,
|
|
1382
|
+
ssrData,
|
|
1383
|
+
clientEntry,
|
|
1384
|
+
title = "Vertz App",
|
|
1385
|
+
headTags: rawHeadTags = "",
|
|
1386
|
+
modulepreload
|
|
1387
|
+
} = options;
|
|
1388
|
+
const modulepreloadTags = modulepreload?.length ? modulepreload.map((p) => `<link rel="modulepreload" href="${escapeAttr(p)}">`).join(`
|
|
1389
|
+
`) : "";
|
|
1390
|
+
const headTags = [rawHeadTags, modulepreloadTags].filter(Boolean).join(`
|
|
1391
|
+
`);
|
|
1392
|
+
const ssrDataScript = ssrData.length > 0 ? `<script>window.__VERTZ_SSR_DATA__ = ${JSON.stringify(ssrData)};</script>` : "";
|
|
1393
|
+
return `<!doctype html>
|
|
1394
|
+
<html lang="en">
|
|
1395
|
+
<head>
|
|
1396
|
+
<meta charset="UTF-8" />
|
|
1397
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1398
|
+
<title>${escapeHtml(title)}</title>
|
|
1399
|
+
${headTags}
|
|
1400
|
+
${css}
|
|
1401
|
+
</head>
|
|
1402
|
+
<body>
|
|
1403
|
+
<div id="app">${appHtml}</div>
|
|
1404
|
+
${ssrDataScript}
|
|
1405
|
+
<script type="module" src="${escapeAttr(clientEntry)}"></script>
|
|
1406
|
+
</body>
|
|
1407
|
+
</html>`;
|
|
1408
|
+
}
|
|
1409
|
+
// src/ssr-prefetch-dev.ts
|
|
1410
|
+
import {
|
|
1411
|
+
analyzeComponentQueries,
|
|
1412
|
+
generatePrefetchManifest
|
|
1413
|
+
} from "@vertz/ui-compiler";
|
|
1414
|
+
function createPrefetchManifestManager(options) {
|
|
1415
|
+
const { routerPath, readFile: readFile2, resolveImport } = options;
|
|
1416
|
+
let currentManifest = null;
|
|
1417
|
+
let currentSSRManifest;
|
|
1418
|
+
let rebuildCount = 0;
|
|
1419
|
+
let lastRebuildMs = null;
|
|
1420
|
+
let lastRebuildAt = null;
|
|
1421
|
+
let fileToRouteIndices = new Map;
|
|
1422
|
+
function buildFileIndex(routes) {
|
|
1423
|
+
const index = new Map;
|
|
1424
|
+
for (let i = 0;i < routes.length; i++) {
|
|
1425
|
+
const file = routes[i]?.file;
|
|
1426
|
+
if (file) {
|
|
1427
|
+
const existing = index.get(file) ?? [];
|
|
1428
|
+
existing.push(i);
|
|
1429
|
+
index.set(file, existing);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
return index;
|
|
1433
|
+
}
|
|
1434
|
+
function toSSRManifest(manifest) {
|
|
1435
|
+
const routeEntries = {};
|
|
1436
|
+
for (const route of manifest.routes) {
|
|
1437
|
+
const existing = routeEntries[route.pattern];
|
|
1438
|
+
if (existing) {
|
|
1439
|
+
existing.queries.push(...route.queries);
|
|
1440
|
+
} else {
|
|
1441
|
+
routeEntries[route.pattern] = { queries: [...route.queries] };
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
return {
|
|
1445
|
+
routePatterns: [...new Set(manifest.routes.map((r) => r.pattern))],
|
|
1446
|
+
routeEntries
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
function fullBuild(routerSourceOverride) {
|
|
1450
|
+
const start = performance.now();
|
|
1451
|
+
const routerSource = routerSourceOverride ?? readFile2(routerPath);
|
|
1452
|
+
if (!routerSource) {
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
try {
|
|
1456
|
+
const manifest = generatePrefetchManifest({
|
|
1457
|
+
routerSource,
|
|
1458
|
+
routerPath,
|
|
1459
|
+
readFile: readFile2,
|
|
1460
|
+
resolveImport
|
|
1461
|
+
});
|
|
1462
|
+
currentManifest = manifest;
|
|
1463
|
+
currentSSRManifest = toSSRManifest(manifest);
|
|
1464
|
+
fileToRouteIndices = buildFileIndex(manifest.routes);
|
|
1465
|
+
rebuildCount++;
|
|
1466
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
1467
|
+
lastRebuildAt = new Date().toISOString();
|
|
1468
|
+
} catch {}
|
|
1469
|
+
}
|
|
1470
|
+
function incrementalUpdate(filePath, sourceText) {
|
|
1471
|
+
if (!currentManifest)
|
|
1472
|
+
return;
|
|
1473
|
+
const indices = fileToRouteIndices.get(filePath);
|
|
1474
|
+
if (!indices || indices.length === 0)
|
|
1475
|
+
return;
|
|
1476
|
+
const start = performance.now();
|
|
1477
|
+
try {
|
|
1478
|
+
const analysis = analyzeComponentQueries(sourceText, filePath);
|
|
1479
|
+
const newRoutes = [...currentManifest.routes];
|
|
1480
|
+
for (const idx of indices) {
|
|
1481
|
+
const existing = newRoutes[idx];
|
|
1482
|
+
if (existing) {
|
|
1483
|
+
newRoutes[idx] = {
|
|
1484
|
+
...existing,
|
|
1485
|
+
queries: analysis.queries,
|
|
1486
|
+
params: analysis.params
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
const newManifest = {
|
|
1491
|
+
...currentManifest,
|
|
1492
|
+
routes: newRoutes,
|
|
1493
|
+
generatedAt: new Date().toISOString()
|
|
1494
|
+
};
|
|
1495
|
+
currentManifest = newManifest;
|
|
1496
|
+
currentSSRManifest = toSSRManifest(newManifest);
|
|
1497
|
+
rebuildCount++;
|
|
1498
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
1499
|
+
lastRebuildAt = new Date().toISOString();
|
|
1500
|
+
} catch {}
|
|
1501
|
+
}
|
|
1502
|
+
return {
|
|
1503
|
+
build() {
|
|
1504
|
+
fullBuild();
|
|
1505
|
+
},
|
|
1506
|
+
onFileChange(filePath, sourceText) {
|
|
1507
|
+
if (filePath === routerPath) {
|
|
1508
|
+
fullBuild(sourceText);
|
|
1509
|
+
} else {
|
|
1510
|
+
incrementalUpdate(filePath, sourceText);
|
|
1511
|
+
}
|
|
1512
|
+
},
|
|
1513
|
+
getSSRManifest() {
|
|
1514
|
+
return currentSSRManifest;
|
|
1515
|
+
},
|
|
1516
|
+
getSnapshot() {
|
|
1517
|
+
return {
|
|
1518
|
+
manifest: currentManifest,
|
|
1519
|
+
rebuildCount,
|
|
1520
|
+
lastRebuildMs,
|
|
1521
|
+
lastRebuildAt
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1034
1526
|
export {
|
|
1035
1527
|
wrapWithHydrationMarkers,
|
|
1036
1528
|
toPrefetchSession,
|
|
@@ -1038,6 +1530,7 @@ export {
|
|
|
1038
1530
|
ssrStorage,
|
|
1039
1531
|
ssrRenderToString,
|
|
1040
1532
|
ssrRenderSinglePass,
|
|
1533
|
+
ssrRenderAot,
|
|
1041
1534
|
ssrDiscoverQueries,
|
|
1042
1535
|
setGlobalSSRTimeout,
|
|
1043
1536
|
serializeToHtml,
|
|
@@ -1054,6 +1547,7 @@ export {
|
|
|
1054
1547
|
rawHtml,
|
|
1055
1548
|
matchUrlToPatterns,
|
|
1056
1549
|
isInSSR,
|
|
1550
|
+
isAotDebugEnabled,
|
|
1057
1551
|
inlineCriticalCss,
|
|
1058
1552
|
getStreamingRuntimeScript,
|
|
1059
1553
|
getSSRUrl,
|
|
@@ -1061,6 +1555,7 @@ export {
|
|
|
1061
1555
|
getGlobalSSRTimeout,
|
|
1062
1556
|
getAccessSetForSSR,
|
|
1063
1557
|
generateSSRHtml,
|
|
1558
|
+
generateAotBuildManifest,
|
|
1064
1559
|
extractFontMetrics,
|
|
1065
1560
|
evaluateAccessRule,
|
|
1066
1561
|
encodeChunk,
|
|
@@ -1072,8 +1567,15 @@ export {
|
|
|
1072
1567
|
createSSRDataChunk,
|
|
1073
1568
|
createSSRAdapter,
|
|
1074
1569
|
createPrefetchManifestManager,
|
|
1570
|
+
createHoles,
|
|
1571
|
+
createAotManifestManager,
|
|
1075
1572
|
createAccessSetScript,
|
|
1076
1573
|
collectStreamChunks,
|
|
1077
1574
|
clearGlobalSSRTimeout,
|
|
1078
|
-
|
|
1575
|
+
__ssr_style_object,
|
|
1576
|
+
__ssr_spread,
|
|
1577
|
+
__esc_attr,
|
|
1578
|
+
__esc,
|
|
1579
|
+
HeadCollector,
|
|
1580
|
+
AotDiagnostics
|
|
1079
1581
|
};
|