Skip to content

Conversation

@martinjlowm
Copy link

This is completely generated with Claude by prompting it to resolve our use case of requiring the valid case of "${{ inputs.abc }}". We use https://github.com/ArmaanT/cdkactions to programatically generate our GitHub workflows and Biome warns us consistently about this.

Summary

GitHub Actions uses ${{ ... }} syntax (double curly braces) which was incorrectly flagged as a template literal placeholder. This fix detects and skips patterns that have double opening braces followed by double closing braces, as these are GitHub Actions expressions, not JavaScript template literal mistakes.

Test Plan

Snapshot testing is implemented to account for the new cases.

@changeset-bot
Copy link

changeset-bot bot commented Jan 3, 2026

🦋 Changeset detected

Latest commit: c34533f

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

@github-actions github-actions bot added A-Linter Area: linter L-JavaScript Language: JavaScript and super languages labels Jan 3, 2026
@martinjlowm martinjlowm marked this pull request as ready for review January 3, 2026 18:57
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 3, 2026

Walkthrough

Replaces a byte-scan with a peekable iterator in the linter rule no-template-curly-in-string to handle both ${ ... } and GitHub Actions ${{ ... }} forms. Detects and consumes the extra { for double-curly sequences, scans for a closing }} and skips the region if found; if not found, falls back to single-brace handling and may report a diagnostic. Adds tests and documentation examples covering GitHub Actions double-curly expressions, including nested and complex expressions.

Suggested reviewers

  • ematipico

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarises the main change: excluding GitHub Actions expressions from the noTemplateCurlyInString lint rule.
Description check ✅ Passed The description clearly explains the problem, the fix, and testing approach, all directly related to the changeset.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8df9605 and c34533f.

⛔ Files ignored due to path filters (1)
  • crates/biome_js_analyze/tests/specs/suspicious/noTemplateCurlyInString/valid.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (3)
  • .changeset/swift-actions-sleep.md
  • crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs
  • crates/biome_js_analyze/tests/specs/suspicious/noTemplateCurlyInString/valid.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_js_analyze/tests/specs/suspicious/noTemplateCurlyInString/valid.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use inline rustdoc documentation for rules, assists, and their options
Use the dbg!() macro for debugging output in Rust tests and code
Use doc tests (doctest) format with code blocks in rustdoc comments; ensure assertions pass in tests

Files:

  • crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs
🧠 Learnings (7)
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Avoid string allocations by using `&str` or `TokenText` instead of `to_string()`

Applied to files:

  • crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Invalid code snippets in rule documentation must emit exactly one diagnostic

Applied to files:

  • crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Use `Markup!` macro for diagnostic messages and code action descriptions to ensure proper formatting

Applied to files:

  • crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Code blocks in rule documentation must specify a language identifier and be tagged with `expect_diagnostic` for invalid examples or remain untagged for valid examples

Applied to files:

  • crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs
📚 Learning: 2025-12-22T09:26:56.943Z
Learnt from: ematipico
Repo: biomejs/biome PR: 8537
File: crates/biome_js_analyze/src/lint/nursery/no_leaked_render.rs:167-210
Timestamp: 2025-12-22T09:26:56.943Z
Learning: When defining lint rules (declare_lint_rule!), only specify fix_kind if the rule implements an action(...) function. Rules that only emit diagnostics without a code fix should omit fix_kind. This applies to all Rust lint rule definitions under crates/.../src/lint (e.g., crates/biome_js_analyze/src/lint/...).

Applied to files:

  • crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : When formatting AST nodes, use mandatory tokens from the AST instead of hardcoding token strings (e.g., use `node.l_paren_token().format()` instead of `token("(")`)

Applied to files:

  • crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs
📚 Learning: 2025-12-21T21:15:03.796Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-12-21T21:15:03.796Z
Learning: For new lint rules in changesets, show an example of invalid case in inline code or code block

Applied to files:

  • crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs
🔇 Additional comments (3)
.changeset/swift-actions-sleep.md (1)

1-5: Changeset looks good!

Clear description of the fix, and the distinction between double curly braces (GitHub Actions) and single curly braces (template literals) is well explained.

crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs (2)

43-47: Documentation example is clear and helpful.

Good addition showing valid GitHub Actions syntax. The example correctly demonstrates the double curly brace syntax that should not trigger the lint rule.


70-117: Excellent refactoring with peekable iterator.

The logic correctly handles both regular template literals (${ ... }) and GitHub Actions expressions (${{ ... }}). The use of prev_was_close to detect consecutive closing braces is clever and correctly handles nested braces within GitHub Actions expressions.

The refactoring addresses ematipico's feedback about risky lookaheads—using next_if() on the peekable iterator is much cleaner and safer.


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.

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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 (1)
crates/biome_js_analyze/tests/specs/suspicious/noTemplateCurlyInString/valid.js (1)

17-21: Consider adding edge case test coverage.

