feat(style): add style loaders (#50)

* feat(style): add style loaders

* refactor: merge utils/style-loader to style preset

* refactor style.ts

Co-authored-by: Pooya Parsa <pyapar@gmail.com>
This commit is contained in:
Xin Du (Clark) 2020-09-21 10:37:13 +01:00 committed by GitHub
parent db050fd0a2
commit 232d3298b4
4 changed files with 135 additions and 201 deletions

View File

@ -285,10 +285,10 @@ export default () => ({
vueStyle: {} vueStyle: {}
} as Loaders, } as Loaders,
loadingScreen: {} as Record<string, any> | false, loadingScreen: {} as Record<string, any> | false,
optimizeCSS: undefined as Record<string, any> | false | undefined,
optimization: { optimization: {
minimize: undefined as boolean | undefined, minimize: undefined as boolean | undefined,
minimizer: undefined, minimizer: undefined,
// cssMinimizer: undefined,
splitChunks: { splitChunks: {
chunks: 'all', chunks: 'all',
name: undefined, name: undefined,
@ -298,9 +298,7 @@ export default () => ({
} }
} }
} }
} as WebpackConfiguration['optimization'] & { } as WebpackConfiguration['optimization'],
// cssMinimizer: undefined | boolean | Record<string, any>
},
/** /**
* Enable [thread-loader](https://github.com/webpack-contrib/thread-loader#thread-loader) in webpack building * Enable [thread-loader](https://github.com/webpack-contrib/thread-loader#thread-loader) in webpack building
* *

View File

@ -345,10 +345,9 @@ function normalizeConfig (_options: CliConfiguration) {
options.build.optimization.minimize = !options.dev options.build.optimization.minimize = !options.dev
} }
// Enable cssMinimizer only when extractCSS is enabled if (options.build.optimizeCSS === undefined) {
// if (options.build.optimization.cssMinimizer === undefined) { options.build.optimizeCSS = options.build.extractCSS ? {} : false
// options.build.optimization.cssMinimizer = options.build.extractCSS ? {} : false }
// }
const { loaders } = options.build const { loaders } = options.build
// const vueLoader = loaders.vue // const vueLoader = loaders.vue

View File

@ -1,62 +1,136 @@
// import MiniCssExtractPlugin from 'mini-css-extract-plugin' import path from 'path'
// import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin' import MiniCssExtractPlugin from 'mini-css-extract-plugin'
// import StyleLoader from '../utils/style-loader' import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'
import { wrapArray } from 'src/utils'
import { fileName, WebpackConfigContext, applyPresets } from '../utils/config'
import { WebpackConfigContext } from '../utils/config' export function style (ctx: WebpackConfigContext) {
applyPresets(ctx, [
loaders,
extractCSS,
minimizer
])
}
function minimizer (ctx: WebpackConfigContext) {
const { options, config } = ctx
if (options.build.optimizeCSS && Array.isArray(config.optimization.minimizer)) {
config.optimization.minimizer.push(new CssMinimizerPlugin({
...options.build.optimizeCSS
}))
}
}
function extractCSS (ctx: WebpackConfigContext) {
const { options, config } = ctx
export function style (_ctx: WebpackConfigContext) {
// // CSS extraction)
// if (options.build.extractCSS) {
// plugins.push(new MiniCssExtractPlugin(Object.assign({
// filename: fileName(ctx, 'css'),
// chunkFilename: fileName(ctx, 'css')
// }, options.build.extractCSS)))
// }
// CSS extraction // CSS extraction
// if (options.build.extractCSS) { if (options.build.extractCSS) {
// plugins.push(new MiniCssExtractPlugin(Object.assign({ config.plugins.push(new MiniCssExtractPlugin({
// filename: fileName(ctx, 'css'), filename: fileName(ctx, 'css'),
// chunkFilename: fileName(ctx, 'css'), chunkFilename: fileName(ctx, 'css'),
// // TODO: https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/132 ...(options.build.extractCSS as object)
// reloadAll: true }))
// }, options.build.extractCSS))) }
// } }
function loaders (ctx: WebpackConfigContext) {
const { config, options } = ctx
// CSS
config.module.rules.push(createdStyleRule('css', /\.css$/i, null, ctx))
// Postcss
config.module.rules.push(createdStyleRule('postcss', /\.p(ost)?css$/i, null, ctx))
// Less
const lessLoader = { loader: 'less-loader', options: options.build.loaders.less }
config.module.rules.push(createdStyleRule('less', /\.less$/i, lessLoader, ctx))
// Sass (TODO: optional dependency)
const sassLoader = { loader: 'sass-loader', options: options.build.loaders.sass }
config.module.rules.push(createdStyleRule('sass', /\.sass$/i, sassLoader, ctx))
const scssLoader = { loader: 'sass-loader', options: options.build.loaders.scss }
config.module.rules.push(createdStyleRule('scss', /\.scss$/i, scssLoader, ctx))
// Stylus
const stylusLoader = { loader: 'stylus-loader', options: options.build.loaders.stylus }
config.module.rules.push(createdStyleRule('stylus', /\.styl(us)?$/i, stylusLoader, ctx))
}
function createdStyleRule (lang: string, test: RegExp, processorLoader, ctx: WebpackConfigContext) {
const { options } = ctx
const styleLoaders = [
processorLoader,
createStyleResourcesLoaderRule(lang, options.build.styleResources, options.rootDir)
].filter(Boolean)
options.build.loaders.css.importLoaders =
options.build.loaders.cssModules.importLoaders =
styleLoaders.length
const cssLoaders = createCssLoadersRule(ctx, options.build.loaders.css)
const cssModuleLoaders = createCssLoadersRule(ctx, options.build.loaders.cssModules)
return {
test,
oneOf: [
// This matches <style module>
{
resourceQuery: /module/,
use: cssModuleLoaders.concat(styleLoaders)
},
// This matches plain <style> or <style scoped>
{
use: cssLoaders.concat(styleLoaders)
}
]
}
}
function createCssLoadersRule (ctx: WebpackConfigContext, cssLoaderOptions) {
const { options } = ctx
const cssLoader = { loader: 'css-loader', options: cssLoaderOptions }
if (options.build.extractCSS) {
if (ctx.isServer) {
return [cssLoader]
}
return [ return [
// { {
// test: /\.css$/i, loader: MiniCssExtractPlugin.loader,
// oneOf: styleLoader.apply('css') options: { reloadAll: ctx.isDev, hot: ctx.isDev }
// }, },
// { cssLoader
// test: /\.p(ost)?css$/i, ]
// oneOf: styleLoader.apply('postcss') }
// },
// { return [
// test: /\.less$/i, {
// oneOf: styleLoader.apply('less', { loader: 'vue-style-loader',
// loader: 'less-loader', options: options.build.loaders.vueStyle
// options: loaders.less },
// }) cssLoader
// },
// {
// test: /\.sass$/i,
// oneOf: styleLoader.apply('sass', {
// loader: 'sass-loader',
// options: loaders.sass
// })
// },
// {
// test: /\.scss$/i,
// oneOf: styleLoader.apply('scss', {
// loader: 'sass-loader',
// options: loaders.scss
// })
// },
// {
// test: /\.styl(us)?$/i,
// oneOf: styleLoader.apply('stylus', {
// loader: 'stylus-loader',
// options: loaders.stylus
// })
// }
] ]
} }
function createStyleResourcesLoaderRule (styleLang, styleResources, rootDir) {
// style-resources-loader
// https://github.com/yenshih/style-resources-loader
if (!styleResources[styleLang]) {
return
}
return {
loader: 'style-resources-loader',
options: {
patterns: wrapArray(styleResources[styleLang]).map(p => path.resolve(rootDir, p)),
...styleResources.options
}
}
}

View File

@ -1,137 +0,0 @@
import path from 'path'
// import ExtractCssChunksPlugin from 'extract-css-chunks-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import type { Nuxt } from 'src/core'
import type { NormalizedConfiguration } from 'src/config'
import { wrapArray } from 'src/utils'
import PostcssConfig from './postcss'
export default class StyleLoader {
options: NormalizedConfiguration
constructor (nuxt: Nuxt, { isServer }) {
this.options = nuxt.options
this.isServer = isServer
if (this.options.build.postcss) {
this.postcssConfig = new PostcssConfig(nuxt)
}
}
get extractCSS () {
return this.options.build.extractCSS
}
normalize (loaders) {
loaders = wrapArray(loaders)
return loaders.map(loader => (typeof loader === 'string' ? { loader } : loader))
}
styleResource (ext) {
const { build: { styleResources }, rootDir } = this.options
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(rootDir, p))
return {
loader: 'style-resources-loader',
options: Object.assign(
{ patterns },
styleResources.options || {}
)
}
}
postcss () {
// postcss-loader
// https://github.com/postcss/postcss-loader
if (!this.postcssConfig) {
return
}
const config = this.postcssConfig.config()
if (!config) {
return
}
return {
loader: 'postcss-loader',
options: Object.assign({ sourceMap: this.options.build.cssSourceMap }, config)
}
}
css (options) {
const cssLoader = { loader: 'css-loader', options }
if (this.isServer && this.extractCSS) {
options.modules = options.modules || {}
options.modules.exportOnlyLocals = true
return [cssLoader]
}
return [this.styleLoader(), cssLoader]
}
cssModules (options) {
return this.css(options)
}
extract () {
if (this.extractCSS) {
const isDev = this.options.dev
return {
loader: MiniCssExtractPlugin.loader,
options: {
// TODO: https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/132
// https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/161#issuecomment-500162574
reloadAll: isDev,
hot: isDev
}
}
}
}
styleLoader () {
return this.extract() || {
loader: 'vue-style-loader',
options: this.options.build.loaders.vueStyle
}
}
apply (ext, loaders = []) {
const { css, cssModules } = this.options.build.loaders
const customLoaders = [].concat(
this.postcss(),
this.normalize(loaders),
this.styleResource(ext)
).filter(Boolean)
css.importLoaders = cssModules.importLoaders = customLoaders.length
return [
// This matches <style module>
{
resourceQuery: /module/,
use: [
this.cssModules(cssModules),
customLoaders
]
},
// This matches plain <style> or <style scoped>
{
use: [
this.css(css),
customLoaders
]
}
]
}
}