diff --git a/packages/nuxt/src/pages/module.ts b/packages/nuxt/src/pages/module.ts index e331c3402e..f8bc385e84 100644 --- a/packages/nuxt/src/pages/module.ts +++ b/packages/nuxt/src/pages/module.ts @@ -1,6 +1,6 @@ import { existsSync } from 'node:fs' import { defineNuxtModule, addTemplate, addPlugin, addVitePlugin, addWebpackPlugin, findPath } from '@nuxt/kit' -import { resolve } from 'pathe' +import { relative, resolve } from 'pathe' import { genString, genImport, genObjectFromRawEntries } from 'knitwork' import escapeRE from 'escape-string-regexp' import type { NuxtApp, NuxtPage } from '@nuxt/schema' @@ -99,6 +99,24 @@ export default defineNuxtModule({ // Add router plugin addPlugin(resolve(runtimeDir, 'router')) + const getSources = (pages: NuxtPage[]): string[] => pages.flatMap(p => + [relative(nuxt.options.srcDir, p.file), ...getSources(p.children || [])] + ) + + // Do not prefetch page chunks + nuxt.hook('build:manifest', async (manifest) => { + const pages = await resolvePagesRoutes() + await nuxt.callHook('pages:extend', pages) + + const sourceFiles = getSources(pages) + for (const key in manifest) { + if (manifest[key].isEntry) { + manifest[key].dynamicImports = + manifest[key].dynamicImports?.filter(i => !sourceFiles.includes(i)) + } + } + }) + // Add routes template addTemplate({ filename: 'routes.mjs', diff --git a/packages/schema/build.config.ts b/packages/schema/build.config.ts index 8a04430474..285a00ae19 100644 --- a/packages/schema/build.config.ts +++ b/packages/schema/build.config.ts @@ -21,6 +21,7 @@ export default defineBuildConfig({ // Type imports 'vue-meta', 'vue-router', + 'vue-bundle-renderer', 'vue', 'hookable', 'nitropack', diff --git a/packages/schema/src/types/hooks.ts b/packages/schema/src/types/hooks.ts index 5dfb838b78..d6d3c7fcba 100644 --- a/packages/schema/src/types/hooks.ts +++ b/packages/schema/src/types/hooks.ts @@ -3,6 +3,7 @@ import type { Server as HttpsServer } from 'node:https' import type { Compiler, Configuration, Stats } from 'webpack' import type { TSConfig } from 'pkg-types' import type { InlineConfig as ViteInlineConfig, ViteDevServer } from 'vite' +import type { Manifest } from 'vue-bundle-renderer' import type { ModuleContainer } from './module' import type { NuxtTemplate, Nuxt, NuxtApp } from './nuxt' import type { Preset as ImportPreset, Import } from 'unimport' @@ -72,6 +73,7 @@ export interface NuxtHooks { 'app:templatesGenerated': (app: NuxtApp) => HookResult 'builder:generateApp': () => HookResult 'pages:extend': (pages: NuxtPage[]) => HookResult + 'build:manifest': (manifest: Manifest) => HookResult // Auto imports 'autoImports:sources': (presets: ImportPresetWithDeprecation[]) => HookResult diff --git a/packages/vite/src/manifest.ts b/packages/vite/src/manifest.ts index 2461ffec4a..30f715cc3d 100644 --- a/packages/vite/src/manifest.ts +++ b/packages/vite/src/manifest.ts @@ -2,7 +2,8 @@ import fse from 'fs-extra' import { resolve } from 'pathe' import { withoutLeadingSlash, withTrailingSlash } from 'ufo' import escapeRE from 'escape-string-regexp' -import { normalizeViteManifest, Manifest } from 'vue-bundle-renderer' +import { normalizeViteManifest } from 'vue-bundle-renderer' +import type { Manifest } from 'vue-bundle-renderer' import type { ViteBuildContext } from './vite' export async function writeManifest (ctx: ViteBuildContext, css: string[] = []) { @@ -45,7 +46,10 @@ export async function writeManifest (ctx: ViteBuildContext, css: string[] = []) } await fse.mkdirp(serverDist) + const manifest = normalizeViteManifest(clientManifest) + await ctx.nuxt.callHook('build:manifest', manifest) + await fse.writeFile(resolve(serverDist, 'client.manifest.json'), JSON.stringify(manifest, null, 2), 'utf8') await fse.writeFile(resolve(serverDist, 'client.manifest.mjs'), 'export default ' + JSON.stringify(manifest, null, 2), 'utf8') } diff --git a/packages/webpack/src/plugins/vue/client.ts b/packages/webpack/src/plugins/vue/client.ts index 49c0e142ac..de8259996b 100644 --- a/packages/webpack/src/plugins/vue/client.ts +++ b/packages/webpack/src/plugins/vue/client.ts @@ -9,14 +9,18 @@ import hash from 'hash-sum' import { uniq } from 'lodash-es' import fse from 'fs-extra' +import type { Nuxt } from '@nuxt/schema' import { isJS, isCSS, isHotUpdate } from './util' -export default class VueSSRClientPlugin { - options: { - filename: string - } +interface PluginOptions { + filename: string + nuxt: Nuxt +} - constructor (options = {}) { +export default class VueSSRClientPlugin { + options: PluginOptions + + constructor (options: PluginOptions) { this.options = Object.assign({ filename: null }, options) @@ -53,7 +57,7 @@ export default class VueSSRClientPlugin { assetsMapping[componentHash].push(name) }) - const manifest = { + const webpackManifest = { publicPath: stats.publicPath, all: allFiles, initial: initialFiles, @@ -64,7 +68,7 @@ export default class VueSSRClientPlugin { const { entrypoints, namedChunkGroups } = stats const assetModules = stats.modules.filter(m => m.assets.length) - const fileToIndex = file => manifest.all.indexOf(file) + const fileToIndex = file => webpackManifest.all.indexOf(file) stats.modules.forEach((m) => { // Ignore modules duplicated in multiple chunks if (m.chunks.length === 1) { @@ -88,15 +92,15 @@ export default class VueSSRClientPlugin { } const files = Array.from(filesSet) - manifest.modules[hash(id)] = files + webpackManifest.modules[hash(id)] = files // In production mode, modules may be concatenated by scope hoisting // Include ConcatenatedModule for not losing module-component mapping if (Array.isArray(m.modules)) { for (const concatenatedModule of m.modules) { const id = hash(concatenatedModule.identifier.replace(/\s\w+$/, '')) - if (!manifest.modules[id]) { - manifest.modules[id] = files + if (!webpackManifest.modules[id]) { + webpackManifest.modules[id] = files } } } @@ -110,7 +114,10 @@ export default class VueSSRClientPlugin { } }) - const src = JSON.stringify(normalizeWebpackManifest(manifest), null, 2) + const manifest = normalizeWebpackManifest(webpackManifest) + await this.options.nuxt.callHook('build:manifest', manifest) + + const src = JSON.stringify(manifest, null, 2) await fse.mkdirp(dirname(this.options.filename)) await fse.writeFile(this.options.filename, src) diff --git a/packages/webpack/src/presets/vue.ts b/packages/webpack/src/presets/vue.ts index 9ab1086d31..8032e696b8 100644 --- a/packages/webpack/src/presets/vue.ts +++ b/packages/webpack/src/presets/vue.ts @@ -22,7 +22,8 @@ export function vue (ctx: WebpackConfigContext) { if (ctx.isClient) { config.plugins.push(new VueSSRClientPlugin({ - filename: resolve(options.buildDir, 'dist/server', `${ctx.name}.manifest.json`) + filename: resolve(options.buildDir, 'dist/server', `${ctx.name}.manifest.json`), + nuxt: ctx.nuxt })) } else { config.plugins.push(new VueSSRServerPlugin({