@@ -127,61 +127,21 @@ export function escapeImportStatement(rule: CSSImportRule): string {
127
127
export function stringifyStylesheet ( s : CSSStyleSheet ) : string | null {
128
128
try {
129
129
const rules = s . rules || s . cssRules ;
130
- const stringifiedRules = [ ] as string [ ] ;
131
- for ( let i = 0 ; i < rules . length ; ++ i ) {
132
- stringifiedRules . push ( stringifyRule ( rules [ i ] ) ) ;
130
+ if ( ! rules ) {
131
+ return null ;
133
132
}
134
- return rules
135
- ? fixBrowserCompatibilityIssuesInCSS ( stringifiedRules . join ( '' ) )
136
- : null ;
133
+ const stringifiedRules = Array . from ( rules , ( rule : CSSRule ) =>
134
+ stringifyRule ( rule , s . href ) ,
135
+ ) . join ( '' ) ;
136
+ return fixBrowserCompatibilityIssuesInCSS ( stringifiedRules ) ;
137
137
} catch ( error ) {
138
138
return null ;
139
139
}
140
140
}
141
141
142
- function replaceChromeGridTemplateAreas ( rule : CSSStyleRule ) : string {
143
- const hasGridTemplateInCSSText = rule . cssText . includes ( 'grid-template:' ) ;
144
- const hasGridTemplateAreaInStyleRules =
145
- rule . style . getPropertyValue ( 'grid-template-areas' ) !== '' ;
146
- const hasGridTemplateAreaInCSSText = rule . cssText . includes (
147
- 'grid-template-areas:' ,
148
- ) ;
149
- if (
150
- isCSSStyleRule ( rule ) &&
151
- hasGridTemplateInCSSText &&
152
- hasGridTemplateAreaInStyleRules &&
153
- ! hasGridTemplateAreaInCSSText
154
- ) {
155
- // chrome does not correctly provide the grid template areas in the rules cssText
156
- // e.g. https://bugs.chromium.org/p/chromium/issues/detail?id=1303968
157
- // we remove the grid-template rule from the text... so everything from grid-template: to the next semicolon
158
- // and then add each grid-template-x rule into the css text because Chrome isn't doing this correctly
159
- const parts = rule . cssText
160
- . split ( ';' )
161
- . filter ( ( s ) => ! s . includes ( 'grid-template:' ) )
162
- . map ( ( s ) => s . trim ( ) ) ;
163
-
164
- const gridStyles : string [ ] = [ ] ;
165
-
166
- for ( let i = 0 ; i < rule . style . length ; i ++ ) {
167
- const styleName = rule . style [ i ] ;
168
- if ( styleName . startsWith ( 'grid-template' ) ) {
169
- gridStyles . push (
170
- `${ styleName } : ${ rule . style . getPropertyValue ( styleName ) } ` ,
171
- ) ;
172
- }
173
- }
174
- parts . splice ( parts . length - 1 , 0 , gridStyles . join ( '; ' ) ) ;
175
- return parts . join ( '; ' ) ;
176
- }
177
- return rule . cssText ;
178
- }
179
-
180
- export function stringifyRule ( rule : CSSRule ) : string {
181
- let importStringified ;
182
- let gridTemplateFixed ;
183
-
142
+ export function stringifyRule ( rule : CSSRule , sheetHref : string | null ) : string {
184
143
if ( isCSSImportRule ( rule ) ) {
144
+ let importStringified ;
185
145
try {
186
146
importStringified =
187
147
// for same-origin stylesheets,
@@ -190,19 +150,25 @@ export function stringifyRule(rule: CSSRule): string {
190
150
// work around browser issues with the raw string `@import url(...)` statement
191
151
escapeImportStatement ( rule ) ;
192
152
} catch ( error ) {
193
- // ignore
153
+ importStringified = rule . cssText ;
194
154
}
195
- } else if ( isCSSStyleRule ( rule ) && rule . selectorText . includes ( ':' ) ) {
196
- // Safari does not escape selectors with : properly
197
- // see https://bugs.webkit.org/show_bug.cgi?id=184604
198
- return fixSafariColons ( rule . cssText ) ;
199
- }
200
-
201
- if ( isCSSStyleRule ( rule ) ) {
202
- gridTemplateFixed = replaceChromeGridTemplateAreas ( rule ) ;
155
+ if ( rule . styleSheet . href ) {
156
+ // url()s within the imported stylesheet are relative to _that_ sheet's href
157
+ return absolutifyURLs ( importStringified , rule . styleSheet . href ) ;
158
+ }
159
+ return importStringified ;
160
+ } else {
161
+ let ruleStringified = rule . cssText ;
162
+ if ( isCSSStyleRule ( rule ) && rule . selectorText . includes ( ':' ) ) {
163
+ // Safari does not escape selectors with : properly
164
+ // see https://bugs.webkit.org/show_bug.cgi?id=184604
165
+ ruleStringified = fixSafariColons ( ruleStringified ) ;
166
+ }
167
+ if ( sheetHref ) {
168
+ return absolutifyURLs ( ruleStringified , sheetHref ) ;
169
+ }
170
+ return ruleStringified ;
203
171
}
204
-
205
- return importStringified || gridTemplateFixed || rule . cssText ;
206
172
}
207
173
208
174
export function fixSafariColons ( cssStringified : string ) : string {
@@ -431,3 +397,62 @@ export function extractFileExtension(
431
397
const match = url . pathname . match ( regex ) ;
432
398
return match ?. [ 1 ] ?? null ;
433
399
}
400
+
401
+ function extractOrigin ( url : string ) : string {
402
+ let origin = '' ;
403
+ if ( url . indexOf ( '//' ) > - 1 ) {
404
+ origin = url . split ( '/' ) . slice ( 0 , 3 ) . join ( '/' ) ;
405
+ } else {
406
+ origin = url . split ( '/' ) [ 0 ] ;
407
+ }
408
+ origin = origin . split ( '?' ) [ 0 ] ;
409
+ return origin ;
410
+ }
411
+
412
+ const URL_IN_CSS_REF = / u r l \( (?: ( ' ) ( [ ^ ' ] * ) ' | ( " ) ( .* ?) " | ( [ ^ ) ] * ) ) \) / gm;
413
+ const URL_PROTOCOL_MATCH = / ^ (?: [ a - z + ] + : ) ? \/ \/ / i;
414
+ const URL_WWW_MATCH = / ^ w w w \. .* / i;
415
+ const DATA_URI = / ^ ( d a t a : ) ( [ ^ , ] * ) , ( .* ) / i;
416
+ export function absolutifyURLs ( cssText : string | null , href : string ) : string {
417
+ return ( cssText || '' ) . replace (
418
+ URL_IN_CSS_REF ,
419
+ (
420
+ origin : string ,
421
+ quote1 : string ,
422
+ path1 : string ,
423
+ quote2 : string ,
424
+ path2 : string ,
425
+ path3 : string ,
426
+ ) => {
427
+ const filePath = path1 || path2 || path3 ;
428
+ const maybeQuote = quote1 || quote2 || '' ;
429
+ if ( ! filePath ) {
430
+ return origin ;
431
+ }
432
+ if ( URL_PROTOCOL_MATCH . test ( filePath ) || URL_WWW_MATCH . test ( filePath ) ) {
433
+ return `url(${ maybeQuote } ${ filePath } ${ maybeQuote } )` ;
434
+ }
435
+ if ( DATA_URI . test ( filePath ) ) {
436
+ return `url(${ maybeQuote } ${ filePath } ${ maybeQuote } )` ;
437
+ }
438
+ if ( filePath [ 0 ] === '/' ) {
439
+ return `url(${ maybeQuote } ${
440
+ extractOrigin ( href ) + filePath
441
+ } ${ maybeQuote } )`;
442
+ }
443
+ const stack = href . split ( '/' ) ;
444
+ const parts = filePath . split ( '/' ) ;
445
+ stack . pop ( ) ;
446
+ for ( const part of parts ) {
447
+ if ( part === '.' ) {
448
+ continue ;
449
+ } else if ( part === '..' ) {
450
+ stack . pop ( ) ;
451
+ } else {
452
+ stack . push ( part ) ;
453
+ }
454
+ }
455
+ return `url(${ maybeQuote } ${ stack . join ( '/' ) } ${ maybeQuote } )` ;
456
+ } ,
457
+ ) ;
458
+ }
0 commit comments