Skip to content

feat(lint/js): add noBaseToString#9838

Merged
dyc3 merged 6 commits into
mainfrom
dyc3/noBaseToString
May 4, 2026
Merged

feat(lint/js): add noBaseToString#9838
dyc3 merged 6 commits into
mainfrom
dyc3/noBaseToString

Conversation

@dyc3

@dyc3 dyc3 commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds noBaseToString which is a port of https://typescript-eslint.io/rules/no-base-to-string/

Generated by gpt 5.4

ngl, this one sucks. It's very complicated.

The goal of this rule is to prevent accidentally rendering [object Object] in a string.

As psuedo code, the rule is essentially:

if expression is a trivial literal {
    return None;
}

let ty = inferred_type(expression);
let certainty = analyze_type(ty, mode);

if certainty == Always {
    None
} else {
    Some(diagnostic)
}

So a lot of the code is mostly dealing with evaluating type info.

AnalysisMode has two cases:

  • ToString: used for String(x), x.toString(), template interpolation, string concatenation.
  • Join: used for array.join(",").

The distinction matters because join() stringifies the elements, not the object itself.

collect_certainty()

collect_certainty() is pretty much the core business logic of the rule. It determines a type's Usefulness, which is a label of how useful the type text is if you call .toString() on it. If we can't resolve a type, we bail to avoid raising false positives.

collect_certainty() uses a stack of AnalysisTasks. The agent's summary:

  • Eval(type, mode) means “analyze this type”
  • Aggregate(kind, count) means “combine the next count child results”
  • Leave(key) means “we’re done exploring this type, remove it from the active cycle set”

Those task types are defined at :172-183.

That function handles types in this order:

  • If the type is a constrained generic, analyze the constraint instead (:397-410).
  • If the type is a primitive or otherwise safe type, return Always (:415-420, :600-633).
  • If the type is in the ignored-type list, return Always (:415-420, :755-911).
  • If the type is a union, analyze all variants and combine them (:423-431, :557-569).
  • If the type is an intersection, analyze all parts and combine them (:439-453, :571-580).
  • If the type is a tuple, analyze all elements (:456-470, :582-598).
  • If the type is an array, analyze the element type (:473-477, :539-555).
  • Otherwise, in ToString mode, ask: “does this ultimately behave like plain Object.prototype.toString?” (:480-487, :635-737).

How unions, intersections, and tuples are handled is explained in doc comments.

How It Detects “Plain Object Stringification”

is_to_string_like_from_object() essentially checks: will this use the default object stringification? or does it implement a custom .toString() or similar to avoid that?

Test Plan

snapshots

Docs

@github-actions github-actions Bot added A-CLI Area: CLI A-Project Area: project A-Linter Area: linter L-JavaScript Language: JavaScript and super languages A-Diagnostic Area: diagnostocis labels Apr 6, 2026
@codspeed-hq

codspeed-hq Bot commented Apr 6, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 63 untouched benchmarks
⏩ 191 skipped benchmarks1


Comparing dyc3/noBaseToString (6ad86c2) with main (491b171)

Open in CodSpeed

Footnotes

  1. 191 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.

@changeset-bot

changeset-bot Bot commented Apr 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6ad86c2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

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

@dyc3 dyc3 force-pushed the dyc3/noBaseToString branch from c85b65d to b1e5659 Compare April 6, 2026 21:28
@dyc3 dyc3 force-pushed the dyc3/noBaseToString branch from f68da94 to f055aab Compare April 25, 2026 17:52
@dyc3 dyc3 marked this pull request as ready for review April 25, 2026 17:53
@dyc3 dyc3 requested review from a team April 25, 2026 17:53
@coderabbitai

coderabbitai Bot commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a new nursery lint rule noBaseToString that detects sites where values may fall back to JavaScript’s default "[object Object]" stringification across String(...), template interpolation, .toString()/.toLocaleString(), .join(), and concatenation. Implements type-graph traversal with cycle detection and certainty levels, supports an ignoredTypeNames option, exposes rule option types, adds ESLint-to-Biome migration for @typescript-eslint/no-base-to-string, and includes tests and CLI migration handling.

Suggested labels

A-Type-Inference

Suggested reviewers

  • ematipico
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and concisely summarises the main change: adding a new noBaseToString linting rule.
Description check ✅ Passed The description is well-related to the changeset, explaining the rule's motivation, core logic, and acknowledging AI assistance as required.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dyc3/noBaseToString

Review rate limit: 4/5 reviews remaining, refill in 12 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
crates/biome_cli/tests/commands/migrate_eslint.rs (1)

421-443: Add an explicit success assert before snapshotting.
The test is great, but an upfront assert!(result.is_ok(), ...) makes breakages fail faster and clearer.

Suggested tweak
     let (fs, result) = run_cli(
         fs,
         &mut console,
         Args::from(
             [
                 "migrate",
                 "eslint",
                 "--include-inspired",
                 "--include-nursery",
             ]
             .as_slice(),
         ),
     );

