feat(vite)!: upgrade vite to v3 (#5398)

This commit is contained in:
Daniel Roe 2022-07-21 11:44:33 +01:00 committed by GitHub
parent 2e794e7a19
commit fa8b0f7c79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 178 additions and 235 deletions

View File

@ -42,7 +42,7 @@
"nuxi": "link:./packages/nuxi", "nuxi": "link:./packages/nuxi",
"nuxt": "link:./packages/nuxt", "nuxt": "link:./packages/nuxt",
"nuxt3": "link:./packages/nuxt", "nuxt3": "link:./packages/nuxt",
"vite": "^2.9.14", "vite": "~3.0.0",
"unbuild": "^0.7.6" "unbuild": "^0.7.6"
}, },
"devDependencies": { "devDependencies": {

View File

@ -31,8 +31,8 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => {
const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href)) const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href))
const query = parseQuery(search) const query = parseQuery(search)
// we only transform render functions // we only transform render functions
// from `type=template` (in Webpack) and bare `.vue` file (in Vite) // from `type=template` (in Webpack), bare `.vue` file and setup=true (Vite 2/3)
return pathname.endsWith('.vue') && (query.type === 'template' || !!query.macro || !search) return pathname.endsWith('.vue') && (query.type === 'template' || !!query.macro || !!query.setup || !search)
}, },
transform (code, id) { transform (code, id) {
const components = options.getComponents() const components = options.getComponents()

View File

@ -41,10 +41,7 @@ export async function initNitro (nuxt: Nuxt) {
generateTsConfig: false generateTsConfig: false
}, },
publicAssets: [ publicAssets: [
{ { dir: resolve(nuxt.options.buildDir, 'dist/client') },
baseURL: nuxt.options.app.buildAssetsDir,
dir: resolve(nuxt.options.buildDir, 'dist/client')
},
...nuxt.options._layers ...nuxt.options._layers
.map(layer => join(layer.config.srcDir, layer.config.dir?.public || 'public')) .map(layer => join(layer.config.srcDir, layer.config.dir?.public || 'public'))
.filter(dir => existsSync(dir)) .filter(dir => existsSync(dir))

View File

@ -211,7 +211,10 @@ export const publicPathTemplate: NuxtTemplate = {
'export const publicAssetsURL = (...path) => {', 'export const publicAssetsURL = (...path) => {',
' const publicBase = appConfig.cdnURL || appConfig.baseURL', ' const publicBase = appConfig.cdnURL || appConfig.baseURL',
' return path.length ? joinURL(publicBase, ...path) : publicBase', ' return path.length ? joinURL(publicBase, ...path) : publicBase',
'}' '}',
'globalThis.__buildAssetsURL = buildAssetsURL',
'globalThis.__publicAssetsURL = publicAssetsURL'
].filter(Boolean).join('\n') ].filter(Boolean).join('\n')
} }
} }

View File

