exadev-eslint-config 2.36.5 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -4
- package/dist/index.cjs +44 -9
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +44 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,6 +42,10 @@ export default defineConfig(
|
|
|
42
42
|
|
|
43
43
|
**Remove your own `plugins: { '@stylistic': ... }` registration too.** The default export bundles [`@stylistic/eslint-plugin`](https://eslint.style) (see [Stylistic comment, class-member and JSX rules](#stylistic-comment-class-member-and-jsx-rules)) and registers it under the `@stylistic` namespace itself, so a consumer block registering its own copy of the plugin (a different version resolved from elsewhere in the lockfile, or a duplicated install) makes ESLint throw `Config: Key "plugins": Cannot redefine plugin "@stylistic".` at config load, the identical flat-config restriction as the typescript-eslint case above. Registering the very same instance is harmless, but the robust migration is to drop the registration entirely: configure any further `@stylistic/*` rules directly in your own `rules` block, since this package's block already registers the plugin for every JS/TS and JSX file.
|
|
44
44
|
|
|
45
|
+
**Your own blocks need no `plugins: { exadev }` and no `files`.** `exadevConfig()` registers the `exadev` plugin for every file it lints, so a block placed after it that sets an `exadev/*` rule written for JavaScript and TypeScript, such as `{ rules: { 'exadev/barrel-policy': ['error', { mode: 'siblings' }] } }`, loads even though it also applies to `tsconfig.json`, other JSON files and Markdown, and a rule that inspects JavaScript or TypeScript syntax leaves those files alone. The JSON and Markdown rules (`package-json-key-order`, `markdown-required-heading`, the [workspace architecture](#workspace-architecture) and [Turbo](#turbo) rules) are the exception: ESLint refuses to enable a rule for a language it does not declare, so a block that sets one needs the `files` of the kind of file it reads, which its preset already supplies.
|
|
46
|
+
|
|
47
|
+
**Rules from other plugins are different.** `@typescript-eslint/*`, `@stylistic/*`, `jsdoc/*`, `unicorn/*` and the rest are registered only for JavaScript and TypeScript files, so a block of your own that sets one needs `files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}']`; without it ESLint fails at config load with `could not find plugin` as soon as the block also applies to a JSON or Markdown file.
|
|
48
|
+
|
|
45
49
|
### What the default export includes
|
|
46
50
|
|
|
47
51
|
**typescript-eslint presets:**
|
|
@@ -209,7 +213,7 @@ export default tseslint.config(
|
|
|
209
213
|
| [Required Markdown headings](#required-markdown-headings) | Off unless given (only a repository can say which documents need which headings); needs the optional peer `@eslint/markdown` | `exadevConfig({ markdownHeadings })` / `markdownHeadingsConfig(options)` |
|
|
210
214
|
| [Agent skills and plugin marketplaces](#agent-skills-and-plugin-marketplaces) | Auto-detected: on if the project has a `.claude-plugin/marketplace.json`, a `skills/<name>/SKILL.md` or a `plugins/<plugin>/skills/<name>/SKILL.md` (symbolic links to those directories count); the SKILL.md rules need the optional peer `@eslint/markdown` and the manifest rules `@eslint/json` | `exadevConfig({ agentSkills })` / `agentSkillsConfig(options)` |
|
|
211
215
|
|
|
212
|
-
Every tri-state option above (`true`/`false`/`undefined`) is passed through the named `exadevConfig(options
|
|
216
|
+
Every tri-state option above (`true`/`false`/`undefined`) is passed through the named `exadevConfig(options)` factory export:
|
|
213
217
|
|
|
214
218
|
```ts
|
|
215
219
|
// eslint.config.ts
|
|
@@ -227,7 +231,7 @@ export default defineConfig(
|
|
|
227
231
|
);
|
|
228
232
|
```
|
|
229
233
|
|
|
230
|
-
|
|
234
|
+
`exadevConfig` takes the options object alone and returns a config array, so your own blocks go after the spread inside `defineConfig`, as above: `defineConfig(...exadevConfig(), { rules: { 'no-console': 'warn' } })` is the default export plus one more block. Passing a block as a second argument is a type error. `import exadev from '@exadev/eslint-config'` (the default export) is just `exadevConfig()` called with no arguments.
|
|
231
235
|
|
|
232
236
|
### Optional React and Next.js support
|
|
233
237
|
|
|
@@ -390,7 +394,7 @@ Bundled into `exadevConfig()`'s default output the same way React/Next.js auto-d
|
|
|
390
394
|
| `no-enum-number-widening` | | **A bare `number` is accepted anywhere a numeric enum is expected**, without checking it is actually one of the enum's members — only a numeric *literal* gets range-checked by `tsc`. No autofix: the only provably safe fix is a genuine runtime membership check against the enum's own values, which is a behavioural choice a mechanical fix cannot responsibly make. Requires type information — only in the default (type-checked) export, not `plugin.configs.recommended`. |
|
|
391
395
|
| `no-enum-reverse-lookup-widening` | suggestion | **A numeric enum's reverse lookup can silently type as `string` for an out-of-range index.** Indexing a numeric enum's reverse mapping (`Direction[n]`) with a bare (non-literal) `number`, or with a different enum's member, types as plain `string` for any index, including one outside the enum's actual members, where it genuinely returns `undefined` at runtime — `tsc` does not range-check even a numeric literal index here. When the indexed expression is the init of a variable with an explicit `: string` annotation, a suggestion widens it to `: string \| undefined`, forcing later uses as a bare `string` to surface as real compile errors; every other syntactic position gets a plain report with no fix, and no case gets a full `--fix` autofix. Requires type information — only in the default (type-checked) export, not `plugin.configs.recommended`. |
|
|
392
396
|
| `prefer-numeric-sort-compare` | suggestion | **`.sort()` on a number array sorts lexicographically by default.** A deliberately narrow addition alongside [`@typescript-eslint/require-array-sort-compare`](https://typescript-eslint.io/rules/require-array-sort-compare/) (which already flags any bare `.sort()`/`.toSorted()` except on a plain string array, with no fix): when the array's element type is definitively `number`, a suggestion offers an ascending compare function (`(a, b) => a - b`), since the default comparator sorts lexicographically (`[1, 2, 10].sort()` becomes `[1, 10, 2]`). Not a full autofix — descending order is a real, if less common, alternative intent. Requires type information — only in the default (type-checked) export, not `plugin.configs.recommended`, since it needs the checker to confirm the array's element type. |
|
|
393
|
-
| `prefer-options-object-param` | suggestion | **A run of 2+ trailing optional parameters (`?`-marked or default-valued) should be bundled into one destructured `options` parameter.** Without this, a caller needing only the last optional parameter must still pass `undefined` for every optional parameter before it (the motivating case: a 10-parameter constructor with 8 trailing optional ones). The threshold is configurable (`{ minTrailingOptional }`, default `2`). A suggestion (not a full autofix) rewrites the parameter list and inserts a `const { ... } = options ?? {};` destructure as the body's first statement; call sites are never rewritten, since the signature edit alone turns every stale positional call site into a real compile error. Still reported, but with no suggestion offered, whenever collapsing the run would not be safe/mechanical: a parameter property in the run, a parameter with no simple resolvable name and explicit type of its own (including a destructured parameter, or one typed only through a separately-declared function-type alias), a decorated parameter, a rest parameter anywhere in the full parameter list, no `BlockStatement` body at all (an arrow's expression body, or any declaration-only signature — including an ambient `.d.ts` function, an interface method/construct signature, or an abstract/ambient class method), a `@param` JSDoc tag naming a parameter in the run, or a parameter/function-scope-local variable already named `options`. Complements `max-params` (see "Individual rule tuning" above), which catches the different shape (excessive REQUIRED parameters) this rule is deliberately blind to. Requires no type information — registered in both `plugin.configs.recommended` and the default (type-checked) export, like `prefer-readonly-array-param` above; unlike its own `prefer-readonly-object-param`/`prefer-numeric-sort-compare` siblings, which genuinely need the checker and so cannot be offered outside the default export at all. |
|
|
397
|
+
| `prefer-options-object-param` | suggestion | **A run of 2+ trailing optional parameters (`?`-marked or default-valued) should be bundled into one destructured `options` parameter, and an optional parameter should not sit directly before a rest parameter.** Without this, a caller needing only the last optional parameter must still pass `undefined` for every optional parameter before it (the motivating case: a 10-parameter constructor with 8 trailing optional ones). The threshold is configurable (`{ minTrailingOptional }`, default `2`). A suggestion (not a full autofix) rewrites the parameter list and inserts a `const { ... } = options ?? {};` destructure as the body's first statement; call sites are never rewritten, since the signature edit alone turns every stale positional call site into a real compile error. Still reported, but with no suggestion offered, whenever collapsing the run would not be safe/mechanical: a parameter property in the run, a parameter with no simple resolvable name and explicit type of its own (including a destructured parameter, or one typed only through a separately-declared function-type alias), a decorated parameter, a rest parameter anywhere in the full parameter list, no `BlockStatement` body at all (an arrow's expression body, or any declaration-only signature — including an ambient `.d.ts` function, an interface method/construct signature, or an abstract/ambient class method), a `@param` JSDoc tag naming a parameter in the run, or a parameter/function-scope-local variable already named `options`. An optional parameter directly before a rest parameter is reported as well, even when it is the only optional one (`(options = {}, ...blocks)`), because a caller wanting only the rest values must pass a placeholder for it; that report has its own message and no suggestion, since the right shape (the rest values alone, or one object holding the optional value and the rest values) is a design decision. Complements `max-params` (see "Individual rule tuning" above), which catches the different shape (excessive REQUIRED parameters) this rule is deliberately blind to. Requires no type information — registered in both `plugin.configs.recommended` and the default (type-checked) export, like `prefer-readonly-array-param` above; unlike its own `prefer-readonly-object-param`/`prefer-numeric-sort-compare` siblings, which genuinely need the checker and so cannot be offered outside the default export at all. |
|
|
394
398
|
| `prefer-doc-comment` | ✓ | **A substantial leading comment on an exported declaration should be a doc comment, not a plain `//`/`/* */` comment.** "Substantial" means 2 or more comment lines, or a single line whose text exceeds a configurable length (`{ maxLineLength }`, default `80`). Covers an exported function declaration, an exported function's own signature (a body-less `TSDeclareFunction`, an ordinary overload signature and a genuinely ambient one alike, `declare function foo(): void;` included, since TypeScript never shows either one's own implementation to callers, only the signature itself), an exported `const` declaration, whatever its initialiser (a function expression or arrow function, an ordinary value such as `export const TIMEOUT_MS = 5000;`, or none at all in an ambient `export declare const x: number;`; reported once even when the statement declares several, e.g. `export const h1 = () => {}, h2 = () => {};`), an exported class declaration, ambient (`export declare class`) or ordinary alike (TypeScript strips only an ambient class's own MEMBER bodies, never its declaration shape, so its leading comment documents the identical public contract an ordinary exported class's does), an exported class's public (non-`private`/`protected`/`#`-private) method (covering both a method's own overload signature and its trailing implementation) and public abstract method (`abstract m(): void;`, needing no equivalent overload-implementation exemption of its own, since TypeScript's own grammar never lets an abstract method carry a body at all), an exported class's public function/arrow-valued property (the class-member equivalent of the function-initialised `const` shape above), an exported interface/type-alias declaration, an exported enum declaration (`const enum` alike; never an individual member, the identical reason an interface's members are never individually checked), an exported namespace/module declaration (ambient or ordinary alike, at any nesting depth whose enclosing namespaces are all exported themselves), and a default-exported expression (`export default (): number => 1;`, `export default 42;`; a default-exported `function`/`class` declaration or body-less signature is covered by its own shape above and reported exactly once, never twice); a non-exported declaration is never reported, regardless of its comment. A TypeScript function/method overload set's own body-carrying IMPLEMENTATION signature is never reported either, however substantial its own comment: TypeScript never shows that signature to callers at all, only the separate overload signatures above it (a body-less `TSDeclareFunction` for a function, written without `declare`, or a `MethodDefinition` whose own `value` is `TSEmptyBodyFunctionExpression` for a class method) are ever checked against a call site, and both of those are themselves reported like any other declaration in this rule's scope, as the overload set's own real public contract; a comment directly above the implementation is instead internal reasoning about how the overloads are actually realised, never a public contract. The implementation is detected via ESLint's own scope analysis for a function (the overload signatures and the implementation all share one Variable) and, for a class method, via an EARLIER (source-order), same-`static`-ness, same-key signature sibling: a same-named `static`/instance pair is never one overload set to TypeScript itself, and a same-named signature declared AFTER the implementation belongs to a different, later member, never the one the implementation itself realises, so neither is ever mistaken for the implementation's own overload signature. A trailing comment on the previous line of code (`const x = 1; // note`) is never treated as the next declaration's own leading comment. A directive-shaped line (the full ESLint directive/config-comment family, `eslint-disable`/`eslint-disable-line`/`eslint-disable-next-line`/`eslint-enable`/`eslint`/`eslint-env`/`global`/`globals`/`exported`, matched case-sensitively, exactly as ESLint's own directive parser matches them, each requiring real whitespace or end-of-string immediately after the keyword rather than a bare word boundary, so an ordinary compound word such as `eslint-plugin-x` is never mistaken for one, and, on a `//` Line comment specifically, recognised only as `eslint-disable-line`/`eslint-disable-next-line`, the two labels ESLint itself honours there; every other member of the family is a directive only in Block form, matching what ESLint itself does; `@ts-expect-error`/`@ts-ignore`/`@ts-nocheck`/`@ts-check`, the `ts-*` markers matched with or without their own leading `@`; `TODO`/`FIXME`, matched case-insensitively when all-uppercase or immediately followed by `:`/`(` (`todo:`, `fixme(scope):`), never a `Todo`/`Fixme` opening ordinary prose; `prettier-ignore`; `c8 ignore`/`v8 ignore`/`istanbul ignore`/`node:coverage ignore`/`node:coverage disable`/`node:coverage enable`; `cspell:disable`/`cspell:disable-line`/`cspell:disable-next-line`/`cspell:enable` (never cspell's own `ignore`/`words` directives, which name dictionary words rather than suppressing a stretch of text); `biome-ignore`; `#region`/`#endregion`, an editor folding marker; and a managed-region marker, a comment of exactly `<word>:<word>:start` or `<word>:<word>:end` such as `content:claude:start`, which a tool locates by its exact line) found anywhere in a `//` run is never itself merged into the fix: only the prose segment directly adjacent to the declaration, the run of lines after the LAST such directive, is ever reported and converted; every earlier segment, and every directive line itself, is left completely untouched, since an earlier segment sits on the far side of a directive from the declaration and documents whatever the directive itself demarcates, never the declaration below it; a directive seated directly above the declaration itself is transparent to this split rather than a boundary of it, since group assembly skips past it before the run is ever collected, so the prose above it is still reported and converted, with the directive preserved untouched in place, still functional, directly above the declaration; a directive found inside an already-consolidated Block comment leaves the whole comment alone, since a partial fix cannot safely be sliced out of one physical comment node. A `///` triple-slash reference/AMD directive (`/// <reference types="..." />`) is recognised separately, by its own un-stripped comment value rather than this same pattern, and excluded from the fix the same way. An existing `/**`-opening doc comment, or a `/*!` license/banner block, is left alone regardless of length. Autofix rewraps the comment's existing text into a `/** ... */` block, one line per original logical line as this rule's own fixer writes it, preserving each line's own indentation beyond the delimiter's single conventional space (never flattening a nested example or sub-list to its neighbours' level), and unwraps a hand-written starred block's (`/* ... */` with its own leading `* ` per line) marker rather than doubling it against the fixer's own. This describes this rule's own fixer alone, not a guarantee that `exadevConfig()`'s combined output preserves that text verbatim: a bundled sibling rule's own fixer can still rewrite it further in the same `--fix` run, which is exactly why the fix is withheld below for the two such collisions this rule already guards against. Handles the leading comment whether it is a run of `//` lines or already a bare block: a bare block may be hand-written, or produced by the bare-block fixer of `exadev/multiline-comment-style` (bundled alongside this rule) or of a consumer's own `@stylistic/multiline-comment-style`. Always reported when substantial, but the fix withheld, rather than silently skipping the violation, whenever the exact resulting text would contain a literal closing-comment delimiter or fails to parse as valid TSDoc (checked directly against `@microsoft/tsdoc`'s own parser, the same one `eslint-plugin-tsdoc`'s `tsdoc/syntax` validates against). Also withheld when any considered line itself begins with a literal `*` once its own leading whitespace is trimmed (a hand-written markdown bullet, `* item`, or a bare block comment whose own lines are only partially starred): spliced verbatim into this fixer's own template, that reads as a repeated delimiter on a middle line, which eslint-plugin-jsdoc's own `jsdoc/no-multi-asterisks` rule (bundled unconditionally alongside this rule, see [`jsdoc.ts`](src/jsdoc.ts)) would then strip in the very same `--fix` run, silently losing real content in a way neither this rule nor `@microsoft/tsdoc`'s own parser has any way to detect on its own. Also withheld when any considered line carries an unescaped tag-shaped `@word` mention, at the line's own start (a genuine TSDoc block or modifier tag, `@remarks`, `@param foo`, `@internal`, `@typeParam T`, `@virtual`, `@override`, ...) or immediately after whitespace anywhere further into the line (a bare mid-line mention, `tseslint.config() is @deprecated upstream`), the identical shape `jsdoc/escape-inline-tags` itself keys on: a line-start tag withholds because the converted doc comment would be validated by every tag-checking sibling rule in the bundle and read by TypeScript's own JSDoc parser, and this rule cannot enumerate in advance which tag either will next object to or act on. jsdoc.ts gives `jsdoc/check-tag-names` the TSDoc vocabulary (`settings.jsdoc.tagNamePreference` entries for the TSDoc tags JSDoc spells differently, `typed: false`, and `jsdoc/empty-tags` off), so the TSDoc tags that once collided (`@internal`, `@public`, `@readonly`, `@typeParam`, `@virtual`, `@override`) survive `--fix` there, but a converted tag line such as `@deprecated` or `@internal` is still read as a REAL tag on the symbol, changing what it means rather than merely its formatting. Withholding on every tag-shaped line, not merely a blank line adjacent to one, is deliberate, so no further one-off guard is needed each time a new collision is found. Requires no type information. Wired only through `stylisticCommentsConfig` into the default (type-checked) `exadevConfig()` output (see [Stylistic comment, class-member and JSX rules](#stylistic-comment-class-member-and-jsx-rules)), never registered in `plugin.configs.recommended`/`.barrel`, so a consumer using the lighter `extends: [plugin.configs.recommended]` option (see [The lighter option](#the-lighter-option-the-plugin-named-export)) never gets it. |
|
|
395
399
|
| `multiline-comment-style` | ✓ | **`@stylistic/eslint-plugin`'s [`multiline-comment-style`](https://eslint.style/rules/multiline-comment-style), never merging a directive into a block comment.** Delegates to the upstream rule with the same options, defaults, messages and fixer, but hides every line `prefer-doc-comment` treats as a directive (`TODO`/`FIXME` task markers, `cspell:*`, `biome-ignore`, `#region`/`#endregion` and the rest of that rule's list) from the upstream rule's grouping, so such a line breaks a `//` run the way upstream's own directives do. A managed-region marker (a comment of exactly `<word>:<word>:start` or `<word>:<word>:end`, such as `content:claude:start`) is treated as a directive too, so no rewrite merges it with other text. The text the fixer writes also has trailing whitespace removed from every line, since upstream leaves padding on blank lines inside a rewritten block. Exists because upstream does not yet treat `TODO`/`FIXME` as directives ([eslint-stylistic/eslint-stylistic#1287](https://github.com/eslint-stylistic/eslint-stylistic/issues/1287)); see [Stylistic comment, class-member and JSX rules](#stylistic-comment-class-member-and-jsx-rules). Requires no type information. Wired at `'bare-block'` only through `stylisticCommentsConfig` into the default `exadevConfig()` output, never in `plugin.configs.recommended`/`.barrel`. |
|
|
396
400
|
| `package-json-key-order` | ✓ | **Requires `package.json`'s keys to match `syncpack format`'s order.** See [Optional package.json key ordering](#optional-packagejson-key-ordering) — opt-in via `exadevConfig({ packageJsonKeyOrder: true })`, not part of `recommended`/`barrel`. A JSON-language rule (`@eslint/json`'s `json/json`), not a TSESLint one — needs no type information and doesn't apply to any `.ts`/`.js` file. |
|
|
@@ -1747,6 +1751,7 @@ pnpm build
|
|
|
1747
1751
|
- `tryResolveJsonPlugin` and `requireJsonPlugin` are the silent and the throwing resolution paths, the second naming the feature that needs the peer and the install command.
|
|
1748
1752
|
- `buildJsonLanguageBlock` assembles the `exadev` and `json` plugin registration for a `files` list under `json/json` or `json/jsonc`; the `json/jsonc` form also admits trailing commas, matching the JSONC config in [`src/json-canonical.ts`](src/json-canonical.ts). `JSONC_FILE_GLOBS` (`tsconfig*.json`, `turbo.json`, `*.jsonc`) lists the files that carry comments, so a rule targeting them must be wired under `json/jsonc`, and a rule meant for both languages declares both in `meta.languages`.
|
|
1749
1753
|
- `buildPackageJsonKeyOrderConfig` and `buildWorkspaceArchitectureConfig` are built on it; a new JSON rule family follows the same shape, taking its tri-state `enabled` option from `exadevConfig` and choosing `requireJsonPlugin` (forced on) or `tryResolveJsonPlugin` (auto-detected).
|
|
1754
|
+
- [`src/rules/estree-source.ts`](src/rules/estree-source.ts) holds `isEstreeSource`, the first check of every rule that reads JavaScript or TypeScript syntax (scope analysis, parser services, JSX): such a rule returns no listeners for a JSON, JSONC or Markdown file, which an unscoped block of a consumer's own can hand it. A rule that reads only the filename needs no such check.
|
|
1750
1755
|
- [`src/rules/file-scope.ts`](src/rules/file-scope.ts) lets a rule scope itself by filename, so it still behaves when a consumer wires it onto a wider `files` list.
|
|
1751
1756
|
- `createFileScope(globs)` returns a `(filename, cwd) => boolean` matching the file relative to `cwd` with minimatch (a leading `!` excludes, wildcards never matching dot-prefixed names). A file outside `cwd` (its relative path starts with `..`) is never in scope, whatever the patterns. Build it once per `create()`.
|
|
1752
1757
|
- `fileGlobsSchema` and `readFileGlobs` are the option schema and runtime validator for a glob list; a list needs at least one include, may not repeat a glob, and every glob must compile in minimatch.
|
|
@@ -1769,7 +1774,7 @@ pnpm build
|
|
|
1769
1774
|
- [`src/rules/run-tracker.ts`](src/rules/run-tracker.ts) decides which lint carries a finding that belongs to a set of files rather than to one, so `require-compiler-options` reports once per run; it reads run boundaries from the order of the lints, as [Compiler options](#compiler-options) describes.
|
|
1770
1775
|
- [`src/rules/tsconfig-attribution.ts`](src/rules/tsconfig-attribution.ts) works out which tsconfig governs a linted file, attributing a program with no tsconfig to the one the file was last linted under while that tsconfig still lists it.
|
|
1771
1776
|
- [`src/create-config.ts`](src/create-config.ts) is config assembly's single source of truth.
|
|
1772
|
-
- `exadevConfig(options
|
|
1777
|
+
- `exadevConfig(options)` concatenates, in order: `buildGitignoreConfig`, a block that registers the `exadev` plugin with no `files`, `ignores` or `rules` (so a consumer's unscoped `exadev/*` rule resolves in every language the later blocks lint), `recommendedTypeChecked`, `jsdocAndTsdoc`, `jsonCanonicalConfig`, `stylisticCommentsConfig`, `buildReactConfig`, `buildNextjsConfig`, `buildPackageJsonKeyOrderConfig` (each tri-state builder fed its matching option), `buildWorkspaceArchitectureConfig` (only when `workspaceArchitecture` is given; there is no auto-detected default), `buildTurboConfig` (only when `turbo` is given, likewise), `buildImportPolicyConfig` (only when `importPolicies` is given, likewise), `buildPureModulesConfig` (only when `pureModules` is given, likewise), `buildTestHygieneConfig` (only when `testHygiene` is given, likewise), `buildMarkdownHeadingsConfig` (only when `markdownHeadings` is given, likewise), `buildAgentSkillsConfig` (tri-state, placed after the headings block so that a SKILL.md stays YAML whatever format `markdownHeadings` sets), and `buildToolingWiringConfig` (only when `toolingWiring` is given, likewise).
|
|
1773
1778
|
- `defaultConfig` is `exadevConfig()` evaluated once, eagerly, at module load.
|
|
1774
1779
|
- [`src/robustness-rules.ts`](src/robustness-rules.ts) holds `UNTYPED_ROBUSTNESS_RULES`, the core ESLint rules that need no type information, spread into both `plugin.configs.recommended` and the type-checked block so the two cannot drift apart.
|
|
1775
1780
|
- [`src/stylistic-comments.ts`](src/stylistic-comments.ts) builds `stylisticCommentsConfig`: the hand-picked `@stylistic/eslint-plugin` rules plus this package's own `exadev/multiline-comment-style` and `exadev/prefer-doc-comment`, in two blocks (one scoped to every JS/TS file, one scoped to JSX files only for the three JSX-specific rules). See [Stylistic comment, class-member and JSX rules](#stylistic-comment-class-member-and-jsx-rules).
|
package/dist/index.cjs
CHANGED
|
@@ -485,7 +485,7 @@ function buildOptionalPluginConfig(preset, options = {}) {
|
|
|
485
485
|
}
|
|
486
486
|
//#endregion
|
|
487
487
|
//#region package.json
|
|
488
|
-
var version = "
|
|
488
|
+
var version = "3.0.1";
|
|
489
489
|
//#endregion
|
|
490
490
|
//#region src/react.ts
|
|
491
491
|
/**
|
|
@@ -3620,7 +3620,7 @@ const preferOptionsObjectParam = createRule$20({
|
|
|
3620
3620
|
meta: {
|
|
3621
3621
|
type: "suggestion",
|
|
3622
3622
|
hasSuggestions: true,
|
|
3623
|
-
docs: { description: "Suggest bundling a run of 2+ trailing optional parameters into a single destructured 'options' parameter — without this, a caller needing only the last optional parameter must still pass 'undefined' for every optional parameter before it." },
|
|
3623
|
+
docs: { description: "Suggest bundling a run of 2+ trailing optional parameters into a single destructured 'options' parameter — without this, a caller needing only the last optional parameter must still pass 'undefined' for every optional parameter before it. Also report an optional parameter directly before a rest parameter, which a caller wanting only the rest values must fill with a placeholder." },
|
|
3624
3624
|
schema: [{
|
|
3625
3625
|
type: "object",
|
|
3626
3626
|
properties: { minTrailingOptional: {
|
|
@@ -3631,15 +3631,32 @@ const preferOptionsObjectParam = createRule$20({
|
|
|
3631
3631
|
}],
|
|
3632
3632
|
messages: {
|
|
3633
3633
|
tooManyTrailingOptional: "This {{ kind }} has {{ count }} trailing optional parameters ({{ names }}) — a caller needing only the last one must still pass 'undefined' for every parameter before it. Bundle the trailing optional run into a single destructured 'options' parameter instead.",
|
|
3634
|
-
wrapInOptionsObject: "Bundle the trailing optional parameters into a single 'options' parameter."
|
|
3634
|
+
wrapInOptionsObject: "Bundle the trailing optional parameters into a single 'options' parameter.",
|
|
3635
|
+
placeholderBeforeRest: "This {{ kind }} takes an optional parameter ({{ name }}) immediately before a rest parameter, so a caller passing only the rest values must supply a placeholder for it. Take the rest values alone, or put the optional value and the rest values in one object."
|
|
3635
3636
|
},
|
|
3636
3637
|
defaultOptions: [{ minTrailingOptional: 2 }]
|
|
3637
3638
|
},
|
|
3638
3639
|
create(context, [{ minTrailingOptional }]) {
|
|
3639
3640
|
const { sourceCode } = context;
|
|
3641
|
+
function reportPlaceholderBeforeRest(node, run) {
|
|
3642
|
+
const optionalBeforeRest = run.at(-1);
|
|
3643
|
+
if (optionalBeforeRest === void 0 || node.params.at(-1)?.type !== _typescript_eslint_utils.AST_NODE_TYPES.RestElement) return;
|
|
3644
|
+
const name = resolveFixableParam(optionalBeforeRest)?.identifierNode.name ?? sourceCode.getText(optionalBeforeRest);
|
|
3645
|
+
context.report({
|
|
3646
|
+
node,
|
|
3647
|
+
messageId: "placeholderBeforeRest",
|
|
3648
|
+
data: {
|
|
3649
|
+
kind: describeFunctionKind(node),
|
|
3650
|
+
name
|
|
3651
|
+
}
|
|
3652
|
+
});
|
|
3653
|
+
}
|
|
3640
3654
|
function checkParams(node) {
|
|
3641
3655
|
const run = getTrailingOptionalRun(node.params);
|
|
3642
|
-
if (run.length < minTrailingOptional)
|
|
3656
|
+
if (run.length < minTrailingOptional) {
|
|
3657
|
+
reportPlaceholderBeforeRest(node, run);
|
|
3658
|
+
return;
|
|
3659
|
+
}
|
|
3643
3660
|
const resolved = run.map(resolveFixableParam);
|
|
3644
3661
|
const names = run.map((param, index) => resolved[index]?.identifierNode.name ?? sourceCode.getText(param)).join(", ");
|
|
3645
3662
|
const data = {
|
|
@@ -4069,6 +4086,14 @@ function asIdentifierName(name) {
|
|
|
4069
4086
|
return name;
|
|
4070
4087
|
}
|
|
4071
4088
|
//#endregion
|
|
4089
|
+
//#region src/rules/estree-source.ts
|
|
4090
|
+
/**
|
|
4091
|
+
* Whether the file under lint was parsed to an ESTree `Program`, the tree every rule that reads JavaScript or TypeScript syntax is written against. A rule that is wired onto a block with no `files` (the documented unscoped override placed after the shared config) is also handed the JSON, JSONC and Markdown files the shared config lints, whose source code objects have a different root and no scope manager or parser services; such a rule starts with `if (!isEstreeSource(context.sourceCode)) return {};` after reading its options, so a file of another language is left alone instead of crashing the run.
|
|
4092
|
+
*/
|
|
4093
|
+
function isEstreeSource(sourceCode) {
|
|
4094
|
+
return sourceCode.ast.type === "Program";
|
|
4095
|
+
}
|
|
4096
|
+
//#endregion
|
|
4072
4097
|
//#region src/rules/no-array-isarray-mutation.ts
|
|
4073
4098
|
const MUTATING_INSERT_METHODS$1 = /* @__PURE__ */ new Set([
|
|
4074
4099
|
"push",
|
|
@@ -4099,6 +4124,7 @@ const noArrayIsArrayMutation = createRule$18({
|
|
|
4099
4124
|
},
|
|
4100
4125
|
defaultOptions: [],
|
|
4101
4126
|
create(context) {
|
|
4127
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
4102
4128
|
const services = _typescript_eslint_utils.ESLintUtils.getParserServices(context);
|
|
4103
4129
|
const checker = services.program.getTypeChecker();
|
|
4104
4130
|
function declarationHasReadonlyArrayConstituent(declarationNode) {
|
|
@@ -4793,6 +4819,7 @@ const noEnumNumberWidening = _typescript_eslint_utils.ESLintUtils.RuleCreator((n
|
|
|
4793
4819
|
defaultOptions: []
|
|
4794
4820
|
},
|
|
4795
4821
|
create(context) {
|
|
4822
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
4796
4823
|
const services = _typescript_eslint_utils.ESLintUtils.getParserServices(context);
|
|
4797
4824
|
const checker = services.program.getTypeChecker();
|
|
4798
4825
|
function checkNode(expression) {
|
|
@@ -4841,6 +4868,7 @@ const noEnumReverseLookupWidening = _typescript_eslint_utils.ESLintUtils.RuleCre
|
|
|
4841
4868
|
defaultOptions: []
|
|
4842
4869
|
},
|
|
4843
4870
|
create(context) {
|
|
4871
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
4844
4872
|
const services = _typescript_eslint_utils.ESLintUtils.getParserServices(context);
|
|
4845
4873
|
const checker = services.program.getTypeChecker();
|
|
4846
4874
|
return { MemberExpression(node) {
|
|
@@ -4964,7 +4992,7 @@ const noExternalMemberJsxTag = _typescript_eslint_utils.ESLintUtils.RuleCreator(
|
|
|
4964
4992
|
create(context, [options]) {
|
|
4965
4993
|
const { allowSource, allowTags } = readNoExternalMemberJsxTagOptions(options);
|
|
4966
4994
|
const { sourceCode } = context;
|
|
4967
|
-
if (hasUseClientDirective(sourceCode.ast)) return {};
|
|
4995
|
+
if (!isEstreeSource(sourceCode) || hasUseClientDirective(sourceCode.ast)) return {};
|
|
4968
4996
|
return { JSXOpeningElement(node) {
|
|
4969
4997
|
if (node.name.type !== _typescript_eslint_utils.AST_NODE_TYPES.JSXMemberExpression) return;
|
|
4970
4998
|
const tag = sourceCode.getText(node.name);
|
|
@@ -5588,6 +5616,7 @@ const noMapInstanceofMutation = createRule$16({
|
|
|
5588
5616
|
},
|
|
5589
5617
|
defaultOptions: [],
|
|
5590
5618
|
create(context) {
|
|
5619
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
5591
5620
|
const services = _typescript_eslint_utils.ESLintUtils.getParserServices(context);
|
|
5592
5621
|
const checker = services.program.getTypeChecker();
|
|
5593
5622
|
function declarationHasReadonlyMapConstituent(declarationNode) {
|
|
@@ -6090,7 +6119,7 @@ const noNonSerialisableServerProp = createRule$14({
|
|
|
6090
6119
|
create(context, [options]) {
|
|
6091
6120
|
const { names, allowElements } = readNoNonSerialisableServerPropOptions(options);
|
|
6092
6121
|
const { sourceCode } = context;
|
|
6093
|
-
if (hasUseClientDirective(sourceCode.ast)) return {};
|
|
6122
|
+
if (!isEstreeSource(sourceCode) || hasUseClientDirective(sourceCode.ast)) return {};
|
|
6094
6123
|
return { JSXAttribute(node) {
|
|
6095
6124
|
if (node.name.type !== _typescript_eslint_utils.AST_NODE_TYPES.JSXIdentifier || !names.has(node.name.name)) return;
|
|
6096
6125
|
if (node.value?.type !== _typescript_eslint_utils.AST_NODE_TYPES.JSXExpressionContainer || node.value.expression.type === _typescript_eslint_utils.AST_NODE_TYPES.JSXEmptyExpression) return;
|
|
@@ -6467,6 +6496,7 @@ const noPointlessReassignment = {
|
|
|
6467
6496
|
messages: { pointlessReassignment: "Pointless reassignment: '{{ name }}' is just an alias for '{{ value }}'. Use the original directly." }
|
|
6468
6497
|
},
|
|
6469
6498
|
create(context) {
|
|
6499
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
6470
6500
|
const moduleScope = findTopLevelScope(context.sourceCode.getScope(context.sourceCode.ast));
|
|
6471
6501
|
return { VariableDeclarator(node) {
|
|
6472
6502
|
if (node.id.type !== "Identifier" || node.init?.type !== "Identifier" || node.id.name.startsWith("_")) return;
|
|
@@ -6533,6 +6563,7 @@ const noSetInstanceofMutation = createRule$11({
|
|
|
6533
6563
|
},
|
|
6534
6564
|
defaultOptions: [],
|
|
6535
6565
|
create(context) {
|
|
6566
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
6536
6567
|
const services = _typescript_eslint_utils.ESLintUtils.getParserServices(context);
|
|
6537
6568
|
const checker = services.program.getTypeChecker();
|
|
6538
6569
|
function declarationHasReadonlySetConstituent(declarationNode) {
|
|
@@ -7483,6 +7514,7 @@ const preferNumericSortCompare = createRule$10({
|
|
|
7483
7514
|
defaultOptions: []
|
|
7484
7515
|
},
|
|
7485
7516
|
create(context) {
|
|
7517
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
7486
7518
|
const services = _typescript_eslint_utils.ESLintUtils.getParserServices(context);
|
|
7487
7519
|
const checker = services.program.getTypeChecker();
|
|
7488
7520
|
return { CallExpression(node) {
|
|
@@ -7638,6 +7670,7 @@ const preferReadonlyObjectParam = createRule$8({
|
|
|
7638
7670
|
defaultOptions: []
|
|
7639
7671
|
},
|
|
7640
7672
|
create(context) {
|
|
7673
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
7641
7674
|
const services = _typescript_eslint_utils.ESLintUtils.getParserServices(context);
|
|
7642
7675
|
const checker = services.program.getTypeChecker();
|
|
7643
7676
|
function checkParam(param) {
|
|
@@ -8549,6 +8582,7 @@ const requireCompilerOptions = createRule$5({
|
|
|
8549
8582
|
create(context, [options]) {
|
|
8550
8583
|
const requirements = readCompilerOptionRequirements(options);
|
|
8551
8584
|
if (requirements.length === 0) return {};
|
|
8585
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
8552
8586
|
const { program } = _typescript_eslint_utils.ESLintUtils.getParserServices(context);
|
|
8553
8587
|
const signature = JSON.stringify(requirements.map((requirement) => [requirement.name, requirement.spelled]));
|
|
8554
8588
|
return { Program(node) {
|
|
@@ -9006,6 +9040,7 @@ const scopedFirstParameter = _typescript_eslint_utils.ESLintUtils.RuleCreator((n
|
|
|
9006
9040
|
create(context, [options]) {
|
|
9007
9041
|
const { interfaces, name: requiredName, type: requiredType } = readScopedFirstParameterOptions(options);
|
|
9008
9042
|
const { sourceCode } = context;
|
|
9043
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
9009
9044
|
const services = _typescript_eslint_utils.ESLintUtils.getParserServices(context);
|
|
9010
9045
|
const checker = services.program.getTypeChecker();
|
|
9011
9046
|
const checkMembers = (owner, members) => {
|
|
@@ -12161,10 +12196,11 @@ function withWorkspaceGroups(turbo, workspaceArchitecture) {
|
|
|
12161
12196
|
*
|
|
12162
12197
|
* The tri-state per feature threads straight into each builder's own `enabled` option: true forces on (throwing if the underlying peer isn't resolvable), false forces off (skipping resolution entirely), undefined auto-detects (silently empty if unresolvable, or if an equivalent tool, syncpack for packageJsonKeyOrder, or a project's own .gitignore for gitignore, already does the job). One resolution pass per feature; no separate pre-check gate that would resolve twice.
|
|
12163
12198
|
*/
|
|
12164
|
-
function exadevConfig(options = {}
|
|
12199
|
+
function exadevConfig(options = {}) {
|
|
12165
12200
|
const gitignore = buildGitignoreConfig({ enabled: options.gitignore });
|
|
12166
12201
|
return toPublicConfigArray([
|
|
12167
12202
|
...gitignore,
|
|
12203
|
+
{ plugins: { exadev: plugin } },
|
|
12168
12204
|
...recommendedTypeChecked,
|
|
12169
12205
|
...jsdocAndTsdoc,
|
|
12170
12206
|
...jsonCanonicalConfig,
|
|
@@ -12183,8 +12219,7 @@ function exadevConfig(options = {}, ...userConfigs) {
|
|
|
12183
12219
|
...options.testHygiene !== void 0 ? buildTestHygieneConfig(options.testHygiene) : [],
|
|
12184
12220
|
...options.markdownHeadings !== void 0 ? buildMarkdownHeadingsConfig(options.markdownHeadings) : [],
|
|
12185
12221
|
...buildAgentSkillsConfig(options.agentSkills, { ignores: gitignore.flatMap(({ ignores = [] }) => ignores) }),
|
|
12186
|
-
...options.toolingWiring !== void 0 ? buildToolingWiringConfig(options.toolingWiring) : []
|
|
12187
|
-
...userConfigs
|
|
12222
|
+
...options.toolingWiring !== void 0 ? buildToolingWiringConfig(options.toolingWiring) : []
|
|
12188
12223
|
]);
|
|
12189
12224
|
}
|
|
12190
12225
|
/**
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "@typescript-eslint/utils";
|
|
2
2
|
import { Config, ConfigObject } from "eslint/config";
|
|
3
3
|
import "@eslint/markdown";
|
|
4
4
|
import "@typescript-eslint/utils/json-schema";
|
|
@@ -438,7 +438,7 @@ interface ExadevConfigOptions {
|
|
|
438
438
|
*
|
|
439
439
|
* The tri-state per feature threads straight into each builder's own `enabled` option: true forces on (throwing if the underlying peer isn't resolvable), false forces off (skipping resolution entirely), undefined auto-detects (silently empty if unresolvable, or if an equivalent tool, syncpack for packageJsonKeyOrder, or a project's own .gitignore for gitignore, already does the job). One resolution pass per feature; no separate pre-check gate that would resolve twice.
|
|
440
440
|
*/
|
|
441
|
-
export declare function exadevConfig(options?: ExadevConfigOptions
|
|
441
|
+
export declare function exadevConfig(options?: ExadevConfigOptions): PublicConfigArray;
|
|
442
442
|
/**
|
|
443
443
|
* Evaluated once, eagerly, at module load -- exactly matching how recommendedTypeChecked itself is already eagerly built today. This is what lets src/index.ts re-export a plain, already-computed array under the name `default`: every existing consumer's `...exadev` spread sees the identical shape and timing as before this file existed, whether or not React/Next.js support resolves in their own project.
|
|
444
444
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -438,7 +438,7 @@ interface ExadevConfigOptions {
|
|
|
438
438
|
*
|
|
439
439
|
* The tri-state per feature threads straight into each builder's own `enabled` option: true forces on (throwing if the underlying peer isn't resolvable), false forces off (skipping resolution entirely), undefined auto-detects (silently empty if unresolvable, or if an equivalent tool, syncpack for packageJsonKeyOrder, or a project's own .gitignore for gitignore, already does the job). One resolution pass per feature; no separate pre-check gate that would resolve twice.
|
|
440
440
|
*/
|
|
441
|
-
export declare function exadevConfig(options?: ExadevConfigOptions
|
|
441
|
+
export declare function exadevConfig(options?: ExadevConfigOptions): PublicConfigArray;
|
|
442
442
|
/**
|
|
443
443
|
* Evaluated once, eagerly, at module load -- exactly matching how recommendedTypeChecked itself is already eagerly built today. This is what lets src/index.ts re-export a plain, already-computed array under the name `default`: every existing consumer's `...exadev` spread sees the identical shape and timing as before this file existed, whether or not React/Next.js support resolves in their own project.
|
|
444
444
|
*/
|
package/dist/index.js
CHANGED
|
@@ -450,7 +450,7 @@ function buildOptionalPluginConfig(preset, options = {}) {
|
|
|
450
450
|
}
|
|
451
451
|
//#endregion
|
|
452
452
|
//#region package.json
|
|
453
|
-
var version = "
|
|
453
|
+
var version = "3.0.1";
|
|
454
454
|
//#endregion
|
|
455
455
|
//#region src/react.ts
|
|
456
456
|
/**
|
|
@@ -3585,7 +3585,7 @@ const preferOptionsObjectParam = createRule$20({
|
|
|
3585
3585
|
meta: {
|
|
3586
3586
|
type: "suggestion",
|
|
3587
3587
|
hasSuggestions: true,
|
|
3588
|
-
docs: { description: "Suggest bundling a run of 2+ trailing optional parameters into a single destructured 'options' parameter — without this, a caller needing only the last optional parameter must still pass 'undefined' for every optional parameter before it." },
|
|
3588
|
+
docs: { description: "Suggest bundling a run of 2+ trailing optional parameters into a single destructured 'options' parameter — without this, a caller needing only the last optional parameter must still pass 'undefined' for every optional parameter before it. Also report an optional parameter directly before a rest parameter, which a caller wanting only the rest values must fill with a placeholder." },
|
|
3589
3589
|
schema: [{
|
|
3590
3590
|
type: "object",
|
|
3591
3591
|
properties: { minTrailingOptional: {
|
|
@@ -3596,15 +3596,32 @@ const preferOptionsObjectParam = createRule$20({
|
|
|
3596
3596
|
}],
|
|
3597
3597
|
messages: {
|
|
3598
3598
|
tooManyTrailingOptional: "This {{ kind }} has {{ count }} trailing optional parameters ({{ names }}) — a caller needing only the last one must still pass 'undefined' for every parameter before it. Bundle the trailing optional run into a single destructured 'options' parameter instead.",
|
|
3599
|
-
wrapInOptionsObject: "Bundle the trailing optional parameters into a single 'options' parameter."
|
|
3599
|
+
wrapInOptionsObject: "Bundle the trailing optional parameters into a single 'options' parameter.",
|
|
3600
|
+
placeholderBeforeRest: "This {{ kind }} takes an optional parameter ({{ name }}) immediately before a rest parameter, so a caller passing only the rest values must supply a placeholder for it. Take the rest values alone, or put the optional value and the rest values in one object."
|
|
3600
3601
|
},
|
|
3601
3602
|
defaultOptions: [{ minTrailingOptional: 2 }]
|
|
3602
3603
|
},
|
|
3603
3604
|
create(context, [{ minTrailingOptional }]) {
|
|
3604
3605
|
const { sourceCode } = context;
|
|
3606
|
+
function reportPlaceholderBeforeRest(node, run) {
|
|
3607
|
+
const optionalBeforeRest = run.at(-1);
|
|
3608
|
+
if (optionalBeforeRest === void 0 || node.params.at(-1)?.type !== AST_NODE_TYPES.RestElement) return;
|
|
3609
|
+
const name = resolveFixableParam(optionalBeforeRest)?.identifierNode.name ?? sourceCode.getText(optionalBeforeRest);
|
|
3610
|
+
context.report({
|
|
3611
|
+
node,
|
|
3612
|
+
messageId: "placeholderBeforeRest",
|
|
3613
|
+
data: {
|
|
3614
|
+
kind: describeFunctionKind(node),
|
|
3615
|
+
name
|
|
3616
|
+
}
|
|
3617
|
+
});
|
|
3618
|
+
}
|
|
3605
3619
|
function checkParams(node) {
|
|
3606
3620
|
const run = getTrailingOptionalRun(node.params);
|
|
3607
|
-
if (run.length < minTrailingOptional)
|
|
3621
|
+
if (run.length < minTrailingOptional) {
|
|
3622
|
+
reportPlaceholderBeforeRest(node, run);
|
|
3623
|
+
return;
|
|
3624
|
+
}
|
|
3608
3625
|
const resolved = run.map(resolveFixableParam);
|
|
3609
3626
|
const names = run.map((param, index) => resolved[index]?.identifierNode.name ?? sourceCode.getText(param)).join(", ");
|
|
3610
3627
|
const data = {
|
|
@@ -4034,6 +4051,14 @@ function asIdentifierName(name) {
|
|
|
4034
4051
|
return name;
|
|
4035
4052
|
}
|
|
4036
4053
|
//#endregion
|
|
4054
|
+
//#region src/rules/estree-source.ts
|
|
4055
|
+
/**
|
|
4056
|
+
* Whether the file under lint was parsed to an ESTree `Program`, the tree every rule that reads JavaScript or TypeScript syntax is written against. A rule that is wired onto a block with no `files` (the documented unscoped override placed after the shared config) is also handed the JSON, JSONC and Markdown files the shared config lints, whose source code objects have a different root and no scope manager or parser services; such a rule starts with `if (!isEstreeSource(context.sourceCode)) return {};` after reading its options, so a file of another language is left alone instead of crashing the run.
|
|
4057
|
+
*/
|
|
4058
|
+
function isEstreeSource(sourceCode) {
|
|
4059
|
+
return sourceCode.ast.type === "Program";
|
|
4060
|
+
}
|
|
4061
|
+
//#endregion
|
|
4037
4062
|
//#region src/rules/no-array-isarray-mutation.ts
|
|
4038
4063
|
const MUTATING_INSERT_METHODS$1 = /* @__PURE__ */ new Set([
|
|
4039
4064
|
"push",
|
|
@@ -4064,6 +4089,7 @@ const noArrayIsArrayMutation = createRule$18({
|
|
|
4064
4089
|
},
|
|
4065
4090
|
defaultOptions: [],
|
|
4066
4091
|
create(context) {
|
|
4092
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
4067
4093
|
const services = ESLintUtils.getParserServices(context);
|
|
4068
4094
|
const checker = services.program.getTypeChecker();
|
|
4069
4095
|
function declarationHasReadonlyArrayConstituent(declarationNode) {
|
|
@@ -4758,6 +4784,7 @@ const noEnumNumberWidening = ESLintUtils.RuleCreator((name) => `https://github.c
|
|
|
4758
4784
|
defaultOptions: []
|
|
4759
4785
|
},
|
|
4760
4786
|
create(context) {
|
|
4787
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
4761
4788
|
const services = ESLintUtils.getParserServices(context);
|
|
4762
4789
|
const checker = services.program.getTypeChecker();
|
|
4763
4790
|
function checkNode(expression) {
|
|
@@ -4806,6 +4833,7 @@ const noEnumReverseLookupWidening = ESLintUtils.RuleCreator((name) => `https://g
|
|
|
4806
4833
|
defaultOptions: []
|
|
4807
4834
|
},
|
|
4808
4835
|
create(context) {
|
|
4836
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
4809
4837
|
const services = ESLintUtils.getParserServices(context);
|
|
4810
4838
|
const checker = services.program.getTypeChecker();
|
|
4811
4839
|
return { MemberExpression(node) {
|
|
@@ -4929,7 +4957,7 @@ const noExternalMemberJsxTag = ESLintUtils.RuleCreator((name) => `https://github
|
|
|
4929
4957
|
create(context, [options]) {
|
|
4930
4958
|
const { allowSource, allowTags } = readNoExternalMemberJsxTagOptions(options);
|
|
4931
4959
|
const { sourceCode } = context;
|
|
4932
|
-
if (hasUseClientDirective(sourceCode.ast)) return {};
|
|
4960
|
+
if (!isEstreeSource(sourceCode) || hasUseClientDirective(sourceCode.ast)) return {};
|
|
4933
4961
|
return { JSXOpeningElement(node) {
|
|
4934
4962
|
if (node.name.type !== AST_NODE_TYPES.JSXMemberExpression) return;
|
|
4935
4963
|
const tag = sourceCode.getText(node.name);
|
|
@@ -5553,6 +5581,7 @@ const noMapInstanceofMutation = createRule$16({
|
|
|
5553
5581
|
},
|
|
5554
5582
|
defaultOptions: [],
|
|
5555
5583
|
create(context) {
|
|
5584
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
5556
5585
|
const services = ESLintUtils.getParserServices(context);
|
|
5557
5586
|
const checker = services.program.getTypeChecker();
|
|
5558
5587
|
function declarationHasReadonlyMapConstituent(declarationNode) {
|
|
@@ -6055,7 +6084,7 @@ const noNonSerialisableServerProp = createRule$14({
|
|
|
6055
6084
|
create(context, [options]) {
|
|
6056
6085
|
const { names, allowElements } = readNoNonSerialisableServerPropOptions(options);
|
|
6057
6086
|
const { sourceCode } = context;
|
|
6058
|
-
if (hasUseClientDirective(sourceCode.ast)) return {};
|
|
6087
|
+
if (!isEstreeSource(sourceCode) || hasUseClientDirective(sourceCode.ast)) return {};
|
|
6059
6088
|
return { JSXAttribute(node) {
|
|
6060
6089
|
if (node.name.type !== AST_NODE_TYPES.JSXIdentifier || !names.has(node.name.name)) return;
|
|
6061
6090
|
if (node.value?.type !== AST_NODE_TYPES.JSXExpressionContainer || node.value.expression.type === AST_NODE_TYPES.JSXEmptyExpression) return;
|
|
@@ -6432,6 +6461,7 @@ const noPointlessReassignment = {
|
|
|
6432
6461
|
messages: { pointlessReassignment: "Pointless reassignment: '{{ name }}' is just an alias for '{{ value }}'. Use the original directly." }
|
|
6433
6462
|
},
|
|
6434
6463
|
create(context) {
|
|
6464
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
6435
6465
|
const moduleScope = findTopLevelScope(context.sourceCode.getScope(context.sourceCode.ast));
|
|
6436
6466
|
return { VariableDeclarator(node) {
|
|
6437
6467
|
if (node.id.type !== "Identifier" || node.init?.type !== "Identifier" || node.id.name.startsWith("_")) return;
|
|
@@ -6498,6 +6528,7 @@ const noSetInstanceofMutation = createRule$11({
|
|
|
6498
6528
|
},
|
|
6499
6529
|
defaultOptions: [],
|
|
6500
6530
|
create(context) {
|
|
6531
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
6501
6532
|
const services = ESLintUtils.getParserServices(context);
|
|
6502
6533
|
const checker = services.program.getTypeChecker();
|
|
6503
6534
|
function declarationHasReadonlySetConstituent(declarationNode) {
|
|
@@ -7448,6 +7479,7 @@ const preferNumericSortCompare = createRule$10({
|
|
|
7448
7479
|
defaultOptions: []
|
|
7449
7480
|
},
|
|
7450
7481
|
create(context) {
|
|
7482
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
7451
7483
|
const services = ESLintUtils.getParserServices(context);
|
|
7452
7484
|
const checker = services.program.getTypeChecker();
|
|
7453
7485
|
return { CallExpression(node) {
|
|
@@ -7603,6 +7635,7 @@ const preferReadonlyObjectParam = createRule$8({
|
|
|
7603
7635
|
defaultOptions: []
|
|
7604
7636
|
},
|
|
7605
7637
|
create(context) {
|
|
7638
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
7606
7639
|
const services = ESLintUtils.getParserServices(context);
|
|
7607
7640
|
const checker = services.program.getTypeChecker();
|
|
7608
7641
|
function checkParam(param) {
|
|
@@ -8514,6 +8547,7 @@ const requireCompilerOptions = createRule$5({
|
|
|
8514
8547
|
create(context, [options]) {
|
|
8515
8548
|
const requirements = readCompilerOptionRequirements(options);
|
|
8516
8549
|
if (requirements.length === 0) return {};
|
|
8550
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
8517
8551
|
const { program } = ESLintUtils.getParserServices(context);
|
|
8518
8552
|
const signature = JSON.stringify(requirements.map((requirement) => [requirement.name, requirement.spelled]));
|
|
8519
8553
|
return { Program(node) {
|
|
@@ -8971,6 +9005,7 @@ const scopedFirstParameter = ESLintUtils.RuleCreator((name) => `https://github.c
|
|
|
8971
9005
|
create(context, [options]) {
|
|
8972
9006
|
const { interfaces, name: requiredName, type: requiredType } = readScopedFirstParameterOptions(options);
|
|
8973
9007
|
const { sourceCode } = context;
|
|
9008
|
+
if (!isEstreeSource(context.sourceCode)) return {};
|
|
8974
9009
|
const services = ESLintUtils.getParserServices(context);
|
|
8975
9010
|
const checker = services.program.getTypeChecker();
|
|
8976
9011
|
const checkMembers = (owner, members) => {
|
|
@@ -12126,10 +12161,11 @@ function withWorkspaceGroups(turbo, workspaceArchitecture) {
|
|
|
12126
12161
|
*
|
|
12127
12162
|
* The tri-state per feature threads straight into each builder's own `enabled` option: true forces on (throwing if the underlying peer isn't resolvable), false forces off (skipping resolution entirely), undefined auto-detects (silently empty if unresolvable, or if an equivalent tool, syncpack for packageJsonKeyOrder, or a project's own .gitignore for gitignore, already does the job). One resolution pass per feature; no separate pre-check gate that would resolve twice.
|
|
12128
12163
|
*/
|
|
12129
|
-
function exadevConfig(options = {}
|
|
12164
|
+
function exadevConfig(options = {}) {
|
|
12130
12165
|
const gitignore = buildGitignoreConfig({ enabled: options.gitignore });
|
|
12131
12166
|
return toPublicConfigArray([
|
|
12132
12167
|
...gitignore,
|
|
12168
|
+
{ plugins: { exadev: plugin } },
|
|
12133
12169
|
...recommendedTypeChecked,
|
|
12134
12170
|
...jsdocAndTsdoc,
|
|
12135
12171
|
...jsonCanonicalConfig,
|
|
@@ -12148,8 +12184,7 @@ function exadevConfig(options = {}, ...userConfigs) {
|
|
|
12148
12184
|
...options.testHygiene !== void 0 ? buildTestHygieneConfig(options.testHygiene) : [],
|
|
12149
12185
|
...options.markdownHeadings !== void 0 ? buildMarkdownHeadingsConfig(options.markdownHeadings) : [],
|
|
12150
12186
|
...buildAgentSkillsConfig(options.agentSkills, { ignores: gitignore.flatMap(({ ignores = [] }) => ignores) }),
|
|
12151
|
-
...options.toolingWiring !== void 0 ? buildToolingWiringConfig(options.toolingWiring) : []
|
|
12152
|
-
...userConfigs
|
|
12187
|
+
...options.toolingWiring !== void 0 ? buildToolingWiringConfig(options.toolingWiring) : []
|
|
12153
12188
|
]);
|
|
12154
12189
|
}
|
|
12155
12190
|
/**
|
package/package.json
CHANGED