@octanejs/vite-plugin 0.1.13 → 0.1.17
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/package.json +5 -5
- package/src/index.js +18 -5
- package/src/server/render-route.js +22 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octanejs/vite-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -46,18 +46,18 @@
|
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@ripple-ts/adapter": "^0.3.
|
|
50
|
-
"@octanejs/app-core": "0.0.
|
|
49
|
+
"@ripple-ts/adapter": "^0.3.112",
|
|
50
|
+
"@octanejs/app-core": "0.0.13"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"vite": "^8.0.16",
|
|
54
|
-
"octane": "0.1.
|
|
54
|
+
"octane": "0.1.17"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@types/node": "^24.13.3",
|
|
58
58
|
"playwright": "^1.61.1",
|
|
59
59
|
"type-fest": "^5.8.0",
|
|
60
60
|
"vite": "^8.1.5",
|
|
61
|
-
"octane": "0.1.
|
|
61
|
+
"octane": "0.1.17"
|
|
62
62
|
}
|
|
63
63
|
}
|
package/src/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { AsyncLocalStorage } from 'node:async_hooks';
|
|
|
9
9
|
import { createRequire } from 'node:module';
|
|
10
10
|
|
|
11
11
|
import { octane as octaneCompiler } from 'octane/compiler/vite';
|
|
12
|
+
import { handleRpcRequest as handleServerRpcRequest } from '@octanejs/app-core';
|
|
12
13
|
|
|
13
14
|
import { createRouter } from './server/router.js';
|
|
14
15
|
import { createContext, runMiddlewareChain } from './server/middleware.js';
|
|
@@ -35,7 +36,7 @@ import {
|
|
|
35
36
|
} from './project-codegen.js';
|
|
36
37
|
import { createClientAssetMap } from './client-assets.js';
|
|
37
38
|
|
|
38
|
-
import { patch_global_fetch, is_rpc_request
|
|
39
|
+
import { patch_global_fetch, is_rpc_request } from '@ripple-ts/adapter/rpc';
|
|
39
40
|
|
|
40
41
|
import { get_route_entry_path } from './routes.js';
|
|
41
42
|
|
|
@@ -52,6 +53,7 @@ export {
|
|
|
52
53
|
get_route_entry_export_name,
|
|
53
54
|
get_route_entry_id,
|
|
54
55
|
get_route_entry_path,
|
|
56
|
+
handleRpcRequest,
|
|
55
57
|
handleServerRoute,
|
|
56
58
|
is_rpc_request,
|
|
57
59
|
runMiddlewareChain,
|
|
@@ -214,7 +216,12 @@ function collect_hydrate_module_paths(config) {
|
|
|
214
216
|
* `handler`/`nodeHandler` module; webworker-target adapters get an importable
|
|
215
217
|
* `createWebWorkerHandler` factory for their deployment wrapper.
|
|
216
218
|
*
|
|
217
|
-
*
|
|
219
|
+
* `devtools: true` enables the compiler's profile mode in DEV ONLY: profiling
|
|
220
|
+
* is on in `vite dev` and fully off in `vite build` (so production tree-shakes
|
|
221
|
+
* it). An explicit `profile` (true or false) always takes precedence over
|
|
222
|
+
* `devtools`.
|
|
223
|
+
*
|
|
224
|
+
* @param {{ hmr?: boolean, profile?: boolean, devtools?: boolean, exclude?: string[], requireDirective?: boolean, renderers?: import('@octanejs/app-core').ExperimentalRendererConfigOptions }} [inlineOptions]
|
|
218
225
|
* @returns {Plugin[]}
|
|
219
226
|
*/
|
|
220
227
|
export function octane(inlineOptions = {}) {
|
|
@@ -873,7 +880,7 @@ export function octane(inlineOptions = {}) {
|
|
|
873
880
|
/**
|
|
874
881
|
* @type {{
|
|
875
882
|
* hmr?: boolean,
|
|
876
|
-
* profile?: boolean,
|
|
883
|
+
* profile?: boolean | 'auto',
|
|
877
884
|
* exclude?: string[],
|
|
878
885
|
* requireDirective?: boolean,
|
|
879
886
|
* renderers?: import('@octanejs/app-core').ExperimentalRendererConfigOptions,
|
|
@@ -881,7 +888,10 @@ export function octane(inlineOptions = {}) {
|
|
|
881
888
|
*/
|
|
882
889
|
const compilerOptions = {};
|
|
883
890
|
if (inlineOptions.hmr !== undefined) compilerOptions.hmr = inlineOptions.hmr;
|
|
891
|
+
// Explicit `profile` wins; otherwise `devtools: true` opts into the
|
|
892
|
+
// command-aware `'auto'` signal the compiler resolves to DEV-only profiling.
|
|
884
893
|
if (inlineOptions.profile !== undefined) compilerOptions.profile = inlineOptions.profile;
|
|
894
|
+
else if (inlineOptions.devtools === true) compilerOptions.profile = 'auto';
|
|
885
895
|
if (inlineOptions.exclude !== undefined) compilerOptions.exclude = inlineOptions.exclude;
|
|
886
896
|
if (inlineOptions.requireDirective !== undefined) {
|
|
887
897
|
compilerOptions.requireDirective = inlineOptions.requireDirective;
|
|
@@ -948,7 +958,7 @@ async function handleRpcRequest(req, res, vite, trustProxy, config) {
|
|
|
948
958
|
const webRequest = nodeRequestToWebRequest(req);
|
|
949
959
|
const asyncContext = getDevAsyncContext(config);
|
|
950
960
|
|
|
951
|
-
const response = await
|
|
961
|
+
const response = await handleServerRpcRequest(webRequest, {
|
|
952
962
|
async resolveFunction(hash) {
|
|
953
963
|
const rpcModules = /** @type {any} */ (globalThis).rpc_modules;
|
|
954
964
|
if (!rpcModules) return null;
|
|
@@ -966,6 +976,9 @@ async function handleRpcRequest(req, res, vite, trustProxy, config) {
|
|
|
966
976
|
},
|
|
967
977
|
asyncContext,
|
|
968
978
|
trustProxy,
|
|
979
|
+
middlewares: config?.middlewares ?? [],
|
|
980
|
+
allowedOrigins: config?.server?.rpc?.allowedOrigins,
|
|
981
|
+
maxBodyBytes: config?.server?.rpc?.maxBodyBytes,
|
|
969
982
|
});
|
|
970
983
|
|
|
971
984
|
await sendWebResponse(res, response);
|
|
@@ -973,7 +986,7 @@ async function handleRpcRequest(req, res, vite, trustProxy, config) {
|
|
|
973
986
|
console.error('[@octanejs/vite-plugin] RPC error:', error);
|
|
974
987
|
res.statusCode = 500;
|
|
975
988
|
res.setHeader('Content-Type', 'application/json');
|
|
976
|
-
res.end(JSON.stringify({ error:
|
|
989
|
+
res.end(JSON.stringify({ error: 'Internal Server Error' }));
|
|
977
990
|
}
|
|
978
991
|
}
|
|
979
992
|
|
|
@@ -37,8 +37,9 @@ import {
|
|
|
37
37
|
*
|
|
38
38
|
* - Scoped CSS rides the STREAM (the shell emits its deduped
|
|
39
39
|
* `<style data-octane>` tags ahead of the body markup, per-wave styles ride
|
|
40
|
-
* their segment chunk), so
|
|
41
|
-
*
|
|
40
|
+
* their segment chunk), so styles are not spliced into `<!--ssr-head-->`.
|
|
41
|
+
* That marker receives the hydration data script and the shell's hoisted
|
|
42
|
+
* `<title>`/`<meta>`/`<link>` (see `headChannel` below). `hydrateRoot()`
|
|
42
43
|
* skips the shell's leading style tags when adopting.
|
|
43
44
|
* - A render error BEFORE the shell completes still produces the dev 500
|
|
44
45
|
* page (`renderToReadableStream` rejects on shell errors). An error AFTER
|
|
@@ -148,17 +149,27 @@ export async function handleRenderRoute(route, context, vite, octaneConfig) {
|
|
|
148
149
|
// Apply Vite's HTML transforms (HMR client, module resolution, etc.).
|
|
149
150
|
template = await vite.transformIndexHtml(context.url.pathname, template);
|
|
150
151
|
|
|
151
|
-
// Validate the raw SSR template and inject the request-nonced hydrate entry
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
html =
|
|
152
|
+
// Validate the raw SSR template and inject the request-nonced hydrate entry.
|
|
153
|
+
// The one required head marker is consumed AFTER the render, once the
|
|
154
|
+
// hoisted metadata is in hand, validation still happens up front here.
|
|
155
|
+
const html = injectHydrationEntry(template, '/@id/virtual:octane-hydrate', nonce);
|
|
155
156
|
|
|
156
157
|
// Start the render. This await resolves at SHELL-ready (so a synchronous
|
|
157
158
|
// render error still falls into the catch below and produces the dev 500
|
|
158
159
|
// page); segments keep flushing through the returned stream afterwards.
|
|
160
|
+
//
|
|
161
|
+
// `headChannel: 'separate'` matches production (see production.js): the
|
|
162
|
+
// route renders into the template's `#root`, not a document, so hoisted
|
|
163
|
+
// `<title>`/`<meta>`/`<link>` must be spliced at `<!--ssr-head-->` rather
|
|
164
|
+
// than folded into a body that has no `</head>`.
|
|
165
|
+
let hoistedHead = '';
|
|
159
166
|
/** @type {ReadableStream<Uint8Array>} */
|
|
160
167
|
const renderStream = await renderToReadableStream(RootComponent, undefined, {
|
|
161
168
|
nonce: nonce ?? undefined,
|
|
169
|
+
headChannel: 'separate',
|
|
170
|
+
onHeadReady(/** @type {string} */ head) {
|
|
171
|
+
hoistedHead = head;
|
|
172
|
+
},
|
|
162
173
|
signal: context.request.signal,
|
|
163
174
|
onError(/** @type {unknown} */ error) {
|
|
164
175
|
if (error instanceof Error) vite.ssrFixStacktrace(error);
|
|
@@ -169,7 +180,11 @@ export async function handleRenderRoute(route, context, vite, octaneConfig) {
|
|
|
169
180
|
const status = route.status ?? 200;
|
|
170
181
|
const headers = { 'Content-Type': 'text/html; charset=utf-8' };
|
|
171
182
|
|
|
172
|
-
|
|
183
|
+
// Function replacement, so `$&`/`` $` ``/`$'`/`$1` inside the route data or
|
|
184
|
+
// author-controlled metadata cannot expand against the match.
|
|
185
|
+
const [prefix, suffix] = splitSsrTemplate(
|
|
186
|
+
html.replace('<!--ssr-head-->', () => headContent + hoistedHead),
|
|
187
|
+
);
|
|
173
188
|
|
|
174
189
|
// Template prefix → render stream (shell, then out-of-order segments) →
|
|
175
190
|
// template suffix. The hydration <script> is in the SUFFIX, so by the time
|