@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.
Files changed (64) hide show
  1. package/README.md +9 -9
  2. package/dist/Config/Config.d.ts +6 -3
  3. package/dist/Config/Config.js +18 -7
  4. package/dist/Config/Recommended.d.ts +1 -1
  5. package/dist/Config/Recommended.js +3 -2
  6. package/dist/Config/Strict.d.ts +1 -1
  7. package/dist/Config/Strict.js +2 -2
  8. package/dist/Debug/Debug.d.ts +34 -0
  9. package/dist/Debug/Debug.js +285 -0
  10. package/dist/Debug/index.d.ts +1 -0
  11. package/dist/{Support → Debug}/index.js +0 -1
  12. package/dist/Elements/Elements.d.ts +11 -9
  13. package/dist/Elements/Elements.js +20 -34
  14. package/dist/Elements/Elements.types.d.ts +1 -0
  15. package/dist/Messages/CustomMessages.d.ts +44 -0
  16. package/dist/Messages/CustomMessages.js +156 -0
  17. package/dist/Messages/CustomMessages.types.d.ts +25 -0
  18. package/dist/Messages/CustomMessages.types.js +2 -0
  19. package/dist/Messages/Messages.d.ts +42 -13
  20. package/dist/Messages/Messages.js +400 -177
  21. package/dist/Messages/index.d.ts +2 -0
  22. package/dist/Messages/index.js +2 -0
  23. package/dist/Public/Config.types.d.ts +2 -2
  24. package/dist/Public/Config.types.js +2 -2
  25. package/dist/Public/Rules.types.d.ts +5 -4
  26. package/dist/Public/Rules.types.js +5 -6
  27. package/dist/Public/Settings.types.d.ts +3 -2
  28. package/dist/Public/Settings.types.js +4 -3
  29. package/dist/Public/index.d.ts +1 -0
  30. package/dist/Rules/Dependencies.d.ts +59 -0
  31. package/dist/Rules/Dependencies.js +439 -0
  32. package/dist/Rules/EntryPoint.js +44 -94
  33. package/dist/Rules/External.js +93 -68
  34. package/dist/Rules/NoIgnored.js +4 -4
  35. package/dist/Rules/NoPrivate.js +18 -5
  36. package/dist/Rules/NoUnknown.js +5 -5
  37. package/dist/Rules/NoUnknownFiles.js +4 -3
  38. package/dist/Rules/Support/DependencyRule.d.ts +9 -1
  39. package/dist/Rules/Support/DependencyRule.js +15 -15
  40. package/dist/Rules/Support/DependencyRule.types.d.ts +1 -1
  41. package/dist/Rules/Support/Helpers.d.ts +6 -2
  42. package/dist/Rules/Support/Helpers.js +7 -31
  43. package/dist/Settings/Helpers.d.ts +83 -1
  44. package/dist/Settings/Helpers.js +197 -7
  45. package/dist/Settings/Settings.d.ts +19 -2
  46. package/dist/Settings/Settings.js +20 -10
  47. package/dist/Settings/Validations.d.ts +11958 -45
  48. package/dist/Settings/Validations.js +797 -184
  49. package/dist/Settings/index.d.ts +0 -1
  50. package/dist/Settings/index.js +0 -1
  51. package/dist/{Settings → Shared}/Settings.types.d.ts +143 -37
  52. package/dist/{Settings → Shared}/Settings.types.js +32 -6
  53. package/dist/{Support/Common.d.ts → Shared/TypeHelpers.d.ts} +18 -0
  54. package/dist/{Support/Common.js → Shared/TypeHelpers.js} +28 -1
  55. package/dist/Shared/index.d.ts +2 -0
  56. package/dist/Shared/index.js +18 -0
  57. package/dist/index.d.ts +1 -1
  58. package/dist/index.js +12 -10
  59. package/package.json +9 -8
  60. package/dist/Rules/ElementTypes.d.ts +0 -25
  61. package/dist/Rules/ElementTypes.js +0 -279
  62. package/dist/Support/Debug.d.ts +0 -5
  63. package/dist/Support/Debug.js +0 -54
  64. 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 element types 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 more
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: "controllers", pattern: "controllers/*" },
67
- { type: "models", pattern: "models/*" },
68
- { type: "views", pattern: "views/*" }
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/element-types": [2, {
80
+ "boundaries/dependencies": [2, {
81
81
  default: "disallow",
82
82
  rules: [
83
- { from: "controllers", allow: ["models", "views"] },
84
- { from: "views", allow: ["models"] },
85
- { from: "models", disallow: ["*"] }
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
  }
@@ -1,7 +1,10 @@
1
1
  import type { Linter } from "eslint";
2
- import type { PluginBoundaries, Config } from "../Settings";
3
- import { PLUGIN_NAME } from "../Settings";
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/element-types": ["error", { default: "disallow" }],
34
+ * "boundaries/dependencies": ["error", { default: "disallow" }],
32
35
  * }
33
36
  * });
34
37
  *
@@ -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([Settings_1.PLUGIN_NAME, pluginName]);
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 "${Settings_1.PLUGIN_NAME}/", or with the provided plugin name "${pluginName}/".`);
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 "${Settings_1.PLUGIN_NAME}/", or with the provided plugin name "${pluginName}/".`);
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 === Settings_1.PLUGIN_NAME) {
48
- const suffix = key.slice(Settings_1.PLUGIN_NAME.length + 1);
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/element-types": ["error", { default: "disallow" }],
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 = Settings_1.PLUGIN_NAME) {
98
+ function createConfig(config, name = Shared_1.PLUGIN_NAME) {
88
99
  const pluginsRegistration = {
89
100
  [name]: index_1.default,
90
101
  };
@@ -1,4 +1,4 @@
1
- import type { Config } from "../Settings";
1
+ import type { Config } from "../Shared";
2
2
  /**
3
3
  * Recommended configuration for eslint-plugin-boundaries.
4
4
  *
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const Settings_1 = require("../Settings");
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, } = Settings_1.SETTINGS;
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,
@@ -1,4 +1,4 @@
1
- import type { Config } from "../Settings";
1
+ import type { Config } from "../Shared";
2
2
  /**
3
3
  * Strict configuration for eslint-plugin-boundaries.
4
4
  *
@@ -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 Settings_1 = require("../Settings");
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 } = Settings_1.SETTINGS;
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 "../Settings";
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 context The ESLint rule context
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 param0 The dependency node info
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 Support_1 = require("../Support");
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 context The ESLint rule context
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 path = projectPath(fileName, settings.rootPath);
79
- const result = matcher.describeElement(path);
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 param0 The dependency node info
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: projectPath(fileName, settings.rootPath),
100
- to: projectPath((0, resolve_1.default)(source, context), settings.rootPath),
85
+ from: fileName,
86
+ to: resolvedPath || undefined,
101
87
  source,
102
- kind: kind || "value", // TODO: Change by runtime in a backwards compatible way
88
+ kind: kind || "value",
103
89
  nodeKind,
104
90
  specifiers: getSpecifiers(node),
105
91
  });
106
- (0, Support_1.debugDescription)(description);
92
+ (0, Debug_1.debugDescription)(description, settings, matcher);
107
93
  return description;
108
94
  }
@@ -1,5 +1,6 @@
1
1
  import type { Rule } from "eslint";
2
2
  import type { Literal } from "estree";
3
+ /** Represents an ESLint literal node with a parent node */
3
4
  export type EslintLiteralNode = Literal & {
4
5
  parent: Rule.Node;
5
6
  };