@boundaries/eslint-plugin 5.4.0 → 6.0.0-beta.2
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 +4 -8
- 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 +9 -7
- package/dist/Elements/Elements.js +12 -7
- 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 -6
- 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 -11
- package/dist/Settings/Validations.d.ts +11958 -43
- package/dist/Settings/Validations.js +783 -157
- package/dist/Settings/index.d.ts +0 -1
- package/dist/Settings/index.js +0 -1
- package/dist/{Settings → Shared}/Settings.types.d.ts +137 -37
- package/dist/{Settings → Shared}/Settings.types.js +30 -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 +16 -14
- 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
|
@@ -1,204 +1,427 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.
|
|
5
|
-
exports.
|
|
6
|
-
exports.
|
|
7
|
-
exports.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
function replaceObjectValuesInTemplates(strings, object, namespace) {
|
|
20
|
-
const finalResult = (0, Support_1.isArray)(strings) ? [...strings] : strings;
|
|
21
|
-
return Object.keys(object).reduce((result, objectKey) => {
|
|
22
|
-
// If template is an array, replace key by value in all patterns
|
|
23
|
-
if ((0, Support_1.isArray)(result)) {
|
|
24
|
-
return result.map((resultEntry) => {
|
|
25
|
-
return replaceObjectValueInTemplate(resultEntry, objectKey, object[objectKey], namespace);
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
return replaceObjectValueInTemplate(result, objectKey, object[objectKey], namespace);
|
|
29
|
-
}, finalResult);
|
|
3
|
+
exports.elementDescriptionMessage = elementDescriptionMessage;
|
|
4
|
+
exports.elementDescriptionMessageFromSelector = elementDescriptionMessageFromSelector;
|
|
5
|
+
exports.dependencyDescriptionMessage = dependencyDescriptionMessage;
|
|
6
|
+
exports.dependencyDescriptionMessageFromSelector = dependencyDescriptionMessageFromSelector;
|
|
7
|
+
exports.dependenciesRuleDefaultErrorMessage = dependenciesRuleDefaultErrorMessage;
|
|
8
|
+
const Shared_1 = require("../Shared");
|
|
9
|
+
const MESSAGE_ERROR = `Not able to create a message for this violation. Please report this at: ${Shared_1.PLUGIN_ISSUES_URL}`;
|
|
10
|
+
const NO_RULE_MESSAGE = "There is no rule allowing dependencies";
|
|
11
|
+
/**
|
|
12
|
+
* Wraps a value in double quotes.
|
|
13
|
+
* @param value - Value to quote.
|
|
14
|
+
* @returns Quoted string.
|
|
15
|
+
*/
|
|
16
|
+
function quote(value) {
|
|
17
|
+
return `"${String(value)}"`;
|
|
30
18
|
}
|
|
31
|
-
|
|
32
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Joins message parts with commas and a final "and".
|
|
21
|
+
* @param parts - List of message parts to join.
|
|
22
|
+
* @returns Joined message string with proper comma and "and" placement.
|
|
23
|
+
*/
|
|
24
|
+
function joinWithCommasAndAnd(parts) {
|
|
25
|
+
/* istanbul ignore next -- Defensive: callers always guard against empty array */
|
|
26
|
+
if (parts.length === 0) {
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
if (parts.length === 1) {
|
|
30
|
+
return parts[0];
|
|
31
|
+
}
|
|
32
|
+
if (parts.length === 2) {
|
|
33
|
+
return `${parts[0]} and ${parts[1]}`;
|
|
34
|
+
}
|
|
35
|
+
return `${parts.slice(0, -1).join(", ")} and ${parts.at(-1)}`;
|
|
33
36
|
}
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Converts a primitive or array value to a quoted string representation.
|
|
39
|
+
* @param value - Value to format, which can be a primitive or an array of primitives.
|
|
40
|
+
* @returns Quoted string representation of the value, with arrays joined by commas.
|
|
41
|
+
*/
|
|
42
|
+
function formatPropertyValue(value) {
|
|
43
|
+
if ((0, Shared_1.isArray)(value)) {
|
|
44
|
+
return value.map((entry) => quote(entry)).join(", ");
|
|
45
|
+
}
|
|
46
|
+
return quote(value);
|
|
36
47
|
}
|
|
37
|
-
|
|
38
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Capitalizes the first character of a message.
|
|
50
|
+
* @param message - Message to capitalize.
|
|
51
|
+
* @returns Message with the first character capitalized.
|
|
52
|
+
*/
|
|
53
|
+
function capitalizeFirstLetter(message) {
|
|
54
|
+
/* istanbul ignore next -- Defensive: capitalizeFirstLetter is always called with non-empty message strings */
|
|
55
|
+
if (!message.length) {
|
|
56
|
+
return message;
|
|
57
|
+
}
|
|
58
|
+
return `${message.charAt(0).toUpperCase()}${message.slice(1)}`;
|
|
39
59
|
}
|
|
40
|
-
|
|
41
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Builds message fragments from a selector-driven list of properties.
|
|
62
|
+
* @param elementDescription - Element to describe.
|
|
63
|
+
* @param properties - List of element properties to include in the description.
|
|
64
|
+
* @param options - Additional options for handling specific properties like "captured".
|
|
65
|
+
* @param options.capturedKeys - If "captured" is included in properties, specifies which captured keys to include in the description.
|
|
66
|
+
* @returns List of formatted message fragments describing the element based on the selected properties.
|
|
67
|
+
*/
|
|
68
|
+
function buildElementPropertyFragments(elementDescription, properties, options) {
|
|
69
|
+
const includeNullValues = options.includeNullValues;
|
|
70
|
+
const fragments = [];
|
|
71
|
+
for (const propertyName of properties) {
|
|
72
|
+
if (propertyName === "parent") {
|
|
73
|
+
const parentFragment = buildParentFragment(elementDescription, options, includeNullValues);
|
|
74
|
+
if (parentFragment) {
|
|
75
|
+
fragments.push(parentFragment);
|
|
76
|
+
}
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const value = getElementPropertyValue(elementDescription, propertyName);
|
|
80
|
+
if (shouldSkipFragmentValue(value, includeNullValues)) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (propertyName === "captured") {
|
|
84
|
+
fragments.push(...buildCapturedFragments(value, options?.capturedKeys, includeNullValues));
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
fragments.push(formatPropertyFragment(propertyName, value));
|
|
88
|
+
}
|
|
89
|
+
return fragments;
|
|
42
90
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Builds a message fragment for the first parent element when requested.
|
|
93
|
+
* @param elementDescription - Element that may include parent information.
|
|
94
|
+
* @param options - Selector-driven options for parent and captured properties.
|
|
95
|
+
* @param includeNullValues - Whether null parent values should be rendered.
|
|
96
|
+
* @returns Parent fragment string, or null when no parent information should be emitted.
|
|
97
|
+
*/
|
|
98
|
+
function buildParentFragment(elementDescription, options, includeNullValues) {
|
|
99
|
+
const firstParent = elementDescription.parents?.[0];
|
|
100
|
+
if (!firstParent) {
|
|
101
|
+
return includeNullValues ? `parent ${quote(null)}` : null;
|
|
46
102
|
}
|
|
47
|
-
|
|
48
|
-
|
|
103
|
+
const parentProperties = options?.parentProperties ?? [];
|
|
104
|
+
const parentFragments = buildElementPropertyFragments(firstParent, parentProperties, {
|
|
105
|
+
capturedKeys: options?.parentCapturedKeys,
|
|
106
|
+
includeNullValues,
|
|
107
|
+
});
|
|
108
|
+
if (!parentFragments.length) {
|
|
109
|
+
return null;
|
|
49
110
|
}
|
|
50
|
-
return
|
|
111
|
+
return `parent ${joinWithCommasAndAnd(parentFragments)}`;
|
|
51
112
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Reads an element property value using a dynamic property name.
|
|
115
|
+
* @param elementDescription - Element metadata source object.
|
|
116
|
+
* @param propertyName - Property name to read.
|
|
117
|
+
* @returns Raw value for the requested property.
|
|
118
|
+
*/
|
|
119
|
+
function getElementPropertyValue(elementDescription, propertyName) {
|
|
120
|
+
return elementDescription[propertyName];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Determines whether a property value should be skipped when building fragments.
|
|
124
|
+
* @param value - Property value to evaluate.
|
|
125
|
+
* @param includeNullValues - Whether null values are allowed in output.
|
|
126
|
+
* @returns True when the value should be ignored for message output.
|
|
127
|
+
*/
|
|
128
|
+
function shouldSkipFragmentValue(value, includeNullValues) {
|
|
129
|
+
return (0, Shared_1.isUndefined)(value) || ((0, Shared_1.isNull)(value) && !includeNullValues);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Builds captured-property message fragments from selected captured keys.
|
|
133
|
+
* @param value - Captured value container to inspect.
|
|
134
|
+
* @param capturedKeys - Captured keys selected by the selector.
|
|
135
|
+
* @param includeNullValues - Whether empty captured values should render as null.
|
|
136
|
+
* @returns List of captured fragments ready to be joined in a message.
|
|
137
|
+
*/
|
|
138
|
+
function buildCapturedFragments(value, capturedKeys, includeNullValues) {
|
|
139
|
+
if (!(0, Shared_1.isObject)(value) || Object.keys(value).length === 0) {
|
|
140
|
+
return includeNullValues ? [`captured ${quote(null)}`] : [];
|
|
61
141
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
142
|
+
const fragments = [];
|
|
143
|
+
const keys = capturedKeys ?? Object.keys(value);
|
|
144
|
+
for (const capturedKey of keys) {
|
|
145
|
+
const capturedValue = value[capturedKey];
|
|
146
|
+
if ((0, Shared_1.isUndefined)(capturedValue)) {
|
|
147
|
+
continue;
|
|
65
148
|
}
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
149
|
+
fragments.push(`${capturedKey} ${formatPropertyValue(capturedValue)}`);
|
|
150
|
+
}
|
|
151
|
+
return fragments;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Formats a generic property fragment using key and value.
|
|
155
|
+
* @param propertyName - Property name label.
|
|
156
|
+
* @param value - Property value to serialize.
|
|
157
|
+
* @returns Formatted property fragment.
|
|
158
|
+
*/
|
|
159
|
+
function formatPropertyFragment(propertyName, value) {
|
|
160
|
+
return `${propertyName} ${formatPropertyValue(value)}`;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Determines whether a dependency property value should be rendered.
|
|
164
|
+
* @param value - Property value to evaluate.
|
|
165
|
+
* @param includeNullValues - Whether null values should be included in the output.
|
|
166
|
+
* @returns True when the value should be rendered.
|
|
167
|
+
*/
|
|
168
|
+
function shouldRenderDependencyValue(value, includeNullValues) {
|
|
169
|
+
return !(0, Shared_1.isUndefined)(value) && (!(0, Shared_1.isNull)(value) || includeNullValues);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Builds a relationship-side fragment when the selected side should be rendered.
|
|
173
|
+
* @param relationship - Relationship metadata value.
|
|
174
|
+
* @param relationshipKey - Relationship side to describe.
|
|
175
|
+
* @param includeNullValues - Whether null values should be included in the output.
|
|
176
|
+
* @returns Formatted relationship fragment, or null when the side should be ignored.
|
|
177
|
+
*/
|
|
178
|
+
function buildRelationshipFragment(relationship, relationshipKey, includeNullValues) {
|
|
179
|
+
const relationshipValue = relationship[relationshipKey];
|
|
180
|
+
if (!shouldRenderDependencyValue(relationshipValue, includeNullValues)) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
return `relationship ${relationshipKey} ${formatPropertyValue(relationshipValue)}`;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Builds message fragments for selected relationship sides.
|
|
187
|
+
* @param relationship - Relationship metadata value.
|
|
188
|
+
* @param relationshipKeys - Relationship sides to include.
|
|
189
|
+
* @param includeNullValues - Whether null values should be included in the output.
|
|
190
|
+
* @returns List of relationship fragments.
|
|
191
|
+
*/
|
|
192
|
+
function buildRelationshipFragments(relationship, relationshipKeys, includeNullValues) {
|
|
193
|
+
const fragments = [];
|
|
194
|
+
for (const relationshipKey of relationshipKeys ?? ["from", "to"]) {
|
|
195
|
+
const fragment = buildRelationshipFragment(relationship, relationshipKey, includeNullValues);
|
|
196
|
+
if (fragment) {
|
|
197
|
+
fragments.push(fragment);
|
|
74
198
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return [key, capturedValuesPattern?.[key]];
|
|
92
|
-
})
|
|
93
|
-
.reduce((message, propertyNameAndMatcher, index) => {
|
|
94
|
-
return `${message}${propertiesConcatenator(capturedValuesPatternKeys, index)} ${propertyNameAndMatcher[0]
|
|
95
|
-
// TODO: Support array patterns
|
|
96
|
-
} ${micromatchPatternMessage(propertyNameAndMatcher[1], elementCapturedValues)}`;
|
|
97
|
-
}, "");
|
|
98
|
-
}
|
|
99
|
-
function elementMatcherMessage(elementMatcher, elementCapturedValues) {
|
|
100
|
-
if (!elementMatcher) {
|
|
199
|
+
}
|
|
200
|
+
return fragments;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Describes elements using the selected relevant properties.
|
|
204
|
+
* @param elementDescription - Element to describe.
|
|
205
|
+
* @param properties - List of element properties to include in the description.
|
|
206
|
+
* @param options - Additional options for message formatting.
|
|
207
|
+
* @param options.singleElement - If `true`, uses singular "element" label instead of "elements of".
|
|
208
|
+
* @returns Formatted message describing the element based on the selected properties.
|
|
209
|
+
*/
|
|
210
|
+
function elementDescriptionMessage(elementDescription, properties, { singleElement = false, includeNullValues = false, } = {}) {
|
|
211
|
+
const propertyFragments = buildElementPropertyFragments(elementDescription, properties, {
|
|
212
|
+
includeNullValues,
|
|
213
|
+
});
|
|
214
|
+
if (!propertyFragments.length) {
|
|
101
215
|
return "";
|
|
102
216
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
categoryMessage(selector[0].category));
|
|
115
|
-
}
|
|
116
|
-
if (selector[0].captured) {
|
|
117
|
-
toAdd.push(capturedValuesMatcherMessage(selector[0].captured, elementCapturedValues));
|
|
118
|
-
}
|
|
119
|
-
parts.push(...toAdd);
|
|
120
|
-
return parts.map((part) => part.trim()).join(" ");
|
|
217
|
+
const elementLabel = singleElement ? "element" : "elements";
|
|
218
|
+
return `${elementLabel} of ${joinWithCommasAndAnd(propertyFragments)}`;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Describes elements using selector-driven relevant properties and captured keys.
|
|
222
|
+
* @param elementDescription - Element to describe.
|
|
223
|
+
* @param selectorData - Selector data that determines which properties and captured keys to include in the description.
|
|
224
|
+
*/
|
|
225
|
+
function elementDescriptionMessageFromSelector(elementDescription, selectorData) {
|
|
226
|
+
if (!selectorData) {
|
|
227
|
+
return null;
|
|
121
228
|
}
|
|
122
|
-
|
|
123
|
-
if (
|
|
124
|
-
return
|
|
229
|
+
const properties = Object.keys(selectorData);
|
|
230
|
+
if (!properties.length) {
|
|
231
|
+
return null;
|
|
125
232
|
}
|
|
126
|
-
|
|
127
|
-
|
|
233
|
+
const capturedKeys = (0, Shared_1.isObject)(selectorData.captured)
|
|
234
|
+
? Object.keys(selectorData.captured)
|
|
235
|
+
: undefined;
|
|
236
|
+
const parentProperties = (0, Shared_1.isObject)(selectorData.parent)
|
|
237
|
+
? Object.keys(selectorData.parent)
|
|
238
|
+
: undefined;
|
|
239
|
+
const parentCapturedKeys = (0, Shared_1.isObject)(selectorData.parent) && (0, Shared_1.isObject)(selectorData.parent.captured)
|
|
240
|
+
? Object.keys(selectorData.parent.captured)
|
|
241
|
+
: undefined;
|
|
242
|
+
const propertyFragments = buildElementPropertyFragments(elementDescription, properties, {
|
|
243
|
+
capturedKeys,
|
|
244
|
+
parentProperties,
|
|
245
|
+
parentCapturedKeys,
|
|
246
|
+
includeNullValues: true,
|
|
247
|
+
});
|
|
248
|
+
if (!propertyFragments.length) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
return `elements of ${joinWithCommasAndAnd(propertyFragments)}`;
|
|
128
252
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
253
|
+
/**
|
|
254
|
+
* Builds message fragments for dependency metadata from selected properties.
|
|
255
|
+
* @param dependencyMetadata - Dependency metadata to describe.
|
|
256
|
+
* @param properties - List of dependency metadata properties to include in the description.
|
|
257
|
+
* @param options - Additional options for handling specific properties like "relationship".
|
|
258
|
+
* @param options.relationshipKeys - If "relationship" is included in properties, specifies which relationship sides ("from", "to") to include in the description.
|
|
259
|
+
* @returns List of formatted message fragments describing the dependency metadata.
|
|
260
|
+
*/
|
|
261
|
+
function buildDependencyPropertyFragments(dependencyMetadata, properties, options) {
|
|
262
|
+
const fragments = [];
|
|
263
|
+
const includeNullValues = options?.includeNullValues ?? false;
|
|
264
|
+
for (const propertyName of properties) {
|
|
265
|
+
const value = dependencyMetadata[propertyName];
|
|
266
|
+
if (!shouldRenderDependencyValue(value, includeNullValues)) {
|
|
267
|
+
continue;
|
|
133
268
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}, "");
|
|
140
|
-
}
|
|
141
|
-
return elementMatcherMessage(elementPatterns, elementCapturedValues);
|
|
142
|
-
}
|
|
143
|
-
function elementPropertiesToReplaceInTemplate(element, importKind) {
|
|
144
|
-
if ((0, elements_1.isElementDescription)(element)) {
|
|
145
|
-
return {
|
|
146
|
-
...element.captured,
|
|
147
|
-
type: element.type || "",
|
|
148
|
-
internalPath: element.internalPath || "",
|
|
149
|
-
source: element.source || "",
|
|
150
|
-
importKind: importKind || "",
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
return {
|
|
154
|
-
...element.captured,
|
|
155
|
-
type: element.type || "",
|
|
156
|
-
internalPath: "",
|
|
157
|
-
source: "",
|
|
158
|
-
importKind: importKind || "",
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
function customErrorMessage(message, dependency, report = {}) {
|
|
162
|
-
let replacedMessage = replaceObjectValuesInTemplate(replaceObjectValuesInTemplate(message, elementPropertiesToReplaceInTemplate(dependency.from, dependency.dependency.kind), "file"), elementPropertiesToReplaceInTemplate(dependency.to, dependency.dependency.kind), "dependency");
|
|
163
|
-
replacedMessage = replaceObjectValuesInTemplate(replaceObjectValuesInTemplate(replacedMessage, elementPropertiesToReplaceInTemplate(dependency.from, dependency.dependency.kind), "from"), elementPropertiesToReplaceInTemplate(dependency.to, dependency.dependency.kind), "target");
|
|
164
|
-
if (dependency.from.parents?.[0]) {
|
|
165
|
-
replacedMessage = replaceObjectValuesInTemplate(replacedMessage, elementPropertiesToReplaceInTemplate(dependency.from.parents?.[0], dependency.dependency.kind), "file.parent");
|
|
166
|
-
replacedMessage = replaceObjectValuesInTemplate(replacedMessage, elementPropertiesToReplaceInTemplate(dependency.from.parents?.[0], dependency.dependency.kind), "from.parent");
|
|
167
|
-
}
|
|
168
|
-
if (dependency.to.parents?.[0]) {
|
|
169
|
-
replacedMessage = replaceObjectValuesInTemplate(replacedMessage, elementPropertiesToReplaceInTemplate(dependency.to.parents?.[0], dependency.dependency.kind), "dependency.parent");
|
|
170
|
-
replacedMessage = replaceObjectValuesInTemplate(replacedMessage, elementPropertiesToReplaceInTemplate(dependency.to.parents?.[0], dependency.dependency.kind), "target.parent");
|
|
171
|
-
}
|
|
172
|
-
return replaceObjectValuesInTemplate(replacedMessage, report, "report");
|
|
173
|
-
}
|
|
174
|
-
function elementCapturedValuesMessage(capturedValues) {
|
|
175
|
-
if (!capturedValues) {
|
|
176
|
-
return "";
|
|
269
|
+
if (propertyName === "relationship" && (0, Shared_1.isObject)(value)) {
|
|
270
|
+
fragments.push(...buildRelationshipFragments(value, options?.relationshipKeys, includeNullValues));
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
fragments.push(formatPropertyFragment(propertyName, value));
|
|
177
274
|
}
|
|
178
|
-
|
|
179
|
-
return capturedValuesKeys
|
|
180
|
-
.map((key) => {
|
|
181
|
-
return [key, capturedValues[key]];
|
|
182
|
-
})
|
|
183
|
-
.reduce((message, propertyNameAndValue, index) => {
|
|
184
|
-
return `${message}${propertiesConcatenator(capturedValuesKeys, index)} ${propertyNameAndValue[0]} ${quote(propertyNameAndValue[1])}`;
|
|
185
|
-
}, "");
|
|
275
|
+
return fragments;
|
|
186
276
|
}
|
|
187
|
-
|
|
188
|
-
|
|
277
|
+
/**
|
|
278
|
+
* Describes dependency metadata using selected relevant properties.
|
|
279
|
+
* @param dependencyMetadata - Dependency metadata to describe.
|
|
280
|
+
* @param properties - List of dependency metadata properties to include in the description.
|
|
281
|
+
* @returns Formatted message describing the dependency metadata based on the selected properties.
|
|
282
|
+
*/
|
|
283
|
+
function dependencyDescriptionMessage(dependencyMetadata, properties, options) {
|
|
284
|
+
const propertyFragments = buildDependencyPropertyFragments(dependencyMetadata, properties, {
|
|
285
|
+
includeNullValues: options?.includeNullValues,
|
|
286
|
+
});
|
|
287
|
+
if (!propertyFragments.length) {
|
|
288
|
+
return "";
|
|
289
|
+
}
|
|
290
|
+
return joinWithCommasAndAnd(propertyFragments);
|
|
189
291
|
}
|
|
190
|
-
|
|
191
|
-
|
|
292
|
+
/**
|
|
293
|
+
* Describes dependency metadata using selector-driven relevant properties.
|
|
294
|
+
* @param dependencyMetadata - Dependency metadata to describe.
|
|
295
|
+
* @param selectorData - Selector data that determines which properties and captured keys to include in the description.
|
|
296
|
+
* @returns Formatted message describing the dependency metadata based on the selected properties.
|
|
297
|
+
*/
|
|
298
|
+
function dependencyDescriptionMessageFromSelector(dependencyMetadata, selectorData) {
|
|
299
|
+
if (!selectorData) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
const properties = Object.keys(selectorData);
|
|
303
|
+
if (!properties.length) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
const relationshipKeys = (0, Shared_1.isObject)(selectorData.relationship)
|
|
307
|
+
? Object.keys(selectorData.relationship)
|
|
308
|
+
: undefined;
|
|
309
|
+
const propertyFragments = buildDependencyPropertyFragments(dependencyMetadata, properties, {
|
|
310
|
+
relationshipKeys,
|
|
311
|
+
includeNullValues: true,
|
|
312
|
+
});
|
|
313
|
+
if (!propertyFragments.length) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
return joinWithCommasAndAnd(propertyFragments);
|
|
192
317
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
318
|
+
/**
|
|
319
|
+
* Resolves the fallback no-rules message from available description fragments.
|
|
320
|
+
* @param fromDescription - Description of the source element.
|
|
321
|
+
* @param toDescription - Description of the target element.
|
|
322
|
+
* @param dependencyDescription - Description of dependency metadata.
|
|
323
|
+
* @param originDescription - Optional origin description when target details are not available.
|
|
324
|
+
* @returns The most specific no-rules message that can be composed.
|
|
325
|
+
*/
|
|
326
|
+
function resolveNoRulesMatchedMessage(fromDescription, toDescription, dependencyDescription, originDescription) {
|
|
327
|
+
if (fromDescription && toDescription) {
|
|
328
|
+
return `${NO_RULE_MESSAGE} from ${fromDescription} to ${toDescription}`;
|
|
329
|
+
}
|
|
330
|
+
if (fromDescription && dependencyDescription && originDescription) {
|
|
331
|
+
return `${NO_RULE_MESSAGE} from ${fromDescription} to ${originDescription} with ${dependencyDescription}`;
|
|
332
|
+
}
|
|
333
|
+
if (fromDescription && dependencyDescription) {
|
|
334
|
+
return `${NO_RULE_MESSAGE} from ${fromDescription} with ${dependencyDescription}`;
|
|
335
|
+
}
|
|
336
|
+
if (toDescription && dependencyDescription) {
|
|
337
|
+
return `${NO_RULE_MESSAGE} to ${toDescription} with ${dependencyDescription}`;
|
|
196
338
|
}
|
|
197
|
-
|
|
339
|
+
if (dependencyDescription && originDescription) {
|
|
340
|
+
return `${NO_RULE_MESSAGE} to ${originDescription} with ${dependencyDescription}`;
|
|
341
|
+
}
|
|
342
|
+
if (fromDescription) {
|
|
343
|
+
return `${NO_RULE_MESSAGE} from ${fromDescription}`;
|
|
344
|
+
}
|
|
345
|
+
if (toDescription) {
|
|
346
|
+
return `${NO_RULE_MESSAGE} to ${toDescription}`;
|
|
347
|
+
}
|
|
348
|
+
if (dependencyDescription) {
|
|
349
|
+
return `${NO_RULE_MESSAGE} with ${dependencyDescription}`;
|
|
350
|
+
}
|
|
351
|
+
if (originDescription) {
|
|
352
|
+
return `${NO_RULE_MESSAGE} to ${originDescription}`;
|
|
353
|
+
}
|
|
354
|
+
return MESSAGE_ERROR;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Builds the fallback no-rules message when no rule matches a dependency.
|
|
358
|
+
* @param dependency - Dependency description used to derive message details.
|
|
359
|
+
* @returns Human-readable fallback message for no-rules scenarios.
|
|
360
|
+
*/
|
|
361
|
+
function elementTypesNoRulesMatchedMessage(dependency) {
|
|
362
|
+
const fromDescription = elementDescriptionMessage(dependency.from, [
|
|
363
|
+
"type",
|
|
364
|
+
"category",
|
|
365
|
+
"captured",
|
|
366
|
+
]);
|
|
367
|
+
const toDescription = elementDescriptionMessage(dependency.to, [
|
|
368
|
+
"type",
|
|
369
|
+
"category",
|
|
370
|
+
"captured",
|
|
371
|
+
]);
|
|
372
|
+
const propertyToShowInDependency = dependency.dependency.module
|
|
373
|
+
? "module"
|
|
374
|
+
: "source";
|
|
375
|
+
const dependencyDescription = dependencyDescriptionMessage(dependency.dependency, [propertyToShowInDependency]);
|
|
376
|
+
const originDescription = toDescription.length
|
|
377
|
+
? null
|
|
378
|
+
: elementDescriptionMessage(dependency.to, ["origin"]);
|
|
379
|
+
return resolveNoRulesMatchedMessage(fromDescription, toDescription, dependencyDescription, originDescription);
|
|
198
380
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
381
|
+
/**
|
|
382
|
+
* Builds the default message for dependencies rule violations from the matching selector data.
|
|
383
|
+
* @param matchResult - Result of matching the dependency against the rule's selector, containing the relevant selector data for the from/to elements and the dependency metadata.
|
|
384
|
+
* @param ruleIndex - Index of the matching rule.
|
|
385
|
+
* @param dependency - Described dependency that triggered the violation, used to extract element and dependency metadata for message construction.
|
|
386
|
+
* @returns Formatted error message describing the violation based on the matching selector data.
|
|
387
|
+
*/
|
|
388
|
+
function dependenciesRuleDefaultErrorMessage(matchResult, ruleIndex, dependency) {
|
|
389
|
+
if ((0, Shared_1.isNull)(ruleIndex)) {
|
|
390
|
+
return elementTypesNoRulesMatchedMessage(dependency);
|
|
391
|
+
}
|
|
392
|
+
const fromProperties = Object.keys((matchResult?.from ?? {}));
|
|
393
|
+
const toProperties = Object.keys((matchResult?.to ?? {}));
|
|
394
|
+
const dependencyProperties = Object.keys((matchResult?.dependency ?? {}));
|
|
395
|
+
const fromPart = fromProperties.length
|
|
396
|
+
? elementDescriptionMessageFromSelector(dependency.from, matchResult.from)
|
|
397
|
+
: null;
|
|
398
|
+
const toPart = toProperties.length
|
|
399
|
+
? elementDescriptionMessageFromSelector(dependency.to, matchResult.to)
|
|
400
|
+
: null;
|
|
401
|
+
const dependencyPart = dependencyProperties.length
|
|
402
|
+
? dependencyDescriptionMessageFromSelector(dependency.dependency, matchResult.dependency)
|
|
403
|
+
: null;
|
|
404
|
+
let message = MESSAGE_ERROR;
|
|
405
|
+
if (dependencyPart && toPart && fromPart) {
|
|
406
|
+
message = `Dependencies with ${dependencyPart} to ${toPart} are not allowed in ${fromPart}`;
|
|
407
|
+
}
|
|
408
|
+
else if (dependencyPart && toPart) {
|
|
409
|
+
message = `Dependencies with ${dependencyPart} to ${toPart} are not allowed`;
|
|
410
|
+
}
|
|
411
|
+
else if (dependencyPart && fromPart) {
|
|
412
|
+
message = `Dependencies with ${dependencyPart} are not allowed in ${fromPart}`;
|
|
413
|
+
}
|
|
414
|
+
else if (toPart && fromPart) {
|
|
415
|
+
message = `Dependencies to ${toPart} are not allowed in ${fromPart}`;
|
|
416
|
+
}
|
|
417
|
+
else if (toPart) {
|
|
418
|
+
message = `Dependencies to ${toPart} are not allowed`;
|
|
419
|
+
}
|
|
420
|
+
else if (fromPart) {
|
|
421
|
+
message = `Dependencies are not allowed in ${fromPart}`;
|
|
422
|
+
}
|
|
423
|
+
else if (dependencyPart) {
|
|
424
|
+
message = `Dependencies with ${dependencyPart} are not allowed`;
|
|
202
425
|
}
|
|
203
|
-
return
|
|
426
|
+
return `${capitalizeFirstLetter(message)}. Denied by rule at index ${ruleIndex}`;
|
|
204
427
|
}
|
package/dist/Messages/index.d.ts
CHANGED
package/dist/Messages/index.js
CHANGED
|
@@ -14,4 +14,6 @@ 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("./CustomMessages"), exports);
|
|
17
18
|
__exportStar(require("./Messages"), exports);
|
|
19
|
+
__exportStar(require("./CustomMessages.types"), exports);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { Rules, Config, PluginBoundaries } from "../
|
|
2
|
-
export { PLUGIN_NAME } from "../
|
|
1
|
+
export type { Rules, Config, PluginBoundaries } from "../Shared";
|
|
2
|
+
export { PLUGIN_NAME } from "../Shared";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PLUGIN_NAME = void 0;
|
|
4
|
-
var
|
|
5
|
-
Object.defineProperty(exports, "PLUGIN_NAME", { enumerable: true, get: function () { return
|
|
4
|
+
var Shared_1 = require("../Shared");
|
|
5
|
+
Object.defineProperty(exports, "PLUGIN_NAME", { enumerable: true, get: function () { return Shared_1.PLUGIN_NAME; } });
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { DependencyKind } from "@boundaries/elements";
|
|
2
|
-
export type { DependencyKind, CapturedValuesSelector, ElementSelector, ElementSelectors, ElementsSelector,
|
|
3
|
-
export { isElementSelector, isElementsSelector
|
|
4
|
-
export type { RulePolicy, RuleBaseOptions, ElementTypesRule, ElementTypesRuleOptions, EntryPointRule, EntryPointRuleOptions, ExternalRule, ExternalRuleOptions, NoPrivateOptions, RuleShortName, RuleName, } from "../
|
|
5
|
-
export { RULE_POLICIES_MAP,
|
|
2
|
+
export type { DependencyKind, CapturedValuesSelector, ElementSelector, ElementSelectors, ElementsSelector, ElementSelectorWithOptions, FlagAsExternalOptions, } from "@boundaries/elements";
|
|
3
|
+
export { isElementSelector, isElementsSelector } from "@boundaries/elements";
|
|
4
|
+
export type { RulePolicy, RuleBaseOptions, ElementTypesRule, ElementTypesRuleOptions, DependenciesRule, DependenciesRuleOptions, EntryPointRule, EntryPointRuleOptions, ExternalRule, ExternalRuleOptions, NoPrivateOptions, RuleShortName, RuleName, } from "../Shared";
|
|
5
|
+
export { RULE_POLICIES_MAP, RULE_SHORT_NAMES_MAP, RULE_NAMES_MAP, } from "../Shared";
|
|
6
|
+
export { isRulePolicy, isRuleShortName, isRuleName } from "../Settings";
|
|
6
7
|
/**
|
|
7
8
|
* Kind of import that the rule applies to (e.g., "type", "value")
|
|
8
9
|
* @deprecated Use DependencyKind instead
|