@boundaries/eslint-plugin 5.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +798 -0
- package/dist/Config/Config.d.ts +40 -0
- package/dist/Config/Config.js +119 -0
- package/dist/Config/Recommended.d.ts +10 -0
- package/dist/Config/Recommended.js +36 -0
- package/dist/Config/Strict.d.ts +8 -0
- package/dist/Config/Strict.js +26 -0
- package/dist/Elements/Elements.d.ts +43 -0
- package/dist/Elements/Elements.js +107 -0
- package/dist/Elements/Elements.types.d.ts +5 -0
- package/dist/Elements/Elements.types.js +2 -0
- package/dist/Elements/index.d.ts +2 -0
- package/dist/Elements/index.js +18 -0
- package/dist/Messages/Messages.d.ts +14 -0
- package/dist/Messages/Messages.js +204 -0
- package/dist/Messages/index.d.ts +1 -0
- package/dist/Messages/index.js +17 -0
- package/dist/Public/Config.types.d.ts +2 -0
- package/dist/Public/Config.types.js +5 -0
- package/dist/Public/Rules.types.d.ts +10 -0
- package/dist/Public/Rules.types.js +15 -0
- package/dist/Public/Settings.types.d.ts +13 -0
- package/dist/Public/Settings.types.js +21 -0
- package/dist/Public/index.d.ts +3 -0
- package/dist/Public/index.js +19 -0
- package/dist/Rules/ElementTypes.d.ts +25 -0
- package/dist/Rules/ElementTypes.js +269 -0
- package/dist/Rules/EntryPoint.d.ts +2 -0
- package/dist/Rules/EntryPoint.js +122 -0
- package/dist/Rules/External.d.ts +2 -0
- package/dist/Rules/External.js +119 -0
- package/dist/Rules/NoIgnored.d.ts +2 -0
- package/dist/Rules/NoIgnored.js +19 -0
- package/dist/Rules/NoPrivate.d.ts +2 -0
- package/dist/Rules/NoPrivate.js +53 -0
- package/dist/Rules/NoUnknown.d.ts +2 -0
- package/dist/Rules/NoUnknown.js +22 -0
- package/dist/Rules/NoUnknownFiles.d.ts +3 -0
- package/dist/Rules/NoUnknownFiles.js +29 -0
- package/dist/Rules/Support/DependencyRule.d.ts +4 -0
- package/dist/Rules/Support/DependencyRule.js +49 -0
- package/dist/Rules/Support/DependencyRule.types.d.ts +17 -0
- package/dist/Rules/Support/DependencyRule.types.js +2 -0
- package/dist/Rules/Support/Helpers.d.ts +8 -0
- package/dist/Rules/Support/Helpers.js +39 -0
- package/dist/Rules/Support/index.d.ts +3 -0
- package/dist/Rules/Support/index.js +19 -0
- package/dist/Settings/Helpers.d.ts +41 -0
- package/dist/Settings/Helpers.js +72 -0
- package/dist/Settings/Settings.d.ts +6 -0
- package/dist/Settings/Settings.js +49 -0
- package/dist/Settings/Settings.types.d.ts +448 -0
- package/dist/Settings/Settings.types.js +190 -0
- package/dist/Settings/Validations.d.ts +137 -0
- package/dist/Settings/Validations.js +359 -0
- package/dist/Settings/index.d.ts +4 -0
- package/dist/Settings/index.js +20 -0
- package/dist/Support/Common.d.ts +30 -0
- package/dist/Support/Common.js +47 -0
- package/dist/Support/Debug.d.ts +5 -0
- package/dist/Support/Debug.js +54 -0
- package/dist/Support/index.d.ts +2 -0
- package/dist/Support/index.js +18 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +76 -0
- package/package.json +81 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Linter } from "eslint";
|
|
2
|
+
import type { PluginBoundaries, Config } from "../Settings";
|
|
3
|
+
import { PLUGIN_NAME } from "../Settings";
|
|
4
|
+
export * from "../Public";
|
|
5
|
+
type PluginFullConfig<PluginName extends string = typeof PLUGIN_NAME> = {
|
|
6
|
+
plugins: Record<PluginName, PluginBoundaries>;
|
|
7
|
+
files: Linter.Config["files"];
|
|
8
|
+
} & Omit<Config<PluginName>, "plugins">;
|
|
9
|
+
/**
|
|
10
|
+
* Returns an ESLint config object with the boundaries plugin registered, providing default JS and TS file patterns
|
|
11
|
+
* and enforcing valid types for settings and rules. Supports renaming the plugin. Rules can be prefixed with either
|
|
12
|
+
* the original plugin name or the provided plugin name.
|
|
13
|
+
*
|
|
14
|
+
* @param config - ESLint config object without the plugins field.
|
|
15
|
+
* @param name - The name of the plugin to register. Defaults to "boundaries".
|
|
16
|
+
* @returns {Linter.Config} The ESLint config object with the boundaries plugin registered and the provided config merged in.
|
|
17
|
+
* @throws {Error} If settings or rules are not from eslint-plugin-boundaries.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { createConfig, recommended } from "eslint-plugin-boundaries/config";
|
|
22
|
+
*
|
|
23
|
+
* const config = createConfig({
|
|
24
|
+
* settings: {
|
|
25
|
+
* ...recommended.settings,
|
|
26
|
+
* "boundaries/elements": [],
|
|
27
|
+
* "boundaries/ignore": ["ignored/*.js"],
|
|
28
|
+
* },
|
|
29
|
+
* rules: {
|
|
30
|
+
* ...recommended.rules,
|
|
31
|
+
* "boundaries/element-types": ["error", { default: "disallow" }],
|
|
32
|
+
* }
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* export default [config];
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function createConfig<PluginName extends string = typeof PLUGIN_NAME>(config: Omit<Config<PluginName> | Config, "plugins">, name?: PluginName): PluginFullConfig<PluginName>;
|
|
39
|
+
export declare const recommended: Config<"boundaries">;
|
|
40
|
+
export declare const strict: Config<"boundaries">;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.strict = exports.recommended = void 0;
|
|
21
|
+
exports.createConfig = createConfig;
|
|
22
|
+
const index_1 = __importDefault(require("../index"));
|
|
23
|
+
const Settings_1 = require("../Settings");
|
|
24
|
+
const Recommended_1 = __importDefault(require("./Recommended"));
|
|
25
|
+
const Strict_1 = __importDefault(require("./Strict"));
|
|
26
|
+
__exportStar(require("../Public"), exports);
|
|
27
|
+
function renamePluginRules(pluginName, rules) {
|
|
28
|
+
if (!rules) {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
const allowedPrefixes = new Set([Settings_1.PLUGIN_NAME, pluginName]);
|
|
32
|
+
// Return the same rules objects, but converting plugin default rule keys with provided plugin name
|
|
33
|
+
return Object.entries(rules).reduce((acc, [key, value]) => {
|
|
34
|
+
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}/".`);
|
|
36
|
+
}
|
|
37
|
+
const splittedRuleKey = key.split("/");
|
|
38
|
+
const rulePrefix = splittedRuleKey[0];
|
|
39
|
+
const ruleName = splittedRuleKey[1];
|
|
40
|
+
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}/".`);
|
|
42
|
+
}
|
|
43
|
+
if (!(0, Settings_1.isRuleShortName)(ruleName)) {
|
|
44
|
+
throw new Error(`Invalid rule name "${ruleName}". When using createConfig, all rules must belong to eslint-plugin-boundaries.`);
|
|
45
|
+
}
|
|
46
|
+
let newKey;
|
|
47
|
+
if (rulePrefix === Settings_1.PLUGIN_NAME) {
|
|
48
|
+
const suffix = key.slice(Settings_1.PLUGIN_NAME.length + 1);
|
|
49
|
+
newKey = `${pluginName}/${suffix}`;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
newKey = key;
|
|
53
|
+
}
|
|
54
|
+
acc[newKey] = value;
|
|
55
|
+
return acc;
|
|
56
|
+
}, {});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns an ESLint config object with the boundaries plugin registered, providing default JS and TS file patterns
|
|
60
|
+
* and enforcing valid types for settings and rules. Supports renaming the plugin. Rules can be prefixed with either
|
|
61
|
+
* the original plugin name or the provided plugin name.
|
|
62
|
+
*
|
|
63
|
+
* @param config - ESLint config object without the plugins field.
|
|
64
|
+
* @param name - The name of the plugin to register. Defaults to "boundaries".
|
|
65
|
+
* @returns {Linter.Config} The ESLint config object with the boundaries plugin registered and the provided config merged in.
|
|
66
|
+
* @throws {Error} If settings or rules are not from eslint-plugin-boundaries.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* import { createConfig, recommended } from "eslint-plugin-boundaries/config";
|
|
71
|
+
*
|
|
72
|
+
* const config = createConfig({
|
|
73
|
+
* settings: {
|
|
74
|
+
* ...recommended.settings,
|
|
75
|
+
* "boundaries/elements": [],
|
|
76
|
+
* "boundaries/ignore": ["ignored/*.js"],
|
|
77
|
+
* },
|
|
78
|
+
* rules: {
|
|
79
|
+
* ...recommended.rules,
|
|
80
|
+
* "boundaries/element-types": ["error", { default: "disallow" }],
|
|
81
|
+
* }
|
|
82
|
+
* });
|
|
83
|
+
*
|
|
84
|
+
* export default [config];
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
function createConfig(config, name = Settings_1.PLUGIN_NAME) {
|
|
88
|
+
const pluginsRegistration = {
|
|
89
|
+
[name]: index_1.default,
|
|
90
|
+
};
|
|
91
|
+
if (Object.hasOwn(config, "plugins")) {
|
|
92
|
+
throw new Error("The 'plugins' field is managed by createConfig and should not be provided in the config argument.");
|
|
93
|
+
}
|
|
94
|
+
if (Object.hasOwn(config, "settings")) {
|
|
95
|
+
const settings = config.settings;
|
|
96
|
+
if (settings) {
|
|
97
|
+
for (const key of Object.keys(settings)) {
|
|
98
|
+
if (!(0, Settings_1.isSettingsKey)(key)) {
|
|
99
|
+
throw new Error(`Invalid settings key "${key}". When using createConfig, all settings keys must belong to eslint-plugin-boundaries.`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
files: [
|
|
106
|
+
"**/*.js",
|
|
107
|
+
"**/*.jsx",
|
|
108
|
+
"**/*.ts",
|
|
109
|
+
"**/*.tsx",
|
|
110
|
+
"**/*.mjs",
|
|
111
|
+
"**/*.cjs",
|
|
112
|
+
],
|
|
113
|
+
...config,
|
|
114
|
+
plugins: pluginsRegistration,
|
|
115
|
+
rules: renamePluginRules(name, config.rules),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
exports.recommended = Recommended_1.default;
|
|
119
|
+
exports.strict = Strict_1.default;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Config } from "../Settings";
|
|
2
|
+
/**
|
|
3
|
+
* Recommended configuration for eslint-plugin-boundaries.
|
|
4
|
+
*
|
|
5
|
+
* It is recommended for applying the plugin to an already existing project.
|
|
6
|
+
* Rules `boundaries/no-unknown`, `boundaries/no-unknown-files` and `boundaries/no-ignored` are disabled,
|
|
7
|
+
* so it allows to have parts of the project non-compliant with defined rules, allowing to refactor the code progressively.
|
|
8
|
+
*/
|
|
9
|
+
declare const config: Config;
|
|
10
|
+
export default config;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const Settings_1 = require("../Settings");
|
|
4
|
+
const { ELEMENTS,
|
|
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;
|
|
7
|
+
// TODO In next major version: Export also files, plugin, etc.
|
|
8
|
+
/**
|
|
9
|
+
* Recommended configuration for eslint-plugin-boundaries.
|
|
10
|
+
*
|
|
11
|
+
* It is recommended for applying the plugin to an already existing project.
|
|
12
|
+
* Rules `boundaries/no-unknown`, `boundaries/no-unknown-files` and `boundaries/no-ignored` are disabled,
|
|
13
|
+
* so it allows to have parts of the project non-compliant with defined rules, allowing to refactor the code progressively.
|
|
14
|
+
*/
|
|
15
|
+
const config = {
|
|
16
|
+
rules: {
|
|
17
|
+
[RULE_ELEMENT_TYPES]: [2],
|
|
18
|
+
[RULE_ENTRY_POINT]: [2],
|
|
19
|
+
[RULE_EXTERNAL]: [2],
|
|
20
|
+
[RULE_NO_IGNORED]: 0,
|
|
21
|
+
[RULE_NO_PRIVATE]: [
|
|
22
|
+
2,
|
|
23
|
+
{
|
|
24
|
+
allowUncles: true,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
[RULE_NO_UNKNOWN_FILES]: 0,
|
|
28
|
+
[RULE_NO_UNKNOWN]: 0,
|
|
29
|
+
},
|
|
30
|
+
settings: {
|
|
31
|
+
[ELEMENTS]: [],
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
exports.default = config;
|
|
35
|
+
// For CommonJS compatibility
|
|
36
|
+
module.exports = config;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Config } from "../Settings";
|
|
2
|
+
/**
|
|
3
|
+
* Strict configuration for eslint-plugin-boundaries.
|
|
4
|
+
*
|
|
5
|
+
* It enables all rules, enforcing full compliance with defined boundaries. Unknown files and importing ignored files are not allowed.
|
|
6
|
+
*/
|
|
7
|
+
declare const config: Config;
|
|
8
|
+
export default config;
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
const Settings_1 = require("../Settings");
|
|
7
|
+
const Recommended_1 = __importDefault(require("./Recommended"));
|
|
8
|
+
const { RULE_NO_IGNORED, RULE_NO_UNKNOWN_FILES, RULE_NO_UNKNOWN } = Settings_1.SETTINGS;
|
|
9
|
+
// TODO In next major version: Export also files, plugin, etc.
|
|
10
|
+
/**
|
|
11
|
+
* Strict configuration for eslint-plugin-boundaries.
|
|
12
|
+
*
|
|
13
|
+
* It enables all rules, enforcing full compliance with defined boundaries. Unknown files and importing ignored files are not allowed.
|
|
14
|
+
*/
|
|
15
|
+
const config = {
|
|
16
|
+
...Recommended_1.default,
|
|
17
|
+
rules: {
|
|
18
|
+
...Recommended_1.default.rules,
|
|
19
|
+
[RULE_NO_IGNORED]: 2,
|
|
20
|
+
[RULE_NO_UNKNOWN_FILES]: 2,
|
|
21
|
+
[RULE_NO_UNKNOWN]: 2,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
exports.default = config;
|
|
25
|
+
// For CommonJS compatibility
|
|
26
|
+
module.exports = config;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Matcher, DependencyDescription, DependencyKind, ElementDescription } from "@boundaries/elements";
|
|
2
|
+
import type { Rule } from "eslint";
|
|
3
|
+
import type { SettingsNormalized } from "../Settings";
|
|
4
|
+
import type { EslintLiteralNode } from "./Elements.types";
|
|
5
|
+
/**
|
|
6
|
+
* Returns the elements matcher based on the ESLint rule context, filtering out invalid descriptors
|
|
7
|
+
* @param context The ESLint rule context
|
|
8
|
+
* @returns The elements matcher
|
|
9
|
+
*/
|
|
10
|
+
export declare function getElementsMatcher(settings: SettingsNormalized): Matcher;
|
|
11
|
+
/**
|
|
12
|
+
* Returns the specifiers used in an import or export statement
|
|
13
|
+
* @param node The AST node representing the import or export
|
|
14
|
+
* @returns The list of specifiers used in the import or export
|
|
15
|
+
*/
|
|
16
|
+
export declare function getSpecifiers(node: Rule.Node): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Returns the description of the current file being linted
|
|
19
|
+
* @param fileName The file name
|
|
20
|
+
* @param settings The ESLint rule context settings normalized
|
|
21
|
+
* @returns The description of the current file being linted
|
|
22
|
+
*/
|
|
23
|
+
export declare function elementDescription(fileName: string, settings: SettingsNormalized): ElementDescription;
|
|
24
|
+
/**
|
|
25
|
+
* Returns the description of a dependency node
|
|
26
|
+
* @param param0 The dependency node info
|
|
27
|
+
* @param context The ESLint rule context
|
|
28
|
+
* @returns The description of the dependency node
|
|
29
|
+
*/
|
|
30
|
+
export declare function dependencyDescription({ node, kind, nodeKind, }: {
|
|
31
|
+
/** The dependency node */
|
|
32
|
+
node: EslintLiteralNode;
|
|
33
|
+
/** The kind of the dependency */
|
|
34
|
+
kind: DependencyKind;
|
|
35
|
+
/** The kind of the node generating the dependency */
|
|
36
|
+
nodeKind?: string;
|
|
37
|
+
},
|
|
38
|
+
/** The file name */
|
|
39
|
+
fileName: string,
|
|
40
|
+
/** The ESLint rule context settings normalized */
|
|
41
|
+
settings: SettingsNormalized,
|
|
42
|
+
/** The ESLint rule context */
|
|
43
|
+
context: Rule.RuleContext): DependencyDescription;
|
|
@@ -0,0 +1,107 @@
|
|
|
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.getElementsMatcher = getElementsMatcher;
|
|
7
|
+
exports.getSpecifiers = getSpecifiers;
|
|
8
|
+
exports.elementDescription = elementDescription;
|
|
9
|
+
exports.dependencyDescription = dependencyDescription;
|
|
10
|
+
const elements_1 = require("@boundaries/elements");
|
|
11
|
+
const resolve_1 = __importDefault(require("eslint-module-utils/resolve"));
|
|
12
|
+
const Support_1 = require("../Support");
|
|
13
|
+
const elements = new elements_1.Elements();
|
|
14
|
+
/**
|
|
15
|
+
* Returns the elements matcher based on the ESLint rule context, filtering out invalid descriptors
|
|
16
|
+
* @param context The ESLint rule context
|
|
17
|
+
* @returns The elements matcher
|
|
18
|
+
*/
|
|
19
|
+
function getElementsMatcher(settings) {
|
|
20
|
+
const elementsMatcher = elements.getMatcher(settings.elementDescriptors, {
|
|
21
|
+
ignorePaths: settings.ignorePaths,
|
|
22
|
+
includePaths: settings.includePaths,
|
|
23
|
+
legacyTemplates: settings.legacyTemplates,
|
|
24
|
+
});
|
|
25
|
+
return elementsMatcher;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Replaces backslashes with forward slashes in a given path
|
|
29
|
+
* @param filePath The file path to modify
|
|
30
|
+
* @returns The modified file path with forward slashes
|
|
31
|
+
*/
|
|
32
|
+
function replacePathSlashes(filePath) {
|
|
33
|
+
return filePath.replaceAll("\\", "/");
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Transforms an absolute path into a project-relative path
|
|
37
|
+
* @param absolutePath The absolute path to transform
|
|
38
|
+
* @param rootPath The root path of the project
|
|
39
|
+
* @returns The relative path from the project root
|
|
40
|
+
*/
|
|
41
|
+
function projectPath(absolutePath, rootPath) {
|
|
42
|
+
if (absolutePath) {
|
|
43
|
+
// TODO: Use path.relative when possible. With caution because this would break current external paths
|
|
44
|
+
return replacePathSlashes(absolutePath).replace(`${replacePathSlashes(rootPath)}/`, "");
|
|
45
|
+
}
|
|
46
|
+
return "";
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Returns the specifiers used in an import or export statement
|
|
50
|
+
* @param node The AST node representing the import or export
|
|
51
|
+
* @returns The list of specifiers used in the import or export
|
|
52
|
+
*/
|
|
53
|
+
function getSpecifiers(node) {
|
|
54
|
+
if (node.parent.type === "ImportDeclaration") {
|
|
55
|
+
return node.parent.specifiers
|
|
56
|
+
.filter((specifier) => specifier.type === "ImportSpecifier" &&
|
|
57
|
+
specifier.imported &&
|
|
58
|
+
specifier.imported.name)
|
|
59
|
+
.map((specifier) => specifier.imported.name);
|
|
60
|
+
}
|
|
61
|
+
if (node.parent.type === "ExportNamedDeclaration") {
|
|
62
|
+
return node.parent.specifiers
|
|
63
|
+
.filter((specifier) => specifier.type === "ExportSpecifier" &&
|
|
64
|
+
specifier.exported.name)
|
|
65
|
+
.map((specifier) => specifier.exported.name);
|
|
66
|
+
}
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Returns the description of the current file being linted
|
|
71
|
+
* @param fileName The file name
|
|
72
|
+
* @param settings The ESLint rule context settings normalized
|
|
73
|
+
* @returns The description of the current file being linted
|
|
74
|
+
*/
|
|
75
|
+
function elementDescription(fileName, settings) {
|
|
76
|
+
const matcher = getElementsMatcher(settings);
|
|
77
|
+
const path = projectPath(fileName, settings.rootPath);
|
|
78
|
+
const result = matcher.describeElement(path);
|
|
79
|
+
(0, Support_1.debugDescription)(result);
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Returns the description of a dependency node
|
|
84
|
+
* @param param0 The dependency node info
|
|
85
|
+
* @param context The ESLint rule context
|
|
86
|
+
* @returns The description of the dependency node
|
|
87
|
+
*/
|
|
88
|
+
function dependencyDescription({ node, kind, nodeKind, },
|
|
89
|
+
/** The file name */
|
|
90
|
+
fileName,
|
|
91
|
+
/** The ESLint rule context settings normalized */
|
|
92
|
+
settings,
|
|
93
|
+
/** The ESLint rule context */
|
|
94
|
+
context) {
|
|
95
|
+
const source = String(node.value);
|
|
96
|
+
const matcher = getElementsMatcher(settings);
|
|
97
|
+
const description = matcher.describeDependency({
|
|
98
|
+
from: projectPath(fileName, settings.rootPath),
|
|
99
|
+
to: projectPath((0, resolve_1.default)(source, context), settings.rootPath),
|
|
100
|
+
source,
|
|
101
|
+
kind: kind || "value", // TODO: Change by runtime in a backwards compatible way
|
|
102
|
+
nodeKind,
|
|
103
|
+
specifiers: getSpecifiers(node),
|
|
104
|
+
});
|
|
105
|
+
(0, Support_1.debugDescription)(description);
|
|
106
|
+
return description;
|
|
107
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./Elements.types"), exports);
|
|
18
|
+
__exportStar(require("./Elements"), exports);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DependencyKind, CapturedValuesSelector, ElementParent, ElementsSelector, CapturedValues, DependencyDescription, ElementDescription } from "@boundaries/elements";
|
|
2
|
+
import type { RuleMatcherElementsCapturedValues } from "../Settings";
|
|
3
|
+
export declare function replaceObjectValuesInTemplates(strings: string | string[], object: Record<string, string>, namespace?: string | null): string | string[];
|
|
4
|
+
export declare function replaceObjectValuesInTemplate(template: string, object: Record<string, string>, namespace?: string | null): string;
|
|
5
|
+
export declare function quote(str: string | undefined | null): string;
|
|
6
|
+
export declare function micromatchPatternReplacingObjectsValues(pattern: string | string[] | undefined, object: Partial<RuleMatcherElementsCapturedValues>): string | string[];
|
|
7
|
+
export declare function ruleElementMessage(elementPatterns: ElementsSelector | undefined, elementCapturedValues: CapturedValues | null): string | import("@boundaries/elements").BaseElementSelectorData | import("@boundaries/elements").BaseElementSelectorWithOptions | CapturedValuesSelector;
|
|
8
|
+
export declare function customErrorMessage(message: string, dependency: DependencyDescription, report?: {}): string;
|
|
9
|
+
export declare function elementMessage(elementInfo: ElementDescription | ElementParent): string;
|
|
10
|
+
export declare function dependencyImportKindMessage(ruleImportKind: DependencyKind | undefined, dependency: DependencyDescription): string;
|
|
11
|
+
export declare function dependencyUsageKindMessage(ruleImportKind: DependencyKind | undefined, dependency: DependencyDescription, { suffix, prefix, }?: {
|
|
12
|
+
suffix?: string;
|
|
13
|
+
prefix?: string;
|
|
14
|
+
}): string;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.replaceObjectValuesInTemplates = replaceObjectValuesInTemplates;
|
|
4
|
+
exports.replaceObjectValuesInTemplate = replaceObjectValuesInTemplate;
|
|
5
|
+
exports.quote = quote;
|
|
6
|
+
exports.micromatchPatternReplacingObjectsValues = micromatchPatternReplacingObjectsValues;
|
|
7
|
+
exports.ruleElementMessage = ruleElementMessage;
|
|
8
|
+
exports.customErrorMessage = customErrorMessage;
|
|
9
|
+
exports.elementMessage = elementMessage;
|
|
10
|
+
exports.dependencyImportKindMessage = dependencyImportKindMessage;
|
|
11
|
+
exports.dependencyUsageKindMessage = dependencyUsageKindMessage;
|
|
12
|
+
const elements_1 = require("@boundaries/elements");
|
|
13
|
+
const Support_1 = require("../Support");
|
|
14
|
+
function replaceObjectValueInTemplate(template, key, value, namespace) {
|
|
15
|
+
const keyToReplace = namespace ? `${namespace}.${key}` : key;
|
|
16
|
+
const regexp = new RegExp(`\\$\\{${keyToReplace}\\}`, "g");
|
|
17
|
+
return template.replace(regexp, value);
|
|
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);
|
|
30
|
+
}
|
|
31
|
+
function replaceObjectValuesInTemplate(template, object, namespace) {
|
|
32
|
+
return replaceObjectValuesInTemplates(template, object, namespace);
|
|
33
|
+
}
|
|
34
|
+
function quote(str) {
|
|
35
|
+
return `'${str || ""}'`;
|
|
36
|
+
}
|
|
37
|
+
function typeMessage(elementMatcher) {
|
|
38
|
+
return `elements of type ${quote(elementMatcher)}`;
|
|
39
|
+
}
|
|
40
|
+
function categoryMessage(category) {
|
|
41
|
+
return `category ${quote(category)}`;
|
|
42
|
+
}
|
|
43
|
+
function propertiesConcatenator(properties, index) {
|
|
44
|
+
if (properties.length > 1 && index === properties.length - 1) {
|
|
45
|
+
return " and";
|
|
46
|
+
}
|
|
47
|
+
if (index === 0) {
|
|
48
|
+
return " with";
|
|
49
|
+
}
|
|
50
|
+
return ",";
|
|
51
|
+
}
|
|
52
|
+
function micromatchPatternReplacingObjectsValues(pattern, object) {
|
|
53
|
+
let patternToReplace = pattern;
|
|
54
|
+
if (!patternToReplace) {
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
// Backward compatibility. Possibly unused, because the value is already replaced in the next step.
|
|
58
|
+
// For the moment, keep it to avoid unexpected issues until the oncoming refactor.
|
|
59
|
+
if (object.from) {
|
|
60
|
+
patternToReplace = replaceObjectValuesInTemplates(patternToReplace, object.from);
|
|
61
|
+
}
|
|
62
|
+
return Object.keys(object).reduce((replacedPattern, namespace) => {
|
|
63
|
+
if (!object[namespace]) {
|
|
64
|
+
return replacedPattern;
|
|
65
|
+
}
|
|
66
|
+
return replaceObjectValuesInTemplates(replacedPattern, object[namespace] || {}, namespace);
|
|
67
|
+
}, patternToReplace);
|
|
68
|
+
}
|
|
69
|
+
function micromatchPatternMessage(micromatchPatterns, elementCapturedValues) {
|
|
70
|
+
const micromatchPatternsWithValues = micromatchPatternReplacingObjectsValues(micromatchPatterns, { from: elementCapturedValues || {} });
|
|
71
|
+
if ((0, Support_1.isArray)(micromatchPatternsWithValues)) {
|
|
72
|
+
if (micromatchPatternsWithValues.length === 1) {
|
|
73
|
+
return quote(micromatchPatternsWithValues[0]);
|
|
74
|
+
}
|
|
75
|
+
return micromatchPatternsWithValues.reduce((message, micromatchPattern, index) => {
|
|
76
|
+
if (index === 0) {
|
|
77
|
+
return quote(micromatchPattern);
|
|
78
|
+
}
|
|
79
|
+
if (index === micromatchPatternsWithValues.length - 1) {
|
|
80
|
+
return `${message} or ${quote(micromatchPattern)}`;
|
|
81
|
+
}
|
|
82
|
+
return `${message}, ${quote(micromatchPattern)}`;
|
|
83
|
+
}, "");
|
|
84
|
+
}
|
|
85
|
+
return quote(micromatchPatternsWithValues);
|
|
86
|
+
}
|
|
87
|
+
function capturedValuesMatcherMessage(capturedValuesPattern, elementCapturedValues) {
|
|
88
|
+
const capturedValuesPatternKeys = Object.keys(capturedValuesPattern || {});
|
|
89
|
+
return capturedValuesPatternKeys
|
|
90
|
+
.map((key) => {
|
|
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) {
|
|
101
|
+
return "";
|
|
102
|
+
}
|
|
103
|
+
if ((0, elements_1.isElementSelector)(elementMatcher)) {
|
|
104
|
+
const selector = (0, elements_1.normalizeElementsSelector)(elementMatcher);
|
|
105
|
+
const parts = [];
|
|
106
|
+
const toAdd = [];
|
|
107
|
+
if (selector[0].type) {
|
|
108
|
+
// @ts-expect-error Types have to be aligned properly
|
|
109
|
+
toAdd.push(typeMessage(selector[0].type));
|
|
110
|
+
}
|
|
111
|
+
if (selector[0].category) {
|
|
112
|
+
toAdd.push(propertiesConcatenator(parts, parts.length + toAdd.length + 1),
|
|
113
|
+
// @ts-expect-error Types have to be aligned properly
|
|
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(" ");
|
|
121
|
+
}
|
|
122
|
+
// Backward compatibility. Code should not reach here normally.
|
|
123
|
+
if ((0, Support_1.isString)(elementMatcher)) {
|
|
124
|
+
return typeMessage(elementMatcher);
|
|
125
|
+
}
|
|
126
|
+
// TODO: Support array patterns
|
|
127
|
+
return `${typeMessage(elementMatcher[0])}${capturedValuesMatcherMessage(elementMatcher[1], elementCapturedValues)}`;
|
|
128
|
+
}
|
|
129
|
+
function ruleElementMessage(elementPatterns, elementCapturedValues) {
|
|
130
|
+
if ((0, Support_1.isArray)(elementPatterns)) {
|
|
131
|
+
if (elementPatterns.length === 1) {
|
|
132
|
+
return elementMatcherMessage(elementPatterns[0], elementCapturedValues);
|
|
133
|
+
}
|
|
134
|
+
return elementPatterns.reduce((message, elementPattern, index) => {
|
|
135
|
+
if (index === 0) {
|
|
136
|
+
return elementMatcherMessage(elementPattern, elementCapturedValues);
|
|
137
|
+
}
|
|
138
|
+
return `${message}, or ${elementMatcherMessage(elementPattern, elementCapturedValues)}`;
|
|
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 "";
|
|
177
|
+
}
|
|
178
|
+
const capturedValuesKeys = Object.keys(capturedValues);
|
|
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
|
+
}, "");
|
|
186
|
+
}
|
|
187
|
+
function elementMessage(elementInfo) {
|
|
188
|
+
return `of type ${quote(elementInfo.type)}${elementCapturedValuesMessage(elementInfo.captured)}`;
|
|
189
|
+
}
|
|
190
|
+
function hasToPrintKindMessage(ruleImportKind, dependency) {
|
|
191
|
+
return ruleImportKind && dependency.dependency.kind;
|
|
192
|
+
}
|
|
193
|
+
function dependencyImportKindMessage(ruleImportKind, dependency) {
|
|
194
|
+
if (hasToPrintKindMessage(ruleImportKind, dependency)) {
|
|
195
|
+
return `kind ${quote(dependency.dependency.kind)} from `;
|
|
196
|
+
}
|
|
197
|
+
return "";
|
|
198
|
+
}
|
|
199
|
+
function dependencyUsageKindMessage(ruleImportKind, dependency, { suffix = " ", prefix = "", } = {}) {
|
|
200
|
+
if (hasToPrintKindMessage(ruleImportKind, dependency)) {
|
|
201
|
+
return `${prefix}${dependency.dependency.kind}${suffix}`;
|
|
202
|
+
}
|
|
203
|
+
return "";
|
|
204
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Messages";
|