@octanejs/vite-plugin 0.1.12 → 0.1.16

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.1.12",
3
+ "version": "0.1.16",
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.101",
50
- "@octanejs/app-core": "0.0.8"
49
+ "@ripple-ts/adapter": "^0.3.112",
50
+ "@octanejs/app-core": "0.0.12"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "vite": "^8.0.16",
54
- "octane": "0.1.12"
54
+ "octane": "0.1.16"
55
55
  },
56
56
  "devDependencies": {
57
- "@types/node": "^24.3.0",
58
- "playwright": "^1.61.0",
59
- "type-fest": "^5.6.0",
60
- "vite": "^8.0.16",
61
- "octane": "0.1.12"
57
+ "@types/node": "^24.13.3",
58
+ "playwright": "^1.61.1",
59
+ "type-fest": "^5.8.0",
60
+ "vite": "^8.1.5",
61
+ "octane": "0.1.16"
62
62
  }
63
63
  }
@@ -32,9 +32,14 @@ function isDeferredHydrationId(id) {
32
32
  *
33
33
  * @param {Record<string, ViteManifestEntry>} manifest
34
34
  * @param {string[]} moduleIds
35
+ * @param {Record<string, string>} [entryFiles]
35
36
  * @returns {Record<string, { js: string, css: string[] }>}
36
37
  */
37
- export function createClientAssetMap(manifest, moduleIds) {
38
+ export function createClientAssetMap(manifest, moduleIds, entryFiles = {}) {
39
+ const manifestKeysByFile = new Map(
40
+ Object.entries(manifest).map(([key, entry]) => [entry.file, key]),
41
+ );
42
+
38
43
  /**
39
44
  * @param {string} key
40
45
  * @param {boolean} deferredHydrationBranch
@@ -69,9 +74,15 @@ export function createClientAssetMap(manifest, moduleIds) {
69
74
  const assets = {};
70
75
  for (const moduleId of moduleIds) {
71
76
  // Vite manifest keys are root-relative without the leading slash.
72
- const manifestKey = moduleId.startsWith('/') ? moduleId.slice(1) : moduleId;
77
+ const sourceKey = moduleId.startsWith('/') ? moduleId.slice(1) : moduleId;
78
+ // A route module that is also statically imported can become a shared
79
+ // non-facade chunk. Vite keys that manifest entry by generated chunk name,
80
+ // so use the Rollup-observed output file to recover the graph root.
81
+ const manifestKey = manifest[sourceKey]
82
+ ? sourceKey
83
+ : manifestKeysByFile.get(entryFiles[moduleId]);
84
+ if (!manifestKey) continue;
73
85
  const entry = manifest[manifestKey];
74
- if (!entry) continue;
75
86
  assets[moduleId] = {
76
87
  js: entry.file,
77
88
  css: [...new Set(collectCss(manifestKey, false, new Set()))],
package/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // @ts-check
2
- /** @import {Plugin, RenderBuiltAssetUrl, ResolvedConfig, ViteDevServer, UserConfig} from 'vite' */
2
+ /** @import {ModulePreloadOptions, Plugin, RenderBuiltAssetUrl, ResolvedConfig, ViteDevServer, UserConfig} from 'vite' */
3
3
  /** @import {LoadedOctaneConfig, OctaneConfigOptions, ResolvedOctaneConfig, RenderRoute} from '@octanejs/vite-plugin' */
4
4
 
5
5
  import fs from 'node:fs';
@@ -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, handle_rpc_request } from '@ripple-ts/adapter/rpc';
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
- * @param {{ hmr?: boolean, profile?: boolean, exclude?: string[], requireDirective?: boolean, renderers?: import('@octanejs/app-core').ExperimentalRendererConfigOptions }} [inlineOptions]
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 = {}) {
@@ -243,6 +250,8 @@ export function octane(inlineOptions = {}) {
243
250
  let buildOctaneConfig = null;
244
251
  /** @type {string[]} Module paths the generated client entry maps statically (build only) */
245
252
  let staticEntries = [];
253
+ /** @type {Record<string, string>} Static module path → emitted client chunk file */
254
+ let staticEntryFiles = Object.create(null);
246
255
  /** @type {Set<string>} Vite-root paths of modules containing `module server` */
247
256
  const serverModuleModules = new Set();
248
257
 
@@ -328,6 +337,25 @@ export function octane(inlineOptions = {}) {
328
337
  if (buildOctaneConfig.build.target !== undefined) {
329
338
  buildConfig.target = buildOctaneConfig.build.target;
330
339
  }
340
+ const userModulePreload = userConfig.build?.modulePreload;
341
+ if (userModulePreload === false) {
342
+ buildConfig.modulePreload = false;
343
+ } else {
344
+ /** @type {ModulePreloadOptions} */
345
+ const modulePreload =
346
+ typeof userModulePreload === 'object' ? { ...userModulePreload } : {};
347
+ const userResolveDependencies = modulePreload.resolveDependencies;
348
+ modulePreload.resolveDependencies = (filename, dependencies, context) => {
349
+ // Vite 8.1 emits entry dependency hints after the entry script.
350
+ // A script that installs <base> in between can redirect those
351
+ // root-relative requests off-origin. Static imports discover the
352
+ // same dependencies safely from the entry module URL, so omit only
353
+ // the redundant HTML hints; retain JS dynamic-import preloading.
354
+ if (context.hostType === 'html') return [];
355
+ return userResolveDependencies?.(filename, dependencies, context) ?? dependencies;
356
+ };
357
+ buildConfig.modulePreload = modulePreload;
358
+ }
331
359
  const userRenderBuiltUrl = userConfig.experimental?.renderBuiltUrl;
332
360
  /** @type {RenderBuiltAssetUrl} */
333
361
  const renderBuiltUrl = (filename, context) => {
@@ -367,6 +395,28 @@ export function octane(inlineOptions = {}) {
367
395
  if (!isBuild || isSSRBuild || !has_route_config(buildOctaneConfig)) return;
368
396
  serverModuleModules.clear();
369
397
  staticEntries = collect_hydrate_module_paths(buildOctaneConfig);
398
+ staticEntryFiles = Object.create(null);
399
+ },
400
+
401
+ /**
402
+ * Preserve the source-to-file relation that Vite's manifest cannot express
403
+ * when Rolldown promotes a dynamic route entry into a shared chunk.
404
+ */
405
+ generateBundle(_options, bundle) {
406
+ if (!isBuild || isSSRBuild || !has_route_config(buildOctaneConfig)) return;
407
+ const entryByModuleId = new Map(
408
+ staticEntries.map((moduleId) => {
409
+ const file = path.resolve(root, moduleId.startsWith('/') ? `.${moduleId}` : moduleId);
410
+ return [file.split(path.sep).join('/'), moduleId];
411
+ }),
412
+ );
413
+ for (const output of Object.values(bundle)) {
414
+ if (output.type !== 'chunk') continue;
415
+ for (const moduleId of output.moduleIds) {
416
+ const entryId = entryByModuleId.get(moduleId.split(path.sep).join('/'));
417
+ if (entryId !== undefined) staticEntryFiles[entryId] = output.fileName;
418
+ }
419
+ }
370
420
  },
371
421
 
372
422
  async configResolved(resolvedConfig) {
@@ -693,7 +743,7 @@ export function octane(inlineOptions = {}) {
693
743
  );
694
744
  }
695
745
 
696
- const clientAssetMap = createClientAssetMap(clientManifest, staticEntries);
746
+ const clientAssetMap = createClientAssetMap(clientManifest, staticEntries, staticEntryFiles);
697
747
 
698
748
  // The manifest was only needed here; leaving .vite/ in dist/client would
699
749
  // publish source file paths through the static server.
@@ -830,7 +880,7 @@ export function octane(inlineOptions = {}) {
830
880
  /**
831
881
  * @type {{
832
882
  * hmr?: boolean,
833
- * profile?: boolean,
883
+ * profile?: boolean | 'auto',
834
884
  * exclude?: string[],
835
885
  * requireDirective?: boolean,
836
886
  * renderers?: import('@octanejs/app-core').ExperimentalRendererConfigOptions,
@@ -838,7 +888,10 @@ export function octane(inlineOptions = {}) {
838
888
  */
839
889
  const compilerOptions = {};
840
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.
841
893
  if (inlineOptions.profile !== undefined) compilerOptions.profile = inlineOptions.profile;
894
+ else if (inlineOptions.devtools === true) compilerOptions.profile = 'auto';
842
895
  if (inlineOptions.exclude !== undefined) compilerOptions.exclude = inlineOptions.exclude;
843
896
  if (inlineOptions.requireDirective !== undefined) {
844
897
  compilerOptions.requireDirective = inlineOptions.requireDirective;
@@ -905,7 +958,7 @@ async function handleRpcRequest(req, res, vite, trustProxy, config) {
905
958
  const webRequest = nodeRequestToWebRequest(req);
906
959
  const asyncContext = getDevAsyncContext(config);
907
960
 
908
- const response = await handle_rpc_request(webRequest, {
961
+ const response = await handleServerRpcRequest(webRequest, {
909
962
  async resolveFunction(hash) {
910
963
  const rpcModules = /** @type {any} */ (globalThis).rpc_modules;
911
964
  if (!rpcModules) return null;
@@ -923,6 +976,9 @@ async function handleRpcRequest(req, res, vite, trustProxy, config) {
923
976
  },
924
977
  asyncContext,
925
978
  trustProxy,
979
+ middlewares: config?.middlewares ?? [],
980
+ allowedOrigins: config?.server?.rpc?.allowedOrigins,
981
+ maxBodyBytes: config?.server?.rpc?.maxBodyBytes,
926
982
  });
927
983
 
928
984
  await sendWebResponse(res, response);
@@ -930,7 +986,7 @@ async function handleRpcRequest(req, res, vite, trustProxy, config) {
930
986
  console.error('[@octanejs/vite-plugin] RPC error:', error);
931
987
  res.statusCode = 500;
932
988
  res.setHeader('Content-Type', 'application/json');
933
- res.end(JSON.stringify({ error: error instanceof Error ? error.message : 'RPC failed' }));
989
+ res.end(JSON.stringify({ error: 'Internal Server Error' }));
934
990
  }
935
991
  }
936
992