Skip to content

Commit

Permalink
use postcss-value-parser for light-dark (#32)
Browse files Browse the repository at this point in the history
* use postcss-value-parser for light-dark

* replace var with let

* fix lint errors
  • Loading branch information
VladBrok committed Apr 21, 2024
1 parent 470b5f9 commit 2c66669
Show file tree
Hide file tree
Showing 4 changed files with 1,247 additions and 1,513 deletions.
56 changes: 23 additions & 33 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
let valueParser = require('postcss-value-parser');

const PREFERS_COLOR_ONLY = /^\(\s*prefers-color-scheme\s*:\s*(dark|light)\s*\)$/
const PREFERS_COLOR = /\(\s*prefers-color-scheme\s*:\s*(dark|light)\s*\)/g
const LIGHT_DARK =
/light-dark\(\s*((?:[^(),]|\(.+\))+?)\s*,\s*((?:[^(),]|\(.+\))+?)\s*\)/gs
const STRING = /"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'/dg

function escapeRegExp(string) {
return string.replace(/[$()*+.?[\\\]^{|}-]/g, '\\$&')
Expand Down Expand Up @@ -35,17 +34,24 @@ function addColorSchemeMedia(isDark, propValue, declaration, postcss) {
declaration.parent.after(mediaQuery)
}

function replaceLightDark(isDark, declarationValue, stringBoundaries) {
return declarationValue.replaceAll(
LIGHT_DARK,
(match, lightColor, darkColor, offset) => {
let isInsideString = stringBoundaries.some(
boundary => offset > boundary[0] && offset < boundary[1]
)
if (isInsideString) return match
return replaceLightDark(isDark, isDark ? darkColor : lightColor, [])
}
)
function extractLightDark(isDark, declarationValue) {
let parsed = valueParser(declarationValue)
mutateLightDarkRec(isDark, parsed)
return valueParser.stringify(parsed)
}

function mutateLightDarkRec(isDark, parsed) {
let wasMutated = false
parsed.walk(node => {
if (wasMutated || node.type !== 'function' || node.value !== 'light-dark') return

let light = node.nodes[0]
let dark = node.nodes.find((x, i) => i > 0 && (x.type === 'word' || x.type === 'function'))
Object.assign(node, isDark ? dark : light)
mutateLightDarkRec(isDark, parsed)
wasMutated = true
return false
})
}

module.exports = (opts = {}) => {
Expand Down Expand Up @@ -151,25 +157,9 @@ module.exports = (opts = {}) => {
DeclarationExit: (declaration, { postcss }) => {
if (!declaration.value.includes('light-dark')) return

let stringBoundaries = []
let value = declaration.value.slice()
let match = STRING.exec(value)
while (match) {
stringBoundaries.push(match.indices[0])
match = STRING.exec(value)
}

let lightValue = replaceLightDark(
false,
declaration.value,
stringBoundaries
)
if (declaration.value === lightValue) return
let darkValue = replaceLightDark(
true,
declaration.value,
stringBoundaries
)
let lightValue = extractLightDark(false, declaration.value)
if (lightValue === declaration.value) return
let darkValue = extractLightDark(true, declaration.value)

addColorSchemeMedia(false, lightValue, declaration, postcss)
addColorSchemeMedia(true, darkValue, declaration, postcss)
Expand Down
24 changes: 24 additions & 0 deletions index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -701,3 +701,27 @@ body:where(.is-light) {
{ rootSelector: 'body' }
)
})

test('transforms complex nested light-dark()', () => {
run(
`.light-dark-function-mix-a {
color: light-dark(color-mix(in oklch, red, light-dark(cyan, rgb(0, 0, 0))), blue);
}`,
`@media (prefers-color-scheme:dark) {
:where(html:not(.is-light)) .light-dark-function-mix-a {
color: blue
}
}
:where(html.is-dark) .light-dark-function-mix-a {
color: blue
}
@media (prefers-color-scheme:light) {
:where(html:not(.is-dark)) .light-dark-function-mix-a {
color: color-mix(in oklch, red, cyan)
}
}
:where(html.is-light) .light-dark-function-mix-a {
color: color-mix(in oklch, red, cyan)
}`
)
})
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,8 @@
},
"clean-publish": {
"cleanDocs": true
},
"dependencies": {
"postcss-value-parser": "^4.2.0"
}
}
Loading

0 comments on commit 2c66669

Please sign in to comment.