@@ -8,19 +8,19 @@ import {
8
8
webpackIgnoreCommentRegexp ,
9
9
} from "../utils" ;
10
10
11
- function visitor ( result , parsedResults , node , key ) {
11
+ function parseNode ( atRule , key ) {
12
12
// Convert only top-level @import
13
- if ( node . parent . type !== "root" ) {
13
+ if ( atRule . parent . type !== "root" ) {
14
14
return ;
15
15
}
16
16
17
17
if (
18
- node . raws &&
19
- node . raws . afterName &&
20
- node . raws . afterName . trim ( ) . length > 0
18
+ atRule . raws &&
19
+ atRule . raws . afterName &&
20
+ atRule . raws . afterName . trim ( ) . length > 0
21
21
) {
22
- const lastCommentIndex = node . raws . afterName . lastIndexOf ( "/*" ) ;
23
- const matched = node . raws . afterName
22
+ const lastCommentIndex = atRule . raws . afterName . lastIndexOf ( "/*" ) ;
23
+ const matched = atRule . raws . afterName
24
24
. slice ( lastCommentIndex )
25
25
. match ( webpackIgnoreCommentRegexp ) ;
26
26
@@ -29,7 +29,7 @@ function visitor(result, parsedResults, node, key) {
29
29
}
30
30
}
31
31
32
- const prevNode = node . prev ( ) ;
32
+ const prevNode = atRule . prev ( ) ;
33
33
34
34
if ( prevNode && prevNode . type === "comment" ) {
35
35
const matched = prevNode . text . match ( webpackIgnoreCommentRegexp ) ;
@@ -40,26 +40,29 @@ function visitor(result, parsedResults, node, key) {
40
40
}
41
41
42
42
// Nodes do not exists - `@import url('http://') :root {}`
43
- if ( node . nodes ) {
44
- result . warn (
45
- "It looks like you didn't end your @import statement correctly. Child nodes are attached to it." ,
46
- { node }
43
+ if ( atRule . nodes ) {
44
+ const error = new Error (
45
+ "It looks like you didn't end your @import statement correctly. Child nodes are attached to it."
47
46
) ;
48
47
49
- return ;
48
+ error . node = atRule ;
49
+
50
+ throw error ;
50
51
}
51
52
52
- const { nodes : paramsNodes } = valueParser ( node [ key ] ) ;
53
+ const { nodes : paramsNodes } = valueParser ( atRule [ key ] ) ;
53
54
54
55
// No nodes - `@import ;`
55
56
// Invalid type - `@import foo-bar;`
56
57
if (
57
58
paramsNodes . length === 0 ||
58
59
( paramsNodes [ 0 ] . type !== "string" && paramsNodes [ 0 ] . type !== "function" )
59
60
) {
60
- result . warn ( `Unable to find uri in "${ node . toString ( ) } "` , { node } ) ;
61
+ const error = new Error ( `Unable to find uri in "${ atRule . toString ( ) } "` ) ;
61
62
62
- return ;
63
+ error . node = atRule ;
64
+
65
+ throw error ;
63
66
}
64
67
65
68
let isStringValue ;
@@ -71,9 +74,11 @@ function visitor(result, parsedResults, node, key) {
71
74
} else {
72
75
// Invalid function - `@import nourl(test.css);`
73
76
if ( paramsNodes [ 0 ] . value . toLowerCase ( ) !== "url" ) {
74
- result . warn ( `Unable to find uri in "${ node . toString ( ) } "` , { node } ) ;
77
+ const error = new Error ( `Unable to find uri in "${ atRule . toString ( ) } "` ) ;
75
78
76
- return ;
79
+ error . node = atRule ;
80
+
81
+ throw error ;
77
82
}
78
83
79
84
isStringValue =
@@ -84,145 +89,139 @@ function visitor(result, parsedResults, node, key) {
84
89
: valueParser . stringify ( paramsNodes [ 0 ] . nodes ) ;
85
90
}
86
91
92
+ url = normalizeUrl ( url , isStringValue ) ;
93
+
94
+ const isRequestable = isUrlRequestable ( url ) ;
95
+ let prefix ;
96
+
97
+ if ( isRequestable ) {
98
+ const queryParts = url . split ( "!" ) ;
99
+
100
+ if ( queryParts . length > 1 ) {
101
+ url = queryParts . pop ( ) ;
102
+ prefix = queryParts . join ( "!" ) ;
103
+ }
104
+ }
105
+
87
106
// Empty url - `@import "";` or `@import url();`
88
107
if ( url . trim ( ) . length === 0 ) {
89
- result . warn ( `Unable to find uri in "${ node . toString ( ) } "` , { node } ) ;
108
+ const error = new Error ( `Unable to find uri in "${ atRule . toString ( ) } "` ) ;
90
109
91
- return ;
110
+ error . node = atRule ;
111
+
112
+ throw error ;
113
+ }
114
+
115
+ const mediaNodes = paramsNodes . slice ( 1 ) ;
116
+ let media ;
117
+
118
+ if ( mediaNodes . length > 0 ) {
119
+ media = valueParser . stringify ( mediaNodes ) . trim ( ) . toLowerCase ( ) ;
92
120
}
93
121
94
- parsedResults . push ( {
95
- node,
96
- url,
97
- isStringValue,
98
- mediaNodes : paramsNodes . slice ( 1 ) ,
99
- } ) ;
122
+ // eslint-disable-next-line consistent-return
123
+ return { atRule, prefix, url, media, isRequestable } ;
100
124
}
101
125
102
126
const plugin = ( options = { } ) => {
103
127
return {
104
128
postcssPlugin : "postcss-import-parser" ,
105
129
prepare ( result ) {
106
- const parsedResults = [ ] ;
130
+ const parsedAtRules = [ ] ;
107
131
108
132
return {
109
133
AtRule : {
110
134
import ( atRule ) {
111
- visitor ( result , parsedResults , atRule , "params" ) ;
135
+ let parsedAtRule ;
136
+
137
+ try {
138
+ parsedAtRule = parseNode ( atRule , "params" , result ) ;
139
+ } catch ( error ) {
140
+ result . warn ( error . message , { node : error . node } ) ;
141
+ }
142
+
143
+ if ( ! parsedAtRule ) {
144
+ return ;
145
+ }
146
+
147
+ parsedAtRules . push ( parsedAtRule ) ;
112
148
} ,
113
149
} ,
114
150
async OnceExit ( ) {
115
- if ( parsedResults . length === 0 ) {
151
+ if ( parsedAtRules . length === 0 ) {
116
152
return ;
117
153
}
118
154
119
- const imports = new Map ( ) ;
120
- const tasks = [ ] ;
121
-
122
- for ( const parsedResult of parsedResults ) {
123
- const { node, url, isStringValue, mediaNodes } = parsedResult ;
155
+ const resolvedAtRules = await Promise . all (
156
+ parsedAtRules . map ( async ( parsedAtRule ) => {
157
+ const {
158
+ atRule,
159
+ isRequestable,
160
+ prefix,
161
+ url,
162
+ media,
163
+ } = parsedAtRule ;
164
+
165
+ if ( options . filter ) {
166
+ const needKeep = await options . filter ( url , media ) ;
167
+
168
+ if ( ! needKeep ) {
169
+ return null ;
170
+ }
171
+ }
124
172
125
- let normalizedUrl = url ;
126
- let prefix = "" ;
173
+ atRule . remove ( ) ;
127
174
128
- const isRequestable = isUrlRequestable ( normalizedUrl ) ;
175
+ if ( isRequestable ) {
176
+ const request = requestify ( url , options . rootContext ) ;
129
177
130
- if ( isRequestable ) {
131
- const queryParts = normalizedUrl . split ( "!" ) ;
178
+ const { resolver, context } = options ;
179
+ const resolvedUrl = await resolveRequests ( resolver , context , [
180
+ ...new Set ( [ request , url ] ) ,
181
+ ] ) ;
132
182
133
- if ( queryParts . length > 1 ) {
134
- normalizedUrl = queryParts . pop ( ) ;
135
- prefix = queryParts . join ( "!" ) ;
183
+ return { url : resolvedUrl , media, prefix, isRequestable } ;
136
184
}
137
185
138
- normalizedUrl = normalizeUrl ( normalizedUrl , isStringValue ) ;
139
-
140
- // Empty url after normalize - `@import '\
141
- // \
142
- // \
143
- // ';
144
- if ( normalizedUrl . trim ( ) . length === 0 ) {
145
- result . warn ( `Unable to find uri in "${ node . toString ( ) } "` , {
146
- node,
147
- } ) ;
186
+ return { url, media, prefix, isRequestable } ;
187
+ } )
188
+ ) ;
148
189
149
- // eslint-disable-next-line no-continue
150
- continue ;
151
- }
152
- }
190
+ const urlToNameMap = new Map ( ) ;
153
191
154
- let media ;
192
+ for ( let index = 0 ; index <= resolvedAtRules . length - 1 ; index ++ ) {
193
+ const resolvedAtRule = resolvedAtRules [ index ] ;
155
194
156
- if ( mediaNodes . length > 0 ) {
157
- media = valueParser . stringify ( mediaNodes ) . trim ( ) . toLowerCase ( ) ;
195
+ if ( ! resolvedAtRule ) {
196
+ // eslint-disable-next-line no-continue
197
+ continue ;
158
198
}
159
199
160
- tasks . push (
161
- ( async ( ) => {
162
- if ( options . filter ) {
163
- const processURL = await options . filter ( normalizedUrl , media ) ;
164
- if ( ! processURL ) {
165
- return null ;
166
- }
167
- }
168
-
169
- node . remove ( ) ;
170
-
171
- if ( isRequestable ) {
172
- const request = requestify (
173
- normalizedUrl ,
174
- options . rootContext
175
- ) ;
176
-
177
- const { resolver, context } = options ;
178
- const resolvedUrl = await resolveRequests ( resolver , context , [
179
- ...new Set ( [ request , normalizedUrl ] ) ,
180
- ] ) ;
200
+ const { url, isRequestable, media } = resolvedAtRule ;
181
201
182
- return { url : resolvedUrl , media, prefix, isRequestable } ;
183
- }
184
-
185
- return { url, media, prefix, isRequestable } ;
186
- } ) ( )
187
- ) ;
188
- }
202
+ if ( ! isRequestable ) {
203
+ options . api . push ( { url, media, index } ) ;
189
204
190
- const results = await Promise . all ( tasks ) ;
191
-
192
- for ( let index = 0 ; index <= results . length - 1 ; index ++ ) {
193
- const item = results [ index ] ;
194
-
195
- if ( item === null ) {
196
205
// eslint-disable-next-line no-continue
197
206
continue ;
198
207
}
199
208
200
- const { url, isRequestable, media } = item ;
201
-
202
- if ( isRequestable ) {
203
- const { prefix } = item ;
204
- const newUrl = prefix ? `${ prefix } !${ url } ` : url ;
205
- const importKey = newUrl ;
206
- let importName = imports . get ( importKey ) ;
209
+ const { prefix } = resolvedAtRule ;
210
+ const newUrl = prefix ? `${ prefix } !${ url } ` : url ;
211
+ let importName = urlToNameMap . get ( newUrl ) ;
207
212
208
- if ( ! importName ) {
209
- importName = `___CSS_LOADER_AT_RULE_IMPORT_${ imports . size } ___` ;
210
- imports . set ( importKey , importName ) ;
213
+ if ( ! importName ) {
214
+ importName = `___CSS_LOADER_AT_RULE_IMPORT_${ urlToNameMap . size } ___` ;
215
+ urlToNameMap . set ( newUrl , importName ) ;
211
216
212
- options . imports . push ( {
213
- importName,
214
- url : options . urlHandler ( newUrl ) ,
215
- index,
216
- } ) ;
217
- }
218
-
219
- options . api . push ( { importName, media, index } ) ;
220
-
221
- // eslint-disable-next-line no-continue
222
- continue ;
217
+ options . imports . push ( {
218
+ importName,
219
+ url : options . urlHandler ( newUrl ) ,
220
+ index,
221
+ } ) ;
223
222
}
224
223
225
- options . api . push ( { url , media, index } ) ;
224
+ options . api . push ( { importName , media, index } ) ;
226
225
}
227
226
} ,
228
227
} ;
0 commit comments