Skip to content

Commit

Permalink
updated: Improve the css selector replacement strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
GianlucaGuarini committed Jul 12, 2024
1 parent 4dc8c83 commit d7c5e75
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 5 deletions.
22 changes: 17 additions & 5 deletions src/generators/css/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import cssEscape from 'cssesc'
import CSSParser from 'css-simple-parser'
import getPreprocessorTypeByAttribute from '../../utils/get-preprocessor-type-by-attribute.js'
import preprocess from '../../utils/preprocess-node.js'
import replaceInRange from '../../utils/replace-in-range.js'

const HOST = ':host'
const DISABLED_SELECTORS = ['from', 'to']
Expand All @@ -18,9 +19,10 @@ const R_MLCOMMS = /\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//g
/**
* Matches the list of css selectors excluding the pseudo selectors
* @const {RegExp}
* @static
*/

const CSS_SELECTOR_LIST =
const R_CSS_SELECTOR_LIST =
/([^,]+)(?::(?!host)\w+(?:[\s|\S]*?\))?(?:[^,:]*)?)+|([^,]+)/g

/**
Expand All @@ -30,7 +32,7 @@ const CSS_SELECTOR_LIST =
* @returns {string} scoped selectors
*/
export function addScopeToSelectorList(tag, selectorList) {
return selectorList.replace(CSS_SELECTOR_LIST, (match, selector) => {
return selectorList.replace(R_CSS_SELECTOR_LIST, (match, selector) => {
const trimmedMatch = match.trim()
const trimmedSelector = selector ? selector.trim() : trimmedMatch
// skip selectors already using the tag name
Expand Down Expand Up @@ -70,7 +72,7 @@ const traverse = (ast, fn) => {
const { children } = ast

children.forEach((child) => {
// if fn returns false we stop the recurstion
// if fn returns false we stop the recursion
if (fn(child) !== false) traverse(child, fn)
})

Expand All @@ -87,15 +89,25 @@ const traverse = (ast, fn) => {
*/
export function generateScopedCss(tag, css) {
const ast = CSSParser.parse(css)
const originalCssLength = css.length

traverse(ast, (node) => {
// calculate the selector offset from the original css length
const newSelectorOffset = css.length - originalCssLength

if (!node.selector.trim().startsWith('@')) {
// the css parser doesn't detect the comments so we manually remove them
const selector = node.selector.replace(R_MLCOMMS, '')

// replace the selector and override the original css
css = css.replace(node.selector, addScopeToSelectorList(tag, selector))
// stop the recurstion
css = replaceInRange(
css,
node.selectorIndex + newSelectorOffset,
node.selectorIndexEnd + newSelectorOffset,
addScopeToSelectorList(tag, selector),
)

// stop the recursion
return false
}
})
Expand Down
16 changes: 16 additions & 0 deletions src/utils/replace-in-range.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Replace a text chunk in a range
* @param {string} originalString - the text we need to patch
* @param {number} start - the start offset where the string should be replaced
* @param {number} end - the end where the string replacement should finish
* @param {string} replacement - the string we need to insert
* @return {string} the original text patched with the replacement string
*/
export default function replaceInRange(
originalString,
start,
end,
replacement,
) {
return `${originalString.substring(0, start)}${replacement}${originalString.substring(end)}`
}

0 comments on commit d7c5e75

Please sign in to comment.