Skip to content

Commit 103ce57

Browse files
authored
Fix incorrect selector transformation when a substring of a selector matches another local classname (#1505)
1 parent 7047243 commit 103ce57

File tree

3 files changed

+74
-5
lines changed

3 files changed

+74
-5
lines changed

.changeset/flat-humans-hammer.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@vanilla-extract/css': patch
3+
---
4+
5+
Fixes a bug that caused invalid selectors to be generated when adjacent classnames contained a substring equal to another local classname

packages/css/src/transformCss.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2299,6 +2299,61 @@ describe('transformCss', () => {
22992299
}
23002300
`);
23012301
});
2302+
2303+
it('should handle adjacent classnames containing a separate local classname as a substring', () => {
2304+
// Note that `classname2` starts and ends with the same character, so when two `classname1`s are
2305+
// adjacent, the resulting string will contain `classname2` as a substring
2306+
const classname1 = 'debugName_hash1';
2307+
const classname2 = 'debugName_hash1d';
2308+
2309+
expect(
2310+
transformCss({
2311+
composedClassLists: [],
2312+
localClassNames: [classname1, classname2],
2313+
2314+
cssObjs: [
2315+
{
2316+
type: 'local',
2317+
selector: classname1,
2318+
rule: {
2319+
selectors: {
2320+
['&&']: {
2321+
background: 'black',
2322+
},
2323+
[`${classname2}&`]: {
2324+
background: 'orange',
2325+
},
2326+
[`&${classname2}&`]: {
2327+
background: 'orange',
2328+
},
2329+
[`${classname2}${classname2}&`]: {
2330+
background: 'orange',
2331+
},
2332+
},
2333+
},
2334+
},
2335+
{
2336+
type: 'local',
2337+
selector: classname2,
2338+
rule: {},
2339+
},
2340+
],
2341+
}).join('\n'),
2342+
).toMatchInlineSnapshot(`
2343+
.debugName_hash1.debugName_hash1 {
2344+
background: black;
2345+
}
2346+
.debugName_hash1d.debugName_hash1 {
2347+
background: orange;
2348+
}
2349+
.debugName_hash1.debugName_hash1d.debugName_hash1 {
2350+
background: orange;
2351+
}
2352+
.debugName_hash1d.debugName_hash1d.debugName_hash1 {
2353+
background: orange;
2354+
}
2355+
`);
2356+
});
23022357
});
23032358

23042359
endFileScope();

packages/css/src/transformCss.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -317,11 +317,20 @@ class Stylesheet {
317317
const [endIndex, [firstMatch]] = results[i];
318318
const startIndex = endIndex - firstMatch.length + 1;
319319

320-
if (startIndex >= lastReplaceIndex) {
321-
// Class names can be substrings of other class names
322-
// e.g. '_1g1ptzo1' and '_1g1ptzo10'
323-
// If the startIndex >= lastReplaceIndex, then
324-
// this is the case and this replace should be skipped
320+
// Class names can be substrings of other class names
321+
// e.g. '_1g1ptzo1' and '_1g1ptzo10'
322+
//
323+
// Additionally, concatenated classnames can contain substrings equal to other classnames
324+
// e.g. '&&' where '&' is 'debugName_hash1' and 'debugName_hash1d' is also a local classname
325+
// Before transforming the selector, this would look like `debugName_hash1debugName_hash1`
326+
// which contains the substring `debugName_hash1d`’.
327+
//
328+
// In either of these cases, the last replace index will occur either before or within the
329+
// current replacement range (from `startIndex` to `endIndex`).
330+
// If this occurs, we skip the replacement to avoid transforming the selector incorrectly.
331+
const skipReplacement = lastReplaceIndex <= endIndex;
332+
333+
if (skipReplacement) {
325334
continue;
326335
}
327336

0 commit comments

Comments
 (0)