8000 117: Support backslash sequences and unicode transformation (#118) · postcss/postcss-simple-vars@65ca9ec · GitHub
Skip to content

Commit 65ca9ec

Browse files
authored
117: Support backslash sequences and unicode transformation (#118)
As detailed in issue #117, it was not previously possible to create variables containing certain characters, specifically `:` (\u003A) in the case of #117. This commit introduces support for "backslash sequences", i.e. sequences immediately following a backslash (`\`). Specifically, the following support is introduced: * Escaped backslash sequences, i.e. `\\u003A` => `\u003A` * 4 character unicode escapes, i.e. `\u003A` => `:` * 8 character unicode escapes, i.e. `\U0001f389` => `🎉`
1 parent aaa29b9 commit 65ca9ec

3 files changed

Lines changed: 60 additions & 4 deletions

File tree

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,23 @@ If you want to escape `$` in the `content` property, use Unicode escape syntax.
9292
}
9393
```
9494

95+
If you want to use e.g. `:` in a selector variable, use the corresponding unicode escape sequence:
96+
97+
```css
98+
/* Given */
99+
$selector: .my-component[data-emoji="\U0001f389"]\u003Adisabled;
100+
101+
$selector {
102+
width: 1px;
103+
}
104+
105+
/* Generates */
106+
.my-component[data-emoji="🎉"]:disabled {
107+
width: 1px;
108+
}
109+
```
110+
111+
Note: Use `\u` for 4-digit sequences and `\U` for 8-digit sequences.
95112

96113
## Usage
97114

index.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const IGNORE = Symbol('ignore')
22

33
function definition (variables, node, opts) {
44
let name = node.prop.slice(1)
5-
variables[name] = node.value
5+
variables[name] = transformBackslashSequences(node.value)
66

77
if (!opts.keep) {
88
node.remove()
@@ -37,6 +37,26 @@ function variable (variables, node, str, name, opts, result) {
3737
return str
3838
}
3939

40+
function transformBackslashSequences (value) {
41+
if (typeof value !== 'string') return value
42+
43+
return (
44+
value
45+
// Unicode 8-digit code support (\Uxxxxxxxx)
46+
// eslint-disable-next-line security/detect-unsafe-regex
47+
.replace(/(?<=[^\\]|^)\\U([0-9abcdefABCDEF]{8})/g, (_, cp) =>
48+
String.fromCodePoint(`0x${cp}`)
49+
)
50+
// Unicode 4-digit code support (\uxxxx)
51+
// eslint-disable-next-line security/detect-unsafe-regex
52+
.replace(/(?<=[^\\]|^)\\u([0-9abcdefABCDEF]{4})/g, (_, cp) =>
53+
String.fromCodePoint(`0x${cp}`)
54+
)
55+
// Backslash
56+
.replace(/\\\\/g, '\\')
57+
)
58+
}
59+
4060
function simpleSyntax (variables, node, str, opts, result) {
4161
return str.replace(/(^|[^\w])\$([\w\d-_]+)/g, (_, bef, name) => {
4262
return bef + variable(variables, node, '$' + name, name, opts, result)
@@ -140,10 +160,11 @@ module.exports = (opts = {}) => {
140160

141161
for (let name in variables) {
142162
if (name[0] === '$') {
143-
let fixed = name.slice(1)
144-
variables[fixed] = variables[name]
145-
delete variables[name]
163+
name = name.slice(1)
164+
variables[name] = variables[`$${name}`]
165+
delete variables[`$${name}`]
146166
}
167+
variables[name] = transformBackslashSequences(variables[name])
147168
}
148169
return {
149170
OnceExit (_, { result }) {

index.test.js

Lines changed: 18 additions & 0 deletions
< CE2A /thead>
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,24 @@ it('allows to use in negative numbers', () => {
7777
run('a{ a: -$a }', 'a{ a: -1 }', { variables: { a: 1 } })
7878
})
7979

80+
it('transforms unicode escapes', () => {
81+
run(
82+
String.raw`$selector: .my-component[data-emoji="\U0001F389"]\u003Adisabled\u003Ahover; $selector { width: 1px }`,
83+
'.my-component[data-emoji="🎉"]:disabled:hover { width: 1px }'
84+
)
85+
})
86+
87+
it('does not transform escaped backslashes', () => {
88+
run(
89+
String.raw`$selector: .my-component[data-emoji="\\U0001F389"]\\u003Adisabled\\u003Ahover; $selector { width: 1px }`,
90+
String.raw`.my-component[data-emoji="\U0001F389"]\u003Adisabled\u003Ahover { width: 1px }`
91+
)
92+
run(
93+
String.raw`$selector: .my-component[data-emoji="\\\\U0001F389"]\\\\u003Adisabled\\\\u003Ahover; $selector { width: 1px }`,
94+
String.raw`.my-component[data-emoji="\\U0001F389"]\\u003Adisabled\\u003Ahover { width: 1px }`
95+
)
96+
})
97+
8098
it('replaces multiple variables', () => {
8199
run('a{ a: $a $a }', 'a{ a: 1 1 }', { variables: { a: 1 } })
82100
})

0 commit comments

Comments
 (0)