@@ -59,128 +59,80 @@ function walkUrls(parsed, callback) {
5959 } ) ;
6060}
6161
62- function getUrlsFromValue ( value , result , filter , decl ) {
63- if ( ! needParseDecl . test ( value ) ) {
64- return ;
65- }
66-
67- const parsed = valueParser ( value ) ;
68- const urls = [ ] ;
69-
70- walkUrls ( parsed , ( node , url , needQuotes , isStringValue ) => {
71- if ( url . trim ( ) . replace ( / \\ [ \r \n ] / g, '' ) . length === 0 ) {
72- result . warn ( `Unable to find uri in '${ decl ? decl . toString ( ) : value } '` , {
73- node : decl ,
74- } ) ;
75-
76- return ;
77- }
62+ export default postcss . plugin ( pluginName , ( options ) => ( css , result ) => {
63+ const importsMap = new Map ( ) ;
64+ const replacersMap = new Map ( ) ;
7865
79- if ( filter && ! filter ( url ) ) {
66+ css . walkDecls ( ( decl ) => {
67+ if ( ! needParseDecl . test ( decl . value ) ) {
8068 return ;
8169 }
8270
83- const splittedUrl = url . split ( / ( \? ) ? # / ) ;
84- const [ urlWithoutHash , singleQuery , hashValue ] = splittedUrl ;
85- const hash =
86- singleQuery || hashValue
87- ? `${ singleQuery ? '?' : '' } ${ hashValue ? `#${ hashValue } ` : '' } `
88- : '' ;
89-
90- const normalizedUrl = normalizeUrl ( urlWithoutHash , isStringValue ) ;
91-
92- urls . push ( { node, url : normalizedUrl , hash, needQuotes } ) ;
93- } ) ;
94-
95- // eslint-disable-next-line consistent-return
96- return { parsed, urls } ;
97- }
98-
99- function walkDecls ( css , result , filter ) {
100- const items = [ ] ;
71+ const parsed = valueParser ( decl . value ) ;
10172
102- css . walkDecls ( ( decl ) => {
103- const item = getUrlsFromValue ( decl . value , result , filter , decl ) ;
73+ walkUrls ( parsed , ( node , url , needQuotes , isStringValue ) => {
74+ if ( url . trim ( ) . replace ( / \\ [ \r \n ] / g, '' ) . length === 0 ) {
75+ result . warn (
76+ `Unable to find uri in '${ decl ? decl . toString ( ) : decl . value } '` ,
77+ { node : decl }
78+ ) ;
10479
105- if ( ! item || item . urls . length === 0 ) {
106- return ;
107- }
80+ return ;
81+ }
10882
109- items . push ( { decl, parsed : item . parsed , urls : item . urls } ) ;
110- } ) ;
83+ if ( options . filter && ! options . filter ( url ) ) {
84+ return ;
85+ }
11186
112- return items ;
113- }
87+ const splittedUrl = url . split ( / ( \? ) ? # / ) ;
88+ const [ urlWithoutHash , singleQuery , hashValue ] = splittedUrl ;
89+ const hash =
90+ singleQuery || hashValue
91+ ? `${ singleQuery ? '?' : '' } ${ hashValue ? `#${ hashValue } ` : '' } `
92+ : '' ;
11493
115- function flatten ( array ) {
116- return array . reduce ( ( a , b ) => a . concat ( b ) , [ ] ) ;
117- }
94+ const normalizedUrl = normalizeUrl ( urlWithoutHash , isStringValue ) ;
11895
119- function collectUniqueUrlsWithNodes ( array ) {
120- return array . reduce ( ( accumulator , currentValue ) => {
121- const { url, needQuotes, hash, node } = currentValue ;
122- const found = accumulator . find (
123- ( item ) =>
124- url === item . url && needQuotes === item . needQuotes && hash === item . hash
125- ) ;
126-
127- if ( ! found ) {
128- accumulator . push ( { url, hash, needQuotes, nodes : [ node ] } ) ;
129- } else {
130- found . nodes . push ( node ) ;
131- }
96+ const importKey = normalizedUrl ;
97+ let importName = importsMap . get ( importKey ) ;
13298
133- return accumulator ;
134- } , [ ] ) ;
135- }
99+ if ( ! importName ) {
100+ importName = `___CSS_LOADER_URL_IMPORT_ ${ importsMap . size } ___` ;
101+ importsMap . set ( importKey , importName ) ;
136102
137- export default postcss . plugin (
138- pluginName ,
139- ( options ) =>
140- function process ( css , result ) {
141- const traversed = walkDecls ( css , result , options . filter ) ;
142- const flattenTraversed = flatten ( traversed . map ( ( item ) => item . urls ) ) ;
143- const urlsWithNodes = collectUniqueUrlsWithNodes ( flattenTraversed ) ;
144- const replacers = new Map ( ) ;
145-
146- urlsWithNodes . forEach ( ( urlWithNodes , index ) => {
147- const { url, hash, needQuotes, nodes } = urlWithNodes ;
148- const replacementName = `___CSS_LOADER_URL_REPLACEMENT_${ index } ___` ;
149-
150- result . messages . push (
151- {
152- pluginName,
153- type : 'import' ,
154- value : { type : 'url' , replacementName, url, needQuotes, hash } ,
103+ result . messages . push ( {
104+ pluginName,
105+ type : 'import' ,
106+ value : {
107+ type : 'url' ,
108+ importName,
109+ url : normalizedUrl ,
155110 } ,
156- {
157- pluginName,
158- type : 'replacer' ,
159- value : { type : 'url' , replacementName } ,
160- }
161- ) ;
162-
163- nodes . forEach ( ( node ) => {
164- replacers . set ( node , replacementName ) ;
165111 } ) ;
166- } ) ;
112+ }
167113
168- traversed . forEach ( ( item ) => {
169- walkUrls ( item . parsed , ( node ) => {
170- const replacementName = replacers . get ( node ) ;
114+ const replacerKey = JSON . stringify ( { importKey, hash, needQuotes } ) ;
171115
172- if ( ! replacementName ) {
173- return ;
174- }
116+ let replacerName = replacersMap . get ( replacerKey ) ;
175117
176- // eslint-disable-next-line no-param-reassign
177- node . type = 'word' ;
178- // eslint-disable-next-line no-param-reassign
179- node . value = replacementName ;
118+ if ( ! replacerName ) {
119+ replacerName = `___CSS_LOADER_URL_REPLACEMENT_${ replacersMap . size } ___` ;
120+ replacersMap . set ( replacerKey , replacerName ) ;
121+
122+ result . messages . push ( {
123+ pluginName,
124+ type : 'replacer' ,
125+ value : { type : 'url' , replacerName, importName, hash, needQuotes } ,
180126 } ) ;
127+ }
181128
182- // eslint-disable-next-line no-param-reassign
183- item . decl . value = item . parsed . toString ( ) ;
184- } ) ;
185- }
186- ) ;
129+ // eslint-disable-next-line no-param-reassign
130+ node . type = 'word' ;
131+ // eslint-disable-next-line no-param-reassign
132+ node . value = replacerName ;
133+ } ) ;
134+
135+ // eslint-disable-next-line no-param-reassign
136+ decl . value = parsed . toString ( ) ;
137+ } ) ;
138+ } ) ;
0 commit comments