@noctcore/eslint-plugin-architecture 0.1.0 → 0.2.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/README.md +8 -2
- package/dist/index.cjs +444 -28
- package/dist/index.d.cts +42 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +444 -28
- package/docs/rules/barrel-purity.md +57 -0
- package/docs/rules/colocated-test-required.md +48 -0
- package/docs/rules/filename-matches-export.md +56 -0
- package/docs/rules/max-import-depth.md +52 -0
- package/package.json +1 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# `noctcore-architecture/colocated-test-required`
|
|
2
|
+
|
|
3
|
+
> A source file matching an `include` glob must have a colocated `*.test.*` / `*.spec.*` sibling on disk. Off until `include` is configured.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
Some files are risky enough that shipping them untested should be a lint error, not a code-review
|
|
8
|
+
afterthought — hooks, services, reducers, money math. This rule lets you name those globs and then
|
|
9
|
+
requires each matching file to carry its test right next to it, so the test travels with the code and
|
|
10
|
+
is obvious when missing.
|
|
11
|
+
|
|
12
|
+
## What it flags
|
|
13
|
+
|
|
14
|
+
For a file matching `include`, the rule reads the file's directory and looks for a sibling whose name
|
|
15
|
+
is `<stem>.test.<ext>` or `<stem>.spec.<ext>` (any extension). If none exists, it reports.
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
src/hooks/useCart.ts ← include: ['**/use*.ts']
|
|
19
|
+
src/hooks/useCart.test.ts ✓ colocated test present
|
|
20
|
+
src/hooks/useWishlist.ts ✗ no useWishlist.test.* / .spec.* sibling
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
A test file that itself matches `include` is never asked to test itself. Directory listings are
|
|
24
|
+
cached for the lint run, so a folder full of gated files is read once.
|
|
25
|
+
|
|
26
|
+
## Options
|
|
27
|
+
|
|
28
|
+
| Option | Type | Default | Meaning |
|
|
29
|
+
| --- | --- | --- | --- |
|
|
30
|
+
| `include` | `string[]` | `[]` | Globs of source files that must have a colocated test. **Empty = the rule is off.** |
|
|
31
|
+
| `ignore` | `string[]` | `[]` | Globs of files to exempt even when they match `include`. |
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
'noctcore-architecture/colocated-test-required': ['error', {
|
|
35
|
+
include: ['**/use*.ts', '**/*.service.ts', '**/*.reducer.ts'],
|
|
36
|
+
ignore: ['**/*.d.ts'],
|
|
37
|
+
}]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## In `recommended`
|
|
41
|
+
|
|
42
|
+
Ships **`'off'`**. There is no universal "everything needs a colocated test" default, so the
|
|
43
|
+
`recommended` preset cannot safely enable it — turn it on with your own `include` globs.
|
|
44
|
+
|
|
45
|
+
## When not to use it
|
|
46
|
+
|
|
47
|
+
If your tests live in a separate `__tests__` tree or a top-level `test/` directory rather than beside
|
|
48
|
+
the source, this colocation check does not model your layout. Leave it off.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# `noctcore-architecture/filename-matches-export`
|
|
2
|
+
|
|
3
|
+
> A file's basename must match its primary export (a default export, or the sole named export).
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
When a module has one clear public export, the filename should announce it. A `helpers.ts` that
|
|
8
|
+
exports a single `formatDate` hides its contents from anyone scanning the file tree, and a rename of
|
|
9
|
+
one without the other leaves the two permanently out of sync. Keeping them aligned makes the tree
|
|
10
|
+
self-describing.
|
|
11
|
+
|
|
12
|
+
## What it flags
|
|
13
|
+
|
|
14
|
+
The rule first resolves the file's **primary export**:
|
|
15
|
+
|
|
16
|
+
1. If the file has a default export with a name (`export default function Foo`, `export default
|
|
17
|
+
class Foo`, `export default Foo`), that name is the primary export. An anonymous default
|
|
18
|
+
(`export default () => …`) has no identifier to compare, so the file is skipped.
|
|
19
|
+
2. Otherwise, if the file has exactly one local named export, that is the primary export.
|
|
20
|
+
3. Otherwise (no primary, or several named exports) the file is skipped.
|
|
21
|
+
|
|
22
|
+
The basename and the identifier are compared **case- and separator-insensitively**, so naming
|
|
23
|
+
conventions never collide:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
// TaskCard.tsx → export default function TaskCard() {} ✓
|
|
27
|
+
// use-thing.ts → export const useThing = () => {} ✓ (kebab ↔ camel)
|
|
28
|
+
// helpers.ts → export const formatDate = () => {} ✗ (genuine mismatch)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`index` files and files matching an `ignore` glob are always skipped.
|
|
32
|
+
|
|
33
|
+
## Suggestion, not autofix
|
|
34
|
+
|
|
35
|
+
A mismatch offers a **suggestion** to rename the export to the filename (the code-side resolution —
|
|
36
|
+
the other is renaming the file). It is never an autofix: renaming a public identifier is a decision a
|
|
37
|
+
human should confirm. When the basename is not a valid identifier (e.g. `2fa.ts`), the mismatch is
|
|
38
|
+
reported without a suggestion.
|
|
39
|
+
|
|
40
|
+
## Options
|
|
41
|
+
|
|
42
|
+
| Option | Type | Default | Meaning |
|
|
43
|
+
| --- | --- | --- | --- |
|
|
44
|
+
| `ignore` | `string[]` | `[]` | Globs (supporting `**`, `*`, `?`) of files to skip. |
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
'noctcore-architecture/filename-matches-export': ['error', {
|
|
48
|
+
ignore: ['**/*.stories.tsx', '**/route.ts'],
|
|
49
|
+
}]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## When not to use it
|
|
53
|
+
|
|
54
|
+
If your files routinely export several unrelated symbols, or you use fixed conventional filenames
|
|
55
|
+
(`route.ts`, `handler.ts`) that will never match their export, add them to `ignore` or leave the rule
|
|
56
|
+
off.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# `noctcore-architecture/max-import-depth`
|
|
2
|
+
|
|
3
|
+
> A relative import may not climb more than `max` parent levels (default 3). Autofixed to a path alias when one is configured.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
A relative import that climbs several directories (`../../../../shared/log`) is brittle and hard to
|
|
8
|
+
read: it couples the file to its exact position in the tree, so moving either end breaks the path. A
|
|
9
|
+
path alias (`@/shared/log`) is stable and self-locating. This rule caps how far a relative import may
|
|
10
|
+
reach before it must become an alias.
|
|
11
|
+
|
|
12
|
+
## What it flags
|
|
13
|
+
|
|
14
|
+
Any relative specifier whose leading `..` run exceeds `max` is reported, on every source-carrying
|
|
15
|
+
construct: `import`, `import()`, `export … from`, and `export * from`.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
// max: 3 (default)
|
|
19
|
+
import a from '../../../shared'; // ✓ exactly at the limit
|
|
20
|
+
import b from '../../../../shared/log'; // ✗ climbs 4 levels
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Autofix
|
|
24
|
+
|
|
25
|
+
When `alias` maps a directory-anchor segment to an alias prefix, a too-deep import that resolves
|
|
26
|
+
*through* that anchor is autofixed:
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
'noctcore-architecture/max-import-depth': ['error', {
|
|
30
|
+
alias: { src: '@' },
|
|
31
|
+
}]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// from src/a/b/c/d/deep.ts
|
|
36
|
+
import x from '../../../../shared/log'; // → import x from '@/shared/log';
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
With no matching alias anchor on the resolved path, the violation is **reported without a fix** —
|
|
40
|
+
there is nothing safe to rewrite it to.
|
|
41
|
+
|
|
42
|
+
## Options
|
|
43
|
+
|
|
44
|
+
| Option | Type | Default | Meaning |
|
|
45
|
+
| --- | --- | --- | --- |
|
|
46
|
+
| `max` | `integer` | `3` | Maximum number of `..` parent hops a relative import may take. |
|
|
47
|
+
| `alias` | `Record<string, string>` | `{}` | Map of directory-anchor segment → alias prefix used to autofix, e.g. `{ src: '@' }`. |
|
|
48
|
+
|
|
49
|
+
## When not to use it
|
|
50
|
+
|
|
51
|
+
If you do not use path aliases and genuinely prefer deep relative imports, raise `max` or leave the
|
|
52
|
+
rule off.
|
package/package.json
CHANGED