Skip to content

Commit

Permalink
feat: experimental dll support
Browse files Browse the repository at this point in the history
`.cache/` should be git ignored in projects.
disabled by default. Can be enabled using `options.build.dll: true`
  • Loading branch information
Pooya Parsa committed Aug 21, 2017
1 parent d1a6b2b commit d7fbe47
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 32 deletions.
49 changes: 45 additions & 4 deletions lib/builder/builder.js
Expand Up @@ -10,11 +10,12 @@ import Tapable from 'tappable'
import MFS from 'memory-fs'
import webpackDevMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'
import { r, wp, wChunk, createRoutes, parallel, relativeTo, isPureObject } from 'utils'
import { r, wp, wChunk, createRoutes, sequence, relativeTo, isPureObject } from 'utils'
import Debug from 'debug'
import Glob from 'glob'
import clientWebpackConfig from './webpack/client.config.js'
import serverWebpackConfig from './webpack/server.config.js'
import dllWebpackConfig from './webpack/dll.config.js'
import vueLoaderConfig from './webpack/vue-loader.config'
import styleLoader from './webpack/style-loader'

Expand Down Expand Up @@ -66,6 +67,29 @@ export default class Builder extends Tapable {
})
}

vendor () {
return [
'vue',
'vue-router',
'vue-meta',
'core-js',
'regenerator-runtime',
'es6-promise',
'babel-runtime',
'vuex'
].concat(this.options.build.vendor).filter(v => v)
}

vendorEntries () {
// Used for dll
const vendor = this.vendor()
const vendorEntries = {}
vendor.forEach(v => {
vendorEntries[v] = [ v ]
})
return vendorEntries
}

forGenerate () {
this.isStatic = true
}
Expand Down Expand Up @@ -361,6 +385,11 @@ export default class Builder extends Tapable {
}
})

// Make a dll plugin after compile to make next dev builds faster
if (this.options.build.dll && this.options.dev) {
compilersOptions.push(dllWebpackConfig.call(this, clientConfig))
}

