Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 05c2683

Browse files
AlbertLuciantogajus
authored andcommitted
fix: handle spread (#243)
* fix: handle spread * test: handle spread * test: add test case for spread * spread: remove keys exclusion, avoid falsy values * refactor: handle spread separated in another file
1 parent 70550bc commit 05c2683

File tree

8 files changed

+191
-1
lines changed

8 files changed

+191
-1
lines changed

‎src/createSpreadMapper.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// @flow
2+
3+
import {
4+
Expression,
5+
memberExpression,
6+
binaryExpression,
7+
stringLiteral,
8+
logicalExpression,
9+
identifier
10+
} from '@babel/types';
11+
import optionsDefaults from './schemas/optionsDefaults';
12+
13+
const createSpreadMapper = (path: *, stats: *): { [destinationName: string]: Expression } => {
14+
const result = {};
15+
16+
let {attributeNames} = optionsDefaults;
17+
18+
if (stats.opts && stats.opts.attributeNames) {
19+
attributeNames = Object.assign({}, attributeNames, stats.opts.attributeNames);
20+
}
21+
22+
const attributes = Object
23+
.entries(attributeNames)
24+
.filter((pair) => {
25+
return pair[1];
26+
});
27+
28+
const attributeKeys = attributes.map((pair) => {
29+
return pair[0];
30+
});
31+
32+
path.traverse({
33+
JSXSpreadAttribute (spreadPath: *) {
34+
const spread = spreadPath.node;
35+
36+
for (const attributeKey of attributeKeys) {
37+
const destinationName = attributeNames[attributeKey];
38+
39+
if (result[destinationName]) {
40+
result[destinationName] = binaryExpression(
41+
'+',
42+
result[destinationName],
43+
binaryExpression(
44+
'+',
45+
stringLiteral(' '),
46+
logicalExpression(
47+
'||',
48+
memberExpression(
49+
spread.argument,
50+
identifier(destinationName),
51+
),
52+
stringLiteral('')
53+
)
54+
),
55+
);
56+
} else {
57+
result[destinationName] = logicalExpression(
58+
'||',
59+
memberExpression(
60+
spread.argument,
61+
identifier(destinationName),
62+
),
63+
stringLiteral('')
64+
);
65+
}
66+
}
67+
}
68+
});
69+
70+
return result;
71+
};
72+
73+
export default createSpreadMapper;

‎src/handleSpreadClassName.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// @flow
2+
3+
import {
4+
Expression,
5+
isStringLiteral,
6+
isJSXExpressionContainer,
7+
jsxExpressionContainer,
8+
binaryExpression,
9+
stringLiteral
10+
} from '@babel/types';
11+
12+
const handleSpreadClassName = (
13+
path: *,
14+
destinationName: string,
15+
classNamesFromSpread: Expression
16+
) => {
17+
const destinationAttribute = path.node.openingElement.attributes
18+
.find((attribute) => {
19+
return typeof attribute.name !== 'undefined' && attribute.name.name === destinationName;
20+
});
21+
22+
if (!destinationAttribute) {
23+
return;
24+
}
25+
26+
if (isStringLiteral(destinationAttribute.value)) {
27+
destinationAttribute.value = jsxExpressionContainer(
28+
binaryExpression(
29+
'+',
30+
destinationAttribute.value,
31+
binaryExpression(
32+
'+',
33+
stringLiteral(' '),
34+
classNamesFromSpread,
35+
)
36+
)
37+
);
38+
} else if (isJSXExpressionContainer(destinationAttribute.value)) {
39+
destinationAttribute.value = jsxExpressionContainer(
40+
binaryExpression(
41+
'+',
42+
destinationAttribute.value.expression,
43+
binaryExpression(
44+
'+',
45+
stringLiteral(' '),
46+
classNamesFromSpread
47+
)
48+
)
49+
);
50+
}
51+
};
52+
53+
export default handleSpreadClassName;

‎src/index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import requireCssModule from './requireCssModule';
1515
import resolveStringLiteral from './resolveStringLiteral';
1616
import replaceJsxExpressionContainer from './replaceJsxExpressionContainer';
1717
import attributeNameExists from './attributeNameExists';
18+
import createSpreadMapper from './createSpreadMapper';
19+
import handleSpreadClassName from './handleSpreadClassName';
1820

1921
const ajv = new Ajv({
2022
// eslint-disable-next-line id-match
@@ -216,6 +218,8 @@ export default ({
216218
autoResolveMultipleImports = optionsDefaults.autoResolveMultipleImports
217219
} = stats.opts || {};
218220

221+
const spreadMap = createSpreadMapper(path, stats);
222+
219223
for (const attribute of attributes) {
220224
const destinationName = attributeNames[attribute.name.name];
221225

@@ -246,6 +250,14 @@ export default ({
246250
options
247251
);
248252
}
253+
254+
if (spreadMap[destinationName]) {
255+
handleSpreadClassName(
256+
path,
257+
destinationName,
258+
spreadMap[destinationName]
259+
);
260+
}
249261
}
250262
},
251263
Program (path: *, stats: *): void {

‎test/fixtures/react-css-modules/does not throw error if attribute has no name property/output.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ require("./bar.css");
55
const props = {
66
foo: 'bar'
77
};
8-
<div className="bar__a" {...props}></div>;
8+
<div className={"bar__a"+(" "+(props.className||""))} {...props}></div>;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.a {}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import './foo.css';
2+
3+
const rest = {};
4+
5+
<div {...rest} styleName="a" className="b"></div>;
6+
7+
<div {...rest} styleName="a"></div>;
8+
9+
<div {...rest} activeClassName={this.props.activeClassName} activeStyleName="a" styleName="a"></div>;
10+
11+
<div {...rest} activeStyleName="a" activeClassName="b"></div>;
12+
13+
// Should be okay if rest is put on last
14+
<div styleName="a" {...rest}></div>;
15+
16+
const rest2 = {};
17+
18+
<div {...rest} {...rest2} styleName="a"></div>;
19+
20+
// Should not do anything
21+
<div {...rest} {...rest2}></div>;
22+
<div {...rest} {...rest2} className="b"></div>;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"plugins": [
3+
[
4+
"../../../../src",
5+
{
6+
"generateScopedName": "[name]__[local]",
7+
"attributeNames": {
8+
"activeStyleName": "activeClassName"
9+
}
10+
}
11+
]
12+
]
13+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"use strict";
2+
3+
require("./foo.css");
4+
5+
const rest = {};
6+
<div {...rest} className={"b foo__a" + (" " + (rest.className || ""))}></div>;
7+
<div {...rest} className={"foo__a" + (" " + (rest.className || ""))}></div>;
8+
<div {...rest} activeClassName={((void 0).props.activeClassName ? (void 0).props.activeClassName + " " : "") + "foo__a" + (" " + (rest.activeClassName || ""))} className={"foo__a" + (" " + (rest.className || ""))}></div>;
9+
<div {...rest} activeClassName={"b foo__a" + (" " + (rest.activeClassName || ""))}></div>; // Should be okay if rest is put on last
10+
11+
<div className={"foo__a" + (" " + (rest.className || ""))} {...rest}></div>;
12+
const rest2 = {};
13+
<div {...rest} {...rest2} className={"foo__a" + (" " + ((rest.className || "") + (" " + (rest2.className || ""))))}></div>; // Should not do anything
14+
15+
<div {...rest} {...rest2}></div>;
16+
<div {...rest} {...rest2} className="b"></div>;

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /