@boundaries/eslint-plugin 5.3.1 → 6.0.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/README.md +9 -9
- package/dist/Config/Config.d.ts +6 -3
- package/dist/Config/Config.js +18 -7
- package/dist/Config/Recommended.d.ts +1 -1
- package/dist/Config/Recommended.js +3 -2
- package/dist/Config/Strict.d.ts +1 -1
- package/dist/Config/Strict.js +2 -2
- package/dist/Debug/Debug.d.ts +34 -0
- package/dist/Debug/Debug.js +285 -0
- package/dist/Debug/index.d.ts +1 -0
- package/dist/{Support → Debug}/index.js +0 -1
- package/dist/Elements/Elements.d.ts +11 -9
- package/dist/Elements/Elements.js +20 -34
- package/dist/Elements/Elements.types.d.ts +1 -0
- package/dist/Messages/CustomMessages.d.ts +44 -0
- package/dist/Messages/CustomMessages.js +156 -0
- package/dist/Messages/CustomMessages.types.d.ts +25 -0
- package/dist/Messages/CustomMessages.types.js +2 -0
- package/dist/Messages/Messages.d.ts +42 -13
- package/dist/Messages/Messages.js +400 -177
- package/dist/Messages/index.d.ts +2 -0
- package/dist/Messages/index.js +2 -0
- package/dist/Public/Config.types.d.ts +2 -2
- package/dist/Public/Config.types.js +2 -2
- package/dist/Public/Rules.types.d.ts +5 -4
- package/dist/Public/Rules.types.js +5 -6
- package/dist/Public/Settings.types.d.ts +3 -2
- package/dist/Public/Settings.types.js +4 -3
- package/dist/Public/index.d.ts +1 -0
- package/dist/Rules/Dependencies.d.ts +59 -0
- package/dist/Rules/Dependencies.js +439 -0
- package/dist/Rules/EntryPoint.js +44 -94
- package/dist/Rules/External.js +93 -68
- package/dist/Rules/NoIgnored.js +4 -4
- package/dist/Rules/NoPrivate.js +18 -5
- package/dist/Rules/NoUnknown.js +5 -5
- package/dist/Rules/NoUnknownFiles.js +4 -3
- package/dist/Rules/Support/DependencyRule.d.ts +9 -1
- package/dist/Rules/Support/DependencyRule.js +15 -15
- package/dist/Rules/Support/DependencyRule.types.d.ts +1 -1
- package/dist/Rules/Support/Helpers.d.ts +6 -2
- package/dist/Rules/Support/Helpers.js +7 -31
- package/dist/Settings/Helpers.d.ts +83 -1
- package/dist/Settings/Helpers.js +197 -7
- package/dist/Settings/Settings.d.ts +19 -2
- package/dist/Settings/Settings.js +20 -10
- package/dist/Settings/Validations.d.ts +11958 -45
- package/dist/Settings/Validations.js +797 -184
- package/dist/Settings/index.d.ts +0 -1
- package/dist/Settings/index.js +0 -1
- package/dist/{Settings → Shared}/Settings.types.d.ts +143 -37
- package/dist/{Settings → Shared}/Settings.types.js +32 -6
- package/dist/{Support/Common.d.ts → Shared/TypeHelpers.d.ts} +18 -0
- package/dist/{Support/Common.js → Shared/TypeHelpers.js} +28 -1
- package/dist/Shared/index.d.ts +2 -0
- package/dist/Shared/index.js +18 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -10
- package/package.json +9 -8
- package/dist/Rules/ElementTypes.d.ts +0 -25
- package/dist/Rules/ElementTypes.js +0 -279
- package/dist/Support/Debug.d.ts +0 -5
- package/dist/Support/Debug.js +0 -54
- package/dist/Support/index.d.ts +0 -2
package/README.md
CHANGED
|
@@ -25,8 +25,8 @@ Enforce architectural boundaries in your JavaScript and TypeScript projects.
|
|
|
25
25
|
|
|
26
26
|
**ESLint Plugin Boundaries** is an ESLint plugin that helps you maintain clean architecture by enforcing boundaries between different parts of your codebase. Define your architectural layers, specify how they can interact, and get instant feedback when boundaries are violated.
|
|
27
27
|
|
|
28
|
-
- **Architectural Enforcement**: Define
|
|
29
|
-
- **Flexible Configuration**: Adapt the plugin to any project structure. It works with monorepos, modular architectures, layered patterns, and
|
|
28
|
+
- **Architectural Enforcement**: Define elements and dependency rules that match your project's architecture
|
|
29
|
+
- **Flexible Configuration**: Adapt the plugin to any project structure. It works with monorepos, modular architectures, layered patterns, and any custom structure you can imagine
|
|
30
30
|
- **Real-time Feedback**: Get immediate ESLint errors when imports violate your architectural rules
|
|
31
31
|
|
|
32
32
|
## Documentation
|
|
@@ -63,9 +63,9 @@ export default [
|
|
|
63
63
|
plugins: { boundaries },
|
|
64
64
|
settings: {
|
|
65
65
|
"boundaries/elements": [
|
|
66
|
-
{ type: "
|
|
67
|
-
{ type: "
|
|
68
|
-
{ type: "
|
|
66
|
+
{ type: "controller", pattern: "controllers/*" },
|
|
67
|
+
{ type: "model", pattern: "models/*" },
|
|
68
|
+
{ type: "view", pattern: "views/*" }
|
|
69
69
|
]
|
|
70
70
|
}
|
|
71
71
|
}
|
|
@@ -77,12 +77,12 @@ Define your dependency rules:
|
|
|
77
77
|
```javascript
|
|
78
78
|
{
|
|
79
79
|
rules: {
|
|
80
|
-
"boundaries/
|
|
80
|
+
"boundaries/dependencies": [2, {
|
|
81
81
|
default: "disallow",
|
|
82
82
|
rules: [
|
|
83
|
-
{ from: "
|
|
84
|
-
{ from: "
|
|
85
|
-
{ from: "
|
|
83
|
+
{ from: { type: "controller" }, allow: { to: { type: ["model", "view"] } } },
|
|
84
|
+
{ from: { type: "view" }, allow: { to: { type: "model" } } },
|
|
85
|
+
{ from: { type: "model" }, disallow: { to: { type: "*" } } }
|
|
86
86
|
]
|
|
87
87
|
}]
|
|
88
88
|
}
|
package/dist/Config/Config.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { Linter } from "eslint";
|
|
2
|
-
import type { PluginBoundaries, Config } from "../
|
|
3
|
-
import { PLUGIN_NAME } from "../
|
|
2
|
+
import type { PluginBoundaries, Config } from "../Shared";
|
|
3
|
+
import { PLUGIN_NAME } from "../Shared";
|
|
4
4
|
export * from "../Public";
|
|
5
|
+
/**
|
|
6
|
+
* The full ESLint config object returned by createConfig, including the plugins field with the boundaries plugin registered.
|
|
7
|
+
*/
|
|
5
8
|
type PluginFullConfig<PluginName extends string = typeof PLUGIN_NAME> = {
|
|
6
9
|
plugins: Record<PluginName, PluginBoundaries>;
|
|
7
10
|
files: Linter.Config["files"];
|
|
@@ -28,7 +31,7 @@ type PluginFullConfig<PluginName extends string = typeof PLUGIN_NAME> = {
|
|
|
28
31
|
* },
|
|
29
32
|
* rules: {
|
|
30
33
|
* ...recommended.rules,
|
|
31
|
-
* "boundaries/
|
|
34
|
+
* "boundaries/dependencies": ["error", { default: "disallow" }],
|
|
32
35
|
* }
|
|
33
36
|
* });
|
|
34
37
|
*
|
package/dist/Config/Config.js
CHANGED
|
@@ -21,31 +21,42 @@ exports.strict = exports.recommended = void 0;
|
|
|
21
21
|
exports.createConfig = createConfig;
|
|
22
22
|
const index_1 = __importDefault(require("../index"));
|
|
23
23
|
const Settings_1 = require("../Settings");
|
|
24
|
+
const Shared_1 = require("../Shared");
|
|
24
25
|
const Recommended_1 = __importDefault(require("./Recommended"));
|
|
25
26
|
const Strict_1 = __importDefault(require("./Strict"));
|
|
26
27
|
__exportStar(require("../Public"), exports);
|
|
28
|
+
/**
|
|
29
|
+
* Rewrites rule keys to the effective plugin namespace used by `createConfig`.
|
|
30
|
+
*
|
|
31
|
+
* It accepts rules prefixed either with the default plugin name (`boundaries`)
|
|
32
|
+
* or with the custom plugin name passed to `createConfig`.
|
|
33
|
+
*
|
|
34
|
+
* @param pluginName - Plugin namespace to enforce in output rule keys.
|
|
35
|
+
* @param rules - Input rule entries from the user config.
|
|
36
|
+
* @returns Rules object normalized to the requested plugin namespace.
|
|
37
|
+
*/
|
|
27
38
|
function renamePluginRules(pluginName, rules) {
|
|
28
39
|
if (!rules) {
|
|
29
40
|
return {};
|
|
30
41
|
}
|
|
31
|
-
const allowedPrefixes = new Set([
|
|
42
|
+
const allowedPrefixes = new Set([Shared_1.PLUGIN_NAME, pluginName]);
|
|
32
43
|
// Return the same rules objects, but converting plugin default rule keys with provided plugin name
|
|
33
44
|
return Object.entries(rules).reduce((acc, [key, value]) => {
|
|
34
45
|
if (!key.includes("/")) {
|
|
35
|
-
throw new Error(`Invalid rule key "${key}". When using createConfig, all rules must belong to eslint-plugin-boundaries. You can prefix them with the original plugin name "${
|
|
46
|
+
throw new Error(`Invalid rule key "${key}". When using createConfig, all rules must belong to eslint-plugin-boundaries. You can prefix them with the original plugin name "${Shared_1.PLUGIN_NAME}/", or with the provided plugin name "${pluginName}/".`);
|
|
36
47
|
}
|
|
37
48
|
const splittedRuleKey = key.split("/");
|
|
38
49
|
const rulePrefix = splittedRuleKey[0];
|
|
39
50
|
const ruleName = splittedRuleKey[1];
|
|
40
51
|
if (!allowedPrefixes.has(rulePrefix)) {
|
|
41
|
-
throw new Error(`Invalid rule key "${key}". When using createConfig, all rules must belong to eslint-plugin-boundaries. You can prefix them with the original plugin name "${
|
|
52
|
+
throw new Error(`Invalid rule key "${key}". When using createConfig, all rules must belong to eslint-plugin-boundaries. You can prefix them with the original plugin name "${Shared_1.PLUGIN_NAME}/", or with the provided plugin name "${pluginName}/".`);
|
|
42
53
|
}
|
|
43
54
|
if (!(0, Settings_1.isRuleShortName)(ruleName)) {
|
|
44
55
|
throw new Error(`Invalid rule name "${ruleName}". When using createConfig, all rules must belong to eslint-plugin-boundaries.`);
|
|
45
56
|
}
|
|
46
57
|
let newKey;
|
|
47
|
-
if (rulePrefix ===
|
|
48
|
-
const suffix = key.slice(
|
|
58
|
+
if (rulePrefix === Shared_1.PLUGIN_NAME) {
|
|
59
|
+
const suffix = key.slice(Shared_1.PLUGIN_NAME.length + 1);
|
|
49
60
|
newKey = `${pluginName}/${suffix}`;
|
|
50
61
|
}
|
|
51
62
|
else {
|
|
@@ -77,14 +88,14 @@ function renamePluginRules(pluginName, rules) {
|
|
|
77
88
|
* },
|
|
78
89
|
* rules: {
|
|
79
90
|
* ...recommended.rules,
|
|
80
|
-
* "boundaries/
|
|
91
|
+
* "boundaries/dependencies": ["error", { default: "disallow" }],
|
|
81
92
|
* }
|
|
82
93
|
* });
|
|
83
94
|
*
|
|
84
95
|
* export default [config];
|
|
85
96
|
* ```
|
|
86
97
|
*/
|
|
87
|
-
function createConfig(config, name =
|
|
98
|
+
function createConfig(config, name = Shared_1.PLUGIN_NAME) {
|
|
88
99
|
const pluginsRegistration = {
|
|
89
100
|
[name]: index_1.default,
|
|
90
101
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const
|
|
3
|
+
const Shared_1 = require("../Shared");
|
|
4
4
|
const { ELEMENTS,
|
|
5
5
|
// rules
|
|
6
|
-
RULE_ELEMENT_TYPES, RULE_ENTRY_POINT, RULE_EXTERNAL, RULE_NO_IGNORED, RULE_NO_PRIVATE, RULE_NO_UNKNOWN_FILES, RULE_NO_UNKNOWN, } =
|
|
6
|
+
RULE_ELEMENT_TYPES, RULE_DEPENDENCIES, RULE_ENTRY_POINT, RULE_EXTERNAL, RULE_NO_IGNORED, RULE_NO_PRIVATE, RULE_NO_UNKNOWN_FILES, RULE_NO_UNKNOWN, } = Shared_1.SETTINGS;
|
|
7
7
|
// TODO In next major version: Export also files, plugin, etc.
|
|
8
8
|
/**
|
|
9
9
|
* Recommended configuration for eslint-plugin-boundaries.
|
|
@@ -15,6 +15,7 @@ RULE_ELEMENT_TYPES, RULE_ENTRY_POINT, RULE_EXTERNAL, RULE_NO_IGNORED, RULE_NO_PR
|
|
|
15
15
|
const config = {
|
|
16
16
|
rules: {
|
|
17
17
|
[RULE_ELEMENT_TYPES]: [2],
|
|
18
|
+
[RULE_DEPENDENCIES]: [2],
|
|
18
19
|
[RULE_ENTRY_POINT]: [2],
|
|
19
20
|
[RULE_EXTERNAL]: [2],
|
|
20
21
|
[RULE_NO_IGNORED]: 0,
|
package/dist/Config/Strict.d.ts
CHANGED
package/dist/Config/Strict.js
CHANGED
|
@@ -3,9 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
6
|
+
const Shared_1 = require("../Shared");
|
|
7
7
|
const Recommended_1 = __importDefault(require("./Recommended"));
|
|
8
|
-
const { RULE_NO_IGNORED, RULE_NO_UNKNOWN_FILES, RULE_NO_UNKNOWN } =
|
|
8
|
+
const { RULE_NO_IGNORED, RULE_NO_UNKNOWN_FILES, RULE_NO_UNKNOWN } = Shared_1.SETTINGS;
|
|
9
9
|
// TODO In next major version: Export also files, plugin, etc.
|
|
10
10
|
/**
|
|
11
11
|
* Strict configuration for eslint-plugin-boundaries.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { DependencyDescription, ElementDescription, Matcher, DependencyMatchResult } from "@boundaries/elements";
|
|
2
|
+
import type { SettingsNormalized } from "../Shared";
|
|
3
|
+
/**
|
|
4
|
+
* Prints a warning message using warning color.
|
|
5
|
+
* @param title - Warning title shown before message.
|
|
6
|
+
* @param message - Warning message.
|
|
7
|
+
*/
|
|
8
|
+
export declare function warn(title: string, message?: string): void;
|
|
9
|
+
/**
|
|
10
|
+
* Prints a warning only once for each unique message.
|
|
11
|
+
*
|
|
12
|
+
* @param title - Warning title shown before message.
|
|
13
|
+
* @param message - Warning message candidate.
|
|
14
|
+
* @returns `true` when warning was emitted, `false` when skipped.
|
|
15
|
+
*/
|
|
16
|
+
export declare function warnOnce(title: string, message?: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Emits debug output for file or dependency descriptions when enabled.
|
|
19
|
+
*
|
|
20
|
+
* @param description - Element or dependency description to debug.
|
|
21
|
+
* @param settings - Normalized plugin settings including debug filters.
|
|
22
|
+
* @param matcher - Matcher used to evaluate debug filters.
|
|
23
|
+
*/
|
|
24
|
+
export declare function debugDescription(description: ElementDescription | DependencyDescription, settings: SettingsNormalized, matcher: Matcher): void;
|
|
25
|
+
/**
|
|
26
|
+
* Prints debug information for a rule result when debug mode is enabled
|
|
27
|
+
*
|
|
28
|
+
* @param dependencyMatchResult - The result of matching a dependency against rules, including match status and captured values.
|
|
29
|
+
* @param ruleIndex - The index of the rule that was matched, if any.
|
|
30
|
+
* @param dependency - The original dependency description that was evaluated against the rules.
|
|
31
|
+
* @param settings - Normalized plugin settings including debug filters.
|
|
32
|
+
* @param matcher - Matcher used to evaluate debug filters.
|
|
33
|
+
*/
|
|
34
|
+
export declare function printDependenciesRuleResult(dependencyMatchResult: DependencyMatchResult | null, ruleIndex: number | null, dependency: DependencyDescription, settings: SettingsNormalized, matcher: Matcher): void;
|
|
@@ -0,0 +1,285 @@
|
|
|
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.warn = warn;
|
|
7
|
+
exports.warnOnce = warnOnce;
|
|
8
|
+
exports.debugDescription = debugDescription;
|
|
9
|
+
exports.printDependenciesRuleResult = printDependenciesRuleResult;
|
|
10
|
+
const elements_1 = require("@boundaries/elements");
|
|
11
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
12
|
+
const Shared_1 = require("../Shared");
|
|
13
|
+
const warns = new Set();
|
|
14
|
+
const debuggedFiles = new Set();
|
|
15
|
+
const debuggedDependencies = new Set();
|
|
16
|
+
const PREFIX_COLOR = "#A8B3D8";
|
|
17
|
+
const CONSOLE_LEVELS = {
|
|
18
|
+
log: "log",
|
|
19
|
+
warn: "warn",
|
|
20
|
+
};
|
|
21
|
+
const LOG_LEVELS = {
|
|
22
|
+
debug: "debug",
|
|
23
|
+
warning: "warning",
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Checks whether debug mode is globally enabled through environment variable.
|
|
27
|
+
*
|
|
28
|
+
* @returns `true` when debug env flag is active.
|
|
29
|
+
*/
|
|
30
|
+
function isDebugModeEnabled() {
|
|
31
|
+
return Boolean(process.env[Shared_1.SETTINGS.DEBUG]);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Computes final debug activation combining setting flag and environment flag.
|
|
35
|
+
*
|
|
36
|
+
* @param settingEnabled - Debug flag configured in plugin settings.
|
|
37
|
+
* @returns `true` when either setting or environment enables debug mode.
|
|
38
|
+
*/
|
|
39
|
+
function isDebugEnabled(settingEnabled) {
|
|
40
|
+
return settingEnabled || isDebugModeEnabled();
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Prints a log line to stdout with optional color formatting.
|
|
44
|
+
*
|
|
45
|
+
* @param message - Message text to print.
|
|
46
|
+
* @param options - Optional parameters for color and log level.
|
|
47
|
+
* @param options.color - Optional color key from the internal color map.
|
|
48
|
+
* @param options.level - Optional log level determining which console method to use (default is "log").
|
|
49
|
+
*/
|
|
50
|
+
function printLog(message, level) {
|
|
51
|
+
// eslint-disable-next-line no-console
|
|
52
|
+
console[level](message);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Indents every line except the first one by a fixed number of spaces.
|
|
56
|
+
*
|
|
57
|
+
* @param text - Multi-line text to indent.
|
|
58
|
+
* @param spaces - Number of spaces to prepend.
|
|
59
|
+
* @returns Text with indentation applied.
|
|
60
|
+
*/
|
|
61
|
+
function indentLines(text, spaces) {
|
|
62
|
+
const pad = " ".repeat(spaces);
|
|
63
|
+
return text
|
|
64
|
+
.split("\n")
|
|
65
|
+
.map((line) => `${pad}${line}`)
|
|
66
|
+
.join("\n");
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Generates a log prefix with plugin name and log level, applying color formatting based on log level.
|
|
70
|
+
* @param logLevel Type of log level to determine prefix formatting (default is "debug").
|
|
71
|
+
* @returns The formatted log prefix string to prepend to debug messages.
|
|
72
|
+
*/
|
|
73
|
+
function getLogPrefix(logLevel) {
|
|
74
|
+
const colorMethod = logLevel === LOG_LEVELS.warning ? chalk_1.default.yellow : chalk_1.default.blue;
|
|
75
|
+
const pluginTag = `[${Shared_1.PLUGIN_NAME}]`;
|
|
76
|
+
const levelTag = `[${logLevel}]`;
|
|
77
|
+
return `${chalk_1.default.hex(PREFIX_COLOR)(pluginTag)}${colorMethod(levelTag)}:`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Prints a titled block of text in debug output, with the title formatted according to the log level.
|
|
81
|
+
* @param title - Block title shown before the message.
|
|
82
|
+
* @param options - Optional parameters for log level.
|
|
83
|
+
* @param options.level - Log level determining the title formatting (default is "debug").
|
|
84
|
+
* @param options.message - Message text to print within the block.
|
|
85
|
+
* @param level - Log level determining the title formatting (default is "debug").
|
|
86
|
+
*/
|
|
87
|
+
function printBlock(title, { level, message, }) {
|
|
88
|
+
const header = `${getLogPrefix(level)} ${title}`;
|
|
89
|
+
const consoleLevel = level === LOG_LEVELS.warning ? CONSOLE_LEVELS.warn : CONSOLE_LEVELS.log;
|
|
90
|
+
if (!message) {
|
|
91
|
+
printLog(header, consoleLevel);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
printLog(`${header}\n${indentLines(message, 2)}\n`, consoleLevel);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Prints a titled JSON block in debug output.
|
|
98
|
+
*
|
|
99
|
+
* @param title - Block title shown before data.
|
|
100
|
+
* @param data - Data serialized as formatted JSON.
|
|
101
|
+
*/
|
|
102
|
+
function printDebugBlock(title, data) {
|
|
103
|
+
printBlock(title, {
|
|
104
|
+
message: JSON.stringify(data, null, 2),
|
|
105
|
+
level: LOG_LEVELS.debug,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Prints a warning message using warning color.
|
|
110
|
+
* @param title - Warning title shown before message.
|
|
111
|
+
* @param message - Warning message.
|
|
112
|
+
*/
|
|
113
|
+
function warn(title, message) {
|
|
114
|
+
printBlock(title, { message, level: LOG_LEVELS.warning });
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Prints a warning only once for each unique message.
|
|
118
|
+
*
|
|
119
|
+
* @param title - Warning title shown before message.
|
|
120
|
+
* @param message - Warning message candidate.
|
|
121
|
+
* @returns `true` when warning was emitted, `false` when skipped.
|
|
122
|
+
*/
|
|
123
|
+
function warnOnce(title, message) {
|
|
124
|
+
const messageKey = `${title}-${message}`;
|
|
125
|
+
if (warns.has(messageKey)) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
warns.add(messageKey);
|
|
129
|
+
warn(title, message);
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Emits debug output for file or dependency descriptions when enabled.
|
|
134
|
+
*
|
|
135
|
+
* @param description - Element or dependency description to debug.
|
|
136
|
+
* @param settings - Normalized plugin settings including debug filters.
|
|
137
|
+
* @param matcher - Matcher used to evaluate debug filters.
|
|
138
|
+
*/
|
|
139
|
+
function debugDescription(description, settings, matcher) {
|
|
140
|
+
if (!isDebugEnabled(settings.debug.enabled)) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if ((0, elements_1.isDependencyDescription)(description)) {
|
|
144
|
+
printDependencyDebug(description, settings, matcher);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
printFileDebug(description, settings, matcher);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Returns an unique identifier for a file description, used to track which files have already been debugged.
|
|
151
|
+
* @param description - File element description.
|
|
152
|
+
* @returns Unique identifier string for the file.
|
|
153
|
+
*/
|
|
154
|
+
function getFileIdentifier(description) {
|
|
155
|
+
/* istanbul ignore next - Fallback for descriptions missing path, which should not happen but ensures stability of debug logging. */
|
|
156
|
+
return description.path || "<unknown-file>";
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Prints debug info for a file description once per file path.
|
|
160
|
+
*
|
|
161
|
+
* @param description - File element description.
|
|
162
|
+
* @param settings - Normalized plugin settings.
|
|
163
|
+
* @param matcher - Matcher used for filter checks.
|
|
164
|
+
*/
|
|
165
|
+
function printFileDebug(description, settings, matcher) {
|
|
166
|
+
if (!settings.debug.messages.files) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const fileIdentifier = getFileIdentifier(description);
|
|
170
|
+
if (debuggedFiles.has(fileIdentifier)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
debuggedFiles.add(fileIdentifier);
|
|
174
|
+
if (shouldPrintFile(description, settings, matcher)) {
|
|
175
|
+
const title = `Description of file "${chalk_1.default.green(fileIdentifier)}":`;
|
|
176
|
+
printDebugBlock(title, description);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Prints debug info for a dependency description once per file/source pair.
|
|
181
|
+
*
|
|
182
|
+
* @param description - Dependency description.
|
|
183
|
+
* @param settings - Normalized plugin settings.
|
|
184
|
+
* @param matcher - Matcher used for filter checks.
|
|
185
|
+
*/
|
|
186
|
+
function printDependencyDebug(description, settings, matcher) {
|
|
187
|
+
printFileDebug(description.from, settings, matcher);
|
|
188
|
+
if (!settings.debug.messages.dependencies) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
/* istanbul ignore next - Fallback for descriptions missing path, which should not happen but ensures stability of debug logging. */
|
|
192
|
+
const dependencySource = description.dependency.source || "<unknown-source>";
|
|
193
|
+
const fileIdentifier = getFileIdentifier(description.from);
|
|
194
|
+
const dependencyIdentifier = `${dependencySource}-${fileIdentifier}`;
|
|
195
|
+
if (debuggedDependencies.has(dependencyIdentifier)) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
debuggedDependencies.add(dependencyIdentifier);
|
|
199
|
+
if (shouldPrintDependency(description, settings, matcher)) {
|
|
200
|
+
const title = `Description of dependency "${chalk_1.default.green(dependencySource)}" in file "${chalk_1.default.green(fileIdentifier)}":`;
|
|
201
|
+
printDebugBlock(title, description);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const DEPENDENCIES_VIOLATION_PREFIX = `${Shared_1.DEPENDENCIES} rule violation:`;
|
|
205
|
+
/**
|
|
206
|
+
* Prints debug information for a rule result when debug mode is enabled
|
|
207
|
+
*
|
|
208
|
+
* @param dependencyMatchResult - The result of matching a dependency against rules, including match status and captured values.
|
|
209
|
+
* @param ruleIndex - The index of the rule that was matched, if any.
|
|
210
|
+
* @param dependency - The original dependency description that was evaluated against the rules.
|
|
211
|
+
* @param settings - Normalized plugin settings including debug filters.
|
|
212
|
+
* @param matcher - Matcher used to evaluate debug filters.
|
|
213
|
+
*/
|
|
214
|
+
function printDependenciesRuleResult(dependencyMatchResult, ruleIndex, dependency, settings, matcher) {
|
|
215
|
+
if (!isDebugEnabled(settings.debug.enabled) ||
|
|
216
|
+
!settings.debug.messages.violations ||
|
|
217
|
+
!shouldPrintDependency(dependency, settings, matcher) ||
|
|
218
|
+
!shouldPrintFile(dependency.from, settings, matcher)) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if ((0, Shared_1.isNull)(ruleIndex) || !dependencyMatchResult) {
|
|
222
|
+
printDebugBlock(`${DEPENDENCIES_VIOLATION_PREFIX} Dependency did not match any rule, and default policy is to deny.`, {
|
|
223
|
+
dependency,
|
|
224
|
+
});
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const title = `${DEPENDENCIES_VIOLATION_PREFIX} Rule at index ${ruleIndex} reported a violation because the following selector matched the dependency:`;
|
|
228
|
+
const selectorRelevantData = {};
|
|
229
|
+
if (dependencyMatchResult.from) {
|
|
230
|
+
selectorRelevantData.from = dependencyMatchResult.from;
|
|
231
|
+
}
|
|
232
|
+
if (dependencyMatchResult.to) {
|
|
233
|
+
selectorRelevantData.to = dependencyMatchResult.to;
|
|
234
|
+
}
|
|
235
|
+
if (dependencyMatchResult.dependency) {
|
|
236
|
+
selectorRelevantData.dependency = dependencyMatchResult.dependency;
|
|
237
|
+
}
|
|
238
|
+
printDebugBlock(title, {
|
|
239
|
+
rule: {
|
|
240
|
+
index: ruleIndex,
|
|
241
|
+
selector: selectorRelevantData,
|
|
242
|
+
},
|
|
243
|
+
dependency,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Checks whether a file description passes debug file filters.
|
|
248
|
+
*
|
|
249
|
+
* @param description - File description to evaluate.
|
|
250
|
+
* @param settings - Normalized plugin settings.
|
|
251
|
+
* @param matcher - Matcher used to evaluate selectors.
|
|
252
|
+
* @returns `true` when file should be printed in debug output.
|
|
253
|
+
*/
|
|
254
|
+
function shouldPrintFile(description, settings, matcher) {
|
|
255
|
+
const fileFilters = settings.debug.filter.files;
|
|
256
|
+
if ((0, Shared_1.isUndefined)(fileFilters)) {
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
if (fileFilters.length === 0) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
return fileFilters.some((selector) => !(0, Shared_1.isNull)(matcher.getElementSelectorMatchingDescription(description, selector)));
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Checks whether a dependency description passes debug dependency filters.
|
|
266
|
+
*
|
|
267
|
+
* @param description - Dependency description to evaluate.
|
|
268
|
+
* @param settings - Normalized plugin settings.
|
|
269
|
+
* @param matcher - Matcher used to evaluate selectors.
|
|
270
|
+
* @returns `true` when dependency should be printed in debug output.
|
|
271
|
+
*/
|
|
272
|
+
function shouldPrintDependency(description, settings, matcher) {
|
|
273
|
+
const dependencyFilters = settings.debug.filter.dependencies;
|
|
274
|
+
if ((0, Shared_1.isUndefined)(dependencyFilters)) {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
if (dependencyFilters.length === 0) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
if (!shouldPrintFile(description.from, settings, matcher)) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
return dependencyFilters.some((selector) => matcher.getDependencySelectorMatchingDescription(description, selector)
|
|
284
|
+
.isMatch);
|
|
285
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Debug";
|
|
@@ -14,5 +14,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./Common"), exports);
|
|
18
17
|
__exportStar(require("./Debug"), exports);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Matcher, DependencyDescription, DependencyKind, ElementDescription } from "@boundaries/elements";
|
|
2
2
|
import type { Rule } from "eslint";
|
|
3
|
-
import type { SettingsNormalized } from "../
|
|
3
|
+
import type { SettingsNormalized } from "../Shared";
|
|
4
4
|
import type { EslintLiteralNode } from "./Elements.types";
|
|
5
5
|
/**
|
|
6
|
-
* Returns the elements matcher based on the ESLint rule context, filtering out invalid descriptors
|
|
7
|
-
* @param
|
|
6
|
+
* Returns the elements matcher based on the ESLint rule context settings already normalized, filtering out invalid descriptors
|
|
7
|
+
* @param settings The ESLint rule context settings normalized
|
|
8
8
|
* @returns The elements matcher
|
|
9
9
|
*/
|
|
10
10
|
export declare function getElementsMatcher(settings: SettingsNormalized): Matcher;
|
|
@@ -16,26 +16,28 @@ export declare function getElementsMatcher(settings: SettingsNormalized): Matche
|
|
|
16
16
|
export declare function getSpecifiers(node: Rule.Node): string[];
|
|
17
17
|
/**
|
|
18
18
|
* Returns the description of the current file being linted
|
|
19
|
-
* @param fileName The file name
|
|
19
|
+
* @param fileName The file name (absolute path)
|
|
20
20
|
* @param settings The ESLint rule context settings normalized
|
|
21
21
|
* @returns The description of the current file being linted
|
|
22
22
|
*/
|
|
23
23
|
export declare function elementDescription(fileName: string, settings: SettingsNormalized): ElementDescription;
|
|
24
24
|
/**
|
|
25
25
|
* Returns the description of a dependency node
|
|
26
|
-
* @param
|
|
26
|
+
* @param options The dependency node info
|
|
27
|
+
* @param options.node The dependency node
|
|
28
|
+
* @param options.kind The kind of the dependency
|
|
29
|
+
* @param options.nodeKind The kind of the node generating the dependency
|
|
30
|
+
* @param fileName The file name (absolute path)
|
|
31
|
+
* @param settings The ESLint rule context settings normalized
|
|
27
32
|
* @param context The ESLint rule context
|
|
28
33
|
* @returns The description of the dependency node
|
|
29
34
|
*/
|
|
30
35
|
export declare function dependencyDescription({ node, kind, nodeKind, }: {
|
|
31
|
-
/** The dependency node */
|
|
32
36
|
node: EslintLiteralNode;
|
|
33
|
-
/** The kind of the dependency */
|
|
34
37
|
kind: DependencyKind;
|
|
35
|
-
/** The kind of the node generating the dependency */
|
|
36
38
|
nodeKind?: string;
|
|
37
39
|
},
|
|
38
|
-
/** The file name */
|
|
40
|
+
/** The file name (absolute path) */
|
|
39
41
|
fileName: string,
|
|
40
42
|
/** The ESLint rule context settings normalized */
|
|
41
43
|
settings: SettingsNormalized,
|
|
@@ -9,11 +9,11 @@ exports.elementDescription = elementDescription;
|
|
|
9
9
|
exports.dependencyDescription = dependencyDescription;
|
|
10
10
|
const elements_1 = require("@boundaries/elements");
|
|
11
11
|
const resolve_1 = __importDefault(require("eslint-module-utils/resolve"));
|
|
12
|
-
const
|
|
12
|
+
const Debug_1 = require("../Debug");
|
|
13
13
|
const elements = new elements_1.Elements();
|
|
14
14
|
/**
|
|
15
|
-
* Returns the elements matcher based on the ESLint rule context, filtering out invalid descriptors
|
|
16
|
-
* @param
|
|
15
|
+
* Returns the elements matcher based on the ESLint rule context settings already normalized, filtering out invalid descriptors
|
|
16
|
+
* @param settings The ESLint rule context settings normalized
|
|
17
17
|
* @returns The elements matcher
|
|
18
18
|
*/
|
|
19
19
|
function getElementsMatcher(settings) {
|
|
@@ -22,30 +22,11 @@ function getElementsMatcher(settings) {
|
|
|
22
22
|
includePaths: settings.includePaths,
|
|
23
23
|
legacyTemplates: settings.legacyTemplates,
|
|
24
24
|
cache: settings.cache,
|
|
25
|
+
flagAsExternal: settings.flagAsExternal,
|
|
26
|
+
rootPath: settings.rootPath,
|
|
25
27
|
});
|
|
26
28
|
return elementsMatcher;
|
|
27
29
|
}
|
|
28
|
-
/**
|
|
29
|
-
* Replaces backslashes with forward slashes in a given path
|
|
30
|
-
* @param filePath The file path to modify
|
|
31
|
-
* @returns The modified file path with forward slashes
|
|
32
|
-
*/
|
|
33
|
-
function replacePathSlashes(filePath) {
|
|
34
|
-
return filePath.replaceAll("\\", "/");
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Transforms an absolute path into a project-relative path
|
|
38
|
-
* @param absolutePath The absolute path to transform
|
|
39
|
-
* @param rootPath The root path of the project
|
|
40
|
-
* @returns The relative path from the project root
|
|
41
|
-
*/
|
|
42
|
-
function projectPath(absolutePath, rootPath) {
|
|
43
|
-
if (absolutePath) {
|
|
44
|
-
// TODO: Use path.relative when possible. With caution because this would break current external paths
|
|
45
|
-
return replacePathSlashes(absolutePath).replace(`${replacePathSlashes(rootPath)}/`, "");
|
|
46
|
-
}
|
|
47
|
-
return "";
|
|
48
|
-
}
|
|
49
30
|
/**
|
|
50
31
|
* Returns the specifiers used in an import or export statement
|
|
51
32
|
* @param node The AST node representing the import or export
|
|
@@ -69,25 +50,29 @@ function getSpecifiers(node) {
|
|
|
69
50
|
}
|
|
70
51
|
/**
|
|
71
52
|
* Returns the description of the current file being linted
|
|
72
|
-
* @param fileName The file name
|
|
53
|
+
* @param fileName The file name (absolute path)
|
|
73
54
|
* @param settings The ESLint rule context settings normalized
|
|
74
55
|
* @returns The description of the current file being linted
|
|
75
56
|
*/
|
|
76
57
|
function elementDescription(fileName, settings) {
|
|
77
58
|
const matcher = getElementsMatcher(settings);
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
(0, Support_1.debugDescription)(result);
|
|
59
|
+
const result = matcher.describeElement(fileName);
|
|
60
|
+
(0, Debug_1.debugDescription)(result, settings, matcher);
|
|
81
61
|
return result;
|
|
82
62
|
}
|
|
83
63
|
/**
|
|
84
64
|
* Returns the description of a dependency node
|
|
85
|
-
* @param
|
|
65
|
+
* @param options The dependency node info
|
|
66
|
+
* @param options.node The dependency node
|
|
67
|
+
* @param options.kind The kind of the dependency
|
|
68
|
+
* @param options.nodeKind The kind of the node generating the dependency
|
|
69
|
+
* @param fileName The file name (absolute path)
|
|
70
|
+
* @param settings The ESLint rule context settings normalized
|
|
86
71
|
* @param context The ESLint rule context
|
|
87
72
|
* @returns The description of the dependency node
|
|
88
73
|
*/
|
|
89
74
|
function dependencyDescription({ node, kind, nodeKind, },
|
|
90
|
-
/** The file name */
|
|
75
|
+
/** The file name (absolute path) */
|
|
91
76
|
fileName,
|
|
92
77
|
/** The ESLint rule context settings normalized */
|
|
93
78
|
settings,
|
|
@@ -95,14 +80,15 @@ settings,
|
|
|
95
80
|
context) {
|
|
96
81
|
const source = String(node.value);
|
|
97
82
|
const matcher = getElementsMatcher(settings);
|
|
83
|
+
const resolvedPath = (0, resolve_1.default)(source, context);
|
|
98
84
|
const description = matcher.describeDependency({
|
|
99
|
-
from:
|
|
100
|
-
to:
|
|
85
|
+
from: fileName,
|
|
86
|
+
to: resolvedPath || undefined,
|
|
101
87
|
source,
|
|
102
|
-
kind: kind || "value",
|
|
88
|
+
kind: kind || "value",
|
|
103
89
|
nodeKind,
|
|
104
90
|
specifiers: getSpecifiers(node),
|
|
105
91
|
});
|
|
106
|
-
(0,
|
|
92
|
+
(0, Debug_1.debugDescription)(description, settings, matcher);
|
|
107
93
|
return description;
|
|
108
94
|
}
|