Skip to content

Commit 16f99b8

Browse files
feat: add custom PostCSS imports plugin (#23)
1 parent d68f725 commit 16f99b8

File tree

5 files changed

+65
-5
lines changed

5 files changed

+65
-5
lines changed

src/transforms/globalCssToCssModule/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ echo "Hello world"
1111
## TODO
1212

1313
- [x] Run code formatters on the updated files
14-
- [] Handle @import statements in SCSS
15-
- [] Add interactive CLI support
14+
- [x] Handle @import statements in SCSS
15+
- [ ] Add interactive CLI support

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

33
exports[`globalCssToCssModule transforms correctly 1`] = `
4-
"@import './RepositoriesPopover';
5-
4+
"
65
/* Woah! */
76
.kek {
87
color: red;

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const replaceWhitespace = (value: string) => value.replace(/\s+/g, ' ').trim()
55
describe('transformFileToCssModule', () => {
66
it('correctly transforms provided CSS to CSS module', async () => {
77
const sourceCss = `
8+
@import './RepositoriesPopover';
9+
810
// .repo-header comment
911
.repo-header {
1012
flex: none;
@@ -29,6 +31,10 @@ describe('transformFileToCssModule', () => {
2931
.navbar-nav {
3032
white-space: nowrap;
3133
}
34+
35+
@media (--xs-breakpoint-down) {
36+
border-radius: var(--border-radius);
37+
}
3238
}
3339
3440
.theme-light {
@@ -39,6 +45,8 @@ describe('transformFileToCssModule', () => {
3945
`
4046

4147
const expectedCssModuleSource = `
48+
@import 'wildcard/src/global-styles/breakpoints';
49+
4250
/* .repo-header comment*/
4351
.repo-header {
4452
flex: none;
@@ -58,6 +66,10 @@ describe('transformFileToCssModule', () => {
5866
:global(.navbar-nav) {
5967
white-space: nowrap;
6068
}
69+
70+
@media (--xs-breakpoint-down) {
71+
border-radius: var(--border-radius);
72+
}
6173
}
6274
6375
/* &__button comment*/

src/transforms/globalCssToCssModule/postcss/transformFileToCssModule.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import path from 'path'
22

33
import { createCssProcessor } from './createCssProcessor'
44
import { postcssToCssModulePlugin } from './postcssToCssModulePlugin'
5+
import { updateImportsPlugin } from './updateImportsPlugin'
56

67
interface TransformFileToCssModuleOptions {
78
sourceCss: string
@@ -18,7 +19,7 @@ export async function transformFileToCssModule(
1819
): Promise<TransformFileToCssModuleResult> {
1920
const { sourceCss, sourceFilePath } = options
2021

21-
const transformFileToCssModuleProcessor = createCssProcessor(postcssToCssModulePlugin())
22+
const transformFileToCssModuleProcessor = createCssProcessor(updateImportsPlugin(), postcssToCssModulePlugin())
2223
const transformedResult = await transformFileToCssModuleProcessor(sourceCss)
2324

2425
const { dir, name } = path.parse(sourceFilePath)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { AcceptedPlugin, Container } from 'postcss'
2+
3+
interface UpdateImportsPluginOptions {
4+
breakpointsImportPath?: string
5+
}
6+
7+
/**
8+
* PostCSS plugin that:
9+
* 1. Adds breakpoints @import statement if needed.
10+
* 2. Removes all other @import statements.
11+
*/
12+
export function updateImportsPlugin(options: UpdateImportsPluginOptions = {}): AcceptedPlugin {
13+
const { breakpointsImportPath = 'wildcard/src/global-styles/breakpoints' } = options
14+
let hasBreakpointsImport = false
15+
16+
return {
17+
postcssPlugin: 'postcss-update-imports',
18+
AtRule(atRule) {
19+
const { name, params } = atRule
20+
21+
/**
22+
* In case of the @import statement:
23+
* 1. Update `hasBreakpointsImport` if breakpoints import if found.
24+
* 2. Remove all other @import statements.
25+
*/
26+
if (name === 'import') {
27+
const isBreakpointImport = params.includes(breakpointsImportPath)
28+
29+
if (isBreakpointImport) {
30+
hasBreakpointsImport = true
31+
} else {
32+
// To avoid resetting `raws` which changes line-breaks — use `Container.removeChild()`
33+
// https://github.com/postcss/postcss/blob/75966a9d069be54c997d8b40358342e23bb81328/lib/root.js#L18
34+
Container.prototype.removeChild.call(atRule.parent, atRule)
35+
}
36+
}
37+
38+
/**
39+
* In case of the @media rule:
40+
* Add breakpoints import statement to the file if it's not yet added.
41+
*/
42+
if (name === 'media' && !hasBreakpointsImport) {
43+
atRule.root().prepend(`@import '${breakpointsImportPath}';`)
44+
hasBreakpointsImport = true
45+
}
46+
},
47+
}
48+
}

0 commit comments

Comments
 (0)