Skip to content

Commit 87a7d73

Browse files
committed
Add stylus preprocessing support. Add tests for stylus. Update readme.
1 parent 69ac16b commit 87a7d73

File tree

8 files changed

+279
-3
lines changed

8 files changed

+279
-3
lines changed

Diff for: README.md

+15-2
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,31 @@ To see an example check the `__tests__/__fixtures__` folder.
1313
## Installation
1414

1515
```
16-
npm i --save-dev babel-plugin-cssta-stylename babel-plugin-cssta
16+
# babel-plugin-cssta has to be a regular dependency
17+
# because it's being used in runtime
18+
npm i --save babel-plugin-cssta
19+
npm i --save-dev babel-plugin-cssta-stylename
1720
```
1821

1922
Specify babel plugins in the following order and before any other plugins you might already have:
2023

2124
```json
2225
[
23-
"babel-plugin-cssta-stylename",
26+
["babel-plugin-cssta-stylename", {
27+
"classAttribute": "styleName",
28+
"addCssHash": false,
29+
"extensions": [".css", ".styl"]
30+
}],
2431
"babel-plugin-cssta"
2532
]
2633
```
2734

35+
### Options
36+
37+
- `classAttribute` - what attribute to use for the class name. Default: `"styleName"`.
38+
- `addCssHash` - automatically add comment with unique hash on the css `import` line of the `.js`/`.jsx` file with the same name and in the same directory as the css file. Useful to have it development to force trigger hot-reloading of components when changing only the css file. Default: `false`
39+
- `extensions` - which style imports to parse. Besides regular css, stylus preprocessor is also supported.
40+
2841
## Test
2942

