@hero-design/snowflake-guard 1.0.13 → 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/constants.d.ts +5 -1
- package/lib/src/reports/constants.js +17 -7
- package/lib/src/reports/reportCustomStyleProperties.d.ts +10 -2
- 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/lib/src/reports/reportStyledComponents.d.ts +1 -1
- 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
|
+
});
|
|
@@ -22,6 +22,7 @@ declare const RULESET_MAP: {
|
|
|
22
22
|
'Checkbox.Group': string[];
|
|
23
23
|
'Checkbox.Button': string[];
|
|
24
24
|
'Checkbox.ButtonGroup': string[];
|
|
25
|
+
Chip: string[];
|
|
25
26
|
Collapse: string[];
|
|
26
27
|
Comment: string[];
|
|
27
28
|
'Comment.Editor': string[];
|
|
@@ -38,6 +39,7 @@ declare const RULESET_MAP: {
|
|
|
38
39
|
'DatePicker.Fortnightly': string[];
|
|
39
40
|
'DatePicker.SingleMonth': string[];
|
|
40
41
|
'DatePicker.SingleYear': string[];
|
|
42
|
+
'DatePicker.FixedRange': string[];
|
|
41
43
|
Divider: string[];
|
|
42
44
|
Dropdown: string[];
|
|
43
45
|
Empty: string[];
|
|
@@ -128,6 +130,7 @@ declare const SX_RULESET_MAP: {
|
|
|
128
130
|
'Checkbox.Group': string[];
|
|
129
131
|
'Checkbox.Button': string[];
|
|
130
132
|
'Checkbox.ButtonGroup': string[];
|
|
133
|
+
Chip: string[];
|
|
131
134
|
Collapse: string[];
|
|
132
135
|
Comment: string[];
|
|
133
136
|
'Comment.Editor': string[];
|
|
@@ -144,6 +147,7 @@ declare const SX_RULESET_MAP: {
|
|
|
144
147
|
'DatePicker.Fortnightly': string[];
|
|
145
148
|
'DatePicker.SingleMonth': string[];
|
|
146
149
|
'DatePicker.SingleYear': string[];
|
|
150
|
+
'DatePicker.FixedRange': string[];
|
|
147
151
|
Divider: string[];
|
|
148
152
|
Dropdown: string[];
|
|
149
153
|
Empty: string[];
|
|
@@ -210,7 +214,7 @@ declare const SX_RULESET_MAP: {
|
|
|
210
214
|
'Typography.Text': string[];
|
|
211
215
|
Widget: string[];
|
|
212
216
|
};
|
|
213
|
-
declare const HD_COMPONENTS: readonly ["Alert", "Badge", "Banner", "Breadcrumb", "Button", "Card", "Carousel", "Chart", "Checkbox", "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"];
|
|
217
|
+
declare const HD_COMPONENTS: readonly ["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"];
|
|
214
218
|
declare const APPROVED_COMMENT = "@snowflake-guard/snowflake-approved-by-andromeda";
|
|
215
219
|
declare const APPROVED_CLASSNAME_COMMENT = "@snowflake-guard/none-css-classname";
|
|
216
220
|
export { HD_COMPONENTS, RULESET_MAP, SX_RULESET_MAP, APPROVED_COMMENT, APPROVED_CLASSNAME_COMMENT, };
|
|
@@ -177,6 +177,12 @@ const RULESET_MAP = {
|
|
|
177
177
|
...SHADOW_ATTRS,
|
|
178
178
|
...HEIGHT_ATTRS,
|
|
179
179
|
],
|
|
180
|
+
Chip: [
|
|
181
|
+
...COMMON_PROHIBITED_ATTRS,
|
|
182
|
+
...SHADOW_ATTRS,
|
|
183
|
+
...WIDTH_ATTRS,
|
|
184
|
+
...HEIGHT_ATTRS,
|
|
185
|
+
],
|
|
180
186
|
Collapse: [...COMMON_PROHIBITED_ATTRS, ...SHADOW_ATTRS, ...HEIGHT_ATTRS],
|
|
181
187
|
Comment: [...COMMON_PROHIBITED_ATTRS, ...SHADOW_ATTRS, ...HEIGHT_ATTRS],
|
|
182
188
|
'Comment.Editor': [
|
|
@@ -255,6 +261,11 @@ const RULESET_MAP = {
|
|
|
255
261
|
...SHADOW_ATTRS,
|
|
256
262
|
...HEIGHT_ATTRS,
|
|
257
263
|
],
|
|
264
|
+
'DatePicker.FixedRange': [
|
|
265
|
+
...COMMON_PROHIBITED_ATTRS,
|
|
266
|
+
...SHADOW_ATTRS,
|
|
267
|
+
...HEIGHT_ATTRS,
|
|
268
|
+
],
|
|
258
269
|
Divider: [
|
|
259
270
|
...COMMON_PROHIBITED_ATTRS,
|
|
260
271
|
...SHADOW_ATTRS,
|
|
@@ -552,12 +563,7 @@ const RULESET_MAP = {
|
|
|
552
563
|
],
|
|
553
564
|
TimePicker: [...COMMON_PROHIBITED_ATTRS, ...SHADOW_ATTRS, ...HEIGHT_ATTRS],
|
|
554
565
|
Timeline: [...COMMON_PROHIBITED_ATTRS, ...SHADOW_ATTRS, ...HEIGHT_ATTRS],
|
|
555
|
-
Tooltip: [
|
|
556
|
-
...COMMON_PROHIBITED_ATTRS,
|
|
557
|
-
...SHADOW_ATTRS,
|
|
558
|
-
...WIDTH_ATTRS,
|
|
559
|
-
...HEIGHT_ATTRS,
|
|
560
|
-
],
|
|
566
|
+
Tooltip: [...COMMON_PROHIBITED_ATTRS, ...SHADOW_ATTRS],
|
|
561
567
|
'Typography.Title': [
|
|
562
568
|
...COMMON_PROHIBITED_ATTRS,
|
|
563
569
|
...SHADOW_ATTRS,
|
|
@@ -605,7 +611,7 @@ const SX_RULESET_MAP = Object.assign(Object.assign({}, RULESET_MAP), { Alert: [.
|
|
|
605
611
|
], 'Checkbox.ButtonGroup': [
|
|
606
612
|
...RULESET_MAP['Checkbox.ButtonGroup'],
|
|
607
613
|
...COMMON_SX_PROHIBITED_ATTRS,
|
|
608
|
-
], Collapse: [...RULESET_MAP.Collapse, ...COMMON_SX_PROHIBITED_ATTRS], Comment: [...RULESET_MAP.Comment, ...COMMON_SX_PROHIBITED_ATTRS], 'Comment.Editor': [
|
|
614
|
+
], Chip: [...RULESET_MAP.Chip, ...COMMON_SX_PROHIBITED_ATTRS], Collapse: [...RULESET_MAP.Collapse, ...COMMON_SX_PROHIBITED_ATTRS], Comment: [...RULESET_MAP.Comment, ...COMMON_SX_PROHIBITED_ATTRS], 'Comment.Editor': [
|
|
609
615
|
...RULESET_MAP['Comment.Editor'],
|
|
610
616
|
...COMMON_SX_PROHIBITED_ATTRS,
|
|
611
617
|
], ContextPanel: [
|
|
@@ -649,6 +655,9 @@ const SX_RULESET_MAP = Object.assign(Object.assign({}, RULESET_MAP), { Alert: [.
|
|
|
649
655
|
], 'DatePicker.SingleYear': [
|
|
650
656
|
...RULESET_MAP['DatePicker.SingleYear'],
|
|
651
657
|
...COMMON_SX_PROHIBITED_ATTRS,
|
|
658
|
+
], 'DatePicker.FixedRange': [
|
|
659
|
+
...RULESET_MAP['DatePicker.FixedRange'],
|
|
660
|
+
...COMMON_SX_PROHIBITED_ATTRS,
|
|
652
661
|
], Divider: [
|
|
653
662
|
...RULESET_MAP.Divider,
|
|
654
663
|
...COMMON_SX_PROHIBITED_ATTRS,
|
|
@@ -799,6 +808,7 @@ const HD_COMPONENTS = [
|
|
|
799
808
|
'Carousel',
|
|
800
809
|
'Chart',
|
|
801
810
|
'Checkbox',
|
|
811
|
+
'Chip',
|
|
802
812
|
'Collapse',
|
|
803
813
|
'Comment',
|
|
804
814
|
'ContextPanel',
|
|
@@ -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
|
-
[k: string]: "Alert" | "Badge" | "Banner" | "Breadcrumb" | "Button" | "Card" | "Carousel" | "Chart" | "Checkbox" | "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
|
+
[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;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as recast from 'recast';
|
|
2
2
|
import type { ComponentName } from './types';
|
|
3
3
|
declare const reportStyledComponents: (ast: recast.types.ASTNode, componentList: {
|
|
4
|
-
[k: string]: "Alert" | "Badge" | "Banner" | "Breadcrumb" | "Button" | "Card" | "Carousel" | "Chart" | "Checkbox" | "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";
|
|
4
|
+
[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
5
|
}, styledAliasName: string) => number[];
|
|
6
6
|
export default reportStyledComponents;
|