Skip to content

Conversation

@pchorus
Copy link

@pchorus pchorus commented Nov 21, 2025

Add ESLint rule dot-notation.

I took some inspiration and some code snippets from #9344.

I also implemented the fixer taking inspiration from https://github.com/oxc-project/oxc/blob/main/crates/oxc_minifier/src/peephole/convert_to_dotted_properties.rs as as suggested by @Boshen in #9344 and from the original ESLint implementation.

@pchorus pchorus requested a review from camc314 as a code owner November 21, 2025 22:32
@graphite-app
Copy link
Contributor

graphite-app bot commented Nov 21, 2025

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • 0-merge - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

@github-actions github-actions bot added A-linter Area - Linter C-enhancement Category - New feature or request labels Nov 21, 2025
Comment on lines +426 to +445
"(let?.true)",
r#"(let?.["true"])"#,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed to add parenthesis here. Otherwise, the parser doesn't accept it as I checked in the AST Explorer.

@pchorus pchorus force-pushed the feature/dot-notation branch from d84c5c2 to 9684761 Compare November 21, 2025 22:43
Comment on lines +250 to +255
fn is_valid_identifier(s: &str) -> bool {
let mut chars = s.bytes();
chars.next().is_some_and(|c|
/* a-zA-Z_$ */ c.is_ascii_alphabetic() || c == b'_' || c == b'$')
&& chars.all(|c| /* a-zA-Z0-9_$ */ c.is_ascii_alphanumeric() || c == b'_' || c == b'$')
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Incorrect validation of identifiers with Unicode characters

The is_valid_identifier function only validates ASCII characters, but JavaScript identifiers can include Unicode letters. This will incorrectly reject valid Unicode identifiers.

For example, these are valid in JavaScript but would be rejected:

foo["你好"]  // Chinese characters are valid identifiers
foo["café"]  // é is a valid identifier character

The function should use proper Unicode identifier validation according to the JavaScript specification, or leverage existing identifier validation utilities from the AST parser.

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

return;
}
if let Some(allow_pattern) = &self.allow_pattern {
if Regex::new(allow_pattern).unwrap().is_match(&value) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical: Potential panic on invalid user-provided regex

The code calls Regex::new(allow_pattern).unwrap() where allow_pattern comes from user configuration. If a user provides an invalid regex pattern, this will panic in production.

if let Some(allow_pattern) = &self.allow_pattern {
    if let Ok(regex) = Regex::new(allow_pattern) {
        if regex.is_match(&value) {
            return;
        }
    }
    // Or handle the error appropriately
}

Alternatively, validate and compile the regex once during configuration parsing in from_configuration() and store the compiled regex in the config struct.

Suggested change
if Regex::new(allow_pattern).unwrap().is_match(&value) {
if let Ok(regex) = Regex::new(allow_pattern) {
if regex.is_match(&value) {

Spotted by Graphite Agent

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@codspeed-hq
Copy link

codspeed-hq bot commented Nov 22, 2025

CodSpeed Performance Report

Merging #15957 will not alter performance

Comparing pchorus:feature/dot-notation (a6dcab3) with main (ef05957)

Summary

✅ 4 untouched
⏩ 41 skipped1

Footnotes

  1. 41 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@pchorus pchorus force-pushed the feature/dot-notation branch from ff9779a to a6dcab3 Compare November 23, 2025 18:42
@pchorus
Copy link
Author

pchorus commented Nov 27, 2025

@camc314 @connorshea Thank you for the support so far.
Is there anything missing from my side?
Do you need me to provide anything else to get this merged?
If so, I am happy to do so. Just let me know.

Comment on lines +433 to +437
("08['prop']", "08 .prop", None),
("090['prop']", "090 .prop", None),
("018['prop']", "018 .prop", None),
("5_000['prop']", "5_000 .prop", None),
("5_000_00['prop']", "5_000_00 .prop", None),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these results actually intended to work/do they work?

Comment on lines +44 to +48
#[schemars(rename_all = "camelCase")]
pub struct DotNotationConfig {
allow_keywords: bool,
allow_pattern: Option<Regex>,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll want to tweak this a bit to make sure the docs that get output are correct and have descriptions for these two config options:

Suggested change
#[schemars(rename_all = "camelCase")]
pub struct DotNotationConfig {
allow_keywords: bool,
allow_pattern: Option<Regex>,
}
#[serde(rename_all = "camelCase", default)]
pub struct DotNotationConfig {
/// TODO: Describe this option
allow_keywords: bool,
/// TODO: Describe this option
#[serde(with = "Regex")]
allow_pattern: Option<Regex>,
}

The default thing just ensures that the JSON Schema emits a default value. #[serde(with = "Regex")] will ensure it gets a type value, otherwise I believe it just sets the type value to null and is not displayed. It may need to be schemars(with = "Regex") if serde doesn't work on that.

You can check what the generated docs look like by running cargo run -p website_linter rules --rule-docs target/rule-docs --git-ref $(git rev-parse HEAD) (or look at just website in the justfile and run that), and the generated docs for this rule will end up in target/rule-docs/eslint/dot-notation.md.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah also you will need to rebase the branch to ensure that website command works, it was changed in the last few days

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-linter Area - Linter C-enhancement Category - New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants