@blumintinc/eslint-plugin-blumint 1.20.95 → 1.20.97

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.
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.harvestRuleTesterCases = void 0;
27
+ const fs = __importStar(require("fs"));
28
+ const os = __importStar(require("os"));
29
+ const path = __importStar(require("path"));
30
+ const sharedTesters = __importStar(require("./ruleTester"));
31
+ const TESTS_DIR = path.join(__dirname, '..', 'tests');
32
+ /**
33
+ * Only suites that import the shared tester module can declare a case, since
34
+ * `src/tests/no-local-rule-tester.test.ts` forbids a locally-built tester. That
35
+ * makes the import a sound admission test rather than a heuristic, and it is
36
+ * what keeps this affordable: the ~28 files it excludes are the meta-suites
37
+ * (`fixer-type-safety`, `docs-examples-conformance`, `rule-crash-robustness`,
38
+ * this guard's own siblings) which run full corpus sweeps at module scope and
39
+ * cost more to load than every rule suite combined.
40
+ *
41
+ * Matching the import rather than a `ruleTesterTs.run(` call site is
42
+ * deliberate: `prefer-next-dynamic.test.ts` aliases the tester
43
+ * (`const jsx = ruleTesterJsx`) before calling `run`, so a call-site pattern
44
+ * drops it.
45
+ */
46
+ const IMPORTS_SHARED_TESTER = /from\s+'\.\.\/utils\/ruleTester'/;
47
+ /**
48
+ * Jest registers a test for every `describe`/`it` a loaded module calls, so
49
+ * loading 271 suites inside a suite would graft their entire test list onto
50
+ * this one. Neutralizing the registrars for the duration of the load keeps the
51
+ * captured declarations and discards the registrations.
52
+ *
53
+ * `describe` bodies still execute — several suites call `run` inside one, and
54
+ * skipping the body would drop those cases — but everything that would register
55
+ * or assert becomes a no-op.
56
+ */
57
+ const REGISTRAR_GLOBALS = [
58
+ 'it',
59
+ 'test',
60
+ 'beforeEach',
61
+ 'afterEach',
62
+ 'beforeAll',
63
+ 'afterAll',
64
+ ];
65
+ /* eslint-disable @typescript-eslint/no-explicit-any */
66
+ const asAny = (value) => value;
67
+ const noop = () => undefined;
68
+ /** `it.each(rows)(name, fn)` is a call chain, so the stub needs the same shape. */
69
+ const withEach = (fn) => {
70
+ fn.each = () => () => undefined;
71
+ fn.only = fn;
72
+ fn.skip = fn;
73
+ fn.todo = noop;
74
+ fn.failing = fn;
75
+ fn.concurrent = fn;
76
+ return fn;
77
+ };
78
+ function harvestRuleTesterCases() {
79
+ const suites = [];
80
+ const failures = [];
81
+ let currentFile = '';
82
+ let filesLoaded = 0;
83
+ let filesSkipped = 0;
84
+ const testerEntries = Object.entries(sharedTesters).filter(([, value]) => typeof asAny(value)?.run === 'function');
85
+ const originalRun = new Map();
86
+ for (const [key, tester] of testerEntries) {
87
+ originalRun.set(key, tester.run);
88
+ tester.run = (name, rule, tests) => {
89
+ const bag = asAny(tests) || {};
90
+ suites.push({
91
+ name,
92
+ tester: key,
93
+ file: currentFile,
94
+ rule,
95
+ valid: bag.valid || [],
96
+ invalid: bag.invalid || [],
97
+ });
98
+ return undefined;
99
+ };
100
+ }
101
+ const globalScope = global;
102
+ const savedGlobals = new Map();
103
+ const stub = (key, value) => {
104
+ savedGlobals.set(key, globalScope[key]);
105
+ globalScope[key] = value;
106
+ };
107
+ stub('describe', withEach((_name, body) => {
108
+ if (typeof body === 'function')
109
+ body();
110
+ }));
111
+ for (const key of REGISTRAR_GLOBALS)
112
+ stub(key, withEach(noop));
113
+ /**
114
+ * A handful of suites write fixture files at module scope, and
115
+ * `test-file-location-enforcement` writes them under
116
+ * `path.join(process.cwd(), '.cursor/tmp/...')` — a path it also `rmSync`s in
117
+ * an `afterAll` that the stubs above turn into a no-op. Loading it from the
118
+ * real working directory would therefore race that suite when it runs
119
+ * concurrently in another worker (its cleanup landing between this harvest's
120
+ * `mkdirSync` and `writeFileSync` throws ENOENT) and would strand fixtures in
121
+ * the repo when it does not. Pointing the working directory at a private
122
+ * scratch root for the duration of the load sends every cwd-derived write
123
+ * somewhere no other worker can see, and it is removed below.
124
+ *
125
+ * The paths the suites *record* are unaffected: they are made relative to the
126
+ * same cwd they were built from, so the harvested filenames come out
127
+ * identical either way.
128
+ */
129
+ const scratchRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'blumint-harvest-'));
130
+ const realCwd = process.cwd();
131
+ process.chdir(scratchRoot);
132
+ try {
133
+ const files = fs
134
+ .readdirSync(TESTS_DIR)
135
+ .filter((file) => file.endsWith('.test.ts'))
136
+ .sort();
137
+ for (const file of files) {
138
+ const fullPath = path.join(TESTS_DIR, file);
139
+ if (!IMPORTS_SHARED_TESTER.test(fs.readFileSync(fullPath, 'utf8'))) {
140
+ filesSkipped++;
141
+ continue;
142
+ }
143
+ currentFile = file;
144
+ try {
145
+ require(fullPath);
146
+ filesLoaded++;
147
+ }
148
+ catch (error) {
149
+ failures.push(`${file}: ${error?.message}`);
150
+ }
151
+ }
152
+ }
153
+ finally {
154
+ process.chdir(realCwd);
155
+ fs.rmSync(scratchRoot, { recursive: true, force: true });
156
+ for (const [key, tester] of testerEntries) {
157
+ const original = originalRun.get(key);
158
+ if (original)
159
+ tester.run = original;
160
+ }
161
+ for (const [key, value] of savedGlobals)
162
+ globalScope[key] = value;
163
+ }
164
+ return { suites, failures, filesLoaded, filesSkipped };
165
+ }
166
+ exports.harvestRuleTesterCases = harvestRuleTesterCases;
167
+ /* eslint-enable @typescript-eslint/no-explicit-any */
168
+ //# sourceMappingURL=harvestRuleTesterCases.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blumintinc/eslint-plugin-blumint",
3
- "version": "1.20.95",
3
+ "version": "1.20.97",
4
4
  "description": "Custom eslint rules for use within BluMint",