+    assert!(result.is_ok(), "run_cli returned {result:?}");
     assert_cli_snapshot(SnapshotPayload::new(
         module_path!(),
         "migrate_eslintrcjson_nursery_rule_options",
         fs,
         console,
         result,
     ));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_cli/tests/commands/migrate_eslint.rs` around lines 421 - 443,
Add an explicit success assertion for the CLI run result before snapshotting:
after calling run_cli (the variable result returned by run_cli) insert an
assertion like assert!(result.is_ok(), "migrate eslint failed: {:?}", result) so
the test fails fast and shows the error before assert_cli_snapshot is invoked;
keep the existing run_cli, BufferConsole, and SnapshotPayload usage and place
the assert immediately prior to the assert_cli_snapshot call.
crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs (1)

742-748: Consider removing the dead binding.

The let _ = saw_unknown; on line 745 suppresses an unused-variable warning, but the logic implicitly handles it via the None return. This is fine, though a brief comment explaining that None covers the unknown case would aid readability.

💡 Optional: clarify intent
     if saw_true {
         Some(true)
     } else {
-        let _ = saw_unknown;
+        // When no definitive `true` result was found, return `None` to signal
+        // uncertainty (possibly due to unresolved types).
         None
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs` around lines
742 - 748, The binding let _ = saw_unknown; in the block that returns Some(true)
/ None is dead; remove that line and either rely on the None return to represent
the unknown case or, if you want to clarify intent, replace the binding with a
short comment (e.g. "None covers the unknown case") near the return to improve
readability; locate this change around the saw_true / saw_unknown logic in
no_base_to_string.rs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_cli/src/execute/migrate/eslint_typescript.rs`:
- Around line 10-14: The use declaration imports several symbols from
biome_rule_options twice, causing E0252; edit the use statement in the module to
remove the duplicated specifiers—keep a single instance of each symbol
(no_base_to_string, no_shadow, use_consistent_array_type,
use_consistent_member_accessibility, use_import_type, use_naming_convention) in
the use biome_rule_options::{...} list so each name is only imported once.

---

Nitpick comments:
In `@crates/biome_cli/tests/commands/migrate_eslint.rs`:
- Around line 421-443: Add an explicit success assertion for the CLI run result
before snapshotting: after calling run_cli (the variable result returned by
run_cli) insert an assertion like assert!(result.is_ok(), "migrate eslint
failed: {:?}", result) so the test fails fast and shows the error before
assert_cli_snapshot is invoked; keep the existing run_cli, BufferConsole, and
SnapshotPayload usage and place the assert immediately prior to the
assert_cli_snapshot call.

In `@crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs`:
- Around line 742-748: The binding let _ = saw_unknown; in the block that
returns Some(true) / None is dead; remove that line and either rely on the None
return to represent the unknown case or, if you want to clarify intent, replace
the binding with a short comment (e.g. "None covers the unknown case") near the
return to improve readability; locate this change around the saw_true /
saw_unknown logic in no_base_to_string.rs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 00bf0213-2242-494b-8e55-aeafdc4346f6

📥 Commits

Reviewing files that changed from the base of the PR and between c548d11 and f055aab.

⛔ Files ignored due to path filters (11)
  • crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs is excluded by !**/migrate/eslint_any_rule_to_biome.rs and included by **
  • crates/biome_cli/tests/snapshots/main_commands_migrate_eslint/migrate_eslintrcjson_nursery_rule_options.snap is excluded by !**/*.snap and included by **
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_configuration/src/generated/domain_selector.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_configuration/src/generated/linter_options_check.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noBaseToString/invalid.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noBaseToString/valid.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noBaseToString/validIgnoredTypeNames.ts.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (12)
  • .changeset/tidy-lamps-bake.md
  • crates/biome_cli/src/execute/migrate/eslint_eslint.rs
  • crates/biome_cli/src/execute/migrate/eslint_to_biome.rs
  • crates/biome_cli/src/execute/migrate/eslint_typescript.rs
  • crates/biome_cli/tests/commands/migrate_eslint.rs
  • crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs
  • crates/biome_js_analyze/tests/specs/nursery/noBaseToString/invalid.ts
  • crates/biome_js_analyze/tests/specs/nursery/noBaseToString/valid.ts
  • crates/biome_js_analyze/tests/specs/nursery/noBaseToString/validIgnoredTypeNames.options.json
  • crates/biome_js_analyze/tests/specs/nursery/noBaseToString/validIgnoredTypeNames.ts
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_rule_options/src/no_base_to_string.rs

Comment thread crates/biome_cli/src/execute/migrate/eslint_typescript.rs
@dyc3 dyc3 force-pushed the dyc3/noBaseToString branch 2 times, most recently from 3ec6eb5 to a5aa0f9 Compare April 25, 2026 18:06

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs`:
- Around line 111-112: The current logic stops at the first bad interpolation
because it uses a find_map-style short-circuit; change it so every offending
interpolation is reported: in the JsTemplateExpression match arm
(AnyNoBaseToStringQuery::JsTemplateExpression) and the similar block around
lines 289-299, have run_template_expression(ctx, template) produce/return all
diagnostics (or an iterator of Signals) and collect them (e.g., via
.into_iter().flat_map(...) or .filter_map(...) and collect/extend into the
diagnostics Vec) instead of using find_map; update run_template_expression
internals if necessary to yield one entry per bad interpolation so all `${...}`
occurrences are reported.
- Around line 230-234: The logic treating direct
`.toString()`/`.toLocaleString()` calls as safe when an object only defines
`valueOf()` is incorrect; update the code paths that handle property_name
"toString" and "toLocaleString" (the match arm calling check_expression with
DiagnosticKind::BaseToString) to use a check that only considers an actual
custom toString member rather than has_custom_stringification_member() (which
conflates valueOf). Either add a new helper like has_custom_to_string_member()
that looks for an explicit toString/toLocaleString definition or change the
existing helper to accept a flag for direct-call semantics, and replace uses at
the shown match (and the other occurrences around lines ~487–495 and ~750–756)
so direct calls are only considered safe when a true custom
toString/toLocaleString exists.
- Around line 355-356: The current exemption checks in is_literal_expression
allow array literals to be treated as literals, which lets expressions like
String([{}]) bypass the rule; update is_literal_expression (and any helper
checks it uses) to stop treating array/array literal AST nodes as literal
expressions so arrays are not exempted—specifically, remove or change the branch
that matches array literal node kinds (e.g., ArrayExpression/ArrayLiteral) so
that is_literal_expression returns false for arrays while still returning true
for true primitives (strings, numbers, booleans, null, regex, template-only
literals) and for the same symbol names used at the call site (the expression
variable in no_base_to_string.rs).
- Around line 579-588: The combine_intersection function currently collapses any
non-Always certainty to Never and thus loses Sometimes; update
combine_intersection so it first returns Usefulness::Always if any constituent
is Always, otherwise returns Usefulness::Sometimes if any constituent is
Sometimes, and only returns Usefulness::Never when all constituents are Never;
update the iterator checks in combine_intersection to test for Always first,
then for Sometimes (e.g., using any on the iterator or by collecting into a
small temp vector/iterator clone) so that Sometimes is preserved through
intersections.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d54df07e-d800-4d1c-a500-01c757df5b76

📥 Commits

Reviewing files that changed from the base of the PR and between f055aab and 3ec6eb5.

📒 Files selected for processing (2)
  • crates/biome_cli/src/execute/migrate/eslint_typescript.rs
  • crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_cli/src/execute/migrate/eslint_typescript.rs

