-
-
Notifications
You must be signed in to change notification settings - Fork 732
feat(biome_js_analyze): custom jsxFactory and jsxFragmentFactory #7847
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
base: main
Are you sure you want to change the base?
Conversation
|
WalkthroughThis PR adds support for custom JSX factories and fragment factories (e.g., Preact's Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks and finishing touchesβ Failed checks (1 warning)
β Passed checks (4 passed)
β¨ Finishing touches
π§ͺ Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Actionable comments posted: 0
Caution
Some comments are outside the diff and canβt be posted inline due to platform limitations.
β οΈ Outside diff range comments (1)
crates/biome_service/src/settings.rs (1)
1796-1819: Propagate factory settings in overrides tooOverrides currently merge only jsx_runtime; jsx_factory/jsx_fragment_factory are dropped, which can surprise users overriding perβpath settings.
Consider:
- language_setting.environment.jsx_runtime = - conf.jsx_runtime.or(parent_settings.environment.jsx_runtime); + language_setting.environment.jsx_runtime = + conf.jsx_runtime.or(parent_settings.environment.jsx_runtime); + language_setting.environment.jsx_factory = + conf.jsx_factory.or_else(|| parent_settings.environment.jsx_factory.clone()); + language_setting.environment.jsx_fragment_factory = + conf.jsx_fragment_factory.or_else(|| parent_settings.environment.jsx_fragment_factory.clone());
π§Ή Nitpick comments (9)
crates/biome_js_analyze/src/react.rs (1)
338-345: Consider validating import source.Unlike
is_global_react_import(which checks declaration type and import source), this function only checks the binding name. This might match local variables with the same name as the factory.If the JSX factory configuration expects imports from specific modules (e.g., 'preact'), consider adding source validation similar to
is_global_react_import.crates/biome_configuration/src/javascript/mod.rs (1)
48-60: JSX factory fields: good addition; consider normalising inputsTwo minor polish items:
- Trim whitespace on values at read time to avoid config nits.
- Clarify in rustdoc that these should be base identifiers (before any dot), since downstream code often expects that.
Happy path as-is; these are non-blocking.
crates/biome_js_analyze/tests/spec_tests.rs (1)
182-200: Avoid double tsconfig lookup and trim identifiers
- Query tsconfig once and reuse for factory + fragment.
- Trim the identifiers to be safe.
Example tweak:
- if options.jsx_runtime() == Some(JsxRuntime::ReactClassic) { - if options.jsx_factory().is_none() { - let factory = project_layout.query_tsconfig_for_path(input_file, |tsconfig| { - tsconfig.jsx_factory_identifier().map(|s| s.to_string()) - }).flatten(); - options.set_jsx_factory(factory.map(|s| s.into())); - } - if options.jsx_fragment_factory().is_none() { - let fragment_factory = project_layout.query_tsconfig_for_path(input_file, |tsconfig| { - tsconfig.jsx_fragment_factory_identifier().map(|s| s.to_string()) - }).flatten(); - options.set_jsx_fragment_factory(fragment_factory.map(|s| s.into())); - } - } + if options.jsx_runtime() == Some(JsxRuntime::ReactClassic) { + if options.jsx_factory().is_none() || options.jsx_fragment_factory().is_none() { + if let Some(tsconfig) = project_layout.find_tsconfig_for_path(input_file) { + if options.jsx_factory().is_none() { + let f = tsconfig.jsx_factory_identifier().map(|s| s.trim().to_string()); + options.set_jsx_factory(f.map(Into::into)); + } + if options.jsx_fragment_factory().is_none() { + let ff = tsconfig.jsx_fragment_factory_identifier().map(|s| s.trim().to_string()); + options.set_jsx_fragment_factory(ff.map(Into::into)); + } + } + } + }crates/biome_js_analyze/src/lint/correctness/no_unused_imports.rs (1)
626-642: Nice exemption for custom JSX factories; consider sharing helperLogic is sound and fixes the false positives. To DRY with use_import_type, extract a shared helper (e.g. react::is_jsx_factory_binding(binding, ctx)) so both rules stay in lockstep when this grows.
crates/biome_analyze/src/context.rs (1)
164-172: Doc nudge: clarify expected shape of identifiersPlease mention these are base identifiers (preβdot), e.g. βReactβ not βReact.createElementβ, matching what services/options pass in. Keeps invariants obvious for rule authors.
crates/biome_service/src/file_handlers/javascript.rs (1)
346-359: Minor normalisation: trim and gate by runtimeTwo tiny tweaks:
- Trim before split to avoid stray spaces.
- Optionally only forward factories when runtime is ReactClassic (purely to reduce surprise).
Example:
- let jsx_factory = environment + let jsx_factory = (matches!(jsx_runtime, biome_analyze::options::JsxRuntime::ReactClassic)) + .then_some(environment) .and_then(|env| env.jsx_factory.as_ref()) - .and_then(|factory| factory.split('.').next()) + .map(|s| s.trim()) + .and_then(|factory| factory.split('.').next()) .map(|s| s.into()); - let jsx_fragment_factory = environment + let jsx_fragment_factory = (matches!(jsx_runtime, biome_analyze::options::JsxRuntime::ReactClassic)) + .then_some(environment) .and_then(|env| env.jsx_fragment_factory.as_ref()) - .and_then(|factory| factory.split('.').next()) + .map(|s| s.trim()) + .and_then(|factory| factory.split('.').next()) .map(|s| s.into());crates/biome_package/src/node_js_package/tsconfig_json.rs (1)
120-136: Trim before splitting to be tolerant of whitespaceA tiny guard improves resilience:
- pub fn jsx_factory_identifier(&self) -> Option<&str> { - self.compiler_options - .jsx_factory - .as_ref() - .map(|factory| factory.split('.').next().unwrap_or(factory.as_str())) - } + pub fn jsx_factory_identifier(&self) -> Option<&str> { + self.compiler_options.jsx_factory.as_deref().map(|factory| { + let f = factory.trim(); + f.split('.').next().unwrap_or(f) + }) + }Apply the same to jsx_fragment_factory_identifier.
crates/biome_js_analyze/src/lint/style/use_import_type.rs (1)
1101-1129: Centraliseis_jsx_factory_bindingto avoid driftGreat extraction. Consider moving this to
crate::react(public helper) so both this rule andnoUnusedImportsshare one source of truth for JSXβfactory recognition. Less chance of future divergence.crates/biome_analyze/src/options.rs (1)
179-193: Consider adding rustdoc and reordering methods.Two suggestions to improve maintainability:
Missing rustdoc comments: Per the coding guidelines, "Document rules, assists, and options with inline rustdoc in source". These public API methods should have doc comments explaining their purpose and return values.
Method ordering: Grouping each field's getter and setter together would improve readability. Current order (jsx_factory getter β factory setter β fragment setter β fragment getter) is a bit unusual.
Example improvement:
+ /// Returns the configured JSX factory identifier, if any (e.g., "h" for Preact). pub fn jsx_factory(&self) -> Option<&str> { self.configuration.jsx_factory.as_deref() } + /// Sets the JSX factory identifier for the classic JSX runtime. pub fn set_jsx_factory(&mut self, jsx_factory: Option<Box<str>>) { self.configuration.jsx_factory = jsx_factory; } - pub fn set_jsx_fragment_factory(&mut self, jsx_fragment_factory: Option<Box<str>>) { - self.configuration.jsx_fragment_factory = jsx_fragment_factory; - } - + /// Returns the configured JSX fragment factory identifier, if any (e.g., "Fragment"). pub fn jsx_fragment_factory(&self) -> Option<&str> { self.configuration.jsx_fragment_factory.as_deref() } + + /// Sets the JSX fragment factory identifier for the classic JSX runtime. + pub fn set_jsx_fragment_factory(&mut self, jsx_fragment_factory: Option<Box<str>>) { + self.configuration.jsx_fragment_factory = jsx_fragment_factory; + }
π Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
β Files ignored due to path filters (1)
crates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.js.snapis excluded by!**/*.snapand included by**
π Files selected for processing (17)
crates/biome_analyze/src/context.rs(4 hunks)crates/biome_analyze/src/options.rs(3 hunks)crates/biome_analyze/src/registry.rs(2 hunks)crates/biome_analyze/src/signals.rs(3 hunks)crates/biome_configuration/src/javascript/mod.rs(1 hunks)crates/biome_js_analyze/src/lint/correctness/no_unused_imports.rs(2 hunks)crates/biome_js_analyze/src/lint/style/use_import_type.rs(6 hunks)crates/biome_js_analyze/src/react.rs(1 hunks)crates/biome_js_analyze/tests/quick_test.rs(1 hunks)crates/biome_js_analyze/tests/spec_tests.rs(1 hunks)crates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.js(1 hunks)crates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.options.json(1 hunks)crates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.tsconfig.json(1 hunks)crates/biome_package/src/node_js_package/tsconfig_json.rs(2 hunks)crates/biome_service/src/file_handlers/javascript.rs(2 hunks)crates/biome_service/src/file_handlers/mod.rs(1 hunks)crates/biome_service/src/settings.rs(1 hunks)
π§° Additional context used
π Path-based instructions (5)
crates/biome_*/**
π CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_analyze/src/registry.rscrates/biome_js_analyze/src/react.rscrates/biome_service/src/settings.rscrates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.tsconfig.jsoncrates/biome_js_analyze/src/lint/correctness/no_unused_imports.rscrates/biome_analyze/src/signals.rscrates/biome_configuration/src/javascript/mod.rscrates/biome_package/src/node_js_package/tsconfig_json.rscrates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.jscrates/biome_service/src/file_handlers/mod.rscrates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.options.jsoncrates/biome_js_analyze/tests/spec_tests.rscrates/biome_js_analyze/tests/quick_test.rscrates/biome_analyze/src/options.rscrates/biome_analyze/src/context.rscrates/biome_service/src/file_handlers/javascript.rscrates/biome_js_analyze/src/lint/style/use_import_type.rs
**/*.rs
π CodeRabbit inference engine (CONTRIBUTING.md)
**/*.rs: Format Rust files before committing (e.g., viajust fwhich formats Rust)
Document rules, assists, and options with inline rustdoc in source
Files:
crates/biome_analyze/src/registry.rscrates/biome_js_analyze/src/react.rscrates/biome_service/src/settings.rscrates/biome_js_analyze/src/lint/correctness/no_unused_imports.rscrates/biome_analyze/src/signals.rscrates/biome_configuration/src/javascript/mod.rscrates/biome_package/src/node_js_package/tsconfig_json.rscrates/biome_service/src/file_handlers/mod.rscrates/biome_js_analyze/tests/spec_tests.rscrates/biome_js_analyze/tests/quick_test.rscrates/biome_analyze/src/options.rscrates/biome_analyze/src/context.rscrates/biome_service/src/file_handlers/javascript.rscrates/biome_js_analyze/src/lint/style/use_import_type.rs
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**
π CodeRabbit inference engine (CLAUDE.md)
Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}
Files:
crates/biome_js_analyze/src/react.rscrates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.tsconfig.jsoncrates/biome_js_analyze/src/lint/correctness/no_unused_imports.rscrates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.jscrates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.options.jsoncrates/biome_js_analyze/tests/spec_tests.rscrates/biome_js_analyze/tests/quick_test.rscrates/biome_js_analyze/src/lint/style/use_import_type.rs
**/tests/**
π CodeRabbit inference engine (CLAUDE.md)
Place test files under a tests/ directory in each crate
Files:
crates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.tsconfig.jsoncrates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.jscrates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.options.jsoncrates/biome_js_analyze/tests/spec_tests.rscrates/biome_js_analyze/tests/quick_test.rs
crates/biome_configuration/src/**
π CodeRabbit inference engine (CLAUDE.md)
Keep configuration sources under biome_configuration/src/
Files:
crates/biome_configuration/src/javascript/mod.rs
π§ Learnings (3)
π Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: Applies to crates/biome_analyze/**/lib/src/{lint,assist}/**/*.rs : Retrieve rule options via ctx.options() inside rules; do not manually handle overrides/extends resolution
Applied to files:
crates/biome_analyze/src/registry.rs
π Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: Applies to crates/biome_analyze/**/lib/src/{lint,assist}/**/*.rs : Set the language field in declare_lint_rule! to the most specific applicable language (js, jsx, ts, or tsx)
Applied to files:
crates/biome_service/src/settings.rscrates/biome_service/src/file_handlers/mod.rs
π Learning: 2025-10-15T09:20:19.139Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-10-15T09:20:19.139Z
Learning: Applies to crates/biome_analyze/**/tests/quick_test.rs : Use the quick test at biome_js_analyze/tests/quick_test.rs by un-ignoring and adjusting SOURCE and RuleFilter for ad-hoc checks
Applied to files:
crates/biome_js_analyze/tests/quick_test.rs
𧬠Code graph analysis (10)
crates/biome_analyze/src/registry.rs (2)
crates/biome_analyze/src/context.rs (2)
jsx_factory(165-167)jsx_fragment_factory(170-172)crates/biome_analyze/src/options.rs (2)
jsx_factory(179-181)jsx_fragment_factory(191-193)
crates/biome_service/src/settings.rs (2)
crates/biome_analyze/src/context.rs (3)
jsx_runtime(160-162)jsx_factory(165-167)jsx_fragment_factory(170-172)crates/biome_analyze/src/options.rs (3)
jsx_runtime(175-177)jsx_factory(179-181)jsx_fragment_factory(191-193)
crates/biome_js_analyze/src/lint/correctness/no_unused_imports.rs (2)
crates/biome_js_analyze/src/react.rs (2)
is_global_react_import(305-336)is_jsx_factory_import(341-345)crates/biome_analyze/src/context.rs (2)
jsx_factory(165-167)jsx_fragment_factory(170-172)
crates/biome_service/src/file_handlers/mod.rs (3)
crates/biome_service/src/settings.rs (1)
analyzer_options(1119-1139)crates/biome_analyze/src/context.rs (1)
options(155-157)packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
JsxRuntime(701-701)
crates/biome_js_analyze/tests/spec_tests.rs (2)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
JsxRuntime(701-701)crates/biome_js_analyze/src/services/module_graph.rs (1)
project_layout(23-25)
crates/biome_js_analyze/tests/quick_test.rs (5)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (4)
JsFileSource(9483-9492)TextRange(9327-9327)JsxRuntime(701-701)Severity(9308-9308)crates/biome_js_syntax/src/file_source.rs (1)
jsx(168-170)crates/biome_analyze/src/context.rs (3)
file_path(187-189)new(33-65)options(155-157)crates/biome_js_analyze/src/lib.rs (1)
analyze(188-209)crates/biome_diagnostics/src/lib.rs (1)
print_diagnostic_to_string(85-103)
crates/biome_analyze/src/options.rs (1)
crates/biome_analyze/src/context.rs (2)
jsx_factory(165-167)jsx_fragment_factory(170-172)
crates/biome_analyze/src/context.rs (1)
crates/biome_analyze/src/options.rs (2)
jsx_factory(179-181)jsx_fragment_factory(191-193)
crates/biome_service/src/file_handlers/javascript.rs (3)
packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
JsxRuntime(701-701)crates/biome_analyze/src/context.rs (3)
jsx_runtime(160-162)jsx_factory(165-167)jsx_fragment_factory(170-172)crates/biome_analyze/src/options.rs (4)
jsx_runtime(175-177)jsx_factory(179-181)jsx_fragment_factory(191-193)configuration(199-201)
crates/biome_js_analyze/src/lint/style/use_import_type.rs (3)
crates/biome_js_analyze/src/react.rs (2)
is_global_react_import(305-336)is_jsx_factory_import(341-345)packages/@biomejs/backend-jsonrpc/src/workspace.ts (1)
JsxRuntime(701-701)crates/biome_analyze/src/context.rs (2)
jsx_factory(165-167)jsx_fragment_factory(170-172)
π Additional comments (13)
crates/biome_service/src/file_handlers/mod.rs (1)
1630-1647: Well-structured tsconfig integration.The logic correctly queries tsconfig.json for JSX factory settings only when needed (ReactClassic runtime, factories not already set). The defensive checks and use of
flatten()are appropriate.crates/biome_analyze/src/registry.rs (2)
409-410: LGTM.Consistent with the existing pattern for extracting options.
424-425: LGTM.Correctly threads JSX factory options through to RuleContext.
crates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.js (1)
1-10: Good test fixture for Preact JSX support.The test case appropriately demonstrates custom JSX factory usage with Preact's
handFragment.crates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.options.json (1)
1-6: LGTM.Correctly configures ReactClassic runtime for the test scenario.
crates/biome_js_analyze/tests/specs/correctness/noUnusedImports/jsxFactoryPreact.tsconfig.json (1)
1-8: LGTM.Standard Preact configuration demonstrating the feature.
crates/biome_analyze/src/signals.rs (3)
373-374: LGTM.Correctly propagates JSX factory options to RuleContext in the diagnostic path.
412-413: LGTM.Consistent with the diagnostic path.
479-480: LGTM.Completes the JSX factory propagation across all execution paths.
crates/biome_js_analyze/tests/quick_test.rs (1)
99-157: Solid test coverage for JSX factory support.The test clearly demonstrates that custom JSX factories (h, Fragment) are correctly recognised as used when configured via ReactClassic runtime options.
crates/biome_service/src/settings.rs (1)
500-510: Environment wiring for JSX factories looks rightThe environment now carries jsx_runtime + factory fields; aligns with downstream usage. No blockers here.
crates/biome_analyze/src/options.rs (2)
78-82: LGTM!The field declarations are well-commented and use appropriate types for optional JSX factory configuration.
104-112: LGTM!The builder methods follow the established pattern and enable fluent configuration chaining.
Closes #6438, #3682
Summary
Problem: When using alternative JSX libraries (Preact, Stencil, or custom JSX implementations) with the classic JSX runtime, Biome incorrectly reports custom JSX factory functions as unused imports.
Root cause: Biome only recognized the hardcoded identifiers when jsxRuntime is set to "reactClassic". It didn't read or respect the jsxFactory and jsxFragmentFactory settings from tsconfig.json.
Changeset
Added support for custom JSX factory functions when using the classic React runtime.
Test Plan
Tests added into "biome/crates/biome_js_analyze/tests/specs/correctness/noUnusedImports"