@hero-design/snowflake-guard 1.1.0 → 1.2.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.
@@ -93,6 +93,12 @@ describe('parseSource', () => {
93
93
  componentName: 'Button',
94
94
  inlineStyleProps: 'style',
95
95
  loc: 51,
96
+ additionalProps: [
97
+ {
98
+ propName: 'variant',
99
+ propValue: '"text"',
100
+ },
101
+ ],
96
102
  },
97
103
  {
98
104
  attributeName: 'padding',
@@ -101,9 +101,7 @@ const parseSource = (source) => {
101
101
  if (report.loc) {
102
102
  return !approvedCmtLocs.includes(report.loc);
103
103
  }
104
- else {
105
- return true;
106
- }
104
+ return true;
107
105
  });
108
106
  }
109
107
  return {
@@ -41,7 +41,10 @@ describe('reportInlineStyle', () => {
41
41
  sx={{
42
42
  padding: 10,
43
43
  backgroundColor: 'blue'
44
- }} />
44
+ }}
45
+ variant="primary"
46
+ intent="success"
47
+ />
45
48
  );
46
49
  `;
47
50
  // Step 1: Parse the code to generate the AST
@@ -50,7 +53,7 @@ describe('reportInlineStyle', () => {
50
53
  // Step 2: Traverse the AST to extract relevant JSXAttributes (style and sx)
51
54
  recast.visit(ast, {
52
55
  visitJSXAttribute(path) {
53
- const node = path.node;
56
+ const { node } = path;
54
57
  attributes.push(node);
55
58
  this.traverse(path);
56
59
  },
@@ -61,6 +64,12 @@ describe('reportInlineStyle', () => {
61
64
  constants_1.SX_RULESET_MAP.Card = ['padding', 'backgroundColor'];
62
65
  // Step 3: Run the function with the dynamically extracted attributes
63
66
  const result = (0, reportInlineStyle_1.default)(ast, attributes, componentName);
67
+ expect(result.additionalProps).toMatchObject([
68
+ {
69
+ propValue: '"primary"',
70
+ propName: 'variant',
71
+ },
72
+ ]);
64
73
  expect(result.locs.style).toEqual([3]);
65
74
  expect(result.locs.sx).toEqual([7]);
66
75
  expect(result.violatingAttributes).toEqual([
@@ -106,7 +115,7 @@ describe('reportInlineStyle', () => {
106
115
  // Step 2: Traverse the AST to extract relevant JSXAttributes (style and sx)
107
116
  recast.visit(ast, {
108
117
  visitJSXAttribute(path) {
109
- const node = path.node;
118
+ const { node } = path;
110
119
  attributes.push(node);
111
120
  this.traverse(path);
112
121
  },
@@ -1,5 +1,5 @@
1
1
  import * as recast from 'recast';
2
- import { InlineStyleProps } from './reportInlineStyle';
2
+ import { AdditionalProp, InlineStyleProps } from './reportInlineStyle';
3
3
  import type { ComponentName, CompoundComponentName } from './types';
4
4
  declare const reportCustomProperties: (ast: recast.types.ASTNode, componentList: {
5
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";
@@ -13,6 +13,7 @@ declare const reportCustomProperties: (ast: recast.types.ASTNode, componentList:
13
13
  inlineStyleProps: InlineStyleProps;
14
14
  componentName: CompoundComponentName;
15
15
  loc: number | undefined;
16
+ additionalProps?: AdditionalProp[];
16
17
  }[];
17
18
  };
18
19
  export default reportCustomProperties;
@@ -29,6 +29,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
29
29
  const recast = __importStar(require("recast"));
30
30
  const reportClassName_1 = __importDefault(require("./reportClassName"));
31
31
  const reportInlineStyle_1 = __importDefault(require("./reportInlineStyle"));
32
+ const mapViolatingAttributesAndAdditionalProps = (violatingAttributes, additionalProps) => {
33
+ return violatingAttributes.map((violatingAttribute) => (Object.assign(Object.assign({}, violatingAttribute), (additionalProps && additionalProps.length ? { additionalProps } : {}))));
34
+ };
32
35
  const reportCustomProperties = (ast, componentList) => {
33
36
  const report = {
34
37
  className: [],
@@ -45,7 +48,7 @@ const reportCustomProperties = (ast, componentList) => {
45
48
  localComponentList.includes(path.value.name.name)) {
46
49
  const attributes = path.value
47
50
  .attributes;
48
- const { locs: styleObjectLocs, violatingAttributes } = (0, reportInlineStyle_1.default)(ast, attributes, componentList[path.value.name.name]);
51
+ const { locs: styleObjectLocs, violatingAttributes, additionalProps, } = (0, reportInlineStyle_1.default)(ast, attributes, componentList[path.value.name.name]);
49
52
  report.className = [
50
53
  ...report.className,
51
54
  ...(0, reportClassName_1.default)(attributes),
@@ -54,7 +57,7 @@ const reportCustomProperties = (ast, componentList) => {
54
57
  report.sx = [...report.sx, ...styleObjectLocs.sx];
55
58
  report.violatingAttributes = [
56
59
  ...report.violatingAttributes,
57
- ...violatingAttributes,
60
+ ...mapViolatingAttributesAndAdditionalProps(violatingAttributes, additionalProps),
58
61
  ];
59
62
  }
60
63
  // Case 2: Custom compound component, e.g. <Card.Header />
@@ -66,7 +69,7 @@ const reportCustomProperties = (ast, componentList) => {
66
69
  ].join('.');
67
70
  const attributes = path.value
68
71
  .attributes;
69
- const { locs: styleObjectLocs, violatingAttributes } = (0, reportInlineStyle_1.default)(ast, attributes, compoundComponentName);
72
+ const { locs: styleObjectLocs, violatingAttributes, additionalProps, } = (0, reportInlineStyle_1.default)(ast, attributes, compoundComponentName);
70
73
  report.className = [
71
74
  ...report.className,
72
75
  ...(0, reportClassName_1.default)(attributes),
@@ -75,7 +78,7 @@ const reportCustomProperties = (ast, componentList) => {
75
78
  report.sx = [...report.sx, ...styleObjectLocs.sx];
76
79
  report.violatingAttributes = [
77
80
  ...report.violatingAttributes,
78
- ...violatingAttributes,
81
+ ...mapViolatingAttributesAndAdditionalProps(violatingAttributes, additionalProps),
79
82
  ];
80
83
  }
81
84
  },
@@ -105,7 +108,7 @@ const reportCustomProperties = (ast, componentList) => {
105
108
  openPath.value.name.name,
106
109
  ].join('.');
107
110
  const { attributes } = openPath.value;
108
- const { locs: styleObjectLocs, violatingAttributes } = (0, reportInlineStyle_1.default)(ast, attributes, compoundComponentName);
111
+ const { locs: styleObjectLocs, violatingAttributes, additionalProps, } = (0, reportInlineStyle_1.default)(ast, attributes, compoundComponentName);
109
112
  report.className = [
110
113
  ...report.className,
111
114
  ...(0, reportClassName_1.default)(attributes),
@@ -114,7 +117,7 @@ const reportCustomProperties = (ast, componentList) => {
114
117
  report.sx = [...report.sx, ...styleObjectLocs.sx];
115
118
  report.violatingAttributes = [
116
119
  ...report.violatingAttributes,
117
- ...violatingAttributes,
120
+ ...mapViolatingAttributesAndAdditionalProps(violatingAttributes, additionalProps),
118
121
  ];
119
122
  }
120
123
  },
@@ -2,18 +2,24 @@ import * as recast from 'recast';
2
2
  import type { CompoundComponentName } from './types';
3
3
  export type InlineStyleProps = 'style' | 'sx';
4
4
  export declare const INLINE_STYLE_PROPERTIES: string[];
5
- type ViolatingAttribute = {
5
+ export declare const ADDITIONAL_PROPERTIES: string[];
6
+ export type ViolatingAttribute = {
6
7
  attributeName: string;
7
8
  attributeValue: string | null;
8
9
  inlineStyleProps: InlineStyleProps;
9
10
  componentName: CompoundComponentName;
10
11
  loc: number | undefined;
11
12
  };
13
+ export type AdditionalProp = {
14
+ propName: string;
15
+ propValue: string | null;
16
+ };
12
17
  declare const reportInlineStyle: (ast: recast.types.ASTNode, attributes: recast.types.namedTypes.JSXAttribute[], componentName: CompoundComponentName) => {
13
18
  locs: {
14
19
  style: number[];
15
20
  sx: number[];
16
21
  };
17
22
  violatingAttributes: ViolatingAttribute[];
23
+ additionalProps: AdditionalProp[];
18
24
  };
19
25
  export default reportInlineStyle;
@@ -23,7 +23,7 @@ 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
+ exports.ADDITIONAL_PROPERTIES = exports.INLINE_STYLE_PROPERTIES = void 0;
27
27
  const recast = __importStar(require("recast"));
28
28
  const constants_1 = require("./constants");
29
29
  const BLACKLIST_PROPERTIES = {
@@ -31,6 +31,7 @@ const BLACKLIST_PROPERTIES = {
31
31
  sx: constants_1.SX_RULESET_MAP,
32
32
  };
33
33
  exports.INLINE_STYLE_PROPERTIES = ['style', 'sx'];
34
+ exports.ADDITIONAL_PROPERTIES = ['variant']; // Add any additional props you want to track
34
35
  const addViolatingAttribute = (prop, styleObjName, componentName, loc, violatingAttributes) => {
35
36
  if (prop.key.type !== 'Identifier') {
36
37
  return;
@@ -43,20 +44,48 @@ const addViolatingAttribute = (prop, styleObjName, componentName, loc, violating
43
44
  loc,
44
45
  });
45
46
  };
47
+ const getPropValue = (attr) => {
48
+ var _a;
49
+ if (!attr.value) {
50
+ return ''; // Handle case where value is missing
51
+ }
52
+ if (typeof attr.value === 'string') {
53
+ return attr.value; // Directly return string value
54
+ }
55
+ if (((_a = attr.value) === null || _a === void 0 ? void 0 : _a.type) === 'JSXExpressionContainer') {
56
+ return recast.print(attr.value.expression).code;
57
+ }
58
+ // Use recast to print the expression if it's not a simple string
59
+ return recast.print(attr.value).code;
60
+ };
46
61
  const reportInlineStyle = (ast, attributes, componentName) => {
47
62
  const locs = {
48
63
  style: [],
49
64
  sx: [],
50
65
  };
51
66
  const violatingAttributes = [];
67
+ const additionalProps = [];
52
68
  let hasCustomStyle = false;
53
69
  let styleObjName;
54
70
  attributes.forEach((attr) => {
55
71
  var _a;
56
- if (attr.type === 'JSXAttribute' &&
57
- typeof attr.name.name === 'string' &&
58
- exports.INLINE_STYLE_PROPERTIES.includes(attr.name.name) &&
59
- ((_a = attr.value) === null || _a === void 0 ? void 0 : _a.type) === 'JSXExpressionContainer') {
72
+ if (attr.type !== 'JSXAttribute') {
73
+ return;
74
+ }
75
+ if (typeof attr.name.name !== 'string') {
76
+ return;
77
+ }
78
+ if (exports.ADDITIONAL_PROPERTIES.includes(attr.name.name)) {
79
+ // Handle expression container like `style={{ ... }}`
80
+ additionalProps.push({
81
+ propName: attr.name.name,
82
+ propValue: getPropValue(attr), // Print expression as string
83
+ });
84
+ }
85
+ if (((_a = attr.value) === null || _a === void 0 ? void 0 : _a.type) !== 'JSXExpressionContainer') {
86
+ return;
87
+ }
88
+ if (exports.INLINE_STYLE_PROPERTIES.includes(attr.name.name)) {
60
89
  styleObjName = attr.name.name;
61
90
  const { expression } = attr.value;
62
91
  if (expression.type === 'ObjectExpression') {
@@ -198,6 +227,6 @@ const reportInlineStyle = (ast, attributes, componentName) => {
198
227
  }
199
228
  }
200
229
  });
201
- return { locs, violatingAttributes };
230
+ return { locs, violatingAttributes, additionalProps };
202
231
  };
203
232
  exports.default = reportInlineStyle;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hero-design/snowflake-guard",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A hero-design bot detecting snowflake usage",
5
5
  "author": "Hau Dao",
6
6
  "license": "ISC",
@@ -18,7 +18,9 @@
18
18
  "scripts": {
19
19
  "build": "tsc",
20
20
  "start": "yarn build && probot run ./lib/src/index.js",
21
+ "type-check": "tsc --noEmit",
21
22
  "test": "jest --passWithNoTests",
23
+ "lint": "eslint src",
22
24
  "deploy": "netlify deploy --site snowflake-guard.netlify.app --prod --auth $NETLIFY_AUTH_TOKEN",
23
25
  "publish:npm": "yarn publish --access public"
24
26
  },
@@ -34,6 +36,7 @@
34
36
  "@types/jest": "^29.0.0",
35
37
  "@types/node": "^18.0.0",
36
38
  "config-tsconfig": "8.42.4",
39
+ "eslint": "^8.56.0",
37
40
  "eslint-config-hd": "8.42.4",
38
41
  "jest": "^29.0.0",
39
42
  "nock": "^13.0.5",