Skip to content

Commit db6975a

Browse files
authored
Fix support for styled options (stylelint#47)
1 parent 9224e76 commit db6975a

File tree

7 files changed

+203
-46
lines changed

7 files changed

+203
-46
lines changed

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,23 @@ PostCSS JSX Syntax
1515

1616
- [aphrodite](https://github.com/Khan/aphrodite)
1717
- [astroturf](https://github.com/4Catalyzer/astroturf)
18+
- [csjs](https://github.com/rtsao/csjs)
19+
- [css-light](https://github.com/streamich/css-light)
20+
- [cssobj](https://github.com/cssobj/cssobj)
21+
- [electron-css](https://github.com/azukaar/electron-css)
1822
- [emotion](https://github.com/emotion-js/emotion)
23+
- [freestyler](https://github.com/streamich/freestyler)
1924
- [glamor](https://github.com/threepointone/glamor)
2025
- [glamorous](https://github.com/paypal/glamorous)
26+
- [j2c](https://github.com/j2css/j2c)
27+
- [linaria](https://github.com/callstack/linaria)
2128
- [lit-css](https://github.com/bashmish/lit-css)
2229
- [react-native](https://github.com/necolas/react-native-web)
2330
- [react-style](https://github.com/js-next/react-style)
2431
- [reactcss](https://github.com/casesandberg/reactcss)
2532
- [styled-components](https://github.com/styled-components/styled-components)
2633
- [styletron-react](https://github.com/rtsao/styletron)
34+
- [styling](https://github.com/andreypopp/styling)
2735
- [typestyle](https://github.com/typestyle/typestyle)
2836

2937
## Getting Started
@@ -39,7 +47,7 @@ npm install postcss-syntax postcss-jsx --save-dev
3947
```js
4048
const postcss = require('postcss');
4149
const stylelint = require('stylelint');
42-
const syntax = require('postcss-jsx');
50+
const syntax = require('postcss-syntax');
4351
postcss([stylelint({ fix: true })]).process(source, { syntax: syntax }).then(function (result) {
4452
// An alias for the result.css property. Use it with syntaxes that generate non-CSS output.
4553
result.content
@@ -68,6 +76,14 @@ const Component1 = glm.a({
6876

6977
## Advanced Use Cases
7078

79+
Add support for more `css-in-js` package:
80+
```js
81+
const syntax = require('postcss-syntax')({
82+
"i-css": (index, namespace) => namespace[index + 1] === "addStyles",
83+
"styled-components": true,
84+
});
85+
```
86+
7187
See: [postcss-syntax](https://github.com/gucong3000/postcss-syntax)
7288

7389
## Style Transformations

extract.js

Lines changed: 88 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,53 +8,79 @@ const {
88
const getTemplate = require("./get-template");
99
const loadSyntax = require("postcss-syntax/load-syntax");
1010

11+
const isStyleSheetCreate = expectAdjacentSibling(["create"]);
1112
const supports = {
12-
// https://github.com/Khan/aphrodite
13-
// https://github.com/necolas/react-native-web
14-
StyleSheet: true,
15-
16-
// https://github.com/emotion-js/emotion
1713
// import styled from '@emotion/styled'
18-
// https://github.com/threepointone/glamor
1914
// import { styled } from 'glamor/styled'
20-
// https://github.com/rtsao/styletron
2115
// import { styled } from "styletron-react";
16+
// import { styled } from 'linaria/react';
2217
styled: true,
2318

24-
// https://github.com/typestyle/typestyle
2519
// import { style } from "typestyle";
2620
style: true,
2721

28-
// https://github.com/4Catalyzer/astroturf
29-
// import { css } from 'astroturf';
30-
// https://github.com/bashmish/lit-css
22+
// import { StyleSheet, css } from 'aphrodite';
23+
// import styled, { css } from 'astroturf';
3124
// import { css } from 'lit-css';
32-
// https://github.com/threepointone/glamor
3325
// import { css } from 'glamor'
26+
// require('css-light').css({color: 'red'});
27+
// import { css } from 'linaria';
3428
css: true,
3529

36-
// https://github.com/emotion-js/emotion
30+
// import { StyleSheet, css } from 'aphrodite';
31+
// import { AppRegistry, StyleSheet, Text, View } from 'react-native';
32+
StyleSheet: isStyleSheetCreate,
33+
34+
// import styled, { css } from 'astroturf';
35+
astroturf: true,
36+
37+
// require('csjs')`css`;
38+
csjs: true,
39+
40+
// require('cssobj')({color: 'red'})
41+
cssobj: true,
42+
43+
// require('electron-css')({color: 'red'})
44+
"electron-css": true,
45+
3746
// import styled from "react-emotion";
38-
emotion: true,
3947
"react-emotion": true,
48+
49+
// import styled from 'preact-emotion'
4050
"preact-emotion": true,
4151

52+
// https://github.com/streamich/freestyler
53+
freestyler: true,
54+
4255
// https://github.com/paypal/glamorous
4356
glamorous: true,
4457

45-
// https://github.com/js-next/react-style
46-
"react-style": true,
58+
// https://github.com/irom-io/i-css
59+
// "i-css": (i, nameSpace) => nameSpace[i + 1] === "addStyles" && nameSpace[i + 2] === "wrapper",
60+
61+
// https://github.com/j2css/j2c
62+
j2c: expectAdjacentSibling(["inline", "sheet"]),
4763

48-
// https://github.com/casesandberg/reactcss
64+
// var styles = StyleSheet.create({color: 'red'})
65+
"react-inline": isStyleSheetCreate,
66+
"react-style": isStyleSheetCreate,
67+
68+
// import reactCSS from 'reactcss'
4969
reactcss: true,
5070

51-
// https://github.com/styled-components/styled-components
71+
// const StyledButton = injectSheet(styles)(Button)
72+
"react-jss": true,
73+
74+
// import styled from 'styled-components';
5275
"styled-components": true,
5376

54-
// https://github.com/rtsao/styletron
55-
// import {styled} from "styletron-react";
5677
// import {withStyle} from "styletron-react";
57-
"styletron-react": true,
78+
"styletron-react": expectAdjacentSibling(["withStyle"]),
79+
80+
"styling": true,
81+
82+
// const rule = superstyle({ color: 'blue' })
83+
"superstyle": true,
5884
};
5985

6086
const plugins = [
@@ -71,38 +97,45 @@ const plugins = [
7197
"optionalCatchBinding",
7298
];
7399

74-
function getSourceType (filename) {
75-
if (filename && /\.m[tj]sx?$/.test(filename)) {
76-
return "module";
77-
}
78-
try {
79-
return loadOptions({
80-
filename,
81-
}).sourceType;
82-
} catch (ex) {
83-
//
84-
}
100+
function expectAdjacentSibling (names) {
101+
return (i, nameSpace) => (
102+
names.some(name => nameSpace[i + 1] === name)
103+
);
85104
}
86105

87-
function getOptions (opts) {
106+
function loadBabelOpts (opts) {
88107
const filename = opts.from && opts.from.replace(/\?.*$/, "");
89-
90-
return {
108+
opts = {
91109
sourceFilename: filename,
92-
sourceType: getSourceType(filename) || "unambiguous",
110+
sourceType: filename && /\.m[tj]sx?$/.test(filename) ? "module" : "unambiguous",
93111
plugins,
94112
allowImportExportEverywhere: true,
95113
allowAwaitOutsideFunction: true,
96114
allowReturnOutsideFunction: true,
97115
allowSuperOutsideMethod: true,
98116
};
117+
let fileOpts;
118+
try {
119+
fileOpts = filename && loadOptions({
120+
filename,
121+
});
122+
} catch (ex) {
123+
//
124+
}
125+
for (const key in fileOpts) {
126+
if (Array.isArray(fileOpts[key]) && !fileOpts[key].length) {
127+
continue;
128+
}
129+
opts[key] = fileOpts[key];
130+
}
131+
return opts;
99132
}
100133

101134
function literalParser (source, opts, styles) {
102135
let ast;
103136
try {
104137
ast = parse(source, {
105-
parserOpts: getOptions(opts),
138+
parserOpts: loadBabelOpts(opts),
106139
});
107140
} catch (ex) {
108141
// console.error(ex);
@@ -113,6 +146,7 @@ function literalParser (source, opts, styles) {
113146
const variableDeclarator = new Map();
114147
let objLiteral = new Set();
115148
let tplLiteral = new Set();
149+
const tplCallee = new Set();
116150
const jobs = [];
117151

118152
function addObjectJob (path) {
@@ -200,7 +234,18 @@ function literalParser (source, opts, styles) {
200234
}
201235

202236
function isStylePath (path) {
203-
return getNameSpace(path, []).some(name => name && supports[name]);
237+
return getNameSpace(path, []).some(function (name) {
238+
let result = name && ((supports.hasOwnProperty(name) && supports[name]) || (opts.syntax.config.hasOwnProperty(name) && opts.syntax.config[name]));
239+
switch (typeof result) {
240+
case "function": {
241+
result = result.apply(this, Array.prototype.slice.call(arguments, 1));
242+
}
243+
// eslint-disable-next-line no-fallthrough
244+
case "boolean": {
245+
return result;
246+
}
247+
}
248+
});
204249
}
205250

206251
const visitor = {
@@ -215,7 +260,7 @@ function literalParser (source, opts, styles) {
215260
});
216261
},
217262
JSXAttribute: (path) => {
218-
if (supports[path.node.name.name]) {
263+
if (/^(?:css|style)$/.test(path.node.name.name)) {
219264
addObjectJob(path.get("value.expression"));
220265
}
221266
},
@@ -259,7 +304,7 @@ function literalParser (source, opts, styles) {
259304
break;
260305
} while (currPath);
261306
});
262-
} else if (isStylePath(path.get("callee"))) {
307+
} else if (!tplCallee.has(callee) && isStylePath(path.get("callee"))) {
263308
path.get("arguments").forEach((arg) => {
264309
addObjectJob(arg.isFunction() ? arg.get("body") : arg);
265310
});
@@ -268,6 +313,9 @@ function literalParser (source, opts, styles) {
268313
TaggedTemplateExpression: (path) => {
269314
if (isStylePath(path.get("tag"))) {
270315
tplLiteral.add(path.node.quasi);
316+
if (path.node.tag.callee) {
317+
tplCallee.add(path.node.tag.callee);
318+
}
271319
}
272320
},
273321
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"debug": "npm run mocha -- --inspect-brk"
4444
},
4545
"dependencies": {
46-
"@babel/core": "^7.1.2"
46+
"@babel/core": ">=7.1.0"
4747
},
4848
"peerDependencies": {
4949
"postcss": ">=5.0.0",

test/fixtures/styled-opts.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import styled from "astroturf";
2+
import Footer from "footer";
3+
4+
const Button = styled(Footer, { allowAs: true })`
5+
position: relative;
6+
display: flex;
7+
`;
8+
9+
export default Button;

test/fixtures/styled-opts.mjs.json

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
{
2+
"raws": {},
3+
"type": "root",
4+
"nodes": [
5+
{
6+
"raws": {
7+
"semicolon": true,
8+
"after": "\n"
9+
},
10+
"type": "root",
11+
"nodes": [
12+
{
13+
"raws": {
14+
"before": "\n\t",
15+
"between": ": "
16+
},
17+
"type": "decl",
18+
"source": {
19+
"start": {
20+
"line": 5,
21+
"column": 2
22+
},
23+
"input": {
24+
"file": "styled-opts.mjs"
25+
},
26+
"end": {
27+
"line": 5,
28+
"column": 20
29+
}
30+
},
31+
"prop": "position",
32+
"value": "relative"
33+
},
34+
{
35+
"raws": {
36+
"before": "\n\t",
37+
"between": ": "
38+
},
39+
"type": "decl",
40+
"source": {
41+
"start": {
42+
"line": 6,
43+
"column": 2
44+
},
45+
"input": {
46+
"file": "styled-opts.mjs"
47+
},
48+
"end": {
49+
"line": 6,
50+
"column": 15
51+
}
52+
},
53+
"prop": "display",
54+
"value": "flex"
55+
}
56+
],
57+
"source": {
58+
"input": {
59+
"file": "styled-opts.mjs"
60+
},
61+
"start": {
62+
"line": 4,
63+
"column": 50
64+
},
65+
"inline": false,
66+
"lang": "css",
67+
"syntax": {}
68+
}
69+
}
70+
],
71+
"source": {
72+
"input": {
73+
"file": "styled-opts.mjs"
74+
},
75+
"start": {
76+
"line": 1,
77+
"column": 1
78+
},
79+
"lang": "jsx"
80+
}
81+
}

test/fixtures/toLocaleString.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// eslint-disable-next-line
2+
(positionsChecked / scansCount).toLocaleString("de-DE", { style: "percent" });

test/supports.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,18 @@ function clean (node) {
2828

2929
describe("should support for each CSS in JS package", () => {
3030
[
31+
"emotion-10.jsx",
32+
"glamorous.jsx",
3133
"interpolation-content.mjs",
3234
"jsx.jsx",
33-
"emotion-10.jsx",
35+
"lit-css.mjs",
3436
"react-emotion.jsx",
3537
"react-native.mjs",
36-
"glamorous.jsx",
37-
"lit-css.mjs",
3838
"styled-components.js",
39+
"styled-opts.mjs",
40+
"tpl-decl.mjs",
3941
"tpl-in-tpl.mjs",
4042
"tpl-selector.mjs",
41-
"tpl-decl.mjs",
4243
"tpl-special.mjs",
4344
].forEach(file => {
4445
it(file, () => {

0 commit comments

Comments
 (0)