2020-07-02 13:02:35 +00:00
import fs from 'fs'
2021-10-02 16:01:17 +00:00
import { resolve } from 'pathe'
2020-07-02 13:02:35 +00:00
import consola from 'consola'
2021-10-02 16:01:17 +00:00
import { createCommonJS } from 'mlly'
import { defaults , merge , cloneDeep } from 'lodash-es'
2020-07-02 13:02:35 +00:00
import createResolver from 'postcss-import-resolver'
2021-11-21 16:14:46 +00:00
import { requireModule } from '@nuxt/kit'
import type { Nuxt } from '@nuxt/schema'
2020-07-02 13:02:35 +00:00
2021-04-02 11:47:01 +00:00
const isPureObject = obj = > obj !== null && ! Array . isArray ( obj ) && typeof obj === 'object'
2020-07-02 13:02:35 +00:00
export const orderPresets = {
cssnanoLast ( names ) {
const nanoIndex = names . indexOf ( 'cssnano' )
if ( nanoIndex !== names . length - 1 ) {
names . push ( names . splice ( nanoIndex , 1 ) [ 0 ] )
}
return names
} ,
2021-09-17 16:20:05 +00:00
autoprefixerLast ( names ) {
const nanoIndex = names . indexOf ( 'autoprefixer' )
2020-07-02 13:02:35 +00:00
if ( nanoIndex !== names . length - 1 ) {
names . push ( names . splice ( nanoIndex , 1 ) [ 0 ] )
}
return names
} ,
2021-09-17 16:20:05 +00:00
autoprefixerAndCssnanoLast ( names ) {
return orderPresets . cssnanoLast ( orderPresets . autoprefixerLast ( names ) )
2020-07-02 13:02:35 +00:00
}
}
2021-04-02 11:47:01 +00:00
let _postcssConfigFileWarningShown
2020-07-02 13:02:35 +00:00
function postcssConfigFileWarning ( ) {
2021-04-02 11:47:01 +00:00
if ( _postcssConfigFileWarningShown ) {
2020-07-02 13:02:35 +00:00
return
}
consola . warn ( 'Please use `build.postcss` in your nuxt.config.js instead of an external config file. Support for such files will be removed in Nuxt 3 as they remove all defaults set by Nuxt and can cause severe problems with features like alias resolving inside your CSS.' )
2021-04-02 11:47:01 +00:00
_postcssConfigFileWarningShown = true
2020-07-02 13:02:35 +00:00
}
2021-09-17 16:20:05 +00:00
export class PostcssConfig {
2020-08-18 20:33:58 +00:00
nuxt : Nuxt
2021-04-02 11:47:01 +00:00
options : Nuxt [ 'options' ]
2020-08-18 20:33:58 +00:00
constructor ( nuxt ) {
this . nuxt = nuxt
this . options = nuxt . options
2020-07-02 13:02:35 +00:00
}
get postcssOptions ( ) {
2021-09-17 16:20:05 +00:00
return this . options . build . postcss . postcssOptions
2020-07-02 13:02:35 +00:00
}
get postcssImportAlias ( ) {
2020-08-18 20:33:58 +00:00
const alias = { . . . this . options . alias }
2020-07-02 13:02:35 +00:00
for ( const key in alias ) {
if ( key . startsWith ( '~' ) ) {
continue
}
const newKey = '~' + key
if ( ! alias [ newKey ] ) {
alias [ newKey ] = alias [ key ]
}
}
return alias
}
get defaultConfig ( ) {
2020-08-18 20:33:58 +00:00
const { dev , srcDir , rootDir , module sDir } = this . options
2020-07-02 13:02:35 +00:00
return {
2020-08-18 20:33:58 +00:00
sourceMap : this.options.build.cssSourceMap ,
2020-07-02 13:02:35 +00:00
plugins : {
// https://github.com/postcss/postcss-import
'postcss-import' : {
resolve : createResolver ( {
alias : this.postcssImportAlias ,
module s : [ srcDir , rootDir , . . . module sDir ]
} )
} ,
// https://github.com/postcss/postcss-url
'postcss-url' : { } ,
2021-09-17 16:20:05 +00:00
autoprefixer : { } ,
cssnano : dev
? false
: {
preset : [ 'default' , {
// Keep quotes in font values to prevent from HEX conversion
// https://github.com/nuxt/nuxt.js/issues/6306
minifyFontValues : { removeQuotes : false }
} ]
}
2020-07-02 13:02:35 +00:00
} ,
// Array, String or Function
2021-09-17 16:20:05 +00:00
order : 'autoprefixerAndCssnanoLast'
2020-07-02 13:02:35 +00:00
}
}
searchConfigFile ( ) {
// Search for postCSS config file and use it if exists
// https://github.com/michael-ciniawsky/postcss-load-config
// TODO: Remove in Nuxt 3
2020-08-18 20:33:58 +00:00
const { srcDir , rootDir } = this . options
2020-07-02 13:02:35 +00:00
for ( const dir of [ srcDir , rootDir ] ) {
for ( const file of [
'postcss.config.js' ,
'.postcssrc.js' ,
'.postcssrc' ,
'.postcssrc.json' ,
'.postcssrc.yaml'
] ) {
2021-10-02 16:01:17 +00:00
const configFile = resolve ( dir , file )
2020-07-02 13:02:35 +00:00
if ( fs . existsSync ( configFile ) ) {
postcssConfigFileWarning ( )
return configFile
}
}
}
}
2021-09-17 16:20:05 +00:00
getConfigFile ( ) {
2021-11-10 12:11:08 +00:00
const loaderConfig = this . postcssOptions && this . postcssOptions . config
2021-09-17 16:20:05 +00:00
const postcssConfigFile = loaderConfig || this . searchConfigFile ( )
2020-07-02 13:02:35 +00:00
2021-09-17 16:20:05 +00:00
if ( typeof postcssConfigFile === 'string' ) {
return postcssConfigFile
2020-07-02 13:02:35 +00:00
}
}
sortPlugins ( { plugins , order } ) {
const names = Object . keys ( plugins )
if ( typeof order === 'string' ) {
order = orderPresets [ order ]
}
return typeof order === 'function' ? order ( names , orderPresets ) : ( order || names )
}
loadPlugins ( config ) {
2021-09-21 15:19:54 +00:00
if ( ! isPureObject ( config . plugins ) ) { return }
// Map postcss plugins into instances on object mode once
2021-10-02 16:01:17 +00:00
const cjs = createCommonJS ( import . meta . url )
2021-09-21 15:19:54 +00:00
config . plugins = this . sortPlugins ( config ) . map ( ( pluginName ) = > {
2021-10-02 16:01:17 +00:00
const pluginFn = requireModule ( pluginName , { paths : [ cjs . __dirname ] } )
2021-09-21 15:19:54 +00:00
const pluginOptions = config . plugins [ pluginName ]
if ( ! pluginOptions || typeof pluginFn !== 'function' ) { return null }
return pluginFn ( pluginOptions )
} ) . filter ( Boolean )
2020-07-02 13:02:35 +00:00
}
config ( ) {
/* istanbul ignore if */
2021-09-17 16:20:05 +00:00
if ( ! this . options . build . postcss ) {
2020-07-02 13:02:35 +00:00
return false
}
2021-09-17 16:20:05 +00:00
const configFile = this . getConfigFile ( )
if ( configFile ) {
return {
postcssOptions : {
config : configFile
} ,
sourceMap : this.options.build.cssSourceMap
}
2020-07-02 13:02:35 +00:00
}
2021-09-17 16:20:05 +00:00
let postcssOptions = cloneDeep ( this . postcssOptions )
2020-07-02 13:02:35 +00:00
// Apply default plugins
2021-09-17 16:20:05 +00:00
if ( isPureObject ( postcssOptions ) ) {
if ( Array . isArray ( postcssOptions . plugins ) ) {
defaults ( postcssOptions , this . defaultConfig )
2020-07-02 13:02:35 +00:00
} else {
// Keep the order of default plugins
2021-09-17 16:20:05 +00:00
postcssOptions = merge ( { } , this . defaultConfig , postcssOptions )
this . loadPlugins ( postcssOptions )
}
delete this . options . build . postcss . order
return {
sourceMap : this.options.build.cssSourceMap ,
. . . this . options . build . postcss ,
postcssOptions
2020-07-02 13:02:35 +00:00
}
}
}
}