feat: update vite implementation (#130)

This commit is contained in:
pooya parsa 2021-04-29 13:51:54 +02:00 committed by GitHub
parent 87bee5eb0b
commit 9732d63c74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 274 additions and 132 deletions

View File

@ -5,7 +5,8 @@
"license": "MIT",
"exports": {
".": "./dist",
"./meta": "./meta.js"
"./meta": "./meta.js",
"./dist/*": "./dist/*"
},
"main": "./dist",
"module": "./dist",

View File

@ -1,6 +1,6 @@
import { $fetch } from 'ohmyfetch'
import logs from 'nuxt/app/plugins/logs.client.dev'
import progress from 'nuxt/app/plugins/progress.client'
import logs from '#app/plugins/logs.client.dev'
import progress from '#app/plugins/progress.client'
<% const plugins = app.plugins.filter(p => p.mode === 'client').map(p => p.src) %>
<%= nxt.importSources(plugins) %>

View File

@ -1,7 +1,7 @@
import head from 'nuxt/app/plugins/head'
import router from 'nuxt/app/plugins/router'
import vuex from 'nuxt/app/plugins/vuex'
import legacy from 'nuxt/app/plugins/legacy'
import head from '#app/plugins/head'
import router from '#app/plugins/router'
import vuex from '#app/plugins/vuex'
import legacy from '#app/plugins/legacy'
<% const plugins = app.plugins.filter(p => p.mode === 'all').map(p => p.src) %>
<%= nxt.importSources(plugins) %>

View File

@ -1,4 +1,4 @@
import preload from 'nuxt/app/plugins/preload.server'
import preload from '#app/plugins/preload.server'
<% const plugins = app.plugins.filter(p => p.mode === 'server').map(p => p.src) %>
<%= nxt.importSources(plugins) %>

View File

@ -7,9 +7,9 @@ import {
} from 'vue-router'
import type { Plugin } from '@nuxt/app'
// @ts-ignore
import routes from 'nuxt/build/routes'
// @ts-ignore
import NuxtPage from './NuxtPage.vue'
import routes from '#build/routes'
// @ts-ignore
export default <Plugin> function router (nuxt) {
const { app } = nuxt

View File

@ -1,4 +1,4 @@
import { join, relative } from 'path'
import { join, relative, resolve } from 'upath'
import fsExtra from 'fs-extra'
import { debounce } from 'lodash'
import { Nuxt } from '@nuxt/kit'
@ -46,6 +46,7 @@ async function _build (builder: Builder) {
if (!nuxt.options.dev) {
await fsExtra.emptyDir(nuxt.options.buildDir)
}
await fsExtra.emptyDir(resolve(nuxt.options.buildDir, 'dist'))
await generate(builder)
if (nuxt.options.dev) {

View File

@ -11,14 +11,18 @@
"prepack": "unbuild"
},
"devDependencies": {
"@types/debounce": "^1.2.0",
"unbuild": "^0.2.3"
},
"dependencies": {
"@nuxt/kit": "^0.5.3",
"@vitejs/plugin-vue": "^1.2.2",
"@vue/compiler-sfc": "^3.0.11",
"chokidar": "^3.5.1",
"consola": "^2.15.3",
"debounce": "^1.2.1",
"fs-extra": "^9.1.0",
"upath": "^2.0.1",
"vite": "^2.2.3",
"vue": "3.0.11"
}

View File

@ -0,0 +1,54 @@
import { resolve } from 'path'
import * as vite from 'vite'
import vitePlugin from '@vitejs/plugin-vue'
import { cacheDirPlugin } from './plugins/cache-dir'
import { replace } from './plugins/replace'
import { ViteBuildContext, ViteOptions } from './vite'
export async function buildClient (ctx: ViteBuildContext) {
const clientConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
define: {
'process.server': false,
'process.client': true,
'module.hot': false,
global: 'globalThis'
},
build: {
outDir: 'dist/client',
assetsDir: '.',
rollupOptions: {
input: resolve(ctx.nuxt.options.buildDir, 'client.js')
}
},
plugins: [
replace({ 'process.env': 'import.meta.env' }),
cacheDirPlugin(ctx.nuxt.options.rootDir, 'client'),
vitePlugin(ctx.config.vue)
],
server: {
middlewareMode: true
}
} as ViteOptions)
await ctx.nuxt.callHook('vite:extendConfig', clientConfig, { isClient: true, isServer: false })
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
if (req.url === '/_nuxt/client.js') {
return res.end('')
}
viteServer.middlewares.handle(req, res, (err) => {
req.url = originalURL
next(err)
})
}
await ctx.nuxt.callHook('server:devMiddleware', viteMiddleware)
ctx.nuxt.hook('close', async () => {
await viteServer.close()
})
}

View File

@ -0,0 +1,13 @@
import type { Plugin } from 'vite'
import { resolve } from 'upath'
export function cacheDirPlugin (rootDir, name: string) {
const optimizeCacheDir = resolve(rootDir, 'node_modules/.cache/vite', name)
return <Plugin> {
name: 'nuxt:cache-dir',
configResolved (resolvedConfig) {
// @ts-ignore
resolvedConfig.optimizeCacheDir = optimizeCacheDir
}
}
}

