Nuxt/packages/webpack/src/webpack.ts

172 lines
5.4 KiB
TypeScript
Raw Normal View History

import type { IncomingMessage, ServerResponse } from 'node:http'
2020-07-02 13:02:35 +00:00
import pify from 'pify'
import webpack from 'webpack'
import { promisifyHandler } from 'h3'
import webpackDevMiddleware, { API, OutputFileSystem } from 'webpack-dev-middleware'
2020-07-02 13:02:35 +00:00
import webpackHotMiddleware from 'webpack-hot-middleware'
import type { Compiler, Watching } from 'webpack'
import type { Nuxt } from '@nuxt/schema'
2022-01-18 16:59:14 +00:00
import { joinURL } from 'ufo'
import { logger, useNuxt } from '@nuxt/kit'
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'
import { createWebpackConfigContext, applyPresets, getWebpackConfig } from './utils/config'
2020-07-02 13:02:35 +00:00
// TODO: Support plugins
// const plugins: string[] = []
export async function bundle (nuxt: Nuxt) {
registerVirtualModules()
2020-09-02 12:27:27 +00:00
const webpackConfigs = [client, ...nuxt.options.ssr ? [server] : []].map((preset) => {
const ctx = createWebpackConfigContext(nuxt)
applyPresets(ctx, preset)
2020-09-02 12:27:27 +00:00
return getWebpackConfig(ctx)
})
2020-07-02 13:02:35 +00:00
await nuxt.callHook('webpack:config', webpackConfigs)
2020-07-02 13:02:35 +00:00
// Initialize shared MFS for dev
const mfs = nuxt.options.dev ? createMFS() : null
2020-07-02 13:02:35 +00:00
// Configure compilers
const compilers = webpackConfigs.map((config) => {
config.plugins!.push(DynamicBasePlugin.webpack({
sourcemap: nuxt.options.sourcemap
}))
config.plugins!.push(composableKeysPlugin.webpack({
sourcemap: nuxt.options.sourcemap,
rootDir: nuxt.options.rootDir
}))
2020-07-02 13:02:35 +00:00
// Create compiler
const compiler = webpack(config)
2020-07-02 13:02:35 +00:00
// In dev, write files in memory FS
if (nuxt.options.dev) {
compiler.outputFileSystem = mfs as unknown as OutputFileSystem
2021-04-02 19:38:11 +00:00
}
2020-07-02 13:02:35 +00:00
return compiler
})
2020-07-02 13:02:35 +00:00
nuxt.hook('close', async () => {
for (const compiler of compilers) {
await new Promise(resolve => compiler.close(resolve))
2020-07-02 13:02:35 +00:00
}
})
2020-07-02 13:02:35 +00:00
// Start Builds
if (nuxt.options.dev) {
return Promise.all(compilers.map(c => compile(c)))
2020-07-02 13:02:35 +00:00
}
for (const c of compilers) {
await compile(c)
2020-07-02 13:02:35 +00:00
}
}
2020-07-02 13:02:35 +00:00
async function createDevMiddleware (compiler: Compiler) {
const nuxt = useNuxt()
logger.debug('Creating webpack middleware...')
// Create webpack dev middleware
const devMiddleware = pify(webpackDevMiddleware(compiler, {
publicPath: joinURL(nuxt.options.app.baseURL, nuxt.options.app.buildAssetsDir),
outputFileSystem: compiler.outputFileSystem as any,
stats: 'none',
...nuxt.options.webpack.devMiddleware
})) as any as API<IncomingMessage, ServerResponse>
// @ts-ignore
nuxt.hook('close', () => pify(devMiddleware.close.bind(devMiddleware))())
const { client: _client, ...hotMiddlewareOptions } = nuxt.options.webpack.hotMiddleware || {}
const hotMiddleware = pify(webpackHotMiddleware(compiler, {
log: false,
heartbeat: 10000,
path: joinURL(nuxt.options.app.baseURL, '__webpack_hmr', compiler.options.name!),
...hotMiddlewareOptions
})) as any as API<IncomingMessage, ServerResponse>
await nuxt.callHook('webpack:devMiddleware', devMiddleware)
await nuxt.callHook('webpack:hotMiddleware', hotMiddleware)
// Register devMiddleware on server
const handlers = [promisifyHandler(devMiddleware), promisifyHandler(hotMiddleware)]
await nuxt.callHook('server:devMiddleware', async (req, res, next) => {
for (const mw of handlers) {
await mw?.(req, res)
2020-07-02 13:02:35 +00:00
}
next()
})
2020-07-02 13:02:35 +00:00
return devMiddleware
}
2020-07-02 13:02:35 +00:00
async function compile (compiler: Compiler) {
const nuxt = useNuxt()
2020-07-02 13:02:35 +00:00
const { name } = compiler.options
2020-07-02 13:02:35 +00:00
await nuxt.callHook('build:compile', { name: name!, compiler })
2020-07-02 13:02:35 +00:00
// Load renderer resources after build
compiler.hooks.done.tap('load-resources', async (stats) => {
await nuxt.callHook('build:compiled', { name: name!, compiler, stats })
// Reload renderer
await nuxt.callHook('build:resources', compiler.outputFileSystem)
})
2020-07-02 13:02:35 +00:00
// --- Dev Build ---
if (nuxt.options.dev) {
const compilersWatching: Watching[] = []
2020-07-02 13:02:35 +00:00
nuxt.hook('close', async () => {
await Promise.all(compilersWatching.map(watching => pify(watching.close.bind(watching))()))
})
2020-07-02 13:02:35 +00:00
// Client build
if (name === 'client') {
return new Promise((resolve, reject) => {
compiler.hooks.done.tap('nuxt-dev', () => { resolve(null) })
compiler.hooks.failed.tap('nuxt-errorlog', (err) => { reject(err) })
// Start watch
createDevMiddleware(compiler).then((devMiddleware) => {
compilersWatching.push(devMiddleware.context.watching)
})
})
2020-07-02 13:02:35 +00:00
}
// Server, build and watch for changes
return new Promise((resolve, reject) => {
const watching = compiler.watch(nuxt.options.watchers.webpack, (err) => {
if (err) { return reject(err) }
resolve(null)
})
compilersWatching.push(watching)
})
2020-07-02 13:02:35 +00:00
}
// --- Production Build ---
const stats = await new Promise<webpack.Stats>((resolve, reject) => compiler.run((err, stats) => err ? reject(err) : resolve(stats!)))
if (stats.hasErrors()) {
// non-quiet mode: errors will be printed by webpack itself
const error = new Error('Nuxt build error')
if (nuxt.options.build.quiet === true) {
error.stack = stats.toString('errors-only')
}
throw error
2020-07-02 13:02:35 +00:00
}
// Await for renderer to load resources (programmatic, tests and generate)
await nuxt.callHook('build:resources')
}