The test cases cover common GitHub Actions patterns well, but consider adding tests for edge cases:

  • Nested braces: "${{ fromJSON('{\"key\": \"value\"}') }}"
  • Multiple expressions: "foo ${{ a }} bar ${{ b }}"
  • Mixed syntax: "${ foo } ${{ bar }}" (should flag the first, skip the second)

These would help ensure the implementation handles complex expressions correctly.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1550e73 and 70ea3a1.

⛔ Files ignored due to path filters (1)
  • crates/biome_js_analyze/tests/specs/suspicious/noTemplateCurlyInString/valid.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (2)
  • crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs
  • crates/biome_js_analyze/tests/specs/suspicious/noTemplateCurlyInString/valid.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use inline rustdoc documentation for rules, assists, and their options
Use the dbg!() macro for debugging output in Rust tests and code
Use doc tests (doctest) format with code blocks in rustdoc comments; ensure assertions pass in tests

Files:

  • crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs
🧠 Learnings (5)
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Avoid string allocations by using `&str` or `TokenText` instead of `to_string()`

Applied to files:

  • crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/*_analyze/**/src/lint/**/*.rs : Use `Markup!` macro for diagnostic messages and code action descriptions to ensure proper formatting

Applied to files:

  • crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs
📚 Learning: 2025-12-22T09:26:56.943Z
Learnt from: ematipico
Repo: biomejs/biome PR: 8537
File: crates/biome_js_analyze/src/lint/nursery/no_leaked_render.rs:167-210
Timestamp: 2025-12-22T09:26:56.943Z
Learning: When defining lint rules (declare_lint_rule!), only specify fix_kind if the rule implements an action(...) function. Rules that only emit diagnostics without a code fix should omit fix_kind. This applies to all Rust lint rule definitions under crates/.../src/lint (e.g., crates/biome_js_analyze/src/lint/...).

Applied to files:

  • crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/tests/specs/**/*.jsonc : Use `.jsonc` format for test files containing multiple code snippets, where each snippet is a string in an array

Applied to files:

  • crates/biome_js_analyze/tests/specs/suspicious/noTemplateCurlyInString/valid.js
📚 Learning: 2026-01-02T14:58:16.536Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2026-01-02T14:58:16.536Z
Learning: Applies to crates/biome_analyze/**/tests/specs/**/* : Create test files with `invalid` and `valid` prefixes to represent code that should and should not trigger the rule

Applied to files:

  • crates/biome_js_analyze/tests/specs/suspicious/noTemplateCurlyInString/valid.js
🔇 Additional comments (2)
crates/biome_js_analyze/src/lint/suspicious/no_template_curly_in_string.rs (2)

43-47: Helpful documentation addition.

The example clearly illustrates the GitHub Actions pattern that should be excluded from diagnostics.


70-70: Correct use of peekable iterator.

The peekable wrapper is necessary for the lookahead logic that follows. The performance overhead is negligible for this use case.

@martinjlowm martinjlowm force-pushed the claude/fix-template-curly-lint-cEewq branch from 70ea3a1 to f44fcfa Compare January 3, 2026 19:16
@martinjlowm
Copy link
Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 3, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

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

Thank you! The changeset is missing, please add one. This information is available in our PR template, which was removed from the AI bot. Please restore it, because it removed sections that need to be filled.

I'm sorry if this comes across harshly, but I have seen a lot of missing info lately because of this.

@martinjlowm
Copy link
Author

Thank you! The changeset is missing, please add one. This information is available in our PR template, which was removed from the AI bot. Please restore it, because it removed sections that need to be filled.

I'm sorry if this comes across harshly, but I have seen a lot of missing info lately because of this.

No problem :) - I'll look into it

@martinjlowm martinjlowm force-pushed the claude/fix-template-curly-lint-cEewq branch from f44fcfa to 8df9605 Compare January 3, 2026 20:02
…tring

GitHub Actions uses `${{ ... }}` syntax (double curly braces) which was
incorrectly flagged as a template literal placeholder. This fix detects
and skips patterns that have double opening braces followed by double
closing braces, as these are GitHub Actions expressions, not JavaScript
template literal mistakes.
@martinjlowm martinjlowm force-pushed the claude/fix-template-curly-lint-cEewq branch from 8df9605 to c34533f Compare January 3, 2026 20:19
@martinjlowm martinjlowm requested a review from ematipico January 3, 2026 20:24
Copy link
Contributor

@dyc3 dyc3 left a comment

Choose a reason for hiding this comment

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

Looks good.

Comment on lines +43 to +48
/// GitHub Actions expressions using double curly braces are also valid:
///
/// ```js
/// const a = "${{ inputs.abc }}";
/// ```
///
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Not sure if its worth calling this out in the rule docs. Feels like an implementation detail.

@codspeed-hq
Copy link

codspeed-hq bot commented Jan 3, 2026

CodSpeed Performance Report

Merging #8665 will not alter performance

Comparing martinjlowm:claude/fix-template-curly-lint-cEewq (c34533f) with main (1550e73)

Summary

✅ 58 untouched
⏩ 95 skipped1

Footnotes

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

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

Labels

A-Linter Area: linter L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants