@octanejs/app-core 0.0.37 → 0.0.38

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/app-core",
3
- "version": "0.0.37",
3
+ "version": "0.0.38",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "engines": {
@@ -79,10 +79,10 @@
79
79
  "esbuild": "^0.28.1"
80
80
  },
81
81
  "peerDependencies": {
82
- "octane": "0.1.41"
82
+ "octane": "0.1.42"
83
83
  },
84
84
  "devDependencies": {
85
85
  "@types/node": "^24.13.3",
86
- "octane": "0.1.41"
86
+ "octane": "0.1.42"
87
87
  }
88
88
  }
@@ -296,21 +296,36 @@ export function createHandler(manifest, deps) {
296
296
  });
297
297
  const dataScript = `<script id="__octane_data" type="application/json"${nonceAttribute(nonce)}>${escapeScript(routeData)}</script>`;
298
298
 
299
- // Per-route asset hints from the client manifest: stylesheet links so
300
- // page CSS applies before hydration, modulepreload so the page chunk
301
- // downloads in parallel with the hydrate entry (which the template's own
302
- // script tag already references).
299
+ // The page, layout, and configured root fallbacks can all contribute
300
+ // server HTML before hydration. Their asset records also include CSS for
301
+ // deferred Hydrate descendants, whose JavaScript must remain lazy. Keep
302
+ // page CSS first, preserve each record's order, and link shared files once.
303
303
  /** @type {string[]} */
304
304
  const preloadTags = [];
305
- const entryAssets = entryPath ? manifest.clientAssets?.[entryPath] : undefined;
306
- if (entryAssets) {
307
- for (const cssFile of entryAssets.css) {
308
- preloadTags.push(`<link rel="stylesheet" href="/${cssFile}">`);
309
- }
310
- if (entryAssets.js) {
311
- preloadTags.push(`<link rel="modulepreload" href="/${entryAssets.js}">`);
305
+ const clientAssets = manifest.clientAssets;
306
+ const entryAssets = entryPath ? clientAssets?.[entryPath] : undefined;
307
+ if (clientAssets) {
308
+ /** @type {Set<string>} */
309
+ const stylesheets = new Set();
310
+ for (const modulePath of [
311
+ entryPath,
312
+ route.layout,
313
+ manifest.rootBoundary?.pending ? manifest.rootBoundaryEntries?.pending?.path : undefined,
314
+ manifest.rootBoundary?.catch ? manifest.rootBoundaryEntries?.catch?.path : undefined,
315
+ ]) {
316
+ if (!modulePath) continue;
317
+ for (const cssFile of clientAssets[modulePath]?.css ?? []) {
318
+ if (stylesheets.has(cssFile)) continue;
319
+ stylesheets.add(cssFile);
320
+ preloadTags.push(`<link rel="stylesheet" href="/${cssFile}">`);
321
+ }
312
322
  }
313
323
  }
324
+ // Only the page chunk was already eager; do not promote layout, fallback,
325
+ // or island JavaScript while making their server-rendered CSS available.
326
+ if (entryAssets?.js) {
327
+ preloadTags.push(`<link rel="modulepreload" href="/${entryAssets.js}">`);
328
+ }
314
329
 
315
330
  const headContent = [...preloadTags, dataScript].join('\n');
316
331
  const noncedTemplate = applyHydrationNonce(htmlTemplate, nonce);