unguard 0.7.1 → 0.9.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 +96 -33
- package/dist/chunk-I4RZLVE2.js +1718 -0
- package/dist/chunk-I4RZLVE2.js.map +1 -0
- package/dist/cli.js +214 -77
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +54 -21
- package/dist/index.js +5 -1
- package/package.json +9 -8
- package/dist/chunk-LEYOVUJE.js +0 -1222
- package/dist/chunk-LEYOVUJE.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Unguard your code. Defend against overdefensive AI-generated code.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Type-aware static analysis powered by the TypeScript compiler API.
|
|
6
|
+
|
|
7
|
+
If `??` is on a non-nullable type, you don't need it.
|
|
8
|
+
|
|
9
|
+
If `?.` is on a guaranteed object, it's noise.
|
|
10
|
+
|
|
11
|
+
unguard proves it with types.
|
|
6
12
|
|
|
7
13
|
## Install
|
|
8
14
|
|
|
@@ -19,40 +25,81 @@ npx unguard
|
|
|
19
25
|
## Usage
|
|
20
26
|
|
|
21
27
|
```bash
|
|
22
|
-
unguard src/
|
|
23
|
-
unguard src
|
|
24
|
-
unguard src
|
|
25
|
-
unguard src
|
|
26
|
-
unguard src
|
|
27
|
-
unguard src
|
|
28
|
+
unguard src # scan files/directories
|
|
29
|
+
unguard src --config ./unguard.config.json # load config
|
|
30
|
+
unguard src --ignore '**/*.gen.ts' # add ignore globs
|
|
31
|
+
unguard src --filter no-any-cast # run a single rule
|
|
32
|
+
unguard src --rule duplicate-*=warning # override rule severity/policy
|
|
33
|
+
unguard src --rule category:cross-file=warning
|
|
34
|
+
unguard src --rule tag:safety=error
|
|
35
|
+
unguard src --severity=error,warning # show errors+warnings
|
|
36
|
+
unguard src --fail-on=error # fail only on errors
|
|
37
|
+
unguard src --format=flat # one-line-per-diagnostic, grepable
|
|
38
|
+
unguard src --format=flat | grep error
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Add `unguard` to your lint check, especially if code is written by AI.
|
|
42
|
+
|
|
43
|
+
### Config
|
|
44
|
+
|
|
45
|
+
`unguard` automatically loads `./unguard.config.json` (or `./.unguardrc.json`). Use `--config <path>` to specify another file.
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"paths": ["src", "apps/web/src"],
|
|
50
|
+
"ignore": ["**/*.gen.ts", "**/routeTree.gen.ts"],
|
|
51
|
+
"rules": {
|
|
52
|
+
"duplicate-*": "warning",
|
|
53
|
+
"category:cross-file": "warning",
|
|
54
|
+
"tag:safety": "error",
|
|
55
|
+
"no-ts-ignore": "error",
|
|
56
|
+
"prefer-*": "off"
|
|
57
|
+
},
|
|
58
|
+
"failOn": "error"
|
|
59
|
+
}
|
|
28
60
|
```
|
|
29
61
|
|
|
30
|
-
|
|
62
|
+
`rules` values can be `off`, `info`, `warning`, or `error`.
|
|
63
|
+
Selectors support:
|
|
64
|
+
- exact rule id: `no-ts-ignore`
|
|
65
|
+
- wildcard: `duplicate-*`
|
|
66
|
+
- category: `category:cross-file`
|
|
67
|
+
- tag: `tag:safety`
|
|
68
|
+
|
|
69
|
+
### Ignore behavior
|
|
70
|
+
|
|
71
|
+
`unguard` ignores:
|
|
72
|
+
- built-in: `node_modules`, `dist`, `.git`, declaration files (`*.d.ts`, `*.d.cts`, `*.d.mts`)
|
|
73
|
+
- generated files: `*.gen.*`, `*.generated.*`
|
|
74
|
+
- project `.gitignore`
|
|
75
|
+
- anything passed via `--ignore` or config `ignore`
|
|
31
76
|
|
|
32
77
|
### Exit codes
|
|
33
78
|
|
|
34
79
|
| Code | Meaning |
|
|
35
80
|
| ---- | ------- |
|
|
36
|
-
| 0 | No issues |
|
|
37
|
-
| 1 |
|
|
38
|
-
| 2 |
|
|
81
|
+
| 0 | No issues, or issues below `--fail-on` threshold |
|
|
82
|
+
| 1 | Failing diagnostics without errors |
|
|
83
|
+
| 2 | Failing diagnostics with at least one error |
|
|
39
84
|
|
|
40
|
-
Use `--
|
|
85
|
+
Use `--fail-on=error` in CI to fail only on errors while still showing all diagnostics:
|
|
41
86
|
|
|
42
87
|
```bash
|
|
43
|
-
unguard src
|
|
88
|
+
unguard src --fail-on=error
|
|
44
89
|
```
|
|
45
90
|
|
|
91
|
+
`--severity` filters display only. `--fail-on` evaluates all diagnostics after rule policy.
|
|
92
|
+
|
|
46
93
|
### Output formats
|
|
47
94
|
|
|
48
|
-
**Grouped** (default)
|
|
95
|
+
**Grouped** (default) -- diagnostics grouped by file:
|
|
49
96
|
|
|
50
97
|
```txt
|
|
51
98
|
src/lib/probe.ts
|
|
52
99
|
37:4 error Empty catch blocks hide failures... no-empty-catch
|
|
53
100
|
```
|
|
54
101
|
|
|
55
|
-
**Flat** (`--format=flat`)
|
|
102
|
+
**Flat** (`--format=flat`) -- one line per diagnostic, grepable:
|
|
56
103
|
|
|
57
104
|
```txt
|
|
58
105
|
src/lib/probe.ts:37:4 error [no-empty-catch] Empty catch blocks hide failures...
|
|
@@ -69,26 +116,28 @@ src/lib/probe.ts:37:4 error [no-empty-catch] Empty catch blocks hide failures...
|
|
|
69
116
|
| `no-type-assertion` | error | `x as unknown as T` |
|
|
70
117
|
| `no-ts-ignore` | error | `@ts-ignore` / `@ts-expect-error` |
|
|
71
118
|
|
|
72
|
-
### Defensive code
|
|
119
|
+
### Defensive code (type-aware)
|
|
120
|
+
|
|
121
|
+
These rules use the TypeScript type checker. Non-nullable types suppress the diagnostic; nullable types are flagged.
|
|
73
122
|
|
|
74
123
|
| Rule | Severity | What it catches |
|
|
75
124
|
| ---- | -------- | --------------- |
|
|
76
|
-
| `no-optional-property-access` |
|
|
77
|
-
| `no-optional-element-access` |
|
|
78
|
-
| `no-optional-call` |
|
|
79
|
-
| `no-nullish-coalescing` |
|
|
80
|
-
| `no-logical-or-fallback` | warning | `
|
|
125
|
+
| `no-optional-property-access` | warning | `obj?.prop` on a non-nullable type |
|
|
126
|
+
| `no-optional-element-access` | warning | `obj?.[key]` on a non-nullable type |
|
|
127
|
+
| `no-optional-call` | warning | `fn?.()` on a non-nullable type |
|
|
128
|
+
| `no-nullish-coalescing` | warning | `x ?? fallback` on a non-nullable type |
|
|
129
|
+
| `no-logical-or-fallback` | warning | `map.get(k) \|\| fallback` -- data-structure lookups where `??` is correct; `\|\|` on numeric types that swallow `0` |
|
|
81
130
|
| `no-null-ternary-normalization` | warning | `x == null ? fallback : x` |
|
|
82
|
-
| `no-non-null-assertion` | warning | `x!` |
|
|
131
|
+
| `no-non-null-assertion` | warning | `x!` on a nullable type without a local narrowing guard |
|
|
83
132
|
| `no-double-negation-coercion` | info | `!!value` |
|
|
84
|
-
| `no-redundant-existence-guard` | warning | `obj && obj.prop` |
|
|
133
|
+
| `no-redundant-existence-guard` | warning | `obj && obj.prop` on a non-nullable type |
|
|
85
134
|
|
|
86
135
|
### Error handling
|
|
87
136
|
|
|
88
137
|
| Rule | Severity | What it catches |
|
|
89
138
|
| ---- | -------- | --------------- |
|
|
90
|
-
| `no-empty-catch` | error | `catch {}` with no body
|
|
91
|
-
| `no-catch-return` | warning | `catch { return fallback }`
|
|
139
|
+
| `no-empty-catch` | error | `catch {}` with no body and no comment |
|
|
140
|
+
| `no-catch-return` | warning | `catch { return fallback }` with no logging or rethrow |
|
|
92
141
|
| `no-error-rewrap` | error | `throw new Error(e.message)` without `{ cause: e }` |
|
|
93
142
|
|
|
94
143
|
### Interface design
|
|
@@ -103,10 +152,10 @@ src/lib/probe.ts:37:4 error [no-empty-catch] Empty catch blocks hide failures...
|
|
|
103
152
|
|
|
104
153
|
| Rule | Severity | What it catches |
|
|
105
154
|
| ---- | -------- | --------------- |
|
|
106
|
-
| `duplicate-type-declaration` |
|
|
107
|
-
| `duplicate-type-name` |
|
|
108
|
-
| `duplicate-function-declaration` |
|
|
109
|
-
| `duplicate-function-name` |
|
|
155
|
+
| `duplicate-type-declaration` | warning | Same type shape in multiple files |
|
|
156
|
+
| `duplicate-type-name` | warning | Same exported type name, different shapes |
|
|
157
|
+
| `duplicate-function-declaration` | warning | Same function body in multiple files |
|
|
158
|
+
| `duplicate-function-name` | warning | Same exported function name, different bodies |
|
|
110
159
|
| `optional-arg-always-used` | warning | Optional param provided at every call site |
|
|
111
160
|
| `explicit-null-arg` | warning | `fn(null)` / `fn(undefined)` to project functions |
|
|
112
161
|
|
|
@@ -132,10 +181,24 @@ file.ts:2:31 error Explicit `any` annotation ... (intentional escape hatch for u
|
|
|
132
181
|
## API
|
|
133
182
|
|
|
134
183
|
```typescript
|
|
135
|
-
import { scan } from "unguard";
|
|
136
|
-
|
|
137
|
-
const result = await scan({ paths: ["src/"] });
|
|
138
|
-
|
|
184
|
+
import { executeScan, scan } from "unguard";
|
|
185
|
+
|
|
186
|
+
const result = await scan({ paths: ["src/"] }); // raw diagnostics
|
|
187
|
+
const execution = await executeScan({
|
|
188
|
+
paths: ["src"],
|
|
189
|
+
ignore: ["**/*.gen.ts"],
|
|
190
|
+
rulePolicy: {
|
|
191
|
+
"duplicate-*": "warning",
|
|
192
|
+
"category:cross-file": "warning",
|
|
193
|
+
"tag:safety": "error",
|
|
194
|
+
"prefer-*": "off",
|
|
195
|
+
},
|
|
196
|
+
showSeverities: ["error", "warning"],
|
|
197
|
+
failOn: "error",
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
console.log(execution.exitCode);
|
|
201
|
+
for (const d of execution.visibleDiagnostics) {
|
|
139
202
|
console.log(`${d.file}:${d.line} [${d.ruleId}] ${d.message}`);
|
|
140
203
|
}
|
|
141
204
|
```
|