View File

@ -0,0 +1,17 @@
import type { Plugin } from 'vite'
export function replace (replacements: Record<string, string>) {
return <Plugin>{
name: 'nuxt:replace',
transform (code) {
Object.entries(replacements).forEach(([key, value]) => {
const escapedKey = key.replace(/\./g, '\\.')
code = code.replace(new RegExp(escapedKey, 'g'), value)
})
return {
code,
map: null
}
}
}
}

View File

@ -0,0 +1,85 @@
import { resolve } from 'path'
import * as vite from 'vite'
import vuePlugin from '@vitejs/plugin-vue'
import { watch } from 'chokidar'
import { mkdirp, writeFile } from 'fs-extra'
import debounce from 'debounce'
import consola from 'consola'
import { ViteBuildContext, ViteOptions } from './vite'
import { wpfs } from './utils/wpfs'
import { cacheDirPlugin } from './plugins/cache-dir'
export async function buildServer (ctx: ViteBuildContext) {
const serverConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
define: {
'process.server': true,
'process.client': false,
'typeof window': '"undefined"',
'typeof document': '"undefined"',
'typeof navigator': '"undefined"',
'typeof location': '"undefined"',
'typeof XMLHttpRequest': '"undefined"'
},
ssr: {
external: [
'axios'
]
},
build: {
outDir: 'dist/server',
ssr: true,
rollupOptions: {
input: resolve(ctx.nuxt.options.buildDir, 'entry.server.mjs'),
onwarn (warning, rollupWarn) {
if (!['UNUSED_EXTERNAL_IMPORT'].includes(warning.code)) {
rollupWarn(warning)
}
}
}
},
plugins: [
cacheDirPlugin(ctx.nuxt.options.rootDir, 'server'),
vuePlugin()
]
} as ViteOptions)
await ctx.nuxt.callHook('vite:extendConfig', serverConfig, { isClient: false, isServer: true })
const serverDist = resolve(ctx.nuxt.options.buildDir, 'dist/server')
await mkdirp(serverDist)
await writeFile(resolve(serverDist, 'server.js'), 'try { module.exports = require("./entry.server") } catch (err) { module.exports = () => { throw err } }', 'utf8')
await writeFile(resolve(serverDist, 'client.manifest.json'), 'false', 'utf8')
const onBuild = () => ctx.nuxt.callHook('build:resources', wpfs)
if (!ctx.nuxt.options.ssr) {
await onBuild()
return
}
const build = debounce(async () => {
const start = Date.now()
await vite.build(serverConfig)
await onBuild()
consola.info(`Server built in ${Date.now() - start}ms`)
}, 300)
await build()
const watcher = watch([
ctx.nuxt.options.buildDir,
ctx.nuxt.options.srcDir,
ctx.nuxt.options.rootDir
], {
ignored: [
'**/dist/server/**'
]
})
watcher.on('change', () => build())
ctx.nuxt.hook('close', async () => {
await watcher.close()
})
}

View File

@ -0,0 +1,15 @@
import type { ViteDevServer } from 'vite'
export async function warmupViteServer (server: ViteDevServer, entries: string[]) {
const warmedUrls = new Set<String>()
const warmup = async (url: string) => {
if (warmedUrls.has(url)) { return undefined }
warmedUrls.add(url)
await server.transformRequest(url)
const deps = Array.from(server.moduleGraph.urlToModuleMap.get(url).importedModules)
await Promise.all(deps.map(m => warmup(m.url)))
}
await Promise.all(entries.map(entry => warmup(entry)))
}

View File

@ -0,0 +1,7 @@
import { join } from 'upath'
import fsExtra from 'fs-extra'
export const wpfs = {
...fsExtra,
join
}

View File

