@hero-design/snowflake-guard 1.2.4-alpha.5 → 1.2.4

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,8 +10,6 @@
10
10
  cp .env.example .env
11
11
  ```
12
12
 
13
- For React Native projects, include your repo name in the `MOBILE_REPO_NAMES` env.
14
-
15
13
  2. Set up [internal-tool-integrations service](https://github.com/Thinkei/internal-tool-integrations) for the bot to store report.
16
14
 
17
15
  - Replace `DB_HOST` with the host of the internal-tool-integrations service.
package/lib/src/index.js CHANGED
@@ -11,14 +11,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
- const parseMobileSource_1 = __importDefault(require("./parseMobileSource"));
15
14
  const parseSource_1 = __importDefault(require("./parseSource"));
16
15
  const constants_1 = require("./reports/constants");
17
16
  const fetchGraphql_1 = __importDefault(require("./graphql/fetchGraphql"));
18
17
  const queryGenerators_1 = require("./graphql/queryGenerators");
19
18
  const getDiffLocs_1 = require("./utils/getDiffLocs");
20
19
  const TSX_REGEX = /\.tsx$/;
21
- const TSX_AND_JSX_REGEX = /\.(tsx|jsx|js)$/;
22
20
  const TEST_REGEX = /__tests__/;
23
21
  const SNOWFLAKE_COMMENTS = {
24
22
  style: 'Snowflake detected! A component is customized using inline styles. Make sure to not use [prohibited CSS properties](https://docs.google.com/spreadsheets/d/1Dj8vqLdFaf-CSaSVoYqyYZIkGqF6OoyP7K4G1_9L62U/edit?usp=sharing).',
@@ -26,7 +24,6 @@ const SNOWFLAKE_COMMENTS = {
26
24
  'styled-component': 'Please do not use styled-component to customize this component, use sx prop or inline style instead.',
27
25
  className: `Please make sure that this className is not used as a CSS classname for component customization purposes, use sx prop or inline style instead. In case this is none-css classname, please flag it with this comment \`${constants_1.APPROVED_CLASSNAME_COMMENT}\`.`,
28
26
  };
