Skip to content

Commit

Permalink
improve splitting by comma and replacing &
Browse files Browse the repository at this point in the history
  • Loading branch information
romainmenke committed Jul 23, 2024
1 parent 5538466 commit d231094
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 23 deletions.
43 changes: 23 additions & 20 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ module.exports = function resolveNestedSelector(selector, node) {
if (parent.type !== 'rule' && !parentIsNestAtRule) return resolveNestedSelector(selector, parent);

var parentSelectors = (parentIsNestAtRule)
? list(parent.params)
? split(parent.params, ',', false).map((x) => x.trim())
: parent.selectors;

var resolvedSelectors = parentSelectors.reduce(function(result, parentSelector) {
if (selector.indexOf('&') !== -1) {
var newlyResolvedSelectors = resolveNestedSelector(parentSelector, parent).map(function(resolvedParentSelector) {
return selector.replace(/&/g, resolvedParentSelector);
return split(selector, '&', true).join(resolvedParentSelector);
});
return result.concat(newlyResolvedSelectors);
}
Expand All @@ -24,17 +24,20 @@ module.exports = function resolveNestedSelector(selector, node) {
return resolvedSelectors;
}

// https://github.com/postcss/postcss/blob/main/lib/list.js#L1
// We should not have `postcss` as a direct dependency so, we inline the same code here.
function list(string) {
let array = []
let current = ''
let split = false
function split(string, separator, splitFunctions) {
var blockPairs = {
'(': ')',
'[': ']',
'{': '}'
}
var array = []
var current = ''
var split = false

let func = 0
let inQuote = false
let prevQuote = ''
let escape = false
var blockClose = []
var inQuote = false
var prevQuote = ''
var escape = false

for (let letter of string) {
if (escape) {
Expand All @@ -48,23 +51,23 @@ function list(string) {
} else if (letter === '"' || letter === "'") {
inQuote = true
prevQuote = letter
} else if (letter === '(') {
func += 1
} else if (letter === ')') {
if (func > 0) func -= 1
} else if (func === 0) {
if (letter === ',') split = true
} else if (letter === '(' || letter === '[' || letter === '{') {
blockClose.push(blockPairs[letter])
} else if (letter === blockClose[blockClose.length - 1]) {
blockClose.pop()
} else if (blockClose.length === 0 || (splitFunctions && blockClose.every((x) => x === ')'))) {
if (letter === separator) split = true
}

if (split) {
if (current !== '') array.push(current.trim())
array.push(current)
current = ''
split = false
} else {
current += letter
}
}

if (current !== '') array.push(current.trim())
array.push(current)
return array
}
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "postcss-resolve-nested-selector",
"version": "0.1.3",
"version": "0.1.4",
"description": "Resolve a nested selector in a PostCSS AST",
"main": "index.js",
"scripts": {
Expand Down
78 changes: 78 additions & 0 deletions test/api.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,48 @@ test(async t => {
);
});

test(async t => {
const code = `.a {
@nest [a=,] & {
& .c {
color: red;
}
}
}`;
assert.deepEqual(
await util.resolveChosenSelector(code, '& .c'),
['[a=,] .a .c'],
);
});

test(async t => {
const code = `.a {
@nest a \\, & {
& .c {
color: red;
}
}
}`;
assert.deepEqual(
await util.resolveChosenSelector(code, '& .c'),
['a \\, .a .c'],
);
});

test(async t => {
const code = `.a {
@nest [a=","] & {
& .c {
color: red;
}
}
}`;
assert.deepEqual(
await util.resolveChosenSelector(code, '& .c'),
['[a=","] .a .c'],
);
});

test(async t => {
const code = `.a {
@nest .b & , & .c , & .d & {
Expand All @@ -105,3 +147,39 @@ test(async t => {
],
);
});

test(async t => {
const code = `.a {
.b [c="&"] & {}
}`;
assert.deepEqual(
await util.resolveChosenSelector(code, '.b [c="&"] &'),
[
'.b [c="&"] .a'
],
);
});

test(async t => {
const code = `.a {
.b [c=&] & {}
}`;
assert.deepEqual(
await util.resolveChosenSelector(code, '.b [c=&] &'),
[
'.b [c=&] .a'
],
);
});

test(async t => {
const code = `.a {
.b \\& + & {
& .c {}
}
}`;
assert.deepEqual(
await util.resolveChosenSelector(code, '& .c'),
['.b \\& + .a .c'],
);
});

0 comments on commit d231094

Please sign in to comment.