2018-10-25 10:55:05 +00:00
|
|
|
import path from 'path'
|
2018-10-24 16:55:18 +00:00
|
|
|
import pify from 'pify'
|
|
|
|
import webpack from 'webpack'
|
|
|
|
import MFS from 'memory-fs'
|
2018-10-25 10:55:05 +00:00
|
|
|
import Glob from 'glob'
|
2018-10-24 16:55:18 +00:00
|
|
|
import webpackDevMiddleware from 'webpack-dev-middleware'
|
|
|
|
import webpackHotMiddleware from 'webpack-hot-middleware'
|
|
|
|
import consola from 'consola'
|
|
|
|
|
|
|
|
import {
|
|
|
|
parallel,
|
2018-10-25 10:55:05 +00:00
|
|
|
sequence,
|
|
|
|
wrapArray
|
2018-12-22 21:05:13 +00:00
|
|
|
} from '@nuxt/utils'
|
2018-10-24 16:55:18 +00:00
|
|
|
|
2018-12-01 10:13:28 +00:00
|
|
|
import { ClientConfig, ModernConfig, ServerConfig } from './config'
|
|
|
|
import PerfLoader from './utils/perf-loader'
|
2018-10-24 16:55:18 +00:00
|
|
|
|
2018-10-25 10:55:05 +00:00
|
|
|
const glob = pify(Glob)
|
|
|
|
|
2018-11-08 09:15:56 +00:00
|
|
|
export class WebpackBundler {
|
2018-10-24 16:55:18 +00:00
|
|
|
constructor(context) {
|
|
|
|
this.context = context
|
|
|
|
// Fields that set on build
|
|
|
|
this.compilers = []
|
|
|
|
this.compilersWatching = []
|
2018-11-07 23:37:06 +00:00
|
|
|
this.devMiddleware = {}
|
|
|
|
this.hotMiddleware = {}
|
2018-10-24 16:55:18 +00:00
|
|
|
|
2018-12-01 10:13:28 +00:00
|
|
|
// Initialize shared MFS for dev
|
2018-10-24 16:55:18 +00:00
|
|
|
if (this.context.options.dev) {
|
|
|
|
this.mfs = new MFS()
|
2018-12-09 10:42:22 +00:00
|
|
|
|
2018-12-12 06:31:49 +00:00
|
|
|
// TODO: Enable when async FS required
|
2018-12-09 10:42:22 +00:00
|
|
|
// this.mfs.exists = function (...args) { return Promise.resolve(this.existsSync(...args)) }
|
|
|
|
// this.mfs.readFile = function (...args) { return Promise.resolve(this.readFileSync(...args)) }
|
2018-10-24 16:55:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async build() {
|
2019-01-17 21:18:29 +00:00
|
|
|
const { options } = this.context
|
2018-10-24 16:55:18 +00:00
|
|
|
|
|
|
|
const compilersOptions = []
|
|
|
|
|
|
|
|
// Client
|
|
|
|
const clientConfig = new ClientConfig(this).config()
|
|
|
|
compilersOptions.push(clientConfig)
|
|
|
|
|
2018-10-31 15:52:35 +00:00
|
|
|
// Modern
|
|
|
|
let modernConfig
|
2018-11-07 23:37:06 +00:00
|
|
|
if (options.modern) {
|
2018-10-31 15:52:35 +00:00
|
|
|
modernConfig = new ModernConfig(this).config()
|
|
|
|
compilersOptions.push(modernConfig)
|
|
|
|
}
|
|
|
|
|
2018-10-24 16:55:18 +00:00
|
|
|
// Server
|
|
|
|
let serverConfig = null
|
|
|
|
if (options.build.ssr) {
|
|
|
|
serverConfig = new ServerConfig(this).config()
|
|
|
|
compilersOptions.push(serverConfig)
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const p of this.context.plugins) {
|
|
|
|
// Client config
|
|
|
|
if (!clientConfig.resolve.alias[p.name]) {
|
2018-12-20 09:28:10 +00:00
|
|
|
clientConfig.resolve.alias[p.name] = p.mode === 'server' ? './empty.js' : p.src
|
2018-10-24 16:55:18 +00:00
|
|
|
}
|
|
|
|
|
2018-12-30 14:28:42 +00:00
|
|
|
// Modern config
|
|
|
|
if (modernConfig && !modernConfig.resolve.alias[p.name]) {
|
|
|
|
modernConfig.resolve.alias[p.name] = p.mode === 'server' ? './empty.js' : p.src
|
|
|
|
}
|
|
|
|
|
2018-10-24 16:55:18 +00:00
|
|
|
// Server config
|
|
|
|
if (serverConfig && !serverConfig.resolve.alias[p.name]) {
|
2018-12-20 09:28:10 +00:00
|
|
|
serverConfig.resolve.alias[p.name] = p.mode === 'client' ? './empty.js' : p.src
|
2018-10-24 16:55:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 10:55:05 +00:00
|
|
|
// Check styleResource existence
|
2019-01-17 21:18:29 +00:00
|
|
|
const { styleResources } = this.context.options.build
|
2018-11-09 23:59:40 +00:00
|
|
|
if (styleResources && Object.keys(styleResources).length) {
|
|
|
|
consola.warn(
|
|
|
|
'Using styleResources without the nuxt-style-resources-module is not suggested and can lead to severe performance issues.',
|
|
|
|
'Please use https://github.com/nuxt-community/style-resources-module'
|
|
|
|
)
|
2018-12-12 10:47:54 +00:00
|
|
|
for (const ext of Object.keys(styleResources)) {
|
|
|
|
await Promise.all(wrapArray(styleResources[ext]).map(async (p) => {
|
|
|
|
const styleResourceFiles = await glob(path.resolve(this.context.options.rootDir, p))
|
|
|
|
|
|
|
|
if (!styleResourceFiles || styleResourceFiles.length === 0) {
|
|
|
|
throw new Error(`Style Resource not found: ${p}`)
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
}
|
2018-12-01 10:13:28 +00:00
|
|
|
}
|
2018-10-25 10:55:05 +00:00
|
|
|
|
2018-10-24 16:55:18 +00:00
|
|
|
// Configure compilers
|
|
|
|
this.compilers = compilersOptions.map((compilersOption) => {
|
|
|
|
const compiler = webpack(compilersOption)
|
|
|
|
|
|
|
|
// In dev, write files in memory FS
|
|
|
|
if (options.dev) {
|
|
|
|
compiler.outputFileSystem = this.mfs
|
|
|
|
}
|
|
|
|
|
|
|
|
return compiler
|
|
|
|
})
|
|
|
|
|
2018-12-12 06:31:49 +00:00
|
|
|
// Warm up perfLoader before build
|
2018-10-24 16:55:18 +00:00
|
|
|
if (options.build.parallel) {
|
|
|
|
consola.info('Warming up worker pools')
|
2018-11-08 22:26:52 +00:00
|
|
|
PerfLoader.warmupAll({ dev: options.dev })
|
2018-10-24 16:55:18 +00:00
|
|
|
consola.success('Worker pools ready')
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start Builds
|
|
|
|
const runner = options.dev ? parallel : sequence
|
|
|
|
|
2019-02-08 16:25:11 +00:00
|
|
|
await runner(this.compilers, compiler => this.webpackCompile(compiler))
|
2018-10-24 16:55:18 +00:00
|
|
|
}
|
|
|
|
|
2018-12-09 10:42:22 +00:00
|
|
|
async webpackCompile(compiler) {
|
2019-01-17 21:18:29 +00:00
|
|
|
const { name } = compiler.options
|
2018-12-09 10:42:22 +00:00
|
|
|
const { nuxt, options } = this.context
|
2018-10-24 16:55:18 +00:00
|
|
|
|
2018-12-09 10:42:22 +00:00
|
|
|
await nuxt.callHook('build:compile', { name, compiler })
|
2018-10-24 16:55:18 +00:00
|
|
|
|
2018-12-09 10:42:22 +00:00
|
|
|
// Load renderer resources after build
|
|
|
|
compiler.hooks.done.tap('load-resources', async (stats) => {
|
|
|
|
await nuxt.callHook('build:compiled', {
|
|
|
|
name,
|
|
|
|
compiler,
|
|
|
|
stats
|
|
|
|
})
|
2018-10-24 16:55:18 +00:00
|
|
|
|
2018-12-09 10:42:22 +00:00
|
|
|
// Reload renderer
|
|
|
|
await nuxt.callHook('build:resources', this.mfs)
|
|
|
|
})
|
2018-10-24 16:55:18 +00:00
|
|
|
|
2018-12-09 10:42:22 +00:00
|
|
|
// --- Dev Build ---
|
|
|
|
if (options.dev) {
|
|
|
|
// Client Build, watch is started by dev-middleware
|
|
|
|
if (['client', 'modern'].includes(name)) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
compiler.hooks.done.tap('nuxt-dev', () => resolve())
|
|
|
|
this.webpackDev(compiler)
|
|
|
|
})
|
|
|
|
}
|
2018-10-24 16:55:18 +00:00
|
|
|
|
2018-12-09 10:42:22 +00:00
|
|
|
// Server, build and watch for changes
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const watching = compiler.watch(options.watchers.webpack, (err) => {
|
2018-10-24 16:55:18 +00:00
|
|
|
if (err) {
|
|
|
|
return reject(err)
|
|
|
|
}
|
2018-12-09 10:42:22 +00:00
|
|
|
watching.close = pify(watching.close)
|
|
|
|
this.compilersWatching.push(watching)
|
2018-10-24 16:55:18 +00:00
|
|
|
resolve()
|
|
|
|
})
|
2018-12-09 10:42:22 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// --- Production Build ---
|
|
|
|
compiler.run = pify(compiler.run)
|
|
|
|
const stats = await compiler.run()
|
|
|
|
|
|
|
|
if (stats.hasErrors()) {
|
|
|
|
if (options.build.quiet === true) {
|
|
|
|
return Promise.reject(stats.toString(options.build.stats))
|
2018-10-24 16:55:18 +00:00
|
|
|
}
|
2019-02-08 16:25:11 +00:00
|
|
|
|
|
|
|
// Actual error will be printed by webpack
|
|
|
|
throw new Error('Nuxt Build Error')
|
2018-12-09 10:42:22 +00:00
|
|
|
}
|
2018-10-24 16:55:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
webpackDev(compiler) {
|
|
|
|
consola.debug('Adding webpack middleware...')
|
|
|
|
|
2019-01-17 21:18:29 +00:00
|
|
|
const { name } = compiler.options
|
2018-10-30 20:42:53 +00:00
|
|
|
const { nuxt: { server }, options } = this.context
|
2019-01-21 11:55:49 +00:00
|
|
|
const { client, ...hotMiddlewareOptions } = options.build.hotMiddleware || {}
|
2018-10-24 16:55:18 +00:00
|
|
|
|
|
|
|
// Create webpack dev middleware
|
2018-11-07 23:37:06 +00:00
|
|
|
this.devMiddleware[name] = pify(
|
2018-10-24 16:55:18 +00:00
|
|
|
webpackDevMiddleware(
|
|
|
|
compiler,
|
|
|
|
Object.assign(
|
|
|
|
{
|
|
|
|
publicPath: options.build.publicPath,
|
|
|
|
stats: false,
|
|
|
|
logLevel: 'silent',
|
|
|
|
watchOptions: options.watchers.webpack
|
|
|
|
},
|
|
|
|
options.build.devMiddleware
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2018-11-07 23:37:06 +00:00
|
|
|
this.devMiddleware[name].close = pify(this.devMiddleware[name].close)
|
2018-10-24 16:55:18 +00:00
|
|
|
|
2018-11-07 23:37:06 +00:00
|
|
|
this.hotMiddleware[name] = pify(
|
2018-10-24 16:55:18 +00:00
|
|
|
webpackHotMiddleware(
|
|
|
|
compiler,
|
|
|
|
Object.assign(
|
|
|
|
{
|
|
|
|
log: false,
|
|
|
|
heartbeat: 10000
|
|
|
|
},
|
2019-01-21 11:55:49 +00:00
|
|
|
hotMiddlewareOptions,
|
2018-11-07 23:37:06 +00:00
|
|
|
{
|
|
|
|
path: `/__webpack_hmr/${name}`
|
|
|
|
}
|
2018-10-24 16:55:18 +00:00
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
// Inject to renderer instance
|
2018-10-30 20:42:53 +00:00
|
|
|
if (server) {
|
2018-11-07 23:37:06 +00:00
|
|
|
server.devMiddleware = this.devMiddleware
|
|
|
|
server.hotMiddleware = this.hotMiddleware
|
2018-10-24 16:55:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async unwatch() {
|
2019-02-08 16:25:11 +00:00
|
|
|
await Promise.all(this.compilersWatching.map(watching => watching.close()))
|
2018-12-09 10:42:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async close() {
|
2018-12-09 22:00:48 +00:00
|
|
|
if (this.__closed) {
|
|
|
|
return
|
|
|
|
}
|
2018-12-09 10:42:22 +00:00
|
|
|
this.__closed = true
|
|
|
|
|
|
|
|
// Unwatch
|
|
|
|
await this.unwatch()
|
|
|
|
|
2018-10-24 16:55:18 +00:00
|
|
|
// Stop webpack middleware
|
2018-11-07 23:37:06 +00:00
|
|
|
for (const devMiddleware of Object.values(this.devMiddleware)) {
|
|
|
|
await devMiddleware.close()
|
2018-10-24 16:55:18 +00:00
|
|
|
}
|
2018-12-09 10:42:22 +00:00
|
|
|
|
|
|
|
// Cleanup MFS
|
|
|
|
if (this.mfs) {
|
|
|
|
delete this.mfs.data
|
|
|
|
delete this.mfs
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cleanup more resources
|
|
|
|
delete this.compilers
|
|
|
|
delete this.compilersWatching
|
|
|
|
delete this.devMiddleware
|
|
|
|
delete this.hotMiddleware
|
2018-10-24 16:55:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
forGenerate() {
|
|
|
|
this.context.isStatic = true
|
|
|
|
}
|
|
|
|
}
|