Skip to content

Commit a11d572

Browse files
committed
fix: Incorrect :scope selector and state output.
When a :scope selector was inserted, the class wasn't properly removed. This was because the code that removed the class didn't always run. When only a single state starting with "is-" belonged to a class, the prefix wasn't removed. I fixed this by just removing prefixes much earlier in the process rather than having it be part of the substate algorithm.
1 parent 37fc443 commit a11d572

File tree

4 files changed

+51
-26
lines changed

4 files changed

+51
-26
lines changed

packages/@css-blocks/bem-to-blocks/src/index.ts

+11-19
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ export function constructBlocksMap(bemSelectorCache: BemSelectorMap): BemToBlock
6868
}
6969
if (bemSelector.modifier) {
7070
blockClass.state = bemSelector.modifier;
71+
if (blockClass.state.match(/^is-/)) {
72+
blockClass.state = blockClass.state.substring(3);
73+
}
7174
}
7275
// add this blockClass to the resultMap
7376
resultMap.set(bemSelector, blockClass);
@@ -119,14 +122,8 @@ export function constructBlocksMap(bemSelectorCache: BemSelectorMap): BemToBlock
119122
let blockClass = resultMap.get(sel);
120123
let lcs = blockClass && blockClass.state && lcsMap[blockClass.state];
121124
if (blockClass && blockClass.state && lcs) {
122-
if (COMMON_PREFIXES_FOR_MODIFIERS.indexOf(lcs) > -1) {
123-
// if we find that the state contains a common prefix, we strip
124-
// it of that prefix
125-
blockClass.state = blockClass.state.replace(`${lcs}-`, "");
126-
} else {
127-
blockClass.subState = blockClass.state.replace(`${lcs}-`, "");
128-
blockClass.state = lcs.replace(/-$/, "");
129-
}
125+
blockClass.subState = blockClass.state.replace(`${lcs}-`, "");
126+
blockClass.state = lcs.replace(/-$/, "");
130127
}
131128
});
132129
}
@@ -204,9 +201,7 @@ export const bemToBlocksPlugin: postcss.Plugin<PostcssAny> = postcss.plugin("bem
204201
let newScopeNode = selectorParser.pseudo({
205202
value: ":scope",
206203
});
207-
if (selector.parent) {
208-
selector.parent.insertBefore(selector, newScopeNode);
209-
}
204+
selector.parent!.insertBefore(selector, newScopeNode);
210205
stripSelector = true;
211206
}
212207

@@ -223,15 +218,12 @@ export const bemToBlocksPlugin: postcss.Plugin<PostcssAny> = postcss.plugin("bem
223218
});
224219

225220
// insert this new node after the current node
226-
if (selector.parent) {
227-
selector.parent.insertAfter(selector, newAttrNode);
228-
}
221+
selector.parent!.insertAfter(selector, newAttrNode);
222+
}
229223

230-
// strip the selector in the end if we replaced it with a pseudo
231-
// class
232-
if (stripSelector) {
233-
selector.remove();
234-
}
224+
// strip the selector in the end if we replaced it with a pseudoclass
225+
if (stripSelector) {
226+
selector.remove();
235227
}
236228
}
237229
}

packages/@css-blocks/bem-to-blocks/test/construct-blocks-map-test.ts

+19
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,25 @@ describe("construct-blocks", () => {
2424
assert.deepEqual(result.get(sel4), new BlockClassSelector({ class: "image" }));
2525
});
2626

27+
it("converts another simple block", async () => {
28+
let sel1 = new BemSelector(".nav");
29+
let sel2 = new BemSelector(".nav--open");
30+
let sel3 = new BemSelector(".nav__item");
31+
let sel4 = new BemSelector(".nav__item--is-selected");
32+
let mockMap = new Map(Object.entries({
33+
".nav": sel1,
34+
".nav--open": sel2,
35+
".nav__item": sel3,
36+
".nav__item--is-selected": sel4,
37+
}));
38+
39+
let result = constructBlocksMap(mockMap);
40+
assert.deepEqual(result.get(sel1), new BlockClassSelector());
41+
assert.deepEqual(result.get(sel2), new BlockClassSelector({ state: "open" }));
42+
assert.deepEqual(result.get(sel3), new BlockClassSelector({ class: "item" }));
43+
assert.deepEqual(result.get(sel4), new BlockClassSelector({ class: "item", state: "selected"}));
44+
});
45+
2746
it("calculates substates correctly", async () => {
2847
let sel1 = new BemSelector(".jobs-hero__image--inverse-red");
2948
let sel2 = new BemSelector(".jobs-hero__image--inverse-black");

packages/@css-blocks/bem-to-blocks/test/plugin-test.ts

+21
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,27 @@ describe("converts BEM to blocks", () => {
1515
assert.equal(result, ".image[inverse-red] {color: blue}");
1616
});
1717

18+
it("converts a simple block", async () => {
19+
let output = await processBEMContents(
20+
"nav.css",
21+
`
22+
.nav { }
23+
.nav--open { }
24+
.nav__item { }
25+
.nav__item--is-selected { }
26+
`,
27+
);
28+
assert.equal(
29+
output.trim(),
30+
`
31+
:scope { }
32+
:scope[open] { }
33+
.item { }
34+
.item[selected] { }
35+
`.trim(),
36+
);
37+
});
38+
1839
it("existing attributes remain unchanged", async () => {
1940
let output = await processBEMContents(
2041
"test-existing-attributes.css",

packages/@css-blocks/cli/test/convert-test.ts

-7
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,5 @@ describe("BEM converter", () => {
3333
let result = tempDir.read();
3434
let convertedFileContents = result["nav.block.css"];
3535
assert.ok(typeof convertedFileContents === "string" && convertedFileContents.match(/:scope/));
36-
// XXX The output isn't correct so we need to add a test case for this.
37-
// `
38-
// :scope { }
39-
// :scope[open] { }
40-
// .item { }
41-
// .item[selected] { }
42-
// `
4336
});
4437
});

0 commit comments

Comments
 (0)