5
5
  "author": {
6
6
  "name": "Brodie McGuire",
@@ -1,4 +1,155 @@
1
1
  [
2
+ {
3
+ "version": "1.20.97",
4
+ "date": "2026-08-04T08:50:14.394Z",
5
+ "rules": [
6
+ {
7
+ "name": "enforce-boolean-naming-prefixes",
8
+ "changeType": "fix",
9
+ "issues": [
10
+ 1690,
11
+ 1691
12
+ ],
13
+ "summary": "infer a callee's return from its body when the annotation is absent (closes #1691); accept a digit or $ fused onto an UPPER_SNAKE prefix (closes #1690)"
14
+ },
15
+ {
16
+ "name": "enforce-positive-naming",
17
+ "changeType": "fix",
18
+ "issues": [
19
+ 1692
20
+ ],
21
+ "summary": "decline when a function's returns yield no verdict (closes #1692)"
22
+ }
23
+ ]
24
+ },
25
+ {
26
+ "version": "1.20.96",
27
+ "date": "2026-08-04T07:23:58.552Z",
28
+ "rules": [
29
+ {
30
+ "name": "enforce-css-media-queries",
31
+ "changeType": "fix",
32
+ "issues": [
33
+ 1675
34
+ ],
35
+ "summary": "exempt capability queries and report once per usage (closes #1675)"
36
+ },
37
+ {
38
+ "name": "enforce-exported-function-types",
39
+ "changeType": "fix",
40
+ "issues": [
41
+ 1677,
42
+ 1686
43
+ ],
44
+ "summary": "read destructured props params and resolve memo(Identifier) components (closes #1686); see arrow and memo/forwardRef component shapes, autofix the type export (closes #1677)"
45
+ },
46
+ {
47
+ "name": "enforce-firestore-doc-ref-generic",
48
+ "changeType": "fix",
49
+ "issues": [
50
+ 1678
51
+ ],
52
+ "summary": "resolve type-alias generics beside interfaces (closes #1678)"
53
+ },
54
+ {
55
+ "name": "enforce-mui-rounded-icons",
56
+ "changeType": "fix",
57
+ "issues": [
58
+ 1674
59
+ ],
60
+ "summary": "rename the binding with the path it retargets (closes #1674)"
61
+ },
62
+ {
63
+ "name": "generic-starts-with-t",
64
+ "changeType": "fix",
65
+ "issues": [
66
+ 1676
67
+ ],
68
+ "summary": "exempt module augmentations where the name is upstream-owned (closes #1676)"
69
+ },
70
+ {
71
+ "name": "global-const-style",
72
+ "changeType": "fix",
73
+ "issues": [
74
+ 1681
75
+ ],
76
+ "summary": "exempt function-expression components and unwrap satisfies/non-null wrappers (closes #1681)"
77
+ },
78
+ {
79
+ "name": "no-always-true-false-conditions",
80
+ "changeType": "fix",
81
+ "issues": [
82
+ 1682
83
+ ],
84
+ "summary": "resolve any const object in optional chains and climb assertion wrappers (closes #1682)"
85
+ },
86
+ {
87
+ "name": "no-unnecessary-verb-suffix",
88
+ "changeType": "fix",
89
+ "issues": [
90
+ 1679
91
+ ],
92
+ "summary": "read intersection-alias contracts constituent by constituent (closes #1679)"
93
+ },
94
+ {
95
+ "name": "parallelize-loop-awaits",
96
+ "changeType": "fix",
97
+ "issues": [
98
+ 1687,
99
+ 1688
100
+ ],
101
+ "summary": "cover do-while, dedupe nested-loop reports, stop property keys vetoing (closes #1688); exempt test files, condition-coupled loops, and for-await-of (closes #1687)"
102
+ },
103
+ {
104
+ "name": "prefer-getter-over-parameterless-method",
105
+ "changeType": "fix",
106
+ "issues": [
107
+ 1684
108
+ ],
109
+ "summary": "skip methods bound by a heritage clause (closes #1684)"
110
+ },
111
+ {
112
+ "name": "prefer-type-alias-over-typeof-constant",
113
+ "changeType": "fix",
114
+ "issues": [
115
+ 1680
116
+ ],
117
+ "summary": "exempt only canonical alias derivations, not nested use sites (closes #1680)"
118
+ },
119
+ {
120
+ "name": "require-https-error",
121
+ "changeType": "fix",
122
+ "issues": [
123
+ 1685
124
+ ],
125
+ "summary": "name the codebase-owned HttpsError module, not a placeholder package (closes #1685)"
126
+ },
127
+ {
128
+ "name": "use-custom-link",
129
+ "changeType": "fix",
130
+ "issues": [
131
+ 1673
132
+ ],
133
+ "summary": "exempt the wrapper's implementation files (closes #1673)"
134
+ },
135
+ {
136
+ "name": "use-custom-memo",
137
+ "changeType": "fix",
138
+ "issues": [
139
+ 1671
140
+ ],
141
+ "summary": "exempt the wrapper module the rule points at (closes #1671)"
142
+ },
143
+ {
144
+ "name": "use-custom-router",
145
+ "changeType": "fix",
146
+ "issues": [
147
+ 1672
148
+ ],
149
+ "summary": "exempt the wrapper module the rule points at (closes #1672)"
150
+ }
151
+ ]
152
+ },
2
153
  {
3
154
  "version": "1.20.95",
4
155
  "date": "2026-08-03T23:44:33.733Z",