11import postcss from 'postcss' ;
22import valueParser from 'postcss-value-parser' ;
3+ import _ from 'lodash' ;
34
45const pluginName = 'postcss-url-parser' ;
56
6- function getArg ( nodes ) {
7- return nodes . length !== 0 && nodes [ 0 ] . type === 'string'
8- ? nodes [ 0 ] . value
9- : valueParser . stringify ( nodes ) ;
7+ const isUrlFunc = / u r l / i;
8+ const isImageSetFunc = / ^ (?: - w e b k i t - ) ? i m a g e - s e t $ / i;
9+ const needParseDecl = / (?: u r l | (?: - w e b k i t - ) ? i m a g e - s e t ) \( / i;
10+
11+ function getNodeFromUrlFunc ( node ) {
12+ return node . nodes && node . nodes [ 0 ] ;
13+ }
14+
15+ function getUrlFromUrlFunc ( node ) {
16+ return node . nodes . length !== 0 && node . nodes [ 0 ] . type === 'string'
17+ ? node . nodes [ 0 ] . value
18+ : valueParser . stringify ( node . nodes ) ;
1019}
1120
1221function walkUrls ( parsed , callback ) {
1322 parsed . walk ( ( node ) => {
14- if ( node . type !== 'function' || node . value . toLowerCase ( ) !== 'url' ) {
23+ if ( node . type !== 'function' ) {
1524 return ;
1625 }
1726
18- /* eslint-disable */
19- node . before = '' ;
20- node . after = '' ;
21- /* eslint-enable */
27+ if ( isUrlFunc . test ( node . value ) ) {
28+ callback ( getNodeFromUrlFunc ( node ) , getUrlFromUrlFunc ( node ) , false ) ;
2229
23- callback ( node , getArg ( node . nodes ) ) ;
30+ // Do not traverse inside `url`
31+ // eslint-disable-next-line consistent-return
32+ return false ;
33+ }
34+
35+ if ( isImageSetFunc . test ( node . value ) ) {
36+ node . nodes . forEach ( ( nNode ) => {
37+ if ( nNode . type === 'function' && isUrlFunc . test ( nNode . value ) ) {
38+ callback ( getNodeFromUrlFunc ( nNode ) , getUrlFromUrlFunc ( nNode ) , false ) ;
39+ }
40+
41+ if ( nNode . type === 'string' ) {
42+ callback ( nNode , nNode . value , true ) ;
43+ }
44+ } ) ;
2445
25- // Do not traverse inside url
26- // eslint-disable-next-line consistent-return
27- return false ;
46+ // Do not traverse inside `image-set`
47+ // eslint-disable-next-line consistent-return
48+ return false ;
49+ }
2850 } ) ;
2951}
3052
3153function walkDeclsWithUrl ( css , result , filter ) {
3254 const items = [ ] ;
3355
3456 css . walkDecls ( ( decl ) => {
35- if ( ! / u r l \( / i . test ( decl . value ) ) {
57+ if ( ! needParseDecl . test ( decl . value ) ) {
3658 return ;
3759 }
3860
3961 const parsed = valueParser ( decl . value ) ;
4062 const urls = [ ] ;
4163
42- walkUrls ( parsed , ( node , url ) => {
64+ walkUrls ( parsed , ( node , url , needQuotes ) => {
4365 if ( url . trim ( ) . replace ( / \\ [ \r \n ] / g, '' ) . length === 0 ) {
4466 result . warn ( `Unable to find uri in '${ decl . toString ( ) } '` , {
4567 node : decl ,
@@ -52,7 +74,7 @@ function walkDeclsWithUrl(css, result, filter) {
5274 return ;
5375 }
5476
55- urls . push ( url ) ;
77+ urls . push ( { url, needQuotes } ) ;
5678 } ) ;
5779
5880 if ( urls . length === 0 ) {
@@ -65,52 +87,49 @@ function walkDeclsWithUrl(css, result, filter) {
6587 return items ;
6688}
6789
68- function flatten ( array ) {
69- return array . reduce ( ( acc , d ) => [ ...acc , ...d ] , [ ] ) ;
70- }
71-
72- function uniq ( array ) {
73- return array . reduce (
74- ( acc , d ) => ( acc . indexOf ( d ) === - 1 ? [ ...acc , d ] : acc ) ,
75- [ ]
76- ) ;
77- }
78-
7990export default postcss . plugin (
8091 pluginName ,
8192 ( options = { } ) =>
8293 function process ( css , result ) {
8394 const traversed = walkDeclsWithUrl ( css , result , options . filter ) ;
84- const paths = uniq ( flatten ( traversed . map ( ( item ) => item . urls ) ) ) ;
95+ const paths = _ . uniqWith (
96+ _ . flatten ( traversed . map ( ( item ) => item . urls ) ) ,
97+ _ . isEqual
98+ ) ;
8599
86100 if ( paths . length === 0 ) {
87101 return ;
88102 }
89103
90- const urls = { } ;
104+ const placeholders = [ ] ;
91105
92106 paths . forEach ( ( path , index ) => {
93107 const placeholder = `___CSS_LOADER_URL___${ index } ___` ;
108+ const { url, needQuotes } = path ;
94109
95- urls [ path ] = placeholder ;
110+ placeholders . push ( { placeholder, path } ) ;
96111
97112 result . messages . push ( {
98113 pluginName,
99114 type : 'url' ,
100- item : { url : path , placeholder } ,
115+ item : { url, placeholder, needQuotes } ,
101116 } ) ;
102117 } ) ;
103118
104119 traversed . forEach ( ( item ) => {
105- walkUrls ( item . parsed , ( node , url ) => {
106- const value = urls [ url ] ;
120+ walkUrls ( item . parsed , ( node , url , needQuotes ) => {
121+ const value = _ . find ( placeholders , { path : { url, needQuotes } } ) ;
107122
108123 if ( ! value ) {
109124 return ;
110125 }
111126
127+ const { placeholder } = value ;
128+
129+ // eslint-disable-next-line no-param-reassign
130+ node . type = 'word' ;
112131 // eslint-disable-next-line no-param-reassign
113- node . nodes = [ { type : 'word' , value } ] ;
132+ node . value = placeholder ;
114133 } ) ;
115134
116135 // eslint-disable-next-line no-param-reassign
0 commit comments