@blumintinc/eslint-plugin-blumint 1.19.10 → 1.19.12
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/lib/index.js
CHANGED
|
@@ -340,13 +340,34 @@ exports.default = (0, createRule_1.createRule)({
|
|
|
340
340
|
fixer.replaceText(idNode, typeAnnotation ? `${newName}${typeText}` : newName),
|
|
341
341
|
];
|
|
342
342
|
for (const ref of declaredVariable.references) {
|
|
343
|
+
const refId = ref.identifier;
|
|
343
344
|
// The declaration write reference is the id node itself and
|
|
344
345
|
// is already handled above. Skipping it also avoids emitting
|
|
345
346
|
// overlapping fix ranges, which ESLint rejects.
|
|
346
|
-
if (
|
|
347
|
+
if (refId === idNode) {
|
|
347
348
|
continue;
|
|
348
349
|
}
|
|
349
|
-
|
|
350
|
+
const refParent = refId.parent;
|
|
351
|
+
// An object-literal shorthand `{ fooBar }` desugars to
|
|
352
|
+
// `{ fooBar: fooBar }`: the one token is both the property key
|
|
353
|
+
// and its value. Rewriting it to `{ FOO_BAR }` would rename
|
|
354
|
+
// the KEY too, silently changing the object's shape. Expand to
|
|
355
|
+
// `oldKey: NEW_NAME` so only the value is renamed.
|
|
356
|
+
if (refParent?.type === utils_1.AST_NODE_TYPES.Property &&
|
|
357
|
+
refParent.shorthand &&
|
|
358
|
+
refParent.parent?.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
|
|
359
|
+
fixes.push(fixer.replaceText(refId, `${name}: ${newName}`));
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
// A re-export specifier `export { fooBar }` binds the public
|
|
363
|
+
// export name to this identifier. Renaming it would change the
|
|
364
|
+
// exported name — a cross-file contract a single-file fixer
|
|
365
|
+
// cannot safely rewrite (the declaration-level export guard
|
|
366
|
+
// above only catches inline `export const`). Decline the fix.
|
|
367
|
+
if (refParent?.type === utils_1.AST_NODE_TYPES.ExportSpecifier) {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
fixes.push(fixer.replaceText(refId, newName));
|
|
350
371
|
}
|
|
351
372
|
return fixes;
|
|
352
373
|
},
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
type Options = [
|
|
2
|
+
{
|
|
3
|
+
additionalSubjectExtensions?: string[];
|
|
4
|
+
}?
|
|
5
|
+
];
|
|
6
|
+
export declare const testFileLocationEnforcement: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"misplacedTestFile", Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
|
|
7
|
+
export {};
|
|
@@ -9,6 +9,10 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const createRule_1 = require("../utils/createRule");
|
|
10
10
|
const TEST_FILE_PATTERN = /\.test\.tsx?$/i;
|
|
11
11
|
const SUPPORTED_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx'];
|
|
12
|
+
const DEFAULT_OPTIONS = {
|
|
13
|
+
additionalSubjectExtensions: [],
|
|
14
|
+
};
|
|
15
|
+
const normalizeExtension = (extension) => extension.startsWith('.') ? extension : `.${extension}`;
|
|
12
16
|
exports.testFileLocationEnforcement = (0, createRule_1.createRule)({
|
|
13
17
|
name: 'test-file-location-enforcement',
|
|
14
18
|
meta: {
|
|
@@ -17,13 +21,31 @@ exports.testFileLocationEnforcement = (0, createRule_1.createRule)({
|
|
|
17
21
|
description: 'Enforce colocating *.test.ts or *.test.tsx files with the code they cover.',
|
|
18
22
|
recommended: 'error',
|
|
19
23
|
},
|
|
20
|
-
schema: [
|
|
24
|
+
schema: [
|
|
25
|
+
{
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
additionalSubjectExtensions: {
|
|
29
|
+
type: 'array',
|
|
30
|
+
items: { type: 'string' },
|
|
31
|
+
default: [],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
additionalProperties: false,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
21
37
|
messages: {
|
|
22
38
|
misplacedTestFile: 'Test file "{{testFile}}" is not colocated with its subject. Keep tests in the same directory as {{expectedNames}} so refactors move code and coverage together and engineers can find the implementation without searching separate test folders.',
|
|
23
39
|
},
|
|
24
40
|
},
|
|
25
|
-
defaultOptions: [],
|
|
26
|
-
create(context) {
|
|
41
|
+
defaultOptions: [DEFAULT_OPTIONS],
|
|
42
|
+
create(context, [options]) {
|
|
43
|
+
const resolvedOptions = { ...DEFAULT_OPTIONS, ...(options ?? {}) };
|
|
44
|
+
const additionalExtensions = (resolvedOptions.additionalSubjectExtensions ?? []).map(normalizeExtension);
|
|
45
|
+
const subjectExtensions = [
|
|
46
|
+
...SUPPORTED_EXTENSIONS,
|
|
47
|
+
...additionalExtensions.filter((extension) => !SUPPORTED_EXTENSIONS.includes(extension)),
|
|
48
|
+
];
|
|
27
49
|
return {
|
|
28
50
|
Program(node) {
|
|
29
51
|
const filename = context.getFilename();
|
|
@@ -36,7 +58,7 @@ exports.testFileLocationEnforcement = (0, createRule_1.createRule)({
|
|
|
36
58
|
const directory = path_1.default.dirname(filename);
|
|
37
59
|
const testFileName = path_1.default.basename(filename);
|
|
38
60
|
const baseName = testFileName.replace(TEST_FILE_PATTERN, '');
|
|
39
|
-
const candidates =
|
|
61
|
+
const candidates = subjectExtensions.map((extension) => path_1.default.join(directory, `${baseName}${extension}`));
|
|
40
62
|
const hasSibling = candidates.some((candidate) => fs_1.default.existsSync(candidate));
|
|
41
63
|
if (hasSibling) {
|
|
42
64
|
return;
|
|
@@ -44,7 +66,9 @@ exports.testFileLocationEnforcement = (0, createRule_1.createRule)({
|
|
|
44
66
|
const relativePath = path_1.default.isAbsolute(filename)
|
|
45
67
|
? path_1.default.relative(process.cwd(), filename) || filename
|
|
46
68
|
: filename;
|
|
47
|
-
const expectedNames =
|
|
69
|
+
const expectedNames = subjectExtensions
|
|
70
|
+
.map((extension) => `"${baseName}${extension}"`)
|
|
71
|
+
.join(' or ');
|
|
48
72
|
context.report({
|
|
49
73
|
node,
|
|
50
74
|
messageId: 'misplacedTestFile',
|
package/package.json
CHANGED
package/release-manifest.json
CHANGED
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "1.19.12",
|
|
4
|
+
"date": "2026-07-17T19:23:55.157Z",
|
|
5
|
+
"rules": [
|
|
6
|
+
{
|
|
7
|
+
"name": "test-file-location-enforcement",
|
|
8
|
+
"changeType": "fix",
|
|
9
|
+
"issues": [
|
|
10
|
+
1314
|
|
11
|
+
],
|
|
12
|
+
"summary": "add opt-in additionalSubjectExtensions for non-JS/TS subjects (closes #1314)"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"version": "1.19.11",
|
|
18
|
+
"date": "2026-07-17T10:46:20.593Z",
|
|
19
|
+
"rules": [
|
|
20
|
+
{
|
|
21
|
+
"name": "global-const-style",
|
|
22
|
+
"changeType": "fix",
|
|
23
|
+
"issues": [
|
|
24
|
+
1313
|
|
25
|
+
],
|
|
26
|
+
"summary": "keep rename autofix reference-safe for shorthand props and re-exports (refs #1313)"
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
},
|
|
2
30
|
{
|
|
3
31
|
"version": "1.19.10",
|
|
4
32
|
"date": "2026-07-17T10:33:03.696Z",
|