Skip to content

Superseded - #2

Closed
ksom wants to merge 34 commits into
mainfrom
improve/rules-and-packaging
Closed

ksom wants to merge 34 commits into
mainfrom
improve/rules-and-packaging

Conversation

@ksom

@ksom ksom commented Sep 19, 2026 •

Copy link
Copy Markdown
Owner

Superseded by a new pull request from the same branch.

ksom added 30 commits August 9, 2026 18:07
…ansform check

GritQL metavariables cannot bind implements clauses, so decorator+class
matching was impossible for implements-classes. Match the @pipe decorator
and require the file to reference PipeTransform instead — covers export,
extends, and implements-other class shapes. The rule now actually fires
on real Angular code (it previously never matched exported classes).
Decorator+class snippets never matched exported classes because GritQL
treats the export modifier as part of the statement; add export class
variants. Align semantics with @angular-eslint: @Injectable and @pipe
only support ngOnDestroy; @component and @directive support all hooks
(directives do receive view hooks). Detect hooks as bare identifiers so
any signature shape matches (: void, async, parameters).
Only flag classes decorated with @component. Plain TS classes ending in
Component (e.g. React or shared code in a monorepo) are no longer flagged.
Uses export-aware decorator+class patterns including extends variants.
Only flag classes decorated with @directive, using export-aware
decorator+class patterns including extends variants.
Only flag classes decorated with @Injectable. NestJS services and plain
TS classes ending in Service are no longer flagged. Uses export-aware
decorator+class patterns including extends variants.
Only flag constructor parameter properties inside @Component/@Directive/
@Injectable/@pipe classes. Plain TS classes using parameter properties
for values (and NestJS constructor DI) are no longer flagged.
Since Angular 19 standalone is the default and the v20 style guide
recommends omitting the property, so flagging its absence produced an
error on every idiomatic modern component, directive, and pipe.
GritQL cannot compare list order, so the rule could only detect the
presence of NgModule metadata arrays and fired on every NgModule —
including sorted and empty arrays — while its message claimed the array
was unsorted. The upstream @angular-eslint rule was likewise deprecated
and removed. Tracked as skipped in rules-to-migrate.json.
…param) shapes

The CLI generates ngOnInit(): void {} — the exact form the rule missed.
Add return-type variants for every hook and parameterized variants for
ngOnChanges, the only hook that takes an argument.
… outputs

Add @output() name: T and @output() name!: T shapes and accept a
decorator argument. readonly properties remain a documented false
negative (GritQL cannot match the readonly modifier on properties).
Implement the input/output pairing the doc always described: capture the
output base name via regex and require a matching @input() in the file.
Unpaired *Change outputs (Material-style events) are no longer flagged.
Biome has no linter.rules.plugins key — configs generated by init/merge
were rejected with 'unknown key' and severity per rule can only be set
inside each .grit via register_diagnostic. init now writes
plugins: ['./node_modules/biome-plugin-angular'] (plugin entries are
paths relative to the config, verified against the packed tarball, and
extends of the preset resolves them from the project root). The strict
preset is removed since severity differentiation is impossible; remove
cleans up the invalid key written by earlier versions.
…endency

The rules CLI command reads rules-to-migrate.json but the file was
excluded from the npm tarball (verified crash on the installed package),
and all README rule-doc links 404'd on npmjs.com without docs/. Declare
@biomejs/biome >=2.4.8 <3 as a peer dependency instead of only a dev
dependency, and drop the reference to a schema file that never existed.
The runner returned an empty diagnostics array when biome crashed or
produced unparseable output, letting every 'valid' test pass vacuously.
Throw instead, and reject non-plugin diagnostics (parse/config errors)
so a broken fixture can never masquerade as a passing test.
Packs the plugin as npm would publish it, installs it into a fresh
project, and lints through every documented onboarding path: direct
plugins entry, cli init, and extends of the preset. This is the only
test that exercises biome-manifest.jsonc resolution and the package
files whitelist — all other tests load .grit files directly.
CI never ran the integration suite — the only tests exercising the real
biome binary. Add it to the test matrix plus a non-blocking job against
@biomejs/biome@latest as an early warning for upstream changes.

The release workflow published on tag push with plain npm publish and
the archived actions/create-release, while package.json configured
semantic-release that nothing ever invoked — so CHANGELOG.md stayed
empty and versions never bumped. Release now runs semantic-release on
push to main after the full test suite.
The ts-morph pattern-matcher had drifted from the GritQL rules in both
directions, certifying behavior the plugin does not have. Relabel it as
the reference-semantics spec (integration tests on the real binary are
the authority), delete the sort-ngmodule matcher, align prefer-standalone
(only standalone: false), contextual-lifecycle (upstream semantics), and
pipe-prefix (prefix must be followed by an uppercase letter).
…tions

README now shows the only valid configs (plugins path or extends of the
preset), documents that severities are fixed in the .grit files, drops
the removed strict preset and sort-ngmodule rule, and replaces the
'heuristic/non-functional' caveats with the real remaining limitations
(implements clauses and readonly properties, both pinned by it.fails
tests). CLAUDE.md gets the verified GritQL pattern-writing rules. Track
the three suffix rules in rules-to-migrate.json.
The implements-clause branches (over half the rule) never matched —
GritQL cannot bind an implements clause — so any class with an
implements clause was invisible, including the partial-implements case
the rule exists for. Match each lifecycle method instead and require the
corresponding interface to be referenced in the file. Diagnostics now
name the exact missing interface. Also matches : void and parameterized
ngOnChanges signatures.
…ures

