-
-
Notifications
You must be signed in to change notification settings - Fork 502
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
feat(useNamingConvention): options for custom conventions #2770
feat(useNamingConvention): options for custom conventions #2770
Conversation
Parser conformance results onjs/262
jsx/babel
symbols/microsoft
ts/babel
ts/microsoft
|
5a3a996
to
dcb4e5d
Compare
6f9d67e
to
b486fe0
Compare
CodSpeed Performance ReportMerging #2770 will not alter performanceComparing Summary
|
I will try to reduce the perf impact. Maybe it is caused by the use of a regex by default. |
66bfdd2
to
e00d9ea
Compare
eab38aa
to
6d40b9f
Compare
I was right, the perf regression was coming from running the regex for each identifier. I removed this default regex and use custom code (as before) to trim underscores and dollar signs before checking the default convention. Unfortunately the benchmark is timing out on a CSS file. Thus, the benchmark results cannot be updated for now. |
59ae55f
to
dd5896b
Compare
9b1cc4c
to
5641657
Compare
Why don't we call the option "custom" doesn't say anything to me, but "conventions" is waaay more descriptive! |
crates/biome_js_analyze/src/lint/style/use_naming_convention.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/style/use_naming_convention.rs
Outdated
Show resolved
Hide resolved
/// ### Regular expression syntax | ||
/// | ||
/// The `match` option takes a regular expression that supports the following syntaxes: | ||
/// | ||
/// - Greedy quantifiers `*`, `?`, `+`, `{n}`, `{n,m}`, `{n,}`, `{m}` | ||
/// - Non-greedy quantifiers `*?`, `??`, `+?`, `{n}?`, `{n,m}?`, `{n,}?`, `{m}?` | ||
/// - Any character matcher `.` | ||
/// - Character classes `[a-z]`, `[xyz]`, `[^a-z]` | ||
/// - Alternations `|` | ||
/// - Capturing groups `()` | ||
/// - Non-capturing groups `(?:)` | ||
/// - A limited set of escaped characters including all special characters | ||
/// and regular string escape characters `\f`, `\n`, `\r`, `\t`, `\v` |
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.
I think we should move this part inside our website, maybe in the analyzer or another section of the website, mostly because this explanation feels reductive. I think we should be more thorough and provide examples for each bullet point where we explain the patterns
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.
You are right. For now, only this rule uses regexes, however this could change in the future.
I will open a PR on the website repository.
I am unsure where this section should be.
Thus, I will keep this overview for now and will remove it in another PR with a proper link to the future regex section on the website.
5641657
to
8abae3f
Compare
I used |
cdd20b1
to
477a5f2
Compare
477a5f2
to
a5a0a3a
Compare
Summary
Fix #1900
Fix #2314
The feature is basically implemented.
I had some hard time to find a balance between simplicity and expressiveness.
I first implemented the solution I described in the related discussion.
Unfortunately this was not enough expressive to cover some usages.
I switched to a solution that is closer to TypeScript ESLint naming-convention, but hopefully simpler.
A user can basically provide an array of (custom) conventions.
A convention has two parts:
A selector selects a declaration based on several criteria:
kind
: the kind of the declarationSome examples:
variableLike
,const
,enum
,typeLike
,enumMember
,classMember
, ...In contrast to ESLint,
enumMember
is aTypeLike
.Also, we provide
const
/let
/var
/using
kinds, while in the ESLint rule they are modifiers.I also tried to reduce the number of kinds and to make them more understandable.
modifiers
: an array of modifiers amongabstract
,private
,protected
,readonly
,static
scope
: the scope where the declaration is. It takes a value amongglobal
andany
(default value)usage
(Not implemented yet): how the declaration is used.I am thinking about three values:
unused
,new
(used in aNewExpression
),component
(used as a component).An empty selector selects any declaration.
A convention must specify at least one requirement among:
formats
: the allowed string cases for the selected names;match
: a regex that the name must match.In the case where the regex matches and returns a capture,
then the capture is checked against
formats
instead of checking the entire name againstformats
.If
formats
is unspecified, then the capture is the name that must follow the next conventions in the array.Only the first capture is considered. Other capturing groups are considered as non-capturing groups.
The algorithm follows several steps:
node_selector
in the code)based on its characteristics.
match
doesn't match orformats
is not followedmatch
matches and doesn't capture or capture an empty stringformats
is respectedIf the traversal is not stopped until the end, then we apply the default convention.
The default convention strips leading and trailing underscores and dollar signs only if the name was not trimmed by a
match
.Let's take an example:
I
except for interface names that end withError
.camelCase
.CONSTANT_CASE
.These conventions can be achieved with the following configuration:
When an interface name is checked against the conventions, we apply the convention in-order.
The selector of the first convention selects the interface.
Then we apply the requirement
match
.IArguemts
, the match capturesArguemts
. The name partArguemts
is then checked against the following conventions.No other convention applies, except the default one which checks that
Arguemts
is inPascalCase
.All conventions are thus respected.
Error
, the match captures an empty string.Thus, the convention is respected and we stop there.
Other
, the first convention is not fullfilled.We stop there and report the mismatch.
Note that because we forward the captures, the previous configuration could also be expressed as follows:
NOTE: We could simplify the system by not forwarding a capture, and thus requiring
fromats
when there is a capture. This could ensure that at most one regex is executed for every member avoiding abuses of regexes by users. I am open to discussions about this simplification.I decided to use regexes because it allows to condensate several features elegantly: We don't have to provide specific fields like
prefix
,suffix
,exceptions
. All these features are provided by a regex.I was uncomfortable with the idea of exposing to user the extended syntax of the regex crate.
Thus, I implemented a wrapper that ensures that the regex follows a restricted syntax.
I also wrap the regex by default between anchors
^
and$
making the regex more readable and strict.This PR also improves diagnostics by reporting the range of the name that must respect a format or a regex.
The PR improves
biome_string_case
crate by using bitsets to checkCase
compatibility and to combine several cases into a bitsetCases
.Unfortunately,
enumflags2
doesn't allow enum variants that set multiple bits.Thus, I had to provide my own implementation.
I am using
enumflags2
for other bitsets, in particular a bitset for members'Modifier
.Default naming convention changes
I changed some defaults to accommodate with known conventions.
override
modifier.UPPERCASE
is now accepted for global function names.This fixes
useNamingConvention
: support uppercase function names for HTTP verbs #2314Remaining tasks
kind
selector has enough kinds or too much?kind
/modifiers
/scope
implement the(in the future - this PR is already too big)usage
selectorImplement a migration with(in another PR - this PR is already too big)migrate eslint
Test Plan
I added new tests and updated previous snapshots