3043
```sh

Diff for: __tests__/__fixtures__/stylus/code.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from 'react'
2+
import './styles.styl'
3+
import { View, Text, Animated } from 'react-native'
4+
5+
export default function App () {
6+
return <>
7+
<View styleName='root' theme='dark'>
8+
<View styleName='topbar' transparent size='m'>
9+
<Text styleName='button' size='s' variant='primary' disabled>
10+
Click me
11+
</Text>
12+
<Text styleName='button' variant='secondary'>
13+
Cancel
14+
</Text>
15+
<View styleName='button' size='m'>
16+
Another button tag
17+
</View>
18+
</View>
19+
<View styleName='content' full>
20+
<Text styleName='header' bold>
21+
Header
22+
</Text>
23+
</View>
24+
</View>
25+
</>
26+
}

Diff for: __tests__/__fixtures__/stylus/output.js

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import _styled from "cssta/native";
2+
import React from "react";
3+
import "./styles.styl";
4+
import { View, Text, Animated } from "react-native";
5+
const Styled_Header = React.memo(_styled(Animated.Text)`
6+
& {
7+
font-size: 24px;
8+
line-height: 32px;
9+
animation: fadeIn 1s ease-out;
10+
}
11+
12+
&[@bold] {
13+
font-weight: bold;
14+
}
15+
@keyframes fadeIn {
16+
0% {
17+
opacity: 0;
18+
}
19+
20+
100% {
21+
opacity: 1;
22+
}
23+
}
24+
`);
25+
const Styled_Content = React.memo(_styled(View)`
26+
& {
27+
background-color: rgba(0,60,120,0.5);
28+
}
29+
30+
&[@full] {
31+
flex: 1;
32+
min-height: 100vh;
33+
}
34+
`);
35+
const Styled_Button2 = React.memo(_styled(View)`
36+
& {
37+
font-size: 12px;
38+
}
39+
40+
@media (min-width: 500px) {
41+
&[@variant='primary'] {
42+
color: #f00;
43+
}
44+
}
45+
`);
46+
const Styled_Button = React.memo(_styled(Text)`
47+
& {
48+
font-size: 12px;
49+
}
50+
51+
@media (min-width: 500px) {
52+
&[@variant='primary'] {
53+
color: #f00;
54+
}
55+
}
56+
`);
57+
const Styled_Topbar = React.memo(_styled(Animated.View)`
58+
& {
59+
height: 40px;
60+
background-color: var(--bgColor);
61+
transition: opacity 0.3s;
62+
animation: fadeIn 0.3s ease-in;
63+
}
64+
65+
&[@transparent] {
66+
opacity: 0.2;
67+
}
68+
69+
&[@size='l'] {
70+
height: 60px;
71+
}
72+
73+
&[@size='s'] {
74+
height: 30px;
75+
}
76+
77+
@media (max-width: 600px) {
78+
&[@size='s'] {
79+
height: 20px;
80+
}
81+
}
82+
@keyframes fadeIn {
83+
0% {
84+
opacity: 0;
85+
}
86+
87+
100% {
88+
opacity: 1;
89+
}
90+
}
91+
`);
92+
const Styled_Root = React.memo(_styled(View)`
93+
& {
94+
flex: 1;
95+
--color: #000;
96+
--bgColor: #fff;
97+
color: var(--color);
98+
background-color: var(--bgColor);
99+
}
100+
101+
&[@theme='dark'] {
102+
--color: #fff;
103+
--bgColor: #000;
104+
}
105+
`);
106+
export default function App() {
107+
return (
108+
<>
109+
<Styled_Root theme="dark">
110+
<Styled_Topbar transparent size="m">
111+
<Styled_Button size="s" variant="primary" disabled>
112+
Click me
113+
</Styled_Button>
114+
<Styled_Button variant="secondary">Cancel</Styled_Button>
115+
<Styled_Button2 size="m">Another button tag</Styled_Button2>
116+
</Styled_Topbar>
117+
<Styled_Content full>
118+
<Styled_Header bold>Header</Styled_Header>
119+
</Styled_Content>
120+
</Styled_Root>
121+
</>
122+
);
123+
}

Diff for: __tests__/__fixtures__/stylus/styles.styl

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
$black = black
2+
$white = white
3+
$topbarHeight = 40px
4+
5+
.root
6+
flex 1
7+
--color $black
8+
--bgColor $white
9+
color var(--color)
10+
background-color var(--bgColor)
11+
&[theme='dark']
12+
--color $white
13+
--bgColor $black
14+
15+
.topbar
16+
height $topbarHeight
17+
background-color var(--bgColor)
18+
transition opacity 0.3s
19+
animation fadeIn 0.3s ease-in
20+
&[transparent]
21+
opacity 0.2
22+
&[size='l']
23+
height 60px
24+
&[size='s']
25+
height 30px
26+
@media (max-width: 600px)
27+
height 20px
28+
29+
.button
30+
font-size 12px
31+
@media (min-width: 500px)
32+
&[variant='primary']
33+
color red
34+
35+
.header
36+
font-size 24px
37+
line-height 32px
38+
animation fadeIn 1s ease-out
39+
&[bold]
40+
font-weight bold
41+
42+
.content
43+
background-color rgba(0, 60, 120, 0.5)
44+
&[full]
45+
flex 1
46+
min-height 100vh
47+
48+
@keyframes fadeIn
49+
0%
50+
opacity 0
51+
100%
52+
opacity 1

Diff for: index.js

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ module.exports = ({ types: t }) => {
9393
], [])
9494
)
9595
const reactIdentifier = utils.getReactImport(t, path)
96+
// TODO: Make React.memo() optional through settings
9697
const memoed = t.callExpression(t.memberExpression(reactIdentifier, t.identifier('memo')), [styled])
9798
const d = t.variableDeclarator(t.identifier(newTag), memoed)
9899
return t.variableDeclaration('const', [d])

Diff for: package.json

+10
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,18 @@
3131
"index.js",
3232
"utils.js",
3333
"transformer.js",
34+
"preprocess.js",
3435
"README.md"
3536
],
37+
"dependencies": {
38+
"@dmapper/stylus-hash-plugin": "0.0.1",
39+
"@startupjs/postcss-rem-to-pixel": "^1004.1.2",
40+
"poststylus": "^1.0.0",
41+
"stylus": ">=0.54.5"
42+
},
43+
"peerDependencies": {
44+
"babel-plugin-cssta": "*"
45+
},
3646
"devDependencies": {
3747
"@babel/core": "^7.8.3",
3848
"@babel/plugin-syntax-jsx": "^7.8.3",

Diff for: preprocessors.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const fs = require('fs')
2+
const path = require('path')
3+
const rem2pixel = require('@startupjs/postcss-rem-to-pixel')
4+
const stylus = require('stylus')
5+
const stylusHashPlugin = require('@dmapper/stylus-hash-plugin')
6+
const poststylus = require('poststylus')
7+
8+
// TODO: Move startupjs-specific hardcode into plugin options
9+
// ref: https://github.com/dmapper/react-native-stylus-transformer/blob/release/index.js#L34
10+
exports.processStylus = function processStylus (src, filename) {
11+
const STYLES_PATH = path.join(process.cwd(), 'styles/index.styl')
12+
const CONFIG_PATH = path.join(process.cwd(), 'startupjs.config.js')
13+
let compiled
14+
15+
// workaround stylus adding vendor prefixes to @keyframes
16+
src = 'vendors = official;\n' + src
17+
18+
const compiler = stylus(src)
19+
compiler.set('filename', filename)
20+
21+
// TODO: Make this a setting
22+
if (fs.existsSync(STYLES_PATH)) {
23+
compiler.import(STYLES_PATH)
24+
}
25+
26+
// TODO: Make this a setting
27+
if (fs.existsSync(CONFIG_PATH)) {
28+
const { ui } = require(CONFIG_PATH)
29+
if (ui) compiler.use(stylusHashPlugin('$UI', ui))
30+
}
31+
32+
// TODO: Make custom unit value a setting
33+
compiler.use(poststylus([rem2pixel])).render((err, res) => {
34+
if (err) {
35+
throw new Error(err)
36+
}
37+
compiled = res
38+
})
39+
40+
return compiled
41+
}

Diff for: utils.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const fs = require('fs')
22
const css = require('css')
33
const nodePath = require('path')
4+
const preprocessors = require('./preprocessors')
45

56
exports.parseAst = function parseAst (modulePath, cssPath) {
67
const pathHint = modulePath.charAt(0)
@@ -15,7 +16,8 @@ exports.parseAst = function parseAst (modulePath, cssPath) {
1516
? nodePath.join(pathReference, cssPath)
1617
: require.resolve(cssPath)
1718
)
18-
const cssString = fs.readFileSync(cssPathReference, 'utf-8')
19+
let cssString = fs.readFileSync(cssPathReference, 'utf-8')
20+
cssString = preprocess(cssString, cssPathReference)
1921
return css.parse(cssString, { source: cssPathReference })
2022
}
2123

@@ -122,3 +124,11 @@ exports.getReactImport = function getReactImport (t, path) {
122124
})
123125
return importBindingsForModule[0] && t.cloneDeep(importBindingsForModule[0].identifier)
124126
}
127+
128+
function preprocess (src, filename) {
129+
if (/\.styl$/.test(filename)) {
130+
return preprocessors.processStylus(src, filename)
131+
} else {
132+
return src
133+
}
134+
}

0 commit comments

Comments
 (0)