@@ -8,19 +8,19 @@ import {
88 webpackIgnoreCommentRegexp ,
99} from "../utils" ;
1010
11- function visitor ( result , parsedResults , node , key ) {
11+ function parseNode ( atRule , key ) {
1212 // Convert only top-level @import
13- if ( node . parent . type !== "root" ) {
13+ if ( atRule . parent . type !== "root" ) {
1414 return ;
1515 }
1616
1717 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
2121 ) {
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
2424 . slice ( lastCommentIndex )
2525 . match ( webpackIgnoreCommentRegexp ) ;
2626
@@ -29,7 +29,7 @@ function visitor(result, parsedResults, node, key) {
2929 }
3030 }
3131
32- const prevNode = node . prev ( ) ;
32+ const prevNode = atRule . prev ( ) ;
3333
3434 if ( prevNode && prevNode . type === "comment" ) {
3535 const matched = prevNode . text . match ( webpackIgnoreCommentRegexp ) ;
@@ -40,26 +40,29 @@ function visitor(result, parsedResults, node, key) {
4040 }
4141
4242 // 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."
4746 ) ;
4847
49- return ;
48+ error . node = atRule ;
49+
50+ throw error ;
5051 }
5152
52- const { nodes : paramsNodes } = valueParser ( node [ key ] ) ;
53+ const { nodes : paramsNodes } = valueParser ( atRule [ key ] ) ;
5354
5455 // No nodes - `@import ;`
5556 // Invalid type - `@import foo-bar;`
5657 if (
5758 paramsNodes . length === 0 ||
5859 ( paramsNodes [ 0 ] . type !== "string" && paramsNodes [ 0 ] . type !== "function" )
5960 ) {
60- result . warn ( `Unable to find uri in "${ node . toString ( ) } "` , { node } ) ;
61+ const error = new Error ( `Unable to find uri in "${ atRule . toString ( ) } "` ) ;
6162
62- return ;
63+ error . node = atRule ;
64+
65+ throw error ;
6366 }
6467
6568 let isStringValue ;
@@ -71,9 +74,11 @@ function visitor(result, parsedResults, node, key) {
7174 } else {
7275 // Invalid function - `@import nourl(test.css);`
7376 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 ( ) } "` ) ;
7578
76- return ;
79+ error . node = atRule ;
80+
81+ throw error ;
7782 }
7883
7984 isStringValue =
@@ -84,145 +89,139 @@ function visitor(result, parsedResults, node, key) {
8489 : valueParser . stringify ( paramsNodes [ 0 ] . nodes ) ;
8590 }
8691
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+
87106 // Empty url - `@import "";` or `@import url();`
88107 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 ( ) } "` ) ;
90109
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 ( ) ;
92120 }
93121
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 } ;
100124}
101125
102126const plugin = ( options = { } ) => {
103127 return {
104128 postcssPlugin : "postcss-import-parser" ,
105129 prepare ( result ) {
106- const parsedResults = [ ] ;
130+ const parsedAtRules = [ ] ;
107131
108132 return {
109133 AtRule : {
110134 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 ) ;
112148 } ,
113149 } ,
114150 async OnceExit ( ) {
115- if ( parsedResults . length === 0 ) {
151+ if ( parsedAtRules . length === 0 ) {
116152 return ;
117153 }
118154
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+ }
124172
125- let normalizedUrl = url ;
126- let prefix = "" ;
173+ atRule . remove ( ) ;
127174
128- const isRequestable = isUrlRequestable ( normalizedUrl ) ;
175+ if ( isRequestable ) {
176+ const request = requestify ( url , options . rootContext ) ;
129177
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+ ] ) ;
132182
133- if ( queryParts . length > 1 ) {
134- normalizedUrl = queryParts . pop ( ) ;
135- prefix = queryParts . join ( "!" ) ;
183+ return { url : resolvedUrl , media, prefix, isRequestable } ;
136184 }
137185
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+ ) ;
148189
149- // eslint-disable-next-line no-continue
150- continue ;
151- }
152- }
190+ const urlToNameMap = new Map ( ) ;
153191
154- let media ;
192+ for ( let index = 0 ; index <= resolvedAtRules . length - 1 ; index ++ ) {
193+ const resolvedAtRule = resolvedAtRules [ index ] ;
155194
156- if ( mediaNodes . length > 0 ) {
157- media = valueParser . stringify ( mediaNodes ) . trim ( ) . toLowerCase ( ) ;
195+ if ( ! resolvedAtRule ) {
196+ // eslint-disable-next-line no-continue
197+ continue ;
158198 }
159199
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 ;
181201
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 } ) ;
189204
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 ) {
196205 // eslint-disable-next-line no-continue
197206 continue ;
198207 }
199208
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 ) ;
207212
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 ) ;
211216
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+ } ) ;
223222 }
224223
225- options . api . push ( { url , media, index } ) ;
224+ options . api . push ( { importName , media, index } ) ;
226225 }
227226 } ,
228227 } ;
0 commit comments