1
1
import postcss from 'postcss' ;
2
2
import valueParser from 'postcss-value-parser' ;
3
+ import _ from 'lodash' ;
3
4
4
5
const pluginName = 'postcss-url-parser' ;
5
6
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 ) ;
10
19
}
11
20
12
21
function walkUrls ( parsed , callback ) {
13
22
parsed . walk ( ( node ) => {
14
- if ( node . type !== 'function' || node . value . toLowerCase ( ) !== 'url' ) {
23
+ if ( node . type !== 'function' ) {
15
24
return ;
16
25
}
17
26
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 ) ;
22
29
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
+ } ) ;
24
45
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
+ }
28
50
} ) ;
29
51
}
30
52
31
53
function walkDeclsWithUrl ( css , result , filter ) {
32
54
const items = [ ] ;
33
55
34
56
css . walkDecls ( ( decl ) => {
35
- if ( ! / u r l \( / i . test ( decl . value ) ) {
57
+ if ( ! needParseDecl . test ( decl . value ) ) {
36
58
return ;
37
59
}
38
60
39
61
const parsed = valueParser ( decl . value ) ;
40
62
const urls = [ ] ;
41
63
42
- walkUrls ( parsed , ( node , url ) => {
64
+ walkUrls ( parsed , ( node , url , needQuotes ) => {
43
65
if ( url . trim ( ) . replace ( / \\ [ \r \n ] / g, '' ) . length === 0 ) {
44
66
result . warn ( `Unable to find uri in '${ decl . toString ( ) } '` , {
45
67
node : decl ,
@@ -52,7 +74,7 @@ function walkDeclsWithUrl(css, result, filter) {
52
74
return ;
53
75
}
54
76
55
- urls . push ( url ) ;
77
+ urls . push ( { url, needQuotes } ) ;
56
78
} ) ;
57
79
58
80
if ( urls . length === 0 ) {
@@ -65,52 +87,49 @@ function walkDeclsWithUrl(css, result, filter) {
65
87
return items ;
66
88
}
67
89
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
-
79
90
export default postcss . plugin (
80
91
pluginName ,
81
92
( options = { } ) =>
82
93
function process ( css , result ) {
83
94
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
+ ) ;
85
99
86
100
if ( paths . length === 0 ) {
87
101
return ;
88
102
}
89
103
90
- const urls = { } ;
104
+ const placeholders = [ ] ;
91
105
92
106
paths . forEach ( ( path , index ) => {
93
107
const placeholder = `___CSS_LOADER_URL___${ index } ___` ;
108
+ const { url, needQuotes } = path ;
94
109
95
- urls [ path ] = placeholder ;
110
+ placeholders . push ( { placeholder, path } ) ;
96
111
97
112
result . messages . push ( {
98
113
pluginName,
99
114
type : 'url' ,
100
- item : { url : path , placeholder } ,
115
+ item : { url, placeholder, needQuotes } ,
101
116
} ) ;
102
117
} ) ;
103
118
104
119
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 } } ) ;
107
122
108
123
if ( ! value ) {
109
124
return ;
110
125
}
111
126
127
+ const { placeholder } = value ;
128
+
129
+ // eslint-disable-next-line no-param-reassign
130
+ node . type = 'word' ;
112
131
// eslint-disable-next-line no-param-reassign
113
- node . nodes = [ { type : 'word' , value } ] ;
132
+ node . value = placeholder ;
114
133
} ) ;
115
134
116
135
// eslint-disable-next-line no-param-reassign
0 commit comments