@cfdez11/vex 0.7.0 ā 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/services/navigation.js +6 -0
- package/package.json +1 -1
- package/server/build-static.js +28 -15
- package/server/index.js +14 -19
- package/server/utils/component-processor.js +11 -9
- package/server/utils/files.js +8 -2
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Barrel file for vex/navigation imports.
|
|
2
|
+
// Components import { useRouteParams } from "vex/navigation" which esbuild
|
|
3
|
+
// rewrites to /_vexjs/services/navigation.js (external). All re-exports go
|
|
4
|
+
// through navigation/index.js so the browser module cache ensures the same
|
|
5
|
+
// runtime instance is shared with the index.js bootstrap.
|
|
6
|
+
export { useRouteParams, useQueryParams, navigate } from "./navigation/index.js";
|
package/package.json
CHANGED
package/server/build-static.js
CHANGED
|
@@ -38,6 +38,7 @@ let shell = rootTemplate
|
|
|
38
38
|
.replace(/\{\{props\.children\}\}/g, "");
|
|
39
39
|
|
|
40
40
|
const frameworkScripts = [
|
|
41
|
+
`<style>vex-root { display: contents; }</style>`,
|
|
41
42
|
`<script type="module" src="/_vexjs/services/index.js"></script>`,
|
|
42
43
|
`<script src="/_vexjs/services/hydrate-client-components.js"></script>`,
|
|
43
44
|
`<script src="/_vexjs/services/hydrate.js" id="hydrate-script"></script>`,
|
|
@@ -46,24 +47,36 @@ const frameworkScripts = [
|
|
|
46
47
|
shell = shell.replace("</head>", ` ${frameworkScripts}\n</head>`);
|
|
47
48
|
await fs.writeFile(path.join(DIST_DIR, "index.html"), shell, "utf-8");
|
|
48
49
|
|
|
49
|
-
// Step 4: Copy framework
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
// Step 4: Copy static framework assets (favicon.ico, app.webmanifest) ā dist/_vexjs/
|
|
51
|
+
// JS runtime files live in .vexjs/services/ (copied in step 5) ā no need to
|
|
52
|
+
// copy CLIENT_DIR/services/ separately.
|
|
53
|
+
console.log("š¦ Copying framework assets...");
|
|
54
|
+
for (const asset of ["favicon.ico", "app.webmanifest"]) {
|
|
55
|
+
try {
|
|
56
|
+
await fs.copyFile(
|
|
57
|
+
path.join(CLIENT_DIR, asset),
|
|
58
|
+
path.join(DIST_DIR, "_vexjs", asset)
|
|
59
|
+
);
|
|
60
|
+
} catch {
|
|
61
|
+
// asset not present ā skip
|
|
62
|
+
}
|
|
63
|
+
}
|
|
52
64
|
|
|
53
|
-
// Step 5: Copy generated
|
|
54
|
-
|
|
65
|
+
// Step 5: Copy generated services ā dist/_vexjs/services/
|
|
66
|
+
// .vexjs/services/ already contains both framework JS (copied by initializeDirectories)
|
|
67
|
+
// and generated files (_routes.js). One copy covers everything.
|
|
68
|
+
console.log("š¦ Copying services...");
|
|
55
69
|
await fs.cp(
|
|
56
|
-
path.join(GENERATED_DIR, "
|
|
57
|
-
path.join(DIST_DIR, "_vexjs", "
|
|
70
|
+
path.join(GENERATED_DIR, "services"),
|
|
71
|
+
path.join(DIST_DIR, "_vexjs", "services"),
|
|
58
72
|
{ recursive: true }
|
|
59
73
|
);
|
|
60
74
|
|
|
61
|
-
// Step 6: Copy generated
|
|
62
|
-
|
|
63
|
-
console.log("š¦ Copying generated services...");
|
|
75
|
+
// Step 6: Copy generated component bundles ā dist/_vexjs/_components/
|
|
76
|
+
console.log("š¦ Copying component bundles...");
|
|
64
77
|
await fs.cp(
|
|
65
|
-
path.join(GENERATED_DIR, "
|
|
66
|
-
path.join(DIST_DIR, "_vexjs", "
|
|
78
|
+
path.join(GENERATED_DIR, "_components"),
|
|
79
|
+
path.join(DIST_DIR, "_vexjs", "_components"),
|
|
67
80
|
{ recursive: true }
|
|
68
81
|
);
|
|
69
82
|
|
|
@@ -77,7 +90,7 @@ try {
|
|
|
77
90
|
// no user JS files ā that's fine
|
|
78
91
|
}
|
|
79
92
|
|
|
80
|
-
// Step 8: Copy public/ ā dist/
|
|
93
|
+
// Step 8: Copy public/ ā dist/
|
|
81
94
|
console.log("š¦ Copying public assets...");
|
|
82
95
|
const publicDir = path.join(PROJECT_ROOT, "public");
|
|
83
96
|
try {
|
|
@@ -86,7 +99,7 @@ try {
|
|
|
86
99
|
// no public/ directory ā that's fine
|
|
87
100
|
}
|
|
88
101
|
|
|
89
|
-
// Step 9: Copy pre-rendered
|
|
102
|
+
// Step 9: Copy pre-rendered SSG pages
|
|
90
103
|
const CACHE_DIR = path.join(GENERATED_DIR, "_cache");
|
|
91
104
|
const ssgRoutes = serverRoutes.filter(
|
|
92
105
|
(r) => r.meta.revalidate === "never" || r.meta.revalidate === false
|
|
@@ -108,7 +121,7 @@ if (ssgRoutes.length > 0) {
|
|
|
108
121
|
}
|
|
109
122
|
}
|
|
110
123
|
|
|
111
|
-
// Step 10: Report SSR-only routes
|
|
124
|
+
// Step 10: Report SSR-only routes (skipped in static build)
|
|
112
125
|
const ssrOnlyRoutes = serverRoutes.filter((r) => r.meta.ssr);
|
|
113
126
|
if (ssrOnlyRoutes.length > 0) {
|
|
114
127
|
console.warn("\nā ļø The following routes require a server and were NOT included in the static build:");
|
package/server/index.js
CHANGED
|
@@ -29,8 +29,6 @@ if (process.env.NODE_ENV === "production") {
|
|
|
29
29
|
const app = express();
|
|
30
30
|
|
|
31
31
|
// Serve generated client component bundles at /_vexjs/_components/
|
|
32
|
-
// Must be registered before the broader /_vexjs static mount below so that
|
|
33
|
-
// .vexjs/_components/ takes priority over anything in CLIENT_DIR/_components/.
|
|
34
32
|
app.use(
|
|
35
33
|
"/_vexjs/_components",
|
|
36
34
|
express.static(path.join(process.cwd(), ".vexjs", "_components"), {
|
|
@@ -42,9 +40,9 @@ app.use(
|
|
|
42
40
|
})
|
|
43
41
|
);
|
|
44
42
|
|
|
45
|
-
// Serve generated
|
|
46
|
-
//
|
|
47
|
-
//
|
|
43
|
+
// Serve framework runtime JS + generated files (_routes.js) at /_vexjs/services/
|
|
44
|
+
// initializeDirectories() pre-populates this dir with framework files; build()
|
|
45
|
+
// adds generated files (_routes.js). Single source of truth for all /_vexjs/services/*.
|
|
48
46
|
app.use(
|
|
49
47
|
"/_vexjs/services",
|
|
50
48
|
express.static(path.join(process.cwd(), ".vexjs", "services"), {
|
|
@@ -56,13 +54,13 @@ app.use(
|
|
|
56
54
|
})
|
|
57
55
|
);
|
|
58
56
|
|
|
59
|
-
// Serve
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
57
|
+
// Serve pre-bundled user JS utility files at /_vexjs/user/
|
|
58
|
+
// Registered before the generic /_vexjs mount so requests don't fall through
|
|
59
|
+
// to CLIENT_DIR unnecessarily. esbuild bundles each file with npm packages
|
|
60
|
+
// inlined; vex/*, @/*, and relative user imports stay external (singletons).
|
|
63
61
|
app.use(
|
|
64
|
-
"/_vexjs",
|
|
65
|
-
express.static(
|
|
62
|
+
"/_vexjs/user",
|
|
63
|
+
express.static(USER_GENERATED_DIR, {
|
|
66
64
|
setHeaders(res, filePath) {
|
|
67
65
|
if (filePath.endsWith(".js")) {
|
|
68
66
|
res.setHeader("Content-Type", "application/javascript");
|
|
@@ -71,15 +69,12 @@ app.use(
|
|
|
71
69
|
})
|
|
72
70
|
);
|
|
73
71
|
|
|
74
|
-
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
// each file with npm packages inlined; vex/*, @/*, and relative user imports
|
|
78
|
-
// stay external so every component shares the same singleton module instance
|
|
79
|
-
// via the browser's ES module cache.
|
|
72
|
+
// Serve static framework assets (favicon.ico, app.webmanifest) from CLIENT_DIR.
|
|
73
|
+
// Runtime JS files (reactive.js, index.js, etc.) are already in .vexjs/services/
|
|
74
|
+
// via initializeDirectories() and are served by the /_vexjs/services route above.
|
|
80
75
|
app.use(
|
|
81
|
-
"/_vexjs
|
|
82
|
-
express.static(
|
|
76
|
+
"/_vexjs",
|
|
77
|
+
express.static(CLIENT_DIR, {
|
|
83
78
|
setHeaders(res, filePath) {
|
|
84
79
|
if (filePath.endsWith(".js")) {
|
|
85
80
|
res.setHeader("Content-Type", "application/javascript");
|
|
@@ -564,6 +564,7 @@ async function renderLayouts(pagePath, pageContent, pageHead = {}) {
|
|
|
564
564
|
// so users don't need to reference framework scripts in their root.html
|
|
565
565
|
const devMode = process.env.NODE_ENV !== "production";
|
|
566
566
|
const frameworkScripts = [
|
|
567
|
+
`<style>vex-root { display: contents; }</style>`,
|
|
567
568
|
`<script type="module" src="/_vexjs/services/index.js"></script>`,
|
|
568
569
|
`<script src="/_vexjs/services/hydrate-client-components.js"></script>`,
|
|
569
570
|
`<script src="/_vexjs/services/hydrate.js" id="hydrate-script"></script>`,
|
|
@@ -832,6 +833,11 @@ export async function generateClientComponentModule({
|
|
|
832
833
|
// āā 5. Assemble the esbuild entry source āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
833
834
|
// This is a valid ESM module that esbuild will bundle. Imports at the top,
|
|
834
835
|
// hydrateClientComponent exported as a named function.
|
|
836
|
+
// Add persistent wrapper element anchors the component in the DOM so that
|
|
837
|
+
// re-renders always have a stable target to replace children into.
|
|
838
|
+
// Using a plain Element (never a DocumentFragment) avoids the fragment-drain
|
|
839
|
+
// problem: after marker.replaceWith(fragment) the fragment empties and
|
|
840
|
+
// disconnects, making subsequent root.replaceWith() calls silently no-op.
|
|
835
841
|
const entrySource = `
|
|
836
842
|
${[...importLines].join("\n")}
|
|
837
843
|
|
|
@@ -840,20 +846,16 @@ export const metadata = ${JSON.stringify(metadata)};
|
|
|
840
846
|
export function hydrateClientComponent(marker, incomingProps = {}) {
|
|
841
847
|
${cleanClientCode}
|
|
842
848
|
|
|
843
|
-
|
|
849
|
+
const wrapper = document.createElement("vex-root");
|
|
850
|
+
marker.replaceWith(wrapper);
|
|
851
|
+
|
|
844
852
|
function render() {
|
|
845
853
|
const node = html\`${processedHtml}\`;
|
|
846
|
-
|
|
847
|
-
root = node;
|
|
848
|
-
marker.replaceWith(node);
|
|
849
|
-
} else {
|
|
850
|
-
root.replaceWith(node);
|
|
851
|
-
root = node;
|
|
852
|
-
}
|
|
854
|
+
wrapper.replaceChildren(node);
|
|
853
855
|
}
|
|
854
856
|
|
|
855
857
|
effect(() => render());
|
|
856
|
-
return
|
|
858
|
+
return wrapper;
|
|
857
859
|
}
|
|
858
860
|
`.trim();
|
|
859
861
|
|
package/server/utils/files.js
CHANGED
|
@@ -112,7 +112,6 @@ const GENERATED_DIR = path.join(PROJECT_ROOT, ".vexjs");
|
|
|
112
112
|
const CACHE_DIR = path.join(GENERATED_DIR, "_cache");
|
|
113
113
|
export const CLIENT_COMPONENTS_DIR = path.join(GENERATED_DIR, "_components");
|
|
114
114
|
export const USER_GENERATED_DIR = path.join(GENERATED_DIR, "user");
|
|
115
|
-
const SERVER_UTILS_DIR = path.join(GENERATED_DIR);
|
|
116
115
|
const ROOT_HTML_USER = path.join(PROJECT_ROOT, "root.html");
|
|
117
116
|
const ROOT_HTML_DEFAULT = path.join(FRAMEWORK_DIR, "server", "root.html");
|
|
118
117
|
export const ROOT_HTML_DIR = ROOT_HTML_USER;
|
|
@@ -134,14 +133,21 @@ export const ROOT_HTML_DIR = ROOT_HTML_USER;
|
|
|
134
133
|
*/
|
|
135
134
|
export async function initializeDirectories() {
|
|
136
135
|
try {
|
|
136
|
+
const servicesDir = path.join(GENERATED_DIR, "services");
|
|
137
137
|
await Promise.all([
|
|
138
138
|
fs.mkdir(GENERATED_DIR, { recursive: true }),
|
|
139
139
|
fs.mkdir(CACHE_DIR, { recursive: true }),
|
|
140
140
|
fs.mkdir(CLIENT_COMPONENTS_DIR, { recursive: true }),
|
|
141
141
|
fs.mkdir(USER_GENERATED_DIR, { recursive: true }),
|
|
142
|
-
fs.mkdir(
|
|
142
|
+
fs.mkdir(servicesDir, { recursive: true }),
|
|
143
143
|
]);
|
|
144
144
|
|
|
145
|
+
// Copy framework client runtime files into .vexjs/services/ so they are
|
|
146
|
+
// served by the /_vexjs/services static route alongside generated files
|
|
147
|
+
// like _routes.js. Generated files (prefixed with _) are written later by
|
|
148
|
+
// the build step and overwrite any stale copies here.
|
|
149
|
+
await fs.cp(CLIENT_SERVICES_DIR, servicesDir, { recursive: true });
|
|
150
|
+
|
|
145
151
|
return true;
|
|
146
152
|
} catch (err) {
|
|
147
153
|
console.error("Failed to create cache directory:", err);
|