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: {}
} as Loaders,
loadingScreen: {} as Record<string, any> | false,
optimizeCSS: undefined as Record<string, any> | false | undefined,
optimization: {
minimize: undefined as boolean | undefined,
minimizer: undefined,
// cssMinimizer: undefined,
splitChunks: {
chunks: 'all',
name: undefined,
@ -298,9 +298,7 @@ export default () => ({
}
}
}
} as WebpackConfiguration['optimization'] & {
// cssMinimizer: undefined | boolean | Record<string, any>
},
} as WebpackConfiguration['optimization'],
/**
* 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
}
// Enable cssMinimizer only when extractCSS is enabled
// if (options.build.optimization.cssMinimizer === undefined) {
// options.build.optimization.cssMinimizer = options.build.extractCSS ? {} : false
// }
if (options.build.optimizeCSS === undefined) {
options.build.optimizeCSS = options.build.extractCSS ? {} : false
}
const { loaders } = options.build
// const vueLoader = loaders.vue

View File

@ -1,62 +1,136 @@
// import MiniCssExtractPlugin from 'mini-css-extract-plugin'
// import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin'
// import StyleLoader from '../utils/style-loader'
import path from 'path'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
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
// if (options.build.extractCSS) {
// plugins.push(new MiniCssExtractPlugin(Object.assign({
// filename: fileName(ctx, 'css'),
// chunkFilename: fileName(ctx, 'css'),
// // TODO: https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/132
// reloadAll: true
// }, options.build.extractCSS)))
// }
if (options.build.extractCSS) {
config.plugins.push(new MiniCssExtractPlugin({
filename: fileName(ctx, 'css'),
chunkFilename: fileName(ctx, 'css'),
...(options.build.extractCSS as object)
}))
}
}
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 [
{
loader: MiniCssExtractPlugin.loader,
options: { reloadAll: ctx.isDev, hot: ctx.isDev }
},
cssLoader
]
}
return [
// {
// test: /\.css$/i,
// oneOf: styleLoader.apply('css')
// },
// {
// test: /\.p(ost)?css$/i,
// oneOf: styleLoader.apply('postcss')
// },
// {
// test: /\.less$/i,
// oneOf: styleLoader.apply('less', {
// loader: 'less-loader',
// options: loaders.less
// })
// },
// {
// 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
// })
// }
{
loader: 'vue-style-loader',
options: options.build.loaders.vueStyle
},
cssLoader
]
}
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
]
}
]
}
}