refactor: style loader and postcss config

This commit is contained in:
Clark Du 2018-03-23 12:09:03 +08:00 committed by Pooya Parsa
parent 3aa8ef6ee2
commit 67d4d5378b
3 changed files with 192 additions and 131 deletions

View File

@ -9,7 +9,7 @@ import VueLoader from 'vue-loader'
import { isUrl, urlJoin } from '../../common/utils'
import customLoaders from './loaders'
import createStyleLoader from './utils/style-loader'
import StyleLoader from './utils/style-loader'
import WarnFixPlugin from './plugins/warnfix'
import ProgressPlugin from './plugins/progress'
import StatsPlugin from './plugins/stats'
@ -128,7 +128,11 @@ export default class WebpackBaseConfig {
}
rules() {
const styleLoader = createStyleLoader({ isServer: this.isServer })
const styleLoader = new StyleLoader(
this.options,
this.builder.nuxt,
{ isServer: this.isServer }
)
return [
{
@ -146,26 +150,26 @@ export default class WebpackBaseConfig {
},
{
test: /\.css$/,
use: styleLoader.call(this.builder, 'css')
use: styleLoader.apply('css')
},
{
test: /\.less$/,
use: styleLoader.call(this.builder, 'less', 'less-loader')
use: styleLoader.apply('less', 'less-loader')
},
{
test: /\.sass$/,
use: styleLoader.call(this.builder, 'sass', {
use: styleLoader.apply('sass', {
loader: 'sass-loader',
options: { indentedSyntax: true }
})
},
{
test: /\.scss$/,
use: styleLoader.call(this.builder, 'scss', 'sass-loader')
use: styleLoader.apply('scss', 'sass-loader')
},
{
test: /\.styl(us)?$/,
use: styleLoader.call(this.builder, 'stylus', 'stylus-loader')
use: styleLoader.apply('stylus', 'stylus-loader')
},
{
test: /\.(png|jpe?g|gif|svg)$/,

View File

@ -6,88 +6,114 @@ import createResolver from 'postcss-import-resolver'
import { isPureObject } from '../../../common/utils'
export default function postcssConfig() {
let config = ـ.cloneDeep(this.options.build.postcss)
/* istanbul ignore if */
if (!config) {
return false
export default class PostcssConfig {
constructor(options, nuxt) {
this.nuxt = nuxt
this.postcss = options.build.postcss
this.srcDir = options.srcDir
this.rootDir = options.rootDir
this.cssSourceMap = options.build.cssSourceMap
this.modulesDir = options.modulesDir
}
// Search for postCSS config file and use it if exists
// https://github.com/michael-ciniawsky/postcss-load-config
for (let dir of [this.options.srcDir, this.options.rootDir]) {
for (let file of [
'postcss.config.js',
'.postcssrc.js',
'.postcssrc',
'.postcssrc.json',
'.postcssrc.yaml'
]) {
if (fs.existsSync(path.resolve(dir, file))) {
const postcssConfigPath = path.resolve(dir, file)
return {
sourceMap: this.options.build.cssSourceMap,
config: {
path: postcssConfigPath
get defaultConfig() {
return {
useConfigFile: false,
sourceMap: this.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)
},
modules: [
this.srcDir,
this.rootDir,
...this.modulesDir
]
})
},
// https://github.com/postcss/postcss-url
'postcss-url': {},
// http://cssnext.io/postcss
'postcss-cssnext': {}
}
}
}
configFromFile() {
// Search for postCSS config file and use it if exists
// https://github.com/michael-ciniawsky/postcss-load-config
for (let dir of [this.srcDir, this.rootDir]) {
for (let file of [
'postcss.config.js',
'.postcssrc.js',
'.postcssrc',
'.postcssrc.json',
'.postcssrc.yaml'
]) {
if (fs.existsSync(path.resolve(dir, file))) {
const postcssConfigPath = path.resolve(dir, file)
return {
sourceMap: this.cssSourceMap,
config: {
path: postcssConfigPath
}
}
}
}
}
}
// Normalize
if (Array.isArray(config)) {
config = { plugins: config }
normalize(config) {
if (Array.isArray(config)) {
config = { plugins: config }
}
return config
}
// Apply default plugins
if (isPureObject(config)) {
config = Object.assign(
{
useConfigFile: false,
sourceMap: this.options.build.cssSourceMap,
plugins: {
// https://github.com/postcss/postcss-import
'postcss-import': {
resolve: createResolver({
alias: {
'~': path.join(this.options.srcDir),
'~~': path.join(this.options.rootDir),
'@': path.join(this.options.srcDir),
'@@': path.join(this.options.rootDir)
},
modules: [
this.options.srcDir,
this.options.rootDir,
...this.options.modulesDir
]
})
},
// https://github.com/postcss/postcss-url
'postcss-url': {},
// http://cssnext.io/postcss
'postcss-cssnext': {}
}
},
config
)
loadPlugins(config) {
const plugins = config.plugins
if (isPureObject(plugins)) {
// Map postcss plugins into instances on object mode once
config.plugins = Object.keys(plugins)
.map(p => {
const plugin = this.nuxt.requireModule(p)
const opts = plugins[p]
if (opts === false) return // Disabled
const instance = plugin(opts)
return instance
})
.filter(e => e)
}
}
// Map postcss plugins into instances on object mode once
if (isPureObject(config) && isPureObject(config.plugins)) {
config.plugins = Object.keys(config.plugins)
.map(p => {
const plugin = this.nuxt.requireModule(p)
const opts = config.plugins[p]
if (opts === false) return // Disabled
const instance = plugin(opts)
return instance
})
.filter(e => e)
}
config() {
/* istanbul ignore if */
if (!this.postcss) {
return false
}
return config
let config = this.configFromFile()
if (config) {
return config
}
config = this.normalize(ـ.cloneDeep(this.postcss))
// Apply default plugins
if (isPureObject(config)) {
config = ـ.defaults(config, this.defaultConfig)
this.loadPlugins(config)
}
return config
}
}

View File

@ -2,88 +2,119 @@ import path from 'path'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import postcssConfig from './postcss'
import PostcssConfig from './postcss'
export default ({ isServer }) => {
return function styleLoader(ext, loaders = []) {
const sourceMap = Boolean(this.options.build.cssSourceMap)
export default class StyleLoader {
constructor(options, nuxt, { isServer }) {
this.isServer = isServer
this.dev = options.dev
this.srcDir = options.srcDir
this.assetsDir = options.dir.assets
this.staticDir = options.dir.static
this.extractCSS = options.build.extractCSS
this.resources = options.build.styleResources
this.sourceMap = Boolean(options.build.cssSourceMap)
// Normalize loaders
loaders = (Array.isArray(loaders) ? loaders : [loaders]).map(loader =>
Object.assign(
{ options: { sourceMap } },
typeof loader === 'string' ? { loader } : loader
)
)
if (options.build.postcss) {
this.postcssConfig = new PostcssConfig(options, nuxt)
}
}
// -- Configure additional loaders --
normalize(loaders) {
loaders = Array.isArray(loaders) ? loaders : [loaders]
return loaders.map(loader => Object.assign(
{ options: { sourceMap: this.sourceMap } },
typeof loader === 'string' ? { loader } : loader
))
}
styleResource(ext, loaders) {
const extResource = this.resources[ext]
// style-resources-loader
// https://github.com/yenshih/style-resources-loader
if (this.options.build.styleResources[ext]) {
const patterns = Array.isArray(this.options.build.styleResources[ext])
? this.options.build.styleResources[ext]
: [this.options.build.styleResources[ext]]
const options = Object.assign(
{},
this.options.build.styleResources.options || {},
{ patterns }
)
if (extResource) {
const patterns = Array.isArray(extResource)
? extResource
: [extResource]
loaders.push({
loader: 'style-resources-loader',
options
options: Object.assign(
{ patterns },
this.resources.options || {}
)
})
}
}
postcss(loaders) {
// postcss-loader
// https://github.com/postcss/postcss-loader
const _postcssConfig = postcssConfig.call(this)
if (_postcssConfig) {
loaders.unshift({
loader: 'postcss-loader',
options: Object.assign({ sourceMap }, _postcssConfig)
})
if (this.postcssConfig) {
const config = this.postcssConfig.config()
if (config) {
loaders.unshift({
loader: 'postcss-loader',
options: Object.assign({ sourceMap: this.sourceMap }, config)
})
}
}
}
css(loaders) {
// css-loader
// https://github.com/webpack-contrib/css-loader
const cssLoaderAlias = {}
cssLoaderAlias[`/${this.options.dir.assets}`] = path.join(this.options.srcDir, this.options.dir.assets)
cssLoaderAlias[`/${this.options.dir.static}`] = path.join(this.options.srcDir, this.options.dir.static)
const cssLoaderAlias = {
[`/${this.assetsDir}`]: path.join(this.srcDir, this.assetsDir),
[`/${this.staticDir}`]: path.join(this.srcDir, this.staticDir)
}
loaders.unshift({
loader: 'css-loader',
options: {
sourceMap,
minimize: !this.options.dev,
sourceMap: this.sourceMap,
minimize: !this.dev,
importLoaders: loaders.length, // Important!
alias: cssLoaderAlias
}
})
}
// -- With extractCSS --
if (this.options.build.extractCSS) {
if (!isServer) {
loaders.unshift(MiniCssExtractPlugin.loader)
if (this.options.dev) {
// css-hot-loader
// https://github.com/shepherdwind/css-hot-loader
loaders.unshift({
loader: 'css-hot-loader',
options: { sourceMap }
})
}
extract(loaders) {
if (this.extractCSS && !this.isServer) {
loaders.unshift(MiniCssExtractPlugin.loader)
if (this.dev) {
// css-hot-loader
// https://github.com/shepherdwind/css-hot-loader
loaders.unshift({
loader: 'css-hot-loader',
options: { sourceMap: this.sourceMap }
})
}
} else {
// Prepare vue-style-loader
// https://github.com/vuejs/vue-style-loader
loaders.unshift({
loader: 'vue-style-loader',
options: { sourceMap }
})
return true
}
}
vueStyle(loaders) {
// https://github.com/vuejs/vue-style-loader
loaders.unshift({
loader: 'vue-style-loader',
options: { sourceMap: this.sourceMap }
})
}
apply(ext, loaders = []) {
// Normalize loaders
loaders = this.normalize(loaders)
// -- Configure additional loaders --
this.styleResource(ext, loaders)
this.postcss(loaders)
this.css(loaders)
if (!this.extract(loaders)) {
this.vueStyle(loaders)
}
return loaders
}
}