@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 CHANGED
@@ -10,9 +10,18 @@
10
10
  cp .env.example .env
11
11
  ```
12
12
 
13
- 2. 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.
13
+ 2. Set up [internal-tool-integrations service](https://github.com/Thinkei/internal-tool-integrations) for the bot to store report.
14
14
 
15
- 3. Start the app.
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
- 4. Open or update a pull request to trigger webhook events.
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
- approvedLocs: [55, 65, 64],
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
  });
@@ -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;
@@ -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,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 type { ComponentName } from './types';
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 locs = {
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
- locs.className = [...locs.className, ...(0, reportClassName_1.default)(attributes)];
49
- locs.style = [...locs.style, ...styleObjectLocs.style];
50
- locs.sx = [...locs.sx, ...styleObjectLocs.sx];
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
- locs.className = [...locs.className, ...(0, reportClassName_1.default)(attributes)];
63
- locs.style = [...locs.style, ...styleObjectLocs.style];
64
- locs.sx = [...locs.sx, ...styleObjectLocs.sx];
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
- locs.className = [
95
- ...locs.className,
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
- locs.style = [...locs.style, ...styleObjectLocs.style];
99
- locs.sx = [...locs.sx, ...styleObjectLocs.sx];
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 locs;
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
- style: number[];
5
- sx: number[];
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
- const INLINE_STYLE_PROPERTIES = ['style', 'sx'];
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hero-design/snowflake-guard",
3
- "version": "1.0.14",
3
+ "version": "1.1.0",
4
4
  "description": "A hero-design bot detecting snowflake usage",
5
5
  "author": "Hau Dao",
6
6
  "license": "ISC",