// Simulate webpack multi compiler interface
// Separate compilers are simpler, safer and faster
this.compiler = { compilers: [] }
Expand All @@ -377,7 +406,7 @@ export default class Builder extends Tapable {
// Initialize compilers
compilersOptions.forEach(compilersOption => {
const compiler = webpack(compilersOption)
if (sharedFS) {
if (sharedFS && !compiler.name.includes('-dll')) {
compiler.outputFileSystem = sharedFS
}
compiler.cache = sharedCache
Expand All @@ -398,6 +427,9 @@ export default class Builder extends Tapable {
if (stats.hasErrors()) {
return
}

// console.log(stats.toString({ chunks: true }))

// Reload renderer if available
if (this.nuxt.renderer) {
this.nuxt.renderer.loadResources(sharedFS || fs)
Expand All @@ -414,12 +446,21 @@ export default class Builder extends Tapable {
await this.applyPluginsAsync('compile', { builder: this, compiler: this.compiler })

// Start Builds
await parallel(this.compiler.compilers, compiler => new Promise((resolve, reject) => {
await sequence(this.compiler.compilers, compiler => new Promise((resolve, reject) => {
if (this.options.dev) {
// --- Dev Build ---
if (compiler.options.name === 'client') {
// Client watch is started by dev-middleware
resolve()
} else if (compiler.options.name.includes('-dll')) {
// DLL builds should run once
compiler.run((err, stats) => {
if (err) {
return reject(err)
}
debug('[DLL] updated')
resolve()
})
} else {
// Build and watch for changes
compiler.watch(this.options.watchers.webpack, (err) => {
Expand All @@ -440,7 +481,7 @@ export default class Builder extends Tapable {
if (err) return console.error(err) // eslint-disable-line no-console

// Show build stats for production
console.log(stats.toString(this.webpackStats))// eslint-disable-line no-console
console.log(stats.toString(this.webpackStats)) // eslint-disable-line no-console

/* istanbul ignore if */
if (stats.hasErrors()) {
Expand Down
5 changes: 3 additions & 2 deletions lib/builder/webpack/base.config.js
Expand Up @@ -12,10 +12,11 @@ import { isUrl, urlJoin } from 'utils'
| webpack config files
|--------------------------------------------------------------------------
*/
export default function webpackBaseConfig ({ isClient, isServer }) {
export default function webpackBaseConfig (name) {
const nodeModulesDir = join(__dirname, '..', 'node_modules')

const config = {
name,
devtool: this.options.dev ? 'cheap-module-source-map' : 'nosources-source-map',
entry: {
app: null
Expand Down Expand Up @@ -63,7 +64,7 @@ export default function webpackBaseConfig ({ isClient, isServer }) {
{
test: /\.vue$/,
loader: 'vue-loader',
options: this.vueLoader({ isClient, isServer })
options: this.vueLoader()
},
{
test: /\.js$/,
Expand Down
51 changes: 31 additions & 20 deletions lib/builder/webpack/client.config.js
Expand Up @@ -7,8 +7,13 @@ import ProgressBarPlugin from 'progress-bar-webpack-plugin'
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'
import MinifyPlugin from 'babel-minify-webpack-plugin'
import { resolve } from 'path'
import { existsSync } from 'fs'
import Debug from 'debug'
import base from './base.config.js'

const debug = Debug('nuxt:build')
debug.color = 2 // Force green color

/*
|--------------------------------------------------------------------------
| Webpack Client Config
Expand All @@ -20,29 +25,16 @@ import base from './base.config.js'
|--------------------------------------------------------------------------
*/
export default function webpackClientConfig () {
let config = base.call(this, { isClient: true })

config.name = 'client'
let config = base.call(this, 'client')

// App entry
config.entry.app = resolve(this.options.buildDir, 'client.js')

// Add vendors
// This vendors should explicitly extracted
// Even if not used in 50% of the chunks!
const vendor = [
'vue',
'vue-router',
'vue-meta',
'core-js',
'regenerator-runtime',
'es6-promise',
'babel-runtime',
'vuex'
].concat(this.options.build.vendor).filter(v => v)

// Extract vendor chunks for better caching
const _this = this
const totalPages = _this.routes ? _this.routes.length : 0
const vendor = this.vendor()

config.plugins.push(
new webpack.optimize.CommonsChunkPlugin({
name: 'common',
Expand All @@ -56,13 +48,11 @@ export default function webpackClientConfig () {
}

// Extract all explicit vendor modules
// Vendor should explicitly extracted even if not used in 50% of the chunks!
if (module.context && vendor.some(v => module.context.includes(v))) {
return true
}

// Total pages
const totalPages = _this.routes ? _this.routes.length : 0

// A module is extracted into the vendor chunk when...
return (
// If it's inside node_modules
Expand Down Expand Up @@ -157,6 +147,27 @@ export default function webpackClientConfig () {
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
)

// DllReferencePlugin
// https://github.com/webpack/webpack/tree/master/examples/dll-user
if (this.options.build.dll) {
const _dlls = []
const vendorEntries = this.vendorEntries()
const dllDir = resolve(this.options.cacheDir, config.name + '-dll')
Object.keys(vendorEntries).forEach(v => {
const dllManifestFile = resolve(dllDir, v + '-manifest.json')
if (existsSync(dllManifestFile)) {
_dlls.push(v)
config.plugins.push(
new webpack.DllReferencePlugin({
// context: this.options.rootDir,
manifest: dllManifestFile // Using full path to allow finding .js dll file
})
)
}
})
debug('[DLL] ' + _dlls.join(','))
}
}

// --------------------------------------
Expand Down
46 changes: 46 additions & 0 deletions lib/builder/webpack/dll.config.js
@@ -0,0 +1,46 @@
import webpack from 'webpack'
import { resolve } from 'path'
import ClientConfig from './client.config'

/*
|--------------------------------------------------------------------------
| Webpack Dll Config
| https://github.com/webpack/webpack/tree/master/examples/dll
|--------------------------------------------------------------------------
*/
export default function webpackDllConfig (_refConfig) {
const refConfig = _refConfig || new ClientConfig()

const name = refConfig.name + '-dll'
const dllDir = resolve(this.options.cacheDir, name)

let config = {
name,
entry: this.vendorEntries(),
// context: this.options.rootDir,
resolve: refConfig.resolve,
target: refConfig.target,
resolveLoader: refConfig.resolveLoader,
module: refConfig.module,
plugins: []
}

config.output = {
path: dllDir,
filename: '[name]_[hash].js',
library: '[name]_[hash]'
}

config.plugins.push(
new webpack.DllPlugin({
// The path to the manifest file which maps between
// modules included in a bundle and the internal IDs
// within that bundle
path: resolve(dllDir, '[name]-manifest.json'),

name: '[name]_[hash]'
})
)

return config
}
4 changes: 1 addition & 3 deletions lib/builder/webpack/server.config.js
Expand Up @@ -12,9 +12,7 @@ import base from './base.config.js'
|--------------------------------------------------------------------------
*/
export default function webpackServerConfig () {
let config = base.call(this, { isServer: true })

config.name = 'server'
let config = base.call(this, 'server')

// env object defined in nuxt.config.js
let env = {}
Expand Down
2 changes: 1 addition & 1 deletion lib/builder/webpack/vue-loader.config.js
@@ -1,4 +1,4 @@
export default function vueLoader ({ isClient }) {
export default function vueLoader () {
// https://vue-loader.vuejs.org/en
const config = {
postcss: this.options.build.postcss,
Expand Down
9 changes: 7 additions & 2 deletions lib/common/options.js
Expand Up @@ -33,7 +33,8 @@ Options.from = function (_options) {
options.rootDir = hasValue(options.rootDir) ? options.rootDir : process.cwd()
options.srcDir = hasValue(options.srcDir) ? resolve(options.rootDir, options.srcDir) : options.rootDir
options.modulesDir = resolve(options.rootDir, hasValue(options.modulesDir) ? options.modulesDir : 'node_modules')
options.buildDir = join(options.rootDir, options.buildDir)
options.buildDir = resolve(options.rootDir, options.buildDir)
options.cacheDir = resolve(options.rootDir, options.cacheDir)

// If app.html is defined, set the template path to the user template
options.appTemplatePath = resolve(options.buildDir, 'views/app.template.html')
Expand Down Expand Up @@ -162,9 +163,11 @@ Options.defaults = {
dev: process.env.NODE_ENV !== 'production',
debug: undefined, // Will be equal to dev if not provided
buildDir: '.nuxt',
cacheDir: '.cache',
nuxtAppDir: resolve(__dirname, '../lib/app/'), // Relative to dist
build: {
analyze: false,
dll: false,
extractCSS: false,
cssSourceMap: undefined,
ssr: undefined,
Expand Down Expand Up @@ -266,7 +269,9 @@ Options.defaults = {
}
},
watchers: {
webpack: {},
webpack: {
ignored: /-dll/
},
chokidar: {}
}
}

0 comments on commit d7fbe47

Please sign in to comment.