@boundaries/eslint-plugin 5.2.0-beta.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/LICENSE +21 -0
- package/README.md +798 -0
- package/dist/Config/Config.d.ts +40 -0
- package/dist/Config/Config.js +119 -0
- package/dist/Config/Recommended.d.ts +10 -0
- package/dist/Config/Recommended.js +36 -0
- package/dist/Config/Strict.d.ts +8 -0
- package/dist/Config/Strict.js +26 -0
- package/dist/Elements/Elements.d.ts +43 -0
- package/dist/Elements/Elements.js +107 -0
- package/dist/Elements/Elements.types.d.ts +5 -0
- package/dist/Elements/Elements.types.js +2 -0
- package/dist/Elements/index.d.ts +2 -0
- package/dist/Elements/index.js +18 -0
- package/dist/Messages/Messages.d.ts +14 -0
- package/dist/Messages/Messages.js +204 -0
- package/dist/Messages/index.d.ts +1 -0
- package/dist/Messages/index.js +17 -0
- package/dist/Public/Config.types.d.ts +2 -0
- package/dist/Public/Config.types.js +5 -0
- package/dist/Public/Rules.types.d.ts +10 -0
- package/dist/Public/Rules.types.js +15 -0
- package/dist/Public/Settings.types.d.ts +13 -0
- package/dist/Public/Settings.types.js +21 -0
- package/dist/Public/index.d.ts +3 -0
- package/dist/Public/index.js +19 -0
- package/dist/Rules/ElementTypes.d.ts +25 -0
- package/dist/Rules/ElementTypes.js +269 -0
- package/dist/Rules/EntryPoint.d.ts +2 -0
- package/dist/Rules/EntryPoint.js +122 -0
- package/dist/Rules/External.d.ts +2 -0
- package/dist/Rules/External.js +119 -0
- package/dist/Rules/NoIgnored.d.ts +2 -0
- package/dist/Rules/NoIgnored.js +19 -0
- package/dist/Rules/NoPrivate.d.ts +2 -0
- package/dist/Rules/NoPrivate.js +53 -0
- package/dist/Rules/NoUnknown.d.ts +2 -0
- package/dist/Rules/NoUnknown.js +22 -0
- package/dist/Rules/NoUnknownFiles.d.ts +3 -0
- package/dist/Rules/NoUnknownFiles.js +29 -0
- package/dist/Rules/Support/DependencyRule.d.ts +4 -0
- package/dist/Rules/Support/DependencyRule.js +49 -0
- package/dist/Rules/Support/DependencyRule.types.d.ts +17 -0
- package/dist/Rules/Support/DependencyRule.types.js +2 -0
- package/dist/Rules/Support/Helpers.d.ts +8 -0
- package/dist/Rules/Support/Helpers.js +39 -0
- package/dist/Rules/Support/index.d.ts +3 -0
- package/dist/Rules/Support/index.js +19 -0
- package/dist/Settings/Helpers.d.ts +41 -0
- package/dist/Settings/Helpers.js +72 -0
- package/dist/Settings/Settings.d.ts +6 -0
- package/dist/Settings/Settings.js +49 -0
- package/dist/Settings/Settings.types.d.ts +448 -0
- package/dist/Settings/Settings.types.js +190 -0
- package/dist/Settings/Validations.d.ts +137 -0
- package/dist/Settings/Validations.js +359 -0
- package/dist/Settings/index.d.ts +4 -0
- package/dist/Settings/index.js +20 -0
- package/dist/Support/Common.d.ts +30 -0
- package/dist/Support/Common.js +47 -0
- package/dist/Support/Debug.d.ts +5 -0
- package/dist/Support/Debug.js +54 -0
- package/dist/Support/index.d.ts +2 -0
- package/dist/Support/index.js +18 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +76 -0
- package/package.json +81 -0
|
@@ -0,0 +1,17 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./Messages"), exports);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PLUGIN_NAME = void 0;
|
|
4
|
+
var Settings_1 = require("../Settings");
|
|
5
|
+
Object.defineProperty(exports, "PLUGIN_NAME", { enumerable: true, get: function () { return Settings_1.PLUGIN_NAME; } });
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DependencyKind } from "@boundaries/elements";
|
|
2
|
+
export type { DependencyKind, CapturedValuesSelector, ElementSelector, ElementSelectors, ElementsSelector, ExternalLibrarySelectorOptions, ExternalLibrarySelectorWithOptions, ExternalLibrarySelector, ExternalLibrarySelectors, ExternalLibrariesSelector, ElementSelectorWithOptions, } from "@boundaries/elements";
|
|
3
|
+
export { isElementSelector, isElementsSelector, isExternalLibrarySelector, isExternalLibrariesSelector, } from "@boundaries/elements";
|
|
4
|
+
export type { RulePolicy, RuleBaseOptions, ElementTypesRule, ElementTypesRuleOptions, EntryPointRule, EntryPointRuleOptions, ExternalRule, ExternalRuleOptions, NoPrivateOptions, RuleShortName, RuleName, } from "../Settings";
|
|
5
|
+
export { RULE_POLICIES_MAP, isRulePolicy, RULE_SHORT_NAMES_MAP, RULE_NAMES_MAP, isRuleShortName, isRuleName, } from "../Settings";
|
|
6
|
+
/**
|
|
7
|
+
* Kind of import that the rule applies to (e.g., "type", "value")
|
|
8
|
+
* @deprecated Use DependencyKind instead
|
|
9
|
+
*/
|
|
10
|
+
export type ImportKind = DependencyKind;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isRuleName = exports.isRuleShortName = exports.RULE_NAMES_MAP = exports.RULE_SHORT_NAMES_MAP = exports.isRulePolicy = exports.RULE_POLICIES_MAP = exports.isExternalLibrariesSelector = exports.isExternalLibrarySelector = exports.isElementsSelector = exports.isElementSelector = void 0;
|
|
4
|
+
var elements_1 = require("@boundaries/elements");
|
|
5
|
+
Object.defineProperty(exports, "isElementSelector", { enumerable: true, get: function () { return elements_1.isElementSelector; } });
|
|
6
|
+
Object.defineProperty(exports, "isElementsSelector", { enumerable: true, get: function () { return elements_1.isElementsSelector; } });
|
|
7
|
+
Object.defineProperty(exports, "isExternalLibrarySelector", { enumerable: true, get: function () { return elements_1.isExternalLibrarySelector; } });
|
|
8
|
+
Object.defineProperty(exports, "isExternalLibrariesSelector", { enumerable: true, get: function () { return elements_1.isExternalLibrariesSelector; } });
|
|
9
|
+
var Settings_1 = require("../Settings");
|
|
10
|
+
Object.defineProperty(exports, "RULE_POLICIES_MAP", { enumerable: true, get: function () { return Settings_1.RULE_POLICIES_MAP; } });
|
|
11
|
+
Object.defineProperty(exports, "isRulePolicy", { enumerable: true, get: function () { return Settings_1.isRulePolicy; } });
|
|
12
|
+
Object.defineProperty(exports, "RULE_SHORT_NAMES_MAP", { enumerable: true, get: function () { return Settings_1.RULE_SHORT_NAMES_MAP; } });
|
|
13
|
+
Object.defineProperty(exports, "RULE_NAMES_MAP", { enumerable: true, get: function () { return Settings_1.RULE_NAMES_MAP; } });
|
|
14
|
+
Object.defineProperty(exports, "isRuleShortName", { enumerable: true, get: function () { return Settings_1.isRuleShortName; } });
|
|
15
|
+
Object.defineProperty(exports, "isRuleName", { enumerable: true, get: function () { return Settings_1.isRuleName; } });
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { DEPENDENCY_KINDS_MAP, isDependencyKind } from "@boundaries/elements";
|
|
2
|
+
export type { ElementDescriptorMode, ElementDescriptor, ElementDescriptors, } from "@boundaries/elements";
|
|
3
|
+
export { ELEMENT_DESCRIPTOR_MODES_MAP, isElementDescriptorMode, } from "@boundaries/elements";
|
|
4
|
+
export type { Settings, IgnoreSetting, IncludeSetting, RootPathSetting, SettingsKey, DependencyNodeKey, DependencyNodeSelector, AliasSetting, } from "../Settings";
|
|
5
|
+
/**
|
|
6
|
+
* Map of the kinds of import, either a type import or a value import.
|
|
7
|
+
* @deprecated Use DEPENDENCY_KINDS_MAP instead
|
|
8
|
+
*/
|
|
9
|
+
export declare const IMPORT_KINDS_MAP: {
|
|
10
|
+
readonly TYPE: "type";
|
|
11
|
+
readonly VALUE: "value";
|
|
12
|
+
};
|
|
13
|
+
export { isImportKind, DEPENDENCY_NODE_KEYS_MAP, isDependencyNodeKey, SETTINGS_KEYS_MAP, isSettingsKey, } from "../Settings";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isSettingsKey = exports.SETTINGS_KEYS_MAP = exports.isDependencyNodeKey = exports.DEPENDENCY_NODE_KEYS_MAP = exports.isImportKind = exports.IMPORT_KINDS_MAP = exports.isElementDescriptorMode = exports.ELEMENT_DESCRIPTOR_MODES_MAP = exports.isDependencyKind = exports.DEPENDENCY_KINDS_MAP = void 0;
|
|
4
|
+
var elements_1 = require("@boundaries/elements");
|
|
5
|
+
Object.defineProperty(exports, "DEPENDENCY_KINDS_MAP", { enumerable: true, get: function () { return elements_1.DEPENDENCY_KINDS_MAP; } });
|
|
6
|
+
Object.defineProperty(exports, "isDependencyKind", { enumerable: true, get: function () { return elements_1.isDependencyKind; } });
|
|
7
|
+
const elements_2 = require("@boundaries/elements");
|
|
8
|
+
var elements_3 = require("@boundaries/elements");
|
|
9
|
+
Object.defineProperty(exports, "ELEMENT_DESCRIPTOR_MODES_MAP", { enumerable: true, get: function () { return elements_3.ELEMENT_DESCRIPTOR_MODES_MAP; } });
|
|
10
|
+
Object.defineProperty(exports, "isElementDescriptorMode", { enumerable: true, get: function () { return elements_3.isElementDescriptorMode; } });
|
|
11
|
+
/**
|
|
12
|
+
* Map of the kinds of import, either a type import or a value import.
|
|
13
|
+
* @deprecated Use DEPENDENCY_KINDS_MAP instead
|
|
14
|
+
*/
|
|
15
|
+
exports.IMPORT_KINDS_MAP = elements_2.DEPENDENCY_KINDS_MAP;
|
|
16
|
+
var Settings_1 = require("../Settings");
|
|
17
|
+
Object.defineProperty(exports, "isImportKind", { enumerable: true, get: function () { return Settings_1.isImportKind; } });
|
|
18
|
+
Object.defineProperty(exports, "DEPENDENCY_NODE_KEYS_MAP", { enumerable: true, get: function () { return Settings_1.DEPENDENCY_NODE_KEYS_MAP; } });
|
|
19
|
+
Object.defineProperty(exports, "isDependencyNodeKey", { enumerable: true, get: function () { return Settings_1.isDependencyNodeKey; } });
|
|
20
|
+
Object.defineProperty(exports, "SETTINGS_KEYS_MAP", { enumerable: true, get: function () { return Settings_1.SETTINGS_KEYS_MAP; } });
|
|
21
|
+
Object.defineProperty(exports, "isSettingsKey", { enumerable: true, get: function () { return Settings_1.isSettingsKey; } });
|
|
@@ -0,0 +1,19 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./Config.types"), exports);
|
|
18
|
+
__exportStar(require("./Settings.types"), exports);
|
|
19
|
+
__exportStar(require("./Rules.types"), exports);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DependencyDescription, Matcher } from "@boundaries/elements";
|
|
2
|
+
import type { ElementTypesRuleOptions, RuleResult, SettingsNormalized } from "../Settings";
|
|
3
|
+
type PolicyMatch = {
|
|
4
|
+
isMatch: boolean;
|
|
5
|
+
specifiers?: string | string[];
|
|
6
|
+
internalPath?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function getRulesResults(ruleOptions: ElementTypesRuleOptions, dependencyDescription: DependencyDescription, matcher: Matcher, settings: SettingsNormalized): {
|
|
9
|
+
index: number;
|
|
10
|
+
originalRuleIndex: any;
|
|
11
|
+
selectorsMatching: {
|
|
12
|
+
selectors: {
|
|
13
|
+
[x: string]: string | import("@boundaries/elements").BaseElementSelectorData | import("@boundaries/elements").BaseElementSelectorWithOptions | import("@boundaries/elements").BaseElementSelector[] | null;
|
|
14
|
+
};
|
|
15
|
+
selectorsData: {
|
|
16
|
+
isMatch: boolean;
|
|
17
|
+
} | null;
|
|
18
|
+
};
|
|
19
|
+
ruleHasImportKind: boolean;
|
|
20
|
+
allowPolicyMatches: PolicyMatch;
|
|
21
|
+
denyPolicyMatches: PolicyMatch;
|
|
22
|
+
}[];
|
|
23
|
+
export declare function elementRulesAllowDependency(dependency: DependencyDescription, settings: SettingsNormalized, ruleOptions?: ElementTypesRuleOptions): RuleResult;
|
|
24
|
+
declare const _default: import("eslint").Rule.RuleModule;
|
|
25
|
+
export default _default;
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getRulesResults = getRulesResults;
|
|
7
|
+
exports.elementRulesAllowDependency = elementRulesAllowDependency;
|
|
8
|
+
const elements_1 = require("@boundaries/elements");
|
|
9
|
+
const micromatch_1 = __importDefault(require("micromatch"));
|
|
10
|
+
const Elements_1 = require("../Elements");
|
|
11
|
+
const Messages_1 = require("../Messages");
|
|
12
|
+
const Settings_1 = require("../Settings");
|
|
13
|
+
const Support_1 = require("../Support");
|
|
14
|
+
const Support_2 = require("./Support");
|
|
15
|
+
const { RULE_ELEMENT_TYPES } = Settings_1.SETTINGS;
|
|
16
|
+
/**
|
|
17
|
+
* Safely matches a dependency selector, catching and logging any errors
|
|
18
|
+
*/
|
|
19
|
+
function createSafeMatcherFunction(dependencyDescription, matcher) {
|
|
20
|
+
return (dependencySelector, extraTemplateData, dependencySelectorsGlobals) => {
|
|
21
|
+
// Just in case selectors are invalid, we catch errors here to avoid breaking the whole rule evaluation
|
|
22
|
+
try {
|
|
23
|
+
return matcher.getSelectorMatchingDescription(dependencyDescription, dependencySelector, {
|
|
24
|
+
extraTemplateData,
|
|
25
|
+
dependencySelectorsGlobals,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
(0, Support_1.warnOnce)(`Error occurred while matching dependency: ${String(error)}`);
|
|
30
|
+
return { isMatch: false };
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Determines the rule matching context (directions, template data, etc.)
|
|
36
|
+
*/
|
|
37
|
+
function createRuleMatchContext(rule, dependencyDescription) {
|
|
38
|
+
const targetElementDirection = rule.from ? "from" : "to";
|
|
39
|
+
const policyElementDirection = rule.from ? "to" : "from";
|
|
40
|
+
const denyKeyToUse = rule.deny ? "deny" : "disallow";
|
|
41
|
+
const capturedValuesTemplateData = targetElementDirection === "from"
|
|
42
|
+
? {
|
|
43
|
+
...dependencyDescription.from.captured,
|
|
44
|
+
from: dependencyDescription.from.captured,
|
|
45
|
+
to: dependencyDescription.to.captured,
|
|
46
|
+
}
|
|
47
|
+
: {
|
|
48
|
+
...dependencyDescription.to.captured,
|
|
49
|
+
from: dependencyDescription.from.captured,
|
|
50
|
+
to: dependencyDescription.to.captured,
|
|
51
|
+
};
|
|
52
|
+
const dependencySelectorsGlobals = rule.importKind ? { kind: rule.importKind } : {};
|
|
53
|
+
const targetElementSelector = rule[targetElementDirection];
|
|
54
|
+
return {
|
|
55
|
+
targetElementDirection,
|
|
56
|
+
policyElementDirection,
|
|
57
|
+
denyKeyToUse,
|
|
58
|
+
capturedValuesTemplateData,
|
|
59
|
+
dependencySelectorsGlobals,
|
|
60
|
+
targetElementSelector,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Evaluates both deny and allow policy matches for a rule
|
|
65
|
+
*/
|
|
66
|
+
function evaluatePolicyMatches(rule, context, isMatch, settings) {
|
|
67
|
+
const { targetElementDirection, policyElementDirection, denyKeyToUse, capturedValuesTemplateData, dependencySelectorsGlobals, targetElementSelector, } = context;
|
|
68
|
+
const disallowPolicyMatches = rule[denyKeyToUse]
|
|
69
|
+
? isMatch({
|
|
70
|
+
[targetElementDirection]: targetElementSelector,
|
|
71
|
+
[policyElementDirection]: rule[denyKeyToUse],
|
|
72
|
+
}, settings.legacyTemplates ? capturedValuesTemplateData : {}, dependencySelectorsGlobals)
|
|
73
|
+
: { isMatch: false };
|
|
74
|
+
const allowPolicyMatches = !disallowPolicyMatches.isMatch && rule.allow
|
|
75
|
+
? isMatch({
|
|
76
|
+
[targetElementDirection]: targetElementSelector,
|
|
77
|
+
[policyElementDirection]: rule.allow,
|
|
78
|
+
}, settings.legacyTemplates ? capturedValuesTemplateData : {}, dependencySelectorsGlobals)
|
|
79
|
+
: { isMatch: false };
|
|
80
|
+
return { disallowPolicyMatches, allowPolicyMatches };
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Creates rule selectors data based on policy matches
|
|
84
|
+
*/
|
|
85
|
+
function createRuleSelectorsData(rule, context, disallowPolicyMatches, allowPolicyMatches) {
|
|
86
|
+
const { targetElementDirection, policyElementDirection, denyKeyToUse } = context;
|
|
87
|
+
const allowPolicyMatchesIsMatch = allowPolicyMatches.isMatch;
|
|
88
|
+
const disallowPolicyMatchesIsMatch = disallowPolicyMatches.isMatch;
|
|
89
|
+
const targetSelector = disallowPolicyMatchesIsMatch || allowPolicyMatchesIsMatch
|
|
90
|
+
? context.targetElementSelector
|
|
91
|
+
: null;
|
|
92
|
+
let policySelector = null;
|
|
93
|
+
if (disallowPolicyMatchesIsMatch) {
|
|
94
|
+
policySelector = rule[denyKeyToUse];
|
|
95
|
+
}
|
|
96
|
+
else if (allowPolicyMatchesIsMatch) {
|
|
97
|
+
policySelector = rule.allow;
|
|
98
|
+
}
|
|
99
|
+
let selectorsData = null;
|
|
100
|
+
if (disallowPolicyMatchesIsMatch) {
|
|
101
|
+
selectorsData = disallowPolicyMatches;
|
|
102
|
+
}
|
|
103
|
+
else if (allowPolicyMatchesIsMatch) {
|
|
104
|
+
selectorsData = allowPolicyMatches;
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
selectors: {
|
|
108
|
+
[targetElementDirection]: targetSelector,
|
|
109
|
+
[policyElementDirection]: policySelector,
|
|
110
|
+
},
|
|
111
|
+
selectorsData,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function getRulesResults(ruleOptions, dependencyDescription, matcher, settings) {
|
|
115
|
+
if (!ruleOptions.rules) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
const isMatch = createSafeMatcherFunction(dependencyDescription, matcher);
|
|
119
|
+
return ruleOptions.rules.map((rule, index) => {
|
|
120
|
+
const context = createRuleMatchContext(rule, dependencyDescription);
|
|
121
|
+
const { disallowPolicyMatches, allowPolicyMatches } = evaluatePolicyMatches(rule, context, isMatch, settings);
|
|
122
|
+
const selectorsMatching = createRuleSelectorsData(rule, context, disallowPolicyMatches, allowPolicyMatches);
|
|
123
|
+
return {
|
|
124
|
+
index,
|
|
125
|
+
// @ts-expect-error Workaround to support both allow and disallow in the same entry point rule
|
|
126
|
+
originalRuleIndex: rule.originalRuleIndex,
|
|
127
|
+
selectorsMatching,
|
|
128
|
+
ruleHasImportKind: !!rule.importKind,
|
|
129
|
+
allowPolicyMatches,
|
|
130
|
+
denyPolicyMatches: disallowPolicyMatches,
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Determines the rule result based on policy matches
|
|
136
|
+
*/
|
|
137
|
+
function determineRuleResult(rulesResults) {
|
|
138
|
+
let isAllowed = false;
|
|
139
|
+
let ruleIndexMatching = null;
|
|
140
|
+
for (const ruleResult of rulesResults) {
|
|
141
|
+
if (ruleResult.denyPolicyMatches.isMatch) {
|
|
142
|
+
isAllowed = false;
|
|
143
|
+
ruleIndexMatching = ruleResult.index;
|
|
144
|
+
}
|
|
145
|
+
else if (ruleResult.allowPolicyMatches.isMatch) {
|
|
146
|
+
isAllowed = true;
|
|
147
|
+
ruleIndexMatching = ruleResult.index;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return { isAllowed, ruleIndexMatching };
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Gets the message for the rule, prioritizing rule-specific messages
|
|
154
|
+
*/
|
|
155
|
+
function getRuleMessage(ruleIndexMatching, ruleOptions) {
|
|
156
|
+
return ((ruleIndexMatching === null
|
|
157
|
+
? ruleOptions.message
|
|
158
|
+
: ruleOptions.rules?.[ruleIndexMatching]?.message) || ruleOptions.message);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Gets specifiers that match the rule for error reporting
|
|
162
|
+
*/
|
|
163
|
+
function getMatchingSpecifiers(ruleIndexMatching, rulesResults, dependency) {
|
|
164
|
+
if (ruleIndexMatching === null)
|
|
165
|
+
return null;
|
|
166
|
+
const selectorDataSpecifiers =
|
|
167
|
+
// @ts-expect-error TODO: Align types. At this point, selectorsData.to must always be defined, because otherwise isMatch would be false
|
|
168
|
+
rulesResults[ruleIndexMatching].selectorsMatching?.selectorsData?.to
|
|
169
|
+
?.specifiers;
|
|
170
|
+
if (!selectorDataSpecifiers) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
if ((0, Support_1.isString)(selectorDataSpecifiers)) {
|
|
174
|
+
const hasMatchingSpecifier = dependency.dependency.specifiers?.some((specifier) => micromatch_1.default.isMatch(specifier, selectorDataSpecifiers));
|
|
175
|
+
return hasMatchingSpecifier ? [selectorDataSpecifiers] : null;
|
|
176
|
+
}
|
|
177
|
+
return selectorDataSpecifiers.filter((pattern) => {
|
|
178
|
+
return dependency.dependency.specifiers?.some((specifier) => micromatch_1.default.isMatch(specifier, pattern));
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Creates the rule report object
|
|
183
|
+
*/
|
|
184
|
+
function createRuleReport(ruleIndexMatching, message, dependency, rulesResults) {
|
|
185
|
+
if (ruleIndexMatching === null) {
|
|
186
|
+
return {
|
|
187
|
+
message,
|
|
188
|
+
isDefault: true,
|
|
189
|
+
importKind: undefined,
|
|
190
|
+
disallow: dependency.to,
|
|
191
|
+
element: dependency.from,
|
|
192
|
+
index: -1,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
message,
|
|
197
|
+
isDefault: false,
|
|
198
|
+
importKind: rulesResults[ruleIndexMatching].ruleHasImportKind
|
|
199
|
+
? dependency.dependency.kind
|
|
200
|
+
: undefined,
|
|
201
|
+
disallow: rulesResults[ruleIndexMatching].selectorsMatching?.selectors.to,
|
|
202
|
+
element: rulesResults[ruleIndexMatching].selectorsMatching?.selectors.from,
|
|
203
|
+
index: rulesResults[ruleIndexMatching].originalRuleIndex ?? ruleIndexMatching,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Determines the report path for error reporting
|
|
208
|
+
*/
|
|
209
|
+
function getReportPath(ruleIndexMatching, rulesResults, dependency) {
|
|
210
|
+
return ruleIndexMatching === null ||
|
|
211
|
+
// @ts-expect-error TODO: Align types. At this point, selectorsData should always be defined
|
|
212
|
+
!rulesResults[ruleIndexMatching].selectorsMatching?.selectorsData?.to
|
|
213
|
+
?.internalPath
|
|
214
|
+
? null
|
|
215
|
+
: dependency.to.internalPath;
|
|
216
|
+
}
|
|
217
|
+
function elementRulesAllowDependency(dependency, settings, ruleOptions = {}) {
|
|
218
|
+
const defaultIsAllowed = ruleOptions.default === "allow";
|
|
219
|
+
const matcher = (0, Elements_1.getElementsMatcher)(settings);
|
|
220
|
+
const rulesResults = getRulesResults(ruleOptions, dependency, matcher, settings);
|
|
221
|
+
const { isAllowed, ruleIndexMatching } = determineRuleResult(rulesResults);
|
|
222
|
+
const finalIsAllowed = ruleIndexMatching === null ? defaultIsAllowed : isAllowed;
|
|
223
|
+
const message = getRuleMessage(ruleIndexMatching, ruleOptions);
|
|
224
|
+
const ruleReport = createRuleReport(ruleIndexMatching, message, dependency, rulesResults);
|
|
225
|
+
const reportPath = getReportPath(ruleIndexMatching, rulesResults, dependency);
|
|
226
|
+
const result = {
|
|
227
|
+
result: finalIsAllowed,
|
|
228
|
+
// @ts-expect-error Temporary workaround for RuleResult type until types are aligned
|
|
229
|
+
ruleReport,
|
|
230
|
+
report: {
|
|
231
|
+
specifiers: getMatchingSpecifiers(ruleIndexMatching, rulesResults, dependency) ||
|
|
232
|
+
undefined,
|
|
233
|
+
path: reportPath,
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
function errorMessage(ruleData, dependency) {
|
|
239
|
+
const ruleReport = ruleData.ruleReport;
|
|
240
|
+
if (!ruleReport) {
|
|
241
|
+
return `No detailed rule report available. This is likely a bug in ${Settings_1.PLUGIN_NAME}. Please report it at ${Settings_1.PLUGIN_ISSUES_URL}`;
|
|
242
|
+
}
|
|
243
|
+
if (ruleReport.message) {
|
|
244
|
+
return (0, Messages_1.customErrorMessage)(ruleReport.message, dependency);
|
|
245
|
+
}
|
|
246
|
+
if (ruleReport.isDefault) {
|
|
247
|
+
return `No rule allowing this dependency was found. File is ${(0, Messages_1.elementMessage)(dependency.from)}. Dependency is ${(0, Messages_1.elementMessage)(dependency.to)}`;
|
|
248
|
+
}
|
|
249
|
+
return `Importing ${(0, Messages_1.dependencyImportKindMessage)(ruleReport.importKind, dependency)}${(0, Messages_1.ruleElementMessage)(ruleReport.disallow, dependency.from.captured)} is not allowed in ${(0, Messages_1.ruleElementMessage)(ruleReport.element, dependency.from.captured)}. Disallowed in rule ${ruleReport.index + 1}`;
|
|
250
|
+
}
|
|
251
|
+
exports.default = (0, Support_2.dependencyRule)({
|
|
252
|
+
ruleName: RULE_ELEMENT_TYPES,
|
|
253
|
+
description: `Check allowed dependencies between element types`,
|
|
254
|
+
schema: (0, Settings_1.rulesOptionsSchema)(),
|
|
255
|
+
}, function ({ dependency, node, context, settings, options }) {
|
|
256
|
+
// TODO: Remove these checks when allowing to use more selectors in ESLint rules
|
|
257
|
+
if ((0, elements_1.isLocalElement)(dependency.to) &&
|
|
258
|
+
!(0, elements_1.isIgnoredElement)(dependency.to) &&
|
|
259
|
+
!(0, elements_1.isUnknownLocalElement)(dependency.to) &&
|
|
260
|
+
!(0, elements_1.isInternalDependency)(dependency)) {
|
|
261
|
+
const ruleData = elementRulesAllowDependency(dependency, settings, options);
|
|
262
|
+
if (!ruleData.result) {
|
|
263
|
+
context.report({
|
|
264
|
+
message: errorMessage(ruleData, dependency),
|
|
265
|
+
node,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const elements_1 = require("@boundaries/elements");
|
|
4
|
+
const Messages_1 = require("../Messages");
|
|
5
|
+
const Settings_1 = require("../Settings");
|
|
6
|
+
const ElementTypes_1 = require("./ElementTypes");
|
|
7
|
+
const Support_1 = require("./Support");
|
|
8
|
+
const { RULE_ENTRY_POINT } = Settings_1.SETTINGS;
|
|
9
|
+
function errorMessage(ruleData, dependency) {
|
|
10
|
+
const ruleReport = ruleData.ruleReport;
|
|
11
|
+
if (!ruleReport) {
|
|
12
|
+
return `No detailed rule report available. This is likely a bug in ${Settings_1.PLUGIN_NAME}. Please report it at ${Settings_1.PLUGIN_ISSUES_URL}`;
|
|
13
|
+
}
|
|
14
|
+
if (ruleReport.message) {
|
|
15
|
+
return (0, Messages_1.customErrorMessage)(ruleReport.message, dependency);
|
|
16
|
+
}
|
|
17
|
+
if (ruleReport.isDefault) {
|
|
18
|
+
return `No rule allows the entry point '${dependency.to.internalPath}' in dependencies ${(0, Messages_1.elementMessage)(dependency.to)}`;
|
|
19
|
+
}
|
|
20
|
+
return `The entry point '${dependency.to.internalPath}' is not allowed in ${(0, Messages_1.ruleElementMessage)(ruleReport.disallow, dependency.to.captured)}${(0, Messages_1.dependencyUsageKindMessage)(ruleReport.importKind, dependency, {
|
|
21
|
+
prefix: " when importing ",
|
|
22
|
+
suffix: "",
|
|
23
|
+
})}. Disallowed in rule ${ruleReport.index + 1}`;
|
|
24
|
+
}
|
|
25
|
+
function modifyTemplates(templates) {
|
|
26
|
+
if (!templates) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
const templatesArray = Array.isArray(templates) ? templates : [templates];
|
|
30
|
+
return templatesArray.map((template) => template.replaceAll("${target.", "${to."));
|
|
31
|
+
}
|
|
32
|
+
function modifyRules(rules) {
|
|
33
|
+
const newRules = [];
|
|
34
|
+
for (let i = 0; i < rules.length; i++) {
|
|
35
|
+
const rule = rules[i];
|
|
36
|
+
const newTargets = (0, elements_1.normalizeElementsSelector)(rule.target);
|
|
37
|
+
const ruleHasDisallow = !!rule.disallow;
|
|
38
|
+
const ruleHasAllow = !!rule.allow;
|
|
39
|
+
let internalPathPatterns = undefined;
|
|
40
|
+
let allowPattern = undefined;
|
|
41
|
+
let disallowPattern = undefined;
|
|
42
|
+
if (ruleHasDisallow && ruleHasAllow) {
|
|
43
|
+
// Workaround to support both allow and disallow in the same rule
|
|
44
|
+
const toAdd = [
|
|
45
|
+
{
|
|
46
|
+
to: newTargets.map((target) => {
|
|
47
|
+
return {
|
|
48
|
+
...target,
|
|
49
|
+
internalPath: modifyTemplates(rule.allow),
|
|
50
|
+
};
|
|
51
|
+
}),
|
|
52
|
+
allow: ["*"],
|
|
53
|
+
importKind: rule.importKind,
|
|
54
|
+
message: rule.message,
|
|
55
|
+
originalRuleIndex: i,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
to: newTargets.map((target) => {
|
|
59
|
+
return {
|
|
60
|
+
...target,
|
|
61
|
+
internalPath: modifyTemplates(rule.disallow),
|
|
62
|
+
};
|
|
63
|
+
}),
|
|
64
|
+
disallow: ["*"],
|
|
65
|
+
importKind: rule.importKind,
|
|
66
|
+
message: rule.message,
|
|
67
|
+
originalRuleIndex: i,
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
newRules.push(...toAdd);
|
|
71
|
+
}
|
|
72
|
+
if (ruleHasDisallow) {
|
|
73
|
+
internalPathPatterns = modifyTemplates(rule.disallow);
|
|
74
|
+
disallowPattern = ["*"];
|
|
75
|
+
}
|
|
76
|
+
else if (ruleHasAllow) {
|
|
77
|
+
internalPathPatterns = modifyTemplates(rule.allow);
|
|
78
|
+
allowPattern = ["*"];
|
|
79
|
+
}
|
|
80
|
+
newRules.push({
|
|
81
|
+
to: newTargets.map((target) => {
|
|
82
|
+
return {
|
|
83
|
+
...target,
|
|
84
|
+
internalPath: internalPathPatterns,
|
|
85
|
+
};
|
|
86
|
+
}),
|
|
87
|
+
allow: allowPattern,
|
|
88
|
+
disallow: disallowPattern,
|
|
89
|
+
importKind: rule.importKind,
|
|
90
|
+
message: rule.message,
|
|
91
|
+
// @ts-expect-error Workaround to support both allow and disallow in the same entry point rule
|
|
92
|
+
originalRuleIndex: i,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return newRules;
|
|
96
|
+
}
|
|
97
|
+
exports.default = (0, Support_1.dependencyRule)({
|
|
98
|
+
ruleName: RULE_ENTRY_POINT,
|
|
99
|
+
description: `Check entry point used for each element type`,
|
|
100
|
+
schema: (0, Settings_1.rulesOptionsSchema)({
|
|
101
|
+
rulesMainKey: "target",
|
|
102
|
+
}),
|
|
103
|
+
}, function ({ dependency, node, context, settings, options }) {
|
|
104
|
+
if (!dependency.to.isIgnored &&
|
|
105
|
+
dependency.to.type &&
|
|
106
|
+
dependency.dependency.relationship.to !==
|
|
107
|
+
elements_1.DEPENDENCY_RELATIONSHIPS_MAP.INTERNAL) {
|
|
108
|
+
const adaptedRuleOptions = {
|
|
109
|
+
...options,
|
|
110
|
+
rules: options && options.rules ? modifyRules(options.rules) : [],
|
|
111
|
+
};
|
|
112
|
+
const ruleData = (0, ElementTypes_1.elementRulesAllowDependency)(dependency, settings, adaptedRuleOptions);
|
|
113
|
+
if (!ruleData.result) {
|
|
114
|
+
context.report({
|
|
115
|
+
message: errorMessage(ruleData, dependency),
|
|
116
|
+
node: node,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}, {
|
|
121
|
+
validateRules: { onlyMainKey: true, mainKey: "target" },
|
|
122
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const elements_1 = require("@boundaries/elements");
|
|
4
|
+
const Messages_1 = require("../Messages");
|
|
5
|
+
const Settings_1 = require("../Settings");
|
|
6
|
+
const Support_1 = require("../Support");
|
|
7
|
+
const ElementTypes_1 = require("./ElementTypes");
|
|
8
|
+
const Support_2 = require("./Support");
|
|
9
|
+
const { RULE_EXTERNAL } = Settings_1.SETTINGS;
|
|
10
|
+
function getErrorReportMessage(report) {
|
|
11
|
+
if (report.path) {
|
|
12
|
+
return report.path;
|
|
13
|
+
}
|
|
14
|
+
return report.specifiers && report.specifiers.length > 0
|
|
15
|
+
? report.specifiers.join(", ")
|
|
16
|
+
: undefined;
|
|
17
|
+
}
|
|
18
|
+
function errorMessage(ruleData, dependency) {
|
|
19
|
+
const ruleReport = ruleData.ruleReport;
|
|
20
|
+
if (!ruleReport) {
|
|
21
|
+
return `No detailed rule report available. This is likely a bug in ${Settings_1.PLUGIN_NAME}. Please report it at ${Settings_1.PLUGIN_ISSUES_URL}`;
|
|
22
|
+
}
|
|
23
|
+
if (ruleReport.message) {
|
|
24
|
+
return (0, Messages_1.customErrorMessage)(ruleReport.message, dependency, {
|
|
25
|
+
specifiers: ruleData.report?.specifiers && ruleData.report?.specifiers.length > 0
|
|
26
|
+
? ruleData.report?.specifiers?.join(", ")
|
|
27
|
+
: undefined,
|
|
28
|
+
path: ruleData.report?.path,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (ruleReport.isDefault) {
|
|
32
|
+
return `No rule allows the usage of external module '${dependency.to.baseSource}' in elements ${(0, Messages_1.elementMessage)(dependency.from)}`;
|
|
33
|
+
}
|
|
34
|
+
const fileReport = `is not allowed in ${(0, Messages_1.ruleElementMessage)(ruleReport.element, dependency.from.captured)}. Disallowed in rule ${ruleReport.index + 1}`;
|
|
35
|
+
if ((ruleData.report?.specifiers && ruleData.report?.specifiers.length > 0) ||
|
|
36
|
+
ruleData.report?.path) {
|
|
37
|
+
return `Usage of ${(0, Messages_1.dependencyUsageKindMessage)(ruleReport.importKind, dependency)}'${getErrorReportMessage(ruleData.report)}' from external module '${dependency.to.baseSource}' ${fileReport}`;
|
|
38
|
+
}
|
|
39
|
+
return `Usage of ${(0, Messages_1.dependencyUsageKindMessage)(ruleReport.importKind, dependency, {
|
|
40
|
+
suffix: " from ",
|
|
41
|
+
})}external module '${dependency.to.baseSource}' ${fileReport}`;
|
|
42
|
+
}
|
|
43
|
+
function modifySelectors(selectors) {
|
|
44
|
+
const originsToMatch = [
|
|
45
|
+
elements_1.ELEMENT_ORIGINS_MAP.EXTERNAL,
|
|
46
|
+
elements_1.ELEMENT_ORIGINS_MAP.CORE,
|
|
47
|
+
];
|
|
48
|
+
if ((0, Support_1.isString)(selectors)) {
|
|
49
|
+
return [{ baseSource: selectors, origin: originsToMatch }];
|
|
50
|
+
}
|
|
51
|
+
return selectors.map((selector) => {
|
|
52
|
+
if ((0, Support_1.isArray)(selector)) {
|
|
53
|
+
return {
|
|
54
|
+
origin: originsToMatch,
|
|
55
|
+
baseSource: selector[0],
|
|
56
|
+
specifiers: selector[1].specifiers,
|
|
57
|
+
internalPath: selector[1].path,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
origin: originsToMatch,
|
|
62
|
+
baseSource: selector,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
exports.default = (0, Support_2.dependencyRule)({
|
|
67
|
+
ruleName: RULE_EXTERNAL,
|
|
68
|
+
description: `Check allowed external dependencies by element type`,
|
|
69
|
+
schema: (0, Settings_1.rulesOptionsSchema)({
|
|
70
|
+
targetMatcherOptions: {
|
|
71
|
+
type: "object",
|
|
72
|
+
properties: {
|
|
73
|
+
specifiers: {
|
|
74
|
+
type: "array",
|
|
75
|
+
items: {
|
|
76
|
+
type: "string",
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
path: {
|
|
80
|
+
oneOf: [
|
|
81
|
+
{
|
|
82
|
+
type: "string",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: "array",
|
|
86
|
+
items: {
|
|
87
|
+
type: "string",
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
additionalProperties: false,
|
|
94
|
+
},
|
|
95
|
+
}),
|
|
96
|
+
}, function ({ dependency, node, context, settings, options }) {
|
|
97
|
+
if ((0, elements_1.isExternalDependencyElement)(dependency.to) ||
|
|
98
|
+
(0, elements_1.isCoreDependencyElement)(dependency.to)) {
|
|
99
|
+
const adaptedRuleOptions = {
|
|
100
|
+
...options,
|
|
101
|
+
rules: options && options.rules
|
|
102
|
+
? options.rules.map((rule) => ({
|
|
103
|
+
...rule,
|
|
104
|
+
allow: rule.allow && modifySelectors(rule.allow),
|
|
105
|
+
disallow: rule.disallow && modifySelectors(rule.disallow),
|
|
106
|
+
}))
|
|
107
|
+
: [],
|
|
108
|
+
};
|
|
109
|
+
const ruleData = (0, ElementTypes_1.elementRulesAllowDependency)(dependency, settings, adaptedRuleOptions);
|
|
110
|
+
if (!ruleData.result) {
|
|
111
|
+
context.report({
|
|
112
|
+
message: errorMessage(ruleData, dependency),
|
|
113
|
+
node: node,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}, {
|
|
118
|
+
validateRules: { onlyMainKey: true },
|
|
119
|
+
});
|