@@ -15,11 +15,6 @@ export function hasCounters(input) {
1515 return / \b c o u n t e r \s * \( | \b c o u n t e r s \s * \( / . test ( input || '' )
1616}
1717
18- /** Replace every CSS string token "..." with its raw content (keeps single quotes). */
19- export function unquoteDoubleStrings ( s ) {
20- return ( s || '' ) . replace ( / " ( [ ^ " ] * ) " / g, '$1' )
21- }
22-
2318/**
2419 * a, b, ..., z, aa, ab, ...
2520 * @param {number } n
@@ -255,9 +250,8 @@ export function buildCounterContext(root) {
255250/**
256251 * Resolves counter()/counters() calls inside a content string for a specific node,
257252 * returning the content with counter() expanded but quoted-string tokens preserved.
258- * The caller is responsible for joining tokens (see pseudo.js collapseCssContent)
259- * or stripping quotes (see resolvePseudoContent below) — keeping quotes here lets
260- * collapseCssContent tokenize properly so source whitespace between adjacent
253+ * The caller (pseudo.js) is responsible for joining tokens and stripping quotes —
254+ * keeping quotes here lets its tokenizer work so source whitespace between adjacent
261255 * tokens (e.g. `counter(x) ")"`) doesn't leak into the rendered text.
262256 *
263257 * @param {string } raw
@@ -289,101 +283,3 @@ export function resolveCountersInContent(raw, node, ctx) {
289283 return '- '
290284 }
291285}
292-
293- /**
294- * Create a derived counter context that applies a pseudo's counter-reset /
295- * counter-increment *for this node only*, before resolving content.
296- * Works with ::before / ::after (and any pseudo with content).
297- *
298- * @param {Element } node
299- * @param {CSSStyleDeclaration|null } pseudoStyle getComputedStyle(node, '::before' | '::after')
300- * @param {{get(node: Element, name: string): number, getStack(node: Element, name: string): number[]} } baseCtx
301- */
302- export function deriveCounterCtxForPseudo ( node , pseudoStyle , baseCtx ) {
303- const modStacks = new Map ( )
304-
305- /** Parse "a 1, b -2" -> [{name:'a', num:1}, {name:'b', num:-2}] */
306- function parseListDecl ( value ) {
307- const out = [ ]
308- if ( ! value || value === 'none' ) return out
309- for ( const part of String ( value ) . split ( ',' ) ) {
310- const toks = part . trim ( ) . split ( / \s + / )
311- const name = toks [ 0 ]
312- const num = Number . isFinite ( Number ( toks [ 1 ] ) ) ? Number ( toks [ 1 ] ) : undefined
313- if ( name ) out . push ( { name, num } )
314- }
315- return out
316- }
317-
318- const resets = parseListDecl ( pseudoStyle ?. counterReset )
319- const sets = parseListDecl ( pseudoStyle ?. counterSet )
320- const incs = parseListDecl ( pseudoStyle ?. counterIncrement )
321-
322- function getStackDerived ( name ) {
323- if ( modStacks . has ( name ) ) return modStacks . get ( name ) . slice ( )
324-
325- // base stack at this node from the element context
326- let stack = baseCtx . getStack ( node , name )
327- stack = stack . length ? stack . slice ( ) : [ ]
328-
329- // counter-reset (push if exists, replace if not)
330- const r = resets . find ( x => x . name === name )
331- if ( r ) {
332- const val = Number . isFinite ( r . num ) ? r . num : 0
333- if ( stack . length ) {
334- stack = stack . slice ( )
335- stack . push ( val )
336- } else {
337- stack = [ val ]
338- }
339- }
340-
341- // counter-set (set top value without creating a new scope)
342- const s = sets . find ( x => x . name === name )
343- if ( s ) {
344- const val = Number . isFinite ( s . num ) ? s . num : 0
345- if ( stack . length === 0 ) stack = [ 0 ]
346- stack [ stack . length - 1 ] = val
347- }
348-
349- // counter-increment (on top; create top=0 if missing)
350- const inc = incs . find ( x => x . name === name )
351- if ( inc ) {
352- const by = Number . isFinite ( inc . num ) ? inc . num : 1
353- if ( stack . length === 0 ) stack = [ 0 ]
354- stack [ stack . length - 1 ] += by
355- }
356-
357- modStacks . set ( name , stack . slice ( ) )
358- return stack
359- }
360-
361- return {
362- get ( _node , name ) {
363- const s = getStackDerived ( name )
364- return s . length ? s [ s . length - 1 ] : 0
365- } ,
366- getStack ( _node , name ) {
367- return getStackDerived ( name )
368- }
369- }
370- }
371-
372- /**
373- * Convenience helper: resolve the final text to render for a pseudo's `content`,
374- * correctly applying the pseudo's own counter-reset/increment before evaluation.
375- *
376- * @param {Element } node
377- * @param {'::before'|'::after' } pseudo
378- * @param {{get(node: Element, name: string): number, getStack(node: Element, name: string): number[]} } baseCtx
379- * @returns {string } resolved content (without surrounding double quotes)
380- */
381- export function resolvePseudoContent ( node , pseudo , baseCtx ) {
382- let ps
383- try { ps = getComputedStyle ( node , pseudo ) } catch { }
384- const raw = ps ?. content
385- if ( ! raw || raw === 'none' || raw === 'normal' ) return ''
386- const derived = deriveCounterCtxForPseudo ( node , ps , baseCtx )
387- let out = resolveCountersInContent ( raw , node , derived )
388- return unquoteDoubleStrings ( out )
389- }
0 commit comments