Skip to content

Commit

Permalink
feat!: migrate to vite 3 (#480)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe committed Sep 26, 2022
1 parent aae51f3 commit 990b28d
Show file tree
Hide file tree
Showing 19 changed files with 308 additions and 649 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Expand Up @@ -84,10 +84,10 @@ jobs:
- name: Test (fixtures)
run: yarn test:fixtures

# - name: Test (fixtures with dev)
# run: yarn test:fixtures:dev
# env:
# NODE_OPTIONS: --max-old-space-size=8192
- name: Test (fixtures with dev)
run: yarn test:fixtures:dev
env:
NODE_OPTIONS: --max-old-space-size=8192

test-fixtures-webpack:
runs-on: ${{ matrix.os }}
Expand Down
5 changes: 3 additions & 2 deletions package.json
Expand Up @@ -37,7 +37,7 @@
"@nuxt/postcss8": "^1.1.3",
"@nuxt/schema": "3.0.0-rc.11",
"@nuxt/ui-templates": "^0.4.0",
"@vitejs/plugin-legacy": "^1.8.2",
"@vitejs/plugin-legacy": "^2.2.0",
"@vitejs/plugin-vue2": "^1.1.2",
"acorn": "^8.8.0",
"cookie-es": "^0.5.0",
Expand Down Expand Up @@ -69,12 +69,13 @@
"scule": "^0.3.2",
"semver": "^7.3.7",
"std-env": "^3.2.1",
"terser": "^5.15.0",
"ufo": "^0.8.5",
"unctx": "^2.0.2",
"unimport": "^0.6.7",
"unplugin": "^0.9.6",
"untyped": "^0.5.0",
"vite": "^2.9.14",
"vite": "~3.1.3",
"vue-bundle-renderer": "^0.4.3"
},
"devDependencies": {
Expand Down
4 changes: 0 additions & 4 deletions renovate.json
Expand Up @@ -3,10 +3,6 @@
"extends": [
"@nuxtjs"
],
"ignoreDeps": [
"vitest",
"vite"
],
"packageRules": [
{
"matchPackageNames": ["vue"],
Expand Down
3 changes: 1 addition & 2 deletions src/module.ts
@@ -1,7 +1,6 @@
import { createRequire } from 'module'
import { defineNuxtModule, installModule, checkNuxtCompatibility } from '@nuxt/kit'
import type { NuxtModule } from '@nuxt/schema'
import { NuxtCompatibility } from '@nuxt/schema'
import type { NuxtModule, NuxtCompatibility } from '@nuxt/schema'
import type { BridgeConfig } from '../types'
import { setupNitroBridge } from './nitro'
import { setupAppBridge } from './app'
Expand Down
2 changes: 1 addition & 1 deletion src/nitro.ts
Expand Up @@ -87,7 +87,7 @@ export async function setupNitroBridge () {
},
publicAssets: [
{
baseURL: nuxt.options.app.buildAssetsDir,
baseURL: (nuxt.options as any).bridge.vite ? '/' : nuxt.options.app.buildAssetsDir,
dir: resolve(nuxt.options.buildDir, 'dist/client')
},
...nuxt.options._layers
Expand Down
3 changes: 2 additions & 1 deletion src/runtime/composables.ts
Expand Up @@ -6,6 +6,7 @@ import type { RuntimeConfig } from '@nuxt/schema'
import { sendRedirect } from 'h3'
import { defu } from 'defu'
import { useRouter } from 'vue-router/composables'
import { joinURL } from 'ufo'
import { useNuxtApp } from './app'

export { useLazyAsyncData, refreshNuxtData } from './asyncData'
Expand Down Expand Up @@ -166,7 +167,7 @@ export const navigateTo = (to: RawLocation, options: NavigateToOptions = {}): Pr
if (process.server && useNuxtApp().ssrContext) {
// Server-side redirection using h3 res from ssrContext
const res = useNuxtApp().ssrContext?.res
const redirectLocation = router.resolve(to).route.fullPath
const redirectLocation = joinURL(useRuntimeConfig().app.baseURL, router.resolve(to).fullPath || '/')
return sendRedirect(res, redirectLocation)
}
// Client-side redirection using vue-router
Expand Down
3 changes: 3 additions & 0 deletions src/runtime/nitro/paths.ts
Expand Up @@ -18,3 +18,6 @@ export function publicAssetsURL (...path: string[]): string {
const publicBase = useRuntimeConfig().app.cdnURL || useRuntimeConfig().app.baseURL
return path.length ? joinURL(publicBase, ...path) : publicBase
}

globalThis.__publicAssetsURL = publicAssetsURL
globalThis.__buildAssetsURL = buildAssetsURL
108 changes: 78 additions & 30 deletions src/vite/client.ts
@@ -1,13 +1,17 @@
import { resolve } from 'pathe'
import * as vite from 'vite'
import { join, resolve } from 'pathe'
import createVuePlugin from '@vitejs/plugin-vue2'
import PluginLegacy from '@vitejs/plugin-legacy'
import { logger } from '@nuxt/kit'
import { joinURL } from 'ufo'
import { joinURL, withLeadingSlash, withoutLeadingSlash, withTrailingSlash } from 'ufo'
import escapeRE from 'escape-string-regexp'
import { getPort } from 'get-port-please'
import type { ServerOptions, Connect, InlineConfig } from 'vite'
import defu from 'defu'
import PluginLegacy from './stub-legacy.cjs'
import vite from './stub-vite.cjs'
import { devStyleSSRPlugin } from './plugins/dev-ssr-css'
import { RelativeAssetPlugin } from './plugins/dynamic-base'
import { jsxPlugin } from './plugins/jsx'
import { ViteBuildContext, ViteOptions } from './types'
import { prepareManifests } from './manifest'

export async function buildClient (ctx: ViteBuildContext) {
const alias = {
Expand All @@ -19,7 +23,16 @@ export async function buildClient (ctx: ViteBuildContext) {
: `defaultexport:${p.src}`
}

const clientConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
const clientConfig: 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.client': true,
'process.server': false,
Expand All @@ -28,7 +41,8 @@ export async function buildClient (ctx: ViteBuildContext) {
},
cacheDir: resolve(ctx.nuxt.options.rootDir, 'node_modules/.cache/vite/client'),
resolve: {
alias
alias,
dedupe: ['vue']
},
build: {
rollupOptions: {
Expand All @@ -41,43 +55,77 @@ export async function buildClient (ctx: ViteBuildContext) {
jsxPlugin(),
createVuePlugin(ctx.config.vue),
PluginLegacy(),
RelativeAssetPlugin(),
devStyleSSRPlugin({
rootDir: ctx.nuxt.options.rootDir,
srcDir: ctx.nuxt.options.srcDir,
buildAssetsURL: joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir)
})
],
appType: 'custom',
server: {
middlewareMode: true
}
} as ViteOptions)

await ctx.nuxt.callHook('vite:extendConfig', clientConfig, { isClient: true, isServer: false })

// Production build
// 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) {
const start = Date.now()
logger.info('Building client...')
await vite.build(clientConfig)
logger.success(`Client built in ${Date.now() - start}ms`)
return
clientConfig.server.hmr = false
}

// Create development server
const viteServer = await vite.createServer(clientConfig)
await ctx.nuxt.callHook('vite:serverCreated', viteServer)

const viteMiddleware = (req, res, next) => {
// Workaround: vite devmiddleware modifies req.url
const originalURL = req.url
viteServer.middlewares.handle(req, res, (err) => {
req.url = originalURL
next(err)
if (clientConfig.server && clientConfig.server.hmr !== false) {
const hmrPortDefault = 24678 // Vite's default HMR port
const hmrPort = await getPort({
port: hmrPortDefault,
ports: Array.from({ length: 20 }, (_, i) => hmrPortDefault + 1 + i)
})
clientConfig.server = defu(clientConfig.server, <ServerOptions> {
https: ctx.nuxt.options.server.https,
hmr: {
protocol: ctx.nuxt.options.server.https ? 'wss' : 'ws',
port: hmrPort
}
})
}
await ctx.nuxt.callHook('server:devMiddleware', viteMiddleware)

ctx.nuxt.hook('close', async () => {
await viteServer.close()
// We want to respect users' own rollup output options
ctx.config.build.rollupOptions = defu(ctx.config.build.rollupOptions, {
output: {
// 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].js')),
entryFileNames: ctx.nuxt.options.dev ? 'entry.js' : withoutLeadingSlash(join(ctx.nuxt.options.app.buildAssetsDir, '[name].[hash].js'))
}
})

await ctx.nuxt.callHook('vite:extendConfig', clientConfig, { isClient: true, isServer: false })

if (ctx.nuxt.options.dev) {
// Dev
const viteServer = await vite.createServer(clientConfig)
ctx.clientServer = viteServer
await ctx.nuxt.callHook('vite:serverCreated', viteServer, { isClient: true, isServer: false })
const baseURL = joinURL(ctx.nuxt.options.app.baseURL.replace(/^\./, '') || '/', ctx.nuxt.options.app.buildAssetsDir)
const BASE_RE = new RegExp(`^${escapeRE(withTrailingSlash(withLeadingSlash(baseURL)))}`)
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: unknown) => {
req.url = originalURL
next(err)
})
}
await ctx.nuxt.callHook('server:devMiddleware', viteMiddleware)

ctx.nuxt.hook('close', async () => {
await viteServer.close()
})
} else {
// Build
const start = Date.now()
await vite.build(clientConfig)
logger.info(`Client built in ${Date.now() - start}ms`)
}

await prepareManifests(ctx)
}
13 changes: 6 additions & 7 deletions src/vite/dev-bundler.ts
@@ -1,10 +1,10 @@
import { pathToFileURL } from 'url'
import { existsSync } from 'fs'
import { builtinModules } from 'module'
import { resolve } from 'pathe'
import * as vite from 'vite'
import { isAbsolute, resolve } from 'pathe'
import { ExternalsOptions, isExternal as _isExternal, ExternalsDefaults } from 'externality'
import { genDynamicImport, genObjectFromRawEntries } from 'knitwork'
import vite from './stub-vite.cjs'
import { hashId, uniq } from './utils'

export interface TransformChunk {
Expand Down Expand Up @@ -75,7 +75,7 @@ async function transformRequest (opts: TransformOptions, id: string) {
// Remove for externals
const withoutVersionQuery = id.replace(/\?v=\w+$/, '')
if (await isExternal(opts, withoutVersionQuery)) {
const path = builtinModules.includes(withoutVersionQuery.split('node:').pop())
const path = builtinModules.includes(withoutVersionQuery.split('node:').pop()) || !isAbsolute(withoutVersionQuery)
? withoutVersionQuery
: pathToFileURL(withoutVersionQuery).href
return {
Expand Down Expand Up @@ -129,12 +129,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)]))
}`
const manifestCode = `const __modules__ = ${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
const ssrModuleLoader = `
Expand Down
39 changes: 28 additions & 11 deletions src/vite/manifest.ts
@@ -1,7 +1,9 @@
import { resolve } from 'pathe'
import fse from 'fs-extra'
import { Manifest as ViteManifest } from 'vite'
import type { Manifest as ViteManifest } from 'vite'
import { normalizeViteManifest, Manifest, ResourceMeta } from 'vue-bundle-renderer'
import { withoutLeadingSlash, withTrailingSlash } from 'ufo'
import escapeRE from 'escape-string-regexp'
import { hash } from './utils'
import { ViteBuildContext } from './types'

Expand Down Expand Up @@ -47,11 +49,26 @@ export async function prepareManifests (ctx: ViteBuildContext) {
export async function generateBuildManifest (ctx: ViteBuildContext) {
const rDist = (...args: string[]): string => resolve(ctx.nuxt.options.buildDir, 'dist', ...args)

const viteClientManifest: ViteManifest = await fse.readJSON(rDist('client/manifest.json'))
const clientManifest: ViteManifest = await fse.readJSON(rDist('client/manifest.json'))

// Remove build assets directory from manifest
const buildAssetsDir = withTrailingSlash(withoutLeadingSlash(ctx.nuxt.options.app.buildAssetsDir))
const BASE_RE = new RegExp(`^${escapeRE(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, ''))
}
}
}

// Search for polyfill file, we don't include it in the client entry
const polyfillName = Object.values(viteClientManifest).find(entry => entry.file.startsWith('polyfills-legacy.'))?.file
const polyfill = await fse.readFile(rDist('client/' + polyfillName), 'utf-8')
const polyfillName = Object.values(clientManifest).find(entry => entry.file.startsWith('polyfills-legacy.'))?.file
const polyfill = await fse.readFile(rDist('client', buildAssetsDir, polyfillName), 'utf-8')

const clientImports = new Set<string>()
const clientEntry: Partial<Record<keyof ResourceMeta, Set<string>>> = {
Expand All @@ -60,16 +77,16 @@ export async function generateBuildManifest (ctx: ViteBuildContext) {
dynamicImports: new Set()
}

for (const entry in viteClientManifest) {
if (!viteClientManifest[entry].file.startsWith('polyfills-legacy')) {
clientImports.add(viteClientManifest[entry].file)
for (const entry in clientManifest) {
if (!clientManifest[entry].file.startsWith('polyfills-legacy')) {
clientImports.add(clientManifest[entry].file)
for (const key of ['css', 'assets', 'dynamicImports']) {
for (const file of viteClientManifest[entry][key] || []) {
for (const file of clientManifest[entry][key] || []) {
clientEntry[key].add(file)
}
}
}
delete viteClientManifest[entry].isEntry
delete clientManifest[entry].isEntry
}

// @vitejs/plugin-legacy uses SystemJS which need to call `System.import` to load modules
Expand All @@ -82,7 +99,7 @@ export async function generateBuildManifest (ctx: ViteBuildContext) {
].join('\n')
const clientEntryName = 'entry-legacy.' + hash(clientEntryCode) + '.js'

await fse.writeFile(rDist('client', clientEntryName), clientEntryCode, 'utf-8')
await fse.writeFile(rDist('client', buildAssetsDir, clientEntryName), clientEntryCode, 'utf-8')

const manifest = normalizeViteManifest({
[clientEntryName]: {
Expand All @@ -93,7 +110,7 @@ export async function generateBuildManifest (ctx: ViteBuildContext) {
assets: [...clientEntry.assets],
dynamicImports: [...clientEntry.dynamicImports]
},
...viteClientManifest
...clientManifest
})

await writeClientManifest(manifest, ctx.nuxt.options.buildDir)
Expand Down
8 changes: 4 additions & 4 deletions src/vite/plugins/dev-ssr-css.ts
@@ -1,9 +1,9 @@
import { joinURL } from 'ufo'
import { Plugin } from 'vite'
import type { Plugin } from 'vite'
import { isCSS } from '../utils'

export interface DevStyleSSRPluginOptions {
rootDir: string
srcDir: string
buildAssetsURL: string
}

Expand All @@ -18,8 +18,8 @@ export function devStyleSSRPlugin (options: DevStyleSSRPluginOptions): Plugin {
}

let moduleId = id
if (moduleId.startsWith(options.rootDir)) {
moduleId = moduleId.slice(options.rootDir.length)
if (moduleId.startsWith(options.srcDir)) {
moduleId = moduleId.slice(options.srcDir.length)
}

// When dev `<style>` is injected, remove the `<link>` styles from manifest
Expand Down

0 comments on commit 990b28d

Please sign in to comment.