diff --git a/package.json b/package.json index 3c859e63d60..4ccf9ff6d94 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "nuxi": "link:./packages/nuxi", "nuxt": "link:./packages/nuxt", "nuxt3": "link:./packages/nuxt", - "vite": "^2.9.14", + "vite": "~3.0.0", "unbuild": "^0.7.6" }, "devDependencies": { diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index acda7f7fdc3..6e3488bf52a 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -31,8 +31,8 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href)) const query = parseQuery(search) // we only transform render functions - // from `type=template` (in Webpack) and bare `.vue` file (in Vite) - return pathname.endsWith('.vue') && (query.type === 'template' || !!query.macro || !search) + // from `type=template` (in Webpack), bare `.vue` file and setup=true (Vite 2/3) + return pathname.endsWith('.vue') && (query.type === 'template' || !!query.macro || !!query.setup || !search) }, transform (code, id) { const components = options.getComponents() diff --git a/packages/nuxt/src/core/nitro.ts b/packages/nuxt/src/core/nitro.ts index f6f2759ea55..086fd8ee1da 100644 --- a/packages/nuxt/src/core/nitro.ts +++ b/packages/nuxt/src/core/nitro.ts @@ -41,10 +41,7 @@ export async function initNitro (nuxt: Nuxt) { generateTsConfig: false }, publicAssets: [ - { - baseURL: nuxt.options.app.buildAssetsDir, - dir: resolve(nuxt.options.buildDir, 'dist/client') - }, + { dir: resolve(nuxt.options.buildDir, 'dist/client') }, ...nuxt.options._layers .map(layer => join(layer.config.srcDir, layer.config.dir?.public || 'public')) .filter(dir => existsSync(dir)) diff --git a/packages/nuxt/src/core/templates.ts b/packages/nuxt/src/core/templates.ts index 81a5e95b4e7..061e9572a33 100644 --- a/packages/nuxt/src/core/templates.ts +++ b/packages/nuxt/src/core/templates.ts @@ -211,7 +211,10 @@ export const publicPathTemplate: NuxtTemplate = { 'export const publicAssetsURL = (...path) => {', ' const publicBase = appConfig.cdnURL || appConfig.baseURL', ' return path.length ? joinURL(publicBase, ...path) : publicBase', - '}' + '}', + + 'globalThis.__buildAssetsURL = buildAssetsURL', + 'globalThis.__publicAssetsURL = publicAssetsURL' ].filter(Boolean).join('\n') } } diff --git a/packages/nuxt/src/pages/macros.ts b/packages/nuxt/src/pages/macros.ts index 965a54b00b3..2ddc9197695 100644 --- a/packages/nuxt/src/pages/macros.ts +++ b/packages/nuxt/src/pages/macros.ts @@ -3,6 +3,7 @@ import { createUnplugin } from 'unplugin' import { parseQuery, parseURL, withQuery } from 'ufo' import { findStaticImports, findExports } from 'mlly' import MagicString from 'magic-string' +import { isAbsolute } from 'pathe' export interface TransformMacroPluginOptions { macros: Record @@ -48,7 +49,8 @@ export const TransformMacroPlugin = createUnplugin((options: TransformMacroPlugi if (scriptImport) { // https://github.com/vuejs/vue-loader/pull/1911 // 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) }) s.overwrite(0, code.length, `export { meta } from "${specifier}"`) return result() diff --git a/packages/schema/package.json b/packages/schema/package.json index 212c4af6888..4c4ef551070 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -17,7 +17,7 @@ "@types/lodash.template": "^4", "@types/semver": "^7", "unbuild": "latest", - "vite": "^2.9.14" + "vite": "~3.0.0" }, "dependencies": { "c12": "^0.2.8", diff --git a/packages/schema/src/config/vite.ts b/packages/schema/src/config/vite.ts index 9351d79c3ca..232197f4ba5 100644 --- a/packages/schema/src/config/vite.ts +++ b/packages/schema/src/config/vite.ts @@ -1,5 +1,5 @@ import { resolve } from 'pathe' -import { joinURL, withoutLeadingSlash } from 'ufo' +import { withoutLeadingSlash } from 'ufo' export default { /** @@ -28,11 +28,6 @@ export default { resolve: { 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: { $resolve: (val, get) => val ?? resolve(get('srcDir'), get('dir').public), }, @@ -61,7 +56,7 @@ export default { clearScreen: false, build: { assetsDir: { - $resolve: (val, get) => val ?? get('dev') ? withoutLeadingSlash(get('app').buildAssetsDir) : '.', + $resolve: (val, get) => val ?? withoutLeadingSlash(get('app').buildAssetsDir), }, emptyOutDir: false, }, diff --git a/packages/vite/package.json b/packages/vite/package.json index 9a71c0c2e71..850d442c002 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -21,8 +21,8 @@ "dependencies": { "@nuxt/kit": "^3.0.0-rc.6", "@rollup/plugin-replace": "^4.0.0", - "@vitejs/plugin-vue": "^2.3.3", - "@vitejs/plugin-vue-jsx": "^1.3.10", + "@vitejs/plugin-vue": "^3.0.0", + "@vitejs/plugin-vue-jsx": "^2.0.0", "autoprefixer": "^10.4.7", "chokidar": "^3.5.3", "cssnano": "^5.1.12", @@ -47,7 +47,7 @@ "rollup-plugin-visualizer": "^5.7.1", "ufo": "^0.8.5", "unplugin": "^0.7.2", - "vite": "^2.9.14", + "vite": "~3.0.0", "vite-node": "^0.18.1", "vite-plugin-checker": "^0.4.9" }, diff --git a/packages/vite/src/client.ts b/packages/vite/src/client.ts index 6434bd463fd..3f38ba83a0e 100644 --- a/packages/vite/src/client.ts +++ b/packages/vite/src/client.ts @@ -1,21 +1,36 @@ -import { resolve } from 'pathe' +import { join, resolve } from 'pathe' import * as vite from 'vite' import vuePlugin from '@vitejs/plugin-vue' import viteJsxPlugin from '@vitejs/plugin-vue-jsx' import type { Connect } from 'vite' 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 { analyzePlugin } from './plugins/analyze' import { wpfs } from './utils/wpfs' import type { ViteBuildContext, ViteOptions } from './vite' import { writeManifest } from './manifest' import { devStyleSSRPlugin } from './plugins/dev-ssr-css' -import { RelativeAssetPlugin } from './plugins/dynamic-base' import { viteNodePlugin } from './vite-node' 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, { + 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: { 'process.server': false, 'process.client': true, @@ -30,8 +45,10 @@ export async function buildClient (ctx: ViteBuildContext) { build: { rollupOptions: { output: { - chunkFileNames: ctx.nuxt.options.dev ? undefined : '[name]-[hash].mjs', - entryFileNames: ctx.nuxt.options.dev ? 'entry.mjs' : '[name]-[hash].mjs' + // https://github.com/vitejs/vite/tree/main/packages/vite/src/node/build.ts#L464-L478 + 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, @@ -41,18 +58,30 @@ export async function buildClient (ctx: ViteBuildContext) { cacheDirPlugin(ctx.nuxt.options.rootDir, 'client'), vuePlugin(ctx.config.vue), viteJsxPlugin(), - RelativeAssetPlugin(), devStyleSSRPlugin({ rootDir: ctx.nuxt.options.rootDir, buildAssetsURL: joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir) }), viteNodePlugin(ctx) ], + appType: 'custom', server: { + hmr: { + // https://github.com/nuxt/framework/issues/4191 + protocol: 'ws', + clientPort: hmrPort, + port: hmrPort + }, middlewareMode: true } } 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 if (ctx.nuxt.options.build.analyze) { clientConfig.plugins.push(...analyzePlugin(ctx)) @@ -65,10 +94,11 @@ export async function buildClient (ctx: ViteBuildContext) { const viteServer = await vite.createServer(clientConfig) ctx.clientServer = viteServer 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) => { // Workaround: vite devmiddleware modifies req.url const originalURL = req.url + req.url = req.url.replace(BASE_RE, '/') viteServer.middlewares.handle(req, res, (err) => { req.url = originalURL next(err) diff --git a/packages/vite/src/dev-bundler.ts b/packages/vite/src/dev-bundler.ts index 5d31b8e5b7d..9d841558a1f 100644 --- a/packages/vite/src/dev-bundler.ts +++ b/packages/vite/src/dev-bundler.ts @@ -1,7 +1,7 @@ import { pathToFileURL } from 'node:url' import { existsSync } from 'node:fs' import { builtinModules } from 'node:module' -import { resolve } from 'pathe' +import { isAbsolute, resolve } from 'pathe' import * as vite from 'vite' import { ExternalsOptions, isExternal as _isExternal, ExternalsDefaults } from 'externality' import { genDynamicImport, genObjectFromRawEntries } from 'knitwork' @@ -77,7 +77,7 @@ async function transformRequest (opts: TransformOptions, id: string) { if (await isExternal(opts, withoutVersionQuery)) { const path = builtinModules.includes(withoutVersionQuery.split('node:').pop()) ? withoutVersionQuery - : pathToFileURL(withoutVersionQuery).href + : isAbsolute(withoutVersionQuery) ? pathToFileURL(withoutVersionQuery).href : withoutVersionQuery return { code: `(global, module, _, exports, importMeta, ssrImport, ssrDynamicImport, ssrExportAll) => ${genDynamicImport(path, { wrapper: false })} @@ -140,11 +140,11 @@ export async function bundleRequest (opts: TransformOptions, entryURL: string) { // Parents: \n${listIds(chunk.parents)} // Dependencies: \n${listIds(chunk.deps)} // -------------------- -const ${hashId(chunk.id)} = ${chunk.code} +const ${hashId(chunk.id + '-' + chunk.code)} = ${chunk.code} `).join('\n') 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 diff --git a/packages/vite/src/manifest.ts b/packages/vite/src/manifest.ts index 123922032cb..fdd705e410f 100644 --- a/packages/vite/src/manifest.ts +++ b/packages/vite/src/manifest.ts @@ -1,6 +1,7 @@ import fse from 'fs-extra' 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' export async function writeManifest (ctx: ViteBuildContext, extraEntries: string[] = []) { @@ -27,6 +28,18 @@ export async function writeManifest (ctx: ViteBuildContext, extraEntries: string ? devClientManifest : 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.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') diff --git a/packages/vite/src/plugins/dynamic-base.ts b/packages/vite/src/plugins/dynamic-base.ts deleted file mode 100644 index e69d9014c7b..00000000000 --- a/packages/vite/src/plugins/dynamic-base.ts +++ /dev/null @@ -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 = /(? '`' + 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 }) - } - } - } - } -}) diff --git a/packages/vite/src/server.ts b/packages/vite/src/server.ts index 13833bd5c9e..0e76e49aef5 100644 --- a/packages/vite/src/server.ts +++ b/packages/vite/src/server.ts @@ -1,23 +1,40 @@ -import { resolve, join, normalize } from 'pathe' +import { resolve, normalize } from 'pathe' import * as vite from 'vite' import vuePlugin from '@vitejs/plugin-vue' import viteJsxPlugin from '@vitejs/plugin-vue-jsx' import { logger, resolveModule, isIgnored } from '@nuxt/kit' import fse from 'fs-extra' import { debounce } from 'perfect-debounce' -import { withoutTrailingSlash } from 'ufo' +import { joinURL, withoutLeadingSlash, withTrailingSlash } from 'ufo' import { ViteBuildContext, ViteOptions } from './vite' import { wpfs } from './utils/wpfs' import { cacheDirPlugin } from './plugins/cache-dir' import { prepareDevServerEntry } from './vite-node' -import { isCSS, isDirectory, readDirRecursively } from './utils' +import { isCSS } from './utils' import { bundleRequest } from './dev-bundler' import { writeManifest } from './manifest' -import { RelativeAssetPlugin } from './plugins/dynamic-base' export async function buildServer (ctx: ViteBuildContext) { const _resolve = id => resolveModule(id, { paths: ctx.nuxt.options.modulesDir }) 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: { 'process.server': true, 'process.client': false, @@ -30,21 +47,22 @@ export async function buildServer (ctx: ViteBuildContext) { resolve: { alias: { '#build/plugins': resolve(ctx.nuxt.options.buildDir, 'plugins/server'), - // Alias vue to ensure we're using the same context in development - 'vue/server-renderer': _resolve('vue/server-renderer'), - 'vue/compiler-sfc': _resolve('vue/compiler-sfc'), - ...ctx.nuxt.options.experimental.externalVue + ...ctx.nuxt.options.experimental.externalVue || ctx.nuxt.options.dev ? {} : { '@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-router': _resolve(`vue-router/dist/vue-router.cjs${ctx.nuxt.options.dev ? '' : '.prod'}.js`) - }, - vue: _resolve(`vue/dist/vue.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`) + } } }, 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: [ ...ctx.nuxt.options.build.transpile, // TODO: Use externality for production (rollup) build @@ -81,7 +99,6 @@ export async function buildServer (ctx: ViteBuildContext) { }, plugins: [ cacheDirPlugin(ctx.nuxt.options.rootDir, 'server'), - RelativeAssetPlugin(), vuePlugin(ctx.config.vue), viteJsxPlugin() ] @@ -95,37 +112,6 @@ export async function buildServer (ctx: ViteBuildContext) { 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) // Production build diff --git a/packages/vite/src/vite.ts b/packages/vite/src/vite.ts index 60e76c8e1ae..1d97060815f 100644 --- a/packages/vite/src/vite.ts +++ b/packages/vite/src/vite.ts @@ -6,11 +6,9 @@ import { logger, isIgnored } from '@nuxt/kit' import type { Options } from '@vitejs/plugin-vue' import replace from '@rollup/plugin-replace' import { sanitizeFilePath } from 'mlly' -import { getPort } from 'get-port-please' import { buildClient } from './client' import { buildServer } from './server' import virtual from './plugins/virtual' -import { DynamicBasePlugin } from './plugins/dynamic-base' import { warmupViteServer } from './utils/warmup' import { resolveCSSOptions } from './css' import { composableKeysPlugin } from './plugins/composable-keys' @@ -28,12 +26,6 @@ export interface ViteBuildContext { } 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 = { nuxt, config: vite.mergeConfig( @@ -74,20 +66,13 @@ export async function bundle (nuxt: Nuxt) { ...Object.fromEntries([';', '(', '{', '}', ' ', '\t', '\n'].map(d => [`${d}global.`, `${d}globalThis.`])), preventAssignment: true }), - virtual(nuxt.vfs), - DynamicBasePlugin.vite({ sourcemap: nuxt.options.sourcemap }) + virtual(nuxt.vfs) ], vue: { reactivityTransform: nuxt.options.experimental.reactivityTransform }, server: { watch: { ignored: isIgnored }, - hmr: { - // https://github.com/nuxt/framework/issues/4191 - protocol: 'ws', - clientPort: hmrPort, - port: hmrPort - }, fs: { allow: [ 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 // to detect whether to inject production or development code (such as HMR code) if (!nuxt.options.dev) { - ctx.config.server.hmr = false ctx.config.server.watch = undefined ctx.config.build.watch = undefined } diff --git a/packages/webpack/src/plugins/dynamic-base.ts b/packages/webpack/src/plugins/dynamic-base.ts new file mode 100644 index 00000000000..8fd6ccf9c1b --- /dev/null +++ b/packages/webpack/src/plugins/dynamic-base.ts @@ -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 }) + } + } + } +}) diff --git a/packages/webpack/src/presets/base.ts b/packages/webpack/src/presets/base.ts index 05396551402..171eeeb3bcf 100644 --- a/packages/webpack/src/presets/base.ts +++ b/packages/webpack/src/presets/base.ts @@ -196,7 +196,7 @@ function getOutput (ctx: WebpackConfigContext): webpack.Configuration['output'] const { options } = ctx 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'), chunkFilename: fileName(ctx, 'chunk'), publicPath: joinURL(options.app.baseURL, options.app.buildAssetsDir) diff --git a/packages/webpack/src/webpack.ts b/packages/webpack/src/webpack.ts index 4696a289012..0952f56d447 100644 --- a/packages/webpack/src/webpack.ts +++ b/packages/webpack/src/webpack.ts @@ -8,8 +8,8 @@ import type { Compiler, Watching } from 'webpack' import type { Nuxt } from '@nuxt/schema' import { joinURL } from 'ufo' import { logger, useNuxt } from '@nuxt/kit' -import { DynamicBasePlugin } from '../../vite/src/plugins/dynamic-base' import { composableKeysPlugin } from '../../vite/src/plugins/composable-keys' +import { DynamicBasePlugin } from './plugins/dynamic-base' import { createMFS } from './utils/mfs' import { registerVirtualModules } from './virtual-modules' import { client, server } from './configs' @@ -35,8 +35,7 @@ export async function bundle (nuxt: Nuxt) { // Configure compilers const compilers = webpackConfigs.map((config) => { config.plugins.push(DynamicBasePlugin.webpack({ - sourcemap: nuxt.options.sourcemap, - globalPublicPath: '__webpack_public_path__' + sourcemap: nuxt.options.sourcemap })) config.plugins.push(composableKeysPlugin.webpack({ sourcemap: nuxt.options.sourcemap, diff --git a/vitest.config.ts b/vitest.config.ts index 77c147dbb35..88f9411f64f 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -3,9 +3,11 @@ import { defineConfig } from 'vite' import { isWindows } from 'std-env' export default defineConfig({ - alias: { - '#app': resolve('./packages/nuxt/src/app/index.ts'), - '@nuxt/test-utils': resolve('./packages/test-utils/src/index.ts') + resolve: { + alias: { + '#app': resolve('./packages/nuxt/src/app/index.ts'), + '@nuxt/test-utils': resolve('./packages/test-utils/src/index.ts') + } }, esbuild: { tsconfigRaw: '{}' diff --git a/yarn.lock b/yarn.lock index c33d1d25786..715932666f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -48,7 +48,7 @@ __metadata: languageName: node 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 resolution: "@babel/core@npm:7.18.6" dependencies: @@ -408,7 +408,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-typescript@npm:^7.16.8": +"@babel/plugin-transform-typescript@npm:^7.18.8": version: 7.18.8 resolution: "@babel/plugin-transform-typescript@npm:7.18.8" dependencies: @@ -1805,7 +1805,7 @@ __metadata: ufo: ^0.8.5 unbuild: latest unimport: ^0.4.5 - vite: ^2.9.14 + vite: ~3.0.0 languageName: unknown linkType: soft @@ -1896,8 +1896,8 @@ __metadata: "@nuxt/schema": ^3.0.0-rc.6 "@rollup/plugin-replace": ^4.0.0 "@types/cssnano": ^5 - "@vitejs/plugin-vue": ^2.3.3 - "@vitejs/plugin-vue-jsx": ^1.3.10 + "@vitejs/plugin-vue": ^3.0.0 + "@vitejs/plugin-vue-jsx": ^2.0.0 autoprefixer: ^10.4.7 chokidar: ^3.5.3 cssnano: ^5.1.12 @@ -1923,7 +1923,7 @@ __metadata: ufo: ^0.8.5 unbuild: latest unplugin: ^0.7.2 - vite: ^2.9.14 + vite: ~3.0.0 vite-node: ^0.18.1 vite-plugin-checker: ^0.4.9 vue: 3.2.37 @@ -2277,7 +2277,7 @@ __metadata: languageName: node 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 resolution: "@rollup/pluginutils@npm:4.2.1" dependencies: @@ -3014,27 +3014,28 @@ __metadata: languageName: node linkType: hard -"@vitejs/plugin-vue-jsx@npm:^1.3.10": - version: 1.3.10 - resolution: "@vitejs/plugin-vue-jsx@npm:1.3.10" +"@vitejs/plugin-vue-jsx@npm:^2.0.0": + version: 2.0.0 + resolution: "@vitejs/plugin-vue-jsx@npm:2.0.0" dependencies: - "@babel/core": ^7.17.9 + "@babel/core": ^7.18.6 "@babel/plugin-syntax-import-meta": ^7.10.4 - "@babel/plugin-transform-typescript": ^7.16.8 - "@rollup/pluginutils": ^4.2.0 + "@babel/plugin-transform-typescript": ^7.18.8 "@vue/babel-plugin-jsx": ^1.1.1 - hash-sum: ^2.0.0 - checksum: 868e90363e1234be43c3ced5097c8da430b9feed526c1f6c2dcc8a0546182e942018e6b5e1a0c16c66810b82cd17fa3a8e04ba969db6f253c6f7c490dce83a0b + peerDependencies: + vite: ^3.0.0 + vue: ^3.0.0 + checksum: 197b0dd0263b5b7df22a055155da6d9b7f21098f7de43e94589ce8daa494c12c9850157bc4e228a766fbe602aafa5935e4bef0edec9158a0507cee5581f0a067 languageName: node linkType: hard -"@vitejs/plugin-vue@npm:^2.3.3": - version: 2.3.3 - resolution: "@vitejs/plugin-vue@npm:2.3.3" +"@vitejs/plugin-vue@npm:^3.0.0": + version: 3.0.0 + resolution: "@vitejs/plugin-vue@npm:3.0.0" peerDependencies: - vite: ^2.5.10 + vite: ^3.0.0 vue: ^3.2.25 - checksum: 9303dcb9c8580d0ee9b33542639ac1a36ad9cc0e773a1f9b9b05623d74574f6a901ce781918b53f5a58eb3c6218ba96c27ef6efbf3e7ef6be16864fc1cae1626 + checksum: e2c3e31afc39f83b16174ef4742e88e07b5516688f02dcbe9814afd62c859925173ec2d6a9f088c6098eb45cdae926122308596a8ece90028ffea169f15c685f languageName: node linkType: hard @@ -5708,7 +5709,7 @@ __metadata: languageName: node 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 resolution: "esbuild@npm:0.14.49" dependencies: @@ -11353,7 +11354,7 @@ __metadata: languageName: node 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 resolution: "resolve@npm:1.22.1" dependencies: @@ -11366,7 +11367,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.1.7#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.10.1#~builtin, resolve@patch:resolve@^1.17.0#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.0#~builtin": +"resolve@patch:resolve@^1.1.7#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.10.1#~builtin, resolve@patch:resolve@^1.17.0#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin": version: 1.22.1 resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&hash=07638b" dependencies: @@ -11512,7 +11513,7 @@ __metadata: languageName: node 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 resolution: "rollup@npm:2.76.0" dependencies: @@ -13217,19 +13218,20 @@ __metadata: languageName: node linkType: hard -"vite@npm:^2.9.14": - version: 2.9.14 - resolution: "vite@npm:2.9.14" +"vite@npm:~3.0.0": + version: 3.0.0 + resolution: "vite@npm:3.0.0" dependencies: - esbuild: ^0.14.27 + esbuild: ^0.14.47 fsevents: ~2.3.2 - postcss: ^8.4.13 - resolve: ^1.22.0 - rollup: ^2.59.0 + postcss: ^8.4.14 + resolve: ^1.22.1 + rollup: ^2.75.6 peerDependencies: less: "*" sass: "*" stylus: "*" + terser: ^5.4.0 dependenciesMeta: fsevents: optional: true @@ -13240,9 +13242,11 @@ __metadata: optional: true stylus: optional: true + terser: + optional: true bin: vite: bin/vite.js - checksum: f78b54f58482ea97d385e36873ae1aa4744c5e467c1d6d4e0835bd55494d2d8f6ce763f17c241c66104be687d5ee535b8e1e96c14210c9ba0c343fe78c58f694 + checksum: 4920b5b0a4d4bd4a003121b2eb6ed41ac2ab69e6ab055db645e678c14e68d6eef780362d4d482cf439b576c37fb65dc9a7ebbbf90354e7ae362034a28eac9130 languageName: node linkType: hard