Skip to content

fix(eslint-plugin): support template literals in no-html-links rule#11907

Open
Injora wants to merge 2 commits intofacebook:mainfrom
Injora:fix/eslint-plugin-template-literals
Open

fix(eslint-plugin): support template literals in no-html-links rule#11907
Injora wants to merge 2 commits intofacebook:mainfrom
Injora:fix/eslint-plugin-template-literals

Conversation

@Injora
Copy link
Copy Markdown

@Injora Injora commented Apr 6, 2026

Pre-flight checklist

  • I have read the Contributing Guidelines on pull requests.
  • If this is a code change: I have written unit tests and/or added dogfooding pages to fully verify the new behavior.
  • If this is a new API or substantial change: the PR has an accompanying issue (closes #0000) and the maintainers have approved on my working plan.

Motivation

This pull request resolves a // TODO in the no-html-links ESLint rule regarding TemplateLiteral parsing. Previously, the rule could not statically determine the validity of a URL if a template literal contained interpolated JSX expressions (e.g., <a href={\https://example.com/docs/\${id}\`} />`), causing it to fail.

This change introduces a base URL prefix check. By evaluating the string segment before the first variable to ensure it contains a valid protocol (https://, mailto:, etc.), the parser can now correctly allow fully resolved external links written as template literals.

Test Plan

  • Updated packages/eslint-plugin/src/rules/no-html-links.ts to implement the prefix checking logic.
  • Moved the previously disabled test case for interpolated template literals to the valid assertions array in packages/eslint-plugin/src/rules/__tests__/no-html-links.test.ts.
  • Verified the fix by successfully running the unit tests: yarn test packages/eslint-plugin/src/rules/__tests__/no-html-links.test.ts.

Test links

Deploy preview: https://deploy-preview-_____--docusaurus-2.netlify.app/

Related issues/PRs

N/A

@Injora Injora requested review from Josh-Cena and slorber as code owners April 6, 2026 06:44
@meta-cla meta-cla bot added the CLA Signed Signed Facebook CLA label Apr 6, 2026
@netlify
Copy link
Copy Markdown

netlify bot commented Apr 6, 2026

[V2]

Built without sensitive environment variables

Name Link
🔨 Latest commit 9af66e6
🔍 Latest deploy log https://app.netlify.com/projects/docusaurus-2/deploys/69d3d12d7c6a3b0008fce9a8
😎 Deploy Preview https://deploy-preview-11907--docusaurus-2.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Copy Markdown
Collaborator

@slorber slorber left a comment

Choose a reason for hiding this comment

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

I don't think this is what I had in mind when adding this todo comment.

isFullyResolvedUrl("${prefix}dummy.com") is unlikely to be a good solution unless you can explain to me why. I'm pretty sure this creates false positives that are not currently covered by tests. More tests should be added to cover more complex template literal cases

const prefix = String(firstQuasi.value.raw);
if (
isFullyResolvedUrl(prefix) ||
isFullyResolvedUrl(`${prefix}dummy.com`)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't understand, what is dummy.com?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks for the feedback! You're right, the dummy.com hack was fragile.
I've refactored the rule to use a more robust staticPreEvaluate utility function that recursively resolves TemplateLiteral, Literal, and BinaryExpression (string concatenation) nodes at static analysis time. This allows us to correctly verify if a generated string is a fully-resolved external URL without relying on string prefixes.
I've also added several new test cases:
Statically evaluatable template literals (e.g., https://x.com/${"docu" + "saurus"}) are now correctly allowed.
String concatenation (e.g., "https://x.com/" + "docusaurus") is also supported.
Dynamic template literals (e.g., https://github.com/${repo}) are now explicitly tested to ensure they are still reported as invalid, since their final URL cannot be statically verified.
Let me know if this approach aligns better with what you had in mind :)

Comment on lines -81 to -87
{
// TODO we might want to make this test pass
// Can template literals be statically pre-evaluated? (Babel can do it)
// eslint-disable-next-line no-template-curly-in-string
code: '<a href={`https://x.com/${"docu" + "saurus"} ${"rex"}`}>Twitter</a>',
options: [{ignoreFullyResolved: true}],
errors: errorsJSX,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We don't have a unit test for when the template literals are dynamic and can't be resolved at static analysis time: we should add one to ensure it keeps failing

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

Labels

CLA Signed Signed Facebook CLA

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants