@hero-design/snowflake-guard 1.0.7-alpha0
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/.dockerignore +12 -0
- package/.env.example +9 -0
- package/.eslintrc.js +8 -0
- package/CHANGELOG.md +47 -0
- package/Dockerfile +8 -0
- package/LICENSE +15 -0
- package/README.md +33 -0
- package/app.yml +137 -0
- package/jest.config.js +9 -0
- package/lib/netlify/functions/snowflake.d.ts +2 -0
- package/lib/netlify/functions/snowflake.js +10 -0
- package/lib/src/__mocks__/sourceSample.d.ts +2 -0
- package/lib/src/__mocks__/sourceSample.js +27 -0
- package/lib/src/__tests__/parseSource.spec.d.ts +1 -0
- package/lib/src/__tests__/parseSource.spec.js +41 -0
- package/lib/src/index.d.ts +3 -0
- package/lib/src/index.js +142 -0
- package/lib/src/parseSource.d.ts +7 -0
- package/lib/src/parseSource.js +102 -0
- package/lib/src/parsers/typescript.d.ts +3 -0
- package/lib/src/parsers/typescript.js +37 -0
- package/lib/src/reports/constants.d.ts +215 -0
- package/lib/src/reports/constants.js +848 -0
- package/lib/src/reports/reportClassName.d.ts +3 -0
- package/lib/src/reports/reportClassName.js +15 -0
- package/lib/src/reports/reportCustomStyleProperties.d.ts +10 -0
- package/lib/src/reports/reportCustomStyleProperties.js +109 -0
- package/lib/src/reports/reportInlineStyle.d.ts +7 -0
- package/lib/src/reports/reportInlineStyle.js +179 -0
- package/lib/src/reports/reportStyledComponents.d.ts +6 -0
- package/lib/src/reports/reportStyledComponents.js +95 -0
- package/lib/src/reports/types.d.ts +3 -0
- package/lib/src/reports/types.js +2 -0
- package/lib/src/test.tsx +123 -0
- package/netlify/functions/snowflake.ts +9 -0
- package/netlify.toml +21 -0
- package/package.json +44 -0
- package/src/__mocks__/sourceSample.tsx +67 -0
- package/src/__tests__/parseSource.spec.ts +15 -0
- package/src/index.ts +201 -0
- package/src/parseSource.ts +97 -0
- package/src/parsers/typescript.ts +8 -0
- package/src/reports/constants.ts +965 -0
- package/src/reports/reportClassName.ts +20 -0
- package/src/reports/reportCustomStyleProperties.ts +125 -0
- package/src/reports/reportInlineStyle.ts +221 -0
- package/src/reports/reportStyledComponents.ts +109 -0
- package/src/reports/types.ts +5 -0
- package/tsconfig.json +15 -0
package/.dockerignore
ADDED
package/.env.example
ADDED
package/.eslintrc.js
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @hero-design/snowflake-guard
|
|
2
|
+
|
|
3
|
+
## 1.0.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#3126](https://github.com/Thinkei/hero-design/pull/3126) [`11e08d8c2`](https://github.com/Thinkei/hero-design/commit/11e08d8c244d0e7bf99e4ccb5a17aadf575bdda8) Thanks [@haudao-eh](https://github.com/haudao-eh)! - [ANG-2671] Allow to bypass snowflakes by comments
|
|
8
|
+
|
|
9
|
+
## 1.0.6
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#3103](https://github.com/Thinkei/hero-design/pull/3103) [`97bfc5144`](https://github.com/Thinkei/hero-design/commit/97bfc514495d50b7d36d0186764744b8244a9166) Thanks [@haudao-eh](https://github.com/haudao-eh)! - Fix getting wrong component name
|
|
14
|
+
|
|
15
|
+
## 1.0.5
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- [#3091](https://github.com/Thinkei/hero-design/pull/3091) [`fbb723675`](https://github.com/Thinkei/hero-design/commit/fbb7236755deb2b2b0bfdbe89154d727bc8f5d0e) Thanks [@haudao-eh](https://github.com/haudao-eh)! - Report snowflakes based on components' individual rulset
|
|
20
|
+
|
|
21
|
+
- [#3078](https://github.com/Thinkei/hero-design/pull/3078) [`8d69517f1`](https://github.com/Thinkei/hero-design/commit/8d69517f16b249d6c3582af826b5ba6df30e1183) Thanks [@haudao-eh](https://github.com/haudao-eh)! - Remove frontend scripts
|
|
22
|
+
|
|
23
|
+
- [#3095](https://github.com/Thinkei/hero-design/pull/3095) [`8a94a5213`](https://github.com/Thinkei/hero-design/commit/8a94a5213126aa154bf7d77c65abf650bab1349c) Thanks [@haudao-eh](https://github.com/haudao-eh)! - Update bot review behaviour
|
|
24
|
+
|
|
25
|
+
## 1.0.4
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- [#3070](https://github.com/Thinkei/hero-design/pull/3070) [`ca5b8f588`](https://github.com/Thinkei/hero-design/commit/ca5b8f5884de2e7c7445d032d4e34092883dd46c) Thanks [@haudao-eh](https://github.com/haudao-eh)! - [ANG-2641] Migrate snowflake script from frontend-script
|
|
30
|
+
|
|
31
|
+
## 1.0.3
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- [#2838](https://github.com/Thinkei/hero-design/pull/2838) [`b8ea41d92`](https://github.com/Thinkei/hero-design/commit/b8ea41d924f8b62d7fd4ff9a2418b27877ca8bda) Thanks [@haudao-eh](https://github.com/haudao-eh)! - Fix comment on wrong lines
|
|
36
|
+
|
|
37
|
+
## 1.0.2
|
|
38
|
+
|
|
39
|
+
### Patch Changes
|
|
40
|
+
|
|
41
|
+
- [#2625](https://github.com/Thinkei/hero-design/pull/2625) [`4bb963963`](https://github.com/Thinkei/hero-design/commit/4bb963963d053604d3cb055b239174abab441271) Thanks [@haudao-eh](https://github.com/haudao-eh)! - Stop fetching removed files.
|
|
42
|
+
|
|
43
|
+
## 1.0.1
|
|
44
|
+
|
|
45
|
+
### Patch Changes
|
|
46
|
+
|
|
47
|
+
- [#2530](https://github.com/Thinkei/hero-design/pull/2530) [`ec37290b4`](https://github.com/Thinkei/hero-design/commit/ec37290b4ea1d09f9034b024bf731c8dcdded7a7) Thanks [@haudao-eh](https://github.com/haudao-eh)! - [Snowflake Guard] Allow to comment on file when detected line is not part of the diff
|
package/Dockerfile
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023, Hau Dao
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# @hero-design/snowflake-guard
|
|
2
|
+
|
|
3
|
+
> A GitHub App built with [Probot](https://github.com/probot/probot) that A hero-design bot detecting snowflake usage
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
# Install dependencies
|
|
9
|
+
npm install
|
|
10
|
+
|
|
11
|
+
# Run the bot
|
|
12
|
+
npm start
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Docker
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
# 1. Build container
|
|
19
|
+
docker build -t @hero-design/snowflake-guard .
|
|
20
|
+
|
|
21
|
+
# 2. Start container
|
|
22
|
+
docker run -e APP_ID=<app-id> -e PRIVATE_KEY=<pem-value> @hero-design/snowflake-guard
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Contributing
|
|
26
|
+
|
|
27
|
+
If you have suggestions for how @hero-design/snowflake-guard could be improved, or want to report a bug, open an issue! We'd love all and any contributions.
|
|
28
|
+
|
|
29
|
+
For more, check out the [Contributing Guide](CONTRIBUTING.md).
|
|
30
|
+
|
|
31
|
+
## License
|
|
32
|
+
|
|
33
|
+
[ISC](LICENSE) © 2023 Hau Dao
|
package/app.yml
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# This is a GitHub App Manifest. These settings will be used by default when
|
|
2
|
+
# initially configuring your GitHub App.
|
|
3
|
+
#
|
|
4
|
+
# NOTE: changing this file will not update your GitHub App settings.
|
|
5
|
+
# You must visit github.com/settings/apps/your-app-name to edit them.
|
|
6
|
+
#
|
|
7
|
+
# Read more about configuring your GitHub App:
|
|
8
|
+
# https://probot.github.io/docs/development/#configuring-a-github-app
|
|
9
|
+
#
|
|
10
|
+
# Read more about GitHub App Manifests:
|
|
11
|
+
# https://developer.github.com/apps/building-github-apps/creating-github-apps-from-a-manifest/
|
|
12
|
+
|
|
13
|
+
# The list of events the GitHub App subscribes to.
|
|
14
|
+
# Uncomment the event names below to enable them.
|
|
15
|
+
default_events:
|
|
16
|
+
# - check_run
|
|
17
|
+
# - check_suite
|
|
18
|
+
# - commit_comment
|
|
19
|
+
# - create
|
|
20
|
+
# - delete
|
|
21
|
+
# - deployment
|
|
22
|
+
# - deployment_status
|
|
23
|
+
# - fork
|
|
24
|
+
# - gollum
|
|
25
|
+
# - issue_comment
|
|
26
|
+
# - issues
|
|
27
|
+
# - label
|
|
28
|
+
# - milestone
|
|
29
|
+
# - member
|
|
30
|
+
# - membership
|
|
31
|
+
# - org_block
|
|
32
|
+
# - organization
|
|
33
|
+
# - page_build
|
|
34
|
+
# - project
|
|
35
|
+
# - project_card
|
|
36
|
+
# - project_column
|
|
37
|
+
# - public
|
|
38
|
+
- pull_request
|
|
39
|
+
# - pull_request_review
|
|
40
|
+
# - pull_request_review_comment
|
|
41
|
+
# - push
|
|
42
|
+
# - release
|
|
43
|
+
# - repository
|
|
44
|
+
# - repository_import
|
|
45
|
+
# - status
|
|
46
|
+
# - team
|
|
47
|
+
# - team_add
|
|
48
|
+
# - watch
|
|
49
|
+
|
|
50
|
+
# The set of permissions needed by the GitHub App. The format of the object uses
|
|
51
|
+
# the permission name for the key (for example, issues) and the access type for
|
|
52
|
+
# the value (for example, write).
|
|
53
|
+
# Valid values are `read`, `write`, and `none`
|
|
54
|
+
default_permissions:
|
|
55
|
+
# Repository creation, deletion, settings, teams, and collaborators.
|
|
56
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-administration
|
|
57
|
+
# administration: read
|
|
58
|
+
|
|
59
|
+
# Checks on code.
|
|
60
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-checks
|
|
61
|
+
# checks: read
|
|
62
|
+
|
|
63
|
+
# Repository contents, commits, branches, downloads, releases, and merges.
|
|
64
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-contents
|
|
65
|
+
contents: read
|
|
66
|
+
|
|
67
|
+
# Deployments and deployment statuses.
|
|
68
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-deployments
|
|
69
|
+
# deployments: read
|
|
70
|
+
|
|
71
|
+
# Issues and related comments, assignees, labels, and milestones.
|
|
72
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-issues
|
|
73
|
+
# issues: write
|
|
74
|
+
|
|
75
|
+
# Search repositories, list collaborators, and access repository metadata.
|
|
76
|
+
# https://developer.github.com/v3/apps/permissions/#metadata-permissions
|
|
77
|
+
metadata: read
|
|
78
|
+
|
|
79
|
+
# Retrieve Pages statuses, configuration, and builds, as well as create new builds.
|
|
80
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-pages
|
|
81
|
+
# pages: read
|
|
82
|
+
|
|
83
|
+
# Pull requests and related comments, assignees, labels, milestones, and merges.
|
|
84
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-pull-requests
|
|
85
|
+
pull_requests: write
|
|
86
|
+
|
|
87
|
+
# Manage the post-receive hooks for a repository.
|
|
88
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-repository-hooks
|
|
89
|
+
# repository_hooks: read
|
|
90
|
+
|
|
91
|
+
# Manage repository projects, columns, and cards.
|
|
92
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-repository-projects
|
|
93
|
+
# repository_projects: read
|
|
94
|
+
|
|
95
|
+
# Retrieve security vulnerability alerts.
|
|
96
|
+
# https://developer.github.com/v4/object/repositoryvulnerabilityalert/
|
|
97
|
+
# vulnerability_alerts: read
|
|
98
|
+
|
|
99
|
+
# Commit statuses.
|
|
100
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-statuses
|
|
101
|
+
# statuses: read
|
|
102
|
+
|
|
103
|
+
# Organization members and teams.
|
|
104
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-members
|
|
105
|
+
# members: read
|
|
106
|
+
|
|
107
|
+
# View and manage users blocked by the organization.
|
|
108
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-organization-user-blocking
|
|
109
|
+
# organization_user_blocking: read
|
|
110
|
+
|
|
111
|
+
# Manage organization projects, columns, and cards.
|
|
112
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-organization-projects
|
|
113
|
+
# organization_projects: read
|
|
114
|
+
|
|
115
|
+
# Manage team discussions and related comments.
|
|
116
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-team-discussions
|
|
117
|
+
# team_discussions: read
|
|
118
|
+
|
|
119
|
+
# Manage the post-receive hooks for an organization.
|
|
120
|
+
# https://developer.github.com/v3/apps/permissions/#permission-on-organization-hooks
|
|
121
|
+
# organization_hooks: read
|
|
122
|
+
|
|
123
|
+
# Get notified of, and update, content references.
|
|
124
|
+
# https://developer.github.com/v3/apps/permissions/
|
|
125
|
+
# organization_administration: read
|
|
126
|
+
# The name of the GitHub App. Defaults to the name specified in package.json
|
|
127
|
+
# name: My Probot App
|
|
128
|
+
|
|
129
|
+
# The homepage of your GitHub App.
|
|
130
|
+
# url: https://example.com/
|
|
131
|
+
|
|
132
|
+
# A description of the GitHub App.
|
|
133
|
+
# description: A description of my awesome app
|
|
134
|
+
|
|
135
|
+
# Set to true when your GitHub App is available to the public or false when it is only accessible to the owner of the app.
|
|
136
|
+
# Default: true
|
|
137
|
+
# public: false
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
exports.handler = void 0;
|
|
7
|
+
const adapter_aws_lambda_serverless_1 = require("@probot/adapter-aws-lambda-serverless");
|
|
8
|
+
const index_1 = __importDefault(require("../../src/index"));
|
|
9
|
+
const handler = (0, adapter_aws_lambda_serverless_1.createLambdaFunction)(index_1.default, { probot: (0, adapter_aws_lambda_serverless_1.createProbot)() });
|
|
10
|
+
exports.handler = handler;
|
|
@@ -0,0 +1,27 @@
|
|
|
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 react_1 = __importDefault(require("react"));
|
|
7
|
+
const react_2 = require("@hero-design/react");
|
|
8
|
+
const styled_components_1 = __importDefault(require("styled-components"));
|
|
9
|
+
// Snowflakes using styled-components;
|
|
10
|
+
const StyledButton = (0, styled_components_1.default)(react_2.Button) `
|
|
11
|
+
padding: 10px;
|
|
12
|
+
`;
|
|
13
|
+
const StyledLinkButton = (0, styled_components_1.default)(react_2.Button.Link) `
|
|
14
|
+
color: red;
|
|
15
|
+
`;
|
|
16
|
+
const { Link } = react_2.Button;
|
|
17
|
+
const StyledLink = (0, styled_components_1.default)(Link) `
|
|
18
|
+
color: red;
|
|
19
|
+
`;
|
|
20
|
+
const Sample = () => {
|
|
21
|
+
<>
|
|
22
|
+
<StyledButton />
|
|
23
|
+
<StyledLinkButton />
|
|
24
|
+
<StyledLink />
|
|
25
|
+
</>;
|
|
26
|
+
};
|
|
27
|
+
exports.default = Sample;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
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 parseSource_1 = __importDefault(require("../parseSource"));
|
|
31
|
+
describe('parseSource', () => {
|
|
32
|
+
it('reports correct snowflakes', () => {
|
|
33
|
+
const source = fs.readFileSync('./src/__mocks__/sourceSample.tsx', 'utf-8');
|
|
34
|
+
expect((0, parseSource_1.default)(source)).toEqual({
|
|
35
|
+
classNameLocs: [44, 42, 43],
|
|
36
|
+
styleLocs: [54, 47, 49, 50, 51, 52, 53],
|
|
37
|
+
sxLocs: [63, 58, 59, 60, 61, 62],
|
|
38
|
+
styledComponentLocs: [6, 10, 15],
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
package/lib/src/index.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
const probot_1 = require("probot");
|
|
15
|
+
const parseSource_1 = __importDefault(require("./parseSource"));
|
|
16
|
+
const TSX_REGEX = /\.tsx$/;
|
|
17
|
+
const TEST_REGEX = /__tests__/;
|
|
18
|
+
const DIFF_LOCS_REGEX = /@@(.*)@@/g;
|
|
19
|
+
const SNOWFLAKE_COMMENTS = {
|
|
20
|
+
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).',
|
|
21
|
+
sx: 'Snowflake detected! A component is customized via sx prop. Make sure to not use [prohibited CSS properties](https://docs.google.com/spreadsheets/d/1Dj8vqLdFaf-CSaSVoYqyYZIkGqF6OoyP7K4G1_9L62U/edit?usp=sharing).',
|
|
22
|
+
'styled-component': 'Please do not use styled-component to customize this component, use sx prop or inline style instead.',
|
|
23
|
+
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.',
|
|
24
|
+
};
|
|
25
|
+
const getDiffLocs = (diffStrs) => {
|
|
26
|
+
const locs = [];
|
|
27
|
+
diffStrs.forEach((diffStr) => {
|
|
28
|
+
const [startLocStr, numberOfLinesStr] = diffStr
|
|
29
|
+
.split('+')[1]
|
|
30
|
+
.split(' ')[0]
|
|
31
|
+
.split(',');
|
|
32
|
+
const startLoc = Number(startLocStr);
|
|
33
|
+
const numberOfLines = Number(numberOfLinesStr);
|
|
34
|
+
locs.push([startLoc, numberOfLines]);
|
|
35
|
+
});
|
|
36
|
+
return locs;
|
|
37
|
+
};
|
|
38
|
+
const checkIfDetectedSnowflakesInDiff = (diffLocs, locToComment) => {
|
|
39
|
+
const locIdx = diffLocs.findIndex(([start, numberOfLines]) => {
|
|
40
|
+
return locToComment >= start && locToComment < start + numberOfLines;
|
|
41
|
+
});
|
|
42
|
+
return locIdx !== -1;
|
|
43
|
+
};
|
|
44
|
+
module.exports = (app) => {
|
|
45
|
+
app.on(['pull_request.opened', 'pull_request.synchronize'], (context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
46
|
+
// Get PR info
|
|
47
|
+
const prNumber = context.payload.number;
|
|
48
|
+
const repoInfo = {
|
|
49
|
+
repo: context.payload.repository.name,
|
|
50
|
+
owner: context.payload.repository.owner.login,
|
|
51
|
+
};
|
|
52
|
+
const prBranch = context.payload.pull_request.head.ref;
|
|
53
|
+
// List all changed files
|
|
54
|
+
const prFiles = yield context.octokit.pulls.listFiles(Object.assign(Object.assign({}, repoInfo), { pull_number: prNumber }));
|
|
55
|
+
const tsxFiles = prFiles.data.filter((file) => TSX_REGEX.test(file.filename) &&
|
|
56
|
+
!TEST_REGEX.test(file.filename) &&
|
|
57
|
+
file.status !== 'removed');
|
|
58
|
+
// Saving file patches to get diff locations
|
|
59
|
+
const prFilePatches = tsxFiles.reduce((acc, file) => {
|
|
60
|
+
acc[file.filename] = file.patch || '';
|
|
61
|
+
return acc;
|
|
62
|
+
}, {});
|
|
63
|
+
// Get file contents
|
|
64
|
+
const prFileContentPromises = tsxFiles.map((file) => context.octokit.repos.getContent(Object.assign(Object.assign({}, repoInfo), { path: file.filename, ref: prBranch })));
|
|
65
|
+
const prFileContents = yield Promise.all(prFileContentPromises);
|
|
66
|
+
const styleComments = [];
|
|
67
|
+
const sxComments = [];
|
|
68
|
+
const styledComponentComments = [];
|
|
69
|
+
const classNameComments = [];
|
|
70
|
+
prFileContents.forEach((file) => __awaiter(void 0, void 0, void 0, function* () {
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
72
|
+
// @ts-ignore
|
|
73
|
+
const filePath = file.data.path;
|
|
74
|
+
const diffLocs = getDiffLocs(prFilePatches[filePath].match(DIFF_LOCS_REGEX) || []);
|
|
75
|
+
const stringContent = Buffer.from(
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
file.data.content, 'base64').toString();
|
|
79
|
+
// Parse file content to check for snowflakes
|
|
80
|
+
const snowflakeReport = (0, parseSource_1.default)(stringContent);
|
|
81
|
+
snowflakeReport.styleLocs.forEach((loc) => {
|
|
82
|
+
if (checkIfDetectedSnowflakesInDiff(diffLocs, loc)) {
|
|
83
|
+
styleComments.push({
|
|
84
|
+
path: filePath,
|
|
85
|
+
body: SNOWFLAKE_COMMENTS['style'],
|
|
86
|
+
line: loc,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
snowflakeReport.sxLocs.forEach((loc) => {
|
|
91
|
+
if (checkIfDetectedSnowflakesInDiff(diffLocs, loc)) {
|
|
92
|
+
sxComments.push({
|
|
93
|
+
path: filePath,
|
|
94
|
+
body: SNOWFLAKE_COMMENTS['sx'],
|
|
95
|
+
line: loc,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
snowflakeReport.styledComponentLocs.forEach((loc) => {
|
|
100
|
+
if (checkIfDetectedSnowflakesInDiff(diffLocs, loc)) {
|
|
101
|
+
styledComponentComments.push({
|
|
102
|
+
path: filePath,
|
|
103
|
+
body: SNOWFLAKE_COMMENTS['styled-component'],
|
|
104
|
+
line: loc,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
snowflakeReport.classNameLocs.forEach((loc) => {
|
|
109
|
+
if (checkIfDetectedSnowflakesInDiff(diffLocs, loc)) {
|
|
110
|
+
classNameComments.push({
|
|
111
|
+
path: filePath,
|
|
112
|
+
body: SNOWFLAKE_COMMENTS['className'],
|
|
113
|
+
line: loc,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}));
|
|
118
|
+
const personalOctokit = new probot_1.ProbotOctokit({
|
|
119
|
+
auth: { token: process.env.EH_BOT_GITHUB_TOKEN },
|
|
120
|
+
});
|
|
121
|
+
// No snowflakes detected or only potential snowflakes using classname
|
|
122
|
+
if (styleComments.length === 0 &&
|
|
123
|
+
sxComments.length === 0 &&
|
|
124
|
+
styledComponentComments.length === 0) {
|
|
125
|
+
const reviewBody = classNameComments.length > 0
|
|
126
|
+
? {
|
|
127
|
+
body: '[WARNING] Potential snowflakes detected in this PR using classnames. Please review the following comments.',
|
|
128
|
+
comments: classNameComments,
|
|
129
|
+
}
|
|
130
|
+
: {
|
|
131
|
+
body: 'No snowflakes detected in this PR.',
|
|
132
|
+
};
|
|
133
|
+
return personalOctokit.pulls.createReview(Object.assign(Object.assign(Object.assign({}, repoInfo), { pull_number: prNumber, commit_id: context.payload.pull_request.head.sha, event: 'APPROVE' }), reviewBody));
|
|
134
|
+
}
|
|
135
|
+
return personalOctokit.pulls.createReview(Object.assign(Object.assign({}, repoInfo), { pull_number: prNumber, commit_id: context.payload.pull_request.head.sha, event: 'REQUEST_CHANGES', body: 'Snowflake Guard Bot has detected some snowflakes in this PR. Please review the following comments.', comments: [
|
|
136
|
+
...styleComments,
|
|
137
|
+
...sxComments,
|
|
138
|
+
...styledComponentComments,
|
|
139
|
+
...classNameComments,
|
|
140
|
+
] }));
|
|
141
|
+
}));
|
|
142
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
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 tsParser = __importStar(require("./parsers/typescript"));
|
|
31
|
+
const reportCustomStyleProperties_1 = __importDefault(require("./reports/reportCustomStyleProperties"));
|
|
32
|
+
const reportStyledComponents_1 = __importDefault(require("./reports/reportStyledComponents"));
|
|
33
|
+
const constants_1 = require("./reports/constants");
|
|
34
|
+
const parseSource = (source) => {
|
|
35
|
+
let hasHeroDesignImport = false;
|
|
36
|
+
let hasStyledComponentsImport = false;
|
|
37
|
+
const componentList = {};
|
|
38
|
+
let styledAliasName = 'styled';
|
|
39
|
+
let styledComponentLocs = [];
|
|
40
|
+
let classNameLocs = [];
|
|
41
|
+
let styleLocs = [];
|
|
42
|
+
let sxLocs = [];
|
|
43
|
+
const approvedCmtLocs = [];
|
|
44
|
+
const ast = recast.parse(source, { parser: tsParser });
|
|
45
|
+
recast.visit(ast, {
|
|
46
|
+
visitImportDeclaration(path) {
|
|
47
|
+
this.traverse(path);
|
|
48
|
+
const importedFrom = path.value.source.value;
|
|
49
|
+
// Check if file imports components from '@hero-design/react'
|
|
50
|
+
if (importedFrom === '@hero-design/react') {
|
|
51
|
+
recast.visit(path.node, {
|
|
52
|
+
visitImportSpecifier(importPath) {
|
|
53
|
+
this.traverse(importPath);
|
|
54
|
+
if (constants_1.HD_COMPONENTS.includes(importPath.value.imported.name)) {
|
|
55
|
+
componentList[importPath.value.local.name] =
|
|
56
|
+
importPath.value.imported.name;
|
|
57
|
+
hasHeroDesignImport = true;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// Check if file imports from 'styled-components'
|
|
63
|
+
if (importedFrom === 'styled-components') {
|
|
64
|
+
recast.visit(path.node, {
|
|
65
|
+
visitImportDefaultSpecifier(importPath) {
|
|
66
|
+
this.traverse(importPath);
|
|
67
|
+
styledAliasName = importPath.value.local.name;
|
|
68
|
+
hasStyledComponentsImport = true;
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
visitComment(path) {
|
|
74
|
+
this.traverse(path);
|
|
75
|
+
const comment = path.value.value;
|
|
76
|
+
if (comment.toLowerCase().includes(constants_1.APPROVED_COMMENT.toLowerCase())) {
|
|
77
|
+
approvedCmtLocs.push(path.value.loc.start.line);
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
const isNotApprovedSnowflakes = (loc) => !approvedCmtLocs.includes(loc);
|
|
82
|
+
if (hasHeroDesignImport) {
|
|
83
|
+
// Case 1: Using className to customise components
|
|
84
|
+
// Case 2: Using style object to customise components
|
|
85
|
+
// Case 3: Using sx object to customise components
|
|
86
|
+
const customPropLocs = (0, reportCustomStyleProperties_1.default)(ast, componentList);
|
|
87
|
+
classNameLocs = customPropLocs.className.filter(isNotApprovedSnowflakes);
|
|
88
|
+
styleLocs = customPropLocs.style.filter(isNotApprovedSnowflakes);
|
|
89
|
+
sxLocs = customPropLocs.sx.filter(isNotApprovedSnowflakes);
|
|
90
|
+
// Case 4: Using styled-components to customise components
|
|
91
|
+
if (hasStyledComponentsImport) {
|
|
92
|
+
styledComponentLocs = (0, reportStyledComponents_1.default)(ast, componentList, styledAliasName).filter(isNotApprovedSnowflakes);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
classNameLocs,
|
|
97
|
+
styleLocs,
|
|
98
|
+
sxLocs,
|
|
99
|
+
styledComponentLocs,
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
exports.default = parseSource;
|