Comment on lines +111 to +112
AnyNoBaseToStringQuery::JsTemplateExpression(template) => {
run_template_expression(ctx, template).into_iter().collect()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Report every bad interpolation in a template.

find_map stops at the first failing ${...}, so a template like `${a}${b}` only yields one diagnostic even though Signals can carry several. The second offender gets a free pass.

Possible fix
-            AnyNoBaseToStringQuery::JsTemplateExpression(template) => {
-                run_template_expression(ctx, template).into_iter().collect()
-            }
+            AnyNoBaseToStringQuery::JsTemplateExpression(template) => {
+                run_template_expression(ctx, template).into_boxed_slice()
+            }

-fn run_template_expression(
+fn run_template_expression(
     ctx: &RuleContext<NoBaseToString>,
     node: &JsTemplateExpression,
-) -> Option<RuleState> {
+) -> Vec<RuleState> {
     if node.tag().is_some() {
-        return None;
+        return Vec::new();
     }

     node.elements()
         .into_iter()
-        .find_map(|element| match element {
+        .filter_map(|element| match element {
             AnyJsTemplateElement::JsTemplateElement(template_element) => {
                 template_element.expression().ok().and_then(|expression| {
                     check_expression(ctx, &expression, DiagnosticKind::BaseToString)
                 })
             }
             AnyJsTemplateElement::JsTemplateChunkElement(_) => None,
         })
+        .collect()
 }

Also applies to: 289-299

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs` around lines
111 - 112, The current logic stops at the first bad interpolation because it
uses a find_map-style short-circuit; change it so every offending interpolation
is reported: in the JsTemplateExpression match arm
(AnyNoBaseToStringQuery::JsTemplateExpression) and the similar block around
lines 289-299, have run_template_expression(ctx, template) produce/return all
diagnostics (or an iterator of Signals) and collect them (e.g., via
.into_iter().flat_map(...) or .filter_map(...) and collect/extend into the
diagnostics Vec) instead of using find_map; update run_template_expression
internals if necessary to yield one entry per bad interpolation so all `${...}`
occurrences are reported.

Comment on lines +230 to +234
match property_name {
"join" => check_expression(ctx, &object, DiagnosticKind::BaseArrayJoin),
"toString" | "toLocaleString" => {
check_expression(ctx, &object, DiagnosticKind::BaseToString)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

wc -l crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 123


🏁 Script executed:

head -n 300 crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | tail -n 100

Repository: biomejs/biome

Length of output: 3051


🏁 Script executed:

sed -n '220,250p' crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 1041


🏁 Script executed:

sed -n '475,510p' crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 1528


🏁 Script executed:

sed -n '740,765p' crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 803


🏁 Script executed:

sed -n '400,500p' crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 4255


🏁 Script executed:

sed -n '150,250p' crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 2937


🏁 Script executed:

sed -n '480,510p' crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 1267


🏁 Script executed:

sed -n '550,650p' crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 3220


🏁 Script executed:

grep -n "is_to_string_like_from_object" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 243


🏁 Script executed:

sed -n '642,750p' crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 3602


🏁 Script executed:

sed -n '1,100p' crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 3203


🏁 Script executed:

grep -n "DiagnosticKind::" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 902


valueOf() should not silence explicit .toString() calls.

Direct obj.toString() never consults valueOf(), but the coercion algorithm does. The analysis currently treats both contexts with the same code path via has_custom_stringification_member(), so objects with only valueOf() defined are incorrectly marked as safe for direct calls, creating false negatives.

Also applies to: 487–495, 750–756

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs` around lines
230 - 234, The logic treating direct `.toString()`/`.toLocaleString()` calls as
safe when an object only defines `valueOf()` is incorrect; update the code paths
that handle property_name "toString" and "toLocaleString" (the match arm calling
check_expression with DiagnosticKind::BaseToString) to use a check that only
considers an actual custom toString member rather than
has_custom_stringification_member() (which conflates valueOf). Either add a new
helper like has_custom_to_string_member() that looks for an explicit
toString/toLocaleString definition or change the existing helper to accept a
flag for direct-call semantics, and replace uses at the shown match (and the
other occurrences around lines ~487–495 and ~750–756) so direct calls are only
considered safe when a true custom toString/toLocaleString exists.

Comment thread crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs
Comment on lines +579 to +588
/// Combine the certainties of intersection constituents. If any constituent is `Always`, the result is `Always`. Otherwise, the result is `Never`.
fn combine_intersection(certainties: impl Iterator<Item = Usefulness>) -> Usefulness {
if certainties
.into_iter()
.any(|certainty| certainty == Usefulness::Always)
{
Usefulness::Always
} else {
Usefulness::Never
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's verify the current state of the file at the specified lines
head -n 590 crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | tail -n 25

Repository: biomejs/biome

Length of output: 1010


🏁 Script executed:

# Let's look at the Usefulness enum definition
rg -A 5 "enum Usefulness" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 116


🏁 Script executed:

# Search for other usages of combine_intersection to understand the context
rg -B 3 -A 3 "combine_intersection" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 841


🏁 Script executed:

# Look for tests related to combine_intersection
rg -B 5 -A 10 "combine_intersection\|#\[test\]" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | head -n 100

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Let's also examine the combine_union function to see the pattern for handling Sometimes
rg -B 3 -A 15 "fn combine_union" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 829


🏁 Script executed:

# Check the combine_tuple function to see how it handles Sometimes
rg -B 3 -A 15 "fn combine_tuple" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 709


🏁 Script executed:

# Let's understand the broader context - check if there are any comments or issues about intersection handling
rg -B 10 -A 20 "AggregateKind::Intersection" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | head -n 60

Repository: biomejs/biome

Length of output: 2276


🏁 Script executed:

# Check git history to see when this function was introduced or modified
git log --oneline -n 20 -- crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 76


🏁 Script executed:

# Search for any related issues or discussions mentioning intersection or usefulness
rg "intersection|Usefulness" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | head -n 30

Repository: biomejs/biome

Length of output: 1800


🏁 Script executed:

# Let's check the actual semantics by looking at how Usefulness is used in diagnostics
rg -B 5 -A 10 "Usefulness::Sometimes\|Usefulness::Never" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | head -n 80

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Let's verify the proposed fix doesn't break any existing tests
fd "no_base_to_string" crates/biome_js_analyze/tests --type f

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Search for test files related to no_base_to_string
fd -i "no_base_to_string" tests/

Repository: biomejs/biome

Length of output: 151


🏁 Script executed:

# Also check if there are snapshot tests for this rule
find tests -type d -name "*base*to*string*" -o -type d -name "*nursery*" | head -20

Repository: biomejs/biome

Length of output: 98


🏁 Script executed:

# Let's look at the actual code context more broadly to understand intersection semantics
cat -n crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | sed -n '550,650p'

Repository: biomejs/biome

Length of output: 3927


🏁 Script executed:

# Search for test specs directory structure
find . -type d -name "specs" -o -type d -name "nursery" 2>/dev/null | head -20

Repository: biomejs/biome

Length of output: 928


🏁 Script executed:

# Look for snapshot test files
find . -name "*.snap" -path "*no_base_to_string*" 2>/dev/null | head -10

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Check the broader repository structure for tests
ls -la crates/biome_js_analyze/tests/specs/ 2>/dev/null | head -20

Repository: biomejs/biome

Length of output: 712


🏁 Script executed:

# Look for test files for no_base_to_string rule
ls -la crates/biome_js_analyze/tests/specs/nursery/ | grep -i "base_to_string\|no_base"

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Check if there are any files in the nursery test directory
ls crates/biome_js_analyze/tests/specs/nursery/ | grep -i base

Repository: biomejs/biome

Length of output: 72


🏁 Script executed:

# Let's find all test files related to this rule
find crates/biome_js_analyze/tests -name "*no_base_to_string*"

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Let's explore the noBaseToString test directory
ls -la crates/biome_js_analyze/tests/specs/nursery/noBaseToString/

Repository: biomejs/biome

Length of output: 670


🏁 Script executed:

# Read the test files to understand what cases are covered
find crates/biome_js_analyze/tests/specs/nursery/noBaseToString/ -type f | head -20

Repository: biomejs/biome

Length of output: 610


🏁 Script executed:

# Let's read the invalid test cases to understand intersection type testing
cat crates/biome_js_analyze/tests/specs/nursery/noBaseToString/invalid.ts

Repository: biomejs/biome

Length of output: 603


🏁 Script executed:

# Read the snapshot to see expected diagnostics
cat crates/biome_js_analyze/tests/specs/nursery/noBaseToString/invalid.ts.snap | head -100

Repository: biomejs/biome

Length of output: 2869


🏁 Script executed:

# Let's understand the semantics better by looking at how Usefulness values are used in the rule
rg -B 10 "Usefulness::Sometimes\|Usefulness::Never\|Usefulness::Always" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | head -150

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Check if there are any comments explaining intersection semantics
rg -B 5 -A 5 "intersection\|Intersection" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | head -100

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Let's understand the semantics better by examining how the rule makes decisions
rg -B 20 "fn collect_certainty" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | head -50

Repository: biomejs/biome

Length of output: 705


🏁 Script executed:

# Check what Usefulness values mean by looking at where diagnostics are emitted
rg -B 5 "emit_diagnostic\|Usefulness" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | grep -A 3 -B 3 "Usefulness::"

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Let's look at the diagnostic emission logic to understand what Usefulness means
cat -n crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | sed -n '470,510p'

Repository: biomejs/biome

Length of output: 1967


🏁 Script executed:

# Let's trace through a specific example to understand the semantics
# Look at how the rule determines whether to emit a diagnostic
rg -B 10 -A 5 "RuleState\|if certainty" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | head -40

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Let's verify the semantics by checking when diagnostics are actually emitted
cat -n crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | sed -n '410,445p'

Repository: biomejs/biome

Length of output: 1591


🏁 Script executed:

# Let's also check the full context around the emit logic
cat -n crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | sed -n '395,430p'

Repository: biomejs/biome

Length of output: 1786


🏁 Script executed:

# Let's look at the end of the file to see the full diagnostic emission logic
cat -n crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs | sed -n '350,380p'

Repository: biomejs/biome

Length of output: 1104


🏁 Script executed:

# Let's check the rule implementation entry point
rg -B 5 -A 15 "impl Rule for NoBaseToString" crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs

Repository: biomejs/biome

Length of output: 844


Preserve Sometimes through intersections.

When analysing an intersection like (A | B) & C, a child can be Sometimes (uncertain). The current implementation collapses all non-Always cases to Never, losing this uncertainty and making the rule overstate certainty. With Sometimes collapsed away, (A | B) & C can emit a definite diagnostic when only one branch is actually problematic.

Proposed fix
 fn combine_intersection(certainties: impl Iterator<Item = Usefulness>) -> Usefulness {
-    if certainties
-        .into_iter()
-        .any(|certainty| certainty == Usefulness::Always)
-    {
-        Usefulness::Always
-    } else {
-        Usefulness::Never
-    }
+    let mut saw_sometimes = false;
+
+    for certainty in certainties {
+        match certainty {
+            Usefulness::Always => return Usefulness::Always,
+            Usefulness::Sometimes => saw_sometimes = true,
+            Usefulness::Never => {}
+        }
+    }
+
+    if saw_sometimes {
+        Usefulness::Sometimes
+    } else {
+        Usefulness::Never
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Combine the certainties of intersection constituents. If any constituent is `Always`, the result is `Always`. Otherwise, the result is `Never`.
fn combine_intersection(certainties: impl Iterator<Item = Usefulness>) -> Usefulness {
if certainties
.into_iter()
.any(|certainty| certainty == Usefulness::Always)
{
Usefulness::Always
} else {
Usefulness::Never
}
/// Combine the certainties of intersection constituents. If any constituent is `Always`, the result is `Always`. Otherwise, the result is `Never`.
fn combine_intersection(certainties: impl Iterator<Item = Usefulness>) -> Usefulness {
let mut saw_sometimes = false;
for certainty in certainties {
match certainty {
Usefulness::Always => return Usefulness::Always,
Usefulness::Sometimes => saw_sometimes = true,
Usefulness::Never => {}
}
}
if saw_sometimes {
Usefulness::Sometimes
} else {
Usefulness::Never
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs` around lines
579 - 588, The combine_intersection function currently collapses any non-Always
certainty to Never and thus loses Sometimes; update combine_intersection so it
first returns Usefulness::Always if any constituent is Always, otherwise returns
Usefulness::Sometimes if any constituent is Sometimes, and only returns
Usefulness::Never when all constituents are Never; update the iterator checks in
combine_intersection to test for Always first, then for Sometimes (e.g., using
any on the iterator or by collecting into a small temp vector/iterator clone) so
that Sometimes is preserved through intersections.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs`:
- Around line 697-707: The RegExp branch in the TypeData::Literal match is
incorrectly marking RegExp as using the base Object toString (setting saw_true);
update the Literal::RegExp(_) arm to indicate it does NOT use the base Object
stringification (e.g., return Some(false) or otherwise avoid setting saw_true)
so RegExp is treated like a type with a custom toString; adjust the
Literal::RegExp(_) match arm in no_base_to_string.rs (inside the
TypeData::Literal match) and ensure tests and DEFAULT_IGNORED_TYPE_NAMES
handling remain consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c21798cc-1825-4dae-9363-6e880efe16d1

📥 Commits

Reviewing files that changed from the base of the PR and between 3ec6eb5 and a5aa0f9.

📒 Files selected for processing (2)
  • crates/biome_cli/src/execute/migrate/eslint_typescript.rs
  • crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_cli/src/execute/migrate/eslint_typescript.rs

Comment thread crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs
@ematipico

ematipico commented Apr 26, 2026

Copy link
Copy Markdown
Member

It's very complicated

Would you mind explaining the logic of the PR in the description? Wouldn't be able to review it if you don't prepare us

@dyc3 dyc3 force-pushed the dyc3/noBaseToString branch from a5aa0f9 to 3e5502b Compare April 26, 2026 11:31
Comment thread .changeset/tidy-lamps-bake.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
.changeset/tidy-lamps-bake.md (1)

5-5: ⚡ Quick win

Show one tiny example in the changeset.

For new rules we usually include a concrete snippet so the release notes are scannable at a glance. A short inline example like String({}) or `${value}` would do nicely.

Possible tweak
-Added the nursery rule [`noBaseToString`](https://biomejs.dev/linter/rules/no-base-to-string/), which reports stringification sites that fall back to Object's default `"[object Object]"` formatting. The rule also supports the `ignoredTypeNames` option.
+Added the nursery rule [`noBaseToString`](https://biomejs.dev/linter/rules/no-base-to-string/), which reports stringification sites that fall back to Object's default `"[object Object]"` formatting, such as `String({})` or `` `${value}` ``. The rule also supports the `ignoredTypeNames` option.
As per coding guidelines, "changeset descriptions should focus on user-facing changes only; internal refactoring without user-visible behavior changes does not require a changeset" and "include examples with inline code snippets or code blocks for new rules".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.changeset/tidy-lamps-bake.md at line 5, Add a tiny inline example showing
the rule in action: mention the new linter rule noBaseToString and append a
short snippet like String({}) or `${value}` to demonstrate the reported
behavior; also note the supported option ignoredTypeNames so readers see usage
at a glance (e.g., "reports when String({}) or `${value}` yields '[object
Object]'; supports ignoredTypeNames"). Ensure the example is concise and appears
in the changeset description text.
crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs (1)

21-70: ⚡ Quick win

Add an options section for ignoredTypeNames.

The rule exposes an option, but the doc block does not yet state the default, show the JSON shape, or include a use_options example. That tends to leave the generated docs a bit skinny for users.

Suggested doc skeleton
     /// `${new CustomToString()}`;
     /// ```
+    ///
+    /// ## Options
+    ///
+    /// The default value of `ignoredTypeNames` is `["Error", "RegExp", "URL", "URLSearchParams"]`.
+    ///
+    /// ```json
+    /// {
+    ///   "linter": {
+    ///     "rules": {
+    ///       "nursery": {
+    ///         "noBaseToString": {
+    ///           "options": {
+    ///             "ignoredTypeNames": ["Error"]
+    ///           }
+    ///         }
+    ///       }
+    ///     }
+    ///   }
+    /// }
+    /// ```
+    ///
+    /// ```ts,use_options
+    /// const value: Error = new Error("boom");
+    /// String(value);
+    /// ```
     pub NoBaseToString {
As per coding guidelines, "Rule documentation for options must include a default value statement, a JSON options block, and a code example with the expect_diagnostic or use_options directive".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs` around lines
21 - 70, The doc comment for the NoBaseToString rule is missing an Options
section describing the ignoredTypeNames option; update the doc block above pub
NoBaseToString to add an "## Options" subsection that states the default value
ignoredTypeNames = ["Error", "RegExp", "URL", "URLSearchParams"], include a JSON
example showing the rule config shape (with "options": { "ignoredTypeNames":
["Error"] }), and add a TypeScript example using the use_options directive
(e.g., const value: Error = new Error("boom"); String(value);) so the generated
docs include the default, JSON options block, and a code example as required by
the guidelines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs`:
- Around line 522-529: The safety check incorrectly treats toLocaleString as
proof of custom stringification; update has_custom_stringification_member (and
callers like is_to_string_like_from_object and the AnalysisMode::ToString match)
to only consider toString and valueOf as valid custom stringification members
for String(x)/`${x}` coercion and ignore toLocaleString entirely, so objects
with only toLocaleString no longer count as safe and will fall through to
Usefulness::Always where appropriate.

---

Nitpick comments:
In @.changeset/tidy-lamps-bake.md:
- Line 5: Add a tiny inline example showing the rule in action: mention the new
linter rule noBaseToString and append a short snippet like String({}) or
`${value}` to demonstrate the reported behavior; also note the supported option
ignoredTypeNames so readers see usage at a glance (e.g., "reports when
String({}) or `${value}` yields '[object Object]'; supports ignoredTypeNames").
Ensure the example is concise and appears in the changeset description text.

In `@crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs`:
- Around line 21-70: The doc comment for the NoBaseToString rule is missing an
Options section describing the ignoredTypeNames option; update the doc block
above pub NoBaseToString to add an "## Options" subsection that states the
default value ignoredTypeNames = ["Error", "RegExp", "URL", "URLSearchParams"],
include a JSON example showing the rule config shape (with "options": {
"ignoredTypeNames": ["Error"] }), and add a TypeScript example using the
use_options directive (e.g., const value: Error = new Error("boom");
String(value);) so the generated docs include the default, JSON options block,
and a code example as required by the guidelines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d24bca95-05f4-4a3f-8dc8-ab36e5b2358a

📥 Commits

Reviewing files that changed from the base of the PR and between 3e5502b and 6ad86c2.

⛔ Files ignored due to path filters (1)
  • crates/biome_js_analyze/tests/specs/nursery/noBaseToString/invalid.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (4)
  • .changeset/tidy-lamps-bake.md
  • crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs
  • crates/biome_module_graph/src/js_module_info/collector.rs
  • crates/biome_module_graph/tests/spec_tests.rs

Comment thread crates/biome_js_analyze/src/lint/nursery/no_base_to_string.rs
@dyc3 dyc3 merged commit 83f7385 into main May 4, 2026
31 checks passed
@dyc3 dyc3 deleted the dyc3/noBaseToString branch May 4, 2026 23:10
@github-actions github-actions Bot mentioned this pull request May 4, 2026
OIRNOIR pushed a commit to OIRNOIR/YouTube-Helper-Server that referenced this pull request May 12, 2026
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [@biomejs/biome](https://biomejs.dev) ([source](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome)) | imports | patch | [`2.4.14` -> `2.4.15`](https://renovatebot.com/diffs/npm/@biomejs%2fbiome/2.4.14/2.4.15) |

---

### Release Notes

<details>
<summary>biomejs/biome (@&#8203;biomejs/biome)</summary>

### [`v2.4.15`](https://github.com/biomejs/biome/blob/HEAD/packages/@&#8203;biomejs/biome/CHANGELOG.md#2415)

[Compare Source](https://github.com/biomejs/biome/compare/@biomejs/biome@2.4.14...@biomejs/biome@2.4.15)

##### Patch Changes

- [#&#8203;9394](biomejs/biome#9394) [`ba3480e`](biomejs/biome@ba3480e) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added the nursery rule [`useTestHooksInOrder`](https://biomejs.dev/linter/rules/use-test-hooks-in-order) in the `test` domain. The rule enforces that Jest/Vitest lifecycle hooks (`beforeAll`, `beforeEach`, `afterEach`, `afterAll`) are declared in the order they execute, making test setup and teardown easier to reason about.

- [#&#8203;10254](biomejs/biome#10254) [`e0a54cc`](biomejs/biome@e0a54cc) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added a new nursery rule [`useVueNextTickPromise`](https://biomejs.dev/linter/rules/use-vue-next-tick-promise/), which enforces Promise syntax when using Vue `nextTick`.

  For example, the following snippet triggers the rule:

  ```js
  import { nextTick } from "vue";

  nextTick(() => {
    updateDom();
  });
  ```

- [#&#8203;10219](biomejs/biome#10219) [`64aee45`](biomejs/biome@64aee45) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added a new nursery rule [`noVueVOnNumberValues`](https://biomejs.dev/linter/rules/no-vue-v-on-number-values/), that disallows deprecated number modifiers on Vue `v-on` directives.

  For example, the following snippet triggers the rule:

  ```vue
  <input @&#8203;keyup.13="submit" />
  ```

- [#&#8203;10195](biomejs/biome#10195) [`7b8d4e1`](biomejs/biome@7b8d4e1) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added the new nursery rule [`useVueValidVFor`](https://biomejs.dev/linter/rules/use-vue-valid-v-for/), which validates Vue `v-for` directives and reports invalid aliases, missing component keys, and keys that do not use iteration variables.

- [#&#8203;10238](biomejs/biome#10238) [`1110256`](biomejs/biome@1110256) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added the recommended nursery rule [`noVueImportCompilerMacros`](https://biomejs.dev/linter/rules/no-vue-import-compiler-macros/), which disallows importing Vue compiler macros such as `defineProps` from `vue` because they are automatically available.

- [#&#8203;10201](biomejs/biome#10201) [`1a08f89`](biomejs/biome@1a08f89) Thanks [@&#8203;realknove](https://github.com/realknove)! - Fixed [#&#8203;10193](biomejs/biome#10193): `style/useReadonlyClassProperties` no longer reports class properties as readonly-able when they are assigned inside arrow callbacks nested in class property initializers.

- [#&#8203;9574](biomejs/biome#9574) [`3bd2b6a`](biomejs/biome@3bd2b6a) Thanks [@&#8203;Conaclos](https://github.com/Conaclos)! - Fixed [#&#8203;9530](biomejs/biome#9530). The diagnostics of [`organizeImports`](https://biomejs.dev/assist/actions/organize-imports/) are now more detailed and more precise. They are also better at localizing where the issue is.

- [#&#8203;10205](biomejs/biome#10205) [`a704a6c`](biomejs/biome@a704a6c) Thanks [@&#8203;Conaclos](https://github.com/Conaclos)! - Fixed [#&#8203;10185](biomejs/biome#10185). [\`organizeImports](https://biomejs.dev/assist/actions/organize-imports/) now errors when it encounters an unknown predefined group.

  The following configuration is now reported as invalid because `:INEXISTENT:` is an unknown predefined group.

  ```json
  {
    "assist": {
      "actions": {
        "source": {
          "organizeImports": { "options": { "groups": [":INEXISTENT:"] } }
        }
      }
    }
  }
  ```

- [#&#8203;10052](biomejs/biome#10052) [`b565bed`](biomejs/biome@b565bed) Thanks [@&#8203;minseong0324](https://github.com/minseong0324)! - Improved [`noMisleadingReturnType`](https://biomejs.dev/linter/rules/no-misleading-return-type/): it now flags union annotations whose extra variants are never returned, and suggests the narrower type (e.g. `string | null` → `string`).

  These functions are now reported because `null` and `number` are included in the return annotations but never returned:

  ```ts
  function getUser(): string | null {
    return "hello";
  } // null is never returned
  function getCode(): string | number {
    return "hello";
  } // number is never returned
  ```

- [#&#8203;10213](biomejs/biome#10213) [`ac30057`](biomejs/biome@ac30057) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Fixed [#&#8203;9450](biomejs/biome#9450): HTML and Vue element formatting now preserves child line breaks when an element contains another element child on its own line, instead of collapsing the child element onto the same line.

- [#&#8203;10275](biomejs/biome#10275) [`9ee6c03`](biomejs/biome@9ee6c03) Thanks [@&#8203;solithcy](https://github.com/solithcy)! - Fixed [#&#8203;10274](biomejs/biome#10274): Svelte templates with missing expressions no longer parsed as `HtmlBogusElement`

- [#&#8203;10143](biomejs/biome#10143) [`56798a7`](biomejs/biome@56798a7) Thanks [@&#8203;minseong0324](https://github.com/minseong0324)! - [`noMisleadingReturnType`](https://biomejs.dev/linter/rules/no-misleading-return-type/) now detects misleading return type annotations when object literal properties are initialized with `as const`.

  This function is now reported because the return annotation widens a property initialized with `as const`:

  ```ts
  function f(): { value: string } {
    return { value: "text" as const };
  }
  ```

- [#&#8203;10143](biomejs/biome#10143) [`56798a7`](biomejs/biome@56798a7) Thanks [@&#8203;minseong0324](https://github.com/minseong0324)! - [`noUselessTypeConversion`](https://biomejs.dev/linter/rules/no-useless-type-conversion/) now detects redundant conversions on object literal properties initialized with `as const`.

  This conversion is now reported because `message.value` is inferred as a string literal:

  ```ts
  const message = { value: "text" as const };
  String(message.value);
  ```

- [#&#8203;9807](biomejs/biome#9807) [`0ae5840`](biomejs/biome@0ae5840) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added the new nursery rule [`useThisInClassMethods`](https://biomejs.dev/linter/rules/use-this-in-class-methods/), based on ESLint's `class-methods-use-this`.

  The rule now reports instance methods, getters, setters, and function-valued instance fields that do not use `this`, and `biome migrate eslint` preserves the supported `ignoreMethods`, `ignoreOverrideMethods`, and `ignoreClassesWithImplements` options.

  **Invalid**:

  ```js
  class Foo {
    bar() {
      // does not use `this`, invalid
      console.log("Hello Biome");
    }
  }
  ```

- [#&#8203;10258](biomejs/biome#10258) [`e7b18f7`](biomejs/biome@e7b18f7) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Improved linter performance by narrowing the query nodes for several lint rules, reducing how often they are evaluated.

- [#&#8203;10273](biomejs/biome#10273) [`04e22a1`](biomejs/biome@04e22a1) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Fixed [#&#8203;10271](biomejs/biome#10271): The HTML parser now correctly parses `of` as text content when in text contexts.

- [#&#8203;9838](biomejs/biome#9838) [`83f7385`](biomejs/biome@83f7385) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added the nursery rule [`noBaseToString`](https://biomejs.dev/linter/rules/no-base-to-string/), which reports stringification sites that fall back to Object's default `"[object Object]"` formatting. The rule also supports the `ignoredTypeNames` option.

- [#&#8203;10143](biomejs/biome#10143) [`56798a7`](biomejs/biome@56798a7) Thanks [@&#8203;minseong0324](https://github.com/minseong0324)! - [`useExhaustiveSwitchCases`](https://biomejs.dev/linter/rules/use-exhaustive-switch-cases/) now checks switch statements over object literal properties initialized with `as const`.

  This switch is now reported because `status.kind` is inferred as the string literal `"ready"` but no case handles it:

  ```ts
  const status = { kind: "ready" as const };
  switch (status.kind) {
  }
  ```

- [#&#8203;10143](biomejs/biome#10143) [`56798a7`](biomejs/biome@56798a7) Thanks [@&#8203;minseong0324](https://github.com/minseong0324)! - [`useStringStartsEndsWith`](https://biomejs.dev/linter/rules/use-string-starts-ends-with/) now detects string index comparisons on object literal properties initialized with `as const`.

  This comparison is now reported because `message.value` is inferred as a string literal:

  ```ts
  const message = { value: "hello" as const };
  message.value[0] === "h";
  ```

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNzMuMyIsInVwZGF0ZWRJblZlciI6IjQzLjE3My4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://git.oirnoir.dev/OIRNOIR/YouTube-Helper-Server/pulls/12
OIRNOIR pushed a commit to OIRNOIR/YouTube-Helper-Client that referenced this pull request May 13, 2026
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [@biomejs/biome](https://biomejs.dev) ([source](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome)) | imports | patch | [`2.4.14` -> `2.4.15`](https://renovatebot.com/diffs/npm/@biomejs%2fbiome/2.4.14/2.4.15) |

---

### Release Notes

<details>
<summary>biomejs/biome (@&#8203;biomejs/biome)</summary>

### [`v2.4.15`](https://github.com/biomejs/biome/blob/HEAD/packages/@&#8203;biomejs/biome/CHANGELOG.md#2415)

[Compare Source](https://github.com/biomejs/biome/compare/@biomejs/biome@2.4.14...@biomejs/biome@2.4.15)

##### Patch Changes

- [#&#8203;9394](biomejs/biome#9394) [`ba3480e`](biomejs/biome@ba3480e) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added the nursery rule [`useTestHooksInOrder`](https://biomejs.dev/linter/rules/use-test-hooks-in-order) in the `test` domain. The rule enforces that Jest/Vitest lifecycle hooks (`beforeAll`, `beforeEach`, `afterEach`, `afterAll`) are declared in the order they execute, making test setup and teardown easier to reason about.

- [#&#8203;10254](biomejs/biome#10254) [`e0a54cc`](biomejs/biome@e0a54cc) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added a new nursery rule [`useVueNextTickPromise`](https://biomejs.dev/linter/rules/use-vue-next-tick-promise/), which enforces Promise syntax when using Vue `nextTick`.

  For example, the following snippet triggers the rule:

  ```js
  import { nextTick } from "vue";

  nextTick(() => {
    updateDom();
  });
  ```

- [#&#8203;10219](biomejs/biome#10219) [`64aee45`](biomejs/biome@64aee45) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added a new nursery rule [`noVueVOnNumberValues`](https://biomejs.dev/linter/rules/no-vue-v-on-number-values/), that disallows deprecated number modifiers on Vue `v-on` directives.

  For example, the following snippet triggers the rule:

  ```vue
  <input @&#8203;keyup.13="submit" />
  ```

- [#&#8203;10195](biomejs/biome#10195) [`7b8d4e1`](biomejs/biome@7b8d4e1) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added the new nursery rule [`useVueValidVFor`](https://biomejs.dev/linter/rules/use-vue-valid-v-for/), which validates Vue `v-for` directives and reports invalid aliases, missing component keys, and keys that do not use iteration variables.

- [#&#8203;10238](biomejs/biome#10238) [`1110256`](biomejs/biome@1110256) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added the recommended nursery rule [`noVueImportCompilerMacros`](https://biomejs.dev/linter/rules/no-vue-import-compiler-macros/), which disallows importing Vue compiler macros such as `defineProps` from `vue` because they are automatically available.

- [#&#8203;10201](biomejs/biome#10201) [`1a08f89`](biomejs/biome@1a08f89) Thanks [@&#8203;realknove](https://github.com/realknove)! - Fixed [#&#8203;10193](biomejs/biome#10193): `style/useReadonlyClassProperties` no longer reports class properties as readonly-able when they are assigned inside arrow callbacks nested in class property initializers.

- [#&#8203;9574](biomejs/biome#9574) [`3bd2b6a`](biomejs/biome@3bd2b6a) Thanks [@&#8203;Conaclos](https://github.com/Conaclos)! - Fixed [#&#8203;9530](biomejs/biome#9530). The diagnostics of [`organizeImports`](https://biomejs.dev/assist/actions/organize-imports/) are now more detailed and more precise. They are also better at localizing where the issue is.

- [#&#8203;10205](biomejs/biome#10205) [`a704a6c`](biomejs/biome@a704a6c) Thanks [@&#8203;Conaclos](https://github.com/Conaclos)! - Fixed [#&#8203;10185](biomejs/biome#10185). [\`organizeImports](https://biomejs.dev/assist/actions/organize-imports/) now errors when it encounters an unknown predefined group.

  The following configuration is now reported as invalid because `:INEXISTENT:` is an unknown predefined group.

  ```json
  {
    "assist": {
      "actions": {
        "source": {
          "organizeImports": { "options": { "groups": [":INEXISTENT:"] } }
        }
      }
    }
  }
  ```

- [#&#8203;10052](biomejs/biome#10052) [`b565bed`](biomejs/biome@b565bed) Thanks [@&#8203;minseong0324](https://github.com/minseong0324)! - Improved [`noMisleadingReturnType`](https://biomejs.dev/linter/rules/no-misleading-return-type/): it now flags union annotations whose extra variants are never returned, and suggests the narrower type (e.g. `string | null` → `string`).

  These functions are now reported because `null` and `number` are included in the return annotations but never returned:

  ```ts
  function getUser(): string | null {
    return "hello";
  } // null is never returned
  function getCode(): string | number {
    return "hello";
  } // number is never returned
  ```

- [#&#8203;10213](biomejs/biome#10213) [`ac30057`](biomejs/biome@ac30057) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Fixed [#&#8203;9450](biomejs/biome#9450): HTML and Vue element formatting now preserves child line breaks when an element contains another element child on its own line, instead of collapsing the child element onto the same line.

- [#&#8203;10275](biomejs/biome#10275) [`9ee6c03`](biomejs/biome@9ee6c03) Thanks [@&#8203;solithcy](https://github.com/solithcy)! - Fixed [#&#8203;10274](biomejs/biome#10274): Svelte templates with missing expressions no longer parsed as `HtmlBogusElement`

- [#&#8203;10143](biomejs/biome#10143) [`56798a7`](biomejs/biome@56798a7) Thanks [@&#8203;minseong0324](https://github.com/minseong0324)! - [`noMisleadingReturnType`](https://biomejs.dev/linter/rules/no-misleading-return-type/) now detects misleading return type annotations when object literal properties are initialized with `as const`.

  This function is now reported because the return annotation widens a property initialized with `as const`:

  ```ts
  function f(): { value: string } {
    return { value: "text" as const };
  }
  ```

- [#&#8203;10143](biomejs/biome#10143) [`56798a7`](biomejs/biome@56798a7) Thanks [@&#8203;minseong0324](https://github.com/minseong0324)! - [`noUselessTypeConversion`](https://biomejs.dev/linter/rules/no-useless-type-conversion/) now detects redundant conversions on object literal properties initialized with `as const`.

  This conversion is now reported because `message.value` is inferred as a string literal:

  ```ts
  const message = { value: "text" as const };
  String(message.value);
  ```

- [#&#8203;9807](biomejs/biome#9807) [`0ae5840`](biomejs/biome@0ae5840) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added the new nursery rule [`useThisInClassMethods`](https://biomejs.dev/linter/rules/use-this-in-class-methods/), based on ESLint's `class-methods-use-this`.

  The rule now reports instance methods, getters, setters, and function-valued instance fields that do not use `this`, and `biome migrate eslint` preserves the supported `ignoreMethods`, `ignoreOverrideMethods`, and `ignoreClassesWithImplements` options.

  **Invalid**:

  ```js
  class Foo {
    bar() {
      // does not use `this`, invalid
      console.log("Hello Biome");
    }
  }
  ```

- [#&#8203;10258](biomejs/biome#10258) [`e7b18f7`](biomejs/biome@e7b18f7) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Improved linter performance by narrowing the query nodes for several lint rules, reducing how often they are evaluated.

- [#&#8203;10273](biomejs/biome#10273) [`04e22a1`](biomejs/biome@04e22a1) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Fixed [#&#8203;10271](biomejs/biome#10271): The HTML parser now correctly parses `of` as text content when in text contexts.

- [#&#8203;9838](biomejs/biome#9838) [`83f7385`](biomejs/biome@83f7385) Thanks [@&#8203;dyc3](https://github.com/dyc3)! - Added the nursery rule [`noBaseToString`](https://biomejs.dev/linter/rules/no-base-to-string/), which reports stringification sites that fall back to Object's default `"[object Object]"` formatting. The rule also supports the `ignoredTypeNames` option.

- [#&#8203;10143](biomejs/biome#10143) [`56798a7`](biomejs/biome@56798a7) Thanks [@&#8203;minseong0324](https://github.com/minseong0324)! - [`useExhaustiveSwitchCases`](https://biomejs.dev/linter/rules/use-exhaustive-switch-cases/) now checks switch statements over object literal properties initialized with `as const`.

  This switch is now reported because `status.kind` is inferred as the string literal `"ready"` but no case handles it:

  ```ts
  const status = { kind: "ready" as const };
  switch (status.kind) {
  }
  ```

- [#&#8203;10143](biomejs/biome#10143) [`56798a7`](biomejs/biome@56798a7) Thanks [@&#8203;minseong0324](https://github.com/minseong0324)! - [`useStringStartsEndsWith`](https://biomejs.dev/linter/rules/use-string-starts-ends-with/) now detects string index comparisons on object literal properties initialized with `as const`.

  This comparison is now reported because `message.value` is inferred as a string literal:

  ```ts
  const message = { value: "hello" as const };
  message.value[0] === "h";
  ```

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNzMuMyIsInVwZGF0ZWRJblZlciI6IjQzLjE3My4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://git.oirnoir.dev/OIRNOIR/YouTube-Helper-Client/pulls/3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-CLI Area: CLI A-Diagnostic Area: diagnostocis A-Linter Area: linter A-Project Area: project L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants