mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-30 09:27:13 +00:00
feat(webpack, builder): allow extending loader options (#3799)
This commit is contained in:
parent
1e0219543a
commit
c77fa479f6
@ -21,6 +21,7 @@ export default class WebpackBaseConfig {
|
|||||||
this.isStatic = builder.isStatic
|
this.isStatic = builder.isStatic
|
||||||
this.options = builder.options
|
this.options = builder.options
|
||||||
this.spinner = builder.spinner
|
this.spinner = builder.spinner
|
||||||
|
this.loaders = this.options.build.loaders
|
||||||
}
|
}
|
||||||
|
|
||||||
get nuxtEnv() {
|
get nuxtEnv() {
|
||||||
@ -129,25 +130,26 @@ export default class WebpackBaseConfig {
|
|||||||
{
|
{
|
||||||
test: /\.vue$/,
|
test: /\.vue$/,
|
||||||
loader: 'vue-loader',
|
loader: 'vue-loader',
|
||||||
options: Object.assign({
|
options: this.loaders.vue
|
||||||
productionMode: !this.options.dev,
|
|
||||||
transformAssetUrls: {
|
|
||||||
video: 'src',
|
|
||||||
source: 'src',
|
|
||||||
object: 'src',
|
|
||||||
embed: 'src'
|
|
||||||
}
|
|
||||||
}, this.options.build.vueLoader)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.pug$/,
|
test: /\.pug$/,
|
||||||
oneOf: [
|
oneOf: [
|
||||||
{
|
{
|
||||||
resourceQuery: /^\?vue/,
|
resourceQuery: /^\?vue/,
|
||||||
use: ['pug-plain-loader']
|
use: [{
|
||||||
|
loader: 'pug-plain-loader',
|
||||||
|
options: this.loaders.pugPlain
|
||||||
|
}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
use: ['raw-loader', 'pug-plain-loader']
|
use: [
|
||||||
|
'raw-loader',
|
||||||
|
{
|
||||||
|
loader: 'pug-plain-loader',
|
||||||
|
options: this.loaders.pugPlain
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -176,50 +178,60 @@ export default class WebpackBaseConfig {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.less$/,
|
test: /\.less$/,
|
||||||
oneOf: perfLoader.poolOneOf('css', styleLoader.apply('less', 'less-loader'))
|
oneOf: perfLoader.poolOneOf('css', styleLoader.apply('less', {
|
||||||
|
loader: 'less-loader',
|
||||||
|
options: this.loaders.less
|
||||||
|
}))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.sass$/,
|
test: /\.sass$/,
|
||||||
oneOf: perfLoader.poolOneOf('css', styleLoader.apply('sass', {
|
oneOf: perfLoader.poolOneOf('css', styleLoader.apply('sass', {
|
||||||
loader: 'sass-loader',
|
loader: 'sass-loader',
|
||||||
options: { indentedSyntax: true }
|
options: this.loaders.sass
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.scss$/,
|
test: /\.scss$/,
|
||||||
oneOf: perfLoader.poolOneOf('css', styleLoader.apply('scss', 'sass-loader'))
|
oneOf: perfLoader.poolOneOf('css', styleLoader.apply('scss', {
|
||||||
|
loader: 'sass-loader',
|
||||||
|
options: this.loaders.scss
|
||||||
|
}))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.styl(us)?$/,
|
test: /\.styl(us)?$/,
|
||||||
oneOf: perfLoader.poolOneOf('css', styleLoader.apply('stylus', 'stylus-loader'))
|
oneOf: perfLoader.poolOneOf('css', styleLoader.apply('stylus', {
|
||||||
|
loader: 'stylus-loader',
|
||||||
|
options: this.loaders.stylus
|
||||||
|
}))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(png|jpe?g|gif|svg|webp)$/,
|
test: /\.(png|jpe?g|gif|svg|webp)$/,
|
||||||
use: perfLoader.pool('assets', {
|
use: perfLoader.pool('assets', {
|
||||||
loader: 'url-loader',
|
loader: 'url-loader',
|
||||||
options: {
|
options: Object.assign(
|
||||||
limit: 1000, // 1KO
|
this.loaders.imgUrl,
|
||||||
name: this.getFileName('img')
|
{ name: this.getFileName('img') }
|
||||||
}
|
)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
||||||
use: perfLoader.pool('assets', {
|
use: perfLoader.pool('assets', {
|
||||||
loader: 'url-loader',
|
loader: 'url-loader',
|
||||||
options: {
|
options: Object.assign(
|
||||||
limit: 1000, // 1 KO
|
this.loaders.fontUrl,
|
||||||
name: this.getFileName('font')
|
{ name: this.getFileName('font') }
|
||||||
}
|
)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(webm|mp4|ogv)$/,
|
test: /\.(webm|mp4|ogv)$/,
|
||||||
use: perfLoader.pool('assets', {
|
use: perfLoader.pool('assets', {
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
options: {
|
options: Object.assign(
|
||||||
name: this.getFileName('video')
|
this.loaders.file,
|
||||||
}
|
{ name: this.getFileName('video') }
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -274,9 +286,11 @@ export default class WebpackBaseConfig {
|
|||||||
return plugins
|
return plugins
|
||||||
}
|
}
|
||||||
|
|
||||||
customize(config) {
|
extendConfig(config) {
|
||||||
if (typeof this.options.build.extend === 'function') {
|
if (typeof this.options.build.extend === 'function') {
|
||||||
const extendedConfig = this.options.build.extend.call(this.builder, config, this.nuxtEnv)
|
const extendedConfig = this.options.build.extend.call(
|
||||||
|
this.builder, config, { loaders: this.loaders, ...this.nuxtEnv }
|
||||||
|
)
|
||||||
// Only overwrite config when something is returned for backwards compatibility
|
// Only overwrite config when something is returned for backwards compatibility
|
||||||
if (extendedConfig !== undefined) {
|
if (extendedConfig !== undefined) {
|
||||||
return extendedConfig
|
return extendedConfig
|
||||||
@ -312,7 +326,9 @@ export default class WebpackBaseConfig {
|
|||||||
plugins: this.plugins()
|
plugins: this.plugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const extendedConfig = this.extendConfig(config)
|
||||||
|
|
||||||
// Clone deep avoid leaking config between Client and Server
|
// Clone deep avoid leaking config between Client and Server
|
||||||
return _.cloneDeep(config)
|
return _.cloneDeep(extendedConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,8 +96,8 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
|
|||||||
return plugins
|
return plugins
|
||||||
}
|
}
|
||||||
|
|
||||||
customize() {
|
extendConfig() {
|
||||||
const config = super.customize(...arguments)
|
const config = super.extendConfig(...arguments)
|
||||||
|
|
||||||
if (!this.options.dev && !config.optimization.minimizer) {
|
if (!this.options.dev && !config.optimization.minimizer) {
|
||||||
config.optimization.minimizer = []
|
config.optimization.minimizer = []
|
||||||
@ -158,6 +158,6 @@ export default class WebpackClientConfig extends WebpackBaseConfig {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.customize(config)
|
return config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,6 @@ export default class WebpackServerConfig extends BaseConfig {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.customize(config)
|
return config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
|
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
|
||||||
|
|
||||||
import PostcssConfig from './postcss'
|
import PostcssConfig from './postcss'
|
||||||
|
|
||||||
export default class StyleLoader {
|
export default class StyleLoader {
|
||||||
@ -11,6 +9,7 @@ export default class StyleLoader {
|
|||||||
this.srcDir = options.srcDir
|
this.srcDir = options.srcDir
|
||||||
this.assetsDir = options.dir.assets
|
this.assetsDir = options.dir.assets
|
||||||
this.staticDir = options.dir.static
|
this.staticDir = options.dir.static
|
||||||
|
this.loaders = options.build.loaders
|
||||||
this.extractCSS = options.build.extractCSS
|
this.extractCSS = options.build.extractCSS
|
||||||
this.resources = options.build.styleResources
|
this.resources = options.build.styleResources
|
||||||
this.sourceMap = Boolean(options.build.cssSourceMap)
|
this.sourceMap = Boolean(options.build.cssSourceMap)
|
||||||
@ -22,10 +21,7 @@ export default class StyleLoader {
|
|||||||
|
|
||||||
normalize(loaders) {
|
normalize(loaders) {
|
||||||
loaders = Array.isArray(loaders) ? loaders : [loaders]
|
loaders = Array.isArray(loaders) ? loaders : [loaders]
|
||||||
return loaders.map(loader => Object.assign(
|
return loaders.map(loader => (typeof loader === 'string' ? { loader } : loader))
|
||||||
{ options: { sourceMap: this.sourceMap } },
|
|
||||||
typeof loader === 'string' ? { loader } : loader
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
styleResource(ext) {
|
styleResource(ext) {
|
||||||
@ -61,7 +57,7 @@ export default class StyleLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
css(importLoaders, options) {
|
css(options) {
|
||||||
// css-loader
|
// css-loader
|
||||||
// https://github.com/webpack-contrib/css-loader
|
// https://github.com/webpack-contrib/css-loader
|
||||||
const cssLoaderAlias = {
|
const cssLoaderAlias = {
|
||||||
@ -71,14 +67,17 @@ export default class StyleLoader {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
loader: (this.isServer && this.extractCSS) ? 'css-loader/locals' : 'css-loader',
|
loader: (this.isServer && this.extractCSS) ? 'css-loader/locals' : 'css-loader',
|
||||||
options: Object.assign({
|
options: Object.assign(options, {
|
||||||
sourceMap: this.sourceMap,
|
|
||||||
importLoaders: importLoaders,
|
|
||||||
alias: cssLoaderAlias
|
alias: cssLoaderAlias
|
||||||
}, options)
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cssModules(options) {
|
||||||
|
options.modules = true
|
||||||
|
return this.css(options)
|
||||||
|
}
|
||||||
|
|
||||||
extract() {
|
extract() {
|
||||||
if (this.extractCSS && !this.isServer) {
|
if (this.extractCSS && !this.isServer) {
|
||||||
return MiniCssExtractPlugin.loader
|
return MiniCssExtractPlugin.loader
|
||||||
@ -89,7 +88,7 @@ export default class StyleLoader {
|
|||||||
// https://github.com/vuejs/vue-style-loader
|
// https://github.com/vuejs/vue-style-loader
|
||||||
return {
|
return {
|
||||||
loader: 'vue-style-loader',
|
loader: 'vue-style-loader',
|
||||||
options: { sourceMap: this.sourceMap }
|
options: this.loaders.vueStyle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +99,9 @@ export default class StyleLoader {
|
|||||||
this.styleResource(ext)
|
this.styleResource(ext)
|
||||||
).filter(Boolean)
|
).filter(Boolean)
|
||||||
|
|
||||||
|
const { css: cssOptions, cssModules: cssModulesOptions } = this.loaders
|
||||||
|
cssOptions.importLoaders = cssModulesOptions.importLoaders = customLoaders.length
|
||||||
|
|
||||||
const styleLoader = this.extract() || this.vueStyle()
|
const styleLoader = this.extract() || this.vueStyle()
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@ -108,10 +110,7 @@ export default class StyleLoader {
|
|||||||
resourceQuery: /module/,
|
resourceQuery: /module/,
|
||||||
use: [].concat(
|
use: [].concat(
|
||||||
styleLoader,
|
styleLoader,
|
||||||
this.css(customLoaders.length, {
|
this.cssModules(cssModulesOptions),
|
||||||
modules: true,
|
|
||||||
localIdentName: '[local]_[hash:base64:5]'
|
|
||||||
}),
|
|
||||||
customLoaders
|
customLoaders
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -119,7 +118,7 @@ export default class StyleLoader {
|
|||||||
{
|
{
|
||||||
use: [].concat(
|
use: [].concat(
|
||||||
styleLoader,
|
styleLoader,
|
||||||
this.css(customLoaders.length),
|
this.css(cssOptions),
|
||||||
customLoaders
|
customLoaders
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,31 @@ export default {
|
|||||||
font: ({ isDev }) => isDev ? '[path][name].[ext]' : 'fonts/[hash:7].[ext]',
|
font: ({ isDev }) => isDev ? '[path][name].[ext]' : 'fonts/[hash:7].[ext]',
|
||||||
video: ({ isDev }) => isDev ? '[path][name].[ext]' : 'videos/[hash:7].[ext]'
|
video: ({ isDev }) => isDev ? '[path][name].[ext]' : 'videos/[hash:7].[ext]'
|
||||||
},
|
},
|
||||||
|
loaders: {
|
||||||
|
file: {},
|
||||||
|
fontUrl: { limit: 1000 },
|
||||||
|
imgUrl: { limit: 1000 },
|
||||||
|
pugPlain: {},
|
||||||
|
vue: {
|
||||||
|
transformAssetUrls: {
|
||||||
|
video: 'src',
|
||||||
|
source: 'src',
|
||||||
|
object: 'src',
|
||||||
|
embed: 'src'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
css: {},
|
||||||
|
cssModules: {
|
||||||
|
localIdentName: '[local]_[hash:base64:5]'
|
||||||
|
},
|
||||||
|
less: {},
|
||||||
|
sass: {
|
||||||
|
indentedSyntax: true
|
||||||
|
},
|
||||||
|
scss: {},
|
||||||
|
stylus: {},
|
||||||
|
vueStyle: {}
|
||||||
|
},
|
||||||
styleResources: {},
|
styleResources: {},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
optimization: {
|
optimization: {
|
||||||
@ -81,7 +106,6 @@ export default {
|
|||||||
cacheDirectory: undefined
|
cacheDirectory: undefined
|
||||||
},
|
},
|
||||||
transpile: [], // Name of NPM packages to be transpiled
|
transpile: [], // Name of NPM packages to be transpiled
|
||||||
vueLoader: {},
|
|
||||||
postcss: {
|
postcss: {
|
||||||
preset: {
|
preset: {
|
||||||
// https://cssdb.org/#staging-process
|
// https://cssdb.org/#staging-process
|
||||||
|
@ -209,6 +209,22 @@ Options.from = function (_options) {
|
|||||||
options.build.extractCSS = false
|
options.build.extractCSS = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loaders = options.build.loaders
|
||||||
|
const vueLoader = loaders.vue
|
||||||
|
if (vueLoader.productionMode === undefined) {
|
||||||
|
vueLoader.productionMode = !options.dev
|
||||||
|
}
|
||||||
|
const styleLoaders = [
|
||||||
|
'css', 'cssModules', 'less',
|
||||||
|
'sass', 'scss', 'stylus', 'vueStyle'
|
||||||
|
]
|
||||||
|
for (const name of styleLoaders) {
|
||||||
|
const loader = loaders[name]
|
||||||
|
if (loader && loader.sourceMap === undefined) {
|
||||||
|
loader.sourceMap = Boolean(options.build.cssSourceMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// include SFCs in node_modules
|
// include SFCs in node_modules
|
||||||
options.build.transpile = [].concat(options.build.transpile || [])
|
options.build.transpile = [].concat(options.build.transpile || [])
|
||||||
.map(module => module instanceof RegExp ? module : new RegExp(module))
|
.map(module => module instanceof RegExp ? module : new RegExp(module))
|
||||||
|
@ -8,6 +8,8 @@ let nuxt = null
|
|||||||
let builder = null
|
let builder = null
|
||||||
let transpile = null
|
let transpile = null
|
||||||
let output = null
|
let output = null
|
||||||
|
let loadersOptions
|
||||||
|
let vueLoader
|
||||||
|
|
||||||
describe('basic dev', () => {
|
describe('basic dev', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@ -26,11 +28,18 @@ describe('basic dev', () => {
|
|||||||
'vue\\.test\\.js',
|
'vue\\.test\\.js',
|
||||||
/vue-test/
|
/vue-test/
|
||||||
],
|
],
|
||||||
extend({ module: { rules }, output: wpOutput }, { isClient }) {
|
loaders: {
|
||||||
|
cssModules: {
|
||||||
|
localIdentName: '[hash:base64:6]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extend({ module: { rules }, output: wpOutput }, { isClient, loaders }) {
|
||||||
if (isClient) {
|
if (isClient) {
|
||||||
const babelLoader = rules.find(loader => loader.test.test('.jsx'))
|
const babelLoader = rules.find(loader => loader.test.test('.jsx'))
|
||||||
transpile = file => !babelLoader.exclude(file)
|
transpile = file => !babelLoader.exclude(file)
|
||||||
output = wpOutput
|
output = wpOutput
|
||||||
|
loadersOptions = loaders
|
||||||
|
vueLoader = rules.find(loader => loader.test.test('.vue'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,6 +71,17 @@ describe('basic dev', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Config: build.loaders', () => {
|
||||||
|
expect(Object.keys(loadersOptions)).toHaveLength(12)
|
||||||
|
expect(loadersOptions).toHaveProperty(
|
||||||
|
'file', 'fontUrl', 'imgUrl', 'pugPlain', 'vue',
|
||||||
|
'css', 'cssModules', 'less', 'sass', 'scss', 'stylus', 'vueStyle'
|
||||||
|
)
|
||||||
|
const { cssModules, vue } = loadersOptions
|
||||||
|
expect(cssModules.localIdentName).toBe('[hash:base64:6]')
|
||||||
|
expect(vueLoader.options).toBe(vue)
|
||||||
|
})
|
||||||
|
|
||||||
test('/stateless', async () => {
|
test('/stateless', async () => {
|
||||||
const window = await nuxt.renderAndGetWindow(url('/stateless'))
|
const window = await nuxt.renderAndGetWindow(url('/stateless'))
|
||||||
const html = window.document.body.innerHTML
|
const html = window.document.body.innerHTML
|
||||||
|
Loading…
Reference in New Issue
Block a user