@@ -2,7 +2,8 @@ type Combinator = '>' | '~' | '+';
22
33interface Options {
44 duplicates ?: 'preserve' | 'remove' ;
5- fill ?: 'fill' | 'no-fill'
5+ fill ?: 'fill' | 'no-fill' ;
6+ mergeNth ?: 'merge' | 'no-merge'
67}
78
89/**
@@ -21,6 +22,20 @@ export function cssToHtml(css: CSSRuleList | string, options: Options = {}): HTM
2122 }
2223 return false ;
2324 }
25+ function mergeElements < T extends HTMLElement | Element > ( mergeFrom : HTMLElement | Element , mergeTo : T ) : T | null {
26+ if ( mergeFrom . tagName !== mergeTo . tagName ) {
27+ return null ;
28+ }
29+ if ( mergeFrom . id && mergeTo . id && mergeFrom . id !== mergeTo . id ) {
30+ return null ;
31+ }
32+ if ( mergeFrom . id ) {
33+ mergeTo . id = mergeFrom . id ;
34+ }
35+ mergeTo . className += ' ' + mergeFrom . className ;
36+ mergeTo . className = mergeTo . className . trim ( ) ;
37+ return mergeTo ;
38+ }
2439 let styleRules : CSSRuleList | undefined ;
2540
2641 // Parse the CSS string into a CSSOM.
@@ -202,20 +217,38 @@ export function cssToHtml(css: CSSRuleList | string, options: Options = {}): HTM
202217 descriptor . previousElement = newElement ;
203218
204219 if ( fillType === 'first' ) {
205- parentElement . prepend ( newElement ) ;
220+ // Check if there is a sibling element in the desired position.
221+ const blockingSibling = parentElement . querySelector ( childType === 'type' ? `${ newElement . tagName } :first-of-type` : '*:first-child' ) ;
222+ if ( blockingSibling ) {
223+ parentElement . insertBefore ( newElement , blockingSibling ) ;
224+ if ( isFillerElement ( blockingSibling ) || ( options . mergeNth !== 'no-merge' && mergeElements ( blockingSibling , newElement ) ) ) {
225+ blockingSibling . remove ( ) ;
226+ }
227+ } else {
228+ parentElement . prepend ( newElement ) ;
229+ }
206230 return ;
207231 }
208232
209233 if ( fillType === 'last' ) {
210- parentElement . append ( newElement ) ;
234+ // Check if there is a sibling element in the desired position.
235+ const blockingSibling = parentElement . querySelector ( childType === 'type' ? `${ newElement . tagName } :last-of-type` : `${ newElement . tagName } :last-child` ) ;
236+ if ( blockingSibling ) {
237+ parentElement . insertBefore ( newElement , blockingSibling . nextElementSibling ) ;
238+ if ( isFillerElement ( blockingSibling ) || ( options . mergeNth !== 'no-merge' && mergeElements ( blockingSibling , newElement ) ) ) {
239+ blockingSibling . remove ( ) ;
240+ }
241+ } else {
242+ parentElement . append ( newElement ) ;
243+ }
211244 return ;
212245 }
213246
214247 // Check if there is a sibling element in the desired position.
215248 const blockingSibling = parentElement . querySelector ( childType === 'type' ? `${ newElement . tagName } :nth-of-type(${ fillAmount } )` : `:nth-child(${ fillAmount } )` ) ;
216249 if ( blockingSibling ) {
217250 parentElement . insertBefore ( newElement , blockingSibling ) ;
218- if ( isFillerElement ( blockingSibling ) ) {
251+ if ( isFillerElement ( blockingSibling ) || ( options . mergeNth !== 'no-merge' && mergeElements ( blockingSibling , newElement ) ) ) {
219252 blockingSibling . remove ( ) ;
220253 }
221254 return ;
0 commit comments