Skip to content

Lint generated proxies

Generated proxy files live inside your source tree, next to the commands and queries they came from — which means your ESLint config sees them too, unless you tell it not to.

The C# generator emits a standard license header, followed by the unchanged // @generated by Cratis metadata format. Fourth-line recognition requires the standard Cratis copyright line, license line, and blank third line; a marker inside an authored multiline template literal does not qualify. Older first-line markers remain recognized. Generated output contains no blanket ESLint disable directives or @ts-ignore comments, so you can keep unused-disable reporting at error, including with a minimal TypeScript parser configuration that has no rule plugins.

If you lint generated files, your consumer configuration must permit their existing exported empty interfaces, including interfaces with inherited members but no own members. These are supported public shapes: they preserve declaration merging and existing assignability. Configure the applicable @typescript-eslint/no-empty-interface and @typescript-eslint/no-empty-object-type policies accordingly. This is a target-language contract, not an unused-directive waiver; keep reportUnusedDisableDirectives at error.

Application checks such as license headers, named import-member sorting, and TypeScript-comment bans can remain active. Import members are sorted without reordering imported modules. Generated response hooks supply their response generic rather than suppressing a type error. Output is not promised to pass arbitrary library-style configurations that prohibit a supported public interface shape. Other application-specific restrictions may still need consumer configuration; for example, React hook rules can flag the generated static .use() methods as hook calls inside a class.

If you prefer not to lint generated files at all, use the opt-in processor below. It does not relax checks on your hand-written source.

A generated .ts file carries a // @generated by Cratis header and a **DO NOT EDIT** banner, and the proxy generator rewrites it whenever a build regenerates its contract (by default Arc skips files whose generated content is unchanged). Generation follows CratisProxiesOutputPath, not the build configuration, so a Release build with that path set regenerates too. Any lint finding on that file is unactionable: you cannot fix it by hand without the fix being discarded on the next build, and you cannot fix the generator’s output style through your project’s lint rules. Left unconfigured, a project either accumulates lint noise it can never resolve, or hand-maintains a path allow-list that has to be kept in sync with every new command and query folder.

@cratis/eslint-plugin-arc ships a skip-generated-proxies processor that detects the // @generated by Cratis header and skips the file wholesale. Because it keys on the header rather than a path pattern, it works regardless of where a proxy sits — intermixed with hand-written .ts/.tsx files in the same slice folder, a layout many projects choose by pointing CratisProxiesOutputPath at their source root — with no allow-list to maintain.

Terminal window
yarn add -D @cratis/eslint-plugin-arc @cratis/eslint-config eslint
eslint.config.mjs
import cratis from '@cratis/eslint-config';
import arc from '@cratis/eslint-plugin-arc';
export default [
...cratis.configs.consumer,
...arc.configs.recommended,
// …your project rules
];

With arc.configs.recommended composed in, a proxy file is never linted — not even for formatting — so nothing about it shows up as a finding to triage.

The plugin also ships two rules that are not about generated files, but are worth enabling alongside the processor if you use MVVM view models or MobX:

RuleWhat it does
no-hooks-in-view-modelDisallows React hook calls inside MVVM view models (classes named *ViewModel). A view model must be a plain, React-free class — inject a Cratis abstraction instead of calling a hook.
no-direct-mobx-react-importDisallows importing directly from mobx-react/mobx-react-lite. The MobX binding is an internal detail of @cratis/arc.react.mvvm, which re-exports observer — import it from there so the binding can evolve without breaking consumers.

no-hooks-in-view-model takes options to fit a project’s own naming conventions:

'@cratis/arc/no-hooks-in-view-model': ['error', {
classSuffix: 'ViewModel', // class-name suffix that marks a view model
hookPattern: '^use[A-Z]', // bare-identifier calls treated as hooks
additionalHooks: ['injectQuery'], // extra call names to forbid
}]

Only bare-identifier calls (useState(...)) are flagged — a member call like this.useDefaults() is not, so a view-model method that merely starts with use is safe.

  • File Index Tracking — the // @generated by Cratis header this processor keys on, and why index.ts deliberately does not carry it.