@hero-design/snowflake-guard 1.2.4 → 1.3.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.
@@ -0,0 +1,214 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.INLINE_STYLE_PROPERTIES = void 0;
27
+ const recast = __importStar(require("recast"));
28
+ const constants_1 = require("./constants");
29
+ const BLACKLIST_PROPERTIES = {
30
+ style: constants_1.MOBILE_RULESET_MAP,
31
+ barStyle: constants_1.BAR_STYLE_RULESET_MAP,
32
+ containerStyle: constants_1.CONTAINER_STYLE_RULESET_MAP,
33
+ textStyle: constants_1.TEXT_STYLE_RULESET_MAP,
34
+ };
35
+ exports.INLINE_STYLE_PROPERTIES = [
36
+ 'style',
37
+ 'barStyle',
38
+ 'containerStyle',
39
+ 'textStyle',
40
+ ];
41
+ const addViolatingAttribute = (prop, styleObjName, componentName, loc, violatingAttributes) => {
42
+ if (prop.key.type !== 'Identifier') {
43
+ return;
44
+ }
45
+ violatingAttributes.push({
46
+ attributeName: prop.key.name,
47
+ attributeValue: recast.print(prop.value).code,
48
+ inlineStyleProps: styleObjName,
49
+ componentName,
50
+ loc,
51
+ });
52
+ };
53
+ const reportInlineStyle = (ast, attributes, componentName) => {
54
+ const locs = {};
55
+ const violatingAttributes = [];
56
+ let hasCustomStyle = false;
57
+ let styleObjName;
58
+ attributes.forEach((attr) => {
59
+ var _a;
60
+ if (attr.type !== 'JSXAttribute') {
61
+ return;
62
+ }
63
+ if (typeof attr.name.name !== 'string') {
64
+ return;
65
+ }
66
+ if (((_a = attr.value) === null || _a === void 0 ? void 0 : _a.type) !== 'JSXExpressionContainer') {
67
+ return;
68
+ }
69
+ if (exports.INLINE_STYLE_PROPERTIES.includes(attr.name.name)) {
70
+ styleObjName = attr.name.name;
71
+ const PROHIBITED_PROPERTIES = BLACKLIST_PROPERTIES[styleObjName][componentName] || [];
72
+ const { expression } = attr.value;
73
+ if (expression.type === 'ObjectExpression') {
74
+ expression.properties.forEach((prop) => {
75
+ var _a;
76
+ // Case 1: Use direct object, e.g. <Button style={{ backgroundColor: 'red' }} />
77
+ if (prop.type === 'ObjectProperty' &&
78
+ prop.key.type === 'Identifier' &&
79
+ PROHIBITED_PROPERTIES.includes(prop.key.name)) {
80
+ hasCustomStyle = true;
81
+ addViolatingAttribute(prop, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
82
+ }
83
+ // Case 2: Use spread operator, e.g. <Button style={{ ...customStyle }} />
84
+ if (prop.type === 'SpreadElement' &&
85
+ prop.argument.type === 'Identifier') {
86
+ const variableName = prop.argument.name;
87
+ recast.visit(ast, {
88
+ visitVariableDeclaration(path) {
89
+ var _a;
90
+ this.traverse(path);
91
+ const declaration = path.value
92
+ .declarations[0];
93
+ if (declaration.id.type === 'Identifier' &&
94
+ declaration.id.name === variableName &&
95
+ ((_a = declaration.init) === null || _a === void 0 ? void 0 : _a.type) === 'ObjectExpression') {
96
+ declaration.init.properties.forEach((deProp) => {
97
+ var _a;
98
+ if (deProp.type === 'ObjectProperty' &&
99
+ deProp.key.type === 'Identifier' &&
100
+ PROHIBITED_PROPERTIES.includes(deProp.key.name)) {
101
+ hasCustomStyle = true;
102
+ addViolatingAttribute(deProp, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
103
+ }
104
+ });
105
+ }
106
+ },
107
+ });
108
+ }
109
+ // Case 3: Use spread operator with variable's property, e.g. <Button style={{ ...customStyle.button }} />
110
+ if (prop.type === 'SpreadElement' &&
111
+ prop.argument.type === 'MemberExpression' &&
112
+ prop.argument.object.type === 'Identifier' &&
113
+ prop.argument.property.type === 'Identifier') {
114
+ const variableName = prop.argument.object.name;
115
+ const propName = prop.argument.property.name;
116
+ recast.visit(ast, {
117
+ visitVariableDeclaration(path) {
118
+ var _a;
119
+ this.traverse(path);
120
+ const declaration = path.value
121
+ .declarations[0];
122
+ if (declaration.id.type === 'Identifier' &&
123
+ declaration.id.name === variableName &&
124
+ ((_a = declaration.init) === null || _a === void 0 ? void 0 : _a.type) === 'ObjectExpression') {
125
+ declaration.init.properties.forEach((deProp) => {
126
+ if (deProp.type === 'ObjectProperty' &&
127
+ deProp.key.type === 'Identifier' &&
128
+ deProp.key.name === propName &&
129
+ deProp.value.type === 'ObjectExpression') {
130
+ deProp.value.properties.forEach((p) => {
131
+ var _a;
132
+ if (p.type === 'ObjectProperty' &&
133
+ p.key.type === 'Identifier' &&
134
+ PROHIBITED_PROPERTIES.includes(p.key.name)) {
135
+ hasCustomStyle = true;
136
+ addViolatingAttribute(p, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
137
+ }
138
+ });
139
+ }
140
+ });
141
+ }
142
+ },
143
+ });
144
+ }
145
+ });
146
+ }
147
+ // Case 4: Use variable, e.g. <Button style={customStyle} />
148
+ if (expression.type === 'Identifier') {
149
+ const variableName = expression.name;
150
+ recast.visit(ast, {
151
+ visitVariableDeclaration(path) {
152
+ var _a;
153
+ this.traverse(path);
154
+ const declaration = path.value
155
+ .declarations[0];
156
+ if (declaration.id.type === 'Identifier' &&
157
+ declaration.id.name === variableName &&
158
+ ((_a = declaration.init) === null || _a === void 0 ? void 0 : _a.type) === 'ObjectExpression') {
159
+ declaration.init.properties.forEach((prop) => {
160
+ var _a;
161
+ if (prop.type === 'ObjectProperty' &&
162
+ prop.key.type === 'Identifier' &&
163
+ PROHIBITED_PROPERTIES.includes(prop.key.name)) {
164
+ hasCustomStyle = true;
165
+ addViolatingAttribute(prop, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
166
+ }
167
+ });
168
+ }
169
+ },
170
+ });
171
+ }
172
+ // Case 5: Use variable's property, e.g. <Button style={customStyle.button} />
173
+ if (expression.type === 'MemberExpression' &&
174
+ expression.object.type === 'Identifier' &&
175
+ expression.property.type === 'Identifier') {
176
+ const objectName = expression.object.name;
177
+ const propName = expression.property.name;
178
+ recast.visit(ast, {
179
+ visitVariableDeclaration(path) {
180
+ var _a;
181
+ this.traverse(path);
182
+ const declaration = path.value
183
+ .declarations[0];
184
+ if (declaration.id.type === 'Identifier' &&
185
+ declaration.id.name === objectName &&
186
+ ((_a = declaration.init) === null || _a === void 0 ? void 0 : _a.type) === 'ObjectExpression') {
187
+ declaration.init.properties.forEach((prop) => {
188
+ if (prop.type === 'ObjectProperty' &&
189
+ prop.key.type === 'Identifier' &&
190
+ prop.key.name === propName &&
191
+ prop.value.type === 'ObjectExpression') {
192
+ prop.value.properties.forEach((p) => {
193
+ var _a;
194
+ if (p.type === 'ObjectProperty' &&
195
+ p.key.type === 'Identifier' &&
196
+ PROHIBITED_PROPERTIES.includes(p.key.name)) {
197
+ hasCustomStyle = true;
198
+ addViolatingAttribute(p, styleObjName, componentName, (_a = attr.loc) === null || _a === void 0 ? void 0 : _a.start.line, violatingAttributes);
199
+ }
200
+ });
201
+ }
202
+ });
203
+ }
204
+ },
205
+ });
206
+ }
207
+ if (hasCustomStyle && attr.loc) {
208
+ locs[styleObjName] = attr.loc.start.line;
209
+ }
210
+ }
211
+ });
212
+ return { locs, violatingAttributes };
213
+ };
214
+ exports.default = reportInlineStyle;
@@ -1,6 +1,6 @@
1
1
  import * as recast from 'recast';
2
2
  import type { MobileComponentName } from './types';
3
3
  declare const reportStyledComponents: (ast: recast.types.ASTNode, componentList: {
4
- [k: string]: "Alert" | "Badge" | "Button" | "Card" | "Carousel" | "Checkbox" | "Chip" | "Collapse" | "DatePicker" | "Divider" | "Empty" | "Icon" | "Portal" | "Progress" | "Radio" | "Rate" | "Select" | "Slider" | "Spinner" | "Switch" | "Tabs" | "Tag" | "TimePicker" | "Typography" | "Error" | "Image" | "Accordion" | "Attachment" | "Avatar" | "BottomNavigation" | "BottomSheet" | "Box" | "Calendar" | "ContentNavigator" | "Drawer" | "FAB" | "HeroDesignProvider" | "MapPin" | "List" | "PinInput" | "Swipeable" | "SectionHeading" | "Skeleton" | "Success" | "TextInput" | "Toast" | "Toolbar" | "RefreshControl" | "RichTextEditor" | "PageControl" | "ScrollViewWithFAB" | "SectionListWithFAB" | "FlatListWithFAB" | "Search" | "FloatingIsland";
4
+ [k: string]: "Accordion" | "Alert" | "Attachment" | "Avatar" | "Badge" | "BottomNavigation" | "BottomSheet" | "Box" | "Button" | "Calendar" | "Carousel" | "Card" | "Chip" | "Collapse" | "Checkbox" | "ContentNavigator" | "DatePicker" | "Divider" | "Drawer" | "Empty" | "Error" | "FAB" | "HeroDesignProvider" | "MapPin" | "Icon" | "Image" | "List" | "PinInput" | "Progress" | "Slider" | "Spinner" | "Swipeable" | "Radio" | "SectionHeading" | "Select" | "Skeleton" | "Success" | "Switch" | "Tabs" | "Tag" | "TextInput" | "TimePicker" | "Toast" | "Toolbar" | "Typography" | "Rate" | "RefreshControl" | "RichTextEditor" | "PageControl" | "Portal" | "ScrollViewWithFAB" | "SectionListWithFAB" | "FlatListWithFAB" | "Search" | "FloatingIsland";
5
5
  }, styledAliasName: string) => number[];
6
6
  export default reportStyledComponents;
@@ -39,13 +39,13 @@ const reportStyledComponents = (ast, componentList, styledAliasName) => {
39
39
  tag.callee.type === 'Identifier' &&
40
40
  tag.callee.name === styledAliasName) {
41
41
  const arg = tag.arguments[0];
42
- // Case 1: Custom default component, e.g. styled(Card)
42
+ // Case 1: Custom default component, e.g. styled(Button)
43
43
  if (arg.type === 'Identifier' &&
44
44
  localComponentList.includes(arg.name) &&
45
45
  tag.loc) {
46
46
  locs.push(tag.loc.start.line);
47
47
  }
48
- // Case 2: Custom compound component, e.g. styled(Card.Header)
48
+ // Case 2: Custom compound component, e.g. styled(Button.Icon)
49
49
  if (arg.type === 'MemberExpression' &&
50
50
  arg.object.type === 'Identifier' &&
51
51
  localComponentList.includes(arg.object.name) &&
@@ -54,7 +54,7 @@ const reportStyledComponents = (ast, componentList, styledAliasName) => {
54
54
  }
55
55
  }
56
56
  }
57
- // Case 3: Custom compound component with spead operator. e.g. const { Header } = Card, then styled(Header)
57
+ // Case 3: Custom compound component with spead operator. e.g. const { Icon } = Button, then styled(Icon)
58
58
  if (declaration.init &&
59
59
  declaration.init.type === 'Identifier' &&
60
60
  localComponentList.includes(declaration.init.name)) {
@@ -1 +1,2 @@
1
1
  export declare const parseTypeScript: (source: string) => any;
2
+ export declare const parseFlow: (source: string) => any;
@@ -23,10 +23,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.parseTypeScript = void 0;
26
+ exports.parseFlow = exports.parseTypeScript = void 0;
27
27
  const recast = __importStar(require("recast"));
28
28
  const tsParser = __importStar(require("../../parsers/typescript"));
29
+ const flowParser = __importStar(require("../../parsers/flow"));
29
30
  const parseTypeScript = (source) => {
30
31
  return recast.parse(source, { parser: tsParser });
31
32
  };
32
33
  exports.parseTypeScript = parseTypeScript;
34
+ const parseFlow = (source) => {
35
+ return recast.parse(source, { parser: flowParser });
36
+ };
37
+ exports.parseFlow = parseFlow;
@@ -2,7 +2,7 @@ import * as recast from 'recast';
2
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
- [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
+ [k: string]: "Alert" | "Badge" | "Button" | "Carousel" | "Card" | "Chip" | "Collapse" | "Checkbox" | "DatePicker" | "Divider" | "Empty" | "Icon" | "Progress" | "Slider" | "Spinner" | "Radio" | "Select" | "Switch" | "Tabs" | "Tag" | "TimePicker" | "Typography" | "Rate" | "Portal" | "Banner" | "Breadcrumb" | "Chart" | "Comment" | "ContextPanel" | "Dropdown" | "File" | "Filters" | "Form" | "Grid" | "InPageNavigation" | "Input" | "MediaQuery" | "Menu" | "Modal" | "Notification" | "PageHeader" | "Pagination" | "Portlet" | "Result" | "SelectButton" | "SideBar" | "Statistic" | "Steps" | "Table" | "TagInput" | "Timeline" | "Tooltip" | "Widget";
6
6
  }, commentList: {
7
7
  classNameCmts: number[];
8
8
  styleCmts: {
@@ -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" | "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";
4
+ [k: string]: "Alert" | "Badge" | "Button" | "Carousel" | "Card" | "Chip" | "Collapse" | "Checkbox" | "DatePicker" | "Divider" | "Empty" | "Icon" | "Progress" | "Slider" | "Spinner" | "Radio" | "Select" | "Switch" | "Tabs" | "Tag" | "TimePicker" | "Typography" | "Rate" | "Portal" | "Banner" | "Breadcrumb" | "Chart" | "Comment" | "ContextPanel" | "Dropdown" | "File" | "Filters" | "Form" | "Grid" | "InPageNavigation" | "Input" | "MediaQuery" | "Menu" | "Modal" | "Notification" | "PageHeader" | "Pagination" | "Portlet" | "Result" | "SelectButton" | "SideBar" | "Statistic" | "Steps" | "Table" | "TagInput" | "Timeline" | "Tooltip" | "Widget";
5
5
  }, styledAliasName: string) => number[];
6
6
  export default reportStyledComponents;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hero-design/snowflake-guard",
3
- "version": "1.2.4",
3
+ "version": "1.3.0",
4
4
  "description": "A hero-design bot detecting snowflake usage",
5
5
  "author": "Hau Dao",
6
6
  "license": "ISC",