mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-27 13:52:38 +00:00
feat: split builder into more refined modules (#4171)
New packages: - `@nuxt/webpack` - `@nuxt/generator`
This commit is contained in:
parent
972c3a7b10
commit
9df5f49e07
@ -54,6 +54,7 @@
|
||||
"@nuxt/cli": "^2.2.0",
|
||||
"@nuxt/common": "^2.2.0",
|
||||
"@nuxt/core": "^2.2.0",
|
||||
"@nuxt/generator": "^2.2.0",
|
||||
"consola": "^1.4.4"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -50,6 +50,7 @@
|
||||
"@nuxt/cli": "^2.2.0",
|
||||
"@nuxt/common": "^2.2.0",
|
||||
"@nuxt/core": "^2.2.0",
|
||||
"@nuxt/generator": "^2.2.0",
|
||||
"consola": "^1.4.4"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -1,2 +1,3 @@
|
||||
export * from '@nuxt/core'
|
||||
export * from '@nuxt/builder'
|
||||
export * from '@nuxt/generator'
|
||||
|
@ -21,7 +21,7 @@ module.exports = {
|
||||
coveragePathIgnorePatterns: [
|
||||
'node_modules',
|
||||
'packages/app',
|
||||
'packages/builder/webpack/plugins/vue'
|
||||
'packages/webpack/plugins/vue'
|
||||
],
|
||||
|
||||
testPathIgnorePatterns: [
|
||||
|
@ -8,52 +8,19 @@
|
||||
],
|
||||
"main": "dist/builder.js",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.1.2",
|
||||
"@babel/polyfill": "^7.0.0",
|
||||
"@nuxt/app": "^2.2.0",
|
||||
"@nuxt/common": "^2.2.0",
|
||||
"@nuxtjs/babel-preset-app": "^0.7.0",
|
||||
"@nuxt/webpack": "^2.2.0",
|
||||
"@nuxtjs/devalue": "^1.0.1",
|
||||
"@nuxtjs/friendly-errors-webpack-plugin": "^2.0.2",
|
||||
"babel-loader": "^8.0.4",
|
||||
"cache-loader": "^1.2.2",
|
||||
"caniuse-lite": "^1.0.30000898",
|
||||
"chalk": "^2.4.1",
|
||||
"chokidar": "^2.0.4",
|
||||
"consola": "^1.4.4",
|
||||
"css-loader": "^1.0.0",
|
||||
"cssnano": "^4.1.7",
|
||||
"file-loader": "^2.0.0",
|
||||
"fs-extra": "^7.0.0",
|
||||
"glob": "^7.1.3",
|
||||
"hash-sum": "^1.0.2",
|
||||
"html-minifier": "^3.5.20",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"lodash": "^4.17.11",
|
||||
"memory-fs": "^0.4.1",
|
||||
"mini-css-extract-plugin": "^0.4.4",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.1",
|
||||
"pify": "^4.0.1",
|
||||
"postcss": "^7.0.5",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-import-resolver": "^1.1.0",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-preset-env": "^6.2.0",
|
||||
"postcss-url": "^8.0.0",
|
||||
"serialize-javascript": "^1.5.0",
|
||||
"style-resources-loader": "^1.2.1",
|
||||
"terser-webpack-plugin": "^1.1.0",
|
||||
"thread-loader": "^1.2.0",
|
||||
"time-fix-plugin": "^2.0.4",
|
||||
"upath": "^1.1.0",
|
||||
"url-loader": "^1.1.2",
|
||||
"vue-loader": "^15.4.2",
|
||||
"webpack": "^4.22.0",
|
||||
"webpack-bundle-analyzer": "^3.0.3",
|
||||
"webpack-dev-middleware": "^3.4.0",
|
||||
"webpack-hot-middleware": "^2.24.3",
|
||||
"webpack-node-externals": "^1.7.2",
|
||||
"webpackbar": "^2.6.3"
|
||||
"upath": "^1.1.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@ -1,66 +1,50 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import pify from 'pify'
|
||||
import uniqBy from 'lodash/uniqBy'
|
||||
import map from 'lodash/map'
|
||||
import debounce from 'lodash/debounce'
|
||||
import concat from 'lodash/concat'
|
||||
import omit from 'lodash/omit'
|
||||
import uniq from 'lodash/uniq'
|
||||
import template from 'lodash/template'
|
||||
import values from 'lodash/values'
|
||||
import chokidar from 'chokidar'
|
||||
import fsExtra from 'fs-extra'
|
||||
import hash from 'hash-sum'
|
||||
import webpack from 'webpack'
|
||||
import serialize from 'serialize-javascript'
|
||||
import MFS from 'memory-fs'
|
||||
import webpackDevMiddleware from 'webpack-dev-middleware'
|
||||
import webpackHotMiddleware from 'webpack-hot-middleware'
|
||||
import Glob from 'glob'
|
||||
import upath from 'upath'
|
||||
import consola from 'consola'
|
||||
import fsExtra from 'fs-extra'
|
||||
import Glob from 'glob'
|
||||
import hash from 'hash-sum'
|
||||
import pify from 'pify'
|
||||
import serialize from 'serialize-javascript'
|
||||
import upath from 'upath'
|
||||
|
||||
import concat from 'lodash/concat'
|
||||
import debounce from 'lodash/debounce'
|
||||
import map from 'lodash/map'
|
||||
import omit from 'lodash/omit'
|
||||
import template from 'lodash/template'
|
||||
import uniq from 'lodash/uniq'
|
||||
import uniqBy from 'lodash/uniqBy'
|
||||
import values from 'lodash/values'
|
||||
|
||||
import devalue from '@nuxtjs/devalue'
|
||||
|
||||
import {
|
||||
Options,
|
||||
BuildContext,
|
||||
r,
|
||||
wp,
|
||||
wChunk,
|
||||
createRoutes,
|
||||
parallel,
|
||||
sequence,
|
||||
relativeTo,
|
||||
waitFor,
|
||||
determineGlobals,
|
||||
stripWhitespace
|
||||
} from '@nuxt/common'
|
||||
|
||||
import PerfLoader from './webpack/utils/perf-loader'
|
||||
import ClientWebpackConfig from './webpack/client'
|
||||
import ServerWebpackConfig from './webpack/server'
|
||||
|
||||
const glob = pify(Glob)
|
||||
|
||||
export default class Builder {
|
||||
constructor(nuxt) {
|
||||
constructor(nuxt, bundleBuilder) {
|
||||
this.nuxt = nuxt
|
||||
this.isStatic = false // Flag to know if the build is for a generated app
|
||||
this.plugins = []
|
||||
this.options = nuxt.options
|
||||
this.globals = determineGlobals(nuxt.options.globalName, nuxt.options.globals)
|
||||
|
||||
// Fields that set on build
|
||||
this.compilers = []
|
||||
this.compilersWatching = []
|
||||
this.webpackDevMiddleware = null
|
||||
this.webpackHotMiddleware = null
|
||||
this.watchers = {
|
||||
files: null,
|
||||
custom: null,
|
||||
restart: null
|
||||
}
|
||||
this.perfLoader = null
|
||||
|
||||
// Helper to resolve build paths
|
||||
this.relativeToBuild = (...args) =>
|
||||
@ -71,11 +55,7 @@ export default class Builder {
|
||||
// Stop watching on nuxt.close()
|
||||
if (this.options.dev) {
|
||||
this.nuxt.hook('close', () => this.unwatch())
|
||||
}
|
||||
|
||||
// Initialize shared FS and Cache
|
||||
if (this.options.dev) {
|
||||
this.mfs = new MFS()
|
||||
this.nuxt.hook('build:done', () => this.watchClient())
|
||||
}
|
||||
|
||||
if (this.options.build.analyze) {
|
||||
@ -97,6 +77,18 @@ export default class Builder {
|
||||
// TODO: enable again when unsafe concern resolved.(common/options.js:42)
|
||||
// this.nuxt.hook('build:done', () => this.generateConfig())
|
||||
// }
|
||||
|
||||
this.bundleBuilder = this.getBundleBuilder(bundleBuilder)
|
||||
}
|
||||
|
||||
getBundleBuilder(bundleBuilder) {
|
||||
if (typeof bundleBuilder === 'object') {
|
||||
return bundleBuilder
|
||||
} else {
|
||||
const context = new BuildContext(this)
|
||||
const BundleBuilder = typeof bundleBuilder === 'function' ? bundleBuilder : require('@nuxt/webpack')
|
||||
return new BundleBuilder(context)
|
||||
}
|
||||
}
|
||||
|
||||
normalizePlugins() {
|
||||
@ -117,8 +109,28 @@ export default class Builder {
|
||||
)
|
||||
}
|
||||
|
||||
resolvePlugins() {
|
||||
// Check plugins exist then set alias to their real path
|
||||
return Promise.all(this.plugins.map(async (p) => {
|
||||
const ext = path.extname(p.src) ? '' : '{.+([^.]),/index.+([^.])}'
|
||||
const pluginFiles = await glob(`${p.src}${ext}`)
|
||||
|
||||
if (!pluginFiles || pluginFiles.length === 0) {
|
||||
throw new Error(`Plugin not found: ${p.src}`)
|
||||
} else if (pluginFiles.length > 1) {
|
||||
consola.warn({
|
||||
message: `Found ${pluginFiles.length} plugins that match the configuration, suggest to specify extension:`,
|
||||
additional: ` ${pluginFiles.join('\n ')}`,
|
||||
badge: true
|
||||
})
|
||||
}
|
||||
|
||||
p.src = this.relativeToBuild(p.src)
|
||||
}))
|
||||
}
|
||||
|
||||
forGenerate() {
|
||||
this.isStatic = true
|
||||
this.bundleBuilder.forGenerate()
|
||||
}
|
||||
|
||||
async build() {
|
||||
@ -186,8 +198,10 @@ export default class Builder {
|
||||
// Generate routes and interpret the template files
|
||||
await this.generateRoutesAndFiles()
|
||||
|
||||
// Start webpack build
|
||||
await this.webpackBuild()
|
||||
await this.resolvePlugins()
|
||||
|
||||
// Start bundle build: webpack, rollup, parcel...
|
||||
await this.bundleBuilder.build()
|
||||
|
||||
// Flag to set that building is done
|
||||
this._buildStatus = STATUS.BUILD_DONE
|
||||
@ -201,7 +215,7 @@ export default class Builder {
|
||||
async generateRoutesAndFiles() {
|
||||
consola.debug(`Generating nuxt files`)
|
||||
|
||||
this.plugins = this.normalizePlugins()
|
||||
this.plugins.push.apply(this.plugins, this.normalizePlugins())
|
||||
|
||||
// -- Templates --
|
||||
let templatesFiles = Array.from(this.template.templatesFiles)
|
||||
@ -457,177 +471,15 @@ export default class Builder {
|
||||
consola.success('Nuxt files generated')
|
||||
}
|
||||
|
||||
async webpackBuild() {
|
||||
this.perfLoader = new PerfLoader(this.options)
|
||||
|
||||
const compilersOptions = []
|
||||
|
||||
// Client
|
||||
const clientConfig = new ClientWebpackConfig(this).config()
|
||||
compilersOptions.push(clientConfig)
|
||||
|
||||
// Server
|
||||
let serverConfig = null
|
||||
if (this.options.build.ssr) {
|
||||
serverConfig = new ServerWebpackConfig(this).config()
|
||||
compilersOptions.push(serverConfig)
|
||||
}
|
||||
|
||||
// Check plugins exist then set alias to their real path
|
||||
await Promise.all(this.plugins.map(async (p) => {
|
||||
const ext = path.extname(p.src) ? '' : '{.+([^.]),/index.+([^.])}'
|
||||
const pluginFiles = await glob(`${p.src}${ext}`)
|
||||
|
||||
if (!pluginFiles || pluginFiles.length === 0) {
|
||||
throw new Error(`Plugin not found: ${p.src}`)
|
||||
} else if (pluginFiles.length > 1) {
|
||||
consola.warn({
|
||||
message: `Found ${pluginFiles.length} plugins that match the configuration, suggest to specify extension:`,
|
||||
additional: ` ${pluginFiles.join('\n ')}`,
|
||||
badge: true
|
||||
})
|
||||
}
|
||||
|
||||
const src = this.relativeToBuild(p.src)
|
||||
// Client config
|
||||
if (!clientConfig.resolve.alias[p.name]) {
|
||||
clientConfig.resolve.alias[p.name] = src
|
||||
}
|
||||
|
||||
// Server config
|
||||
if (serverConfig && !serverConfig.resolve.alias[p.name]) {
|
||||
// Alias to noop for ssr:false plugins
|
||||
serverConfig.resolve.alias[p.name] = p.ssr ? src : './empty.js'
|
||||
}
|
||||
}))
|
||||
|
||||
// Configure compilers
|
||||
this.compilers = compilersOptions.map((compilersOption) => {
|
||||
const compiler = webpack(compilersOption)
|
||||
|
||||
// In dev, write files in memory FS
|
||||
if (this.options.dev) {
|
||||
compiler.outputFileSystem = this.mfs
|
||||
}
|
||||
|
||||
return compiler
|
||||
})
|
||||
|
||||
// Warmup perfLoader before build
|
||||
if (this.options.build.parallel) {
|
||||
consola.info('Warming up worker pools')
|
||||
this.perfLoader.warmupAll()
|
||||
consola.success('Worker pools ready')
|
||||
}
|
||||
|
||||
// Start Builds
|
||||
const runner = this.options.dev ? parallel : sequence
|
||||
|
||||
await runner(this.compilers, (compiler) => {
|
||||
return this.webpackCompile(compiler)
|
||||
})
|
||||
}
|
||||
|
||||
webpackCompile(compiler) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const name = compiler.options.name
|
||||
|
||||
await this.nuxt.callHook('build:compile', { name, compiler })
|
||||
|
||||
// Load renderer resources after build
|
||||
compiler.hooks.done.tap('load-resources', async (stats) => {
|
||||
await this.nuxt.callHook('build:compiled', {
|
||||
name,
|
||||
compiler,
|
||||
stats
|
||||
})
|
||||
|
||||
// Reload renderer if available
|
||||
this.nuxt.renderer.loadResources(this.mfs || fs)
|
||||
|
||||
// Resolve on next tick
|
||||
process.nextTick(resolve)
|
||||
})
|
||||
|
||||
if (this.options.dev) {
|
||||
// --- Dev Build ---
|
||||
// Client Build, watch is started by dev-middleware
|
||||
if (compiler.options.name === 'client') {
|
||||
return this.webpackDev(compiler)
|
||||
}
|
||||
// Server, build and watch for changes
|
||||
this.compilersWatching.push(
|
||||
compiler.watch(this.options.watchers.webpack, (err) => {
|
||||
/* istanbul ignore if */
|
||||
if (err) return reject(err)
|
||||
})
|
||||
)
|
||||
} else {
|
||||
// --- Production Build ---
|
||||
compiler.run((err, stats) => {
|
||||
/* istanbul ignore next */
|
||||
if (err) {
|
||||
return reject(err)
|
||||
} else if (stats.hasErrors()) {
|
||||
if (this.options.build.quiet === true) {
|
||||
err = stats.toString(this.options.build.stats)
|
||||
}
|
||||
if (!err) {
|
||||
// actual errors will be printed by webpack itself
|
||||
err = 'Nuxt Build Error'
|
||||
}
|
||||
|
||||
return reject(err)
|
||||
}
|
||||
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
webpackDev(compiler) {
|
||||
consola.debug('Adding webpack middleware...')
|
||||
|
||||
// Create webpack dev middleware
|
||||
this.webpackDevMiddleware = pify(
|
||||
webpackDevMiddleware(
|
||||
compiler,
|
||||
Object.assign(
|
||||
{
|
||||
publicPath: this.options.build.publicPath,
|
||||
stats: false,
|
||||
logLevel: 'silent',
|
||||
watchOptions: this.options.watchers.webpack
|
||||
},
|
||||
this.options.build.devMiddleware
|
||||
)
|
||||
)
|
||||
// TODO: remove ignore when generateConfig enabled again
|
||||
async generateConfig() /* istanbul ignore next */ {
|
||||
const config = path.resolve(this.options.buildDir, 'build.config.js')
|
||||
const options = omit(this.options, Options.unsafeKeys)
|
||||
await fsExtra.writeFile(
|
||||
config,
|
||||
`export default ${JSON.stringify(options, null, ' ')}`,
|
||||
'utf8'
|
||||
)
|
||||
|
||||
this.webpackDevMiddleware.close = pify(this.webpackDevMiddleware.close)
|
||||
|
||||
this.webpackHotMiddleware = pify(
|
||||
webpackHotMiddleware(
|
||||
compiler,
|
||||
Object.assign(
|
||||
{
|
||||
log: false,
|
||||
heartbeat: 10000
|
||||
},
|
||||
this.options.build.hotMiddleware
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// Inject to renderer instance
|
||||
if (this.nuxt.renderer) {
|
||||
this.nuxt.renderer.webpackDevMiddleware = this.webpackDevMiddleware
|
||||
this.nuxt.renderer.webpackHotMiddleware = this.webpackHotMiddleware
|
||||
}
|
||||
|
||||
// Start watching client files
|
||||
this.watchClient()
|
||||
}
|
||||
|
||||
watchClient() {
|
||||
@ -694,24 +546,10 @@ export default class Builder {
|
||||
}
|
||||
}
|
||||
|
||||
this.compilersWatching.forEach(watching => watching.close())
|
||||
|
||||
// Stop webpack middleware
|
||||
if (this.webpackDevMiddleware) {
|
||||
await this.webpackDevMiddleware.close()
|
||||
if (this.bundleBuilder.unwatch) {
|
||||
await this.bundleBuilder.unwatch()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove ignore when generateConfig enabled again
|
||||
async generateConfig() /* istanbul ignore next */ {
|
||||
const config = path.resolve(this.options.buildDir, 'build.config.js')
|
||||
const options = omit(this.options, Options.unsafeKeys)
|
||||
await fsExtra.writeFile(
|
||||
config,
|
||||
`export default ${JSON.stringify(options, null, ' ')}`,
|
||||
'utf8'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const STATUS = {
|
||||
|
@ -1,2 +1 @@
|
||||
export { default as Builder } from './builder'
|
||||
export { default as Generator } from './generator'
|
||||
|
@ -5,7 +5,8 @@ import { loadNuxtConfig } from '../common/utils'
|
||||
|
||||
export default async function build() {
|
||||
const { Nuxt } = await import('@nuxt/core')
|
||||
const { Builder, Generator } = await import('@nuxt/builder')
|
||||
const { Builder } = await import('@nuxt/builder')
|
||||
const { Generator } = await import('@nuxt/generator')
|
||||
|
||||
const argv = parseArgs(process.argv.slice(2), {
|
||||
alias: {
|
||||
|
@ -5,7 +5,8 @@ import { loadNuxtConfig } from '../common/utils'
|
||||
|
||||
export default async function generate() {
|
||||
const { Nuxt } = await import('@nuxt/core')
|
||||
const { Builder, Generator } = await import('@nuxt/builder')
|
||||
const { Builder } = await import('@nuxt/builder')
|
||||
const { Generator } = await import('@nuxt/generator')
|
||||
|
||||
const argv = parseArgs(process.argv.slice(2), {
|
||||
alias: {
|
||||
|
8
packages/common/src/build/context.js
Normal file
8
packages/common/src/build/context.js
Normal file
@ -0,0 +1,8 @@
|
||||
export default class BuildContext {
|
||||
constructor(builder) {
|
||||
this.nuxt = builder.nuxt
|
||||
this.options = builder.nuxt.options
|
||||
this.isStatic = false
|
||||
this.plugins = builder.plugins
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
export { default as Modes } from './modes'
|
||||
export { default as NuxtConfig } from './nuxt.config'
|
||||
export { default as Options } from './options'
|
||||
export { default as BuildContext } from './build/context'
|
||||
export * from './utils'
|
||||
|
3
packages/generator/package.js
Normal file
3
packages/generator/package.js
Normal file
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
build: true
|
||||
}
|
20
packages/generator/package.json
Normal file
20
packages/generator/package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@nuxt/generator",
|
||||
"version": "2.2.0",
|
||||
"repository": "nuxt/nuxt.js",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"main": "dist/generator.js",
|
||||
"dependencies": {
|
||||
"@nuxt/common": "^2.2.0",
|
||||
"chalk": "^2.4.1",
|
||||
"consola": "^1.4.4",
|
||||
"fs-extra": "^7.0.0",
|
||||
"html-minifier": "^3.5.20"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import path from 'path'
|
||||
import htmlMinifier from 'html-minifier'
|
||||
import Chalk from 'chalk'
|
||||
import fsExtra from 'fs-extra'
|
||||
import consola from 'consola'
|
||||
import fsExtra from 'fs-extra'
|
||||
import htmlMinifier from 'html-minifier'
|
||||
|
||||
import { flatRoutes, isUrl, promisifyRoute, waitFor, isString } from '@nuxt/common'
|
||||
|
1
packages/generator/src/index.js
Normal file
1
packages/generator/src/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default as Generator } from './generator'
|
3
packages/webpack/package.js
Normal file
3
packages/webpack/package.js
Normal file
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
build: true
|
||||
}
|
53
packages/webpack/package.json
Normal file
53
packages/webpack/package.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "@nuxt/webpack",
|
||||
"version": "2.2.0",
|
||||
"repository": "nuxt/nuxt.js",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"main": "dist/webpack.js",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.1.2",
|
||||
"@babel/polyfill": "^7.0.0",
|
||||
"@nuxt/common": "^2.2.0",
|
||||
"@nuxtjs/babel-preset-app": "^0.7.0",
|
||||
"@nuxtjs/friendly-errors-webpack-plugin": "^2.0.2",
|
||||
"babel-loader": "^8.0.4",
|
||||
"cache-loader": "^1.2.2",
|
||||
"caniuse-lite": "^1.0.30000898",
|
||||
"chalk": "^2.4.1",
|
||||
"consola": "^1.4.4",
|
||||
"css-loader": "^1.0.0",
|
||||
"cssnano": "^4.1.7",
|
||||
"file-loader": "^2.0.0",
|
||||
"fs-extra": "^7.0.0",
|
||||
"hash-sum": "^1.0.2",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"lodash": "^4.17.11",
|
||||
"memory-fs": "^0.4.1",
|
||||
"mini-css-extract-plugin": "^0.4.4",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.1",
|
||||
"postcss": "^7.0.5",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-import-resolver": "^1.1.0",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-preset-env": "^6.2.0",
|
||||
"postcss-url": "^8.0.0",
|
||||
"style-resources-loader": "^1.2.1",
|
||||
"terser-webpack-plugin": "^1.1.0",
|
||||
"thread-loader": "^1.2.0",
|
||||
"time-fix-plugin": "^2.0.4",
|
||||
"url-loader": "^1.1.2",
|
||||
"vue-loader": "^15.4.2",
|
||||
"webpack": "^4.22.0",
|
||||
"webpack-bundle-analyzer": "^3.0.3",
|
||||
"webpack-dev-middleware": "^3.4.0",
|
||||
"webpack-hot-middleware": "^2.24.3",
|
||||
"webpack-node-externals": "^1.7.2",
|
||||
"webpackbar": "^2.6.3"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
205
packages/webpack/src/builder.js
Normal file
205
packages/webpack/src/builder.js
Normal file
@ -0,0 +1,205 @@
|
||||
import fs from 'fs'
|
||||
import pify from 'pify'
|
||||
import webpack from 'webpack'
|
||||
import MFS from 'memory-fs'
|
||||
import webpackDevMiddleware from 'webpack-dev-middleware'
|
||||
import webpackHotMiddleware from 'webpack-hot-middleware'
|
||||
import consola from 'consola'
|
||||
|
||||
import {
|
||||
parallel,
|
||||
sequence
|
||||
} from '@nuxt/common'
|
||||
|
||||
import { ClientConfig, ServerConfig, PerfLoader } from './config'
|
||||
|
||||
export default class WebpackBuilder {
|
||||
constructor(context) {
|
||||
this.context = context
|
||||
// Fields that set on build
|
||||
this.compilers = []
|
||||
this.compilersWatching = []
|
||||
this.webpackDevMiddleware = null
|
||||
this.webpackHotMiddleware = null
|
||||
this.perfLoader = null
|
||||
|
||||
// Initialize shared FS and Cache
|
||||
if (this.context.options.dev) {
|
||||
this.mfs = new MFS()
|
||||
}
|
||||
}
|
||||
|
||||
async build() {
|
||||
const options = this.context.options
|
||||
|
||||
this.perfLoader = new PerfLoader(options)
|
||||
|
||||
const compilersOptions = []
|
||||
|
||||
// Client
|
||||
const clientConfig = new ClientConfig(this).config()
|
||||
compilersOptions.push(clientConfig)
|
||||
|
||||
// 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]) {
|
||||
clientConfig.resolve.alias[p.name] = p.src
|
||||
}
|
||||
|
||||
// Server config
|
||||
if (serverConfig && !serverConfig.resolve.alias[p.name]) {
|
||||
// Alias to noop for ssr:false plugins
|
||||
serverConfig.resolve.alias[p.name] = p.ssr ? p.src : './empty.js'
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
})
|
||||
|
||||
// Warmup perfLoader before build
|
||||
if (options.build.parallel) {
|
||||
consola.info('Warming up worker pools')
|
||||
this.perfLoader.warmupAll()
|
||||
consola.success('Worker pools ready')
|
||||
}
|
||||
|
||||
// Start Builds
|
||||
const runner = options.dev ? parallel : sequence
|
||||
|
||||
await runner(this.compilers, (compiler) => {
|
||||
return this.webpackCompile(compiler)
|
||||
})
|
||||
}
|
||||
|
||||
webpackCompile(compiler) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const name = compiler.options.name
|
||||
const { nuxt, options } = this.context
|
||||
|
||||
await nuxt.callHook('build:compile', { name, compiler })
|
||||
|
||||
// Load renderer resources after build
|
||||
compiler.hooks.done.tap('load-resources', async (stats) => {
|
||||
await nuxt.callHook('build:compiled', {
|
||||
name,
|
||||
compiler,
|
||||
stats
|
||||
})
|
||||
|
||||
// Reload renderer if available
|
||||
nuxt.renderer.loadResources(this.mfs || fs)
|
||||
|
||||
// Resolve on next tick
|
||||
process.nextTick(resolve)
|
||||
})
|
||||
|
||||
if (options.dev) {
|
||||
// --- Dev Build ---
|
||||
// Client Build, watch is started by dev-middleware
|
||||
if (compiler.options.name === 'client') {
|
||||
return this.webpackDev(compiler)
|
||||
}
|
||||
// Server, build and watch for changes
|
||||
this.compilersWatching.push(
|
||||
compiler.watch(options.watchers.webpack, (err) => {
|
||||
/* istanbul ignore if */
|
||||
if (err) return reject(err)
|
||||
})
|
||||
)
|
||||
} else {
|
||||
// --- Production Build ---
|
||||
compiler.run((err, stats) => {
|
||||
/* istanbul ignore next */
|
||||
if (err) {
|
||||
return reject(err)
|
||||
} else if (stats.hasErrors()) {
|
||||
if (options.build.quiet === true) {
|
||||
err = stats.toString(options.build.stats)
|
||||
}
|
||||
if (!err) {
|
||||
// actual errors will be printed by webpack itself
|
||||
err = 'Nuxt Build Error'
|
||||
}
|
||||
|
||||
return reject(err)
|
||||
}
|
||||
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
webpackDev(compiler) {
|
||||
consola.debug('Adding webpack middleware...')
|
||||
|
||||
const { nuxt: { renderer }, options } = this.context
|
||||
|
||||
// Create webpack dev middleware
|
||||
this.webpackDevMiddleware = pify(
|
||||
webpackDevMiddleware(
|
||||
compiler,
|
||||
Object.assign(
|
||||
{
|
||||
publicPath: options.build.publicPath,
|
||||
stats: false,
|
||||
logLevel: 'silent',
|
||||
watchOptions: options.watchers.webpack
|
||||
},
|
||||
options.build.devMiddleware
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
this.webpackDevMiddleware.close = pify(this.webpackDevMiddleware.close)
|
||||
|
||||
this.webpackHotMiddleware = pify(
|
||||
webpackHotMiddleware(
|
||||
compiler,
|
||||
Object.assign(
|
||||
{
|
||||
log: false,
|
||||
heartbeat: 10000
|
||||
},
|
||||
options.build.hotMiddleware
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// Inject to renderer instance
|
||||
if (renderer) {
|
||||
renderer.webpackDevMiddleware = this.webpackDevMiddleware
|
||||
renderer.webpackHotMiddleware = this.webpackHotMiddleware
|
||||
}
|
||||
}
|
||||
|
||||
async unwatch() {
|
||||
for (const watching of this.compilersWatching) {
|
||||
watching.close()
|
||||
}
|
||||
// Stop webpack middleware
|
||||
if (this.webpackDevMiddleware) {
|
||||
await this.webpackDevMiddleware.close()
|
||||
}
|
||||
}
|
||||
|
||||
forGenerate() {
|
||||
this.context.isStatic = true
|
||||
}
|
||||
}
|
@ -18,9 +18,9 @@ export default class WebpackBaseConfig {
|
||||
this.name = options.name
|
||||
this.isServer = options.isServer
|
||||
this.builder = builder
|
||||
this.nuxt = this.builder.nuxt
|
||||
this.isStatic = builder.isStatic
|
||||
this.options = builder.options
|
||||
this.nuxt = builder.context.nuxt
|
||||
this.isStatic = builder.context.isStatic
|
||||
this.options = builder.context.options
|
||||
this.spinner = builder.spinner
|
||||
this.loaders = this.options.build.loaders
|
||||
}
|
||||
@ -117,7 +117,7 @@ export default class WebpackBaseConfig {
|
||||
rules() {
|
||||
const styleLoader = new StyleLoader(
|
||||
this.options,
|
||||
this.builder.nuxt,
|
||||
this.nuxt,
|
||||
{ isServer: this.isServer }
|
||||
)
|
||||
|
3
packages/webpack/src/config/index.js
Normal file
3
packages/webpack/src/config/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
export { default as ClientConfig } from './client'
|
||||
export { default as ServerConfig } from './server'
|
||||
export { default as PerfLoader } from './utils/perf-loader'
|
@ -3,10 +3,10 @@ import fs from 'fs'
|
||||
import webpack from 'webpack'
|
||||
import nodeExternals from 'webpack-node-externals'
|
||||
|
||||
import BaseConfig from './base'
|
||||
import WebpackBaseConfig from './base'
|
||||
import VueSSRServerPlugin from './plugins/vue/server'
|
||||
|
||||
export default class WebpackServerConfig extends BaseConfig {
|
||||
export default class WebpackServerConfig extends WebpackBaseConfig {
|
||||
constructor(builder) {
|
||||
super(builder, { name: 'server', isServer: true })
|
||||
}
|
1
packages/webpack/src/index.js
Normal file
1
packages/webpack/src/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './builder'
|
@ -1,5 +1,5 @@
|
||||
import consola from 'consola'
|
||||
import { Builder, getPort, loadFixture, Nuxt, rp } from '../utils'
|
||||
import { Builder, WebpackBuilder, getPort, loadFixture, Nuxt, rp } from '../utils'
|
||||
|
||||
let port
|
||||
const url = route => 'http://localhost:' + port + route
|
||||
@ -45,7 +45,7 @@ describe('basic dev', () => {
|
||||
}
|
||||
})
|
||||
nuxt = new Nuxt(config)
|
||||
builder = new Builder(nuxt)
|
||||
builder = new Builder(nuxt, WebpackBuilder)
|
||||
await builder.build()
|
||||
port = await getPort()
|
||||
await nuxt.listen(port, 'localhost')
|
||||
|
@ -11,6 +11,7 @@ const url = route => 'http://localhost:' + port + route
|
||||
const rootDir = resolve(__dirname, '..', 'fixtures/basic')
|
||||
const distDir = resolve(rootDir, '.nuxt-generate')
|
||||
|
||||
let builder
|
||||
let server = null
|
||||
let generator = null
|
||||
let pathsBefore
|
||||
@ -29,7 +30,7 @@ describe('basic generate', () => {
|
||||
writeFileSync(changedFileName, '')
|
||||
})
|
||||
|
||||
const builder = new Builder(nuxt)
|
||||
builder = new Builder(nuxt)
|
||||
builder.build = jest.fn()
|
||||
generator = new Generator(nuxt, builder)
|
||||
|
||||
@ -45,8 +46,8 @@ describe('basic generate', () => {
|
||||
})
|
||||
|
||||
test('Check builder', () => {
|
||||
expect(generator.builder.isStatic).toBe(true)
|
||||
expect(generator.builder.build).toHaveBeenCalledTimes(1)
|
||||
expect(builder.bundleBuilder.context.isStatic).toBe(true)
|
||||
expect(builder.build).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
test('Check ready hook called', () => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
import path from 'path'
|
||||
import PerfLoader from '../../packages/builder/src/webpack/utils/perf-loader'
|
||||
import PerfLoader from '../../packages/webpack/src/config/utils/perf-loader'
|
||||
|
||||
describe('webpack configuration', () => {
|
||||
test('performance loader', () => {
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import klawSync from 'klaw-sync'
|
||||
import { waitFor } from '../../packages/common/src/utils'
|
||||
export { default as getPort } from 'get-port'
|
||||
|
@ -5,7 +5,9 @@ import { defaultsDeep } from 'lodash'
|
||||
|
||||
export { version } from '../../packages/core/package.json'
|
||||
export { Nuxt } from '../../packages/core/src/index'
|
||||
export { Builder, Generator } from '../../packages/builder/src/index'
|
||||
export { Builder } from '../../packages/builder/src/index'
|
||||
export { Generator } from '../../packages/generator/src/index'
|
||||
export { default as WebpackBuilder } from '../../packages/webpack/src/index'
|
||||
export * from '../../packages/common/src/index'
|
||||
|
||||
export const loadFixture = async function (fixture, overrides) {
|
||||
|
Loading…
Reference in New Issue
Block a user