-
Notifications
You must be signed in to change notification settings - Fork 129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ESLint plugin #1991
Add ESLint plugin #1991
Conversation
🦋 Changeset detectedLatest commit: ba51a18 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
packages/eslint-plugin-circuit-ui/no-invalid-custom-properties/index.ts
Fixed
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mid-implementation notes 👇
This is still a draft (I'll work on tests and benchmarking this thing's performance next)
packages/eslint-plugin-circuit-ui/no-invalid-custom-properties/index.ts
Outdated
Show resolved
Hide resolved
Benchmark ( Full output
Rule | Time (ms) | Relative
:------------------------------------------------------|----------:|--------:
prettier/prettier | 2770.764 | 20.1%
@typescript-eslint/no-unsafe-assignment | 2600.208 | 18.9%
compat/compat | 719.023 | 5.2%
import/no-cycle | 706.740 | 5.1%
import/no-relative-packages | 598.244 | 4.3%
@typescript-eslint/no-floating-promises | 508.647 | 3.7%
import/order | 487.787 | 3.5%
@typescript-eslint/no-unsafe-argument | 449.478 | 3.3%
import/no-extraneous-dependencies | 403.686 | 2.9%
import/no-useless-path-segments | 257.532 | 1.9%
@typescript-eslint/no-redeclare | 249.347 | 1.8%
@typescript-eslint/naming-convention | 228.019 | 1.7%
@typescript-eslint/no-unsafe-return | 204.671 | 1.5%
react/no-direct-mutation-state | 204.657 | 1.5%
jest/no-standalone-expect | 171.139 | 1.2%
jest/no-conditional-expect | 131.775 | 1.0%
import/no-self-import | 126.274 | 0.9%
jest/no-disabled-tests | 124.265 | 0.9%
jest/no-identical-title | 115.534 | 0.8%
import/no-duplicates | 112.125 | 0.8%
notice/notice | 110.038 | 0.8%
jest/unbound-method | 93.470 | 0.7%
jest/expect-expect | 92.148 | 0.7%
import/no-import-module-exports | 86.865 | 0.6%
react/require-render-return | 73.505 | 0.5%
jest/valid-expect-in-promise | 64.051 | 0.5%
jest/no-done-callback | 63.826 | 0.5%
jest/valid-title | 60.895 | 0.4%
jest/no-test-prefixes | 58.607 | 0.4%
jest/valid-describe-callback | 57.867 | 0.4%
jest/no-export | 57.551 | 0.4%
jest/no-focused-tests | 56.524 | 0.4%
react/no-unknown-property | 54.447 | 0.4%
@typescript-eslint/no-unsafe-member-access | 53.201 | 0.4%
@typescript-eslint/no-misused-promises | 50.672 | 0.4%
max-len | 48.151 | 0.3%
@typescript-eslint/keyword-spacing | 42.305 | 0.3%
@typescript-eslint/comma-dangle | 36.467 | 0.3%
import/named | 34.464 | 0.3%
@typescript-eslint/unbound-method | 34.046 | 0.2%
@typescript-eslint/no-unsafe-call | 33.285 | 0.2%
react/jsx-uses-react | 28.641 | 0.2%
no-restricted-globals | 27.698 | 0.2%
react/no-deprecated | 26.266 | 0.2%
object-shorthand | 24.260 | 0.2%
import/no-unresolved | 22.822 | 0.2%
comma-style | 22.705 | 0.2%
spaced-comment | 22.493 | 0.2%
import/no-named-as-default | 22.235 | 0.2%
react/no-danger-with-children | 20.354 | 0.1%
@typescript-eslint/func-call-spacing | 19.759 | 0.1%
@typescript-eslint/no-empty-function | 19.624 | 0.1%
@typescript-eslint/space-infix-ops | 18.831 | 0.1%
@typescript-eslint/no-unused-vars | 18.550 | 0.1%
@typescript-eslint/object-curly-spacing | 18.128 | 0.1%
camelcase | 17.740 | 0.1%
one-var | 16.664 | 0.1%
@typescript-eslint/no-shadow | 15.513 | 0.1%
react/no-string-refs | 15.019 | 0.1%
@typescript-eslint/comma-spacing | 14.623 | 0.1%
semi-spacing | 14.590 | 0.1%
space-in-parens | 14.544 | 0.1%
react-hooks/rules-of-hooks | 14.460 | 0.1%
@typescript-eslint/no-loss-of-precision | 14.397 | 0.1%
@typescript-eslint/no-unnecessary-type-assertion | 14.081 | 0.1%
no-useless-return | 14.018 | 0.1%
react-hooks/exhaustive-deps | 13.870 | 0.1%
@typescript-eslint/no-use-before-define | 13.102 | 0.1%
no-whitespace-before-property | 12.603 | 0.1%
no-unused-vars | 12.266 | 0.1%
no-global-assign | 11.917 | 0.1%
no-trailing-spaces | 11.850 | 0.1%
no-extend-native | 11.719 | 0.1%
@typescript-eslint/semi | 10.914 | 0.1%
no-param-reassign | 10.853 | 0.1%
react/no-children-prop | 10.548 | 0.1%
no-redeclare | 10.470 | 0.1%
no-unexpected-multiline | 10.340 | 0.1%
@typescript-eslint/restrict-template-expressions | 10.061 | 0.1%
jsx-a11y/interactive-supports-focus | 9.616 | 0.1%
no-alert | 9.498 | 0.1%
no-multiple-empty-lines | 9.258 | 0.1%
jsx-a11y/no-noninteractive-element-interactions | 9.223 | 0.1%
@typescript-eslint/quotes | 9.019 | 0.1%
@typescript-eslint/no-unused-expressions | 8.887 | 0.1%
no-underscore-dangle | 8.816 | 0.1%
@typescript-eslint/ban-types | 8.668 | 0.1%
jest/valid-expect | 8.536 | 0.1%
prefer-exponentiation-operator | 8.511 | 0.1%
no-undef-init | 8.469 | 0.1%
no-regex-spaces | 8.214 | 0.1%
react/jsx-no-comment-textnodes | 8.073 | 0.1%
jest/no-deprecated-functions | 7.858 | 0.1%
@typescript-eslint/await-thenable | 7.733 | 0.1%
semi-style | 7.728 | 0.1%
+ @sumup/circuit-ui/no-invalid-custom-properties | 7.564 | 0.1%
key-spacing | 7.467 | 0.1%
no-script-url | 7.455 | 0.1%
linebreak-style | 7.442 | 0.1%
padded-blocks | 7.365 | 0.1%
no-unsafe-optional-chaining | 7.267 | 0.1%
react/no-render-return-value | 7.196 | 0.1%
import/newline-after-import | 7.150 | 0.1%
array-callback-return | 6.806 | 0.0%
import/export | 6.781 | 0.0%
no-spaced-func | 6.693 | 0.0%
@typescript-eslint/brace-style | 6.600 | 0.0%
arrow-spacing | 6.527 | 0.0%
react/jsx-no-duplicate-props | 6.416 | 0.0%
@typescript-eslint/no-inferrable-types | 6.390 | 0.0%
@typescript-eslint/dot-notation | 6.268 | 0.0%
react/no-unescaped-entities | 5.975 | 0.0%
@typescript-eslint/no-throw-literal | 5.832 | 0.0%
@typescript-eslint/no-implied-eval | 5.825 | 0.0%
@typescript-eslint/triple-slash-reference | 5.795 | 0.0%
import/no-amd | 5.772 | 0.0%
no-multi-spaces | 5.736 | 0.0%
no-useless-escape | 5.723 | 0.0%
no-restricted-properties | 5.632 | 0.0%
no-mixed-spaces-and-tabs | 5.512 | 0.0%
no-constant-condition | 5.437 | 0.0%
react/jsx-key | 5.280 | 0.0%
no-misleading-character-class | 5.249 | 0.0%
block-spacing | 5.236 | 0.0%
react/jsx-no-undef | 4.974 | 0.0%
@typescript-eslint/restrict-plus-operands | 4.950 | 0.0%
no-shadow-restricted-names | 4.842 | 0.0%
no-control-regex | 4.835 | 0.0%
no-unreachable-loop | 4.802 | 0.0%
jsx-a11y/media-has-caption | 4.801 | 0.0%
jest/no-interpolation-in-snapshots | 4.759 | 0.0%
@typescript-eslint/lines-between-class-members | 4.756 | 0.0%
keyword-spacing | 4.743 | 0.0%
no-octal-escape | 4.732 | 0.0%
jest/no-jasmine-globals | 4.689 | 0.0%
jsx-a11y/alt-text | 4.597 | 0.0%
react/prop-types | 4.586 | 0.0%
consistent-return | 4.567 | 0.0%
no-irregular-whitespace | 4.505 | 0.0%
function-call-argument-newline | 4.498 | 0.0%
import/no-named-as-default-member | 4.447 | 0.0%
no-promise-executor-return | 4.358 | 0.0%
@typescript-eslint/space-before-blocks | 4.354 | 0.0%
@typescript-eslint/no-loop-func | 4.352 | 0.0%
jsx-a11y/no-redundant-roles | 4.334 | 0.0%
no-eval | 4.244 | 0.0%
no-tabs | 4.167 | 0.0%
@typescript-eslint/adjacent-overload-signatures | 4.029 | 0.0%
react/jsx-no-target-blank | 4.023 | 0.0%
prefer-spread | 3.943 | 0.0%
prefer-template | 3.863 | 0.0%
@typescript-eslint/ban-ts-comment | 3.761 | 0.0%
import/no-absolute-path | 3.741 | 0.0%
jsx-a11y/no-static-element-interactions | 3.703 | 0.0%
no-lone-blocks | 3.689 | 0.0%
jsx-a11y/autocomplete-valid | 3.639 | 0.0%
strict | 3.585 | 0.0%
lines-around-directive | 3.518 | 0.0%
import/no-dynamic-require | 3.516 | 0.0%
@typescript-eslint/space-before-function-paren | 3.455 | 0.0%
curly | 3.422 | 0.0%
template-curly-spacing | 3.332 | 0.0%
@typescript-eslint/require-await | 3.132 | 0.0%
jsx-a11y/role-supports-aria-props | 3.099 | 0.0%
jsx-a11y/aria-activedescendant-has-tabindex | 3.049 | 0.0%
jsx-a11y/aria-unsupported-elements | 2.999 | 0.0%
arrow-body-style | 2.998 | 0.0%
grouped-accessor-pairs | 2.976 | 0.0%
@typescript-eslint/return-await | 2.974 | 0.0%
dot-location | 2.958 | 0.0%
no-nonoctal-decimal-escape | 2.944 | 0.0%
new-cap | 2.842 | 0.0%
space-unary-ops | 2.787 | 0.0%
no-multi-str | 2.752 | 0.0%
no-template-curly-in-string | 2.734 | 0.0%
jsx-a11y/label-has-associated-control | 2.679 | 0.0%
prefer-regex-literals | 2.664 | 0.0%
computed-property-spacing | 2.594 | 0.0%
block-scoped-var | 2.581 | 0.0%
prefer-const | 2.535 | 0.0%
newline-per-chained-call | 2.506 | 0.0%
import/no-webpack-loader-syntax | 2.491 | 0.0%
global-require | 2.414 | 0.0%
no-restricted-exports | 2.400 | 0.0%
wrap-iife | 2.400 | 0.0%
no-useless-backreference | 2.390 | 0.0%
jsx-a11y/click-events-have-key-events | 2.381 | 0.0%
prefer-promise-reject-errors | 2.344 | 0.0%
no-restricted-syntax | 2.299 | 0.0%
no-prototype-builtins | 2.278 | 0.0%
jsx-a11y/aria-role | 2.232 | 0.0%
prefer-numeric-literals | 2.203 | 0.0%
react/jsx-uses-vars | 2.197 | 0.0%
jsx-a11y/aria-proptypes | 2.175 | 0.0%
import/no-mutable-exports | 2.167 | 0.0%
react/no-find-dom-node | 2.157 | 0.0%
no-invalid-regexp | 2.156 | 0.0%
@typescript-eslint/no-explicit-any | 2.137 | 0.0%
@typescript-eslint/no-array-constructor | 2.108 | 0.0%
one-var-declaration-per-line | 2.102 | 0.0%
@typescript-eslint/default-param-last | 2.084 | 0.0%
object-property-newline | 2.054 | 0.0%
arrow-parens | 2.043 | 0.0%
prefer-object-spread | 2.042 | 0.0%
@typescript-eslint/no-dupe-class-members | 1.994 | 0.0%
no-extra-bind | 1.954 | 0.0%
no-empty-function | 1.953 | 0.0%
no-extra-boolean-cast | 1.944 | 0.0%
no-mixed-operators | 1.939 | 0.0%
@typescript-eslint/no-empty-interface | 1.912 | 0.0%
jest/no-mocks-import | 1.887 | 0.0%
prefer-destructuring | 1.878 | 0.0%
no-floating-decimal | 1.827 | 0.0%
array-bracket-spacing | 1.817 | 0.0%
no-useless-rename | 1.741 | 0.0%
no-useless-computed-key | 1.692 | 0.0%
space-infix-ops | 1.627 | 0.0%
jsx-a11y/anchor-has-content | 1.606 | 0.0%
class-methods-use-this | 1.594 | 0.0%
import/first | 1.558 | 0.0%
jsx-a11y/aria-props | 1.535 | 0.0%
@typescript-eslint/no-unnecessary-type-constraint | 1.527 | 0.0%
eol-last | 1.520 | 0.0%
no-iterator | 1.502 | 0.0%
jsx-a11y/no-noninteractive-tabindex | 1.474 | 0.0%
no-unsafe-finally | 1.473 | 0.0%
no-constructor-return | 1.470 | 0.0%
react/no-is-mounted | 1.462 | 0.0%
no-multi-assign | 1.448 | 0.0%
func-call-spacing | 1.374 | 0.0%
@typescript-eslint/prefer-as-const | 1.369 | 0.0%
comma-spacing | 1.359 | 0.0%
jsx-a11y/anchor-is-valid | 1.344 | 0.0%
@typescript-eslint/no-this-alias | 1.330 | 0.0%
max-classes-per-file | 1.322 | 0.0%
no-unreachable | 1.320 | 0.0%
no-octal | 1.307 | 0.0%
object-curly-spacing | 1.283 | 0.0%
jsx-a11y/img-redundant-alt | 1.242 | 0.0%
use-isnan | 1.242 | 0.0%
import/no-named-default | 1.227 | 0.0%
no-inner-declarations | 1.222 | 0.0%
@typescript-eslint/no-namespace | 1.200 | 0.0%
no-fallthrough | 1.184 | 0.0%
yoda | 1.174 | 0.0%
jest/no-commented-out-tests | 1.167 | 0.0%
jsx-a11y/mouse-events-have-key-events | 1.163 | 0.0%
radix | 1.162 | 0.0%
jsx-a11y/heading-has-content | 1.157 | 0.0%
no-var | 1.142 | 0.0%
generator-star-spacing | 1.130 | 0.0%
no-bitwise | 1.123 | 0.0%
no-dupe-else-if | 1.122 | 0.0%
no-caller | 1.117 | 0.0%
no-self-assign | 1.052 | 0.0%
jsx-a11y/no-distracting-elements | 1.051 | 0.0%
no-cond-assign | 1.051 | 0.0%
no-empty | 1.035 | 0.0%
rest-spread-spacing | 1.021 | 0.0%
prefer-arrow-callback | 1.003 | 0.0%
symbol-description | 0.978 | 0.0%
no-proto | 0.972 | 0.0%
no-console | 0.951 | 0.0%
no-self-compare | 0.921 | 0.0%
template-tag-spacing | 0.919 | 0.0%
jsx-a11y/no-access-key | 0.910 | 0.0%
jsx-a11y/no-interactive-element-to-noninteractive-role | 0.903 | 0.0%
jsx-a11y/role-has-required-aria-props | 0.861 | 0.0%
jsx-a11y/no-autofocus | 0.848 | 0.0%
@typescript-eslint/no-non-null-asserted-optional-chain | 0.842 | 0.0%
@typescript-eslint/no-extra-semi | 0.816 | 0.0%
no-import-assign | 0.803 | 0.0%
getter-return | 0.762 | 0.0%
no-loss-of-precision | 0.756 | 0.0%
no-this-before-super | 0.752 | 0.0%
jsx-a11y/scope | 0.743 | 0.0%
jsx-a11y/no-noninteractive-element-to-interactive-role | 0.743 | 0.0%
jsx-a11y/tabindex-no-positive | 0.732 | 0.0%
constructor-super | 0.725 | 0.0%
no-compare-neg-zero | 0.712 | 0.0%
eqeqeq | 0.702 | 0.0%
no-sequences | 0.655 | 0.0%
no-implied-eval | 0.655 | 0.0%
no-shadow | 0.654 | 0.0%
no-return-assign | 0.650 | 0.0%
nonblock-statement-body-position | 0.630 | 0.0%
no-else-return | 0.623 | 0.0%
no-dupe-keys | 0.620 | 0.0%
jsx-a11y/html-has-lang | 0.619 | 0.0%
semi | 0.618 | 0.0%
no-extra-label | 0.605 | 0.0%
space-before-function-paren | 0.583 | 0.0%
no-await-in-loop | 0.570 | 0.0%
default-case | 0.559 | 0.0%
switch-colon-spacing | 0.557 | 0.0%
no-obj-calls | 0.556 | 0.0%
no-unneeded-ternary | 0.545 | 0.0%
no-lonely-if | 0.543 | 0.0%
quotes | 0.535 | 0.0%
yield-star-spacing | 0.529 | 0.0%
@typescript-eslint/no-non-null-assertion | 0.523 | 0.0%
unicode-bom | 0.520 | 0.0%
require-yield | 0.506 | 0.0%
@typescript-eslint/no-useless-constructor | 0.503 | 0.0%
jsx-a11y/iframe-has-title | 0.494 | 0.0%
no-useless-concat | 0.470 | 0.0%
no-path-concat | 0.461 | 0.0%
lines-between-class-members | 0.461 | 0.0%
no-duplicate-case | 0.409 | 0.0%
no-sparse-arrays | 0.400 | 0.0%
prefer-rest-params | 0.399 | 0.0%
no-empty-pattern | 0.397 | 0.0%
no-setter-return | 0.393 | 0.0%
@typescript-eslint/no-for-in-array | 0.389 | 0.0%
operator-assignment | 0.385 | 0.0%
func-names | 0.374 | 0.0%
@typescript-eslint/no-extra-non-null-assertion | 0.371 | 0.0%
no-const-assign | 0.370 | 0.0%
brace-style | 0.347 | 0.0%
no-nested-ternary | 0.335 | 0.0%
no-unused-expressions | 0.328 | 0.0%
@typescript-eslint/no-misused-new | 0.327 | 0.0%
new-parens | 0.326 | 0.0%
@typescript-eslint/prefer-namespace-keyword | 0.318 | 0.0%
no-loop-func | 0.314 | 0.0%
no-plusplus | 0.310 | 0.0%
no-dupe-class-members | 0.304 | 0.0%
no-empty-character-class | 0.284 | 0.0%
no-new-wrappers | 0.281 | 0.0%
no-labels | 0.276 | 0.0%
no-unused-labels | 0.271 | 0.0%
for-direction | 0.266 | 0.0%
valid-typeof | 0.265 | 0.0%
no-delete-var | 0.264 | 0.0%
no-new-object | 0.263 | 0.0%
space-before-blocks | 0.253 | 0.0%
no-void | 0.247 | 0.0%
vars-on-top | 0.230 | 0.0%
no-new-require | 0.222 | 0.0%
@typescript-eslint/no-var-requires | 0.206 | 0.0%
no-func-assign | 0.201 | 0.0%
no-return-await | 0.199 | 0.0%
no-unsafe-negation | 0.194 | 0.0%
no-class-assign | 0.187 | 0.0%
no-case-declarations | 0.186 | 0.0%
no-array-constructor | 0.172 | 0.0%
default-case-last | 0.170 | 0.0%
dot-notation | 0.166 | 0.0%
no-async-promise-executor | 0.166 | 0.0%
no-continue | 0.158 | 0.0%
no-ex-assign | 0.152 | 0.0%
no-label-var | 0.147 | 0.0%
no-buffer-constructor | 0.137 | 0.0%
no-useless-catch | 0.137 | 0.0%
no-debugger | 0.133 | 0.0%
default-param-last | 0.126 | 0.0%
no-new | 0.123 | 0.0%
no-new-symbol | 0.102 | 0.0%
no-dupe-args | 0.100 | 0.0%
jest/no-jest-import | 0.095 | 0.0%
no-extra-semi | 0.095 | 0.0%
no-undef | 0.089 | 0.0%
guard-for-in | 0.087 | 0.0%
no-with | 0.086 | 0.0%
no-new-func | 0.084 | 0.0%
no-useless-constructor | 0.046 | 0.0%
no-throw-literal | 0.039 | 0.0% TL;DR: the rule runs in 7ms on the repo, or about 0.1% of the full linting time (most expensive rules are |
Codecov Report
@@ Coverage Diff @@
## main #1991 +/- ##
==========================================
+ Coverage 91.95% 92.15% +0.20%
==========================================
Files 170 168 -2
Lines 3541 3533 -8
Branches 1176 1176
==========================================
Hits 3256 3256
+ Misses 265 257 -8
Partials 20 20
|
Planning on releasing the plugin as a beta (<v1) version initially, and release v1.0.0 after testing in production apps. |
44ac249
to
f178040
Compare
☝️ rebased to fix conflict with #2000 (comment) |
Co-authored-by: Connor Bär <[email protected]>
This purposefully still includes the debugging RegExp I used. Will clean up later on.
This surfaced a bug with the regexp that doesn't flag invalid properties when there are trailing [\w] characters, e.g.
c9a5b94
to
236127c
Compare
We could also ignore js config files globally from the root .eslintignore, but since there's already a file in every package, I'm following the same approach for .
This time |
Purpose
This PR adds a new package to the Circuit UI monorepo,
@sumup/eslint-plugin-circuit-ui
.Approach and changes
We're in the process of migrating from a JSON-based theme to CSS custom properties (#1880, #1944, #1951).
This has performance benefits and enables using tokens outside of JavaScript. However, it also has a major downside: IDEs and lint tools have no way (currently) of warning developers if they use an invalid custom property, which can result in uncaught bugs.
To mitigate this, we are releasing a new ESLint plugin which currently contains a single rule that checks that all custom properties prefixed with
--cui
are valid, by matching them against the theme:--cui
. Playground and breakdown: https://regex101.com/r/NJLHgL/4--cui-${colorMap[state]}
. This is a good thing, because full-length variables are easier to find, maintain and codemod in future theme changes.@typescript-eslint
on top ofeslint
, even though it isn't type-aware, since this is what we already use in the project. Documentation: https://typescript-eslint.io/custom-rules. Some knowledge of custom ESLint rules is still a prerequisite to work with@typescript-eslint
rulesBaseStyles
, Storybook docs, Storybook themes, and this plugin). Add ESLint plugin #1991 (comment)Definition of done