@ -1,131 +1,74 @@
import { resolve } from 'path'
import type { Nuxt } from '@nuxt/kit'
import { mkdirp, writeFile } from 'fs-extra'
import vue from '@vitejs/plugin-vue'
import consola from 'consola'
import * as vite from 'vite'
import consola from 'consola'
import type { Nuxt } from '@nuxt/kit'
import type { InlineConfig, SSROptions } from 'vite'
import type { Options } from '@vitejs/plugin-vue'
import { buildClient } from './client'
import { buildServer } from './server'
import { warmupViteServer } from './utils/warmup'
interface ViteBuildContext {
export interface ViteOptions extends InlineConfig {
vue?: Options
ssr?: SSROptions
}
export interface ViteBuildContext {
nuxt: Nuxt
config: vite.InlineConfig
config: ViteOptions
}
export async function bundle (nuxt: Nuxt) {
const ctx: ViteBuildContext = {
nuxt,
config: {
root: nuxt.options.buildDir,
mode: nuxt.options.dev ? 'development' : 'production',
logLevel: 'warn',
resolve: {
alias: {
'nuxt/app': nuxt.options.appDir,
'nuxt/build': nuxt.options.buildDir,
'~': nuxt.options.srcDir,
'@': nuxt.options.srcDir
}
},
clearScreen: false,
plugins: [
vue({})
],
build: {
emptyOutDir: false
}
}
config: vite.mergeConfig(
nuxt.options.vite as any || {},
{
root: nuxt.options.buildDir,
mode: nuxt.options.dev ? 'development' : 'production',
logLevel: 'warn',
define: {
'process.dev': nuxt.options.dev
},
resolve: {
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
alias: {
...nuxt.options.alias,
'#build': nuxt.options.buildDir,
'#app': nuxt.options.appDir,
'~': nuxt.options.srcDir,
'@': nuxt.options.srcDir,
'web-streams-polyfill/ponyfill/es2018': 'unenv/runtime/mock/empty',
// Cannot destructure property 'AbortController' of ..
'abort-controller': 'unenv/runtime/mock/empty'
}
},
vue: {},
css: {},
optimizeDeps: {
exclude: []
},
esbuild: {
jsxFactory: 'h',
jsxFragment: 'Fragment'
},
clearScreen: false,
build: {
emptyOutDir: false
},
plugins: []
} as ViteOptions
)
}
await nuxt.callHook('vite:extend', ctx)
await mkdirp(nuxt.options.buildDir)
await mkdirp(resolve(nuxt.options.buildDir, '.vite/temp'))
nuxt.hook('vite:serverCreated', (server: vite.ViteDevServer) => {
const start = Date.now()
warmupViteServer(server, ['/entry.client.mjs']).then(() => {
consola.info(`Vite warmed up in ${Date.now() - start}ms`)
}).catch(consola.error)
})
const callBuild = async (fn, name) => {
try {
const start = Date.now()
await fn(ctx)
const time = (Date.now() - start) / 1000
consola.success(`${name} compiled successfully in ${time}s`)
} catch (err) {
consola.error(`${name} compiled with errors:`, err)
}
}
if (nuxt.options.dev) {
await Promise.all([
callBuild(buildClient, 'Client'),
callBuild(buildServer, 'Server')
])
} else {
await callBuild(buildClient, 'Client')
await callBuild(buildServer, 'Server')
}
}
async function buildClient (ctx: ViteBuildContext) {
const clientConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
define: {
'process.server': false,
'process.client': true
},
build: {
outDir: 'dist/client',
assetsDir: '.',
rollupOptions: {
input: resolve(ctx.nuxt.options.buildDir, './entry.client')
}
},
server: {
middlewareMode: true
}
} as vite.InlineConfig)
if (ctx.nuxt.options.dev) {
const viteServer = await vite.createServer(clientConfig)
await ctx.nuxt.callHook('server:devMiddleware', (req, res, next) => {
// Workaround: vite devmiddleware modifies req.url
const originalURL = req.url
viteServer.middlewares.handle(req, res, (err) => {
req.url = originalURL
next(err)
})
})
} else {
await vite.build(clientConfig)
}
}
async function buildServer (ctx: ViteBuildContext) {
const serverConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
define: {
'process.server': true,
'process.client': false,
window: undefined
},
build: {
outDir: 'dist/server',
ssr: true,
rollupOptions: {
input: resolve(ctx.nuxt.options.buildDir, './entry.server'),
onwarn (warning, rollupWarn) {
if (!['UNUSED_EXTERNAL_IMPORT'].includes(warning.code)) {
rollupWarn(warning)
}
}
}
}
} as vite.InlineConfig)
const serverDist = resolve(ctx.nuxt.options.buildDir, 'dist/server')
await mkdirp(serverDist)
await writeFile(resolve(serverDist, 'client.manifest.json'), 'false')
await writeFile(resolve(serverDist, 'server.js'), 'const entry = require("./entry.server"); module.exports = entry.default || entry;')
await vite.build(serverConfig)
if (ctx.nuxt.options.dev) {
ctx.nuxt.hook('builder:watch', () => {
vite.build(serverConfig).catch(consola.error)
})
}
await buildClient(ctx)
await buildServer(ctx)
}

View File

@ -34,8 +34,6 @@ function serverStandalone (ctx: WebpackConfigContext) {
// TODO: Refactor this out of webpack
const inline = [
'src/',
'nuxt/app',
'nuxt/build',
'@nuxt/app',
'vuex5',
'!',

View File

@ -109,8 +109,8 @@ function baseAlias (ctx: WebpackConfigContext) {
const { options } = ctx
ctx.alias = {
'nuxt/app': options.appDir,
'nuxt/build': options.buildDir,
'#app': options.appDir,
'#build': options.buildDir,
...options.alias,
...ctx.alias
}

View File

@ -1716,11 +1716,15 @@ __metadata:
resolution: "@nuxt/vite-builder@workspace:packages/vite"
dependencies:
"@nuxt/kit": ^0.5.3
"@types/debounce": ^1.2.0
"@vitejs/plugin-vue": ^1.2.2
"@vue/compiler-sfc": ^3.0.11
chokidar: ^3.5.1
consola: ^2.15.3
debounce: ^1.2.1
fs-extra: ^9.1.0
unbuild: ^0.2.3
upath: ^2.0.1
vite: ^2.2.3
vue: 3.0.11
languageName: unknown