@noctcore/lint-meta-rules 0.5.0 → 0.6.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 +94 -60
- package/dist/{chunk-Z7TXSZR4.js → chunk-OYFQKSJN.js} +9 -1
- package/dist/i18n.js +1 -1
- package/dist/index.js +1 -1
- package/dist/prisma.js +3 -3
- package/dist/session.cjs +297 -0
- package/dist/session.d.cts +226 -0
- package/dist/session.d.ts +226 -0
- package/dist/session.js +244 -0
- package/dist/trpc.cjs +122 -0
- package/dist/trpc.d.cts +59 -0
- package/dist/trpc.d.ts +59 -0
- package/dist/trpc.js +73 -0
- package/docs/rules/agents-doc-presence.md +13 -4
- package/docs/rules/canonical-helpers-single-home.md +13 -3
- package/docs/rules/dockerfile-base-image-digest-pin.md +17 -4
- package/docs/rules/eslint-config-no-warn.md +22 -7
- package/docs/rules/file-size-ratchet.md +19 -9
- package/docs/rules/github-actions-least-privilege-permissions.md +20 -11
- package/docs/rules/github-actions-no-template-injection.md +21 -12
- package/docs/rules/github-actions-runner-pinned.md +19 -6
- package/docs/rules/github-actions-sha-pinned.md +19 -5
- package/docs/rules/idempotency-key-parity.md +91 -0
- package/docs/rules/layer-rank.md +17 -6
- package/docs/rules/no-cloned-component-folders.md +13 -4
- package/docs/rules/no-warn-severity.md +14 -3
- package/docs/rules/package-shape.md +13 -6
- package/docs/rules/prisma-method-surface.md +19 -4
- package/docs/rules/security-scanner-version-parity.md +16 -7
- package/docs/rules/service-image-digest-pin.md +21 -7
- package/docs/rules/session-epoch-captured.md +89 -0
- package/docs/rules/session-kind-stamped.md +89 -0
- package/docs/rules/session-landing-declared.md +95 -0
- package/docs/rules/session-mint-callers.md +85 -0
- package/docs/rules/tenant-model-registry-parity.md +18 -4
- package/docs/rules/test-runner-segregation.md +15 -5
- package/docs/rules/test-sibling-enforcement.md +12 -5
- package/docs/rules/test-workspace-enrollment.md +14 -6
- package/docs/rules/translation-dead-keys.md +33 -20
- package/docs/rules/ui-primitive-shape.md +13 -4
- package/docs/rules/workspace-graph-parity.md +15 -5
- package/package.json +18 -8
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
> Every translation catalog key is reachable from the source: named by a translation call, or spelled
|
|
4
4
|
> by some string in the code.
|
|
5
5
|
|
|
6
|
+
<!-- begin generated rule header -->
|
|
7
|
+
Runs under `@noctcore/harness`, not ESLint · Factory `createTranslationDeadKeysRule` from `@noctcore/lint-meta-rules/i18n` · Category `source-text` · Fails CI by default: yes
|
|
8
|
+
<!-- end generated rule header -->
|
|
9
|
+
|
|
6
10
|
Import it from the `i18n` entry point, which (unlike the main one) loads ESLint:
|
|
7
11
|
|
|
8
12
|
```ts
|
|
@@ -21,7 +25,27 @@ This rule resolves call sites with the same visitor and catalog loader as
|
|
|
21
25
|
two checks never disagree on which key a call means. It then adds the data routes a per-file rule
|
|
22
26
|
cannot see.
|
|
23
27
|
|
|
24
|
-
## What
|
|
28
|
+
## What it flags
|
|
29
|
+
|
|
30
|
+
Every key in `catalogs` that no scanned source reaches (the routes are listed under
|
|
31
|
+
[What it does not flag](#what-it-does-not-flag)). What is left was named by no call and spelled by no
|
|
32
|
+
string. On a production app with 1,824 keys this reported 55, and every one of them had zero
|
|
33
|
+
references outside the catalogs.
|
|
34
|
+
|
|
35
|
+
### Blind spots
|
|
36
|
+
|
|
37
|
+
It is conservative, not complete. It reports a reached key as dead when the key arrives by a route it
|
|
38
|
+
cannot see:
|
|
39
|
+
|
|
40
|
+
- **Keys that never appear in the scanned source**: server-sent codes, a CMS, another app, JSON
|
|
41
|
+
config. List them in `allow`, or add the files that spell them to `sourceGlobs`.
|
|
42
|
+
- **A variable key under a `keyPrefix` binding**: `useTranslation('ns', { keyPrefix: 'form' })` then
|
|
43
|
+
`t(field)` with `field = 'name'` reaches `form.name`, but no string spells `form.name`. (A static key
|
|
44
|
+
under a `keyPrefix` is resolved correctly.)
|
|
45
|
+
- **Keys built by anything other than `+` or a template literal**: `[a, b].join('.')`,
|
|
46
|
+
`` `${a}` `` split across variables, `String.prototype.concat`.
|
|
47
|
+
|
|
48
|
+
## What it does not flag
|
|
25
49
|
|
|
26
50
|
A key is reached, and never reported, when ANY of these holds:
|
|
27
51
|
|
|
@@ -36,29 +60,13 @@ A key is reached, and never reported, when ANY of these holds:
|
|
|
36
60
|
`` `nav.${id}.label` `` and `'errors.' + code` are patterns, not just call arguments;
|
|
37
61
|
- it matches an `allow` pattern.
|
|
38
62
|
|
|
39
|
-
|
|
63
|
+
It reports **no dead key at all** when a scanned file cannot be analysed (a parse error), or when
|
|
40
64
|
`sourceGlobs` match nothing: in both cases the rule would otherwise call reached keys dead.
|
|
41
65
|
|
|
42
|
-
What is left was named by no call and spelled by no string. On a production app with 1,824 keys this
|
|
43
|
-
reported 55, and every one of them had zero references outside the catalogs.
|
|
44
|
-
|
|
45
|
-
## Blind spots
|
|
46
|
-
|
|
47
|
-
It is conservative, not complete. It reports a reached key as dead when the key arrives by a route it
|
|
48
|
-
cannot see:
|
|
49
|
-
|
|
50
|
-
- **Keys that never appear in the scanned source**: server-sent codes, a CMS, another app, JSON
|
|
51
|
-
config. List them in `allow`, or add the files that spell them to `sourceGlobs`.
|
|
52
|
-
- **A variable key under a `keyPrefix` binding**: `useTranslation('ns', { keyPrefix: 'form' })` then
|
|
53
|
-
`t(field)` with `field = 'name'` reaches `form.name`, but no string spells `form.name`. (A static key
|
|
54
|
-
under a `keyPrefix` is resolved correctly.)
|
|
55
|
-
- **Keys built by anything other than `+` or a template literal**: `[a, b].join('.')`,
|
|
56
|
-
`` `${a}` `` split across variables, `String.prototype.concat`.
|
|
57
|
-
|
|
58
66
|
It also misses some dead keys, by design: any string that happens to equal a key (in any namespace)
|
|
59
67
|
keeps it alive, and a broad pattern such as `` `${x}.title` `` keeps every `*.title` alive.
|
|
60
68
|
|
|
61
|
-
##
|
|
69
|
+
## Options
|
|
62
70
|
|
|
63
71
|
```ts
|
|
64
72
|
createTranslationDeadKeysRule(options?: TranslationDeadKeysOptions): IMetaRule
|
|
@@ -92,7 +100,12 @@ createTranslationDeadKeysRule({
|
|
|
92
100
|
});
|
|
93
101
|
```
|
|
94
102
|
|
|
95
|
-
|
|
103
|
+
### Requirements
|
|
96
104
|
|
|
97
105
|
The `i18n` entry needs the optional peers `eslint` (>= 9) and `@typescript-eslint/parser`. Scanned
|
|
98
106
|
files are parsed as TypeScript with JSX enabled, without type information.
|
|
107
|
+
|
|
108
|
+
## When not to use it
|
|
109
|
+
|
|
110
|
+
If most keys reach the app from outside the scanned source (server-sent codes, a CMS, another app),
|
|
111
|
+
`allow` would have to list most of the catalog and the rule proves little, so skip it.
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
> A folder primitive must ship its proof siblings; a flat primitive must carry none at the ui root.
|
|
4
4
|
|
|
5
|
+
<!-- begin generated rule header -->
|
|
6
|
+
Runs under `@noctcore/harness`, not ESLint · Factory `createUiPrimitiveShapeRule` from `@noctcore/lint-meta-rules` · Category `source-text` · Fails CI by default: yes
|
|
7
|
+
<!-- end generated rule header -->
|
|
8
|
+
|
|
5
9
|
## Why
|
|
6
10
|
|
|
7
11
|
The primitives root is the one place exempt from folder-per-component: flat single files are fine for
|
|
@@ -18,7 +22,15 @@ tested + storied.
|
|
|
18
22
|
`<Name>.<role><extension>` at the ui root is flagged: those proof files belong inside a `<Name>/`
|
|
19
23
|
folder.
|
|
20
24
|
|
|
21
|
-
##
|
|
25
|
+
## What it does not flag
|
|
26
|
+
|
|
27
|
+
- A bare flat primitive (`<uiRoot>/Button.tsx`) with no proof sibling at the ui root.
|
|
28
|
+
- A folder primitive that ships every configured role (`Dialog/Dialog.test.tsx`,
|
|
29
|
+
`Dialog/Dialog.stories.tsx`).
|
|
30
|
+
- A folder without the `barrelFile`, and flat files whose name does not start with a capital letter.
|
|
31
|
+
- Anything outside `uiRoot`.
|
|
32
|
+
|
|
33
|
+
## Options
|
|
22
34
|
|
|
23
35
|
```ts
|
|
24
36
|
createUiPrimitiveShapeRule(options?: UiPrimitiveShapeOptions): IMetaRule
|
|
@@ -32,9 +44,6 @@ createUiPrimitiveShapeRule(options?: UiPrimitiveShapeOptions): IMetaRule
|
|
|
32
44
|
| `extension` | `string` | `'.tsx'` | Extension of primitive and proof files. |
|
|
33
45
|
| `ciCritical` | `boolean` | `true` | Whether a violation fails CI. |
|
|
34
46
|
|
|
35
|
-
De-projected from nightcore, which hardcoded `apps/web/src/components/ui`, the `test`/`stories` roles,
|
|
36
|
-
and the `.tsx` extension.
|
|
37
|
-
|
|
38
47
|
## When not to use it
|
|
39
48
|
|
|
40
49
|
If you do not maintain a flat-vs-folder primitive convention or do not colocate stories/tests, skip
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
> Imported `<scope>/*` specifiers must be declared `workspace:*` deps, and tsconfig `references` must
|
|
4
4
|
> mirror those deps.
|
|
5
5
|
|
|
6
|
+
<!-- begin generated rule header -->
|
|
7
|
+
Runs under `@noctcore/harness`, not ESLint · Factory `createWorkspaceGraphParityRule` from `@noctcore/lint-meta-rules` · Category `config` · Fails CI by default: yes
|
|
8
|
+
<!-- end generated rule header -->
|
|
9
|
+
|
|
6
10
|
## Why
|
|
7
11
|
|
|
8
12
|
A cross-package edge is real in three places at once: the import in source, the `workspace:*` entry in
|
|
@@ -14,13 +18,21 @@ so an edge can never be half-wired.
|
|
|
14
18
|
|
|
15
19
|
For each workspace `package.json`:
|
|
16
20
|
|
|
17
|
-
- **(a) imported ⊆ declared** — every `<scope>/<pkg>` imported under the source dir
|
|
18
|
-
|
|
21
|
+
- **(a) imported ⊆ declared** — every `<scope>/<pkg>` imported under the source dir must appear as a
|
|
22
|
+
`"<scope>/<pkg>": "workspace:*"` dependency.
|
|
19
23
|
- **(b) references mirror deps** — the tsconfig `references` must reference exactly the declared
|
|
20
24
|
workspace deps: a declared dep missing from references, or a referenced package that is not a
|
|
21
25
|
declared dep, is a violation.
|
|
22
26
|
|
|
23
|
-
##
|
|
27
|
+
## What it does not flag
|
|
28
|
+
|
|
29
|
+
- A package importing itself by its own `<scope>/<dir>` name.
|
|
30
|
+
- Imports in test files (`.test.ts`, `.test.tsx`) and files outside `srcDir`.
|
|
31
|
+
- Imports without a `from` clause (`import()`, `require()`, a side-effect `import`): only
|
|
32
|
+
`from '<scope>/<pkg>'` is read.
|
|
33
|
+
- Check (b) for a package with no `tsconfig.json`.
|
|
34
|
+
|
|
35
|
+
## Options
|
|
24
36
|
|
|
25
37
|
```ts
|
|
26
38
|
createWorkspaceGraphParityRule(options?: WorkspaceGraphParityOptions): IMetaRule
|
|
@@ -33,8 +45,6 @@ createWorkspaceGraphParityRule(options?: WorkspaceGraphParityOptions): IMetaRule
|
|
|
33
45
|
| `srcDir` | `string` | `'src'` | Source directory (per package) scanned for imports. |
|
|
34
46
|
| `ciCritical` | `boolean` | `true` | Whether a violation fails CI. |
|
|
35
47
|
|
|
36
|
-
De-projected from nightcore, which hardcoded the `@nightcore` scope and a `src` source directory.
|
|
37
|
-
|
|
38
48
|
## When not to use it
|
|
39
49
|
|
|
40
50
|
If your project does not use the `workspace:*` protocol, or does not use TypeScript project
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noctcore/lint-meta-rules",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Portable, parameterized lint-meta rules — whole-repo / cross-file invariants ESLint cannot reach — for the @noctcore/harness lint-meta runner.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -28,6 +28,16 @@
|
|
|
28
28
|
"import": "./dist/resolved-config.js",
|
|
29
29
|
"require": "./dist/resolved-config.cjs"
|
|
30
30
|
},
|
|
31
|
+
"./session": {
|
|
32
|
+
"types": "./dist/session.d.ts",
|
|
33
|
+
"import": "./dist/session.js",
|
|
34
|
+
"require": "./dist/session.cjs"
|
|
35
|
+
},
|
|
36
|
+
"./trpc": {
|
|
37
|
+
"types": "./dist/trpc.d.ts",
|
|
38
|
+
"import": "./dist/trpc.js",
|
|
39
|
+
"require": "./dist/trpc.cjs"
|
|
40
|
+
},
|
|
31
41
|
"./package.json": "./package.json"
|
|
32
42
|
},
|
|
33
43
|
"files": [
|
|
@@ -53,16 +63,16 @@
|
|
|
53
63
|
"url": "git+https://github.com/noctcore/eslint-plugins.git",
|
|
54
64
|
"directory": "packages/lint-meta-rules"
|
|
55
65
|
},
|
|
56
|
-
"homepage": "https://github.
|
|
66
|
+
"homepage": "https://noctcore.github.io/eslint-plugins/packages/lint-meta-rules/",
|
|
57
67
|
"bugs": "https://github.com/noctcore/eslint-plugins/issues",
|
|
58
68
|
"scripts": {
|
|
59
|
-
"build": "tsup src/index.ts src/i18n.ts src/prisma.ts src/resolved-config.ts --format esm,cjs --dts --clean",
|
|
69
|
+
"build": "tsup src/index.ts src/i18n.ts src/prisma.ts src/resolved-config.ts src/session.ts src/trpc.ts --format esm,cjs --dts --clean",
|
|
60
70
|
"typecheck": "tsc --noEmit",
|
|
61
71
|
"test": "bun test"
|
|
62
72
|
},
|
|
63
73
|
"dependencies": {
|
|
64
|
-
"@noctcore/eslint-plugin-contracts": "^0.7.
|
|
65
|
-
"@noctcore/eslint-plugin-prisma": "^0.5.
|
|
74
|
+
"@noctcore/eslint-plugin-contracts": "^0.7.1",
|
|
75
|
+
"@noctcore/eslint-plugin-prisma": "^0.5.1",
|
|
66
76
|
"@noctcore/harness": "^0.3.0",
|
|
67
77
|
"@typescript-eslint/utils": "^8.61.1"
|
|
68
78
|
},
|
|
@@ -79,9 +89,9 @@
|
|
|
79
89
|
}
|
|
80
90
|
},
|
|
81
91
|
"devDependencies": {
|
|
82
|
-
"@types/node": "^22.
|
|
83
|
-
"@typescript-eslint/parser": "^8.
|
|
84
|
-
"eslint": "^10.
|
|
92
|
+
"@types/node": "^22.20.3",
|
|
93
|
+
"@typescript-eslint/parser": "^8.70.0",
|
|
94
|
+
"eslint": "^10.10.0",
|
|
85
95
|
"tsup": "^8.5.1",
|
|
86
96
|
"typescript": "^5.6.0"
|
|
87
97
|
}
|