@octanejs/vite-plugin 0.2.0 → 0.3.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octanejs/vite-plugin",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "engines": {
@@ -46,19 +46,19 @@
46
46
  }
47
47
  },
48
48
  "dependencies": {
49
- "@ripple-ts/adapter": "^0.4.0",
50
- "@octanejs/app-core": "0.1.0"
49
+ "@octanejs/app-core": "0.1.2",
50
+ "@ripple-ts/adapter": "^0.4.0"
51
51
  },
52
52
  "peerDependencies": {
53
- "vite": "^8.0.16",
54
- "octane": "^0.8.0"
53
+ "octane": "^0.10.0",
54
+ "vite": "^8.0.16"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/jsdom": "30.0.0",
58
58
  "@types/node": "^24.13.3",
59
+ "octane": "0.10.0",
59
60
  "playwright": "^1.61.1",
60
61
  "playwright-webkit": "1.63.0",
61
- "vite": "^8.1.5",
62
- "octane": "0.8.0"
62
+ "vite": "^8.1.5"
63
63
  }
64
64
  }
package/src/index.js CHANGED
@@ -26,7 +26,7 @@ import { handleRenderRoute } from './server/render-route.js';
26
26
  import { handleServerRoute } from './server/server-route.js';
27
27
  import { HYDRATION_NONCE_PLACEHOLDER, injectHydrationEntry } from './server/html-template.js';
28
28
  import { generateServerEntry } from './server/virtual-entry.js';
29
- import { nodeRequestToWebRequest, sendWebResponse } from './server/node-http.js';
29
+ import { nodeRequestToWebRequest, nodeRequestUrl, sendWebResponse } from './server/node-http.js';
30
30
  import { ENTRY_FILENAME } from './constants.js';
31
31
  import {
32
32
  getOctaneConfigPath,
@@ -233,6 +233,16 @@ function has_islands_route(config) {
233
233
  );
234
234
  }
235
235
 
236
+ /**
237
+ * Every rendered document of this app is an islands-only page.
238
+ * @param {ResolvedOctaneConfig | null} config
239
+ * @returns {boolean}
240
+ */
241
+ function has_only_islands_routes(config) {
242
+ const routes = config?.router.routes.filter((route) => route.type === 'render') ?? [];
243
+ return routes.length > 0 && routes.every((route) => route.hydrate === 'islands');
244
+ }
245
+
236
246
  /**
237
247
  * Check every islands-only route's shell for client work. The shell's modules
238
248
  * never load in the browser, so every component it renders outside its
@@ -419,7 +429,7 @@ function collect_hydrate_module_paths(config) {
419
429
  * it). An explicit `profile` (true or false) always takes precedence over
420
430
  * `devtools`.
421
431
  *
422
- * @param {{ hmr?: boolean, profile?: boolean, devtools?: boolean, strong?: boolean, textTypes?: import('octane/compiler/vite').OctaneVitePluginOptions['textTypes'], knownAttributeSpreads?: import('octane/compiler/vite').OctaneVitePluginOptions['knownAttributeSpreads'], domBindingFixedProps?: import('octane/compiler/vite').OctaneVitePluginOptions['domBindingFixedProps'], exclude?: string[], requireDirective?: boolean, renderers?: import('@octanejs/app-core').ExperimentalRendererConfigOptions, cssModuleConstants?: import('octane/compiler/vite').OctaneVitePluginOptions['cssModuleConstants'] }} [inlineOptions]
432
+ * @param {{ hmr?: boolean, profile?: boolean, devtools?: boolean, strong?: boolean, textTypes?: import('octane/compiler/vite').OctaneVitePluginOptions['textTypes'], knownAttributeSpreads?: import('octane/compiler/vite').OctaneVitePluginOptions['knownAttributeSpreads'], domBindingFixedProps?: import('octane/compiler/vite').OctaneVitePluginOptions['domBindingFixedProps'], opaqueSignalHandles?: boolean, exclude?: string[], requireDirective?: boolean, renderers?: import('@octanejs/app-core').ExperimentalRendererConfigOptions, cssModuleConstants?: import('octane/compiler/vite').OctaneVitePluginOptions['cssModuleConstants'] }} [inlineOptions]
423
433
  * @returns {Plugin[]}
424
434
  */
425
435
  export function octane(inlineOptions = {}) {
@@ -595,8 +605,27 @@ export function octane(inlineOptions = {}) {
595
605
  return { relative: true };
596
606
  }
597
607
  };
