@octanejs/vite-plugin 0.1.12 → 0.1.13

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.13",
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.108",
50
+ "@octanejs/app-core": "0.0.9"
51
51
  },
52
52
  "peerDependencies": {
53
53
  "vite": "^8.0.16",
54
- "octane": "0.1.12"
54
+ "octane": "0.1.13"
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.13"
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';
@@ -243,6 +243,8 @@ export function octane(inlineOptions = {}) {
243
243
  let buildOctaneConfig = null;
244
244
  /** @type {string[]} Module paths the generated client entry maps statically (build only) */
245
245
  let staticEntries = [];
246
+ /** @type {Record<string, string>} Static module path → emitted client chunk file */
247
+ let staticEntryFiles = Object.create(null);
246
248
  /** @type {Set<string>} Vite-root paths of modules containing `module server` */
247
249
  const serverModuleModules = new Set();
248
250
 
@@ -328,6 +330,25 @@ export function octane(inlineOptions = {}) {
328
330
  if (buildOctaneConfig.build.target !== undefined) {
329
331
  buildConfig.target = buildOctaneConfig.build.target;
330
332
  }
333
+ const userModulePreload = userConfig.build?.modulePreload;
334
+ if (userModulePreload === false) {
335
+ buildConfig.modulePreload = false;
336
+ } else {
337
+ /** @type {ModulePreloadOptions} */
338
+ const modulePreload =
339
+ typeof userModulePreload === 'object' ? { ...userModulePreload } : {};
340
+ const userResolveDependencies = modulePreload.resolveDependencies;
341
+ modulePreload.resolveDependencies = (filename, dependencies, context) => {
342
+ // Vite 8.1 emits entry dependency hints after the entry script.
343
+ // A script that installs <base> in between can redirect those
344
+ // root-relative requests off-origin. Static imports discover the
345
+ // same dependencies safely from the entry module URL, so omit only
346
+ // the redundant HTML hints; retain JS dynamic-import preloading.
347
+ if (context.hostType === 'html') return [];
348
+ return userResolveDependencies?.(filename, dependencies, context) ?? dependencies;
349
+ };
350
+ buildConfig.modulePreload = modulePreload;
351
+ }
331
352
  const userRenderBuiltUrl = userConfig.experimental?.renderBuiltUrl;
332
353
  /** @type {RenderBuiltAssetUrl} */
333
354
  const renderBuiltUrl = (filename, context) => {
@@ -367,6 +388,28 @@ export function octane(inlineOptions = {}) {
367
388
  if (!isBuild || isSSRBuild || !has_route_config(buildOctaneConfig)) return;
368
389
  serverModuleModules.clear();
369
390
  staticEntries = collect_hydrate_module_paths(buildOctaneConfig);
391
+ staticEntryFiles = Object.create(null);
392
+ },
393
+
394
+ /**
395
+ * Preserve the source-to-file relation that Vite's manifest cannot express
396
+ * when Rolldown promotes a dynamic route entry into a shared chunk.
397
+ */
398
+ generateBundle(_options, bundle) {
399
+ if (!isBuild || isSSRBuild || !has_route_config(buildOctaneConfig)) return;
400
+ const entryByModuleId = new Map(
401
+ staticEntries.map((moduleId) => {
402
+ const file = path.resolve(root, moduleId.startsWith('/') ? `.${moduleId}` : moduleId);
403
+ return [file.split(path.sep).join('/'), moduleId];
404
+ }),
405
+ );
406
+ for (const output of Object.values(bundle)) {
407
+ if (output.type !== 'chunk') continue;
408
+ for (const moduleId of output.moduleIds) {
409
+ const entryId = entryByModuleId.get(moduleId.split(path.sep).join('/'));
410
+ if (entryId !== undefined) staticEntryFiles[entryId] = output.fileName;
411
+ }
412
+ }
370
413
  },
371
414
 
372
415
  async configResolved(resolvedConfig) {
@@ -693,7 +736,7 @@ export function octane(inlineOptions = {}) {
693
736
  );
694
737
  }
695
738
 
696
- const clientAssetMap = createClientAssetMap(clientManifest, staticEntries);
739
+ const clientAssetMap = createClientAssetMap(clientManifest, staticEntries, staticEntryFiles);
697
740
 
698
741
  // The manifest was only needed here; leaving .vite/ in dist/client would
699
742
  // publish source file paths through the static server.