mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
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:
parent
db050fd0a2
commit
232d3298b4
@ -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
|
||||
*
|
||||
|
@ -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
|
||||
|
@ -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 [
|
||||
// {
|
||||
// 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: MiniCssExtractPlugin.loader,
|
||||
options: { reloadAll: ctx.isDev, hot: ctx.isDev }
|
||||
},
|
||||
cssLoader
|
||||
]
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user