Every violation test now asserts on the diagnostic message, not just the
count. New fixtures: attribute and multi-selector components, alias
options objects, transform inputs, readonly outputs and queries, getter
host bindings, setter inputs, and partial-implements lifecycle classes.
Known engine gaps (multi-selector false positive, per-file interface
check) are pinned with it.fails sentinels that flip when Biome lifts
the limitation.
Every doc now shows the only valid Biome config, states the fixed
severity from its .grit, and documents the real matching semantics:
flipped prefer-standalone, functional contextual-lifecycle and
use-pipe-transform-interface, decorator gating, per-file checks, and
the implements/readonly engine limitations. Removed the fictional
configurable-prefix claims and an unverifiable schematic reference.
All 109 integration tests pass unchanged on 2.5.5. Migrate the repo
config (rules.recommended -> preset). Test-runner configs keep the
deprecated rules.recommended form, the only shape valid on both the
2.4.8 peer-dependency floor and current releases. 2.5.0+ also unlocks
fixable GritQL diagnostics and per-plugin file scoping for future use.
Snippet patterns only matched the exact shape they spelled out: a decorated
class with an implements clause, a readonly member, or a method with an
override modifier was invisible. On a CLI-generated Angular app every
component implements OnInit, so the suffix rules, contextual-lifecycle and
prefer-inject-function missed most real code.

Rules now match through Biome's native AST nodes (JsClassDeclaration,
JsExport, JsPropertyClassMember, JsMethodClassMember...), which expose
decorators, implements clauses, modifiers and members as fields. Effects:

- implements, extends, generics and export default classes are matched
- readonly properties are matched
- use-lifecycle-interface and use-pipe-transform-interface check the
  implements clause of each class instead of the whole file
- prefer-inject-function flags every constructor parameter of a decorated
  class, like @angular-eslint/prefer-inject, not only parameter properties
- no-input-rename / no-output-rename / no-output-on-prefix cover the
  input(), model(), output() and outputFromObservable() functions
- component-selector accepts comma-separated selector lists
- every message starts with "[rule-name]" so the suppression name
  (lint/plugin/rule-name) is discoverable; metavariables are not
  interpolated in messages

The it.fails sentinels for these engine limitations now pass and were
flipped to regular tests. The test runner enables
unsafeParameterDecoratorsEnabled, which Angular constructors need.
Biome 2.5 plugins can rewrite code. The empty hook is deleted with a safe
fix (biome lint --write) when the class does not implement the matching
interface. When it does, deleting the method alone fails compilation with
TS2420, so that case only reports and asks to remove both.

Biome pairs rewrites with diagnostics by order of appearance in the file,
so a single pattern cannot mix fixable and non-fixable branches: the rule
is split in two files with the same stem, which Biome loads under one
plugin name (one suppression name for users).

The rule also moved to the native method node, so hooks with modifiers
(protected override, async) and comment-only bodies are matched.
Port of @angular-eslint/directive-selector with the Angular CLI defaults:
camelCase attribute selector with the app prefix. Comma-separated lists are
accepted when every entry is valid.
Port of @angular-eslint/no-output-native. Covers @output() on every property
shape (readonly included) and the output()/outputFromObservable() functions.
Port of @angular-eslint/no-conflicting-lifecycle: ngDoCheck and ngOnChanges
must not be declared on the same class.
Port of @angular-eslint/no-lifecycle-call. Explicit hook calls are flagged;
super.ngXxx() chaining is allowed.
Port of @angular-eslint/no-async-lifecycle-method. The async keyword is
detected on the method text so every modifier combination is covered.
Port of @angular-eslint/no-pipe-impure: flags @pipe({ pure: false }).
ksom added 4 commits September 4, 2026 18:09
Port of @angular-eslint/no-attribute-decorator, pointing to
inject(new HostAttributeToken()) (Angular 17.4+).
Two default Biome settings break real Angular projects:

- parameter decorators (@Inject, @optional, @Attribute...) are a parse
  error unless javascript.parser.unsafeParameterDecoratorsEnabled is on,
  and Biome skips the whole file
- the recommended style/useImportType safe fix rewrites tokens that are
  only used as constructor parameter types to import type, after which
  the Angular build fails with NG2003 (no suitable injection token)

The preset now sets both, scopes the plugin to **/*.ts minus spec files,
and the CLI writes/merges/removes an extends entry pointing at it instead
of a bare plugins path. The consumer smoke test covers the CLI round trip,
parameter decorators, the absence of import type rewrites, the safe fix
through the packed package, and the spec exclusion.
Native AST node patterns and plugin rewrites both need Biome 2.5.0, so
the peer range moves from >=2.4.8 to >=2.5.0. Development and schemas
move to 2.5.12. A CI job runs the integration suite against 2.5.0 so the
peer range stays honest; the latest-Biome job remains as an early
warning.
…rn rules

README: preset rationale (parameter decorators, useImportType), the
[rule-name] message prefix and where a biome-ignore comment must sit for
a decorated class, the fix policy and the official schematics for the
larger migrations, Angular 17-22, the seven new rules, and the Biome
caveat on per-file rewrites.

CONTRIBUTING and CLAUDE.md: native AST node patterns are the way to match
classes and members; verified engine rules (full-match regex, static
messages, unreliable pattern definitions, rewrite ordering, same-stem
split for conditional fixes).
@codecov-commenter

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment

Thanks for integrating Codecov - We've got you covered ☂️

@ksom ksom closed this Sep 19, 2026
@ksom ksom changed the title Native AST rules, Angular-safe preset, safe fix, and 7 new rules Superseded Sep 19, 2026
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.

2 participants