@hero-design/snowflake-guard 1.2.3 → 1.2.4-alpha.1

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.
Files changed (29) hide show
  1. package/README.md +2 -0
  2. package/lib/src/index.js +30 -22
  3. package/lib/src/parseMobileSource.d.ts +15 -0
  4. package/lib/src/parseMobileSource.js +118 -0
  5. package/lib/src/parsers/flow.d.ts +3 -0
  6. package/lib/src/parsers/flow.js +37 -0
  7. package/lib/src/reports/mobile/__tests__/reportCustomStyleProperties.spec.d.ts +1 -0
  8. package/lib/src/reports/mobile/__tests__/reportCustomStyleProperties.spec.js +123 -0
  9. package/lib/src/reports/mobile/__tests__/reportInlineStyle.spec.d.ts +1 -0
  10. package/lib/src/reports/mobile/__tests__/reportInlineStyle.spec.js +103 -0
  11. package/lib/src/reports/mobile/__tests__/reportStyledComponents.spec.d.ts +1 -0
  12. package/lib/src/reports/mobile/__tests__/reportStyledComponents.spec.js +54 -0
  13. package/lib/src/reports/mobile/constants.d.ts +260 -0
  14. package/lib/src/reports/mobile/constants.js +429 -0
  15. package/lib/src/reports/mobile/reportCustomStyleProperties.d.ts +38 -0
  16. package/lib/src/reports/mobile/reportCustomStyleProperties.js +155 -0
  17. package/lib/src/reports/mobile/reportInlineStyle.d.ts +21 -0
  18. package/lib/src/reports/mobile/reportInlineStyle.js +214 -0
  19. package/lib/src/reports/mobile/reportStyledComponents.d.ts +6 -0
  20. package/lib/src/reports/mobile/reportStyledComponents.js +95 -0
  21. package/lib/src/reports/mobile/reportToastStyle.d.ts +0 -0
  22. package/lib/src/reports/mobile/reportToastStyle.js +1 -0
  23. package/lib/src/reports/mobile/testUtils.d.ts +1 -0
  24. package/lib/src/reports/mobile/testUtils.js +32 -0
  25. package/lib/src/reports/mobile/types.d.ts +3 -0
  26. package/lib/src/reports/mobile/types.js +2 -0
  27. package/lib/src/reports/reportCustomStyleProperties.d.ts +1 -1
  28. package/lib/src/reports/reportStyledComponents.d.ts +1 -1
  29. package/package.json +2 -2
@@ -0,0 +1,38 @@
1
+ import * as recast from 'recast';
2
+ import { InlineStyleProps, ViolatingAttribute } from './reportInlineStyle';
3
+ import type { CompoundMobileComponentName, MobileComponentName } from './types';
4
+ export declare const getNonApprovedInlineLocs: (reportedLocs: {
5
+ style?: number;
6
+ barStyle?: number;
7
+ containerStyle?: number;
8
+ textStyle?: number;
9
+ }, violatingAttributes: ViolatingAttribute[], approvedCmts: {
10
+ loc: number;
11
+ comment: string;
12
+ }[], elementLoc: number) => {
13
+ reportedLocs: {
14
+ style?: number | undefined;
15
+ barStyle?: number | undefined;
16
+ containerStyle?: number | undefined;
17
+ textStyle?: number | undefined;
18
+ };
19
+ noneApprovedAttributes: ViolatingAttribute[];
20
+ };
21
+ declare const reportCustomProperties: (ast: recast.types.ASTNode, componentList: {
22
+ [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";
23
+ }, commentList: {
24
+ styleCmts: {
25
+ loc: number;
26
+ comment: string;
27
+ }[];
28
+ }) => {
29
+ style: number[];
30
+ violatingAttributes: {
31
+ attributeName: string;
32
+ attributeValue: string | null;
33
+ inlineStyleProps: InlineStyleProps;
34
+ componentName: CompoundMobileComponentName;
35
+ loc: number | undefined;
36
+ }[];
37
+ };
38
+ export default reportCustomProperties;
@@ -0,0 +1,155 @@
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
+ exports.getNonApprovedInlineLocs = void 0;
30
+ const recast = __importStar(require("recast"));
31
+ const reportInlineStyle_1 = __importDefault(require("./reportInlineStyle"));
32
+ const mapViolatingAttributesAndAdditionalProps = (violatingAttributes) => {
33
+ return violatingAttributes.map((violatingAttribute) => (Object.assign({}, violatingAttribute)));
34
+ };
35
+ const getNonApprovedInlineLocs = (reportedLocs, violatingAttributes, approvedCmts, elementLoc) => {
36
+ var _a, _b;
37
+ const approvedCmt = approvedCmts.find((cmt) => cmt.loc === elementLoc - 1);
38
+ if (approvedCmt) {
39
+ const approvedCmtAttrNames = (_b = (_a = approvedCmt.comment
40
+ .toLowerCase()
41
+ .replace(/\s+/g, '')
42
+ .split('attributes:')[1]) === null || _a === void 0 ? void 0 : _a.split(',')) !== null && _b !== void 0 ? _b : [];
43
+ const noneApprovedStyleAttributes = violatingAttributes.filter((attr) => attr.loc === reportedLocs.style &&
44
+ !approvedCmtAttrNames.includes(attr.attributeName.toLowerCase()));
45
+ const nonApprovedBarStyleAttributes = violatingAttributes.filter((attr) => attr.loc === reportedLocs.barStyle &&
46
+ !approvedCmtAttrNames.includes(attr.attributeName.toLowerCase()));
47
+ const nonApprovedContainerStyleAttributes = violatingAttributes.filter((attr) => attr.loc === reportedLocs.containerStyle &&
48
+ !approvedCmtAttrNames.includes(attr.attributeName.toLowerCase()));
49
+ const nonApprovedTextStyleAttributes = violatingAttributes.filter((attr) => attr.loc === reportedLocs.textStyle &&
50
+ !approvedCmtAttrNames.includes(attr.attributeName.toLowerCase()));
51
+ if (noneApprovedStyleAttributes.length > 0) {
52
+ return {
53
+ reportedLocs,
54
+ noneApprovedAttributes: [
55
+ ...noneApprovedStyleAttributes,
56
+ ...nonApprovedBarStyleAttributes,
57
+ ...nonApprovedContainerStyleAttributes,
58
+ ...nonApprovedTextStyleAttributes,
59
+ ],
60
+ };
61
+ }
62
+ return { reportedLocs: {}, noneApprovedAttributes: [] };
63
+ }
64
+ return { reportedLocs, noneApprovedAttributes: violatingAttributes };
65
+ };
66
+ exports.getNonApprovedInlineLocs = getNonApprovedInlineLocs;
67
+ const reportCustomProperties = (ast, componentList, commentList) => {
68
+ const report = {
69
+ style: [],
70
+ violatingAttributes: [],
71
+ };
72
+ const localComponentList = Object.keys(componentList);
73
+ recast.visit(ast, {
74
+ visitJSXOpeningElement(path) {
75
+ this.traverse(path);
76
+ // Case 1: Custom default component, e.g. <Card />
77
+ if (path.value.name.type === 'JSXIdentifier' &&
78
+ localComponentList.includes(path.value.name.name)) {
79
+ const attributes = path.value
80
+ .attributes;
81
+ const { locs: styleObjectLocs, violatingAttributes } = (0, reportInlineStyle_1.default)(ast, attributes, componentList[path.value.name.name]);
82
+ const { reportedLocs, noneApprovedAttributes } = (0, exports.getNonApprovedInlineLocs)(styleObjectLocs, violatingAttributes, commentList.styleCmts, path.value.loc.start.line);
83
+ if (reportedLocs.style) {
84
+ report.style.push(reportedLocs.style);
85
+ }
86
+ report.violatingAttributes = [
87
+ ...report.violatingAttributes,
88
+ ...mapViolatingAttributesAndAdditionalProps(noneApprovedAttributes),
89
+ ];
90
+ }
91
+ // Case 2: Custom compound component, e.g. <Card.Header />
92
+ if (path.value.name.type === 'JSXMemberExpression' &&
93
+ localComponentList.includes(path.value.name.object.name)) {
94
+ const compoundComponentName = [
95
+ componentList[path.value.name.object.name],
96
+ path.value.name.property.name,
97
+ ].join('.');
98
+ const attributes = path.value
99
+ .attributes;
100
+ const { locs: styleObjectLocs, violatingAttributes } = (0, reportInlineStyle_1.default)(ast, attributes, compoundComponentName);
101
+ const { reportedLocs, noneApprovedAttributes } = (0, exports.getNonApprovedInlineLocs)(styleObjectLocs, violatingAttributes, commentList.styleCmts, path.value.loc.start.line);
102
+ if (reportedLocs.style) {
103
+ report.style.push(reportedLocs.style);
104
+ }
105
+ report.violatingAttributes = [
106
+ ...report.violatingAttributes,
107
+ ...mapViolatingAttributesAndAdditionalProps(noneApprovedAttributes),
108
+ ];
109
+ }
110
+ },
111
+ // Case 3: Custom compound component with spead operator. e.g. const { Header } = Card, then <Header />
112
+ visitVariableDeclaration(path) {
113
+ this.traverse(path);
114
+ const declaration = path.value
115
+ .declarations[0];
116
+ if (declaration.init &&
117
+ declaration.init.type === 'Identifier' &&
118
+ localComponentList.includes(declaration.init.name)) {
119
+ const componentName = declaration.init.name;
120
+ const { id } = declaration;
121
+ if (id.type === 'ObjectPattern') {
122
+ const compoundComponentNames = id.properties
123
+ .map((prop) => prop.type === 'ObjectProperty' && prop.key.type === 'Identifier'
124
+ ? prop.key.name
125
+ : null)
126
+ .filter((name) => name !== null);
127
+ recast.visit(ast, {
128
+ visitJSXOpeningElement(openPath) {
129
+ this.traverse(openPath);
130
+ if (openPath.value.name.type === 'JSXIdentifier' &&
131
+ compoundComponentNames.includes(openPath.value.name.name)) {
132
+ const compoundComponentName = [
133
+ componentList[componentName],
134
+ openPath.value.name.name,
135
+ ].join('.');
136
+ const { attributes } = openPath.value;
137
+ const { locs: styleObjectLocs, violatingAttributes } = (0, reportInlineStyle_1.default)(ast, attributes, compoundComponentName);
138
+ const { reportedLocs, noneApprovedAttributes } = (0, exports.getNonApprovedInlineLocs)(styleObjectLocs, violatingAttributes, commentList.styleCmts, openPath.value.loc.start.line);
139
+ if (reportedLocs.style) {
140
+ report.style.push(reportedLocs.style);
141
+ }
142
+ report.violatingAttributes = [
143
+ ...report.violatingAttributes,
144
+ ...mapViolatingAttributesAndAdditionalProps(noneApprovedAttributes),
145
+ ];
146
+ }
147
+ },
148
+ });
149
+ }
150
+ }
151
+ },
152
+ });
153
+ return report;
154
+ };
155
+ exports.default = reportCustomProperties;
@@ -0,0 +1,21 @@
1
+ import * as recast from 'recast';
2
+ import type { CompoundMobileComponentName } from './types';
3
+ export type InlineStyleProps = 'style' | 'barStyle' | 'containerStyle' | 'textStyle';
4
+ export declare const INLINE_STYLE_PROPERTIES: string[];
5
+ export type ViolatingAttribute = {
6
+ attributeName: string;
7
+ attributeValue: string | null;
8
+ inlineStyleProps: InlineStyleProps;
9
+ componentName: CompoundMobileComponentName;
10
+ loc: number | undefined;
11
+ };
12
+ declare const reportInlineStyle: (ast: recast.types.ASTNode, attributes: recast.types.namedTypes.JSXAttribute[], componentName: CompoundMobileComponentName) => {
13
+ locs: {
14
+ style?: number | undefined;
15
+ barStyle?: number | undefined;
16
+ containerStyle?: number | undefined;
17
+ textStyle?: number | undefined;
18
+ };
19
+ violatingAttributes: ViolatingAttribute[];
20
+ };
21
+ export default reportInlineStyle;
@@ -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. <Card style={{ color: '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. <Card 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. <Card style={{ ...customStyle.tileCard }} />
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. <Card 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. <Card style={customStyle.tileCard} />
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;
@@ -0,0 +1,6 @@
1
+ import * as recast from 'recast';
2
+ import type { MobileComponentName } from './types';
3
+ declare const reportStyledComponents: (ast: recast.types.ASTNode, componentList: {
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
+ }, styledAliasName: string) => number[];
6
+ export default reportStyledComponents;
@@ -0,0 +1,95 @@
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
+ const recast = __importStar(require("recast"));
27
+ const reportStyledComponents = (ast, componentList, styledAliasName) => {
28
+ const locs = [];
29
+ const localComponentList = Object.keys(componentList);
30
+ recast.visit(ast, {
31
+ visitVariableDeclaration(path) {
32
+ this.traverse(path);
33
+ const declaration = path.value
34
+ .declarations[0];
35
+ if (declaration.init &&
36
+ declaration.init.type === 'TaggedTemplateExpression') {
37
+ const { tag } = declaration.init;
38
+ if (tag.type === 'CallExpression' &&
39
+ tag.callee.type === 'Identifier' &&
40
+ tag.callee.name === styledAliasName) {
41
+ const arg = tag.arguments[0];
42
+ // Case 1: Custom default component, e.g. styled(Card)
43
+ if (arg.type === 'Identifier' &&
44
+ localComponentList.includes(arg.name) &&
45
+ tag.loc) {
46
+ locs.push(tag.loc.start.line);
47
+ }
48
+ // Case 2: Custom compound component, e.g. styled(Card.Header)
49
+ if (arg.type === 'MemberExpression' &&
50
+ arg.object.type === 'Identifier' &&
51
+ localComponentList.includes(arg.object.name) &&
52
+ tag.loc) {
53
+ locs.push(tag.loc.start.line);
54
+ }
55
+ }
56
+ }
57
+ // Case 3: Custom compound component with spead operator. e.g. const { Header } = Card, then styled(Header)
58
+ if (declaration.init &&
59
+ declaration.init.type === 'Identifier' &&
60
+ localComponentList.includes(declaration.init.name)) {
61
+ const { id } = declaration;
62
+ if (id.type === 'ObjectPattern') {
63
+ const compoundComponentNames = id.properties
64
+ .map((prop) => prop.type === 'ObjectProperty' && prop.key.type === 'Identifier'
65
+ ? prop.key.name
66
+ : null)
67
+ .filter((name) => name !== null);
68
+ recast.visit(ast, {
69
+ visitVariableDeclaration(spreadPath) {
70
+ this.traverse(spreadPath);
71
+ const spreadDeclaration = spreadPath.value
72
+ .declarations[0];
73
+ if (spreadDeclaration.init &&
74
+ spreadDeclaration.init.type === 'TaggedTemplateExpression') {
75
+ const { tag } = spreadDeclaration.init;
76
+ if (tag.type === 'CallExpression' &&
77
+ tag.callee.type === 'Identifier' &&
78
+ tag.callee.name === styledAliasName) {
79
+ const arg = tag.arguments[0];
80
+ if (arg.type === 'Identifier' &&
81
+ compoundComponentNames.includes(arg.name) &&
82
+ tag.loc) {
83
+ locs.push(tag.loc.start.line);
84
+ }
85
+ }
86
+ }
87
+ },
88
+ });
89
+ }
90
+ }
91
+ },
92
+ });
93
+ return locs;
94
+ };
95
+ exports.default = reportStyledComponents;
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1 @@
1
+ export declare const parseTypeScript: (source: string) => any;
@@ -0,0 +1,32 @@
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.parseTypeScript = void 0;
27
+ const recast = __importStar(require("recast"));
28
+ const tsParser = __importStar(require("../../parsers/typescript"));
29
+ const parseTypeScript = (source) => {
30
+ return recast.parse(source, { parser: tsParser });
31
+ };
32
+ exports.parseTypeScript = parseTypeScript;
@@ -0,0 +1,3 @@
1
+ import { HD_MOBILE_COMPONENTS, MOBILE_RULESET_MAP } from './constants';
2
+ export type MobileComponentName = typeof HD_MOBILE_COMPONENTS[number];
3
+ export type CompoundMobileComponentName = keyof typeof MOBILE_RULESET_MAP;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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.3",
3
+ "version": "1.2.4-alpha.1",
4
4
  "description": "A hero-design bot detecting snowflake usage",
5
5
  "author": "Hau Dao",
6
6
  "license": "ISC",
@@ -34,7 +34,7 @@
34
34
  "@eslint/compat": "^1.1.1",
35
35
  "@eslint/eslintrc": "^3.1.0",
36
36
  "@types/jest": "^29.0.0",
37
- "@types/node": "^18.0.0",
37
+ "@types/node": "^20.14.8",
38
38
  "config-tsconfig": "8.42.4",
39
39
  "eslint": "^8.56.0",
40
40
  "eslint-config-hd": "8.42.4",