Skip to content

Commit 2535ba2

Browse files
fix: edge case fixes based on codemod application (#30)
1 parent 5867f20 commit 2535ba2

14 files changed

Lines changed: 249 additions & 90 deletions

src/transforms/globalCssToCssModule/__tests__/__snapshots__/globalCssToCssModule.test.ts.snap

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -48,37 +48,34 @@ exports[`globalCssToCssModule transforms correctly 1`] = `
4848
:global(.copy-link-action) {
4949
opacity: 0;
5050
}
51+
5152
&:hover,
5253
&:focus-within {
5354
:global(.copy-link-action) {
5455
opacity: 1;
5556
}
5657
}
5758
}
58-
.spacer {
59-
flex: 1 1 0;
60-
}
6159
62-
/* &__icon-chevron { */
63-
.icon-chevron {
64-
opacity: 0.6;
65-
margin: auto;
66-
}
67-
.alert {
68-
margin: 0 0.25rem;
69-
padding: 0.125rem 0.25rem;
70-
cursor: default;
71-
user-select: none;
60+
.logo {
61+
display: flex;
7262
}
7363
74-
/* &__action { */
75-
.action {
76-
margin: 0.5rem 0.625rem 0.5rem 0;
77-
padding: 0.25rem;
64+
/* &__action-list-item { */
65+
.action-list-item {
66+
/* Have a small gap between buttons so they are visually distinct when pressed */
67+
/* stylelint-disable-next-line declaration-property-unit-whitelist */
68+
margin-left: 1px;
7869
79-
:global(.theme-light) & {
80-
margin-top: 0;
81-
margin-bottom: 0;
70+
/* &:hover { */
71+
&:hover {
72+
/* background: var(--color-bg-1); */
73+
background: var(--color-bg-1);
74+
75+
/* .theme-light & { */
76+
:global(.theme-light) & {
77+
background: inherit;
78+
}
8279
}
8380
}
8481
@@ -94,31 +91,38 @@ exports[`globalCssToCssModule transforms correctly 1`] = `
9491
}
9592
}
9693
97-
/* &__action-list-item { */
98-
.action-list-item {
99-
/* Have a small gap between buttons so they are visually distinct when pressed */
100-
/* stylelint-disable-next-line declaration-property-unit-whitelist */
101-
margin-left: 1px;
102-
103-
/* &:hover { */
104-
&:hover {
105-
/* background: var(--color-bg-1); */
106-
background: var(--color-bg-1);
94+
/* &__action { */
95+
.action {
96+
margin: 0.5rem 0.625rem 0.5rem 0;
97+
padding: 0.25rem;
10798
108-
/* .theme-light & { */
109-
:global(.theme-light) & {
110-
background: inherit;
111-
}
99+
:global(.theme-light) & {
100+
margin-top: 0;
101+
margin-bottom: 0;
112102
}
113103
}
114104
105+
.alert {
106+
margin: 0 0.25rem;
107+
padding: 0.125rem 0.25rem;
108+
cursor: default;
109+
user-select: none;
110+
}
111+
112+
/* &__icon-chevron { */
113+
.icon-chevron {
114+
opacity: 0.6;
115+
margin: auto;
116+
}
117+
118+
.spacer {
119+
flex: 1 1 0;
120+
}
121+
115122
/* &__kek-pek { */
116123
.kek-pek {
117124
color: red;
118125
}
119-
.logo {
120-
display: flex;
121-
}
122126
"
123127
`;
124128

src/transforms/globalCssToCssModule/globalCssToCssModule.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import { addClassNamesUtilImportIfNeeded } from '../../utils/classNamesUtility'
99

1010
import { getCssModuleExportNameMap } from './postcss/getCssModuleExportNameMap'
1111
import { transformFileToCssModule } from './postcss/transformFileToCssModule'
12-
import { getNodesWithClassName } from './ts/getNodesWithClassName'
13-
import { STYLES_IDENTIFIER, processNodesWithClassName } from './ts/processNodesWithClassName'
12+
import { STYLES_IDENTIFIER } from './ts/processNodesWithClassName'
13+
import { transformComponentFile } from './ts/transformComponentFile'
1414

1515
/**
1616
* Convert globally scoped stylesheet tied to the React component into a CSS Module.
@@ -68,11 +68,7 @@ export async function globalCssToCssModule(options: CodemodOptions): CodemodResu
6868
sourceFilePath: cssFilePath,
6969
})
7070

71-
processNodesWithClassName({
72-
exportNameMap,
73-
nodesWithClassName: getNodesWithClassName(tsSourceFile),
74-
})
75-
71+
transformComponentFile({ tsSourceFile, exportNameMap, cssModuleFileName })
7672
addClassNamesUtilImportIfNeeded(tsSourceFile)
7773
tsSourceFile.addImportDeclaration({
7874
defaultImport: STYLES_IDENTIFIER,

src/transforms/globalCssToCssModule/postcss/__tests__/transformFileToCssModule.test.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ describe('transformFileToCssModule', () => {
3232
white-space: nowrap;
3333
}
3434
35+
&:disabled &__button {
36+
display: none;
37+
}
38+
3539
@media (--xs-breakpoint-down) {
3640
border-radius: var(--border-radius);
3741
}
@@ -67,21 +71,25 @@ describe('transformFileToCssModule', () => {
6771
white-space: nowrap;
6872
}
6973
74+
&:disabled .button {
75+
display: none;
76+
}
77+
7078
@media (--xs-breakpoint-down) {
7179
border-radius: var(--border-radius);
7280
}
7381
}
7482
75-
/* &__button comment*/
76-
.button {
77-
margin-top: 1px;
78-
}
79-
8083
:global(.theme-light) {
8184
.spacer {
8285
flex: 1 1 0;
8386
}
8487
}
88+
89+
/* &__button comment*/
90+
.button {
91+
margin-top: 1px;
92+
}
8593
`
8694

8795
const { css, filePath } = await transformFileToCssModule({ sourceCss, sourceFilePath: 'whatever.scss' })

src/transforms/globalCssToCssModule/postcss/exportNameMapPrefixes.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import camelcase from 'camelcase'
2+
13
import { decapitalize, isDefined } from '../../../utils'
24

35
interface RemovedPrefix {
@@ -62,7 +64,7 @@ export function getPrefixesToRemove(exportNameMap: Record<string, string>): Remo
6264
if (matches) {
6365
return {
6466
prefix: matches[0],
65-
exportName: exportNameMap[matches[1]],
67+
exportName: camelcase(matches[1]),
6668
}
6769
}
6870

src/transforms/globalCssToCssModule/postcss/postcssToCssModulePlugin.ts

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AcceptedPlugin, Rule, ChildNode } from 'postcss'
1+
import { AcceptedPlugin, Rule, ChildNode, Root } from 'postcss'
22
import parser, { isRoot, Selector } from 'postcss-selector-parser'
33

44
interface PostcssToCssModulePluginOptions {
@@ -110,15 +110,19 @@ function updateChildSelectors(parent: Rule, child: Rule): string[] {
110110
* Some important comment about &__button selector.
111111
* .button { ... }
112112
*/
113-
const parentOrComment = pickComment(child.prev(), parent)
114-
parentOrComment.after(child)
113+
pickComment(child.prev(), parent.root())
114+
parent.root().last?.after(child)
115+
}
116+
117+
if (parent.nodes.length === 0) {
118+
parent.remove()
115119
}
116120

117121
return updatedChildSelectors
118122
}
119123

120124
function replaceSelectorNodesIfNeeded(nodes: Selector): boolean {
121-
return nodes.reduce<boolean>((shouldRemoveNesting, node) => {
125+
return nodes.reduce<boolean>((shouldRemoveNesting, node, index) => {
122126
/**
123127
* Assume that all nested classes and ids not starting with `&` are global:
124128
*
@@ -167,7 +171,26 @@ function replaceSelectorNodesIfNeeded(nodes: Selector): boolean {
167171
node.replaceWith(parse(''))
168172
nextNode.replaceWith(parse(nextNodeValue.replace('__', '.')))
169173

170-
return true
174+
/**
175+
* If its not the first node of the selector — keep nesting in place
176+
*
177+
* ```scss
178+
* .menu {
179+
* &:hover &__button { ... }
180+
* }
181+
* ```
182+
*
183+
* Turns into:
184+
*
185+
* ```scss
186+
* .menu {
187+
* &:hover .button { ... }
188+
* }
189+
* ```
190+
*/
191+
if (index === 0) {
192+
return true
193+
}
171194
}
172195
}
173196
}
@@ -181,9 +204,10 @@ function wrapSelectorInGlobalKeyword(selector: string): string {
181204
}
182205

183206
// If passed node is a comment -> attach it to the end of the file and return it, otherwise return the passed node.
184-
function pickComment(maybeCommentNode: ChildNode | undefined, parent: Rule): Rule {
207+
function pickComment(maybeCommentNode: ChildNode | undefined, parent: Rule | Root): Rule | Root {
185208
if (maybeCommentNode && maybeCommentNode.type === 'comment') {
186-
parent.after(maybeCommentNode)
209+
parent.last?.after(maybeCommentNode)
210+
187211
return maybeCommentNode as unknown as Rule
188212
}
189213

src/transforms/globalCssToCssModule/ts/__tests__/getClassNameNodeReplacement.test.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,21 @@ describe('getClassNameNodeReplacement', () => {
5252

5353
describe.each(testCases)('in parent kind $parentKind', ({ fileSource, replacement: expectedReplacement }) => {
5454
const parentNode = getParentFromFirstClassNameNode(fileSource)
55-
const getReplacement = (options?: Partial<GetClassNameNodeReplacementOptions>) =>
56-
getClassNameNodeReplacement({
55+
const getReplacement = (options?: Partial<GetClassNameNodeReplacementOptions>) => {
56+
const result = getClassNameNodeReplacement({
5757
parentNode,
5858
exportNameReferences,
5959
leftOverClassName,
6060
...options,
6161
})
6262

63+
if (result.isParentTransformed) {
64+
throw new Error('No parent transform is expected')
65+
}
66+
67+
return result.replacement
68+
}
69+
6370
it('returns correct replacement with `leftOverClassName` provided', () => {
6471
const replacement = getReplacement()
6572

src/transforms/globalCssToCssModule/ts/__tests__/processNodesWithClassName.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ describe('processNodesWithClassName', () => {
2121
`)
2222

2323
processNodesWithClassName({
24+
usageStats: {},
2425
nodesWithClassName: getNodesWithClassName(sourceFile),
2526
exportNameMap: {
2627
kek: 'kek',

src/transforms/globalCssToCssModule/ts/__tests__/splitClassName.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ import { splitClassName } from '../splitClassName'
22

33
describe('splitClassName', () => {
44
it('splits correctly', () => {
5-
const { exportNames, leftOverClassnames } = splitClassName('kek kek--wow d-flex mr-1', {
6-
kek: 'kek',
7-
'kek--wow': 'kekWow',
5+
const { exportNames, leftOverClassnames } = splitClassName({
6+
usageStats: {},
7+
className: 'kek kek--wow d-flex mr-1',
8+
exportNameMap: {
9+
kek: 'kek',
10+
'kek--wow': 'kekWow',
11+
},
812
})
913

1014
expect(exportNames).toEqual(['kek', 'kekWow'])

0 commit comments

Comments
 (0)