@ -3,6 +3,7 @@ import { createUnplugin } from 'unplugin'
import { parseQuery, parseURL, withQuery } from 'ufo' import { parseQuery, parseURL, withQuery } from 'ufo'
import { findStaticImports, findExports } from 'mlly' import { findStaticImports, findExports } from 'mlly'
import MagicString from 'magic-string' import MagicString from 'magic-string'
import { isAbsolute } from 'pathe'
export interface TransformMacroPluginOptions { export interface TransformMacroPluginOptions {
macros: Record<string, string> macros: Record<string, string>
@ -48,7 +49,8 @@ export const TransformMacroPlugin = createUnplugin((options: TransformMacroPlugi
if (scriptImport) { if (scriptImport) {
// https://github.com/vuejs/vue-loader/pull/1911 // https://github.com/vuejs/vue-loader/pull/1911
// https://github.com/vitejs/vite/issues/8473 // https://github.com/vitejs/vite/issues/8473
const parsed = parseURL(scriptImport.specifier.replace('?macro=true', '')) const url = isAbsolute(scriptImport.specifier) ? pathToFileURL(scriptImport.specifier).href : scriptImport.specifier
const parsed = parseURL(decodeURIComponent(url).replace('?macro=true', ''))
const specifier = withQuery(parsed.pathname, { macro: 'true', ...parseQuery(parsed.search) }) const specifier = withQuery(parsed.pathname, { macro: 'true', ...parseQuery(parsed.search) })
s.overwrite(0, code.length, `export { meta } from "${specifier}"`) s.overwrite(0, code.length, `export { meta } from "${specifier}"`)
return result() return result()

View File

@ -17,7 +17,7 @@
"@types/lodash.template": "^4", "@types/lodash.template": "^4",
"@types/semver": "^7", "@types/semver": "^7",
"unbuild": "latest", "unbuild": "latest",
"vite": "^2.9.14" "vite": "~3.0.0"
}, },
"dependencies": { "dependencies": {
"c12": "^0.2.8", "c12": "^0.2.8",

View File

@ -1,5 +1,5 @@
import { resolve } from 'pathe' import { resolve } from 'pathe'
import { joinURL, withoutLeadingSlash } from 'ufo' import { withoutLeadingSlash } from 'ufo'
export default { export default {
/** /**
@ -28,11 +28,6 @@ export default {
resolve: { resolve: {
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'], extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
}, },
base: {
$resolve: (val, get) => val ?? get('dev')
? joinURL(get('app').baseURL, get('app').buildAssetsDir)
: '/__NUXT_BASE__/',
},
publicDir: { publicDir: {
$resolve: (val, get) => val ?? resolve(get('srcDir'), get('dir').public), $resolve: (val, get) => val ?? resolve(get('srcDir'), get('dir').public),
}, },
@ -61,7 +56,7 @@ export default {
clearScreen: false, clearScreen: false,
build: { build: {
assetsDir: { assetsDir: {
$resolve: (val, get) => val ?? get('dev') ? withoutLeadingSlash(get('app').buildAssetsDir) : '.', $resolve: (val, get) => val ?? withoutLeadingSlash(get('app').buildAssetsDir),
}, },
emptyOutDir: false, emptyOutDir: false,
}, },

View File

@ -21,8 +21,8 @@
"dependencies": { "dependencies": {
"@nuxt/kit": "^3.0.0-rc.6", "@nuxt/kit": "^3.0.0-rc.6",
"@rollup/plugin-replace": "^4.0.0", "@rollup/plugin-replace": "^4.0.0",
"@vitejs/plugin-vue": "^2.3.3", "@vitejs/plugin-vue": "^3.0.0",
"@vitejs/plugin-vue-jsx": "^1.3.10", "@vitejs/plugin-vue-jsx": "^2.0.0",
"autoprefixer": "^10.4.7", "autoprefixer": "^10.4.7",
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
"cssnano": "^5.1.12", "cssnano": "^5.1.12",
@ -47,7 +47,7 @@
"rollup-plugin-visualizer": "^5.7.1", "rollup-plugin-visualizer": "^5.7.1",
"ufo": "^0.8.5", "ufo": "^0.8.5",
"unplugin": "^0.7.2", "unplugin": "^0.7.2",
"vite": "^2.9.14", "vite": "~3.0.0",
"vite-node": "^0.18.1", "vite-node": "^0.18.1",
"vite-plugin-checker": "^0.4.9" "vite-plugin-checker": "^0.4.9"
}, },

View File

@ -1,21 +1,36 @@
import { resolve } from 'pathe' import { join, 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 type { Connect } from 'vite' import type { Connect } from 'vite'
import { logger } from '@nuxt/kit' import { logger } from '@nuxt/kit'
import { joinURL } from 'ufo' import { getPort } from 'get-port-please'
import { joinURL, withLeadingSlash, withoutLeadingSlash, withTrailingSlash } from 'ufo'
import escapeRE from 'escape-string-regexp'
import { cacheDirPlugin } from './plugins/cache-dir' import { cacheDirPlugin } from './plugins/cache-dir'
import { analyzePlugin } from './plugins/analyze' import { analyzePlugin } from './plugins/analyze'
import { wpfs } from './utils/wpfs' import { wpfs } from './utils/wpfs'
import type { ViteBuildContext, ViteOptions } from './vite' import type { ViteBuildContext, ViteOptions } from './vite'
import { writeManifest } from './manifest' import { writeManifest } from './manifest'
import { devStyleSSRPlugin } from './plugins/dev-ssr-css' import { devStyleSSRPlugin } from './plugins/dev-ssr-css'
import { RelativeAssetPlugin } from './plugins/dynamic-base'
import { viteNodePlugin } from './vite-node' import { viteNodePlugin } from './vite-node'
export async function buildClient (ctx: ViteBuildContext) { export async function buildClient (ctx: ViteBuildContext) {
const hmrPortDefault = 24678 // Vite's default HMR port
const hmrPort = await getPort({
port: hmrPortDefault,
ports: Array.from({ length: 20 }, (_, i) => hmrPortDefault + 1 + i)
})
const clientConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, { const clientConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
experimental: {
renderBuiltUrl: (filename, { type, hostType }) => {
if (hostType !== 'js' || type === 'asset') {
// In CSS we only use relative paths until we craft a clever runtime CSS hack
return { relative: true }
}
return { runtime: `globalThis.__publicAssetsURL(${JSON.stringify(filename)})` }
}
},
define: { define: {
'process.server': false, 'process.server': false,
'process.client': true, 'process.client': true,
@ -30,8 +45,10 @@ export async function buildClient (ctx: ViteBuildContext) {
build: { build: {
rollupOptions: { rollupOptions: {
output: { output: {
chunkFileNames: ctx.nuxt.options.dev ? undefined : '[name]-[hash].mjs', // https://github.com/vitejs/vite/tree/main/packages/vite/src/node/build.ts#L464-L478
entryFileNames: ctx.nuxt.options.dev ? 'entry.mjs' : '[name]-[hash].mjs' assetFileNames: ctx.nuxt.options.dev ? undefined : withoutLeadingSlash(join(ctx.nuxt.options.app.buildAssetsDir, '[name].[hash].[ext]')),
chunkFileNames: ctx.nuxt.options.dev ? undefined : withoutLeadingSlash(join(ctx.nuxt.options.app.buildAssetsDir, '[name].[hash].mjs')),
entryFileNames: ctx.nuxt.options.dev ? 'entry.mjs' : withoutLeadingSlash(join(ctx.nuxt.options.app.buildAssetsDir, '[name].[hash].mjs'))
} }
}, },
manifest: true, manifest: true,
@ -41,18 +58,30 @@ export async function buildClient (ctx: ViteBuildContext) {
cacheDirPlugin(ctx.nuxt.options.rootDir, 'client'), cacheDirPlugin(ctx.nuxt.options.rootDir, 'client'),
vuePlugin(ctx.config.vue), vuePlugin(ctx.config.vue),
viteJsxPlugin(), viteJsxPlugin(),
RelativeAssetPlugin(),
devStyleSSRPlugin({ devStyleSSRPlugin({
rootDir: ctx.nuxt.options.rootDir, rootDir: ctx.nuxt.options.rootDir,
buildAssetsURL: joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir) buildAssetsURL: joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir)
}), }),
viteNodePlugin(ctx) viteNodePlugin(ctx)
], ],
appType: 'custom',
server: { server: {
hmr: {
// https://github.com/nuxt/framework/issues/4191
protocol: 'ws',
clientPort: hmrPort,
port: hmrPort
},
middlewareMode: true middlewareMode: true
} }
} as ViteOptions) } as ViteOptions)
// In build mode we explicitly override any vite options that vite is relying on
// to detect whether to inject production or development code (such as HMR code)
if (!ctx.nuxt.options.dev) {
clientConfig.server.hmr = false
}
// Add analyze plugin if needed // Add analyze plugin if needed
if (ctx.nuxt.options.build.analyze) { if (ctx.nuxt.options.build.analyze) {
clientConfig.plugins.push(...analyzePlugin(ctx)) clientConfig.plugins.push(...analyzePlugin(ctx))
@ -65,10 +94,11 @@ export async function buildClient (ctx: ViteBuildContext) {
const viteServer = await vite.createServer(clientConfig) const viteServer = await vite.createServer(clientConfig)
ctx.clientServer = viteServer ctx.clientServer = viteServer
await ctx.nuxt.callHook('vite:serverCreated', viteServer, { isClient: true, isServer: false }) await ctx.nuxt.callHook('vite:serverCreated', viteServer, { isClient: true, isServer: false })
const BASE_RE = new RegExp(`^${escapeRE(withTrailingSlash(withLeadingSlash(joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir))))}`)
const viteMiddleware: Connect.NextHandleFunction = (req, res, next) => { const viteMiddleware: Connect.NextHandleFunction = (req, res, next) => {
// Workaround: vite devmiddleware modifies req.url // Workaround: vite devmiddleware modifies req.url
const originalURL = req.url const originalURL = req.url
req.url = req.url.replace(BASE_RE, '/')
viteServer.middlewares.handle(req, res, (err) => { viteServer.middlewares.handle(req, res, (err) => {
req.url = originalURL req.url = originalURL
next(err) next(err)

View File

@ -1,7 +1,7 @@
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 { resolve } from 'pathe' import { isAbsolute, resolve } from 'pathe'
import * as vite from 'vite' import * as vite from 'vite'
import { ExternalsOptions, isExternal as _isExternal, ExternalsDefaults } from 'externality' import { ExternalsOptions, isExternal as _isExternal, ExternalsDefaults } from 'externality'
import { genDynamicImport, genObjectFromRawEntries } from 'knitwork' import { genDynamicImport, genObjectFromRawEntries } from 'knitwork'
@ -77,7 +77,7 @@ async function transformRequest (opts: TransformOptions, id: string) {
if (await isExternal(opts, withoutVersionQuery)) { if (await isExternal(opts, withoutVersionQuery)) {
const path = builtinModules.includes(withoutVersionQuery.split('node:').pop()) const path = builtinModules.includes(withoutVersionQuery.split('node:').pop())
? withoutVersionQuery ? withoutVersionQuery
: pathToFileURL(withoutVersionQuery).href : isAbsolute(withoutVersionQuery) ? pathToFileURL(withoutVersionQuery).href : withoutVersionQuery
return { return {
code: `(global, module, _, exports, importMeta, ssrImport, ssrDynamicImport, ssrExportAll) => code: `(global, module, _, exports, importMeta, ssrImport, ssrDynamicImport, ssrExportAll) =>
${genDynamicImport(path, { wrapper: false })} ${genDynamicImport(path, { wrapper: false })}
@ -140,11 +140,11 @@ export async function bundleRequest (opts: TransformOptions, entryURL: string) {
// Parents: \n${listIds(chunk.parents)} // Parents: \n${listIds(chunk.parents)}
// Dependencies: \n${listIds(chunk.deps)} // Dependencies: \n${listIds(chunk.deps)}
// -------------------- // --------------------
const ${hashId(chunk.id)} = ${chunk.code} const ${hashId(chunk.id + '-' + chunk.code)} = ${chunk.code}
`).join('\n') `).join('\n')
const manifestCode = `const __modules__ = ${ const manifestCode = `const __modules__ = ${
genObjectFromRawEntries(chunks.map(chunk => [chunk.id, hashId(chunk.id)])) genObjectFromRawEntries(chunks.map(chunk => [chunk.id, hashId(chunk.id + '-' + chunk.code)]))
}` }`
// https://github.com/vitejs/vite/blob/main/packages/vite/src/node/ssr/ssrModuleLoader.ts // https://github.com/vitejs/vite/blob/main/packages/vite/src/node/ssr/ssrModuleLoader.ts

View File

@ -1,6 +1,7 @@
import fse from 'fs-extra' import fse from 'fs-extra'
import { resolve } from 'pathe' import { resolve } from 'pathe'
import { joinURL } from 'ufo' import { joinURL, withoutLeadingSlash, withTrailingSlash } from 'ufo'
import escapeRE from 'escape-string-regexp'
import type { ViteBuildContext } from './vite' import type { ViteBuildContext } from './vite'
export async function writeManifest (ctx: ViteBuildContext, extraEntries: string[] = []) { export async function writeManifest (ctx: ViteBuildContext, extraEntries: string[] = []) {
@ -27,6 +28,18 @@ export async function writeManifest (ctx: ViteBuildContext, extraEntries: string
? devClientManifest ? devClientManifest
: await fse.readJSON(resolve(clientDist, 'manifest.json')) : await fse.readJSON(resolve(clientDist, 'manifest.json'))
const BASE_RE = new RegExp(`^${escapeRE(withTrailingSlash(withoutLeadingSlash(joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir))))}`)
for (const key in clientManifest) {
if (clientManifest[key].file) {
clientManifest[key].file = clientManifest[key].file.replace(BASE_RE, '')
}
for (const item of ['css', 'assets']) {
if (clientManifest[key][item]) {
clientManifest[key][item] = clientManifest[key][item].map(i => i.replace(BASE_RE, ''))
}
}
}
await fse.mkdirp(serverDist) await fse.mkdirp(serverDist)
await fse.writeFile(resolve(serverDist, 'client.manifest.json'), JSON.stringify(clientManifest, null, 2), 'utf8') await fse.writeFile(resolve(serverDist, 'client.manifest.json'), JSON.stringify(clientManifest, null, 2), 'utf8')
await fse.writeFile(resolve(serverDist, 'client.manifest.mjs'), 'export default ' + JSON.stringify(clientManifest, null, 2), 'utf8') await fse.writeFile(resolve(serverDist, 'client.manifest.mjs'), 'export default ' + JSON.stringify(clientManifest, null, 2), 'utf8')

View File

@ -1,103 +0,0 @@
import { createUnplugin } from 'unplugin'
import escapeRE from 'escape-string-regexp'
import type { Plugin } from 'vite'
import MagicString from 'magic-string'
interface DynamicBasePluginOptions {
globalPublicPath?: string
sourcemap?: boolean
}
export const RelativeAssetPlugin = function (): Plugin {
return {
name: 'nuxt:vite-relative-asset',
generateBundle (_, bundle) {
const generatedAssets = Object.entries(bundle).filter(([_, asset]) => asset.type === 'asset').map(([key]) => escapeRE(key))
const assetRE = new RegExp(`\\/__NUXT_BASE__\\/(${generatedAssets.join('|')})`, 'g')
for (const file in bundle) {
const asset = bundle[file]
if (asset.fileName.includes('legacy') && asset.type === 'chunk' && asset.code.includes('innerHTML')) {
for (const delimiter of ['`', '"', "'"]) {
asset.code = asset.code.replace(
new RegExp(`(?<=innerHTML=)${delimiter}([^${delimiter}]*)\\/__NUXT_BASE__\\/([^${delimiter}]*)${delimiter}`, 'g'),
/* eslint-disable-next-line no-template-curly-in-string */
'`$1${(window?.__NUXT__?.config.app.cdnURL || window?.__NUXT__?.config.app.baseURL) + window?.__NUXT__?.config.app.buildAssetsDir.slice(1)}$2`'
)
}
}
if (asset.type === 'asset' && typeof asset.source === 'string' && asset.fileName.endsWith('.css')) {
const depth = file.split('/').length - 1
const assetBase = depth === 0 ? '.' : Array.from({ length: depth }).map(() => '..').join('/')
const publicBase = Array.from({ length: depth + 1 }).map(() => '..').join('/')
asset.source = asset.source
.replace(assetRE, r => r.replace(/\/__NUXT_BASE__/g, assetBase))
.replace(/\/__NUXT_BASE__/g, publicBase)
}
if (asset.type === 'chunk' && typeof asset.code === 'string') {
asset.code = asset.code
.replace(/`\$\{(_?_?publicAssetsURL|buildAssetsURL|)\(\)\}([^`]*)`/g, '$1(`$2`)')
.replace(/"\/__NUXT_BASE__\/([^"]*)"\.replace\("\/__NUXT_BASE__", ""\)/g, '"$1"')
.replace(/'\/__NUXT_BASE__\/([^']*)'\.replace\("\/__NUXT_BASE__", ""\)/g, '"$1"')
}
}
}
}
}
const VITE_ASSET_RE = /^export default ["'](__VITE_ASSET.*)["']$/
export const DynamicBasePlugin = createUnplugin(function (options: DynamicBasePluginOptions = {}) {
return {
name: 'nuxt:dynamic-base-path',
resolveId (id) {
if (id.startsWith('/__NUXT_BASE__')) {
return id.replace('/__NUXT_BASE__', '')
}
return null
},
enforce: 'post',
transform (code, id) {
const s = new MagicString(code)
if (options.globalPublicPath && id.includes('paths.mjs') && code.includes('const appConfig = ')) {
s.append(`${options.globalPublicPath} = buildAssetsURL();\n`)
}
const assetId = code.match(VITE_ASSET_RE)
if (assetId) {
s.overwrite(0, code.length,
[
'import { buildAssetsURL } from \'#build/paths.mjs\';',
`export default buildAssetsURL("${assetId[1]}".replace("/__NUXT_BASE__", ""));`
].join('\n')
)
}
if (!id.includes('paths.mjs') && code.includes('NUXT_BASE') && !code.includes('import { publicAssetsURL as __publicAssetsURL }')) {
s.prepend('import { publicAssetsURL as __publicAssetsURL } from \'#build/paths.mjs\';\n')
}
if (id === 'vite/preload-helper') {
// Define vite base path as buildAssetsUrl (i.e. including _nuxt/)
s.prepend('import { buildAssetsURL } from \'#build/paths.mjs\';\n')
s.replace(/const base = ['"]\/__NUXT_BASE__\/['"]/, 'const base = buildAssetsURL()')
}
// Sanitize imports
s.replace(/from *['"]\/__NUXT_BASE__(\/[^'"]*)['"]/g, 'from "$1"')
// Dynamically compute string URLs featuring baseURL
const delimiterRE = /(?<!(const base = |from *))((?<!\\)`([^`]*)\/__NUXT_BASE__\/([^`]*)(?<!\\)`|(?<!\\)'([^\n']*)\/__NUXT_BASE__\/([^\n']*)(?<!\\)'|(?<!\\)"([^\n"]*)\/__NUXT_BASE__\/([^\n"]*)(?<!\\)")/g
/* eslint-disable-next-line no-template-curly-in-string */
s.replace(delimiterRE, r => '`' + r.replace(/\/__NUXT_BASE__\//g, '${__publicAssetsURL()}').slice(1, -1) + '`')
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap && s.generateMap({ source: id, includeContent: true })
}
}
}
}
})

View File

@ -1,23 +1,40 @@
import { resolve, join, normalize } from 'pathe' import { resolve, normalize } 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, isIgnored } from '@nuxt/kit'
import fse from 'fs-extra' import fse from 'fs-extra'
import { debounce } from 'perfect-debounce' import { debounce } from 'perfect-debounce'
import { withoutTrailingSlash } 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 { prepareDevServerEntry } from './vite-node'
import { isCSS, isDirectory, readDirRecursively } from './utils' import { isCSS } from './utils'
import { bundleRequest } from './dev-bundler' import { bundleRequest } from './dev-bundler'
import { writeManifest } from './manifest' import { writeManifest } from './manifest'
import { RelativeAssetPlugin } from './plugins/dynamic-base'
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 => 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
? joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir)
: undefined,
experimental: {
renderBuiltUrl: (filename, { type, hostType }) => {
if (hostType !== 'js') {
// In CSS we only use relative paths until we craft a clever runtime CSS hack
return { relative: true }
}
if (type === 'public') {
return { runtime: `globalThis.__publicAssetsURL(${JSON.stringify(filename)})` }
}
if (type === 'asset') {
const relativeFilename = filename.replace(withTrailingSlash(withoutLeadingSlash(ctx.nuxt.options.app.buildAssetsDir)), '')
return { runtime: `globalThis.__buildAssetsURL(${JSON.stringify(relativeFilename)})` }
}
}
},
define: { define: {
'process.server': true, 'process.server': true,
'process.client': false, 'process.client': false,
@ -30,21 +47,22 @@ export async function buildServer (ctx: ViteBuildContext) {
resolve: { resolve: {
alias: { alias: {
'#build/plugins': resolve(ctx.nuxt.options.buildDir, 'plugins/server'), '#build/plugins': resolve(ctx.nuxt.options.buildDir, 'plugins/server'),
// Alias vue to ensure we're using the same context in development ...ctx.nuxt.options.experimental.externalVue || ctx.nuxt.options.dev
'vue/server-renderer': _resolve('vue/server-renderer'),
'vue/compiler-sfc': _resolve('vue/compiler-sfc'),
...ctx.nuxt.options.experimental.externalVue
? {} ? {}
: { : {
'@vue/reactivity': _resolve(`@vue/reactivity/dist/reactivity.cjs${ctx.nuxt.options.dev ? '' : '.prod'}.js`), '@vue/reactivity': _resolve(`@vue/reactivity/dist/reactivity.cjs${ctx.nuxt.options.dev ? '' : '.prod'}.js`),
'@vue/shared': _resolve(`@vue/shared/dist/shared.cjs${ctx.nuxt.options.dev ? '' : '.prod'}.js`), '@vue/shared': _resolve(`@vue/shared/dist/shared.cjs${ctx.nuxt.options.dev ? '' : '.prod'}.js`),
'vue-router': _resolve(`vue-router/dist/vue-router.cjs${ctx.nuxt.options.dev ? '' : '.prod'}.js`) 'vue-router': _resolve(`vue-router/dist/vue-router.cjs${ctx.nuxt.options.dev ? '' : '.prod'}.js`),
}, 'vue/server-renderer': _resolve('vue/server-renderer'),
'vue/compiler-sfc': _resolve('vue/compiler-sfc'),
vue: _resolve(`vue/dist/vue.cjs${ctx.nuxt.options.dev ? '' : '.prod'}.js`) vue: _resolve(`vue/dist/vue.cjs${ctx.nuxt.options.dev ? '' : '.prod'}.js`)
} }
}
}, },
ssr: { ssr: {
external: ctx.nuxt.options.experimental.externalVue ? ['#internal/nitro', 'vue', 'vue-router'] : ['#internal/nitro'], external: ctx.nuxt.options.experimental.externalVue
? ['#internal/nitro', '#internal/nitro/utils', 'vue', 'vue-router']
: ['#internal/nitro', '#internal/nitro/utils'],
noExternal: [ noExternal: [
...ctx.nuxt.options.build.transpile, ...ctx.nuxt.options.build.transpile,
// TODO: Use externality for production (rollup) build // TODO: Use externality for production (rollup) build
@ -81,7 +99,6 @@ export async function buildServer (ctx: ViteBuildContext) {
}, },
plugins: [ plugins: [
cacheDirPlugin(ctx.nuxt.options.rootDir, 'server'), cacheDirPlugin(ctx.nuxt.options.rootDir, 'server'),
RelativeAssetPlugin(),
vuePlugin(ctx.config.vue), vuePlugin(ctx.config.vue),
viteJsxPlugin() viteJsxPlugin()
] ]
@ -95,37 +112,6 @@ export async function buildServer (ctx: ViteBuildContext) {
await ctx.nuxt.callHook('vite:extendConfig', serverConfig, { isClient: false, isServer: true }) await ctx.nuxt.callHook('vite:extendConfig', serverConfig, { isClient: false, isServer: true })
ctx.nuxt.hook('nitro:build:before', async () => {
if (ctx.nuxt.options.dev) {
return
}
const clientDist = resolve(ctx.nuxt.options.buildDir, 'dist/client')
// Remove public files that have been duplicated into buildAssetsDir
// TODO: Add option to configure this behavior in vite
const publicDir = join(ctx.nuxt.options.srcDir, ctx.nuxt.options.dir.public)
let publicFiles: string[] = []
if (await isDirectory(publicDir)) {
publicFiles = readDirRecursively(publicDir).map(r => r.replace(publicDir, ''))
for (const file of publicFiles) {
try {
fse.rmSync(join(clientDist, file))
} catch {}
}
}
// Copy doubly-nested /_nuxt/_nuxt files into buildAssetsDir
// TODO: Workaround vite issue
if (await isDirectory(clientDist)) {
const nestedAssetsPath = withoutTrailingSlash(join(clientDist, ctx.nuxt.options.app.buildAssetsDir))
if (await isDirectory(nestedAssetsPath)) {
await fse.copy(nestedAssetsPath, clientDist, { recursive: true })
await fse.remove(nestedAssetsPath)
}
}
})
const onBuild = () => ctx.nuxt.callHook('build:resources', wpfs) const onBuild = () => ctx.nuxt.callHook('build:resources', wpfs)
// Production build // Production build

View File

@ -6,11 +6,9 @@ import { logger, isIgnored } from '@nuxt/kit'
import type { Options } from '@vitejs/plugin-vue' import type { Options } from '@vitejs/plugin-vue'
import replace from '@rollup/plugin-replace' import replace from '@rollup/plugin-replace'
import { sanitizeFilePath } from 'mlly' import { sanitizeFilePath } from 'mlly'
import { getPort } from 'get-port-please'
import { buildClient } from './client' import { buildClient } from './client'
import { buildServer } from './server' import { buildServer } from './server'
import virtual from './plugins/virtual' import virtual from './plugins/virtual'
import { DynamicBasePlugin } from './plugins/dynamic-base'
import { warmupViteServer } from './utils/warmup' import { warmupViteServer } from './utils/warmup'
import { resolveCSSOptions } from './css' import { resolveCSSOptions } from './css'
import { composableKeysPlugin } from './plugins/composable-keys' import { composableKeysPlugin } from './plugins/composable-keys'
@ -28,12 +26,6 @@ export interface ViteBuildContext {
} }
export async function bundle (nuxt: Nuxt) { export async function bundle (nuxt: Nuxt) {
const hmrPortDefault = 24678 // Vite's default HMR port
const hmrPort = await getPort({
port: hmrPortDefault,
ports: Array.from({ length: 20 }, (_, i) => hmrPortDefault + 1 + i)
})
const ctx: ViteBuildContext = { const ctx: ViteBuildContext = {
nuxt, nuxt,
config: vite.mergeConfig( config: vite.mergeConfig(
@ -74,20 +66,13 @@ export async function bundle (nuxt: Nuxt) {
...Object.fromEntries([';', '(', '{', '}', ' ', '\t', '\n'].map(d => [`${d}global.`, `${d}globalThis.`])), ...Object.fromEntries([';', '(', '{', '}', ' ', '\t', '\n'].map(d => [`${d}global.`, `${d}globalThis.`])),
preventAssignment: true preventAssignment: true
}), }),
virtual(nuxt.vfs), virtual(nuxt.vfs)
DynamicBasePlugin.vite({ sourcemap: nuxt.options.sourcemap })
], ],
vue: { vue: {
reactivityTransform: nuxt.options.experimental.reactivityTransform reactivityTransform: nuxt.options.experimental.reactivityTransform
}, },
server: { server: {
watch: { ignored: isIgnored }, watch: { ignored: isIgnored },
hmr: {
// https://github.com/nuxt/framework/issues/4191
protocol: 'ws',
clientPort: hmrPort,
port: hmrPort
},
fs: { fs: {
allow: [ allow: [
nuxt.options.appDir nuxt.options.appDir
@ -102,7 +87,6 @@ export async function bundle (nuxt: Nuxt) {
// In build mode we explicitly override any vite options that vite is relying on // In build mode we explicitly override any vite options that vite is relying on
// to detect whether to inject production or development code (such as HMR code) // to detect whether to inject production or development code (such as HMR code)
if (!nuxt.options.dev) { if (!nuxt.options.dev) {
ctx.config.server.hmr = false
ctx.config.server.watch = undefined ctx.config.server.watch = undefined
ctx.config.build.watch = undefined ctx.config.build.watch = undefined
} }

View File

@ -0,0 +1,31 @@
import { createUnplugin } from 'unplugin'
import MagicString from 'magic-string'
interface DynamicBasePluginOptions {
globalPublicPath?: string
sourcemap?: boolean
}
const defaults: DynamicBasePluginOptions = {
globalPublicPath: '__webpack_public_path__',
sourcemap: true
}
export const DynamicBasePlugin = createUnplugin((options: DynamicBasePluginOptions = {}) => {
options = { ...defaults, ...options }
return {
name: 'nuxt:dynamic-base-path',
enforce: 'post',
transform (code, id) {
if (!id.includes('paths.mjs') || !code.includes('const appConfig = ')) {
return
}
const s = new MagicString(code)
s.append(`${options.globalPublicPath} = buildAssetsURL();\n`)
return {
code: s.toString(),
map: options.sourcemap && s.generateMap({ source: id, includeContent: true })
}
}
}
})

View File

@ -196,7 +196,7 @@ function getOutput (ctx: WebpackConfigContext): webpack.Configuration['output']
const { options } = ctx const { options } = ctx
return { return {
path: resolve(options.buildDir, 'dist', ctx.isServer ? 'server' : 'client'), path: resolve(options.buildDir, 'dist', ctx.isServer ? 'server' : joinURL('client', options.app.buildAssetsDir)),
filename: fileName(ctx, 'app'), filename: fileName(ctx, 'app'),
chunkFilename: fileName(ctx, 'chunk'), chunkFilename: fileName(ctx, 'chunk'),
publicPath: joinURL(options.app.baseURL, options.app.buildAssetsDir) publicPath: joinURL(options.app.baseURL, options.app.buildAssetsDir)

View File

@ -8,8 +8,8 @@ import type { Compiler, Watching } from 'webpack'
import type { Nuxt } from '@nuxt/schema' import type { Nuxt } from '@nuxt/schema'
import { joinURL } from 'ufo' import { joinURL } from 'ufo'
import { logger, useNuxt } from '@nuxt/kit' import { logger, useNuxt } from '@nuxt/kit'
import { DynamicBasePlugin } from '../../vite/src/plugins/dynamic-base'
import { composableKeysPlugin } from '../../vite/src/plugins/composable-keys' import { composableKeysPlugin } from '../../vite/src/plugins/composable-keys'
import { DynamicBasePlugin } from './plugins/dynamic-base'
import { createMFS } from './utils/mfs' import { createMFS } from './utils/mfs'
import { registerVirtualModules } from './virtual-modules' import { registerVirtualModules } from './virtual-modules'
import { client, server } from './configs' import { client, server } from './configs'
@ -35,8 +35,7 @@ export async function bundle (nuxt: Nuxt) {
// Configure compilers // Configure compilers
const compilers = webpackConfigs.map((config) => { const compilers = webpackConfigs.map((config) => {
config.plugins.push(DynamicBasePlugin.webpack({ config.plugins.push(DynamicBasePlugin.webpack({
sourcemap: nuxt.options.sourcemap, sourcemap: nuxt.options.sourcemap
globalPublicPath: '__webpack_public_path__'
})) }))
config.plugins.push(composableKeysPlugin.webpack({ config.plugins.push(composableKeysPlugin.webpack({
sourcemap: nuxt.options.sourcemap, sourcemap: nuxt.options.sourcemap,

View File

@ -3,9 +3,11 @@ import { defineConfig } from 'vite'
import { isWindows } from 'std-env' import { isWindows } from 'std-env'
export default defineConfig({ export default defineConfig({
resolve: {
alias: { alias: {
'#app': resolve('./packages/nuxt/src/app/index.ts'), '#app': resolve('./packages/nuxt/src/app/index.ts'),
'@nuxt/test-utils': resolve('./packages/test-utils/src/index.ts') '@nuxt/test-utils': resolve('./packages/test-utils/src/index.ts')
}
}, },
esbuild: { esbuild: {
tsconfigRaw: '{}' tsconfigRaw: '{}'

View File

@ -48,7 +48,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/core@npm:^7.17.7, @babel/core@npm:^7.17.9": "@babel/core@npm:^7.17.7, @babel/core@npm:^7.18.6":
version: 7.18.6 version: 7.18.6
resolution: "@babel/core@npm:7.18.6" resolution: "@babel/core@npm:7.18.6"
dependencies: dependencies:
@ -408,7 +408,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/plugin-transform-typescript@npm:^7.16.8": "@babel/plugin-transform-typescript@npm:^7.18.8":
version: 7.18.8 version: 7.18.8
resolution: "@babel/plugin-transform-typescript@npm:7.18.8" resolution: "@babel/plugin-transform-typescript@npm:7.18.8"
dependencies: dependencies:
@ -1805,7 +1805,7 @@ __metadata:
ufo: ^0.8.5 ufo: ^0.8.5
unbuild: latest unbuild: latest
unimport: ^0.4.5 unimport: ^0.4.5
vite: ^2.9.14 vite: ~3.0.0
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@ -1896,8 +1896,8 @@ __metadata:
"@nuxt/schema": ^3.0.0-rc.6 "@nuxt/schema": ^3.0.0-rc.6
"@rollup/plugin-replace": ^4.0.0 "@rollup/plugin-replace": ^4.0.0
"@types/cssnano": ^5 "@types/cssnano": ^5
"@vitejs/plugin-vue": ^2.3.3 "@vitejs/plugin-vue": ^3.0.0
"@vitejs/plugin-vue-jsx": ^1.3.10 "@vitejs/plugin-vue-jsx": ^2.0.0
autoprefixer: ^10.4.7 autoprefixer: ^10.4.7
chokidar: ^3.5.3 chokidar: ^3.5.3
cssnano: ^5.1.12 cssnano: ^5.1.12
@ -1923,7 +1923,7 @@ __metadata:
ufo: ^0.8.5 ufo: ^0.8.5
unbuild: latest unbuild: latest
unplugin: ^0.7.2 unplugin: ^0.7.2
vite: ^2.9.14 vite: ~3.0.0
vite-node: ^0.18.1 vite-node: ^0.18.1
vite-plugin-checker: ^0.4.9 vite-plugin-checker: ^0.4.9
vue: 3.2.37 vue: 3.2.37
@ -2277,7 +2277,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/pluginutils@npm:^4.1.1, @rollup/pluginutils@npm:^4.2.0, @rollup/pluginutils@npm:^4.2.1": "@rollup/pluginutils@npm:^4.1.1, @rollup/pluginutils@npm:^4.2.1":
version: 4.2.1 version: 4.2.1
resolution: "@rollup/pluginutils@npm:4.2.1" resolution: "@rollup/pluginutils@npm:4.2.1"
dependencies: dependencies:
@ -3014,27 +3014,28 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@vitejs/plugin-vue-jsx@npm:^1.3.10": "@vitejs/plugin-vue-jsx@npm:^2.0.0":
version: 1.3.10 version: 2.0.0
resolution: "@vitejs/plugin-vue-jsx@npm:1.3.10" resolution: "@vitejs/plugin-vue-jsx@npm:2.0.0"
dependencies: dependencies:
"@babel/core": ^7.17.9 "@babel/core": ^7.18.6
"@babel/plugin-syntax-import-meta": ^7.10.4 "@babel/plugin-syntax-import-meta": ^7.10.4
"@babel/plugin-transform-typescript": ^7.16.8 "@babel/plugin-transform-typescript": ^7.18.8
"@rollup/pluginutils": ^4.2.0
"@vue/babel-plugin-jsx": ^1.1.1 "@vue/babel-plugin-jsx": ^1.1.1
hash-sum: ^2.0.0 peerDependencies:
checksum: 868e90363e1234be43c3ced5097c8da430b9feed526c1f6c2dcc8a0546182e942018e6b5e1a0c16c66810b82cd17fa3a8e04ba969db6f253c6f7c490dce83a0b vite: ^3.0.0
vue: ^3.0.0
checksum: 197b0dd0263b5b7df22a055155da6d9b7f21098f7de43e94589ce8daa494c12c9850157bc4e228a766fbe602aafa5935e4bef0edec9158a0507cee5581f0a067
languageName: node languageName: node
linkType: hard linkType: hard
"@vitejs/plugin-vue@npm:^2.3.3": "@vitejs/plugin-vue@npm:^3.0.0":
version: 2.3.3 version: 3.0.0
resolution: "@vitejs/plugin-vue@npm:2.3.3" resolution: "@vitejs/plugin-vue@npm:3.0.0"
peerDependencies: peerDependencies:
vite: ^2.5.10 vite: ^3.0.0
vue: ^3.2.25 vue: ^3.2.25
checksum: 9303dcb9c8580d0ee9b33542639ac1a36ad9cc0e773a1f9b9b05623d74574f6a901ce781918b53f5a58eb3c6218ba96c27ef6efbf3e7ef6be16864fc1cae1626 checksum: e2c3e31afc39f83b16174ef4742e88e07b5516688f02dcbe9814afd62c859925173ec2d6a9f088c6098eb45cdae926122308596a8ece90028ffea169f15c685f
languageName: node languageName: node
linkType: hard linkType: hard
@ -5708,7 +5709,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"esbuild@npm:^0.14.27, esbuild@npm:^0.14.39, esbuild@npm:^0.14.47, esbuild@npm:^0.14.49": "esbuild@npm:^0.14.39, esbuild@npm:^0.14.47, esbuild@npm:^0.14.49":
version: 0.14.49 version: 0.14.49
resolution: "esbuild@npm:0.14.49" resolution: "esbuild@npm:0.14.49"
dependencies: dependencies:
@ -11353,7 +11354,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.10.1, resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.0": "resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.10.1, resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.0, resolve@npm:^1.22.1":
version: 1.22.1 version: 1.22.1
resolution: "resolve@npm:1.22.1" resolution: "resolve@npm:1.22.1"
dependencies: dependencies:
@ -11366,7 +11367,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"resolve@patch:resolve@^1.1.7#~builtin<compat/resolve>, resolve@patch:resolve@^1.10.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.10.1#~builtin<compat/resolve>, resolve@patch:resolve@^1.17.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.19.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.20.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.22.0#~builtin<compat/resolve>": "resolve@patch:resolve@^1.1.7#~builtin<compat/resolve>, resolve@patch:resolve@^1.10.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.10.1#~builtin<compat/resolve>, resolve@patch:resolve@^1.17.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.19.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.20.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.22.0#~builtin<compat/resolve>, resolve@patch:resolve@^1.22.1#~builtin<compat/resolve>":
version: 1.22.1 version: 1.22.1
resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin<compat/resolve>::version=1.22.1&hash=07638b" resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin<compat/resolve>::version=1.22.1&hash=07638b"
dependencies: dependencies:
@ -11512,7 +11513,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"rollup@npm:^2.59.0, rollup@npm:^2.76.0": "rollup@npm:^2.75.6, rollup@npm:^2.76.0":
version: 2.76.0 version: 2.76.0
resolution: "rollup@npm:2.76.0" resolution: "rollup@npm:2.76.0"
dependencies: dependencies:
@ -13217,19 +13218,20 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"vite@npm:^2.9.14": "vite@npm:~3.0.0":
version: 2.9.14 version: 3.0.0
resolution: "vite@npm:2.9.14" resolution: "vite@npm:3.0.0"
dependencies: dependencies:
esbuild: ^0.14.27 esbuild: ^0.14.47
fsevents: ~2.3.2 fsevents: ~2.3.2
postcss: ^8.4.13 postcss: ^8.4.14
resolve: ^1.22.0 resolve: ^1.22.1
rollup: ^2.59.0 rollup: ^2.75.6
peerDependencies: peerDependencies:
less: "*" less: "*"
sass: "*" sass: "*"
stylus: "*" stylus: "*"
terser: ^5.4.0
dependenciesMeta: dependenciesMeta:
fsevents: fsevents:
optional: true optional: true
@ -13240,9 +13242,11 @@ __metadata:
optional: true optional: true
stylus: stylus:
optional: true optional: true
terser:
optional: true
bin: bin:
vite: bin/vite.js vite: bin/vite.js
checksum: f78b54f58482ea97d385e36873ae1aa4744c5e467c1d6d4e0835bd55494d2d8f6ce763f17c241c66104be687d5ee535b8e1e96c14210c9ba0c343fe78c58f694 checksum: 4920b5b0a4d4bd4a003121b2eb6ed41ac2ab69e6ab055db645e678c14e68d6eef780362d4d482cf439b576c37fb65dc9a7ebbbf90354e7ae362034a28eac9130
languageName: node languageName: node
linkType: hard linkType: hard