eslint-plugin-unslop 0.6.0 → 0.6.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 +9 -5
- package/dist/index.cjs +53 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +53 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -85,13 +85,16 @@ Each value is a policy object:
|
|
|
85
85
|
|
|
86
86
|
```ts
|
|
87
87
|
{
|
|
88
|
-
imports?: string[]
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
imports?: string[] // exact module, direct child via /*, self-or-child via /+, or '*' for all
|
|
89
|
+
typeImports?: string[] // same patterns as imports, but only for type-only imports
|
|
90
|
+
exports?: string[] // regex patterns symbols exported from entrypoints must match
|
|
91
|
+
entrypoints?: string[] // public files allowed for external and test imports
|
|
92
|
+
shared?: boolean // marks module as shared; enables no-false-sharing
|
|
92
93
|
}
|
|
93
94
|
```
|
|
94
95
|
|
|
96
|
+
`typeImports` defaults to `[]` when omitted. Type-only imports are also allowed when the target matches `imports`, so `typeImports` is only needed for modules you want to allow type access to without allowing value imports.
|
|
97
|
+
|
|
95
98
|
Architecture keys and import allowlists use different matching rules:
|
|
96
99
|
|
|
97
100
|
- keys assign ownership to subtrees
|
|
@@ -99,7 +102,7 @@ Architecture keys and import allowlists use different matching rules:
|
|
|
99
102
|
- `imports: ['models/*']` allows only direct children like `models/user`
|
|
100
103
|
- `imports: ['models/+']` allows `models` and direct children like `models/user`
|
|
101
104
|
|
|
102
|
-
When multiple keys cover the same canonical module path, the winner is chosen by nearest owner first, then exact named path over wildcard path at the same depth, then longer selector path, then declaration order. Unmatched canonical module paths become anonymous modules with empty `imports`, empty `exports`, `shared: false`, and default `entrypoints: ['index.ts']`.
|
|
105
|
+
When multiple keys cover the same canonical module path, the winner is chosen by nearest owner first, then exact named path over wildcard path at the same depth, then longer selector path, then declaration order. Unmatched canonical module paths become anonymous modules with empty `imports`, empty `typeImports`, empty `exports`, `shared: false`, and default `entrypoints: ['index.ts']`.
|
|
103
106
|
|
|
104
107
|
All architecture rules take no options. Policy comes entirely from this shared settings block.
|
|
105
108
|
|
|
@@ -112,6 +115,7 @@ Customs control for your modules: you declare which modules are allowed to impor
|
|
|
112
115
|
Deny-by-default for cross-module imports, so forgetting to declare a dependency is a loud error rather than a silent free-for-all. It also enforces:
|
|
113
116
|
|
|
114
117
|
- cross-module imports must arrive through the public gate (configured entrypoints)
|
|
118
|
+
- type-only imports can be separately allowed via `typeImports` (value imports from those modules remain forbidden)
|
|
115
119
|
- local cross-module namespace imports are forbidden (`import * as X from '<local-module>'`)
|
|
116
120
|
- same-module relative imports can only go one level deeper - no tunnelling into internals
|
|
117
121
|
- files that don't match any declared module become anonymous modules and are denied by default
|
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.6.
|
|
40
|
+
version: "0.6.1",
|
|
41
41
|
description: "ESLint plugin with rules for reducing AI-generated code smells",
|
|
42
42
|
repository: {
|
|
43
43
|
type: "git",
|
|
@@ -632,11 +632,13 @@ function getUnsupportedArchitectureKeyDetails(matcher) {
|
|
|
632
632
|
function parseModulePolicy(rawPolicy) {
|
|
633
633
|
if (!isRecord(rawPolicy)) return void 0;
|
|
634
634
|
const imports = readStringList(rawPolicy.imports);
|
|
635
|
+
const typeImports = readStringList(rawPolicy.typeImports);
|
|
635
636
|
const exports2 = readStringList(rawPolicy.exports);
|
|
636
637
|
const entrypoints = readStringList(rawPolicy.entrypoints);
|
|
637
638
|
const shared = rawPolicy.shared === true;
|
|
638
639
|
return {
|
|
639
640
|
imports,
|
|
641
|
+
typeImports,
|
|
640
642
|
exports: exports2,
|
|
641
643
|
entrypoints: entrypoints.length > 0 ? entrypoints : ["index.ts"],
|
|
642
644
|
shared
|
|
@@ -704,7 +706,13 @@ function makeAnonymousModule(canonicalPath) {
|
|
|
704
706
|
canonicalPath,
|
|
705
707
|
ownerKey: canonicalPath,
|
|
706
708
|
ownerPath: canonicalPath,
|
|
707
|
-
policy: {
|
|
709
|
+
policy: {
|
|
710
|
+
imports: [],
|
|
711
|
+
typeImports: [],
|
|
712
|
+
exports: [],
|
|
713
|
+
entrypoints: ["index.ts"],
|
|
714
|
+
shared: false
|
|
715
|
+
},
|
|
708
716
|
order: 0,
|
|
709
717
|
anonymous: true,
|
|
710
718
|
ownerDepth: depth,
|
|
@@ -1802,7 +1810,7 @@ function checkModuleEdge(options) {
|
|
|
1802
1810
|
return;
|
|
1803
1811
|
}
|
|
1804
1812
|
if (isShallowRelativeEntrypoint(specifier, targetFile, importee.policy)) return;
|
|
1805
|
-
if (!
|
|
1813
|
+
if (!allowsCrossModuleDeclaration(importer.policy, importee.canonicalPath, node)) {
|
|
1806
1814
|
context.report({
|
|
1807
1815
|
node,
|
|
1808
1816
|
messageId: "notAllowed",
|
|
@@ -1849,9 +1857,48 @@ function isRelativeDepthTooDeep(specifier) {
|
|
|
1849
1857
|
const parts = specifier.slice(2).split("/").filter(Boolean);
|
|
1850
1858
|
return parts.length > 2;
|
|
1851
1859
|
}
|
|
1852
|
-
function
|
|
1853
|
-
if (policy.imports
|
|
1854
|
-
return
|
|
1860
|
+
function allowsCrossModuleDeclaration(policy, targetMatcher, node) {
|
|
1861
|
+
if (allowsImportPattern(policy.imports, targetMatcher)) return true;
|
|
1862
|
+
return isTypeOnlyDeclaration(node) && allowsImportPattern(policy.typeImports, targetMatcher);
|
|
1863
|
+
}
|
|
1864
|
+
function isTypeOnlyDeclaration(node) {
|
|
1865
|
+
if (node.type === "ImportDeclaration") return isTypeOnlyImportDeclaration(node);
|
|
1866
|
+
if (node.type === "ExportAllDeclaration") return getExportKind(node) === "type";
|
|
1867
|
+
return isTypeOnlyExportNamedDeclaration(node);
|
|
1868
|
+
}
|
|
1869
|
+
function isTypeOnlyImportDeclaration(node) {
|
|
1870
|
+
if (getImportKind(node) === "type") return true;
|
|
1871
|
+
if (node.specifiers.length === 0) return false;
|
|
1872
|
+
return node.specifiers.every(isTypeOnlyImportSpecifier);
|
|
1873
|
+
}
|
|
1874
|
+
function isTypeOnlyImportSpecifier(specifier) {
|
|
1875
|
+
return specifier.type === "ImportSpecifier" && getImportKind(specifier) === "type";
|
|
1876
|
+
}
|
|
1877
|
+
function isTypeOnlyExportNamedDeclaration(node) {
|
|
1878
|
+
if (getExportKind(node) === "type") return true;
|
|
1879
|
+
if (node.specifiers.length === 0) return false;
|
|
1880
|
+
return node.specifiers.every(isTypeOnlyExportSpecifier);
|
|
1881
|
+
}
|
|
1882
|
+
function isTypeOnlyExportSpecifier(specifier) {
|
|
1883
|
+
return getExportKind(specifier) === "type";
|
|
1884
|
+
}
|
|
1885
|
+
function getImportKind(value) {
|
|
1886
|
+
return getDeclarationKind(value, "importKind");
|
|
1887
|
+
}
|
|
1888
|
+
function getExportKind(value) {
|
|
1889
|
+
return getDeclarationKind(value, "exportKind");
|
|
1890
|
+
}
|
|
1891
|
+
function getDeclarationKind(value, key) {
|
|
1892
|
+
if (!isRecord2(value)) return void 0;
|
|
1893
|
+
const kind = value[key];
|
|
1894
|
+
return kind === "type" || kind === "value" ? kind : void 0;
|
|
1895
|
+
}
|
|
1896
|
+
function isRecord2(value) {
|
|
1897
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1898
|
+
}
|
|
1899
|
+
function allowsImportPattern(patterns, targetMatcher) {
|
|
1900
|
+
if (patterns.includes("*")) return true;
|
|
1901
|
+
return patterns.some((pattern) => importPatternMatches(pattern, targetMatcher));
|
|
1855
1902
|
}
|
|
1856
1903
|
function importPatternMatches(pattern, target) {
|
|
1857
1904
|
if (pattern === target) return true;
|