refactor: unify context in webpack module (#5054)

This commit is contained in:
Xin Du (Clark) 2019-02-18 17:00:51 +00:00 committed by GitHub
parent 3ed9f3e6a6
commit 9860eb6a7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 194 additions and 163 deletions

View File

@ -6,6 +6,10 @@ export default class BuildContext {
this.isStatic = false
}
get buildOptions() {
return this.options.build
}
get plugins() {
return this._builder.plugins
}

View File

@ -20,4 +20,14 @@ describe('builder: buildContext', () => {
const context = new BuildContext(builder)
expect(context.plugins).toEqual(builder.plugins)
})
test('should return builder build options', () => {
const buildOptions = { id: 'test-build-options' }
const builder = {
plugins: [],
nuxt: { options: { build: buildOptions } }
}
const context = new BuildContext(builder)
expect(context.buildOptions).toEqual(buildOptions)
})
})

View File

@ -19,8 +19,8 @@ import PerfLoader from './utils/perf-loader'
const glob = pify(Glob)
export class WebpackBundler {
constructor(context) {
this.context = context
constructor(buildContext) {
this.buildContext = buildContext
// Fields that set on build
this.compilers = []
this.compilersWatching = []
@ -28,7 +28,7 @@ export class WebpackBundler {
this.hotMiddleware = {}
// Initialize shared MFS for dev
if (this.context.options.dev) {
if (this.buildContext.options.dev) {
this.mfs = new MFS()
// TODO: Enable when async FS required
@ -38,7 +38,7 @@ export class WebpackBundler {
}
async build() {
const { options } = this.context
const { options } = this.buildContext
const compilersOptions = []
@ -60,7 +60,7 @@ export class WebpackBundler {
compilersOptions.push(serverConfig)
}
for (const p of this.context.plugins) {
for (const p of this.buildContext.plugins) {
// Client config
if (!clientConfig.resolve.alias[p.name]) {
clientConfig.resolve.alias[p.name] = p.mode === 'server' ? './empty.js' : p.src
@ -78,7 +78,7 @@ export class WebpackBundler {
}
// Check styleResource existence
const { styleResources } = this.context.options.build
const { styleResources } = this.buildContext.options.build
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.',
@ -86,7 +86,7 @@ export class WebpackBundler {
)
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))
const styleResourceFiles = await glob(path.resolve(this.buildContext.options.rootDir, p))
if (!styleResourceFiles || styleResourceFiles.length === 0) {
throw new Error(`Style Resource not found: ${p}`)
@ -122,7 +122,7 @@ export class WebpackBundler {
async webpackCompile(compiler) {
const { name } = compiler.options
const { nuxt, options } = this.context
const { nuxt, options } = this.buildContext
await nuxt.callHook('build:compile', { name, compiler })
@ -179,7 +179,7 @@ export class WebpackBundler {
consola.debug('Adding webpack middleware...')
const { name } = compiler.options
const { nuxt: { server }, options } = this.context
const { nuxt: { server }, options } = this.buildContext
const { client, ...hotMiddlewareOptions } = options.build.hotMiddleware || {}
// Create webpack dev middleware
@ -255,6 +255,6 @@ export class WebpackBundler {
}
forGenerate() {
this.context.isStatic = true
this.buildContext.isStatic = true
}
}

View File

@ -20,16 +20,9 @@ import WarnFixPlugin from '../plugins/warnfix'
import { reservedVueTags } from '../utils/reserved-tags'
export default class WebpackBaseConfig {
constructor(builder, options) {
this.name = options.name
this.isServer = options.isServer
this.isModern = options.isModern
constructor(builder) {
this.builder = builder
this.nuxt = builder.context.nuxt
this.isStatic = builder.context.isStatic
this.options = builder.context.options
this.loaders = this.options.build.loaders
this.buildMode = this.options.dev ? 'development' : 'production'
this.buildContext = builder.buildContext
this.modulesToTranspile = this.normalizeTranspile()
}
@ -43,17 +36,29 @@ export default class WebpackBaseConfig {
get nuxtEnv() {
return {
isDev: this.options.dev,
isDev: this.dev,
isServer: this.isServer,
isClient: !this.isServer,
isModern: !!this.isModern
}
}
get mode() {
return this.dev ? 'development' : 'production'
}
get dev() {
return this.buildContext.options.dev
}
get loaders() {
return this.buildContext.buildOptions.loaders
}
normalizeTranspile() {
// include SFCs in node_modules
const items = [/\.vue\.js/i]
for (const pattern of this.options.build.transpile) {
for (const pattern of this.buildContext.buildOptions.transpile) {
if (pattern instanceof RegExp) {
items.push(pattern)
} else {
@ -65,7 +70,7 @@ export default class WebpackBaseConfig {
}
getBabelOptions() {
const options = clone(this.options.build.babel)
const options = clone(this.buildContext.buildOptions.babel)
if (typeof options.presets === 'function') {
options.presets = options.presets({ isServer: this.isServer })
@ -86,11 +91,11 @@ export default class WebpackBaseConfig {
}
getFileName(key) {
let fileName = this.options.build.filenames[key]
let fileName = this.buildContext.buildOptions.filenames[key]
if (typeof fileName === 'function') {
fileName = fileName(this.nuxtEnv)
}
if (this.options.dev) {
if (this.dev) {
const hash = /\[(chunkhash|contenthash|hash)(?::(\d+))?]/.exec(fileName)
if (hash) {
consola.warn(`Notice: Please do not use ${hash[1]} in dev mode to prevent memory leak`)
@ -105,11 +110,11 @@ export default class WebpackBaseConfig {
env() {
const env = {
'process.env.NODE_ENV': JSON.stringify(this.buildMode),
'process.mode': JSON.stringify(this.options.mode),
'process.static': this.isStatic
'process.env.NODE_ENV': JSON.stringify(this.mode),
'process.mode': JSON.stringify(this.mode),
'process.static': this.buildContext.isStatic
}
Object.entries(this.options.env).forEach(([key, value]) => {
Object.entries(this.buildContext.options.env).forEach(([key, value]) => {
env['process.env.' + key] =
['boolean', 'number'].includes(typeof value)
? value
@ -119,19 +124,21 @@ export default class WebpackBaseConfig {
}
output() {
const {
options: { buildDir, router },
buildOptions: { publicPath }
} = this.buildContext
return {
path: path.resolve(this.options.buildDir, 'dist', this.isServer ? 'server' : 'client'),
path: path.resolve(buildDir, 'dist', this.isServer ? 'server' : 'client'),
filename: this.getFileName('app'),
futureEmitAssets: true, // TODO: Remove when using webpack 5
chunkFilename: this.getFileName('chunk'),
publicPath: isUrl(this.options.build.publicPath)
? this.options.build.publicPath
: urlJoin(this.options.router.base, this.options.build.publicPath)
publicPath: isUrl(publicPath) ? publicPath : urlJoin(router.base, publicPath)
}
}
optimization() {
const optimization = cloneDeep(this.options.build.optimization)
const optimization = cloneDeep(this.buildContext.buildOptions.optimization)
if (optimization.minimize && optimization.minimizer === undefined) {
optimization.minimizer = this.minimizer()
@ -142,13 +149,14 @@ export default class WebpackBaseConfig {
minimizer() {
const minimizer = []
const { terser, cache } = this.buildContext.buildOptions
// https://github.com/webpack-contrib/terser-webpack-plugin
if (this.options.build.terser) {
if (terser) {
minimizer.push(
new TerserWebpackPlugin(Object.assign({
parallel: true,
cache: this.options.build.cache,
cache,
sourceMap: this.devtool && /source-?map/.test(this.devtool),
extractComments: {
filename: 'LICENSES'
@ -164,7 +172,7 @@ export default class WebpackBaseConfig {
reserved: reservedVueTags
}
}
}, this.options.build.terser))
}, terser))
)
}
@ -172,7 +180,7 @@ export default class WebpackBaseConfig {
}
alias() {
const { srcDir, rootDir, dir: { assets: assetsDir, static: staticDir } } = this.options
const { srcDir, rootDir, dir: { assets: assetsDir, static: staticDir } } = this.buildContext.options
return {
'~': path.join(srcDir),
@ -185,10 +193,9 @@ export default class WebpackBaseConfig {
}
rules() {
const perfLoader = new PerfLoader(this)
const perfLoader = new PerfLoader(this.name, this.buildContext)
const styleLoader = new StyleLoader(
this.options,
this.nuxt,
this.buildContext,
{ isServer: this.isServer, perfLoader }
)
const babelLoader = {
@ -329,25 +336,26 @@ export default class WebpackBaseConfig {
plugins() {
const plugins = []
const { nuxt, buildOptions } = this.buildContext
// Add timefix-plugin before others plugins
if (this.options.dev) {
if (this.dev) {
plugins.push(new TimeFixPlugin())
}
// CSS extraction)
if (this.options.build.extractCSS) {
if (buildOptions.extractCSS) {
plugins.push(new ExtractCssChunksPlugin(Object.assign({
filename: this.getFileName('css'),
chunkFilename: this.getFileName('css'),
// TODO: https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/132
reloadAll: true
}, this.options.build.extractCSS)))
}, buildOptions.extractCSS)))
}
plugins.push(new VueLoader.VueLoaderPlugin())
plugins.push(...(this.options.build.plugins || []))
plugins.push(...(buildOptions.plugins || []))
// Hide warnings about plugins without a default export (#1179)
plugins.push(new WarnFixPlugin())
@ -362,37 +370,38 @@ export default class WebpackBaseConfig {
'profile',
'stats'
],
basic: !this.options.build.quiet && env.minimalCLI,
fancy: !this.options.build.quiet && !env.minimalCLI,
profile: !this.options.build.quiet && this.options.build.profile,
stats: !this.options.build.quiet && !this.options.dev && this.options.build.stats,
basic: !buildOptions.quiet && env.minimalCLI,
fancy: !buildOptions.quiet && !env.minimalCLI,
profile: !buildOptions.quiet && buildOptions.profile,
stats: !buildOptions.quiet && !this.dev && buildOptions.stats,
reporter: {
change: (_, { shortPath }) => {
if (!this.isServer) {
this.nuxt.callHook('bundler:change', shortPath)
nuxt.callHook('bundler:change', shortPath)
}
},
done: (context) => {
if (context.hasErrors) {
this.nuxt.callHook('bundler:error')
done: (buildContext) => {
if (buildContext.hasErrors) {
nuxt.callHook('bundler:error')
}
},
allDone: () => {
this.nuxt.callHook('bundler:done')
nuxt.callHook('bundler:done')
}
}
}))
if (this.options.build.hardSource) {
plugins.push(new HardSourcePlugin(Object.assign({}, this.options.build.hardSource)))
if (buildOptions.hardSource) {
plugins.push(new HardSourcePlugin(Object.assign({}, buildOptions.hardSource)))
}
return plugins
}
extendConfig(config) {
if (typeof this.options.build.extend === 'function') {
const extendedConfig = this.options.build.extend.call(
const { extend } = this.buildContext.buildOptions
if (typeof extend === 'function') {
const extendedConfig = extend.call(
this.builder, config, { loaders: this.loaders, ...this.nuxtEnv }
)
// Only overwrite config when something is returned for backwards compatibility
@ -405,17 +414,17 @@ export default class WebpackBaseConfig {
config() {
// Prioritize nested node_modules in webpack search path (#2558)
const webpackModulesDir = ['node_modules'].concat(this.options.modulesDir)
const webpackModulesDir = ['node_modules'].concat(this.buildContext.options.modulesDir)
const config = {
name: this.name,
mode: this.buildMode,
mode: this.mode,
devtool: this.devtool,
optimization: this.optimization(),
output: this.output(),
performance: {
maxEntrypointSize: 1000 * 1024,
hints: this.options.dev ? false : 'warning'
hints: this.dev ? false : 'warning'
},
resolve: {
extensions: ['.wasm', '.mjs', '.js', '.json', '.vue', '.jsx', '.ts', '.tsx'],

View File

@ -14,12 +14,15 @@ import VueSSRClientPlugin from '../plugins/vue/client'
import WebpackBaseConfig from './base'
export default class WebpackClientConfig extends WebpackBaseConfig {
constructor(builder, options) {
super(builder, options || { name: 'client', isServer: false })
constructor(builder) {
super(builder)
this.name = 'client'
this.isServer = false
this.isModern = false
}
getFileName(...args) {
if (this.options.build.analyze) {
if (this.buildContext.buildOptions.analyze) {
const [key] = args
if (['app', 'chunk'].includes(key)) {
return `${this.isModern ? 'modern-' : ''}[name].js`
@ -44,7 +47,7 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
// Small, known and common modules which are usually used project-wise
// Sum of them may not be more than 244 KiB
if (
this.options.build.splitChunks.commons === true &&
this.buildContext.buildOptions.splitChunks.commons === true &&
optimization.splitChunks.cacheGroups.commons === undefined
) {
optimization.splitChunks.cacheGroups.commons = {
@ -60,14 +63,13 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
minimizer() {
const minimizer = super.minimizer()
const { optimizeCSS } = this.buildContext.buildOptions
// https://github.com/NMFR/optimize-css-assets-webpack-plugin
// https://github.com/webpack-contrib/mini-css-extract-plugin#minimizing-for-production
// TODO: Remove OptimizeCSSAssetsPlugin when upgrading to webpack 5
if (this.options.build.optimizeCSS) {
minimizer.push(
new OptimizeCSSAssetsPlugin(Object.assign({}, this.options.build.optimizeCSS))
)
if (optimizeCSS) {
minimizer.push(new OptimizeCSSAssetsPlugin(Object.assign({}, optimizeCSS)))
}
return minimizer
@ -75,14 +77,15 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
plugins() {
const plugins = super.plugins()
const { buildOptions, options: { appTemplatePath, buildDir, rootDir, modern } } = this.buildContext
// Generate output HTML for SSR
if (this.options.build.ssr) {
if (buildOptions.ssr) {
plugins.push(
new HTMLPlugin({
filename: '../server/index.ssr.html',
template: this.options.appTemplatePath,
minify: this.options.build.html.minify,
template: appTemplatePath,
minify: buildOptions.html.minify,
inject: false // Resources will be injected using bundleRenderer
})
)
@ -91,8 +94,8 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
plugins.push(
new HTMLPlugin({
filename: '../server/index.spa.html',
template: this.options.appTemplatePath,
minify: this.options.build.html.minify,
template: appTemplatePath,
minify: buildOptions.html.minify,
inject: true,
chunksSortMode: 'dependency'
}),
@ -102,53 +105,53 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
new webpack.DefinePlugin(this.env())
)
if (this.options.dev) {
if (this.dev) {
// TODO: webpackHotUpdate is not defined: https://github.com/webpack/webpack/issues/6693
plugins.push(new webpack.HotModuleReplacementPlugin())
}
// Webpack Bundle Analyzer
// https://github.com/webpack-contrib/webpack-bundle-analyzer
if (!this.options.dev && this.options.build.analyze) {
const statsDir = path.resolve(this.options.buildDir, 'stats')
if (!this.dev && buildOptions.analyze) {
const statsDir = path.resolve(buildDir, 'stats')
plugins.push(new BundleAnalyzer.BundleAnalyzerPlugin(Object.assign({
analyzerMode: 'static',
defaultSizes: 'gzip',
generateStatsFile: true,
openAnalyzer: !this.options.build.quiet,
openAnalyzer: !buildOptions.quiet,
reportFilename: path.resolve(statsDir, `${this.name}.html`),
statsFilename: path.resolve(statsDir, `${this.name}.json`)
}, this.options.build.analyze)))
}, buildOptions.analyze)))
}
if (this.options.modern) {
if (modern) {
plugins.push(new ModernModePlugin({
targetDir: path.resolve(this.options.buildDir, 'dist', 'client'),
targetDir: path.resolve(buildDir, 'dist', 'client'),
isModernBuild: this.isModern
}))
}
if (this.options.build.crossorigin) {
if (buildOptions.crossorigin) {
plugins.push(new CorsPlugin({
crossorigin: this.options.build.crossorigin
crossorigin: buildOptions.crossorigin
}))
}
// TypeScript type checker
// Only performs once per client compilation and only if `ts-loader` checker is not used (transpileOnly: true)
if (!this.isModern && this.loaders.ts.transpileOnly && this.options.build.useForkTsChecker) {
const forkTsCheckerResolvedPath = this.nuxt.resolver.resolveModule('fork-ts-checker-webpack-plugin')
if (!this.isModern && this.loaders.ts.transpileOnly && buildOptions.useForkTsChecker) {
const forkTsCheckerResolvedPath = this.buildContext.nuxt.resolver.resolveModule('fork-ts-checker-webpack-plugin')
if (forkTsCheckerResolvedPath) {
const ForkTsCheckerWebpackPlugin = require(forkTsCheckerResolvedPath)
plugins.push(new ForkTsCheckerWebpackPlugin(Object.assign({
vue: true,
tsconfig: path.resolve(this.options.rootDir, 'tsconfig.json'),
tsconfig: path.resolve(rootDir, 'tsconfig.json'),
// https://github.com/Realytics/fork-ts-checker-webpack-plugin#options - tslint: boolean | string - So we set it false if file not found
tslint: (tslintPath => fs.existsSync(tslintPath) && tslintPath)(path.resolve(this.options.rootDir, 'tslint.json')),
tslint: (tslintPath => fs.existsSync(tslintPath) && tslintPath)(path.resolve(rootDir, 'tslint.json')),
formatter: 'codeframe',
logger: consola
}, this.options.build.useForkTsChecker)))
}, buildOptions.useForkTsChecker)))
} else {
consola.warn('You need to install `fork-ts-checker-webpack-plugin` as devDependency to enable TypeScript type checking !')
}
@ -159,8 +162,12 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
config() {
const config = super.config()
const {
options: { router, buildDir },
buildOptions: { hotMiddleware, quiet, friendlyErrors }
} = this.buildContext
const { client = {} } = this.options.build.hotMiddleware || {}
const { client = {} } = hotMiddleware || {}
const { ansiColors, overlayStyles, ...options } = client
const hotMiddlewareClientOptions = {
reload: true,
@ -170,17 +177,17 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
...options,
name: this.name
}
const clientPath = `${this.options.router.base}/__webpack_hmr/${this.name}`
const clientPath = `${router.base}/__webpack_hmr/${this.name}`
const hotMiddlewareClientOptionsStr =
`${querystring.stringify(hotMiddlewareClientOptions)}&path=${clientPath}`.replace(/\/\//g, '/')
// Entry points
config.entry = {
app: [path.resolve(this.options.buildDir, 'client.js')]
app: [path.resolve(buildDir, 'client.js')]
}
// Add HMR support
if (this.options.dev) {
if (this.dev) {
config.entry.app.unshift(
// https://github.com/webpack-contrib/webpack-hot-middleware/issues/53#issuecomment-162823945
'eventsource-polyfill',
@ -190,7 +197,7 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
}
// Add friendly error plugin
if (this.options.dev && !this.options.build.quiet && this.options.build.friendlyErrors) {
if (this.dev && !quiet && friendlyErrors) {
config.plugins.push(
new FriendlyErrorsWebpackPlugin({
clearConsole: false,

View File

@ -2,8 +2,10 @@ import clone from 'lodash/clone'
import WebpackClientConfig from './client'
export default class WebpackModernConfig extends WebpackClientConfig {
constructor(builder) {
super(builder, { name: 'modern', isServer: false, isModern: true })
constructor(...args) {
super(...args)
this.name = 'modern'
this.isModern = true
}
env() {
@ -13,7 +15,7 @@ export default class WebpackModernConfig extends WebpackClientConfig {
}
getBabelOptions() {
const options = clone(this.options.build.babel)
const options = clone(this.buildContext.buildOptions.babel)
options.presets = [
[

View File

@ -9,8 +9,10 @@ import VueSSRServerPlugin from '../plugins/vue/server'
import WebpackBaseConfig from './base'
export default class WebpackServerConfig extends WebpackBaseConfig {
constructor(builder) {
super(builder, { name: 'server', isServer: true })
constructor(...args) {
super(...args)
this.name = 'server'
this.isServer = true
this.whitelist = this.normalizeWhitelist()
}
@ -18,7 +20,7 @@ export default class WebpackServerConfig extends WebpackBaseConfig {
const whitelist = [
/\.(?!js(x|on)?$)/i
]
for (const pattern of this.options.build.transpile) {
for (const pattern of this.buildContext.buildOptions.transpile) {
if (pattern instanceof RegExp) {
whitelist.push(pattern)
} else {
@ -68,7 +70,7 @@ export default class WebpackServerConfig extends WebpackBaseConfig {
target: 'node',
node: false,
entry: {
app: [path.resolve(this.options.buildDir, 'server.js')]
app: [path.resolve(this.buildContext.options.buildDir, 'server.js')]
},
output: Object.assign({}, config.output, {
filename: 'server.js',
@ -85,8 +87,8 @@ export default class WebpackServerConfig extends WebpackBaseConfig {
// https://webpack.js.org/configuration/externals/#externals
// https://github.com/liady/webpack-node-externals
// https://vue-loader.vuejs.org/migrating.html#ssr-externals
if (!this.options.build.standalone) {
this.options.modulesDir.forEach((dir) => {
if (!this.buildContext.buildOptions.standalone) {
this.buildContext.options.modulesDir.forEach((dir) => {
if (fs.existsSync(dir)) {
config.externals.push(
nodeExternals({

View File

@ -6,10 +6,10 @@ import { warmup } from 'thread-loader'
// https://github.com/webpack-contrib/cache-loader
export default class PerfLoader {
constructor(config) {
this.name = config.name
this.options = config.options
this.workerPools = PerfLoader.defaultPools(this.options)
constructor(name, buildContext) {
this.name = name
this.buildContext = buildContext
this.workerPools = PerfLoader.defaultPools({ dev: buildContext.options.dev })
return new Proxy(this, {
get(target, name) {
return target[name] ? target[name] : target.use.bind(target, name)
@ -25,13 +25,13 @@ export default class PerfLoader {
}
}
static warmupAll(options) {
options = PerfLoader.defaultPools(options)
PerfLoader.warmup(options.js, [
static warmupAll({ dev }) {
const pools = PerfLoader.defaultPools({ dev })
PerfLoader.warmup(pools.js, [
require.resolve('babel-loader'),
require.resolve('@babel/preset-env')
])
PerfLoader.warmup(options.css, ['css-loader'])
PerfLoader.warmup(pools.css, ['css-loader'])
}
static warmup(...args) {
@ -41,7 +41,7 @@ export default class PerfLoader {
use(poolName) {
const loaders = []
if (this.options.build.cache) {
if (this.buildContext.buildOptions.cache) {
loaders.push({
loader: 'cache-loader',
options: {
@ -50,7 +50,7 @@ export default class PerfLoader {
})
}
if (this.options.build.parallel) {
if (this.buildContext.buildOptions.parallel) {
const pool = this.workerPools[poolName]
if (pool) {
loaders.push({

View File

@ -19,34 +19,29 @@ export const orderPresets = {
}
export default class PostcssConfig {
constructor(options, nuxt) {
this.nuxt = nuxt
this.dev = options.dev
this.postcss = options.build.postcss
this.srcDir = options.srcDir
this.rootDir = options.rootDir
this.cssSourceMap = options.build.cssSourceMap
this.modulesDir = options.modulesDir
constructor(buildContext) {
this.buildContext = buildContext
}
get postcssOptions() {
return this.buildContext.buildOptions.postcss
}
get defaultConfig() {
const { dev, srcDir, rootDir, modulesDir } = this.buildContext.options
return {
sourceMap: this.cssSourceMap,
sourceMap: this.buildContext.buildOptions.cssSourceMap,
plugins: {
// https://github.com/postcss/postcss-import
'postcss-import': {
resolve: createResolver({
alias: {
'~': path.join(this.srcDir),
'~~': path.join(this.rootDir),
'@': path.join(this.srcDir),
'@@': path.join(this.rootDir)
'~': path.join(srcDir),
'~~': path.join(rootDir),
'@': path.join(srcDir),
'@@': path.join(rootDir)
},
modules: [
this.srcDir,
this.rootDir,
...this.modulesDir
]
modules: [ srcDir, rootDir, ...modulesDir ]
})
},
@ -55,7 +50,7 @@ export default class PostcssConfig {
// https://github.com/csstools/postcss-preset-env
'postcss-preset-env': this.preset || {},
'cssnano': this.dev ? false : { preset: 'default' }
'cssnano': dev ? false : { preset: 'default' }
},
// Array, String or Function
order: 'cssnanoLast'
@ -65,7 +60,8 @@ export default class PostcssConfig {
searchConfigFile() {
// Search for postCSS config file and use it if exists
// https://github.com/michael-ciniawsky/postcss-load-config
for (const dir of [this.srcDir, this.rootDir]) {
const { srcDir, rootDir } = this.buildContext.options
for (const dir of [ srcDir, rootDir ]) {
for (const file of [
'postcss.config.js',
'.postcssrc.js',
@ -82,12 +78,12 @@ export default class PostcssConfig {
}
configFromFile() {
const loaderConfig = (this.postcss && this.postcss.config) || {}
const loaderConfig = (this.postcssOptions && this.postcssOptions.config) || {}
loaderConfig.path = loaderConfig.path || this.searchConfigFile()
if (loaderConfig.path) {
return {
sourceMap: this.cssSourceMap,
sourceMap: this.buildContext.buildOptions.cssSourceMap,
config: loaderConfig
}
}
@ -117,7 +113,7 @@ export default class PostcssConfig {
// Map postcss plugins into instances on object mode once
config.plugins = this.sortPlugins(config)
.map((p) => {
const plugin = this.nuxt.resolver.requireModule(p)
const plugin = this.buildContext.nuxt.resolver.requireModule(p)
const opts = plugins[p]
if (opts === false) {
return // Disabled
@ -130,7 +126,7 @@ export default class PostcssConfig {
config() {
/* istanbul ignore if */
if (!this.postcss) {
if (!this.postcssOptions) {
return false
}
@ -139,7 +135,7 @@ export default class PostcssConfig {
return config
}
config = this.normalize(cloneDeep(this.postcss))
config = this.normalize(cloneDeep(this.postcssOptions))
// Apply default plugins
if (isPureObject(config)) {

View File

@ -6,24 +6,20 @@ import { wrapArray } from '@nuxt/utils'
import PostcssConfig from './postcss'
export default class StyleLoader {
constructor(options, nuxt, { isServer, perfLoader }) {
constructor(buildContext, { isServer, perfLoader }) {
this.buildContext = buildContext
this.isServer = isServer
this.perfLoader = perfLoader
this.rootDir = options.rootDir
this.loaders = {
vueStyle: options.build.loaders.vueStyle,
css: options.build.loaders.css,
cssModules: options.build.loaders.cssModules
}
this.extractCSS = options.build.extractCSS
this.resources = options.build.styleResources
this.sourceMap = Boolean(options.build.cssSourceMap)
if (options.build.postcss) {
this.postcssConfig = new PostcssConfig(options, nuxt)
if (buildContext.options.build.postcss) {
this.postcssConfig = new PostcssConfig(buildContext)
}
}
get extractCSS() {
return this.buildContext.buildOptions.extractCSS
}
get exportOnlyLocals() {
return Boolean(this.isServer && this.extractCSS)
}
@ -34,19 +30,20 @@ export default class StyleLoader {
}
styleResource(ext) {
const extResource = this.resources[ext]
const { buildOptions: { styleResources }, options: { rootDir } } = this.buildContext
const extResource = styleResources[ext]
// style-resources-loader
// https://github.com/yenshih/style-resources-loader
if (!extResource) {
return
}
const patterns = wrapArray(extResource).map(p => path.resolve(this.rootDir, p))
const patterns = wrapArray(extResource).map(p => path.resolve(rootDir, p))
return {
loader: 'style-resources-loader',
options: Object.assign(
{ patterns },
this.resources.options || {}
styleResources.options || {}
)
}
}
@ -66,7 +63,7 @@ export default class StyleLoader {
return {
loader: 'postcss-loader',
options: Object.assign({ sourceMap: this.sourceMap }, config)
options: Object.assign({ sourceMap: this.buildContext.buildOptions.cssSourceMap }, config)
}
}
@ -94,32 +91,34 @@ export default class StyleLoader {
styleLoader() {
return this.extract() || {
loader: 'vue-style-loader',
options: this.loaders.vueStyle
options: this.buildContext.buildOptions.loaders.vueStyle
}
}
apply(ext, loaders = []) {
const { css, cssModules } = this.buildContext.buildOptions.loaders
const customLoaders = [].concat(
this.postcss(),
this.normalize(loaders),
this.styleResource(ext)
).filter(Boolean)
this.loaders.css.importLoaders = this.loaders.cssModules.importLoaders = customLoaders.length
css.importLoaders = cssModules.importLoaders = customLoaders.length
return [
// This matches <style module>
{
resourceQuery: /module/,
use: this.perfLoader.css().concat(
this.cssModules(this.loaders.cssModules),
this.cssModules(cssModules),
customLoaders
)
},
// This matches plain <style> or <style scoped>
{
use: this.perfLoader.css().concat(
this.css(this.loaders.css),
this.css(css),
customLoaders
)
}

View File

@ -46,7 +46,7 @@ describe('basic generate', () => {
})
test('Check builder', () => {
expect(builder.bundleBuilder.context.isStatic).toBe(true)
expect(builder.bundleBuilder.buildContext.isStatic).toBe(true)
expect(builder.build).toHaveBeenCalledTimes(1)
})

View File

@ -15,16 +15,18 @@ describe('webpack configuration', () => {
])
expect(PerfLoader.warmup).toHaveBeenCalledWith(css, ['css-loader'])
const perfLoader = new PerfLoader({
name: 'test-perf',
const perfLoader = new PerfLoader(
'test-perf',
{
options: {
dev: true,
build: {
dev: true
},
buildOptions: {
parallel: true,
cache: true
}
}
})
)
expect(perfLoader.workerPools).toMatchObject({ js, css })
const loaders = perfLoader.use('js')
const cacheDirectory = path.resolve('node_modules/.cache/cache-loader/test-perf')