Skip to content

Commit 860808b

Browse files
authored
Parser of template placeholder (stylelint#42)
1 parent 2024e1b commit 860808b

16 files changed

+355
-23
lines changed

object-stringifier.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class ObjectStringifier extends Stringifier {
1818
this.builder("}", node, "end");
1919
}
2020
literal (node, semicolon) {
21-
this.builder(node.text + (semicolon ? "," : ""));
21+
this.builder(node.text + (semicolon ? "," : ""), node);
2222
}
2323
decl (node, semicolon) {
2424
let prop = this.rawValue(node, "prop");

template-parser-helper.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"use strict";
2+
const Literal = require("./literal");
3+
const isLiteral = token => token[0] === "word" && /^\$\{.*\}$/.test(token[1]);
4+
function literal (start) {
5+
if (!isLiteral(start)) {
6+
return;
7+
}
8+
const tokens = [];
9+
let hasWord;
10+
let type;
11+
let token;
12+
while ((token = this.tokenizer.nextToken())) {
13+
tokens.push(token);
14+
type = token[0];
15+
if (type.length === 1) {
16+
break;
17+
} else if (type === "word") {
18+
hasWord = true;
19+
}
20+
}
21+
22+
while (tokens.length) {
23+
this.tokenizer.back(tokens.pop());
24+
}
25+
26+
if (type === "{" || (type === ":" && !hasWord)) {
27+
return;
28+
}
29+
30+
const node = new Literal({
31+
text: start[1],
32+
});
33+
34+
this.init(node, start[2], start[3]);
35+
36+
return node;
37+
}
38+
39+
function freeSemicolon (token) {
40+
this.spaces += token[1];
41+
const nodes = this.current.nodes;
42+
const prev = nodes && nodes[nodes.length - 1];
43+
if (prev && /^(rule|literal)$/.test(prev.type) && !prev.raws.ownSemicolon) {
44+
prev.raws.ownSemicolon = this.spaces;
45+
this.spaces = "";
46+
}
47+
}
48+
49+
module.exports = {
50+
freeSemicolon: freeSemicolon,
51+
literal: literal,
52+
};

template-parser.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
"use strict";
22
const Parser = require("postcss/lib/parser");
33
const templateTokenize = require("./template-tokenize");
4+
const helper = require("./template-parser-helper");
5+
46
class TemplateParser extends Parser {
57
createTokenizer () {
68
this.tokenizer = templateTokenize(this.input);
79
}
10+
other () {
11+
const args = arguments;
12+
return helper.literal.apply(this, args) || super.other.apply(this, args);
13+
}
14+
freeSemicolon () {
15+
return helper.freeSemicolon.apply(this, arguments);
16+
}
817
}
918
module.exports = TemplateParser;

template-safe-parser.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
"use strict";
22
const SafeParser = require("postcss-safe-parser/lib/safe-parser");
33
const templateTokenize = require("./template-tokenize");
4+
const helper = require("./template-parser-helper");
5+
46
class TemplateSafeParser extends SafeParser {
57
createTokenizer () {
68
this.tokenizer = templateTokenize(this.input, { ignoreErrors: true });
79
}
10+
other () {
11+
const args = arguments;
12+
return helper.literal.apply(this, args) || super.other.apply(this, args);
13+
}
14+
freeSemicolon () {
15+
return helper.freeSemicolon.apply(this, arguments);
16+
}
817
}
918
module.exports = TemplateSafeParser;

template-stringifier.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"use strict";
2+
const Stringifier = require("postcss/lib/stringifier");
3+
4+
class TemplateStringifier extends Stringifier {
5+
literal (node) {
6+
this.builder(node.text, node);
7+
if (node.raws.ownSemicolon) {
8+
this.builder(node.raws.ownSemicolon, node, "end");
9+
}
10+
}
11+
};
12+
13+
module.exports = TemplateStringifier;

template-stringify.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"use strict";
2+
const TemplateStringifier = require("./template-stringifier");
3+
4+
module.exports = function TemplateStringify (node, builder) {
5+
const str = new TemplateStringifier(builder);
6+
str.stringify(node);
7+
};

template-tokenize.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,18 @@ function templateTokenize (input) {
3232
returned.push(token);
3333
}
3434
if (returned.length) {
35-
const lastToken = returned[returned.length - 1];
35+
let lastToken = returned[returned.length - 1];
3636
if (token && token !== lastToken) {
37-
back(token);
37+
if (token[0] === returned[0][0]) {
38+
returned.push(token);
39+
lastToken = token;
40+
} else {
41+
back(token);
42+
}
43+
}
44+
while (lastToken[0] === "space") {
45+
back(returned.pop());
46+
lastToken = returned[returned.length - 1];
3847
}
3948
token = [
4049
returned[0][0],
@@ -45,7 +54,6 @@ function templateTokenize (input) {
4554
lastToken[5] || lastToken[3],
4655
];
4756
}
48-
4957
return token;
5058
}
5159
return Object.assign({}, tokenizer, {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import styled, { css } from "styled-components";
2+
3+
export const buttonStyles = css`
4+
display: inline-block;
5+
`;
6+
7+
export const ButtonStyled1 = styled.button`
8+
${buttonStyles}
9+
color: red;
10+
`;
11+
12+
export const ButtonStyled2 = styled.button`
13+
${buttonStyles};
14+
color: red;
15+
`;
16+
17+
export const ButtonStyled3 = styled.button`
18+
;
19+
color: red;
20+
${buttonStyles}
21+
`;
22+
23+
export const ButtonStyled4 = styled.button`
24+
;
25+
color: red;
26+
${buttonStyles};
27+
`;

test/fixtures/multiline-arrow-function.js

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import styled from "styled-components";
2+
3+
export const StatusText = styled.div`
4+
color: ${(props) =>
5+
(props.status === "signed" && "red") ||
6+
"blue"};
7+
`;

test/fixtures/tpl-decl.mjs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import styled from "styled-components";
2+
3+
const prop = "prop";
4+
const value = "value";
5+
6+
export const Row = styled.div`
7+
prop {
8+
${prop}: value
9+
}
10+
prop prefix {
11+
prefix-${prop}: value
12+
}
13+
prop suffix {
14+
${prop}-suffix: value
15+
}
16+
value {
17+
prop: ${value}
18+
}
19+
value prefix {
20+
prop: prefix-${value}
21+
}
22+
value suffix {
23+
prop: ${value}-suffix
24+
}
25+
value semicolon {
26+
prop: ${value};
27+
}
28+
value prefix semicolon {
29+
prop: prefix-${value};
30+
}
31+
value suffix semicolon {
32+
prop: ${value}-suffix;
33+
}
34+
`;
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import styled from 'styled-components';
1+
import styled from "styled-components";
22

3-
const color = '#ddd';
3+
const color = "#ddd";
44

55
export const Row = styled.div`
6-
border-bottom: ${(props) => (props.border ? `1px solid ${color}` : '0')};
6+
border-bottom: ${(props) => (props.border ? `1px solid ${color}` : "0")};
77
`;

test/fixtures/tpl-selector.mjs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import styled from "styled-components";
2+
3+
const selector = "div";
4+
5+
export const Row = styled.div`
6+
${selector} a {
7+
display: block
8+
}
9+
a ${selector} {
10+
display: block
11+
}
12+
${selector}
13+
a {
14+
display: block
15+
}
16+
a
17+
${selector} {
18+
display: block
19+
}
20+
${selector},
21+
a {
22+
display: block
23+
}
24+
a,
25+
${selector} {
26+
display: block
27+
}
28+
`;

test/fixtures/tpl-special.mjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import styled from "styled-components";
2+
3+
const img = "/images/logo.png";
4+
5+
export const Row = styled.div`
6+
img[${img}] {
7+
background-image: url(${img});
8+
}
9+
img[att="${img}"] {
10+
background-image: url("${img}");
11+
}
12+
`;

0 commit comments

Comments
 (0)