@alexcodeplace/slopgate 0.2.0 → 0.3.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 +2 -1
- package/package.json +1 -2
- package/rules/baseline/ast/empty-catch-block-ts.yml +3 -0
- package/rules/baseline/ast/empty-catch-block-tsx.yml +3 -0
- package/rules/baseline/ast/inner-html.yml +4 -1
- package/rules/baseline/ast/target-blank-norel.yml +3 -0
- package/rules/baseline/ast/window-in-render.yml +2 -0
- package/rules/baseline/fixtures/checker-outputs/depcruise.expected.json +5 -0
- package/rules/baseline/fixtures/checker-outputs/depcruise.json +11 -0
- package/rules/baseline/fixtures/checker-outputs/jscpd.expected.json +3 -0
- package/rules/baseline/fixtures/checker-outputs/jscpd.json +13 -0
- package/rules/baseline/fixtures/checker-outputs/knip.expected.json +6 -0
- package/rules/baseline/fixtures/checker-outputs/knip.json +31 -0
- package/rules/baseline/fixtures/checker-outputs/tsc.expected.json +5 -0
- package/rules/baseline/fixtures/checker-outputs/tsc.txt +4 -0
- package/rules/baseline/fixtures/checker-outputs/type-coverage.expected.json +5 -0
- package/rules/baseline/fixtures/checker-outputs/type-coverage.txt +5 -0
- package/rules/baseline/fixtures/src/canary.tsx +25 -0
- package/rules/baseline/fixtures/src/components/window-in-render.tsx +4 -0
- package/rules/baseline/fixtures/src/empty-catch.ts +3 -0
- package/rules/baseline/fixtures/src/empty-catch.tsx +4 -0
- package/rules/baseline/fixtures/src/target-blank.tsx +3 -0
- package/rules/baseline/fixtures/src/ux-ast.tsx +43 -0
- package/rules/baseline/selftest.config.toml +2 -1
- package/vendor/darwin-arm64/slopgate-rs +0 -0
- package/vendor/darwin-x64/slopgate-rs +0 -0
- package/vendor/linux-arm64/slopgate-rs +0 -0
- package/vendor/linux-x64/slopgate-rs +0 -0
- package/vendor/win32-x64/slopgate-rs.exe +0 -0
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ A global code-quality / anti-slop gate for Claude Code and git. Engine is shared
|
|
|
23
23
|
- **diff-shape** — wide commits spanning too many directories (encourages focused changes)
|
|
24
24
|
|
|
25
25
|
- **Shared regex + AST rule packs** — fast-tier and commit-tier both run these
|
|
26
|
-
- Convention: `no-stubs`, `ts-suppress`, `as-any`, `raw-hex` (design tokens), `sql-safety`
|
|
26
|
+
- Convention: `no-stubs`, `ts-suppress`, `as-any`, `no-narration-comments`, `raw-hex` (design tokens), `sql-safety`
|
|
27
27
|
- Security: `live-secrets`, `eval-ban`, `pii-logs`, `weak-hash`
|
|
28
28
|
- Cloudflare boundary: `kv-ban` (plus the opt-in `stack = ["cloudflare"]` pack)
|
|
29
29
|
- Built-in AST rules: empty-catch, unsafe `innerHTML`/`dangerouslySetInnerHTML`, `target="_blank"` without `rel`, `window` access during render
|
|
@@ -388,6 +388,7 @@ All are opt-in via the `baseline` array in config. Severity drives the gate thre
|
|
|
388
388
|
| `no-stubs` | critical | convention | Stub / placeholder / "not implemented" / deferred-work markers |
|
|
389
389
|
| `ts-suppress` | high | convention | `@ts-ignore` / `@ts-expect-error` — suppressing tsc instead of fixing the cause |
|
|
390
390
|
| `as-any` | high | convention | `as any` casts that disable type safety |
|
|
391
|
+
| `no-narration-comments` | high | convention | Comments that narrate a diff/history/removal (`changed from`, `used to`, `no longer needed`, `track later`) instead of explaining a non-obvious constraint |
|
|
391
392
|
| `raw-hex` | high | convention | Hardcoded hex / `rgb()` colors + raw multi-digit `px` — use design tokens |
|
|
392
393
|
| `sql-safety` | critical | convention | `SELECT … FOR UPDATE` with an aggregate (Postgres rejects this at runtime) |
|
|
393
394
|
| `kv-ban` | critical | boundary | Cloudflare KV in read-after-write paths (eventually-consistent) |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexcodeplace/slopgate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Global code-quality / anti-slop gate — engine shared, rules per-project.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
"rules",
|
|
23
23
|
"skills",
|
|
24
24
|
"vendor",
|
|
25
|
-
"!rules/baseline/fixtures",
|
|
26
25
|
"!bin/slopgate-rs"
|
|
27
26
|
],
|
|
28
27
|
"engines": {
|
|
@@ -5,3 +5,6 @@ message: Empty catch block swallows error silently
|
|
|
5
5
|
note: '{"severity":"high","category":"convention","resolution":"Handle or rethrow. Minimum: log with context (console.error). Comment-only bodies count as empty."}'
|
|
6
6
|
rule:
|
|
7
7
|
pattern: 'try { $A } catch ($E) {}'
|
|
8
|
+
ignores:
|
|
9
|
+
- '**/*.test.ts'
|
|
10
|
+
- '**/*.test.tsx'
|
|
@@ -5,3 +5,6 @@ message: Empty catch block swallows error silently
|
|
|
5
5
|
note: '{"severity":"high","category":"convention","resolution":"Handle or rethrow. Minimum: log with context (console.error). Comment-only bodies count as empty."}'
|
|
6
6
|
rule:
|
|
7
7
|
pattern: 'try { $A } catch ($E) {}'
|
|
8
|
+
ignores:
|
|
9
|
+
- '**/*.test.ts'
|
|
10
|
+
- '**/*.test.tsx'
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "rule": "no-circular", "severity": "error", "from": "src/a.ts", "to": "src/b.ts" },
|
|
3
|
+
{ "rule": "no-orphans", "severity": "warn", "from": "src/orphan.ts", "to": "src/orphan.ts" },
|
|
4
|
+
{ "rule": "fyi-only", "severity": "info", "from": "src/ui/page.ts", "to": "src/db/client.ts" }
|
|
5
|
+
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"summary": {
|
|
3
|
+
"violations": [
|
|
4
|
+
{ "type": "cycle", "from": "src/a.ts", "to": "src/b.ts", "rule": { "severity": "error", "name": "no-circular" } },
|
|
5
|
+
{ "type": "module", "from": "src/orphan.ts", "to": "src/orphan.ts", "rule": { "severity": "warn", "name": "no-orphans" } },
|
|
6
|
+
{ "type": "dependency", "from": "src/ui/page.ts", "to": "src/db/client.ts", "rule": { "severity": "info", "name": "fyi-only" } }
|
|
7
|
+
],
|
|
8
|
+
"error": 1, "warn": 1, "info": 1
|
|
9
|
+
},
|
|
10
|
+
"modules": []
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"duplicates": [
|
|
3
|
+
{
|
|
4
|
+
"format": "typescript",
|
|
5
|
+
"lines": 18,
|
|
6
|
+
"tokens": 120,
|
|
7
|
+
"firstFile": { "name": "src/features/a.ts", "start": 10, "end": 27, "startLoc": { "line": 10, "column": 1 }, "endLoc": { "line": 27, "column": 2 } },
|
|
8
|
+
"secondFile": { "name": "src/features/b.ts", "start": 40, "end": 57, "startLoc": { "line": 40, "column": 1 }, "endLoc": { "line": 57, "column": 2 } },
|
|
9
|
+
"fragment": "function dup() { /* body */ }"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"statistics": {}
|
|
13
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "type": "files", "file": "src/orphan.ts", "line": 1, "name": "src/orphan.ts" },
|
|
3
|
+
{ "type": "exports", "file": "src/util.ts", "line": 14, "name": "unusedHelper" },
|
|
4
|
+
{ "type": "types", "file": "src/util.ts", "line": 2, "name": "UnusedType" },
|
|
5
|
+
{ "type": "dependencies", "file": "package.json", "line": 12, "name": "left-pad" }
|
|
6
|
+
]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files": ["src/orphan.ts"],
|
|
3
|
+
"issues": [
|
|
4
|
+
{
|
|
5
|
+
"file": "src/util.ts",
|
|
6
|
+
"dependencies": [],
|
|
7
|
+
"devDependencies": [],
|
|
8
|
+
"optionalPeerDependencies": [],
|
|
9
|
+
"unlisted": [],
|
|
10
|
+
"binaries": [],
|
|
11
|
+
"unresolved": [],
|
|
12
|
+
"exports": [{ "name": "unusedHelper", "line": 14, "col": 14, "pos": 300 }],
|
|
13
|
+
"types": [{ "name": "UnusedType", "line": 2, "col": 13, "pos": 30 }],
|
|
14
|
+
"enumMembers": {},
|
|
15
|
+
"duplicates": []
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"file": "package.json",
|
|
19
|
+
"dependencies": [{ "name": "left-pad", "line": 12, "col": 6, "pos": 200 }],
|
|
20
|
+
"devDependencies": [],
|
|
21
|
+
"optionalPeerDependencies": [],
|
|
22
|
+
"unlisted": [],
|
|
23
|
+
"binaries": [],
|
|
24
|
+
"unresolved": [],
|
|
25
|
+
"exports": [],
|
|
26
|
+
"types": [],
|
|
27
|
+
"enumMembers": {},
|
|
28
|
+
"duplicates": []
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "file": "src/a.ts", "line": 12, "code": "TS2322", "message": "Type 'string' is not assignable to type 'number'." },
|
|
3
|
+
{ "file": "src/b.tsx", "line": 3, "code": "TS2304", "message": "Cannot find name 'foo'." },
|
|
4
|
+
{ "file": "src/long.ts", "line": 7, "code": "TS2345", "message": "Argument of type '{ a: string; }' is not assignable to parameter of type 'Opts'. Property 'b' is missing in type '{ a: string; }' but required in type 'Opts'." }
|
|
5
|
+
]
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
src/a.ts(12,5): error TS2322: Type 'string' is not assignable to type 'number'.
|
|
2
|
+
src/b.tsx(3,1): error TS2304: Cannot find name 'foo'.
|
|
3
|
+
src/long.ts(7,9): error TS2345: Argument of type '{ a: string; }' is not assignable to parameter of type 'Opts'.
|
|
4
|
+
Property 'b' is missing in type '{ a: string; }' but required in type 'Opts'.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Fixture for slopgate self-test. Contains deliberate canary tokens.
|
|
2
|
+
// no-stubs SHOULD match stub markers (not bare placeholder UI/i18n tokens):
|
|
3
|
+
// placeholder for now
|
|
4
|
+
// TODO: implement later
|
|
5
|
+
// not implemented
|
|
6
|
+
export const x = __SLOPGATE_AST_CANARY__;
|
|
7
|
+
// negative: must NOT match no-stubs — placeholder={...}, placeholder: class, i18n keys
|
|
8
|
+
const _neg = <input placeholder={t('x')} className="placeholder:text-ink" />;
|
|
9
|
+
const _titleKey = admin.incidents.titlePlaceholder;
|
|
10
|
+
const namePlaceholder = 1;
|
|
11
|
+
// ts-suppress SHOULD match @ts-* only (not eslint-disable):
|
|
12
|
+
// @ts-ignore
|
|
13
|
+
/* eslint-disable zync/no-raw-html-in-pages */
|
|
14
|
+
// changed from foo to bar
|
|
15
|
+
function bad(el: HTMLElement) { el.innerHTML = '<b>hi</b>'; }
|
|
16
|
+
// live-secrets canary:
|
|
17
|
+
const _sk = "sk_live_aaaaaaaaaaaa";
|
|
18
|
+
// eval-ban canary:
|
|
19
|
+
// eval(userCode); — commented to avoid actual eval in fixture
|
|
20
|
+
const _evalStr = 'eval(userCode)';
|
|
21
|
+
// pii-logs canary:
|
|
22
|
+
console.log(user.phone, user.email);
|
|
23
|
+
// weak-hash canary:
|
|
24
|
+
// createHash('md5') — weak hash
|
|
25
|
+
const _wh = "createHash('md5').update(data)";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Fixtures for the UX module ast rules. Each construct below must trigger
|
|
2
|
+
// exactly the rule named in its comment (selftest asserts every yml fires).
|
|
3
|
+
// Parsed syntactically by ast-grep — no imports / type-checking involved.
|
|
4
|
+
|
|
5
|
+
// triggers ux-div-onclick — onClick on a non-semantic element, no role
|
|
6
|
+
export const ClickyDiv = () => <div onClick={onThing}>Open</div>;
|
|
7
|
+
|
|
8
|
+
// triggers ux-img-no-dimensions — <img> with alt but no width/height
|
|
9
|
+
export const Hero = () => <img src="/hero.png" alt="hero" />;
|
|
10
|
+
|
|
11
|
+
// triggers ux-img-no-alt — <img> with dimensions but no alt
|
|
12
|
+
export const NoAlt = () => <img src="/x.png" width={32} height={32} />;
|
|
13
|
+
|
|
14
|
+
// triggers ux-anchor-no-href — <a onClick> with no href
|
|
15
|
+
export const FakeLink = () => <a onClick={onThing}>go</a>;
|
|
16
|
+
|
|
17
|
+
// triggers ux-button-no-type — <button> with no explicit type
|
|
18
|
+
export const BareButton = () => <button onClick={onThing}>Save</button>;
|
|
19
|
+
|
|
20
|
+
// triggers ux-media-no-dimensions — <iframe> with no width/height
|
|
21
|
+
export const Embed = () => <iframe src="/player" />;
|
|
22
|
+
|
|
23
|
+
// triggers ux-async-onclick-no-disable — async onClick, no disabled (type set so
|
|
24
|
+
// it does NOT also trip ux-button-no-type)
|
|
25
|
+
export const AsyncSave = () => <button type="button" onClick={async () => save()}>Save</button>;
|
|
26
|
+
|
|
27
|
+
// triggers ux-modal-no-close — Dialog with no onClose/onDismiss/onOpenChange
|
|
28
|
+
export const Sheet = ({ open }) => <Dialog open={open}>body</Dialog>;
|
|
29
|
+
|
|
30
|
+
// negative: all of these are correct and must NOT trigger anything
|
|
31
|
+
export const Ok = () => (
|
|
32
|
+
<>
|
|
33
|
+
<button type="button" onClick={onThing}>Open</button>
|
|
34
|
+
<button type="button" disabled onClick={async () => save()}>Save</button>
|
|
35
|
+
<a href="/page" onClick={onThing}>real link</a>
|
|
36
|
+
<img src="/ok.png" alt="ok" width={320} height={200} />
|
|
37
|
+
<Dialog open onOpenChange={onThing}>ok</Dialog>
|
|
38
|
+
</>
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
function onThing() {}
|
|
42
|
+
function save() {}
|
|
43
|
+
declare const Dialog: (props: Record<string, unknown>) => unknown;
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
roots = ["rules/baseline/fixtures/src"]
|
|
7
7
|
exts = [".ts", ".tsx"]
|
|
8
8
|
skipDirs = ["node_modules"]
|
|
9
|
-
baseline = ["no-stubs", "ts-suppress", "as-any", "raw-hex", "kv-ban", "live-secrets", "eval-ban", "pii-logs", "weak-hash", "sql-safety"]
|
|
9
|
+
baseline = ["no-stubs", "ts-suppress", "as-any", "no-narration-comments", "markers", "raw-hex", "kv-ban", "live-secrets", "eval-ban", "pii-logs", "weak-hash", "sql-safety"]
|
|
10
|
+
stack = ["debug"]
|
|
10
11
|
rules = []
|
|
11
12
|
suppressions = "./fixtures/suppressions.json"
|
|
12
13
|
fixtures = "./fixtures"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|