608
+ // Only the renderer consumes the signal Action frame. Islands-only
609
+ // documents load signals without it, so this condition moves the
610
+ // frame and its candidate producers from the signal modules into the
611
+ // renderer (octane's action-capability.ts). Either placement is
612
+ // complete; it chooses which pages pay, so mixed apps keep the default.
613
+ /** @type {UserConfig['resolve']} */
614
+ let resolve;
615
+ if (has_only_islands_routes(buildOctaneConfig)) {
616
+ const { defaultClientConditions } = await import('vite');
617
+ // Vite concatenates plugin conditions onto the user's own, and
618
+ // replaces its defaults only when none were configured.
619
+ resolve = {
620
+ conditions: [
621
+ ...(userConfig.resolve?.conditions ? [] : defaultClientConditions),
622
+ 'octane-islands',
623
+ ],
624
+ };
625
+ }
598
626
  return {
599
627
  ...base,
628
+ ...(resolve ? { resolve } : null),
600
629
  build: buildConfig,
601
630
  experimental: {
602
631
  ...userConfig.experimental,
@@ -937,7 +966,7 @@ export function octane(inlineOptions = {}) {
937
966
  return;
938
967
  }
939
968
 
940
- const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
969
+ const url = nodeRequestUrl(req);
941
970
  const method = req.method || 'GET';
942
971
 
943
972
  // RPC requests for `module server` declarations.
@@ -990,7 +1019,9 @@ export function octane(inlineOptions = {}) {
990
1019
  await warnIslandsShells(octaneConfig).catch(() => {});
991
1020
  }
992
1021
 
993
- const request = nodeRequestToWebRequest(req, res);
1022
+ const request = nodeRequestToWebRequest(req, res, {
1023
+ trustProxy: octaneConfig.server.trustProxy,
1024
+ });
994
1025
  const context = createContext(request, freshMatch.params);
995
1026
  Object.defineProperty(context, 'clientBuild', { get: () => clientBuild.metadata() });
996
1027
  const globalMiddlewares = octaneConfig.middlewares;
@@ -1359,6 +1390,7 @@ export function octane(inlineOptions = {}) {
1359
1390
  * textTypes?: import('octane/compiler/vite').OctaneVitePluginOptions['textTypes'],
1360
1391
  * knownAttributeSpreads?: import('octane/compiler/vite').OctaneVitePluginOptions['knownAttributeSpreads'],
1361
1392
  * domBindingFixedProps?: import('octane/compiler/vite').OctaneVitePluginOptions['domBindingFixedProps'],
1393
+ * opaqueSignalHandles?: boolean,
1362
1394
  * exclude?: string[],
1363
1395
  * requireDirective?: boolean,
1364
1396
  * renderers?: import('@octanejs/app-core').ExperimentalRendererConfigOptions,
@@ -1384,6 +1416,9 @@ export function octane(inlineOptions = {}) {
1384
1416
  if (inlineOptions.domBindingFixedProps !== undefined) {
1385
1417
  compilerOptions.domBindingFixedProps = inlineOptions.domBindingFixedProps;
1386
1418
  }
1419
+ if (inlineOptions.opaqueSignalHandles !== undefined) {
1420
+ compilerOptions.opaqueSignalHandles = inlineOptions.opaqueSignalHandles;
1421
+ }
1387
1422
  if (inlineOptions.exclude !== undefined) compilerOptions.exclude = inlineOptions.exclude;
1388
1423
  if (inlineOptions.requireDirective !== undefined) {
1389
1424
  compilerOptions.requireDirective = inlineOptions.requireDirective;
@@ -1455,7 +1490,7 @@ export function defineConfig(/** @type {OctaneConfigOptions} */ options) {
1455
1490
  */
1456
1491
  async function handleRpcRequest(req, res, vite, trustProxy, config) {
1457
1492
  try {
1458
- const webRequest = nodeRequestToWebRequest(req, res);
1493
+ const webRequest = nodeRequestToWebRequest(req, res, { trustProxy });
1459
1494
  const asyncContext = getDevAsyncContext(config);
1460
1495
  const signalOwners = await loadDevSignalRequestHooks(vite);
1461
1496
 
package/types/index.d.ts CHANGED
@@ -23,6 +23,8 @@ export interface OctanePluginOptions {
23
23
  knownAttributeSpreads?: OctaneVitePluginOptions['knownAttributeSpreads'];
24
24
  /** Opt in to fixed primitive child props by name; keep text and IDs live for sharing. */
25
25
  domBindingFixedProps?: OctaneVitePluginOptions['domBindingFixedProps'];
26
+ /** Bind signal handles in opaque holes of modules that do not import `octane/signals`. */
27
+ opaqueSignalHandles?: OctaneVitePluginOptions['opaqueSignalHandles'];
26
28
  /**
27
29
  * Path fragments the compiler's plain `.ts`/`.js` hook-slotting pass must
28
30
  * skip. Prefer package manifest `octane.hookSlots.manual` declarations.