@@ -11,6 +11,7 @@ import { dev } from '../../../state.js';
1111 * @typedef {{
1212 * code: MagicString;
1313 * hash: string;
14+ * minify: boolean;
1415 * selector: string;
1516 * keyframes: string[];
1617 * specificity: {
@@ -32,6 +33,7 @@ export function render_stylesheet(source, analysis, options) {
3233 const state = {
3334 code,
3435 hash : analysis . css . hash ,
36+ minify : analysis . inject_styles && ! options . dev ,
3537 selector : `.${ analysis . css . hash } ` ,
3638 keyframes : analysis . css . keyframes ,
3739 specificity : {
@@ -45,6 +47,9 @@ export function render_stylesheet(source, analysis, options) {
4547
4648 code . remove ( 0 , ast . content . start ) ;
4749 code . remove ( /** @type {number } */ ( ast . content . end ) , source . length ) ;
50+ if ( state . minify ) {
51+ remove_preceeding_whitespace ( ast . content . end , state ) ;
52+ }
4853
4954 const css = {
5055 code : code . toString ( ) ,
@@ -116,22 +121,47 @@ const visitors = {
116121
117122 index ++ ;
118123 }
124+ } else if ( state . minify ) {
125+ remove_preceeding_whitespace ( node . start , state ) ;
126+
127+ // Don't minify whitespace in custom properties, since some browsers (Chromium < 99)
128+ // treat --foo: ; and --foo:; differently
129+ if ( ! node . property . startsWith ( '--' ) ) {
130+ let start = node . start + node . property . length + 1 ;
131+ let end = start ;
132+ while ( / \s / . test ( state . code . original [ end ] ) ) end ++ ;
133+ if ( end > start ) state . code . remove ( start , end ) ;
134+ }
119135 }
120136 } ,
121137 Rule ( node , { state, next, visit } ) {
138+ if ( state . minify ) {
139+ remove_preceeding_whitespace ( node . start , state ) ;
140+ remove_preceeding_whitespace ( node . block . end - 1 , state ) ;
141+ }
142+
122143 // keep empty rules in dev, because it's convenient to
123144 // see them in devtools
124145 if ( ! dev && is_empty ( node ) ) {
125- state . code . prependRight ( node . start , '/* (empty) ' ) ;
126- state . code . appendLeft ( node . end , '*/' ) ;
127- escape_comment_close ( node , state . code ) ;
146+ if ( state . minify ) {
147+ state . code . remove ( node . start , node . end ) ;
148+ } else {
149+ state . code . prependRight ( node . start , '/* (empty) ' ) ;
150+ state . code . appendLeft ( node . end , '*/' ) ;
151+ escape_comment_close ( node , state . code ) ;
152+ }
153+
128154 return ;
129155 }
130156
131157 if ( ! is_used ( node ) ) {
132- state . code . prependRight ( node . start , '/* (unused) ' ) ;
133- state . code . appendLeft ( node . end , '*/' ) ;
134- escape_comment_close ( node , state . code ) ;
158+ if ( state . minify ) {
159+ state . code . remove ( node . start , node . end ) ;
160+ } else {
161+ state . code . prependRight ( node . start , '/* (unused) ' ) ;
162+ state . code . appendLeft ( node . end , '*/' ) ;
163+ escape_comment_close ( node , state . code ) ;
164+ }
135165
136166 return ;
137167 }
@@ -141,11 +171,16 @@ const visitors = {
141171
142172 if ( selector . children . length === 1 && selector . children [ 0 ] . selectors . length === 1 ) {
143173 // `:global {...}`
144- state . code . prependRight ( node . start , '/* ' ) ;
145- state . code . appendLeft ( node . block . start + 1 , '*/' ) ;
174+ if ( state . minify ) {
175+ state . code . remove ( node . start , node . block . start + 1 ) ;
176+ state . code . remove ( node . block . end - 1 , node . end ) ;
177+ } else {
178+ state . code . prependRight ( node . start , '/* ' ) ;
179+ state . code . appendLeft ( node . block . start + 1 , '*/' ) ;
146180
147- state . code . prependRight ( node . block . end - 1 , '/*' ) ;
148- state . code . appendLeft ( node . block . end , '*/' ) ;
181+ state . code . prependRight ( node . block . end - 1 , '/*' ) ;
182+ state . code . appendLeft ( node . block . end , '*/' ) ;
183+ }
149184
150185 // don't recurse into selector or body
151186 return ;
@@ -162,7 +197,8 @@ const visitors = {
162197 // Only add comments if we're not inside a complex selector that itself is unused
163198 if ( ! path . find ( ( n ) => n . type === 'ComplexSelector' && ! n . metadata . used ) ) {
164199 let pruning = false ;
165- let last = node . children [ 0 ] . start ;
200+ let prune_start = node . children [ 0 ] . start ;
201+ let last = prune_start ;
166202
167203 for ( let i = 0 ; i < node . children . length ; i += 1 ) {
168204 const selector = node . children [ i ] ;
@@ -172,12 +208,20 @@ const visitors = {
172208 let i = selector . start ;
173209 while ( state . code . original [ i ] !== ',' ) i -- ;
174210
175- state . code . overwrite ( i , i + 1 , '*/' ) ;
176- } else {
177- if ( i === 0 ) {
178- state . code . prependRight ( selector . start , '/* (unused) ' ) ;
211+ if ( state . minify ) {
212+ state . code . remove ( prune_start , i + 1 ) ;
179213 } else {
180- state . code . overwrite ( last , selector . start , ' /* (unused) ' ) ;
214+ state . code . overwrite ( i , i + 1 , '*/' ) ;
215+ }
216+ } else {
217+ prune_start = selector . start ;
218+
219+ if ( ! state . minify ) {
220+ if ( i === 0 ) {
221+ state . code . prependRight ( selector . start , '/* (unused) ' ) ;
222+ } else {
223+ state . code . overwrite ( last , selector . start , ' /* (unused) ' ) ;
224+ }
181225 }
182226 }
183227
@@ -188,7 +232,11 @@ const visitors = {
188232 }
189233
190234 if ( pruning ) {
191- state . code . appendLeft ( last , '*/' ) ;
235+ if ( state . minify ) {
236+ state . code . remove ( prune_start , last ) ;
237+ } else {
238+ state . code . appendLeft ( last , '*/' ) ;
239+ }
192240 }
193241 }
194242
@@ -320,6 +368,17 @@ const visitors = {
320368 }
321369} ;
322370
371+ /**
372+ * Walk backwards until we find a non-whitespace character
373+ * @param {number } end
374+ * @param {State } state
375+ */
376+ function remove_preceeding_whitespace ( end , state ) {
377+ let start = end ;
378+ while ( / \s / . test ( state . code . original [ start - 1 ] ) ) start -- ;
379+ if ( start < end ) state . code . remove ( start , end ) ;
380+ }
381+
323382/** @param {Css.Rule } rule */
324383function is_empty ( rule ) {
325384 if ( rule . metadata . is_global_block ) {
0 commit comments