29
- const MOBILE_REPO_NAMES = JSON.parse(process.env.MOBILE_REPO_NAMES || '[]');
30
27
  const checkIfDetectedSnowflakesInDiff = (diffLocs, locToComment) => {
31
28
  const locIdx = diffLocs.findIndex(([start, end]) => {
32
29
  return locToComment >= start && locToComment <= end;
@@ -45,19 +42,16 @@ module.exports = (app) => {
45
42
  const prBranch = context.payload.pull_request.head.ref;
46
43
  // List all changed files
47
44
  const prFiles = yield context.octokit.pulls.listFiles(Object.assign(Object.assign({}, repoInfo), { pull_number: prNumber }));
48
- const isMobile = MOBILE_REPO_NAMES.includes(context.payload.repository.name);
49
- const parseSource = isMobile ? parseMobileSource_1.default : parseSource_1.default;
50
- const SOURCE_REGEX = isMobile ? TSX_AND_JSX_REGEX : TSX_REGEX;
51
- const sourceFiles = prFiles.data.filter((file) => SOURCE_REGEX.test(file.filename) &&
45
+ const tsxFiles = prFiles.data.filter((file) => TSX_REGEX.test(file.filename) &&
52
46
  !TEST_REGEX.test(file.filename) &&
53
47
  file.status !== 'removed');
54
48
  // Saving file patches to get diff locations
55
- const prFilePatches = sourceFiles.reduce((acc, file) => {
49
+ const prFilePatches = tsxFiles.reduce((acc, file) => {
56
50
  acc[file.filename] = file.patch || '';
57
51
  return acc;
58
52
  }, {});
59
53
  // Get file contents
60
- const prFileContentPromises = sourceFiles.map((file) => context.octokit.repos.getContent(Object.assign(Object.assign({}, repoInfo), { path: file.filename, ref: prBranch })));
54
+ const prFileContentPromises = tsxFiles.map((file) => context.octokit.repos.getContent(Object.assign(Object.assign({}, repoInfo), { path: file.filename, ref: prBranch })));
61
55
  const prFileContents = yield Promise.all(prFileContentPromises);
62
56
  const snowflakeComments = [];
63
57
  const approvedSnowflakeLocs = [];
@@ -71,7 +65,7 @@ module.exports = (app) => {
71
65
  // @ts-ignore
72
66
  file.data.content, 'base64').toString();
73
67
  // Parse file content to check for snowflakes
74
- const snowflakeReport = parseSource(stringContent);
68
+ const snowflakeReport = (0, parseSource_1.default)(stringContent);
75
69
  snowflakeReport.styleLocs.forEach((loc) => {
76
70
  if (checkIfDetectedSnowflakesInDiff(diffLocs, loc)) {
77
71
  snowflakeComments.push({
@@ -81,6 +75,15 @@ module.exports = (app) => {
81
75
  });
82
76
  }
83
77
  });
78
+ snowflakeReport.sxLocs.forEach((loc) => {
79
+ if (checkIfDetectedSnowflakesInDiff(diffLocs, loc)) {
80
+ snowflakeComments.push({
81
+ path: filePath,
82
+ body: SNOWFLAKE_COMMENTS['sx'],
83
+ line: loc,
84
+ });
85
+ }
86
+ });
84
87
  snowflakeReport.styledComponentLocs.forEach((loc) => {
85
88
  if (checkIfDetectedSnowflakesInDiff(diffLocs, loc)) {
86
89
  snowflakeComments.push({
@@ -90,31 +93,20 @@ module.exports = (app) => {
90
93
  });
91
94
  }
92
95
  });
96
+ snowflakeReport.classNameLocs.forEach((loc) => {
97
+ if (checkIfDetectedSnowflakesInDiff(diffLocs, loc)) {
98
+ snowflakeComments.push({
99
+ path: filePath,
100
+ body: SNOWFLAKE_COMMENTS['className'],
101
+ line: loc,
102
+ });
103
+ }
104
+ });
93
105
  snowflakeReport.approvedLocs.forEach((loc) => {
94
106
  if (checkIfDetectedSnowflakesInDiff(diffLocs, loc)) {
95
107
  approvedSnowflakeLocs.push(loc);
96
108
  }
97
109
  });
98
- if (!isMobile) {
99
- snowflakeReport.sxLocs.forEach((loc) => {
100
- if (checkIfDetectedSnowflakesInDiff(diffLocs, loc)) {
101
- snowflakeComments.push({
102
- path: filePath,
103
- body: SNOWFLAKE_COMMENTS['sx'],
104
- line: loc,
105
- });
106
- }
107
- });
108
- snowflakeReport.classNameLocs.forEach((loc) => {
109
- if (checkIfDetectedSnowflakesInDiff(diffLocs, loc)) {
110
- snowflakeComments.push({
111
- path: filePath,
112
- body: SNOWFLAKE_COMMENTS['className'],
113
- line: loc,
114
- });
115
- }
116
- });
117
- }
118
110
  }));
119
111
  // Saving report
120
112
  const snowflakeCount = snowflakeComments.length;
@@ -82,71 +82,4 @@ declare const MOBILE_RULESET_MAP: {
82
82
  'Typography.Caption': string[];
83
83
  'Typography.Label': string[];
84
84
  };
85
- declare const BAR_STYLE_RULESET_MAP: {
86
- Tabs: string[];
87
- 'Tabs.Scroll': string[];
88
- };
89
- declare const CONTAINER_STYLE_RULESET_MAP: {
90
- Tabs: string[];
91
- 'Tabs.Scroll': string[];
92
- };
93
- declare const TEXT_STYLE_RULESET_MAP: {
94
- 'Toolbar.Message': string[];
95
- TextInput: string[];
96
- 'Search.OneLine': {
97
- [x: number]: string;
98
- length: number;
99
- toString(): string;
100
- toLocaleString(): string;
101
- pop(): string | undefined;
102
- push(...items: string[]): number;
103
- concat(...items: ConcatArray<string>[]): string[];
104
- concat(...items: (string | ConcatArray<string>)[]): string[];
105
- join(separator?: string | undefined): string;
106
- reverse(): string[];
107
- shift(): string | undefined;
108
- slice(start?: number | undefined, end?: number | undefined): string[];
109
- sort(compareFn?: ((a: string, b: string) => number) | undefined): string[];
110
- splice(start: number, deleteCount?: number | undefined): string[];
111
- splice(start: number, deleteCount: number, ...items: string[]): string[];
112
- unshift(...items: string[]): number;
113
- indexOf(searchElement: string, fromIndex?: number | undefined): number;
114
- lastIndexOf(searchElement: string, fromIndex?: number | undefined): number;
115
- every<S extends string>(predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): this is S[];
116
- every(predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): boolean;
117
- some(predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): boolean;
118
- forEach(callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any): void;
119
- map<U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any): U[];
120
- filter<S_1 extends string>(predicate: (value: string, index: number, array: string[]) => value is S_1, thisArg?: any): S_1[];
121
- filter(predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): string[];
122
- reduce(callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string;
123
- reduce(callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string;
124
- reduce<U_1>(callbackfn: (previousValue: U_1, currentValue: string, currentIndex: number, array: string[]) => U_1, initialValue: U_1): U_1;
125
- reduceRight(callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string;
126
- reduceRight(callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string;
127
- reduceRight<U_2>(callbackfn: (previousValue: U_2, currentValue: string, currentIndex: number, array: string[]) => U_2, initialValue: U_2): U_2;
128
- find<S_2 extends string>(predicate: (this: void, value: string, index: number, obj: string[]) => value is S_2, thisArg?: any): S_2 | undefined;
129
- find(predicate: (value: string, index: number, obj: string[]) => unknown, thisArg?: any): string | undefined;
130
- findIndex(predicate: (value: string, index: number, obj: string[]) => unknown, thisArg?: any): number;
131
- fill(value: string, start?: number | undefined, end?: number | undefined): string[];
132
- copyWithin(target: number, start: number, end?: number | undefined): string[];
133
- entries(): IterableIterator<[number, string]>;
134
- keys(): IterableIterator<number>;
135
- values(): IterableIterator<string>;
136
- includes(searchElement: string, fromIndex?: number | undefined): boolean;
137
- flatMap<U_3, This = undefined>(callback: (this: This, value: string, index: number, array: string[]) => U_3 | readonly U_3[], thisArg?: This | undefined): U_3[];
138
- flat<A, D extends number = 1>(this: A, depth?: D | undefined): FlatArray<A, D>[];
139
- [Symbol.iterator](): IterableIterator<string>;
140
- [Symbol.unscopables](): {
141
- copyWithin: boolean;
142
- entries: boolean;
143
- fill: boolean;
144
- find: boolean;
145
- findIndex: boolean;
146
- keys: boolean;
147
- values: boolean;
148
- };
149
- at(index: number): string | undefined;
150
- };
151
- };
152
- export { HD_MOBILE_COMPONENTS, MOBILE_RULESET_MAP, BAR_STYLE_RULESET_MAP, CONTAINER_STYLE_RULESET_MAP, TEXT_STYLE_RULESET_MAP, };
85
+ export { HD_MOBILE_COMPONENTS, MOBILE_RULESET_MAP };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TEXT_STYLE_RULESET_MAP = exports.CONTAINER_STYLE_RULESET_MAP = exports.BAR_STYLE_RULESET_MAP = exports.MOBILE_RULESET_MAP = exports.HD_MOBILE_COMPONENTS = void 0;
3
+ exports.MOBILE_RULESET_MAP = exports.HD_MOBILE_COMPONENTS = void 0;
4
4
  const PADDING_ATTRS = [
5
5
  'padding',
6
6
  'paddingBottom',
@@ -347,94 +347,42 @@ const MOBILE_RULESET_MAP = {
347
347
  ...HEIGHT_ATTRS,
348
348
  ],
349
349
  'Toast.Container': [
350
- ...COMMON_PROHIBITED_ATTRS,
351
- ...WIDTH_ATTRS,
352
- ...HEIGHT_ATTRS,
353
- ...MARGIN_ATTRS,
354
- ],
355
- 'Toast.Provider': [
356
- ...COMMON_PROHIBITED_ATTRS,
357
- ...WIDTH_ATTRS,
358
- ...HEIGHT_ATTRS,
359
- ...MARGIN_ATTRS,
360
- ],
361
- 'Toolbar.Group': [
362
- ...COMMON_PROHIBITED_ATTRS,
363
- ...WIDTH_ATTRS,
364
- ...HEIGHT_ATTRS,
365
- ...MARGIN_ATTRS,
366
- ],
367
- 'Toolbar.Item': [
368
- ...COMMON_PROHIBITED_ATTRS,
369
- ...WIDTH_ATTRS,
370
- ...HEIGHT_ATTRS,
371
- ...MARGIN_ATTRS,
372
- ],
373
- 'Toolbar.Message': [
374
- ...COMMON_PROHIBITED_ATTRS,
375
- ...WIDTH_ATTRS,
376
- ...HEIGHT_ATTRS,
377
- ...MARGIN_ATTRS,
378
- ],
379
- 'Typography.Body': [...COMMON_PROHIBITED_ATTRS, ...HEIGHT_ATTRS],
380
- 'Typography.Caption': [...COMMON_PROHIBITED_ATTRS, ...HEIGHT_ATTRS],
381
- 'Typography.Label': [...COMMON_PROHIBITED_ATTRS, ...HEIGHT_ATTRS],
382
- };
383
- exports.MOBILE_RULESET_MAP = MOBILE_RULESET_MAP;
384
- const BAR_STYLE_RULESET_MAP = {
385
- Tabs: [
386
350
  ...PADDING_ATTRS,
387
351
  ...BORDER_ATTRS,
388
- ...TEXT_ATTRS,
389
352
  ...SHADOW_ATTRS,
390
- ...WIDTH_ATTRS,
353
+ ...TEXT_ATTRS,
391
354
  ...HEIGHT_ATTRS,
392
- ...MARGIN_ATTRS,
393
355
  ],
394
- 'Tabs.Scroll': [
356
+ 'Toast.Provider': [
395
357
  ...PADDING_ATTRS,
396
358
  ...BORDER_ATTRS,
397
- ...TEXT_ATTRS,
398
359
  ...SHADOW_ATTRS,
399
- ...WIDTH_ATTRS,
360
+ ...TEXT_ATTRS,
400
361
  ...HEIGHT_ATTRS,
401
- ...MARGIN_ATTRS,
402
362
  ],
403
- };
404
- exports.BAR_STYLE_RULESET_MAP = BAR_STYLE_RULESET_MAP;
405
- const CONTAINER_STYLE_RULESET_MAP = {
406
- Tabs: [
363
+ 'Toolbar.Group': [
364
+ ...PADDING_ATTRS,
407
365
  ...BORDER_ATTRS,
408
- ...TEXT_ATTRS,
409
366
  ...SHADOW_ATTRS,
410
- ...WIDTH_ATTRS,
367
+ ...TEXT_ATTRS,
411
368
  ...HEIGHT_ATTRS,
412
- ...MARGIN_ATTRS,
413
369
  ],
414
- 'Tabs.Scroll': [
370
+ 'Toolbar.Item': [
371
+ ...PADDING_ATTRS,
415
372
  ...BORDER_ATTRS,
416
- ...TEXT_ATTRS,
417
373
  ...SHADOW_ATTRS,
418
- ...WIDTH_ATTRS,
374
+ ...TEXT_ATTRS,
419
375
  ...HEIGHT_ATTRS,
420
- ...MARGIN_ATTRS,
421
376
  ],
422
- };
423
- exports.CONTAINER_STYLE_RULESET_MAP = CONTAINER_STYLE_RULESET_MAP;
424
- const TEXT_STYLE_RULESET_MAP = {
425
377
  'Toolbar.Message': [
426
- ...COMMON_PROHIBITED_ATTRS,
427
- ...WIDTH_ATTRS,
428
- ...HEIGHT_ATTRS,
429
- ...MARGIN_ATTRS,
430
- ],
431
- TextInput: [
432
378
  ...PADDING_ATTRS,
433
379
  ...BORDER_ATTRS,
434
380
  ...SHADOW_ATTRS,
435
381
  ...TEXT_ATTRS,
436
382
  ...HEIGHT_ATTRS,
437
383
  ],
438
- 'Search.OneLine': Object.assign(Object.assign(Object.assign(Object.assign({}, COMMON_PROHIBITED_ATTRS), WIDTH_ATTRS), HEIGHT_ATTRS), MARGIN_ATTRS),
384
+ 'Typography.Body': [...COMMON_PROHIBITED_ATTRS, ...HEIGHT_ATTRS],
385
+ 'Typography.Caption': [...COMMON_PROHIBITED_ATTRS, ...HEIGHT_ATTRS],
386
+ 'Typography.Label': [...COMMON_PROHIBITED_ATTRS, ...HEIGHT_ATTRS],
439
387
  };
440
- exports.TEXT_STYLE_RULESET_MAP = TEXT_STYLE_RULESET_MAP;
388
+ exports.MOBILE_RULESET_MAP = MOBILE_RULESET_MAP;
@@ -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]: "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";
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";
5
5
  }, styledAliasName: string) => number[];
6
6
  export default reportStyledComponents;
@@ -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" | "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
+ [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";
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" | "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";
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hero-design/snowflake-guard",
3
- "version": "1.2.4-alpha.5",
3
+ "version": "1.2.4",
4
4
  "description": "A hero-design bot detecting snowflake usage",
5
5
  "author": "Hau Dao",
6
6
  "license": "ISC",
@@ -1 +0,0 @@
1
- export {};
@@ -1,43 +0,0 @@
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 fs = __importStar(require("fs"));
30
- const parseMobileSource_1 = __importDefault(require("../parseMobileSource"));
31
- describe('parseSource', () => {
32
- it('reports correct snowflakes', () => {
33
- const source = fs.readFileSync('./src/__mocks__/mobileSourceSample.tsx', 'utf-8');
34
- expect((0, parseMobileSource_1.default)(source)).toEqual({
35
- approvedLocs: [],
36
- classNameLocs: [],
37
- styleLocs: [],
38
- styledComponentLocs: [],
39
- sxLocs: [],
40
- violatingAttributes: [],
41
- });
42
- });
43
- });
@@ -1,15 +0,0 @@
1
- import { InlineStyleProps } from './reports/mobile/reportInlineStyle';
2
- import type { CompoundMobileComponentName } from './reports/mobile/types';
3
- declare const parseMobileSource: (source: string) => {
4
- styleLocs: number[];
5
- styledComponentLocs: number[];
6
- approvedLocs: number[];
7
- violatingAttributes: {
8
- attributeName: string;
9
- attributeValue: string | null;
10
- inlineStyleProps: InlineStyleProps;
11
- componentName: CompoundMobileComponentName;
12
- loc: number | undefined;
13
- }[];
14
- };
15
- export default parseMobileSource;
@@ -1,118 +0,0 @@
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 flowParser = __importStar(require("./parsers/flow"));
31
- const tsParser = __importStar(require("./parsers/typescript"));
32
- const constants_1 = require("./reports/mobile/constants");
33
- const constants_2 = require("./reports/constants");
34
- const reportCustomStyleProperties_1 = __importDefault(require("./reports/mobile/reportCustomStyleProperties"));
35
- const reportStyledComponents_1 = __importDefault(require("./reports/mobile/reportStyledComponents"));
36
- const parseMobileSource = (source) => {
37
- let hasHeroDesignImport = false;
38
- let hasStyledComponentsImport = false;
39
- const componentList = {};
40
- let styledAliasName = 'styled';
41
- let styledComponentLocs = [];
42
- let styleLocs = [];
43
- let violatingAttributes = [];
44
- const approvedInlineStyleCmts = [];
45
- const approvedStyledComponentLocs = [];
46
- const ast = recast.parse(source, {
47
- parser: source.includes('@flow') ? flowParser : tsParser,
48
- });
49
- recast.visit(ast, {
50
- visitImportDeclaration(path) {
51
- this.traverse(path);
52
- const importedFrom = path.value.source.value;
53
- // Check if file imports components from '@hero-design/rn'
54
- if (importedFrom === '@hero-design/rn') {
55
- recast.visit(path.node, {
56
- visitImportSpecifier(importPath) {
57
- this.traverse(importPath);
58
- if (constants_1.HD_MOBILE_COMPONENTS.includes(importPath.value.imported.name)) {
59
- componentList[importPath.value.local.name] =
60
- importPath.value.imported.name;
61
- hasHeroDesignImport = true;
62
- }
63
- },
64
- });
65
- }
66
- // Check if file imports from '@emotion/native'
67
- if (importedFrom === '@emotion/native') {
68
- recast.visit(path.node, {
69
- visitImportDefaultSpecifier(importPath) {
70
- this.traverse(importPath);
71
- styledAliasName = importPath.value.local.name;
72
- hasStyledComponentsImport = true;
73
- },
74
- });
75
- }
76
- },
77
- visitComment(path) {
78
- this.traverse(path);
79
- const comment = path.value.value;
80
- if (comment
81
- .toLowerCase()
82
- .includes(constants_2.APPROVED_INLINE_STYLE_COMMENT.toLowerCase())) {
83
- approvedInlineStyleCmts.push({
84
- loc: path.value.loc.start.line,
85
- comment,
86
- });
87
- }
88
- if (comment
89
- .toLowerCase()
90
- .includes(constants_2.APPROVED_STYLED_COMPONENTS_COMMENT.toLowerCase())) {
91
- approvedStyledComponentLocs.push(path.value.loc.start.line);
92
- }
93
- },
94
- });
95
- const isNotApprovedStyledComponentSnowflakes = (loc) => !approvedStyledComponentLocs.includes(loc - 1);
96
- if (hasHeroDesignImport) {
97
- // Case 1: Using style object to customise components
98
- const customPropLocs = (0, reportCustomStyleProperties_1.default)(ast, componentList, {
99
- styleCmts: approvedInlineStyleCmts,
100
- });
101
- styleLocs = customPropLocs.style;
102
- // Case 2: Using styled-components to customise components
103
- if (hasStyledComponentsImport) {
104
- styledComponentLocs = (0, reportStyledComponents_1.default)(ast, componentList, styledAliasName).filter(isNotApprovedStyledComponentSnowflakes);
105
- }
106
- violatingAttributes = customPropLocs.violatingAttributes;
107
- }
108
- return {
109
- styleLocs,
110
- styledComponentLocs,
111
- approvedLocs: [
112
- ...approvedInlineStyleCmts.map((cmt) => cmt.loc),
113
- ...approvedStyledComponentLocs,
114
- ],
115
- violatingAttributes,
116
- };
117
- };
118
- exports.default = parseMobileSource;
@@ -1,3 +0,0 @@
1
- import * as babelParser from '@babel/parser';
2
- import { Overrides } from 'recast/parsers/_babel_options';
3
- export declare const parse: (source: string, options?: Overrides) => babelParser.ParseResult<import("@babel/types").File>;
@@ -1,37 +0,0 @@
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.parse = void 0;
30
- const babelParser = __importStar(require("@babel/parser"));
31
- const _babel_options_1 = __importDefault(require("recast/parsers/_babel_options"));
32
- const parse = (source, options) => {
33
- const babelOptions = (0, _babel_options_1.default)(options);
34
- babelOptions.plugins.push('jsx', 'flow');
35
- return babelParser.parse(source, babelOptions);
36
- };
37
- exports.parse = parse;
@@ -1,123 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const reportCustomStyleProperties_1 = __importDefault(require("../reportCustomStyleProperties"));
7
- const testUtils_1 = require("../testUtils");
8
- const componentList = {
9
- Card: 'Card',
10
- Button: 'Button',
11
- };
12
- const commentList = {
13
- styleCmts: [],
14
- };
15
- describe('reportCustomStyleProperties', () => {
16
- beforeEach(() => {
17
- jest.clearAllMocks();
18
- });
19
- it('should detect component with inline styles', () => {
20
- const source = `const styles = {
21
- card: {
22
- padding: 10,
23
- },
24
- };
25
-
26
- const App = () => (
27
- <Card style={styles.card}>Content</Card>
28
- );
29
- `;
30
- const ast = (0, testUtils_1.parseTypeScript)(source);
31
- const result = (0, reportCustomStyleProperties_1.default)(ast, componentList, commentList);
32
- expect(result).toEqual({
33
- style: [8],
34
- violatingAttributes: [
35
- {
36
- attributeName: 'padding',
37
- attributeValue: '10',
38
- componentName: 'Card',
39
- inlineStyleProps: 'style',
40
- loc: 8,
41
- },
42
- ],
43
- });
44
- });
45
- it('should detect compound component with inline styles', () => {
46
- const source = `const App = () => (
47
- <Button.Icon style={{ padding: 10 }} />
48
- );
49
- `;
50
- const ast = (0, testUtils_1.parseTypeScript)(source);
51
- const result = (0, reportCustomStyleProperties_1.default)(ast, componentList, commentList);
52
- expect(result).toEqual({
53
- style: [2],
54
- violatingAttributes: [
55
- {
56
- attributeName: 'padding',
57
- attributeValue: '10',
58
- componentName: 'Button.Icon',
59
- inlineStyleProps: 'style',
60
- loc: 2,
61
- },
62
- ],
63
- });
64
- });
65
- it('should detect compound component using spread operator with inline styles', () => {
66
- const source = `const { Icon } = Button;
67
- const App = () => (
68
- <Icon style={{ padding: 10 }} />
69
- );
70
- `;
71
- const ast = (0, testUtils_1.parseTypeScript)(source);
72
- const result = (0, reportCustomStyleProperties_1.default)(ast, componentList, commentList);
73
- expect(result).toEqual({
74
- style: [3],
75
- violatingAttributes: [
76
- {
77
- attributeName: 'padding',
78
- attributeValue: '10',
79
- componentName: 'Button.Icon',
80
- inlineStyleProps: 'style',
81
- loc: 3,
82
- },
83
- ],
84
- });
85
- });
86
- it('should not detect non-custom style properties', () => {
87
- const source = `
88
- const App = () => (
89
- <Card>Content</Card>
90
- );
91
- `;
92
- const ast = (0, testUtils_1.parseTypeScript)(source);
93
- const result = (0, reportCustomStyleProperties_1.default)(ast, componentList, commentList);
94
- expect(result).toEqual({ style: [], violatingAttributes: [] });
95
- });
96
- it('should ignore approved inline styles', () => {
97
- const mockedCommentList = {
98
- styleCmts: [
99
- {
100
- loc: 10,
101
- comment: '@snowflake-guard/approved-inline-style attributes: padding',
102
- },
103
- ],
104
- };
105
- const source = `
106
- const styles = {
107
- button: {
108
- padding: 10,
109
- },
110
- };
111
-
112
- const App = () => (
113
- <>
114
- {/* @snowflake-guard/approved-inline-style attributes: padding */}
115
- <Button style={styles.button} />
116
- </>
117
- );
118
- `;
119
- const ast = (0, testUtils_1.parseTypeScript)(source);
120
- const result = (0, reportCustomStyleProperties_1.default)(ast, componentList, mockedCommentList);
121
- expect(result).toEqual({ style: [], violatingAttributes: [] });
122
- });
123
- });
@@ -1,103 +0,0 @@
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
- />
42
- );
43
- `;
44
- // Step 1: Parse the code to generate the AST
45
- const ast = recast.parse(code, { parser: tsParser });
46
- const attributes = [];
47
- // Step 2: Traverse the AST to extract relevant JSXAttributes (style)
48
- recast.visit(ast, {
49
- visitJSXAttribute(path) {
50
- const { node } = path;
51
- attributes.push(node);
52
- this.traverse(path);
53
- },
54
- });
55
- const componentName = 'Card';
56
- // Mock the RULESET_MAP for this test
57
- constants_1.MOBILE_RULESET_MAP.Card = ['color', 'backgroundColor'];
58
- // Step 3: Run the function with the dynamically extracted attributes
59
- const result = (0, reportInlineStyle_1.default)(ast, attributes, componentName);
60
- expect(result.locs.style).toEqual(3);
61
- expect(result.violatingAttributes).toEqual([
62
- {
63
- attributeName: 'color',
64
- attributeValue: "'red'",
65
- componentName: 'Card',
66
- inlineStyleProps: 'style',
67
- loc: 3,
68
- },
69
- {
70
- attributeName: 'backgroundColor',
71
- attributeValue: "'blue'",
72
- componentName: 'Card',
73
- inlineStyleProps: 'style',
74
- loc: 3,
75
- },
76
- ]);
77
- });
78
- it('should return empty arrays when no violations are found', () => {
79
- const code = `
80
- const MyComponent = () => (
81
- <Card style={{ margin: 10 }} />
82
- );
83
- `;
84
- // Step 1: Parse the code to generate the AST
85
- const ast = recast.parse(code, { parser: tsParser });
86
- const attributes = [];
87
- // Step 2: Traverse the AST to extract relevant JSXAttributes (style)
88
- recast.visit(ast, {
89
- visitJSXAttribute(path) {
90
- const { node } = path;
91
- attributes.push(node);
92
- this.traverse(path);
93
- },
94
- });
95
- const componentName = 'Card';
96
- // Mock the RULESET_MAP and SX_RULESET_MAP for this test
97
- constants_1.MOBILE_RULESET_MAP.Card = ['color'];
98
- const result = (0, reportInlineStyle_1.default)(ast, attributes, componentName);
99
- expect(result.locs.style).toEqual(undefined);
100
- expect(result.violatingAttributes).toEqual([]);
101
- expect(result.violatingAttributes).toEqual([]);
102
- });
103
- });
@@ -1,38 +0,0 @@
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;
@@ -1,155 +0,0 @@
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;
@@ -1,21 +0,0 @@
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;
@@ -1,214 +0,0 @@
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;