eslint-plugin-unslop 0.5.2 → 0.5.3
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 +9 -1
- package/dist/index.cjs +68 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +68 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,11 +42,12 @@ export default [
|
|
|
42
42
|
]
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
Architecture rules (`import-control`, `export-control`, `no-false-sharing`, `no-single-use-constants`) require a reachable `tsconfig.json`. Set `compilerOptions.rootDir`, and if you use aliases, configure `compilerOptions.paths`.
|
|
45
|
+
Architecture rules (`import-control`, `no-whitebox-testing`, `export-control`, `no-false-sharing`, `no-single-use-constants`) require a reachable `tsconfig.json`. Set `compilerOptions.rootDir`, and if you use aliases, configure `compilerOptions.paths`.
|
|
46
46
|
|
|
47
47
|
| Rule | What it does |
|
|
48
48
|
| -------------------------------- | ---------------------------------------------------------------------- |
|
|
49
49
|
| `unslop/import-control` | Enforces module boundaries and forbids local namespace imports |
|
|
50
|
+
| `unslop/no-whitebox-testing` | Keeps tests on module entrypoints instead of same-folder internals |
|
|
50
51
|
| `unslop/export-control` | Restricts export patterns and forbids `export *` in module entrypoints |
|
|
51
52
|
| `unslop/no-false-sharing` | Flags shared entrypoint symbols with fewer than two consumer groups |
|
|
52
53
|
| `unslop/no-single-use-constants` | Flags module-scope constants used once or never across the project |
|
|
@@ -71,6 +72,7 @@ All architecture rules read from `settings.unslop.architecture`. Each key is a m
|
|
|
71
72
|
{
|
|
72
73
|
imports?: string[] // module matchers this module may import from; '*' allows all
|
|
73
74
|
exports?: string[] // regex patterns symbols exported from index.ts/types.ts must match
|
|
75
|
+
entrypoints?: string[] // public files allowed for external and test imports
|
|
74
76
|
shared?: boolean // marks module as shared; enables no-false-sharing
|
|
75
77
|
}
|
|
76
78
|
```
|
|
@@ -92,6 +94,12 @@ Deny-by-default for cross-module imports, so forgetting to declare a dependency
|
|
|
92
94
|
|
|
93
95
|
Alias imports are resolved via `compilerOptions.paths` from `tsconfig.json`.
|
|
94
96
|
|
|
97
|
+
### `unslop/no-whitebox-testing`
|
|
98
|
+
|
|
99
|
+
Keeps test files black-boxed. When a recognized test file lives beside a module's implementation, it must import that module through its public entrypoint (`.`, `./index`, or a configured `entrypoints` file) instead of reaching into sibling files like `./model.ts`.
|
|
100
|
+
|
|
101
|
+
This rule only checks recognized test filenames (`*.test.*`, `*.spec.*`, `*.*-test.*`, `*.*-spec.*`). Child submodule imports and cross-module imports are left to `unslop/import-control`.
|
|
102
|
+
|
|
95
103
|
### `unslop/export-control`
|
|
96
104
|
|
|
97
105
|
The customs declaration form for the other direction: what are you actually exporting from your module's public entrypoints?
|
package/dist/index.cjs
CHANGED
|
@@ -37,7 +37,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
37
37
|
// package.json
|
|
38
38
|
var package_default = {
|
|
39
39
|
name: "eslint-plugin-unslop",
|
|
40
|
-
version: "0.5.
|
|
40
|
+
version: "0.5.3",
|
|
41
41
|
description: "ESLint plugin with rules for reducing AI-generated code smells",
|
|
42
42
|
repository: {
|
|
43
43
|
type: "git",
|
|
@@ -721,7 +721,7 @@ function reportUnsharedSymbols(context, node, options) {
|
|
|
721
721
|
for (const symbol of exportedSymbols) {
|
|
722
722
|
const consumerGroups = consumerGroupsBySymbol.get(symbol.exportedName);
|
|
723
723
|
if (consumerGroups === void 0) continue;
|
|
724
|
-
if (consumerGroups.size >=
|
|
724
|
+
if (consumerGroups.size >= 2) continue;
|
|
725
725
|
context.report({
|
|
726
726
|
node,
|
|
727
727
|
messageId: "notTrulyShared",
|
|
@@ -929,7 +929,6 @@ function getSingleConsumerGroup(groups) {
|
|
|
929
929
|
const [single] = [...groups];
|
|
930
930
|
return ` (group: ${single})`;
|
|
931
931
|
}
|
|
932
|
-
var MIN_CONSUMER_GROUPS = 2;
|
|
933
932
|
|
|
934
933
|
// src/rules/read-friendly-order/ast-utils.ts
|
|
935
934
|
function getDeclName(node) {
|
|
@@ -1753,6 +1752,70 @@ function importPatternMatches(pattern, target) {
|
|
|
1753
1752
|
return patternSegs.every((seg, i) => seg === "*" || seg === targetSegs[i]);
|
|
1754
1753
|
}
|
|
1755
1754
|
|
|
1755
|
+
// src/rules/no-whitebox-testing/index.ts
|
|
1756
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
1757
|
+
var no_whitebox_testing_default = {
|
|
1758
|
+
meta: {
|
|
1759
|
+
type: "problem",
|
|
1760
|
+
docs: {
|
|
1761
|
+
description: "Require tests to import a module through its public entrypoint",
|
|
1762
|
+
recommended: false
|
|
1763
|
+
},
|
|
1764
|
+
schema: [],
|
|
1765
|
+
messages: {
|
|
1766
|
+
usePublicEntrypoint: "White-box test import denied: tests must import this module through its public entrypoint (offending import: {{specifier}})."
|
|
1767
|
+
}
|
|
1768
|
+
},
|
|
1769
|
+
create(context) {
|
|
1770
|
+
if (!isRecognizedTestFile(context.filename)) return {};
|
|
1771
|
+
const state = getArchitectureRuleState(context);
|
|
1772
|
+
if (state === void 0) return {};
|
|
1773
|
+
return {
|
|
1774
|
+
ImportDeclaration(node) {
|
|
1775
|
+
checkImportDeclaration(context, node, state);
|
|
1776
|
+
}
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1780
|
+
function checkImportDeclaration(context, node, state) {
|
|
1781
|
+
const resolvedImport = resolveImport(node, state);
|
|
1782
|
+
if (resolvedImport === void 0) return;
|
|
1783
|
+
if (resolvedImport.targetModule.instance !== state.moduleMatch.instance) return;
|
|
1784
|
+
if (!isSameDirectoryImport(state.filename, resolvedImport.targetFile)) return;
|
|
1785
|
+
if (isAllowedModuleEntrypoint2(resolvedImport.targetFile, state.moduleMatch.policy.entrypoints))
|
|
1786
|
+
return;
|
|
1787
|
+
context.report({
|
|
1788
|
+
node,
|
|
1789
|
+
messageId: "usePublicEntrypoint",
|
|
1790
|
+
data: { specifier: resolvedImport.specifier }
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
function resolveImport(node, state) {
|
|
1794
|
+
const specifier = getSpecifier2(node);
|
|
1795
|
+
if (specifier === void 0) return void 0;
|
|
1796
|
+
const resolvedTarget = resolveImportTarget(state.filename, state.policy.projectContext, specifier);
|
|
1797
|
+
if (resolvedTarget === void 0) return void 0;
|
|
1798
|
+
const targetFile = normalizeResolvedPath2(resolvedTarget);
|
|
1799
|
+
const targetModule = matchFileToArchitectureModule(targetFile, state.policy);
|
|
1800
|
+
if (targetModule === void 0) return void 0;
|
|
1801
|
+
return { specifier, targetFile, targetModule };
|
|
1802
|
+
}
|
|
1803
|
+
function getSpecifier2(node) {
|
|
1804
|
+
const value = node.source.value;
|
|
1805
|
+
return typeof value === "string" ? value : void 0;
|
|
1806
|
+
}
|
|
1807
|
+
function isRecognizedTestFile(filename) {
|
|
1808
|
+
if (filename.length === 0) return false;
|
|
1809
|
+
const basename = import_node_path6.default.basename(filename);
|
|
1810
|
+
return /^.+\.test\..+$/.test(basename) || /^.+\.spec\..+$/.test(basename) || /^.+\.[^.]+-test\..+$/.test(basename) || /^.+\.[^.]+-spec\..+$/.test(basename);
|
|
1811
|
+
}
|
|
1812
|
+
function isSameDirectoryImport(importerFile, targetFile) {
|
|
1813
|
+
return isSamePath(import_node_path6.default.dirname(importerFile), import_node_path6.default.dirname(targetFile));
|
|
1814
|
+
}
|
|
1815
|
+
function isAllowedModuleEntrypoint2(targetFile, entrypoints) {
|
|
1816
|
+
return entrypoints.includes(import_node_path6.default.basename(targetFile));
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1756
1819
|
// src/rules/export-control/index.ts
|
|
1757
1820
|
var export_control_default = {
|
|
1758
1821
|
meta: {
|
|
@@ -2043,6 +2106,7 @@ var rules_default = {
|
|
|
2043
2106
|
"no-false-sharing": no_false_sharing_default,
|
|
2044
2107
|
"read-friendly-order": read_friendly_order_default,
|
|
2045
2108
|
"import-control": import_control_default,
|
|
2109
|
+
"no-whitebox-testing": no_whitebox_testing_default,
|
|
2046
2110
|
"export-control": export_control_default,
|
|
2047
2111
|
"no-single-use-constants": no_single_use_constants_default
|
|
2048
2112
|
};
|
|
@@ -2071,6 +2135,7 @@ var full = {
|
|
|
2071
2135
|
"unslop/no-special-unicode": "error",
|
|
2072
2136
|
"unslop/no-unicode-escape": "error",
|
|
2073
2137
|
"unslop/import-control": "error",
|
|
2138
|
+
"unslop/no-whitebox-testing": "error",
|
|
2074
2139
|
"unslop/export-control": "error",
|
|
2075
2140
|
"unslop/no-false-sharing": "error",
|
|
2076
2141
|
"unslop/no-single-use-constants": "error",
|