Skip to content

Add template-lint-disable pragma for template regions#2627

Open
NullVoxPopuli-ai-agent wants to merge 9 commits intoember-cli:masterfrom
NullVoxPopuli-ai-agent:feat/template-lint-disable-pragma
Open

Add template-lint-disable pragma for template regions#2627
NullVoxPopuli-ai-agent wants to merge 9 commits intoember-cli:masterfrom
NullVoxPopuli-ai-agent:feat/template-lint-disable-pragma

Conversation

@NullVoxPopuli-ai-agent
Copy link
Copy Markdown

Summary

  • Adds a template-lint-disable comment directive that suppresses lint errors on the next line in template regions of gjs/gts/hbs files
  • Works like eslint-disable-next-line but uses the template-lint-disable naming convention familiar to ember-template-lint users
  • Supports all template comment formats: {{! ... }}, {{!-- ... --}}, and <!-- ... -->

Usage

{{! disable all rules on the next line }}
{{! template-lint-disable }}
{{test}}

{{! disable a specific rule }}
{{!-- template-lint-disable no-undef --}}
{{test}}

<!-- also works with HTML comments -->
<!-- template-lint-disable no-undef -->
{{test}}

Rule names can be specified as either:

  • Direct eslint rule IDs: no-undef, ember/template-no-bare-strings
  • Template-lint style names: no-bare-strings (maps to ember/template-no-bare-strings)

Implementation

Wraps the existing noop processor with a new processor that:

  1. In preprocess: parses source text for template-lint-disable comments and records which lines/rules should be suppressed
  2. In postprocess: filters out suppressed messages before returning results

The processor is registered under the same noop key so all existing config references (ember/noop) continue to work unchanged.

Test plan

  • Disables all rules on next line with mustache comment {{! template-lint-disable }}
  • Disables all rules on next line with mustache block comment {{!-- template-lint-disable --}}
  • Disables all rules on next line with HTML comment <!-- template-lint-disable -->
  • Disables a specific rule by name
  • Only disables the next line, not subsequent lines
  • Does not suppress unrelated rules when a specific rule is named
  • Supports template-lint rule name format (maps to ember/template- prefix)
  • Supports multiple rule names
  • Works with multiple disable comments in the same file
  • All 28 existing parser tests continue to pass
  • All existing template-no-html-comments and template-no-bare-strings tests pass (these already reference template-lint-disable as valid comments)

🤖 Generated with Claude Code

Implements a `template-lint-disable` comment directive that suppresses
lint errors on the next line in template regions of gjs/gts/hbs files.
This works like `eslint-disable-next-line` but uses the template-lint
naming convention familiar to ember-template-lint users.

Supported comment formats:
  {{! template-lint-disable }}
  {{!-- template-lint-disable rule-name --}}
  <!-- template-lint-disable rule-name -->

Rule names can be specified as either eslint rule IDs (e.g. `no-undef`)
or with `ember/template-` prefix (e.g. `ember/template-no-bare-strings`).
When no rule is specified, all rules are suppressed on the next line.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
NullVoxPopuli and others added 4 commits March 22, 2026 00:49
Only mustache comments ({{! ... }} and {{!-- ... --}}) are supported
for the template-lint-disable directive.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Apply the processor to hbs files in base configs so template-lint-disable
works in standalone .hbs templates, not just gjs/gts files. Also handle
template AST edge case where TextNodes start on the comment line.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Split the single regex with overlapping quantifiers into two separate
patterns for mustache comments and mustache block comments. Each uses
a specific character class ([\w\s,/-]*) that cannot cause polynomial
backtracking. Also removes the -+$ cleanup regex since the block
comment pattern now captures only the rules content without trailing
dashes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Reorder regex char class per unicorn/better-regex
- Use for-of loop per unicorn/no-for-loop
- Move initHbsESLint to outer scope per unicorn/consistent-function-scoping
- Disable import/no-unresolved for ember-eslint-parser/hbs (valid export)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Revert recommended.mjs to original (cannot change recommended config)
- Keep noop processor unchanged; export template-lint-disable as a
  separate processor (ember/template-lint-disable) to avoid breaking
  existing gjs/gts users
- Wire hbs files to use the new processor by default in base configs
  (hbs had no prior config, so this is additive)
- Move hbs tests to a separate hbs-parser-test.js file
- Update gjs template-lint-disable tests to explicitly use the new
  processor via initESLintWithTemplateLintDisable()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
NullVoxPopuli and others added 3 commits March 22, 2026 01:22
Cannot change existing configs; the template-lint-disable processor
is available for users to opt into via their own config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
No config changes in this PR; the template-lint-disable processor
is exported for users to opt into in their own config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@NullVoxPopuli NullVoxPopuli left a comment

Choose a reason for hiding this comment

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

this will need a perf test / benchmark

Copy link
Copy Markdown
Contributor

@johanrd johanrd left a comment

Choose a reason for hiding this comment

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

Claude Review of template-lint-disable processor

Claude reviewed the final state across all 9 commits. The core approach — wrapping the noop processor to filter messages in postprocess — is sound. A few issues to address:

Must fix

1. Add early-exit in preprocess for files without directives

Every file pays the cost of text.split('\n') + regex matching even when no template-lint-disable comment exists. Possible fix:

preprocess(text, fileName) {
  if (!text.includes('template-lint-disable')) {
    fileDisableDirectives.delete(fileName);
    return [text];
  }
  // ... existing parsing logic
}

This makes the overhead near-zero for the common case (no disable comments).

2. commentLine suppression is over-broad

if (message.line !== directive.commentLine && message.line !== directive.nextLine) {

This suppresses errors on the comment line itself, not just the next line. That means:

Hello world {{! template-lint-disable }}

Would suppress the no-bare-strings error for "Hello world" — the error comes before the comment but is on the same line. The comment explains this is for a TextNode edge case where the newline after the comment belongs to the next TextNode. But it's a blunt instrument.

Options:

  • (a) Accept this and document it (ESLint's own eslint-disable-line also suppresses the whole line)
  • (b) Only suppress commentLine when message.column > commentEndColumn (would need to track comment position)

I'd go with (a) and document it, but it should be a conscious choice.

3. Misleading test name in gjs tests

it('supports template-lint rule name format (maps to ember/ prefix)', async () => {
    // ...
    {{! template-lint-disable no-undef }}

This test passes because no-undef matches directly as an ESLint rule ID (ruleId === disableRuleName), not because of the ember/template- prefix mapping. The test name claims it's testing the mapping but it isn't. The hbs test with no-bare-stringsember/template-no-bare-strings correctly tests this. Fix the gjs test name or use an actual ember/template-* rule.

Should fix

4. Semantic mismatch with ember-template-lint

In ember-template-lint, {{! template-lint-disable }} disables from that point for the rest of the scope (until template-lint-enable). This PR gives it "next line only" semantics (like eslint-disable-next-line).

Users migrating from ember-template-lint will expect disable/enable pairs to work:

{{! template-lint-disable }}
  line 1
  line 2
  line 3
{{! template-lint-enable }}

But only line 1 would be suppressed here.

Consider either:

  • (a) Rename to template-lint-disable-next-line to avoid confusion, or
  • (b) Implement range-based disable/enable to match ember-template-lint behavior, or
  • (c) Document the difference prominently

5. Regex doesn't support @ for scoped plugin rules

[\s\w,/-] can't match @typescript-eslint/no-unused-vars or @ember/no-classic-classes. Adding @ to the character class would fix this:

/{{!\s+template-lint-disable([\s\w,/@-]*)}}/g

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants