@shivaedev/quality 0.9.0 → 0.11.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.11.0 - 2026-10-05
4
+
5
+ ### Added
6
+
7
+ - The preset turns on `noNonScalableViewport` at `error`: a `<meta name="viewport">` with `user-scalable=no` stops people from zooming. The rule has no fix. A repository that upgrades records its existing findings with `quality baseline write --rule biome`.
8
+
9
+ ### Changed
10
+
11
+ - The preset sets `noUndeclaredClasses` and `noInlineStyles` off and declares why: the first cannot resolve a stylesheet imported through an alias and does not know Tailwind utilities, and an inline style is the right tool for a value computed at run time.
12
+
13
+ ## 0.10.0 - 2026-10-05
14
+
15
+ ### Added
16
+
17
+ - `suppressions/no-ignore-deprecations` reports `compilerOptions.ignoreDeprecations` in every tsconfig the repository owns, whatever the file is named, outside the sources too, and names the line of the setting. The setting silences TypeScript's errors for deprecated options; replace the deprecated option and remove it. The rule takes no registry exceptions. A repository that upgrades records its existing findings with `quality baseline write --rule suppressions/no-ignore-deprecations`.
18
+
19
+ ### Changed
20
+
21
+ - `useConsistentTestIt`, which asks for `it` over `test`, inside a `describe` too, only reports: `quality fix` no longer rewrites `test` to `it`, and the author decides.
22
+
3
23
  ## 0.9.0 - 2026-10-04
4
24
 
5
25
  ### Changed
package/README.md CHANGED
@@ -101,13 +101,14 @@ A repository with existing comments adopts the rules through the baseline, for e
101
101
 
102
102
  ### Suppressions
103
103
 
104
- A check that is silenced at one site hides the problem instead of fixing it. Three rules close the escape hatches, and none of them takes registry exceptions: a registry entry that names one fails the gate as stale. A repository with existing suppressions adopts the rules through the baseline.
104
+ A check that is silenced at one site hides the problem instead of fixing it. Four rules close the escape hatches, and none of them takes registry exceptions: a registry entry that names one fails the gate as stale. A repository with existing suppressions adopts the rules through the baseline.
105
105
 
106
106
  | Rule | Reports | Options |
107
107
  | --- | --- | --- |
108
108
  | `suppressions/no-inline` | Every comment directive that silences a linter, the compiler or a formatter, except `@ts-expect-error` in a type test | none |
109
109
  | `suppressions/no-double-cast` | A cast through `unknown`, `any` or `never`: `x as unknown as T`, `x as any as T`, `x as never as T`, `<T><unknown>x` | none |
110
110
  | `suppressions/biome-overrides` | A Biome setting that turns a check off or down, or keeps files out of it, without a declaration, and a declaration that matches no setting | `declared` |
111
+ | `suppressions/no-ignore-deprecations` | `compilerOptions.ignoreDeprecations` in a tsconfig, which silences TypeScript's errors for deprecated options | none |
111
112
 
112
113
  `suppressions/no-inline` reports these directives, wherever a line of a comment starts with one:
113
114
 
@@ -119,6 +120,8 @@ A check that is silenced at one site hides the problem instead of fixing it. Thr
119
120
 
120
121
  Coverage hints (`c8 ignore`, `v8 ignore`, `istanbul ignore`) are allowed: they leave code out of a coverage measure and silence no linter or compiler. The rule reads the TypeScript and JavaScript modules among the sources, declaration files included, and every `.css`, `.scss` and `.less` file among the checked files, whatever `extensions` says. Each finding names the line its comment starts on and has the directive as its subject.
121
122
 
123
+ `suppressions/no-ignore-deprecations` reads every JSON file in the repository whose `compilerOptions` sets `ignoreDeprecations`, whatever the file is named, so a shared config such as `tsconfig/base.json` counts as much as a `tsconfig.json`. Like `manifests/sorted`, it walks the whole repository, not only the sources, and skips files ignored by git and `node_modules`. Each finding names the line of the setting. Replace the deprecated option it hides, then remove it.
124
+
122
125
  #### Type tests
123
126
 
124
127
  A type test proves that an API rejects what its types forbid, and TypeScript asserts a compile error only through `@ts-expect-error`, which fails as soon as the error it expects goes away. So `@ts-expect-error`, and no other directive, is allowed in a type test: a file named `*.typecheck.test.ts` or `typecheck.test.ts` (or `.tsx`). The compiler checks these files; [Vitest projects](#vitest-projects) never run them. `@ts-ignore`, `@ts-nocheck` and every linter and formatter directive stay reported in them. A test that must pass a rejected value at run time, to prove the runtime refuses it too, calls the API through `Reflect.apply` instead of a directive.
@@ -386,15 +389,15 @@ quality baseline migrate [--config <file>] [--from <file>]
386
389
  The preset sets:
387
390
 
388
391
  - **Formatting:** tabs, a line width of 150, double quotes, semicolons, trailing commas and operators at the start of a wrapped line. An object key keeps its quotes, because a quoted key marks a [foreign name](#foreign-names).
389
- - **Lint:** every rule Biome recommends, at `error`, and a list of stricter rules on top, among them `noUnsafeTypeAssertion`, `useBlockStatements`, `useNumericSeparators`, `useUnicodeRegex`, `useLiteralKeys`, `noFloatingPromises`, `useExhaustiveDependencies`, `it` for every test, function declarations over function expressions, interfaces for object types, a cognitive complexity limit of 15, no nested ternaries, and the naming rules under [Naming](#naming).
392
+ - **Lint:** every rule Biome recommends, at `error`, and a list of stricter rules on top, among them `noUnsafeTypeAssertion`, `useBlockStatements`, `useNumericSeparators`, `useUnicodeRegex`, `useLiteralKeys`, `noFloatingPromises`, `useExhaustiveDependencies`, `it` for every test, no viewport that disables zoom (`noNonScalableViewport`), function declarations over function expressions, interfaces for object types, a cognitive complexity limit of 15, no nested ternaries, and the naming rules under [Naming](#naming).
390
393
  - **Imports:** organized in five groups: Node and Bun builtins, packages, `@shivaedev/*` packages, aliases and relative paths. Biome counts as an alias a specifier that starts with `#`, `@/`, `~`, `$` or `%`; a tsconfig path such as `@app/*` sorts with the packages, and Biome's `noUndeclaredDependencies` takes it for one, so name aliases in a form Biome recognizes.
391
394
  - **Assist:** organized imports and sorted keys, attributes, enum members, interface members and properties. Keys are sorted in JSON and in object literals alike, but only reported: `quality fix` leaves the order to the author. `package.json` is left out, because `manifests/sorted` gives it the order npm users expect.
392
395
  - **Plugins:** GritQL rules that ban ambient time, randomness, `console` and `process.env` for Effect's services, and the naming plugins under [Naming](#naming). They load from `./node_modules/@shivaedev/quality/biome/plugins`, so the package must be installed at the repository root.
393
396
  - Files ignored by git are skipped.
394
397
 
395
- The preset turns off `noUnusedVariables` and `noUnusedFunctionParameters`, because the tsconfig presets report them through TypeScript, allows default exports in `*.config.*` files, which tools load through the default export, and turns off `useComponentExportOnlyModules` in `*.test.*` and `*.spec.*` files, which define the helper and harness components they render. It also turns off `noProcessGlobal`, `useJsonImportAttributes`, `noMisusedPromises`, `useExhaustiveSwitchCases`, `useSortedClasses`, `noDelete`, `useConsistentArrayType`, `useConsistentCurlyBraces`, `noEqualsToNull` and `noSkippedTests`, because `quality fix` applies every lint fix and their fixes changed behavior or did not terminate on real code. It declares these weakenings in its `declarations.json`, so `suppressions/biome-overrides` takes them as declared. Every other weakening a repository adds is an override it declares with a reason.
398
+ The preset turns off `noUnusedVariables` and `noUnusedFunctionParameters`, because the tsconfig presets report them through TypeScript, allows default exports in `*.config.*` files, which tools load through the default export, and turns off `useComponentExportOnlyModules` in `*.test.*` and `*.spec.*` files, which define the helper and harness components they render. It also turns off `noProcessGlobal`, `useJsonImportAttributes`, `noMisusedPromises`, `useExhaustiveSwitchCases`, `useSortedClasses`, `noDelete`, `useConsistentArrayType`, `useConsistentCurlyBraces`, `noEqualsToNull` and `noSkippedTests`, because `quality fix` applies every lint fix and their fixes changed behavior or did not terminate on real code. It keeps `noUndeclaredClasses` off, because the rule cannot resolve a stylesheet imported through an alias and Tailwind utilities are not declared in CSS, and `noInlineStyles` off, because an inline style is the right tool for a value computed at run time. It declares these weakenings in its `declarations.json`, so `suppressions/biome-overrides` takes them as declared. Every other weakening a repository adds is an override it declares with a reason.
396
399
 
397
- An autofix applies only a change that removes no decision; a fix that can change behavior or delete something written on purpose reports only, and the author decides. The preset keeps `noAccessKey`, `noAriaHiddenOnFocusable`, `noAutofocus`, `noInteractiveElementToNoninteractiveRole`, `noNoninteractiveElementToInteractiveRole`, `noNoninteractiveTabindex`, `noRedundantRoles`, `useValidAriaProps`, `useValidAriaRole`, `noImportantStyles`, `noConstAssign`, `noUnusedPrivateClassMembers`, `useExhaustiveDependencies`, `noFloatingPromises`, `useRegexpTest`, `useUnicodeRegex`, `noNonNullAssertion`, `useAtIndex`, `noParametersOnlyUsedInRecursion` and `useNamingConvention` at `error` with their fixes off, and `quality fix` skips `noDuplicateObjectKeys` and `useSortedKeys`, whose fixes Biome's config cannot turn off.
400
+ An autofix applies only a change that removes no decision; a fix that can change behavior or delete something written on purpose reports only, and the author decides. The preset keeps `noAccessKey`, `noAriaHiddenOnFocusable`, `noAutofocus`, `noInteractiveElementToNoninteractiveRole`, `noNoninteractiveElementToInteractiveRole`, `noNoninteractiveTabindex`, `noRedundantRoles`, `useValidAriaProps`, `useValidAriaRole`, `noImportantStyles`, `noConstAssign`, `noUnusedPrivateClassMembers`, `useExhaustiveDependencies`, `noFloatingPromises`, `useConsistentTestIt`, `useRegexpTest`, `useUnicodeRegex`, `noNonNullAssertion`, `useAtIndex`, `noParametersOnlyUsedInRecursion` and `useNamingConvention` at `error` with their fixes off, and `quality fix` skips `noDuplicateObjectKeys` and `useSortedKeys`, whose fixes Biome's config cannot turn off.
398
401
 
399
402
  The `biome` rule runs `biome check` with the repository's config and reports each finding as `biome/<category>`, such as `biome/lint/style/useBlockStatements`, `biome/assist/source/useSortedKeys`, `biome/format` or `biome/plugin`, so Biome's findings go through the baseline like any other rule's. `quality baseline write --rule biome` takes in every Biome category at once. A finding below `error`, such as a rule a repository declared at `warn`, is not reported. The rule also asks for a root `biome.json` or `biome.jsonc` that extends the preset, and it takes no registry exceptions. A Biome config that Biome cannot load stops the run with Biome's message.
400
403
 
@@ -73,5 +73,15 @@
73
73
  "includes": ["**"],
74
74
  "reason": "An unused private member asks whether it is still needed or should be used; deleting it automatically skips that decision.",
75
75
  "rule": "lint/correctness/noUnusedPrivateClassMembers"
76
+ },
77
+ {
78
+ "includes": ["**"],
79
+ "reason": "The rule cannot resolve a stylesheet imported through an alias, and Tailwind utilities are not declared in any CSS file, so it misses real findings and reports valid classes.",
80
+ "rule": "lint/nursery/noUndeclaredClasses"
81
+ },
82
+ {
83
+ "includes": ["**"],
84
+ "reason": "An inline style is the right tool for a value computed at run time, such as a position, a transform or a drag offset.",
85
+ "rule": "lint/nursery/noInlineStyles"
76
86
  }
77
87
  ]
package/biome/preset.json CHANGED
@@ -287,6 +287,7 @@
287
287
  "noExtendNative": "error",
288
288
  "noFloatingPromises": { "fix": "none", "level": "error" },
289
289
  "noIdenticalTestTitle": "error",
290
+ "noInlineStyles": "off",
290
291
  "noInvalidFileInputAccept": "error",
291
292
  "noInvalidPropertyInitValue": "error",
292
293
  "noJsonUnsafeValues": "error",
@@ -295,6 +296,7 @@
295
296
  "noLoopFunc": "error",
296
297
  "noMisusedPromises": "off",
297
298
  "noNegationInEqualityCheck": "error",
299
+ "noNonScalableViewport": "error",
298
300
  "noObsoleteTags": "error",
299
301
  "noPlaywrightElementHandle": "error",
300
302
  "noPlaywrightEval": "error",
@@ -309,6 +311,7 @@
309
311
  "noReactStringRefs": "error",
310
312
  "noThisOutsideOfClass": "error",
311
313
  "noTopLevelLiterals": "error",
314
+ "noUndeclaredClasses": "off",
312
315
  "noUnmodifiedLoopCondition": "error",
313
316
  "noUnnecessaryTemplateExpression": "error",
314
317
  "noUnsafeIframeSandbox": "error",
@@ -332,6 +335,7 @@
332
335
  },
333
336
  "useConsistentObjectKeys": "error",
334
337
  "useConsistentTestIt": {
338
+ "fix": "none",
335
339
  "level": "error",
336
340
  "options": {
337
341
  "function": "it",
@@ -7,7 +7,7 @@ export declare const builtInRules: readonly [import("../rule.ts").Rule<"structur
7
7
  }>, import("../rule.ts").Rule<"comments/no-line-reference", undefined>, import("../rule.ts").Rule<"comments/no-pr-reference", undefined>, import("../rule.ts").Rule<"comments/no-banner", undefined>, import("../rule.ts").Rule<"comments/no-todo", undefined>, import("../rule.ts").Rule<"comments/no-environment-pragma", undefined>, import("../rule.ts").Rule<"comments/max-per-file", {
8
8
  readonly allow?: readonly string[];
9
9
  readonly max?: number;
10
- }>, import("../rule.ts").Rule<"suppressions/no-inline", undefined>, import("../rule.ts").Rule<"suppressions/no-double-cast", undefined>, import("../rule.ts").Rule<"suppressions/biome-overrides", {
10
+ }>, import("../rule.ts").Rule<"suppressions/no-inline", undefined>, import("../rule.ts").Rule<"suppressions/no-double-cast", undefined>, import("../rule.ts").Rule<"suppressions/no-ignore-deprecations", undefined>, import("../rule.ts").Rule<"suppressions/biome-overrides", {
11
11
  readonly declared?: readonly {
12
12
  readonly includes: readonly [string, ...string[]];
13
13
  readonly reason: string;
@@ -1 +1 @@
1
- {"version":3,"file":"built-in.d.ts","sourceRoot":"","sources":["../../src/rules/built-in.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuBf,CAAC"}
1
+ {"version":3,"file":"built-in.d.ts","sourceRoot":"","sources":["../../src/rules/built-in.ts"],"names":[],"mappings":"AAwBA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwBf,CAAC"}
@@ -15,6 +15,7 @@ import { importsResolvable } from "#rules/imports/resolvable.ts";
15
15
  import { biomeOverrides } from "#rules/suppressions/biome-overrides.ts";
16
16
  import { noDoubleCast } from "#rules/suppressions/no-double-cast.ts";
17
17
  import { noInline } from "#rules/suppressions/no-inline.ts";
18
+ import { noIgnoreDeprecations } from "#rules/suppressions/noIgnoreDeprecations.ts";
18
19
  import { testsColocated } from "#rules/test-names/colocated.ts";
19
20
  import { testsFollow } from "#rules/test-names/follow.ts";
20
21
  import { biome } from "./biome.js";
@@ -31,6 +32,7 @@ export const builtInRules = [
31
32
  maxPerFile,
32
33
  noInline,
33
34
  noDoubleCast,
35
+ noIgnoreDeprecations,
34
36
  biomeOverrides,
35
37
  biome,
36
38
  importCycles,
@@ -1 +1 @@
1
- {"version":3,"file":"built-in.js","sourceRoot":"","sources":["../../src/rules/built-in.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,CAAC,MAAM,YAAY,GAAG;IAC3B,QAAQ;IACR,OAAO;IACP,eAAe;IACf,aAAa;IACb,QAAQ;IACR,MAAM;IACN,mBAAmB;IACnB,UAAU;IACV,QAAQ;IACR,YAAY;IACZ,cAAc;IACd,KAAK;IACL,YAAY;IACZ,YAAY;IACZ,iBAAiB;IACjB,cAAc;IACd,eAAe;IACf,gBAAgB;IAChB,WAAW;IACX,UAAU;IACV,WAAW;IACX,cAAc;CACL,CAAC"}
1
+ {"version":3,"file":"built-in.js","sourceRoot":"","sources":["../../src/rules/built-in.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,6CAA6C,CAAC;AACnF,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,CAAC,MAAM,YAAY,GAAG;IAC3B,QAAQ;IACR,OAAO;IACP,eAAe;IACf,aAAa;IACb,QAAQ;IACR,MAAM;IACN,mBAAmB;IACnB,UAAU;IACV,QAAQ;IACR,YAAY;IACZ,oBAAoB;IACpB,cAAc;IACd,KAAK;IACL,YAAY;IACZ,YAAY;IACZ,iBAAiB;IACjB,cAAc;IACd,eAAe;IACf,gBAAgB;IAChB,WAAW;IACX,UAAU;IACV,WAAW;IACX,cAAc;CACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const noIgnoreDeprecations: import("#rule.ts").Rule<"suppressions/no-ignore-deprecations", undefined>;
2
+ //# sourceMappingURL=noIgnoreDeprecations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noIgnoreDeprecations.d.ts","sourceRoot":"","sources":["../../../src/rules/suppressions/noIgnoreDeprecations.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,oBAAoB,2EAW/B,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { relative } from "node:path";
2
+ import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
3
+ import { Effect } from "effect";
4
+ import { posix } from "#inventory/ignore-scope.ts";
5
+ import { walk } from "#inventory/walk.ts";
6
+ import { defineRule } from "#rule.ts";
7
+ import { member, parseJsonc } from "#rules/suppressions/biome/json.ts";
8
+ const OPTION = "ignoreDeprecations";
9
+ const MESSAGE = `Sets "${OPTION}", which silences TypeScript's errors for deprecated options. Remove it and replace the deprecated option it hides.`;
10
+ function settingIn(path, text) {
11
+ const parsed = parseJsonc(path, text);
12
+ const setting = parsed._tag === "Parsed" ? member(member(parsed.json, "compilerOptions"), OPTION) : undefined;
13
+ return setting === undefined ? [] : [{ file: path, line: setting.line, message: MESSAGE, subject: OPTION }];
14
+ }
15
+ export const noIgnoreDeprecations = defineRule({
16
+ check: async ({ readText, root }) => {
17
+ const paths = await Effect.runPromise(walk(root).pipe(Effect.provide(NodeFileSystem.layer)));
18
+ const configs = paths.map((path) => posix(relative(root, path))).filter((path) => path.endsWith(".json"));
19
+ const texts = await Promise.all(configs.toSorted().map(async (path) => ({ path, text: (await readText(path)) ?? "" })));
20
+ return texts.filter(({ text }) => text.includes(OPTION)).flatMap(({ path, text }) => settingIn(path, text));
21
+ },
22
+ description: "ignoreDeprecations in a tsconfig silences TypeScript's errors for deprecated options, so the project keeps an option the next TypeScript removes. Replace the deprecated option and remove ignoreDeprecations.",
23
+ id: "suppressions/no-ignore-deprecations",
24
+ registrable: false,
25
+ });
26
+ //# sourceMappingURL=noIgnoreDeprecations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noIgnoreDeprecations.js","sourceRoot":"","sources":["../../../src/rules/suppressions/noIgnoreDeprecations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,KAAK,cAAc,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAgB,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAEvE,MAAM,MAAM,GAAG,oBAAoB,CAAC;AAEpC,MAAM,OAAO,GAAG,SAAS,MAAM,qHAAqH,CAAC;AAErJ,SAAS,SAAS,CAAC,IAAY,EAAE,IAAY;IAC5C,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9G,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AAC7G,CAAC;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG,UAAU,CAAC;IAC9C,KAAK,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE;QACnC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7F,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QAC1G,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACxH,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAC7G,CAAC;IACD,WAAW,EACV,gNAAgN;IACjN,EAAE,EAAE,qCAAqC;IACzC,WAAW,EAAE,KAAK;CAClB,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/quality",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Repository quality gate with typed rules, a baseline of existing debt and a registry of reasoned exceptions",
5
5
  "homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/quality#readme",
6
6
  "bugs": {
@@ -41,6 +41,8 @@
41
41
  "dist",
42
42
  "biome",
43
43
  "src",
44
+ "!src/**/*.test.ts",
45
+ "!src/**/*.spec.ts",
44
46
  "tsconfig",
45
47
  "CHANGELOG.md",
46
48
  "README.md"
@@ -15,6 +15,7 @@ import { importsResolvable } from "#rules/imports/resolvable.ts";
15
15
  import { biomeOverrides } from "#rules/suppressions/biome-overrides.ts";
16
16
  import { noDoubleCast } from "#rules/suppressions/no-double-cast.ts";
17
17
  import { noInline } from "#rules/suppressions/no-inline.ts";
18
+ import { noIgnoreDeprecations } from "#rules/suppressions/noIgnoreDeprecations.ts";
18
19
  import { testsColocated } from "#rules/test-names/colocated.ts";
19
20
  import { testsFollow } from "#rules/test-names/follow.ts";
20
21
  import { biome } from "./biome.ts";
@@ -32,6 +33,7 @@ export const builtInRules = [
32
33
  maxPerFile,
33
34
  noInline,
34
35
  noDoubleCast,
36
+ noIgnoreDeprecations,
35
37
  biomeOverrides,
36
38
  biome,
37
39
  importCycles,
@@ -0,0 +1,30 @@
1
+ import { relative } from "node:path";
2
+ import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
3
+ import { Effect } from "effect";
4
+ import { posix } from "#inventory/ignore-scope.ts";
5
+ import { walk } from "#inventory/walk.ts";
6
+ import { defineRule, type Finding } from "#rule.ts";
7
+ import { member, parseJsonc } from "#rules/suppressions/biome/json.ts";
8
+
9
+ const OPTION = "ignoreDeprecations";
10
+
11
+ const MESSAGE = `Sets "${OPTION}", which silences TypeScript's errors for deprecated options. Remove it and replace the deprecated option it hides.`;
12
+
13
+ function settingIn(path: string, text: string): readonly Finding[] {
14
+ const parsed = parseJsonc(path, text);
15
+ const setting = parsed._tag === "Parsed" ? member(member(parsed.json, "compilerOptions"), OPTION) : undefined;
16
+ return setting === undefined ? [] : [{ file: path, line: setting.line, message: MESSAGE, subject: OPTION }];
17
+ }
18
+
19
+ export const noIgnoreDeprecations = defineRule({
20
+ check: async ({ readText, root }) => {
21
+ const paths = await Effect.runPromise(walk(root).pipe(Effect.provide(NodeFileSystem.layer)));
22
+ const configs = paths.map((path) => posix(relative(root, path))).filter((path) => path.endsWith(".json"));
23
+ const texts = await Promise.all(configs.toSorted().map(async (path) => ({ path, text: (await readText(path)) ?? "" })));
24
+ return texts.filter(({ text }) => text.includes(OPTION)).flatMap(({ path, text }) => settingIn(path, text));
25
+ },
26
+ description:
27
+ "ignoreDeprecations in a tsconfig silences TypeScript's errors for deprecated options, so the project keeps an option the next TypeScript removes. Replace the deprecated option and remove ignoreDeprecations.",
28
+ id: "suppressions/no-ignore-deprecations",
29
+ registrable: false,
30
+ });