@hero-design/snowflake-guard 1.0.14 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -3
- package/lib/src/__tests__/parseSource.spec.js +110 -3
- package/lib/src/parseSource.d.ts +9 -0
- package/lib/src/parseSource.js +10 -0
- package/lib/src/reports/__tests__/reportInlineStyle.spec.d.ts +1 -0
- package/lib/src/reports/__tests__/reportInlineStyle.spec.js +124 -0
- package/lib/src/reports/reportCustomStyleProperties.d.ts +9 -1
- package/lib/src/reports/reportCustomStyleProperties.js +34 -15
- package/lib/src/reports/reportInlineStyle.d.ts +14 -2
- package/lib/src/reports/reportInlineStyle.js +27 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,9 +10,18 @@
|
|
|
10
10
|
cp .env.example .env
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
2.
|
|
13
|
+
2. Set up [internal-tool-integrations service](https://github.com/Thinkei/internal-tool-integrations) for the bot to store report.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
- Replace `DB_HOST` with the host of the internal-tool-integrations service.
|
|
16
|
+
- Use the same `SNOWFLAKE_GUARD_SECRET` as the one in the internal-tool-integrations service.
|
|
17
|
+
|
|
18
|
+
In case you don't want to store snowflake report, comment out the `saving report` section in `src/index.ts`.
|
|
19
|
+
|
|
20
|
+
3. Install snowflake-guard[Dev] [app](https://github.com/apps/hero-design-snowflake-guard-dev) to a repository that you wish to run the app on.
|
|
21
|
+
|
|
22
|
+
4. Start `internal-tool-integrations` service.
|
|
23
|
+
|
|
24
|
+
5. Start the bot.
|
|
16
25
|
|
|
17
26
|
```sh
|
|
18
27
|
# Install dependencies
|
|
@@ -21,7 +30,7 @@ yarn install
|
|
|
21
30
|
# Run the bot
|
|
22
31
|
yarn start
|
|
23
32
|
```
|
|
24
|
-
|
|
33
|
+
6. Open or update a pull request to trigger webhook events.
|
|
25
34
|
|
|
26
35
|
## Note
|
|
27
36
|
|
|
@@ -32,11 +32,118 @@ describe('parseSource', () => {
|
|
|
32
32
|
it('reports correct snowflakes', () => {
|
|
33
33
|
const source = fs.readFileSync('./src/__mocks__/sourceSample.tsx', 'utf-8');
|
|
34
34
|
expect((0, parseSource_1.default)(source)).toEqual({
|
|
35
|
+
approvedLocs: [55, 65, 64],
|
|
35
36
|
classNameLocs: [44, 42, 43],
|
|
36
|
-
styleLocs: [54, 47, 49, 50, 51, 52, 53],
|
|
37
|
-
sxLocs: [63, 58, 59, 60, 61, 62],
|
|
37
|
+
styleLocs: [54, 67, 47, 49, 50, 51, 52, 53],
|
|
38
38
|
styledComponentLocs: [6, 10, 15],
|
|
39
|
-
|
|
39
|
+
sxLocs: [63, 58, 59, 60, 61, 62],
|
|
40
|
+
violatingAttributes: [
|
|
41
|
+
{
|
|
42
|
+
attributeName: 'padding',
|
|
43
|
+
attributeValue: '20',
|
|
44
|
+
componentName: 'Button.Link',
|
|
45
|
+
inlineStyleProps: 'style',
|
|
46
|
+
loc: 54,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
attributeName: 'padding',
|
|
50
|
+
attributeValue: '20',
|
|
51
|
+
componentName: 'Button.Link',
|
|
52
|
+
inlineStyleProps: 'sx',
|
|
53
|
+
loc: 63,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
attributeName: 'padding',
|
|
57
|
+
attributeValue: '20',
|
|
58
|
+
componentName: 'Button.Link',
|
|
59
|
+
inlineStyleProps: 'style',
|
|
60
|
+
loc: 67,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
attributeName: 'backgroundColor',
|
|
64
|
+
attributeValue: "'red'",
|
|
65
|
+
componentName: 'Button.Link',
|
|
66
|
+
inlineStyleProps: 'style',
|
|
67
|
+
loc: 67,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
attributeName: 'width',
|
|
71
|
+
attributeValue: '200',
|
|
72
|
+
componentName: 'Empty',
|
|
73
|
+
inlineStyleProps: 'style',
|
|
74
|
+
loc: 47,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
attributeName: 'width',
|
|
78
|
+
attributeValue: '200',
|
|
79
|
+
componentName: 'Card.Header',
|
|
80
|
+
inlineStyleProps: 'style',
|
|
81
|
+
loc: 49,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
attributeName: 'padding',
|
|
85
|
+
attributeValue: '30',
|
|
86
|
+
componentName: 'Button',
|
|
87
|
+
inlineStyleProps: 'style',
|
|
88
|
+
loc: 50,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
attributeName: 'padding',
|
|
92
|
+
attributeValue: '30',
|
|
93
|
+
componentName: 'Button',
|
|
94
|
+
inlineStyleProps: 'style',
|
|
95
|
+
loc: 51,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
attributeName: 'padding',
|
|
99
|
+
attributeValue: '30',
|
|
100
|
+
componentName: 'Button',
|
|
101
|
+
inlineStyleProps: 'style',
|
|
102
|
+
loc: 52,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
attributeName: 'padding',
|
|
106
|
+
attributeValue: '30',
|
|
107
|
+
componentName: 'Button',
|
|
108
|
+
inlineStyleProps: 'style',
|
|
109
|
+
loc: 53,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
attributeName: 'mt',
|
|
113
|
+
attributeValue: '10',
|
|
114
|
+
componentName: 'Badge.Count',
|
|
115
|
+
inlineStyleProps: 'sx',
|
|
116
|
+
loc: 58,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
attributeName: 'pt',
|
|
120
|
+
attributeValue: '10',
|
|
121
|
+
componentName: 'Badge',
|
|
122
|
+
inlineStyleProps: 'sx',
|
|
123
|
+
loc: 59,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
attributeName: 'pt',
|
|
127
|
+
attributeValue: '10',
|
|
128
|
+
componentName: 'Badge',
|
|
129
|
+
inlineStyleProps: 'sx',
|
|
130
|
+
loc: 60,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
attributeName: 'pt',
|
|
134
|
+
attributeValue: '10',
|
|
135
|
+
componentName: 'Badge',
|
|
136
|
+
inlineStyleProps: 'sx',
|
|
137
|
+
loc: 61,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
attributeName: 'pt',
|
|
141
|
+
attributeValue: '10',
|
|
142
|
+
componentName: 'Badge',
|
|
143
|
+
inlineStyleProps: 'sx',
|
|
144
|
+
loc: 62,
|
|
145
|
+
},
|
|
146
|
+
],
|
|
40
147
|
});
|
|
41
148
|
});
|
|
42
149
|
});
|
package/lib/src/parseSource.d.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
+
import type { CompoundComponentName } from './reports/types';
|
|
2
|
+
import { InlineStyleProps } from './reports/reportInlineStyle';
|
|
1
3
|
declare const parseSource: (source: string) => {
|
|
2
4
|
classNameLocs: number[];
|
|
3
5
|
styleLocs: number[];
|
|
4
6
|
sxLocs: number[];
|
|
5
7
|
styledComponentLocs: number[];
|
|
6
8
|
approvedLocs: number[];
|
|
9
|
+
violatingAttributes: {
|
|
10
|
+
attributeName: string;
|
|
11
|
+
attributeValue: string | null;
|
|
12
|
+
inlineStyleProps: InlineStyleProps;
|
|
13
|
+
componentName: CompoundComponentName;
|
|
14
|
+
loc: number | undefined;
|
|
15
|
+
}[];
|
|
7
16
|
};
|
|
8
17
|
export default parseSource;
|
package/lib/src/parseSource.js
CHANGED
|
@@ -40,6 +40,7 @@ const parseSource = (source) => {
|
|
|
40
40
|
let classNameLocs = [];
|
|
41
41
|
let styleLocs = [];
|
|
42
42
|
let sxLocs = [];
|
|
43
|
+
let violatingAttributes = [];
|
|
43
44
|
const approvedCmtLocs = [];
|
|
44
45
|
const approvedClassnameLocs = [];
|
|
45
46
|
const ast = recast.parse(source, { parser: tsParser });
|
|
@@ -96,6 +97,14 @@ const parseSource = (source) => {
|
|
|
96
97
|
if (hasStyledComponentsImport) {
|
|
97
98
|
styledComponentLocs = (0, reportStyledComponents_1.default)(ast, componentList, styledAliasName).filter(isNotApprovedSnowflakes);
|
|
98
99
|
}
|
|
100
|
+
violatingAttributes = customPropLocs.violatingAttributes.filter((report) => {
|
|
101
|
+
if (report.loc) {
|
|
102
|
+
return !approvedCmtLocs.includes(report.loc);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
99
108
|
}
|
|
100
109
|
return {
|
|
101
110
|
classNameLocs,
|
|
@@ -103,6 +112,7 @@ const parseSource = (source) => {
|
|
|
103
112
|
sxLocs,
|
|
104
113
|
styledComponentLocs,
|
|
105
114
|
approvedLocs: [...approvedCmtLocs, ...approvedClassnameLocs],
|
|
115
|
+
violatingAttributes,
|
|
106
116
|
};
|
|
107
117
|
};
|
|
108
118
|
exports.default = parseSource;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
const recast = __importStar(require("recast"));
|
|
30
|
+
const reportInlineStyle_1 = __importDefault(require("../reportInlineStyle"));
|
|
31
|
+
const constants_1 = require("../constants");
|
|
32
|
+
const tsParser = __importStar(require("../../parsers/typescript"));
|
|
33
|
+
describe('reportInlineStyle', () => {
|
|
34
|
+
it('should return line numbers and violating attributes for inline styles that violate the ruleset', () => {
|
|
35
|
+
const code = `
|
|
36
|
+
const MyComponent = () => (
|
|
37
|
+
<Card style={{
|
|
38
|
+
color: 'red',
|
|
39
|
+
backgroundColor: 'blue'
|
|
40
|
+
}}
|
|
41
|
+
sx={{
|
|
42
|
+
padding: 10,
|
|
43
|
+
backgroundColor: 'blue'
|
|
44
|
+
}} />
|
|
45
|
+
);
|
|
46
|
+
`;
|
|
47
|
+
// Step 1: Parse the code to generate the AST
|
|
48
|
+
const ast = recast.parse(code, { parser: tsParser });
|
|
49
|
+
const attributes = [];
|
|
50
|
+
// Step 2: Traverse the AST to extract relevant JSXAttributes (style and sx)
|
|
51
|
+
recast.visit(ast, {
|
|
52
|
+
visitJSXAttribute(path) {
|
|
53
|
+
const node = path.node;
|
|
54
|
+
attributes.push(node);
|
|
55
|
+
this.traverse(path);
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
const componentName = 'Card';
|
|
59
|
+
// Mock the RULESET_MAP and SX_RULESET_MAP for this test
|
|
60
|
+
constants_1.RULESET_MAP.Card = ['color', 'backgroundColor'];
|
|
61
|
+
constants_1.SX_RULESET_MAP.Card = ['padding', 'backgroundColor'];
|
|
62
|
+
// Step 3: Run the function with the dynamically extracted attributes
|
|
63
|
+
const result = (0, reportInlineStyle_1.default)(ast, attributes, componentName);
|
|
64
|
+
expect(result.locs.style).toEqual([3]);
|
|
65
|
+
expect(result.locs.sx).toEqual([7]);
|
|
66
|
+
expect(result.violatingAttributes).toEqual([
|
|
67
|
+
{
|
|
68
|
+
attributeName: 'color',
|
|
69
|
+
attributeValue: "'red'",
|
|
70
|
+
componentName: 'Card',
|
|
71
|
+
inlineStyleProps: 'style',
|
|
72
|
+
loc: 3,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
attributeName: 'backgroundColor',
|
|
76
|
+
attributeValue: "'blue'",
|
|
77
|
+
componentName: 'Card',
|
|
78
|
+
inlineStyleProps: 'style',
|
|
79
|
+
loc: 3,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
attributeName: 'padding',
|
|
83
|
+
attributeValue: '10',
|
|
84
|
+
componentName: 'Card',
|
|
85
|
+
inlineStyleProps: 'sx',
|
|
86
|
+
loc: 7,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
attributeName: 'backgroundColor',
|
|
90
|
+
attributeValue: "'blue'",
|
|
91
|
+
componentName: 'Card',
|
|
92
|
+
inlineStyleProps: 'sx',
|
|
93
|
+
loc: 7,
|
|
94
|
+
},
|
|
95
|
+
]);
|
|
96
|
+
});
|
|
97
|
+
it('should return empty arrays when no violations are found', () => {
|
|
98
|
+
const code = `
|
|
99
|
+
const MyComponent = () => (
|
|
100
|
+
<Card style={{ margin: '10px' }} sx={{ margin: 5 }} />
|
|
101
|
+
);
|
|
102
|
+
`;
|
|
103
|
+
// Step 1: Parse the code to generate the AST
|
|
104
|
+
const ast = recast.parse(code, { parser: tsParser });
|
|
105
|
+
const attributes = [];
|
|
106
|
+
// Step 2: Traverse the AST to extract relevant JSXAttributes (style and sx)
|
|
107
|
+
recast.visit(ast, {
|
|
108
|
+
visitJSXAttribute(path) {
|
|
109
|
+
const node = path.node;
|
|
110
|
+
attributes.push(node);
|
|
111
|
+
this.traverse(path);
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
const componentName = 'Card';
|
|
115
|
+
// Mock the RULESET_MAP and SX_RULESET_MAP for this test
|
|
116
|
+
constants_1.RULESET_MAP.Card = ['color'];
|
|
117
|
+
constants_1.SX_RULESET_MAP.Card = ['padding'];
|
|
118
|
+
const result = (0, reportInlineStyle_1.default)(ast, attributes, componentName);
|
|
119
|
+
expect(result.locs.style).toEqual([]);
|
|
120
|
+
expect(result.locs.sx).toEqual([]);
|
|
121
|
+
expect(result.violatingAttributes).toEqual([]);
|
|
122
|
+
expect(result.violatingAttributes).toEqual([]);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import * as recast from 'recast';
|
|
2
|
-
import
|
|
2
|
+
import { InlineStyleProps } from './reportInlineStyle';
|
|
3
|
+
import type { ComponentName, CompoundComponentName } from './types';
|
|
3
4
|
declare const reportCustomProperties: (ast: recast.types.ASTNode, componentList: {
|
|
4
5
|
[k: string]: "Alert" | "Badge" | "Banner" | "Breadcrumb" | "Button" | "Card" | "Carousel" | "Chart" | "Checkbox" | "Chip" | "Collapse" | "Comment" | "ContextPanel" | "DatePicker" | "Divider" | "Dropdown" | "Empty" | "File" | "Filters" | "Form" | "Grid" | "Icon" | "InPageNavigation" | "Input" | "MediaQuery" | "Menu" | "Modal" | "Notification" | "PageHeader" | "Pagination" | "Portal" | "Portlet" | "Progress" | "Radio" | "Rate" | "Result" | "Select" | "SelectButton" | "SideBar" | "Slider" | "Spinner" | "Statistic" | "Steps" | "Switch" | "Table" | "Tabs" | "Tag" | "TagInput" | "TimePicker" | "Timeline" | "Tooltip" | "Typography" | "Widget";
|
|
5
6
|
}) => {
|
|
6
7
|
className: number[];
|
|
7
8
|
style: number[];
|
|
8
9
|
sx: number[];
|
|
10
|
+
violatingAttributes: {
|
|
11
|
+
attributeName: string;
|
|
12
|
+
attributeValue: string | null;
|
|
13
|
+
inlineStyleProps: InlineStyleProps;
|
|
14
|
+
componentName: CompoundComponentName;
|
|
15
|
+
loc: number | undefined;
|
|
16
|
+
}[];
|
|
9
17
|
};
|
|
10
18
|
export default reportCustomProperties;
|
|
@@ -30,10 +30,11 @@ const recast = __importStar(require("recast"));
|
|
|
30
30
|
const reportClassName_1 = __importDefault(require("./reportClassName"));
|
|
31
31
|
const reportInlineStyle_1 = __importDefault(require("./reportInlineStyle"));
|
|
32
32
|
const reportCustomProperties = (ast, componentList) => {
|
|
33
|
-
const
|
|
33
|
+
const report = {
|
|
34
34
|
className: [],
|
|
35
35
|
style: [],
|
|
36
36
|
sx: [],
|
|
37
|
+
violatingAttributes: [],
|
|
37
38
|
};
|
|
38
39
|
const localComponentList = Object.keys(componentList);
|
|
39
40
|
recast.visit(ast, {
|
|
@@ -44,10 +45,17 @@ const reportCustomProperties = (ast, componentList) => {
|
|
|
44
45
|
localComponentList.includes(path.value.name.name)) {
|
|
45
46
|
const attributes = path.value
|
|
46
47
|
.attributes;
|
|
47
|
-
const styleObjectLocs = (0, reportInlineStyle_1.default)(ast, attributes, componentList[path.value.name.name]);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
const { locs: styleObjectLocs, violatingAttributes } = (0, reportInlineStyle_1.default)(ast, attributes, componentList[path.value.name.name]);
|
|
49
|
+
report.className = [
|
|
50
|
+
...report.className,
|
|
51
|
+
...(0, reportClassName_1.default)(attributes),
|
|
52
|
+
];
|
|
53
|
+
report.style = [...report.style, ...styleObjectLocs.style];
|
|
54
|
+
report.sx = [...report.sx, ...styleObjectLocs.sx];
|
|
55
|
+
report.violatingAttributes = [
|
|
56
|
+
...report.violatingAttributes,
|
|
57
|
+
...violatingAttributes,
|
|
58
|
+
];
|
|
51
59
|
}
|
|
52
60
|
// Case 2: Custom compound component, e.g. <Card.Header />
|
|
53
61
|
if (path.value.name.type === 'JSXMemberExpression' &&
|
|
@@ -58,10 +66,17 @@ const reportCustomProperties = (ast, componentList) => {
|
|
|
58
66
|
].join('.');
|
|
59
67
|
const attributes = path.value
|
|
60
68
|
.attributes;
|
|
61
|
-
const styleObjectLocs = (0, reportInlineStyle_1.default)(ast, attributes, compoundComponentName);
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
const { locs: styleObjectLocs, violatingAttributes } = (0, reportInlineStyle_1.default)(ast, attributes, compoundComponentName);
|
|
70
|
+
report.className = [
|
|
71
|
+
...report.className,
|
|
72
|
+
...(0, reportClassName_1.default)(attributes),
|
|
73
|
+
];
|
|
74
|
+
report.style = [...report.style, ...styleObjectLocs.style];
|
|
75
|
+
report.sx = [...report.sx, ...styleObjectLocs.sx];
|
|
76
|
+
report.violatingAttributes = [
|
|
77
|
+
...report.violatingAttributes,
|
|
78
|
+
...violatingAttributes,
|
|
79
|
+
];
|
|
65
80
|
}
|
|
66
81
|
},
|
|
67
82
|
// Case 3: Custom compound component with spead operator. e.g. const { Header } = Card, then <Header />
|
|
@@ -90,13 +105,17 @@ const reportCustomProperties = (ast, componentList) => {
|
|
|
90
105
|
openPath.value.name.name,
|
|
91
106
|
].join('.');
|
|
92
107
|
const { attributes } = openPath.value;
|
|
93
|
-
const styleObjectLocs = (0, reportInlineStyle_1.default)(ast, attributes, compoundComponentName);
|
|
94
|
-
|
|
95
|
-
...
|
|
108
|
+
const { locs: styleObjectLocs, violatingAttributes } = (0, reportInlineStyle_1.default)(ast, attributes, compoundComponentName);
|
|
109
|
+
report.className = [
|
|
110
|
+
...report.className,
|
|
96
111
|
...(0, reportClassName_1.default)(attributes),
|
|
97
112
|
];
|
|
98
|
-
|
|
99
|
-
|
|
113
|
+
report.style = [...report.style, ...styleObjectLocs.style];
|
|
114
|
+
report.sx = [...report.sx, ...styleObjectLocs.sx];
|
|
115
|
+
report.violatingAttributes = [
|
|
116
|
+
...report.violatingAttributes,
|
|
117
|
+
...violatingAttributes,
|
|
118
|
+
];
|
|
100
119
|
}
|
|
101
120
|
},
|
|
102
121
|
});
|
|
@@ -104,6 +123,6 @@ const reportCustomProperties = (ast, componentList) => {
|
|
|
104
123
|
}
|
|
105
124
|
},
|
|
106
125
|
});
|
|
107
|
-
return
|
|
126
|
+
return report;
|
|
108
127
|
};
|
|
109
128
|
exports.default = reportCustomProperties;
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import * as recast from 'recast';
|
|
2
2
|
import type { CompoundComponentName } from './types';
|
|
3
|
+
export type InlineStyleProps = 'style' | 'sx';
|
|
4
|
+
export declare const INLINE_STYLE_PROPERTIES: string[];
|
|
5
|
+
type ViolatingAttribute = {
|
|
6
|
+
attributeName: string;
|
|
7
|
+
attributeValue: string | null;
|
|
8
|
+
inlineStyleProps: InlineStyleProps;
|
|
9
|
+
componentName: CompoundComponentName;
|
|
10
|
+
loc: number | undefined;
|
|
11
|
+
};
|
|
3
12
|
declare const reportInlineStyle: (ast: recast.types.ASTNode, attributes: recast.types.namedTypes.JSXAttribute[], componentName: CompoundComponentName) => {
|
|
4
|
-
|
|
5
|
-
|
|
13
|
+
locs: {
|
|
14
|
+
style: number[];
|
|
15
|
+
sx: number[];
|
|
16
|
+
};
|
|
17
|
+
violatingAttributes: ViolatingAttribute[];
|
|
6
18
|
};
|
|
7
19
|
export default reportInlineStyle;
|
|
@@ -23,35 +23,51 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.INLINE_STYLE_PROPERTIES = void 0;
|
|
26
27
|
const recast = __importStar(require("recast"));
|
|
27
28
|
const constants_1 = require("./constants");
|
|
28
29
|
const BLACKLIST_PROPERTIES = {
|
|
29
30
|
style: constants_1.RULESET_MAP,
|
|
30
31
|
sx: constants_1.SX_RULESET_MAP,
|
|
31
32
|
};
|
|
32
|
-
|
|
33
|
+
exports.INLINE_STYLE_PROPERTIES = ['style', 'sx'];
|
|
34
|
+
const addViolatingAttribute = (prop, styleObjName, componentName, loc, violatingAttributes) => {
|
|
35
|
+
if (prop.key.type !== 'Identifier') {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
violatingAttributes.push({
|
|
39
|
+
attributeName: prop.key.name,
|
|
40
|
+
attributeValue: recast.print(prop.value).code,
|
|
41
|
+
inlineStyleProps: styleObjName,
|
|
42
|
+
componentName,
|
|
43
|
+
loc,
|
|
44
|
+
});
|
|
45
|
+
};
|
|
33
46
|
const reportInlineStyle = (ast, attributes, componentName) => {
|
|
34
47
|
const locs = {
|
|
35
48
|
style: [],
|
|
36
49
|
sx: [],
|
|
37
50
|
};
|
|
51
|
+
const violatingAttributes = [];
|
|
38
52
|
let hasCustomStyle = false;
|
|
39
53
|
let styleObjName;
|
|
40
54
|
attributes.forEach((attr) => {
|
|
41
55
|
var _a;
|
|
42
56
|
if (attr.type === 'JSXAttribute' &&
|
|
43
57
|
typeof attr.name.name === 'string' &&
|
|
44
|
-
INLINE_STYLE_PROPERTIES.includes(attr.name.name) &&
|
|
58
|
+
exports.INLINE_STYLE_PROPERTIES.includes(attr.name.name) &&
|
|
45
59
|
((_a = attr.value) === null || _a === void 0 ? void 0 : _a.type) === 'JSXExpressionContainer') {
|
|
46
60
|
styleObjName = attr.name.name;
|
|
47
61
|
const { expression } = attr.value;
|
|
48
62
|
if (expression.type === 'ObjectExpression') {
|
|
49
63
|
expression.properties.forEach((prop) => {
|
|
64
|
+
var _a;
|
|
50
65
|
// Case 1: Use direct object, e.g. <Card style={{ color: 'red' }} />
|
|
51
66
|
if (prop.type === 'ObjectProperty' &&
|
|
52
67
|
prop.key.type === 'Identifier' &&
|
|
53
68
|
BLACKLIST_PROPERTIES[styleObjName][componentName].includes(prop.key.name)) {
|
|
54
69
|
hasCustomStyle = true;
|
|
70
|
+
addViolatingAttribute(prop, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
|
|
55
71
|
}
|
|
56
72
|
// Case 2: Use spread operator, e.g. <Card style={{ ...customStyle }} />
|
|
57
73
|
if (prop.type === 'SpreadElement' &&
|
|
@@ -67,10 +83,12 @@ const reportInlineStyle = (ast, attributes, componentName) => {
|
|
|
67
83
|
declaration.id.name === variableName &&
|
|
68
84
|
((_a = declaration.init) === null || _a === void 0 ? void 0 : _a.type) === 'ObjectExpression') {
|
|
69
85
|
declaration.init.properties.forEach((deProp) => {
|
|
86
|
+
var _a;
|
|
70
87
|
if (deProp.type === 'ObjectProperty' &&
|
|
71
88
|
deProp.key.type === 'Identifier' &&
|
|
72
89
|
BLACKLIST_PROPERTIES[styleObjName][componentName].includes(deProp.key.name)) {
|
|
73
90
|
hasCustomStyle = true;
|
|
91
|
+
addViolatingAttribute(deProp, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
|
|
74
92
|
}
|
|
75
93
|
});
|
|
76
94
|
}
|
|
@@ -99,10 +117,12 @@ const reportInlineStyle = (ast, attributes, componentName) => {
|
|
|
99
117
|
deProp.key.name === propName &&
|
|
100
118
|
deProp.value.type === 'ObjectExpression') {
|
|
101
119
|
deProp.value.properties.forEach((p) => {
|
|
120
|
+
var _a;
|
|
102
121
|
if (p.type === 'ObjectProperty' &&
|
|
103
122
|
p.key.type === 'Identifier' &&
|
|
104
123
|
BLACKLIST_PROPERTIES[styleObjName][componentName].includes(p.key.name)) {
|
|
105
124
|
hasCustomStyle = true;
|
|
125
|
+
addViolatingAttribute(p, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
|
|
106
126
|
}
|
|
107
127
|
});
|
|
108
128
|
}
|
|
@@ -126,10 +146,12 @@ const reportInlineStyle = (ast, attributes, componentName) => {
|
|
|
126
146
|
declaration.id.name === variableName &&
|
|
127
147
|
((_a = declaration.init) === null || _a === void 0 ? void 0 : _a.type) === 'ObjectExpression') {
|
|
128
148
|
declaration.init.properties.forEach((prop) => {
|
|
149
|
+
var _a;
|
|
129
150
|
if (prop.type === 'ObjectProperty' &&
|
|
130
151
|
prop.key.type === 'Identifier' &&
|
|
131
152
|
BLACKLIST_PROPERTIES[styleObjName][componentName].includes(prop.key.name)) {
|
|
132
153
|
hasCustomStyle = true;
|
|
154
|
+
addViolatingAttribute(prop, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
|
|
133
155
|
}
|
|
134
156
|
});
|
|
135
157
|
}
|
|
@@ -157,10 +179,12 @@ const reportInlineStyle = (ast, attributes, componentName) => {
|
|
|
157
179
|
prop.key.name === propName &&
|
|
158
180
|
prop.value.type === 'ObjectExpression') {
|
|
159
181
|
prop.value.properties.forEach((p) => {
|
|
182
|
+
var _a;
|
|
160
183
|
if (p.type === 'ObjectProperty' &&
|
|
161
184
|
p.key.type === 'Identifier' &&
|
|
162
185
|
BLACKLIST_PROPERTIES[styleObjName][componentName].includes(p.key.name)) {
|
|
163
186
|
hasCustomStyle = true;
|
|
187
|
+
addViolatingAttribute(p, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
|
|
164
188
|
}
|
|
165
189
|
});
|
|
166
190
|
}
|
|
@@ -174,6 +198,6 @@ const reportInlineStyle = (ast, attributes, componentName) => {
|
|
|
174
198
|
}
|
|
175
199
|
}
|
|
176
200
|
});
|
|
177
|
-
return locs;
|
|
201
|
+
return { locs, violatingAttributes };
|
|
178
202
|
};
|
|
179
203
|
exports.default = reportInlineStyle;
|