@@ -2,131 +2,166 @@ import postcss from 'postcss';
2
2
import valueParser from 'postcss-value-parser' ;
3
3
import { isUrlRequest } from 'loader-utils' ;
4
4
5
- import { normalizeUrl } from '../utils' ;
5
+ import { normalizeUrl , resolveRequests } from '../utils' ;
6
6
7
7
const pluginName = 'postcss-import-parser' ;
8
8
9
9
export default postcss . plugin ( pluginName , ( options ) => ( css , result ) => {
10
- const importsMap = new Map ( ) ;
11
-
12
- css . walkAtRules ( / ^ i m p o r t $ / i, ( atRule ) => {
13
- // Convert only top-level @import
14
- if ( atRule . parent . type !== 'root' ) {
15
- return ;
16
- }
17
-
18
- // Nodes do not exists - `@import url('http://') :root {}`
19
- if ( atRule . nodes ) {
20
- result . warn (
21
- "It looks like you didn't end your @import statement correctly. Child nodes are attached to it." ,
22
- { node : atRule }
23
- ) ;
10
+ return new Promise ( ( resolve , reject ) => {
11
+ const importsMap = new Map ( ) ;
12
+ const tasks = [ ] ;
24
13
25
- return ;
26
- }
27
-
28
- const { nodes } = valueParser ( atRule . params ) ;
29
-
30
- // No nodes - `@import ;`
31
- // Invalid type - `@import foo-bar;`
32
- if (
33
- nodes . length === 0 ||
34
- ( nodes [ 0 ] . type !== 'string' && nodes [ 0 ] . type !== 'function' )
35
- ) {
36
- result . warn ( `Unable to find uri in "${ atRule . toString ( ) } "` , {
37
- node : atRule ,
38
- } ) ;
39
-
40
- return ;
41
- }
42
-
43
- let isStringValue ;
44
- let url ;
45
-
46
- if ( nodes [ 0 ] . type === 'string' ) {
47
- isStringValue = true ;
48
- url = nodes [ 0 ] . value ;
49
- } else if ( nodes [ 0 ] . type === 'function' ) {
50
- // Invalid function - `@import nourl(test.css);`
51
- if ( nodes [ 0 ] . value . toLowerCase ( ) !== 'url' ) {
52
- result . warn ( `Unable to find uri in "${ atRule . toString ( ) } "` , {
53
- node : atRule ,
54
- } ) ;
14
+ // A counter is used instead of an index in callback css.walkAtRules because we mutate AST (atRule.remove())
15
+ let index = 0 ;
55
16
17
+ css . walkAtRules ( / ^ i m p o r t $ / i, ( atRule ) => {
18
+ // Convert only top-level @import
19
+ if ( atRule . parent . type !== 'root' ) {
56
20
return ;
57
21
}
58
22
59
- isStringValue =
60
- nodes [ 0 ] . nodes . length !== 0 && nodes [ 0 ] . nodes [ 0 ] . type === 'string' ;
61
- url = isStringValue
62
- ? nodes [ 0 ] . nodes [ 0 ] . value
63
- : valueParser . stringify ( nodes [ 0 ] . nodes ) ;
64
- }
23
+ // Nodes do not exists - `@import url('http://') :root {}`
24
+ if ( atRule . nodes ) {
25
+ result . warn (
26
+ "It looks like you didn't end your @import statement correctly. Child nodes are attached to it." ,
27
+ { node : atRule }
28
+ ) ;
29
+
30
+ return ;
31
+ }
65
32
66
- // Empty url - `@import "";` or `@import url();`
67
- if ( url . trim ( ) . length === 0 ) {
68
- result . warn ( `Unable to find uri in "${ atRule . toString ( ) } "` , {
69
- node : atRule ,
70
- } ) ;
33
+ const { nodes } = valueParser ( atRule . params ) ;
71
34
72
- return ;
73
- }
35
+ // No nodes - `@import ;`
36
+ // Invalid type - `@import foo-bar;`
37
+ if (
38
+ nodes . length === 0 ||
39
+ ( nodes [ 0 ] . type !== 'string' && nodes [ 0 ] . type !== 'function' )
40
+ ) {
41
+ result . warn ( `Unable to find uri in "${ atRule . toString ( ) } "` , {
42
+ node : atRule ,
43
+ } ) ;
74
44
75
- const isRequestable = isUrlRequest ( url ) ;
45
+ return ;
46
+ }
76
47
77
- if ( isRequestable ) {
78
- url = normalizeUrl ( url , isStringValue ) ;
48
+ let isStringValue ;
49
+ let url ;
50
+
51
+ if ( nodes [ 0 ] . type === 'string' ) {
52
+ isStringValue = true ;
53
+ url = nodes [ 0 ] . value ;
54
+ } else if ( nodes [ 0 ] . type === 'function' ) {
55
+ // Invalid function - `@import nourl(test.css);`
56
+ if ( nodes [ 0 ] . value . toLowerCase ( ) !== 'url' ) {
57
+ result . warn ( `Unable to find uri in "${ atRule . toString ( ) } "` , {
58
+ node : atRule ,
59
+ } ) ;
60
+
61
+ return ;
62
+ }
63
+
64
+ isStringValue =
65
+ nodes [ 0 ] . nodes . length !== 0 && nodes [ 0 ] . nodes [ 0 ] . type === 'string' ;
66
+ url = isStringValue
67
+ ? nodes [ 0 ] . nodes [ 0 ] . value
68
+ : valueParser . stringify ( nodes [ 0 ] . nodes ) ;
69
+ }
79
70
80
- // Empty url after normalize - `@import '\
81
- // \
82
- // \
83
- // ';
71
+ // Empty url - `@import "";` or `@import url();`
84
72
if ( url . trim ( ) . length === 0 ) {
85
73
result . warn ( `Unable to find uri in "${ atRule . toString ( ) } "` , {
86
74
node : atRule ,
87
75
} ) ;
88
76
89
77
return ;
90
78
}
91
- }
92
-
93
- const media = valueParser . stringify ( nodes . slice ( 1 ) ) . trim ( ) . toLowerCase ( ) ;
94
-
95
- if ( options . filter && ! options . filter ( { url, media } ) ) {
96
- return ;
97
- }
98
79
99
- atRule . remove ( ) ;
80
+ const isRequestable = isUrlRequest ( url ) ;
100
81
101
- if ( isRequestable ) {
102
- const importKey = url ;
103
- let importName = importsMap . get ( importKey ) ;
82
+ if ( isRequestable ) {
83
+ url = normalizeUrl ( url , isStringValue ) ;
104
84
105
- if ( ! importName ) {
106
- importName = `___CSS_LOADER_AT_RULE_IMPORT_${ importsMap . size } ___` ;
107
- importsMap . set ( importKey , importName ) ;
85
+ // Empty url after normalize - `@import '\
86
+ // \
87
+ // \
88
+ // ';
89
+ if ( url . trim ( ) . length === 0 ) {
90
+ result . warn ( `Unable to find uri in "${ atRule . toString ( ) } "` , {
91
+ node : atRule ,
92
+ } ) ;
108
93
109
- result . messages . push ( {
110
- type : 'import' ,
111
- value : {
112
- importName,
113
- url : options . urlHandler ? options . urlHandler ( url ) : url ,
114
- } ,
115
- } ) ;
94
+ return ;
95
+ }
116
96
}
117
97
118
- result . messages . push ( {
119
- type : 'api-import' ,
120
- value : { type : 'internal' , importName, media } ,
121
- } ) ;
98
+ const media = valueParser . stringify ( nodes . slice ( 1 ) ) . trim ( ) . toLowerCase ( ) ;
122
99
123
- return ;
124
- }
100
+ if ( options . filter && ! options . filter ( { url, media } ) ) {
101
+ return ;
102
+ }
125
103
126
- result . messages . push ( {
127
- pluginName,
128
- type : 'api-import' ,
129
- value : { type : 'external' , url, media } ,
104
+ atRule . remove ( ) ;
105
+
106
+ index += 1 ;
107
+
108
+ tasks . push (
109
+ Promise . resolve ( index ) . then ( async ( currentIndex ) => {
110
+ if ( isRequestable ) {
111
+ const importKey = url ;
112
+ let importName = importsMap . get ( importKey ) ;
113
+
114
+ if ( ! importName ) {
115
+ importName = `___CSS_LOADER_AT_RULE_IMPORT_${ importsMap . size } ___` ;
116
+ importsMap . set ( importKey , importName ) ;
117
+
118
+ const { resolver, context } = options ;
119
+
120
+ let resolvedUrl ;
121
+
122
+ try {
123
+ resolvedUrl = await resolveRequests ( resolver , context , [ url ] ) ;
124
+ } catch ( error ) {
125
+ throw error ;
126
+ }
127
+
128
+ result . messages . push ( {
129
+ type : 'import' ,
130
+ value : {
131
+ importName,
132
+ url : options . urlHandler
133
+ ? options . urlHandler ( resolvedUrl )
134
+ : resolvedUrl ,
135
+ index : currentIndex ,
136
+ } ,
137
+ } ) ;
138
+ }
139
+
140
+ result . messages . push ( {
141
+ type : 'api-import' ,
142
+ value : {
143
+ type : 'internal' ,
144
+ importName,
145
+ media,
146
+ index : currentIndex ,
147
+ } ,
148
+ } ) ;
149
+
150
+ return ;
151
+ }
152
+
153
+ result . messages . push ( {
154
+ pluginName,
155
+ type : 'api-import' ,
156
+ value : { type : 'external' , url, media, index : currentIndex } ,
157
+ } ) ;
158
+ } )
159
+ ) ;
130
160
} ) ;
161
+
162
+ Promise . all ( tasks ) . then (
163
+ ( ) => resolve ( ) ,
164
+ ( error ) => reject ( error )
165
+ ) ;
131
166
} ) ;
132
167
} ) ;
0 commit comments