mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-31 15:50:32 +00:00
refactor(vite): reuse logic and improve code splitting (#6164)
This commit is contained in:
parent
fe0b469ca9
commit
d15c4727a8
@ -1,11 +1,17 @@
|
|||||||
import { pathToFileURL } from 'node:url'
|
import { pathToFileURL } from 'node:url'
|
||||||
import { existsSync } from 'node:fs'
|
import { existsSync } from 'node:fs'
|
||||||
import { builtinModules } from 'node:module'
|
import { builtinModules } from 'node:module'
|
||||||
import { isAbsolute, resolve } from 'pathe'
|
import { isAbsolute, normalize, resolve } from 'pathe'
|
||||||
import * as vite from 'vite'
|
import * as vite from 'vite'
|
||||||
import { ExternalsOptions, isExternal as _isExternal, ExternalsDefaults } from 'externality'
|
import { isExternal } from 'externality'
|
||||||
import { genDynamicImport, genObjectFromRawEntries } from 'knitwork'
|
import { genDynamicImport, genObjectFromRawEntries } from 'knitwork'
|
||||||
import { hashId, uniq } from './utils'
|
import fse from 'fs-extra'
|
||||||
|
import { debounce } from 'perfect-debounce'
|
||||||
|
import { isIgnored, logger } from '@nuxt/kit'
|
||||||
|
import { hashId, isCSS, uniq } from './utils'
|
||||||
|
import { createIsExternal } from './utils/external'
|
||||||
|
import { writeManifest } from './manifest'
|
||||||
|
import { ViteBuildContext } from './vite'
|
||||||
|
|
||||||
export interface TransformChunk {
|
export interface TransformChunk {
|
||||||
id: string,
|
id: string,
|
||||||
@ -23,29 +29,7 @@ export interface SSRTransformResult {
|
|||||||
|
|
||||||
export interface TransformOptions {
|
export interface TransformOptions {
|
||||||
viteServer: vite.ViteDevServer
|
viteServer: vite.ViteDevServer
|
||||||
}
|
isExternal(id: string): ReturnType<typeof isExternal>
|
||||||
|
|
||||||
function isExternal (opts: TransformOptions, id: string) {
|
|
||||||
// Externals
|
|
||||||
const ssrConfig = (opts.viteServer.config as any).ssr
|
|
||||||
|
|
||||||
const externalOpts: ExternalsOptions = {
|
|
||||||
inline: [
|
|
||||||
/virtual:/,
|
|
||||||
/\.ts$/,
|
|
||||||
...ExternalsDefaults.inline,
|
|
||||||
...ssrConfig.noExternal
|
|
||||||
],
|
|
||||||
external: [
|
|
||||||
...ssrConfig.external,
|
|
||||||
/node_modules/
|
|
||||||
],
|
|
||||||
resolve: {
|
|
||||||
type: 'module',
|
|
||||||
extensions: ['.ts', '.js', '.json', '.vue', '.mjs', '.jsx', '.tsx', '.wasm']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _isExternal(id, opts.viteServer.config.root, externalOpts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function transformRequest (opts: TransformOptions, id: string) {
|
async function transformRequest (opts: TransformOptions, id: string) {
|
||||||
@ -74,7 +58,7 @@ async function transformRequest (opts: TransformOptions, id: string) {
|
|||||||
// Vite will add ?v=123 to bypass browser cache
|
// Vite will add ?v=123 to bypass browser cache
|
||||||
// Remove for externals
|
// Remove for externals
|
||||||
const withoutVersionQuery = id.replace(/\?v=\w+$/, '')
|
const withoutVersionQuery = id.replace(/\?v=\w+$/, '')
|
||||||
if (await isExternal(opts, withoutVersionQuery)) {
|
if (await opts.isExternal(withoutVersionQuery)) {
|
||||||
const path = builtinModules.includes(withoutVersionQuery.split('node:').pop())
|
const path = builtinModules.includes(withoutVersionQuery.split('node:').pop())
|
||||||
? withoutVersionQuery
|
? withoutVersionQuery
|
||||||
: isAbsolute(withoutVersionQuery) ? pathToFileURL(withoutVersionQuery).href : withoutVersionQuery
|
: isAbsolute(withoutVersionQuery) ? pathToFileURL(withoutVersionQuery).href : withoutVersionQuery
|
||||||
@ -245,3 +229,36 @@ async function __instantiateModule__(url, urlStack) {
|
|||||||
ids: chunks.map(i => i.id)
|
ids: chunks.map(i => i.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function initViteDevBundler (ctx: ViteBuildContext, onBuild: () => Promise<any>) {
|
||||||
|
const viteServer = ctx.ssrServer
|
||||||
|
const options: TransformOptions = {
|
||||||
|
viteServer,
|
||||||
|
isExternal: createIsExternal(viteServer, ctx.nuxt.options.rootDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build and watch
|
||||||
|
const _doBuild = async () => {
|
||||||
|
const start = Date.now()
|
||||||
|
const { code, ids } = await bundleRequest(options, resolve(ctx.nuxt.options.appDir, 'entry'))
|
||||||
|
await fse.writeFile(resolve(ctx.nuxt.options.buildDir, 'dist/server/server.mjs'), code, 'utf-8')
|
||||||
|
// Have CSS in the manifest to prevent FOUC on dev SSR
|
||||||
|
await writeManifest(ctx, ids.filter(isCSS).map(i => i.slice(1)))
|
||||||
|
const time = (Date.now() - start)
|
||||||
|
logger.success(`Vite server built in ${time}ms`)
|
||||||
|
await onBuild()
|
||||||
|
}
|
||||||
|
const doBuild = debounce(_doBuild)
|
||||||
|
|
||||||
|
// Initial build
|
||||||
|
await _doBuild()
|
||||||
|
|
||||||
|
// Watch
|
||||||
|
viteServer.watcher.on('all', (_event, file) => {
|
||||||
|
file = normalize(file) // Fix windows paths
|
||||||
|
if (file.indexOf(ctx.nuxt.options.buildDir) === 0 || isIgnored(file)) { return }
|
||||||
|
doBuild()
|
||||||
|
})
|
||||||
|
// ctx.nuxt.hook('builder:watch', () => doBuild())
|
||||||
|
ctx.nuxt.hook('app:templatesGenerated', () => doBuild())
|
||||||
|
}
|
||||||
|
@ -1,22 +1,16 @@
|
|||||||
import { resolveTSConfig } from 'pkg-types'
|
import { resolveTSConfig } from 'pkg-types'
|
||||||
import { resolve, normalize } from 'pathe'
|
import { resolve } from 'pathe'
|
||||||
import * as vite from 'vite'
|
import * as vite from 'vite'
|
||||||
import vuePlugin from '@vitejs/plugin-vue'
|
import vuePlugin from '@vitejs/plugin-vue'
|
||||||
import viteJsxPlugin from '@vitejs/plugin-vue-jsx'
|
import viteJsxPlugin from '@vitejs/plugin-vue-jsx'
|
||||||
import { logger, resolveModule, isIgnored } from '@nuxt/kit'
|
import { logger, resolveModule } from '@nuxt/kit'
|
||||||
import fse from 'fs-extra'
|
|
||||||
import { debounce } from 'perfect-debounce'
|
|
||||||
import { joinURL, withoutLeadingSlash, withTrailingSlash } from 'ufo'
|
import { joinURL, withoutLeadingSlash, withTrailingSlash } from 'ufo'
|
||||||
import { ViteBuildContext, ViteOptions } from './vite'
|
import { ViteBuildContext, ViteOptions } from './vite'
|
||||||
import { wpfs } from './utils/wpfs'
|
import { wpfs } from './utils/wpfs'
|
||||||
import { cacheDirPlugin } from './plugins/cache-dir'
|
import { cacheDirPlugin } from './plugins/cache-dir'
|
||||||
import { prepareDevServerEntry } from './vite-node'
|
|
||||||
import { isCSS } from './utils'
|
|
||||||
import { bundleRequest } from './dev-bundler'
|
|
||||||
import { writeManifest } from './manifest'
|
|
||||||
|
|
||||||
export async function buildServer (ctx: ViteBuildContext) {
|
export async function buildServer (ctx: ViteBuildContext) {
|
||||||
const _resolve = id => resolveModule(id, { paths: ctx.nuxt.options.modulesDir })
|
const _resolve = (id: string) => resolveModule(id, { paths: ctx.nuxt.options.modulesDir })
|
||||||
const serverConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
|
const serverConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
|
||||||
base: ctx.nuxt.options.dev
|
base: ctx.nuxt.options.dev
|
||||||
? joinURL(ctx.nuxt.options.app.baseURL.replace(/^\.\//, '/') || '/', ctx.nuxt.options.app.buildAssetsDir)
|
? joinURL(ctx.nuxt.options.app.baseURL.replace(/^\.\//, '/') || '/', ctx.nuxt.options.app.buildAssetsDir)
|
||||||
@ -149,31 +143,8 @@ export async function buildServer (ctx: ViteBuildContext) {
|
|||||||
|
|
||||||
if (ctx.nuxt.options.experimental.viteNode) {
|
if (ctx.nuxt.options.experimental.viteNode) {
|
||||||
logger.info('Vite server using experimental `vite-node`...')
|
logger.info('Vite server using experimental `vite-node`...')
|
||||||
await prepareDevServerEntry(ctx)
|
await import('./vite-node').then(r => r.initViteNodeServer(ctx))
|
||||||
} else {
|
} else {
|
||||||
// Build and watch
|
await import('./dev-bundler').then(r => r.initViteDevBundler(ctx, onBuild))
|
||||||
const _doBuild = async () => {
|
|
||||||
const start = Date.now()
|
|
||||||
const { code, ids } = await bundleRequest({ viteServer }, resolve(ctx.nuxt.options.appDir, 'entry'))
|
|
||||||
await fse.writeFile(resolve(ctx.nuxt.options.buildDir, 'dist/server/server.mjs'), code, 'utf-8')
|
|
||||||
// Have CSS in the manifest to prevent FOUC on dev SSR
|
|
||||||
await writeManifest(ctx, ids.filter(isCSS).map(i => i.slice(1)))
|
|
||||||
const time = (Date.now() - start)
|
|
||||||
logger.success(`Vite server built in ${time}ms`)
|
|
||||||
await onBuild()
|
|
||||||
}
|
|
||||||
const doBuild = debounce(_doBuild)
|
|
||||||
|
|
||||||
// Initial build
|
|
||||||
await _doBuild()
|
|
||||||
|
|
||||||
// Watch
|
|
||||||
viteServer.watcher.on('all', (_event, file) => {
|
|
||||||
file = normalize(file) // Fix windows paths
|
|
||||||
if (file.indexOf(ctx.nuxt.options.buildDir) === 0 || isIgnored(file)) { return }
|
|
||||||
doBuild()
|
|
||||||
})
|
|
||||||
// ctx.nuxt.hook('builder:watch', () => doBuild())
|
|
||||||
ctx.nuxt.hook('app:templatesGenerated', () => doBuild())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
packages/vite/src/utils/external.ts
Normal file
23
packages/vite/src/utils/external.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ExternalsOptions, ExternalsDefaults, isExternal } from 'externality'
|
||||||
|
import { ViteDevServer } from 'vite'
|
||||||
|
|
||||||
|
export function createIsExternal (viteServer: ViteDevServer, rootDir: string) {
|
||||||
|
const externalOpts: ExternalsOptions = {
|
||||||
|
inline: [
|
||||||
|
/virtual:/,
|
||||||
|
/\.ts$/,
|
||||||
|
...ExternalsDefaults.inline,
|
||||||
|
...viteServer.config.ssr.noExternal as string[]
|
||||||
|
],
|
||||||
|
external: [
|
||||||
|
...viteServer.config.ssr.external,
|
||||||
|
/node_modules/
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
type: 'module',
|
||||||
|
extensions: ['.ts', '.js', '.json', '.vue', '.mjs', '.jsx', '.tsx', '.wasm']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (id:string) => isExternal(id, rootDir, externalOpts)
|
||||||
|
}
|
@ -5,11 +5,11 @@ import fse from 'fs-extra'
|
|||||||
import { resolve } from 'pathe'
|
import { resolve } from 'pathe'
|
||||||
import { addServerMiddleware } from '@nuxt/kit'
|
import { addServerMiddleware } from '@nuxt/kit'
|
||||||
import type { Plugin as VitePlugin, ViteDevServer } from 'vite'
|
import type { Plugin as VitePlugin, ViteDevServer } from 'vite'
|
||||||
import { ExternalsOptions, isExternal, ExternalsDefaults } from 'externality'
|
|
||||||
import { resolve as resolveModule } from 'mlly'
|
import { resolve as resolveModule } from 'mlly'
|
||||||
import { distDir } from './dirs'
|
import { distDir } from './dirs'
|
||||||
import type { ViteBuildContext } from './vite'
|
import type { ViteBuildContext } from './vite'
|
||||||
import { isCSS } from './utils'
|
import { isCSS } from './utils'
|
||||||
|
import { createIsExternal } from './utils/external'
|
||||||
|
|
||||||
// TODO: Remove this in favor of registerViteNodeMiddleware
|
// TODO: Remove this in favor of registerViteNodeMiddleware
|
||||||
// after Nitropack or h3 fixed for adding middlewares after setup
|
// after Nitropack or h3 fixed for adding middlewares after setup
|
||||||
@ -72,27 +72,11 @@ function createViteNodeMiddleware (ctx: ViteBuildContext) {
|
|||||||
web: []
|
web: []
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const externalOpts: ExternalsOptions = {
|
const isExternal = createIsExternal(viteServer, ctx.nuxt.options.rootDir)
|
||||||
inline: [
|
|
||||||
/virtual:/,
|
|
||||||
/\.ts$/,
|
|
||||||
...ExternalsDefaults.inline,
|
|
||||||
...viteServer.config.ssr.noExternal as string[]
|
|
||||||
],
|
|
||||||
external: [
|
|
||||||
...viteServer.config.ssr.external,
|
|
||||||
/node_modules/
|
|
||||||
],
|
|
||||||
resolve: {
|
|
||||||
type: 'module',
|
|
||||||
extensions: ['.ts', '.js', '.json', '.vue', '.mjs', '.jsx', '.tsx', '.wasm']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const rootDir = ctx.nuxt.options.rootDir
|
|
||||||
node.shouldExternalize = async (id: string) => {
|
node.shouldExternalize = async (id: string) => {
|
||||||
const result = await isExternal(id, rootDir, externalOpts)
|
const result = await isExternal(id)
|
||||||
if (result?.external) {
|
if (result?.external) {
|
||||||
return resolveModule(result.id, { url: rootDir })
|
return resolveModule(result.id, { url: ctx.nuxt.options.rootDir })
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -110,7 +94,7 @@ function createViteNodeMiddleware (ctx: ViteBuildContext) {
|
|||||||
return app.nodeHandler
|
return app.nodeHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function prepareDevServerEntry (ctx: ViteBuildContext) {
|
export async function initViteNodeServer (ctx: ViteBuildContext) {
|
||||||
let entryPath = resolve(ctx.nuxt.options.appDir, 'entry.async.mjs')
|
let entryPath = resolve(ctx.nuxt.options.appDir, 'entry.async.mjs')
|
||||||
if (!fse.existsSync(entryPath)) {
|
if (!fse.existsSync(entryPath)) {
|
||||||
entryPath = resolve(ctx.nuxt.options.appDir, 'entry.async')
|
entryPath = resolve(ctx.nuxt.options.appDir, 'entry.async')
|
||||||
|
Loading…
Reference in New Issue
Block a user