@vertz/ui-server 0.2.25 → 0.2.28
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 +250 -4
- package/dist/bun-plugin/index.d.ts +1 -1
- package/dist/index.d.ts +346 -86
- package/dist/index.js +658 -154
- 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) {
|
|
@@ -428,14 +484,16 @@ async function renderToHTML(appOrOptions, maybeOptions) {
|
|
|
428
484
|
});
|
|
429
485
|
}
|
|
430
486
|
// src/ssr-access-evaluator.ts
|
|
431
|
-
function toPrefetchSession(ssrAuth) {
|
|
487
|
+
function toPrefetchSession(ssrAuth, accessSet) {
|
|
432
488
|
if (!ssrAuth || ssrAuth.status !== "authenticated" || !ssrAuth.user) {
|
|
433
489
|
return { status: "unauthenticated" };
|
|
434
490
|
}
|
|
435
491
|
const roles = ssrAuth.user.role ? [ssrAuth.user.role] : undefined;
|
|
492
|
+
const entitlements = accessSet != null ? Object.fromEntries(Object.entries(accessSet.entitlements).map(([name, check]) => [name, check.allowed])) : undefined;
|
|
436
493
|
return {
|
|
437
494
|
status: "authenticated",
|
|
438
495
|
roles,
|
|
496
|
+
entitlements,
|
|
439
497
|
tenantId: ssrAuth.user.tenantId
|
|
440
498
|
};
|
|
441
499
|
}
|
|
@@ -467,144 +525,168 @@ function evaluateAccessRule(rule, session) {
|
|
|
467
525
|
return false;
|
|
468
526
|
}
|
|
469
527
|
}
|
|
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);
|
|
528
|
+
// src/ssr-aot-diagnostics.ts
|
|
529
|
+
var MAX_DIVERGENCES = 20;
|
|
530
|
+
|
|
531
|
+
class AotDiagnostics {
|
|
532
|
+
_components = new Map;
|
|
533
|
+
_divergences = [];
|
|
534
|
+
recordCompilation(components) {
|
|
535
|
+
for (const comp of components) {
|
|
536
|
+
this._components.set(comp.name, {
|
|
537
|
+
tier: comp.tier,
|
|
538
|
+
holes: comp.holes
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
recordDivergence(component, aotHtml, domHtml) {
|
|
543
|
+
this._divergences.push({
|
|
544
|
+
component,
|
|
545
|
+
aotHtml,
|
|
546
|
+
domHtml,
|
|
547
|
+
timestamp: new Date().toISOString()
|
|
548
|
+
});
|
|
549
|
+
if (this._divergences.length > MAX_DIVERGENCES) {
|
|
550
|
+
this._divergences = this._divergences.slice(this._divergences.length - MAX_DIVERGENCES);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
clear() {
|
|
554
|
+
this._components.clear();
|
|
555
|
+
this._divergences = [];
|
|
556
|
+
}
|
|
557
|
+
clearComponents() {
|
|
558
|
+
this._components.clear();
|
|
559
|
+
}
|
|
560
|
+
getClassificationLog() {
|
|
561
|
+
const lines = [];
|
|
562
|
+
for (const [name, comp] of this._components) {
|
|
563
|
+
let line = `${name}: ${comp.tier}`;
|
|
564
|
+
if (comp.holes.length > 0) {
|
|
565
|
+
line += `, ${comp.holes.length} hole${comp.holes.length > 1 ? "s" : ""} (${comp.holes.join(", ")})`;
|
|
523
566
|
}
|
|
567
|
+
lines.push(line);
|
|
524
568
|
}
|
|
525
|
-
|
|
569
|
+
const snapshot = this.getSnapshot();
|
|
570
|
+
const { total, aot, percentage } = snapshot.coverage;
|
|
571
|
+
if (total > 0) {
|
|
572
|
+
lines.push(`Coverage: ${aot}/${total} components (${percentage}%)`);
|
|
573
|
+
}
|
|
574
|
+
return lines;
|
|
526
575
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
if (
|
|
532
|
-
|
|
576
|
+
getSnapshot() {
|
|
577
|
+
let aot = 0;
|
|
578
|
+
let runtime = 0;
|
|
579
|
+
for (const comp of this._components.values()) {
|
|
580
|
+
if (comp.tier === "runtime-fallback") {
|
|
581
|
+
runtime++;
|
|
533
582
|
} else {
|
|
534
|
-
|
|
583
|
+
aot++;
|
|
535
584
|
}
|
|
536
585
|
}
|
|
586
|
+
const total = aot + runtime;
|
|
537
587
|
return {
|
|
538
|
-
|
|
539
|
-
|
|
588
|
+
components: Object.fromEntries(this._components),
|
|
589
|
+
coverage: {
|
|
590
|
+
total,
|
|
591
|
+
aot,
|
|
592
|
+
runtime,
|
|
593
|
+
percentage: total === 0 ? 0 : Math.round(aot / total * 100)
|
|
594
|
+
},
|
|
595
|
+
divergences: [...this._divergences]
|
|
540
596
|
};
|
|
541
597
|
}
|
|
542
|
-
|
|
598
|
+
}
|
|
599
|
+
// src/ssr-aot-manifest-dev.ts
|
|
600
|
+
import { compileForSSRAot as compileForSSRAot2 } from "@vertz/ui-compiler";
|
|
601
|
+
function createAotManifestManager(options) {
|
|
602
|
+
const { readFile: readFile2, listFiles } = options;
|
|
603
|
+
let currentManifest = null;
|
|
604
|
+
const diagnostics = new AotDiagnostics;
|
|
605
|
+
let rebuildCount = 0;
|
|
606
|
+
let lastRebuildMs = null;
|
|
607
|
+
let lastRebuildAt = null;
|
|
608
|
+
function compileFile(filePath, source) {
|
|
609
|
+
try {
|
|
610
|
+
const result = compileForSSRAot2(source, { filename: filePath });
|
|
611
|
+
return result.components.map((comp) => ({
|
|
612
|
+
name: comp.name,
|
|
613
|
+
entry: {
|
|
614
|
+
tier: comp.tier,
|
|
615
|
+
holes: comp.holes,
|
|
616
|
+
file: filePath
|
|
617
|
+
}
|
|
618
|
+
}));
|
|
619
|
+
} catch {
|
|
620
|
+
return [];
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
function updateDiagnostics(manifest, isFullBuild) {
|
|
624
|
+
if (isFullBuild) {
|
|
625
|
+
diagnostics.clear();
|
|
626
|
+
} else {
|
|
627
|
+
diagnostics.clearComponents();
|
|
628
|
+
}
|
|
629
|
+
const entries = Object.entries(manifest.components).map(([name, entry]) => ({
|
|
630
|
+
name,
|
|
631
|
+
tier: entry.tier,
|
|
632
|
+
holes: entry.holes
|
|
633
|
+
}));
|
|
634
|
+
diagnostics.recordCompilation(entries);
|
|
635
|
+
}
|
|
636
|
+
function fullBuild() {
|
|
543
637
|
const start = performance.now();
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
638
|
+
const files = listFiles();
|
|
639
|
+
const components = {};
|
|
640
|
+
for (const filePath of files) {
|
|
641
|
+
if (!filePath.endsWith(".tsx"))
|
|
642
|
+
continue;
|
|
643
|
+
const source = readFile2(filePath);
|
|
644
|
+
if (!source)
|
|
645
|
+
continue;
|
|
646
|
+
const entries = compileFile(filePath, source);
|
|
647
|
+
for (const { name, entry } of entries) {
|
|
648
|
+
components[name] = entry;
|
|
649
|
+
}
|
|
547
650
|
}
|
|
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 {}
|
|
651
|
+
currentManifest = { components };
|
|
652
|
+
updateDiagnostics(currentManifest, true);
|
|
653
|
+
rebuildCount++;
|
|
654
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
655
|
+
lastRebuildAt = new Date().toISOString();
|
|
562
656
|
}
|
|
563
657
|
function incrementalUpdate(filePath, sourceText) {
|
|
564
658
|
if (!currentManifest)
|
|
565
659
|
return;
|
|
566
|
-
const indices = fileToRouteIndices.get(filePath);
|
|
567
|
-
if (!indices || indices.length === 0)
|
|
568
|
-
return;
|
|
569
660
|
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
|
-
}
|
|
661
|
+
const newComponents = { ...currentManifest.components };
|
|
662
|
+
for (const [name, entry] of Object.entries(newComponents)) {
|
|
663
|
+
if (entry.file === filePath) {
|
|
664
|
+
delete newComponents[name];
|
|
582
665
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
666
|
+
}
|
|
667
|
+
if (sourceText.trim()) {
|
|
668
|
+
const entries = compileFile(filePath, sourceText);
|
|
669
|
+
for (const { name, entry } of entries) {
|
|
670
|
+
newComponents[name] = entry;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
currentManifest = { components: newComponents };
|
|
674
|
+
updateDiagnostics(currentManifest, false);
|
|
675
|
+
rebuildCount++;
|
|
676
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
677
|
+
lastRebuildAt = new Date().toISOString();
|
|
594
678
|
}
|
|
595
679
|
return {
|
|
596
680
|
build() {
|
|
597
681
|
fullBuild();
|
|
598
682
|
},
|
|
599
683
|
onFileChange(filePath, sourceText) {
|
|
600
|
-
if (filePath
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
incrementalUpdate(filePath, sourceText);
|
|
604
|
-
}
|
|
684
|
+
if (!filePath.endsWith(".tsx"))
|
|
685
|
+
return;
|
|
686
|
+
incrementalUpdate(filePath, sourceText);
|
|
605
687
|
},
|
|
606
|
-
|
|
607
|
-
return
|
|
688
|
+
getManifest() {
|
|
689
|
+
return currentManifest;
|
|
608
690
|
},
|
|
609
691
|
getSnapshot() {
|
|
610
692
|
return {
|
|
@@ -613,9 +695,53 @@ function createPrefetchManifestManager(options) {
|
|
|
613
695
|
lastRebuildMs,
|
|
614
696
|
lastRebuildAt
|
|
615
697
|
};
|
|
698
|
+
},
|
|
699
|
+
getDiagnostics() {
|
|
700
|
+
return diagnostics;
|
|
616
701
|
}
|
|
617
702
|
};
|
|
618
703
|
}
|
|
704
|
+
// src/ssr-aot-pipeline.ts
|
|
705
|
+
import { compileTheme as compileTheme3 } from "@vertz/ui";
|
|
706
|
+
|
|
707
|
+
// src/ssr-route-matcher.ts
|
|
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
|
+
}
|
|
741
|
+
|
|
742
|
+
// src/ssr-single-pass.ts
|
|
743
|
+
import { compileTheme as compileTheme2 } from "@vertz/ui";
|
|
744
|
+
|
|
619
745
|
// src/ssr-manifest-prefetch.ts
|
|
620
746
|
function reconstructDescriptors(queries, routeParams, apiClient) {
|
|
621
747
|
if (!apiClient)
|
|
@@ -704,42 +830,8 @@ function resolveQueryBindings(bindings, routeParams) {
|
|
|
704
830
|
resolved.limit = bindings.limit;
|
|
705
831
|
return resolved;
|
|
706
832
|
}
|
|
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
|
-
}
|
|
833
|
+
|
|
741
834
|
// src/ssr-single-pass.ts
|
|
742
|
-
import { compileTheme as compileTheme2 } from "@vertz/ui";
|
|
743
835
|
async function ssrRenderSinglePass(module, url, options) {
|
|
744
836
|
if (options?.prefetch === false) {
|
|
745
837
|
return ssrRenderToString(module, url, options);
|
|
@@ -1031,6 +1123,408 @@ function collectCSS(themeCss, module) {
|
|
|
1031
1123
|
return [themeTag, globalTag, componentTag].filter(Boolean).join(`
|
|
1032
1124
|
`);
|
|
1033
1125
|
}
|
|
1126
|
+
|
|
1127
|
+
// src/ssr-aot-pipeline.ts
|
|
1128
|
+
function createHoles(holeNames, module, url, queryCache, ssrAuth) {
|
|
1129
|
+
if (holeNames.length === 0)
|
|
1130
|
+
return {};
|
|
1131
|
+
const holes = {};
|
|
1132
|
+
for (const name of holeNames) {
|
|
1133
|
+
holes[name] = () => {
|
|
1134
|
+
const holeCtx = createRequestContext(url);
|
|
1135
|
+
for (const [key, data] of queryCache) {
|
|
1136
|
+
holeCtx.queryCache.set(key, data);
|
|
1137
|
+
}
|
|
1138
|
+
if (ssrAuth) {
|
|
1139
|
+
holeCtx.ssrAuth = ssrAuth;
|
|
1140
|
+
}
|
|
1141
|
+
holeCtx.resolvedComponents = new Map;
|
|
1142
|
+
return ssrStorage.run(holeCtx, () => {
|
|
1143
|
+
ensureDomShim2();
|
|
1144
|
+
const factory = resolveHoleComponent(module, name);
|
|
1145
|
+
if (!factory) {
|
|
1146
|
+
return `<!-- AOT hole: ${name} not found -->`;
|
|
1147
|
+
}
|
|
1148
|
+
const node = factory();
|
|
1149
|
+
const vnode = toVNode(node);
|
|
1150
|
+
return serializeToHtml(vnode);
|
|
1151
|
+
});
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
return holes;
|
|
1155
|
+
}
|
|
1156
|
+
function resolveHoleComponent(module, name) {
|
|
1157
|
+
const moduleRecord = module;
|
|
1158
|
+
const exported = moduleRecord[name];
|
|
1159
|
+
if (typeof exported === "function") {
|
|
1160
|
+
return exported;
|
|
1161
|
+
}
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
async function ssrRenderAot(module, url, options) {
|
|
1165
|
+
const { aotManifest, manifest } = options;
|
|
1166
|
+
const ssrTimeout = options.ssrTimeout ?? 300;
|
|
1167
|
+
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
1168
|
+
const fallbackOptions = {
|
|
1169
|
+
ssrTimeout,
|
|
1170
|
+
fallbackMetrics: options.fallbackMetrics,
|
|
1171
|
+
ssrAuth: options.ssrAuth,
|
|
1172
|
+
manifest,
|
|
1173
|
+
prefetchSession: options.prefetchSession
|
|
1174
|
+
};
|
|
1175
|
+
const aotPatterns = Object.keys(aotManifest.routes);
|
|
1176
|
+
const matches = matchUrlToPatterns(normalizedUrl, aotPatterns);
|
|
1177
|
+
if (matches.length === 0) {
|
|
1178
|
+
return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1179
|
+
}
|
|
1180
|
+
const match = matches[matches.length - 1];
|
|
1181
|
+
if (!match) {
|
|
1182
|
+
return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1183
|
+
}
|
|
1184
|
+
const aotEntry = aotManifest.routes[match.pattern];
|
|
1185
|
+
if (!aotEntry) {
|
|
1186
|
+
return ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1187
|
+
}
|
|
1188
|
+
const queryCache = new Map;
|
|
1189
|
+
try {
|
|
1190
|
+
setGlobalSSRTimeout(ssrTimeout);
|
|
1191
|
+
const holes = createHoles(aotEntry.holes, module, normalizedUrl, queryCache, options.ssrAuth);
|
|
1192
|
+
const ctx = {
|
|
1193
|
+
holes,
|
|
1194
|
+
getData: (key) => queryCache.get(key),
|
|
1195
|
+
session: options.prefetchSession,
|
|
1196
|
+
params: match.params
|
|
1197
|
+
};
|
|
1198
|
+
const data = {};
|
|
1199
|
+
for (const [key, value] of queryCache) {
|
|
1200
|
+
data[key] = value;
|
|
1201
|
+
}
|
|
1202
|
+
const html = aotEntry.render(data, ctx);
|
|
1203
|
+
if (options.diagnostics && isAotDebugEnabled()) {
|
|
1204
|
+
try {
|
|
1205
|
+
const domResult = await ssrRenderSinglePass(module, normalizedUrl, fallbackOptions);
|
|
1206
|
+
if (domResult.html !== html) {
|
|
1207
|
+
options.diagnostics.recordDivergence(match.pattern, html, domResult.html);
|
|
1208
|
+
}
|
|
1209
|
+
} catch {}
|
|
1210
|
+
}
|
|
1211
|
+
const css = collectCSSFromModule(module, options.fallbackMetrics);
|
|
1212
|
+
const ssrData = [];
|
|
1213
|
+
for (const [key, data2] of queryCache) {
|
|
1214
|
+
ssrData.push({ key, data: JSON.parse(JSON.stringify(data2)) });
|
|
1215
|
+
}
|
|
1216
|
+
return {
|
|
1217
|
+
html,
|
|
1218
|
+
css: css.cssString,
|
|
1219
|
+
ssrData,
|
|
1220
|
+
headTags: css.preloadTags
|
|
1221
|
+
};
|
|
1222
|
+
} finally {
|
|
1223
|
+
clearGlobalSSRTimeout();
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
function isAotDebugEnabled() {
|
|
1227
|
+
const env = process.env.VERTZ_DEBUG;
|
|
1228
|
+
if (!env)
|
|
1229
|
+
return false;
|
|
1230
|
+
return env === "1" || env.split(",").includes("aot");
|
|
1231
|
+
}
|
|
1232
|
+
var domShimInstalled2 = false;
|
|
1233
|
+
function ensureDomShim2() {
|
|
1234
|
+
if (domShimInstalled2 && typeof document !== "undefined")
|
|
1235
|
+
return;
|
|
1236
|
+
domShimInstalled2 = true;
|
|
1237
|
+
installDomShim();
|
|
1238
|
+
}
|
|
1239
|
+
function collectCSSFromModule(module, fallbackMetrics) {
|
|
1240
|
+
let themeCss = "";
|
|
1241
|
+
let preloadTags = "";
|
|
1242
|
+
if (module.theme) {
|
|
1243
|
+
try {
|
|
1244
|
+
const compiled = compileTheme3(module.theme, { fallbackMetrics });
|
|
1245
|
+
themeCss = compiled.css;
|
|
1246
|
+
preloadTags = compiled.preloadTags;
|
|
1247
|
+
} catch (e) {
|
|
1248
|
+
console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
const alreadyIncluded = new Set;
|
|
1252
|
+
if (themeCss)
|
|
1253
|
+
alreadyIncluded.add(themeCss);
|
|
1254
|
+
if (module.styles) {
|
|
1255
|
+
for (const s of module.styles)
|
|
1256
|
+
alreadyIncluded.add(s);
|
|
1257
|
+
}
|
|
1258
|
+
const componentCss = module.getInjectedCSS ? module.getInjectedCSS().filter((s) => !alreadyIncluded.has(s)) : [];
|
|
1259
|
+
const themeTag = themeCss ? `<style data-vertz-css>${themeCss}</style>` : "";
|
|
1260
|
+
const globalTag = module.styles && module.styles.length > 0 ? `<style data-vertz-css>${module.styles.join(`
|
|
1261
|
+
`)}</style>` : "";
|
|
1262
|
+
const componentTag = componentCss.length > 0 ? `<style data-vertz-css>${componentCss.join(`
|
|
1263
|
+
`)}</style>` : "";
|
|
1264
|
+
const cssString = [themeTag, globalTag, componentTag].filter(Boolean).join(`
|
|
1265
|
+
`);
|
|
1266
|
+
return { cssString, preloadTags };
|
|
1267
|
+
}
|
|
1268
|
+
// src/ssr-aot-runtime.ts
|
|
1269
|
+
var UNITLESS_PROPERTIES = new Set([
|
|
1270
|
+
"animationIterationCount",
|
|
1271
|
+
"aspectRatio",
|
|
1272
|
+
"borderImageOutset",
|
|
1273
|
+
"borderImageSlice",
|
|
1274
|
+
"borderImageWidth",
|
|
1275
|
+
"boxFlex",
|
|
1276
|
+
"boxFlexGroup",
|
|
1277
|
+
"boxOrdinalGroup",
|
|
1278
|
+
"columnCount",
|
|
1279
|
+
"columns",
|
|
1280
|
+
"flex",
|
|
1281
|
+
"flexGrow",
|
|
1282
|
+
"flexPositive",
|
|
1283
|
+
"flexShrink",
|
|
1284
|
+
"flexNegative",
|
|
1285
|
+
"flexOrder",
|
|
1286
|
+
"fontWeight",
|
|
1287
|
+
"gridArea",
|
|
1288
|
+
"gridColumn",
|
|
1289
|
+
"gridColumnEnd",
|
|
1290
|
+
"gridColumnSpan",
|
|
1291
|
+
"gridColumnStart",
|
|
1292
|
+
"gridRow",
|
|
1293
|
+
"gridRowEnd",
|
|
1294
|
+
"gridRowSpan",
|
|
1295
|
+
"gridRowStart",
|
|
1296
|
+
"lineClamp",
|
|
1297
|
+
"lineHeight",
|
|
1298
|
+
"opacity",
|
|
1299
|
+
"order",
|
|
1300
|
+
"orphans",
|
|
1301
|
+
"scale",
|
|
1302
|
+
"tabSize",
|
|
1303
|
+
"widows",
|
|
1304
|
+
"zIndex",
|
|
1305
|
+
"zoom"
|
|
1306
|
+
]);
|
|
1307
|
+
function __esc(value) {
|
|
1308
|
+
if (value == null || value === false)
|
|
1309
|
+
return "";
|
|
1310
|
+
if (value === true)
|
|
1311
|
+
return "true";
|
|
1312
|
+
if (typeof value === "number")
|
|
1313
|
+
return String(value);
|
|
1314
|
+
if (Array.isArray(value))
|
|
1315
|
+
return value.map(__esc).join("");
|
|
1316
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1317
|
+
}
|
|
1318
|
+
function __esc_attr(value) {
|
|
1319
|
+
const str = typeof value === "string" ? value : String(value);
|
|
1320
|
+
return str.replace(/&/g, "&").replace(/"/g, """);
|
|
1321
|
+
}
|
|
1322
|
+
function __ssr_spread(props) {
|
|
1323
|
+
const parts = [];
|
|
1324
|
+
for (const key in props) {
|
|
1325
|
+
const value = props[key];
|
|
1326
|
+
if (value == null || value === false)
|
|
1327
|
+
continue;
|
|
1328
|
+
const third = key.charAt(2);
|
|
1329
|
+
if (key.length > 2 && key.charAt(0) === "o" && key.charAt(1) === "n" && third >= "A" && third <= "Z") {
|
|
1330
|
+
continue;
|
|
1331
|
+
}
|
|
1332
|
+
if (typeof value === "function")
|
|
1333
|
+
continue;
|
|
1334
|
+
if (key === "key" || key === "ref" || key === "children")
|
|
1335
|
+
continue;
|
|
1336
|
+
const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
|
|
1337
|
+
if (key === "style" && typeof value === "object") {
|
|
1338
|
+
const css = __ssr_style_object(value);
|
|
1339
|
+
if (css) {
|
|
1340
|
+
parts.push(` style="${__esc_attr(css)}"`);
|
|
1341
|
+
}
|
|
1342
|
+
continue;
|
|
1343
|
+
}
|
|
1344
|
+
if (value === true) {
|
|
1345
|
+
parts.push(` ${attrName}`);
|
|
1346
|
+
continue;
|
|
1347
|
+
}
|
|
1348
|
+
parts.push(` ${attrName}="${__esc_attr(value)}"`);
|
|
1349
|
+
}
|
|
1350
|
+
return parts.join("");
|
|
1351
|
+
}
|
|
1352
|
+
function camelToKebab(prop) {
|
|
1353
|
+
if (prop.startsWith("--"))
|
|
1354
|
+
return prop;
|
|
1355
|
+
if (prop.startsWith("ms")) {
|
|
1356
|
+
return `-${prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}`;
|
|
1357
|
+
}
|
|
1358
|
+
const first = prop.charAt(0);
|
|
1359
|
+
if (first >= "A" && first <= "Z") {
|
|
1360
|
+
return `-${first.toLowerCase()}${prop.slice(1).replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)}`;
|
|
1361
|
+
}
|
|
1362
|
+
return prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
1363
|
+
}
|
|
1364
|
+
function __ssr_style_object(style) {
|
|
1365
|
+
const parts = [];
|
|
1366
|
+
for (const prop in style) {
|
|
1367
|
+
const value = style[prop];
|
|
1368
|
+
if (value == null || value === "")
|
|
1369
|
+
continue;
|
|
1370
|
+
const cssProp = camelToKebab(prop);
|
|
1371
|
+
if (typeof value === "number" && value !== 0 && !UNITLESS_PROPERTIES.has(prop)) {
|
|
1372
|
+
parts.push(`${cssProp}: ${value}px`);
|
|
1373
|
+
} else {
|
|
1374
|
+
parts.push(`${cssProp}: ${value}`);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
return parts.join("; ");
|
|
1378
|
+
}
|
|
1379
|
+
// src/ssr-html.ts
|
|
1380
|
+
function generateSSRHtml(options) {
|
|
1381
|
+
const {
|
|
1382
|
+
appHtml,
|
|
1383
|
+
css,
|
|
1384
|
+
ssrData,
|
|
1385
|
+
clientEntry,
|
|
1386
|
+
title = "Vertz App",
|
|
1387
|
+
headTags: rawHeadTags = "",
|
|
1388
|
+
modulepreload
|
|
1389
|
+
} = options;
|
|
1390
|
+
const modulepreloadTags = modulepreload?.length ? modulepreload.map((p) => `<link rel="modulepreload" href="${escapeAttr(p)}">`).join(`
|
|
1391
|
+
`) : "";
|
|
1392
|
+
const headTags = [rawHeadTags, modulepreloadTags].filter(Boolean).join(`
|
|
1393
|
+
`);
|
|
1394
|
+
const ssrDataScript = ssrData.length > 0 ? `<script>window.__VERTZ_SSR_DATA__ = ${JSON.stringify(ssrData)};</script>` : "";
|
|
1395
|
+
return `<!doctype html>
|
|
1396
|
+
<html lang="en">
|
|
1397
|
+
<head>
|
|
1398
|
+
<meta charset="UTF-8" />
|
|
1399
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1400
|
+
<title>${escapeHtml(title)}</title>
|
|
1401
|
+
${headTags}
|
|
1402
|
+
${css}
|
|
1403
|
+
</head>
|
|
1404
|
+
<body>
|
|
1405
|
+
<div id="app">${appHtml}</div>
|
|
1406
|
+
${ssrDataScript}
|
|
1407
|
+
<script type="module" src="${escapeAttr(clientEntry)}"></script>
|
|
1408
|
+
</body>
|
|
1409
|
+
</html>`;
|
|
1410
|
+
}
|
|
1411
|
+
// src/ssr-prefetch-dev.ts
|
|
1412
|
+
import {
|
|
1413
|
+
analyzeComponentQueries,
|
|
1414
|
+
generatePrefetchManifest
|
|
1415
|
+
} from "@vertz/ui-compiler";
|
|
1416
|
+
function createPrefetchManifestManager(options) {
|
|
1417
|
+
const { routerPath, readFile: readFile2, resolveImport } = options;
|
|
1418
|
+
let currentManifest = null;
|
|
1419
|
+
let currentSSRManifest;
|
|
1420
|
+
let rebuildCount = 0;
|
|
1421
|
+
let lastRebuildMs = null;
|
|
1422
|
+
let lastRebuildAt = null;
|
|
1423
|
+
let fileToRouteIndices = new Map;
|
|
1424
|
+
function buildFileIndex(routes) {
|
|
1425
|
+
const index = new Map;
|
|
1426
|
+
for (let i = 0;i < routes.length; i++) {
|
|
1427
|
+
const file = routes[i]?.file;
|
|
1428
|
+
if (file) {
|
|
1429
|
+
const existing = index.get(file) ?? [];
|
|
1430
|
+
existing.push(i);
|
|
1431
|
+
index.set(file, existing);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
return index;
|
|
1435
|
+
}
|
|
1436
|
+
function toSSRManifest(manifest) {
|
|
1437
|
+
const routeEntries = {};
|
|
1438
|
+
for (const route of manifest.routes) {
|
|
1439
|
+
const existing = routeEntries[route.pattern];
|
|
1440
|
+
if (existing) {
|
|
1441
|
+
existing.queries.push(...route.queries);
|
|
1442
|
+
} else {
|
|
1443
|
+
routeEntries[route.pattern] = { queries: [...route.queries] };
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
return {
|
|
1447
|
+
routePatterns: [...new Set(manifest.routes.map((r) => r.pattern))],
|
|
1448
|
+
routeEntries
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
function fullBuild(routerSourceOverride) {
|
|
1452
|
+
const start = performance.now();
|
|
1453
|
+
const routerSource = routerSourceOverride ?? readFile2(routerPath);
|
|
1454
|
+
if (!routerSource) {
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
try {
|
|
1458
|
+
const manifest = generatePrefetchManifest({
|
|
1459
|
+
routerSource,
|
|
1460
|
+
routerPath,
|
|
1461
|
+
readFile: readFile2,
|
|
1462
|
+
resolveImport
|
|
1463
|
+
});
|
|
1464
|
+
currentManifest = manifest;
|
|
1465
|
+
currentSSRManifest = toSSRManifest(manifest);
|
|
1466
|
+
fileToRouteIndices = buildFileIndex(manifest.routes);
|
|
1467
|
+
rebuildCount++;
|
|
1468
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
1469
|
+
lastRebuildAt = new Date().toISOString();
|
|
1470
|
+
} catch {}
|
|
1471
|
+
}
|
|
1472
|
+
function incrementalUpdate(filePath, sourceText) {
|
|
1473
|
+
if (!currentManifest)
|
|
1474
|
+
return;
|
|
1475
|
+
const indices = fileToRouteIndices.get(filePath);
|
|
1476
|
+
if (!indices || indices.length === 0)
|
|
1477
|
+
return;
|
|
1478
|
+
const start = performance.now();
|
|
1479
|
+
try {
|
|
1480
|
+
const analysis = analyzeComponentQueries(sourceText, filePath);
|
|
1481
|
+
const newRoutes = [...currentManifest.routes];
|
|
1482
|
+
for (const idx of indices) {
|
|
1483
|
+
const existing = newRoutes[idx];
|
|
1484
|
+
if (existing) {
|
|
1485
|
+
newRoutes[idx] = {
|
|
1486
|
+
...existing,
|
|
1487
|
+
queries: analysis.queries,
|
|
1488
|
+
params: analysis.params
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
const newManifest = {
|
|
1493
|
+
...currentManifest,
|
|
1494
|
+
routes: newRoutes,
|
|
1495
|
+
generatedAt: new Date().toISOString()
|
|
1496
|
+
};
|
|
1497
|
+
currentManifest = newManifest;
|
|
1498
|
+
currentSSRManifest = toSSRManifest(newManifest);
|
|
1499
|
+
rebuildCount++;
|
|
1500
|
+
lastRebuildMs = Math.round(performance.now() - start);
|
|
1501
|
+
lastRebuildAt = new Date().toISOString();
|
|
1502
|
+
} catch {}
|
|
1503
|
+
}
|
|
1504
|
+
return {
|
|
1505
|
+
build() {
|
|
1506
|
+
fullBuild();
|
|
1507
|
+
},
|
|
1508
|
+
onFileChange(filePath, sourceText) {
|
|
1509
|
+
if (filePath === routerPath) {
|
|
1510
|
+
fullBuild(sourceText);
|
|
1511
|
+
} else {
|
|
1512
|
+
incrementalUpdate(filePath, sourceText);
|
|
1513
|
+
}
|
|
1514
|
+
},
|
|
1515
|
+
getSSRManifest() {
|
|
1516
|
+
return currentSSRManifest;
|
|
1517
|
+
},
|
|
1518
|
+
getSnapshot() {
|
|
1519
|
+
return {
|
|
1520
|
+
manifest: currentManifest,
|
|
1521
|
+
rebuildCount,
|
|
1522
|
+
lastRebuildMs,
|
|
1523
|
+
lastRebuildAt
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1034
1528
|
export {
|
|
1035
1529
|
wrapWithHydrationMarkers,
|
|
1036
1530
|
toPrefetchSession,
|
|
@@ -1038,6 +1532,7 @@ export {
|
|
|
1038
1532
|
ssrStorage,
|
|
1039
1533
|
ssrRenderToString,
|
|
1040
1534
|
ssrRenderSinglePass,
|
|
1535
|
+
ssrRenderAot,
|
|
1041
1536
|
ssrDiscoverQueries,
|
|
1042
1537
|
setGlobalSSRTimeout,
|
|
1043
1538
|
serializeToHtml,
|
|
@@ -1054,6 +1549,7 @@ export {
|
|
|
1054
1549
|
rawHtml,
|
|
1055
1550
|
matchUrlToPatterns,
|
|
1056
1551
|
isInSSR,
|
|
1552
|
+
isAotDebugEnabled,
|
|
1057
1553
|
inlineCriticalCss,
|
|
1058
1554
|
getStreamingRuntimeScript,
|
|
1059
1555
|
getSSRUrl,
|
|
@@ -1061,6 +1557,7 @@ export {
|
|
|
1061
1557
|
getGlobalSSRTimeout,
|
|
1062
1558
|
getAccessSetForSSR,
|
|
1063
1559
|
generateSSRHtml,
|
|
1560
|
+
generateAotBuildManifest,
|
|
1064
1561
|
extractFontMetrics,
|
|
1065
1562
|
evaluateAccessRule,
|
|
1066
1563
|
encodeChunk,
|
|
@@ -1072,8 +1569,15 @@ export {
|
|
|
1072
1569
|
createSSRDataChunk,
|
|
1073
1570
|
createSSRAdapter,
|
|
1074
1571
|
createPrefetchManifestManager,
|
|
1572
|
+
createHoles,
|
|
1573
|
+
createAotManifestManager,
|
|
1075
1574
|
createAccessSetScript,
|
|
1076
1575
|
collectStreamChunks,
|
|
1077
1576
|
clearGlobalSSRTimeout,
|
|
1078
|
-
|
|
1577
|
+
__ssr_style_object,
|
|
1578
|
+
__ssr_spread,
|
|
1579
|
+
__esc_attr,
|
|
1580
|
+
__esc,
|
|
1581
|
+
HeadCollector,
|
|
1582
|
+
AotDiagnostics
|
|
1079
1583
|
};
|