@@ -7,8 +7,8 @@ import atImport from 'postcss-import'
77const isDev = ( ) => process . env . NODE_ENV === 'development'
88const isProd = ( ) => ! isDev ( )
99
10- const toProdExport = ( code ) => `export default ${ code } `
11- const toDevExport = ( code ) => `let config = ${ code } ;
10+ const toProdExport = code => `export default ${ code } `
11+ const toDevExport = code => `let config = ${ code } ;
1212if (typeof Proxy !== 'undefined') {
1313 config = new Proxy(config, {
1414 get(target, key) {
@@ -25,43 +25,128 @@ if (typeof Proxy !== 'undefined') {
2525 });
2626}
2727export default config`
28- const toES5Export = ( code ) => `module.exports = ${ code } `
28+ const toES5Export = code => `module.exports = ${ code } `
2929
30- const toExport = cond ( [
31- [ isDev , toDevExport ] ,
32- [ isProd , toProdExport ]
33- ] )
30+ const toExport = cond ( [ [ isDev , toDevExport ] , [ isProd , toProdExport ] ] )
3431
35- const toString = ( data ) => `${ JSON . stringify ( data , null , '\t' ) } `
32+ const toString = data => `${ JSON . stringify ( data , null , '\t' ) } `
3633
3734const dashesCamelCase = str =>
3835 str . replace ( / - + ( \w ) / g, ( match , firstLetter ) => firstLetter . toUpperCase ( ) )
3936
40- const objectify = ( root , filepath ) => {
41- const result = { }
37+ const EXTENSION_RE = / \( \s * - - ( [ \w - ] + ) \s * \) / g
4238
39+ const resolveCustomMediaValue = ( query , depChain , map ) => {
40+ if ( ! EXTENSION_RE . test ( query . value ) ) {
41+ return query . value
42+ }
43+ const val = query . value . replace ( EXTENSION_RE , function ( orig , name ) {
44+ name = dashesCamelCase ( name . replace ( / ^ - + / , '' ) )
45+ if ( ! map [ name ] ) {
46+ return orig
47+ }
48+ const mq = map [ name ]
49+ if ( mq . resolved ) {
50+ return mq . value
51+ }
52+
53+ if ( depChain . indexOf ( name ) !== - 1 ) {
54+ mq . circular = true
55+ return orig
56+ }
57+ depChain . push ( name )
58+ mq . value = resolveCustomMediaValue ( mq , depChain , map )
59+
60+ return mq . value
61+ } )
62+ if ( val === query . value ) {
63+ query . circular = true
64+ }
65+ return val
66+ }
67+
68+ const objectify = ( result , filepath ) => {
69+ const obj = { }
70+ const { root } = result
71+ const customMediaMap = { }
4372 if ( ! root ) {
44- return result
73+ return obj
4574 }
4675
47- root . walkDecls ( ( rule ) => {
76+ root . walk ( rule => {
4877 if ( rule . source . input . file !== filepath ) {
4978 return
5079 }
51- if ( rule . parent && rule . parent . selectors . find ( ( sel ) => sel === ':root' ) ) {
52- const { value } = rule
53- const key = dashesCamelCase (
54- rule . prop . replace ( / ^ - + / , '' ) // replace "--"
55- )
80+ if ( rule . type === 'atrule' && rule . name === 'custom-media' ) {
81+ const params = rule . params . split ( ' ' )
82+ const originalKey = params . shift ( )
83+ const key = dashesCamelCase ( originalKey . replace ( / ^ - + / , '' ) )
84+ if ( typeof obj [ key ] === 'string' ) {
85+ rule . warn (
86+ result ,
87+ `Existing exported CSS variable was replaced by @custom-media variable [${ originalKey } ]` ,
88+ {
89+ plugin : 'postcss-variables-loader' ,
90+ word : originalKey
91+ }
92+ )
93+ }
94+ customMediaMap [ key ] = obj [ key ] = {
95+ value : params . join ( ' ' ) ,
96+ resolved : false ,
97+ circular : false ,
98+ rule,
99+ originalKey
100+ }
101+ } else if ( rule . type === 'decl' ) {
102+ if ( rule . parent && rule . parent . selectors . find ( sel => sel === ':root' ) ) {
103+ const { value } = rule
104+ const key = dashesCamelCase ( rule . prop . replace ( / ^ - + / , '' ) )
105+
106+ if ( typeof obj [ key ] === 'object' ) {
107+ rule . warn (
108+ result ,
109+ `Existing exported @custom-media variable was replaced by CSS variable [${ rule . prop } ]` ,
110+ { plugin : 'postcss-variables-loader' , word : rule . prop }
111+ )
112+ }
56113
57- result [ key ] = value . match ( / ^ [ + - ] ? \d * .? ( \d * ) ? ( p x ) $ / i) ? parseFloat ( value ) : value
114+ obj [ key ] = value . match ( / ^ [ + - ] ? \d * .? ( \d * ) ? ( p x ) $ / i) ? parseFloat ( value ) : value
115+ }
58116 }
59117 } )
60- return result
118+ Object . keys ( obj ) . forEach ( function ( key ) {
119+ const val = obj [ key ]
120+ if ( typeof val === 'object' ) {
121+ const mq = customMediaMap [ key ]
122+ const value = resolveCustomMediaValue ( mq , [ key ] , customMediaMap )
123+ mq . value = value
124+ mq . resolved = true
125+ if ( ! mq . circular ) {
126+ obj [ key ] = value
127+ } else {
128+ mq . rule . warn (
129+ result ,
130+ `Circular @custom-media definition for [${
131+ mq . originalKey
132+ } ]. The entire rule has been removed from the output.`,
133+ { node : mq . rule }
134+ )
135+ delete obj [ key ]
136+ }
137+ }
138+ } )
139+
140+ return obj
61141}
62142
63143export const toConfig = compose ( toExport , toString , objectify )
64144export const toES5Config = compose ( toES5Export , toString , objectify )
65- export const getPostcss = ( async ) => postcss ( )
66- . use ( atImport ( { async } ) )
67- . use ( cssnext ( { features : { customProperties : { preserve : 'computed' } } } ) )
145+ export const getPostcss = async =>
146+ postcss ( )
147+ . use ( atImport ( { async } ) )
148+ . use (
149+ cssnext ( {
150+ features : { customProperties : { preserve : 'computed' } , customMedia : { preserve : true } }
151+ } )
152+ )
0 commit comments