eslint-plugin-use-agnostic 0.9.6 → 0.9.8
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 +11 -4
- package/library/_commons/constants/bases.js +33 -1
- package/library/_commons/utilities/helpers.js +45 -48
- package/library/agnostic20/_commons/constants/bases.js +42 -31
- package/library/agnostic20/_commons/rules/import-rules.js +5 -1
- package/library/agnostic20/_commons/utilities/flows.js +48 -37
- package/library/agnostic20/_commons/utilities/helpers.js +39 -56
- package/library/agnostic20/config.js +5 -1
- package/library/directive21/_commons/constants/bases.js +46 -60
- package/library/directive21/_commons/rules/import-rules.js +5 -1
- package/library/directive21/_commons/utilities/flows.js +51 -42
- package/library/directive21/_commons/utilities/helpers.js +72 -101
- package/library/directive21/config.js +5 -1
- package/library/index.js +9 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
npm install eslint@^9.0.0 eslint-plugin-use-agnostic --save-dev
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
## Setup
|
|
13
|
+
## Setup
|
|
14
14
|
|
|
15
15
|
```js
|
|
16
16
|
// eslint.config.js
|
|
@@ -20,13 +20,20 @@ import { defineConfig, globalIgnores } from "eslint/config";
|
|
|
20
20
|
import useAgnostic, {
|
|
21
21
|
useAgnosticPluginName,
|
|
22
22
|
agnostic20ConfigName,
|
|
23
|
-
|
|
23
|
+
// enforceEffectiveDirectivesRuleName
|
|
24
|
+
} from "eslint-plugin-use-agnostic";
|
|
24
25
|
|
|
25
26
|
export default defineConfig([
|
|
26
27
|
globalIgnores([".next", ".react-router", "node_modules"]),
|
|
27
28
|
{
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
files: [
|
|
30
|
+
"**/*.tsx",
|
|
31
|
+
"**/*.ts",
|
|
32
|
+
"**/*.jsx",
|
|
33
|
+
"**/*.js",
|
|
34
|
+
"**/*.mjs",
|
|
35
|
+
"**/*.cjs",
|
|
36
|
+
],
|
|
30
37
|
plugins: {
|
|
31
38
|
[useAgnosticPluginName]: useAgnostic,
|
|
32
39
|
},
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('../../../types/_commons/typedefs').Extensions} Extensions
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
/* plugin names */
|
|
2
6
|
// use-agnostic
|
|
3
7
|
export const useAgnosticPluginName = "use-agnostic";
|
|
@@ -45,6 +49,7 @@ export const exportNotStrategized =
|
|
|
45
49
|
// all "resolved" directives (from AIA/agnostic20 & DFA/directive21)
|
|
46
50
|
// - AIA: Agnostic-Included Architecture (agnostic20)
|
|
47
51
|
// - DFA: Directive-First Architecture (directive21)
|
|
52
|
+
// agnostic20
|
|
48
53
|
export const USE_SERVER_LOGICS = "use server logics";
|
|
49
54
|
export const USE_CLIENT_LOGICS = "use client logics";
|
|
50
55
|
export const USE_AGNOSTIC_LOGICS = "use agnostic logics";
|
|
@@ -52,11 +57,13 @@ export const USE_SERVER_COMPONENTS = "use server components";
|
|
|
52
57
|
export const USE_CLIENT_COMPONENTS = "use client components";
|
|
53
58
|
export const USE_AGNOSTIC_COMPONENTS = "use agnostic components";
|
|
54
59
|
export const USE_SERVER_FUNCTIONS = "use server functions";
|
|
60
|
+
// and directive21
|
|
55
61
|
export const USE_CLIENT_CONTEXTS = "use client contexts";
|
|
56
62
|
export const USE_AGNOSTIC_CONDITIONS = "use agnostic conditions";
|
|
57
63
|
export const USE_AGNOSTIC_STRATEGIES = "use agnostic strategies";
|
|
58
64
|
|
|
59
65
|
// all "resolved" modules (from AIA/agnostic20 & DFA/directive21)
|
|
66
|
+
// agnostic20
|
|
60
67
|
export const SERVER_LOGICS_MODULE = "Server Logics Module";
|
|
61
68
|
export const CLIENT_LOGICS_MODULE = "Client Logics Module";
|
|
62
69
|
export const AGNOSTIC_LOGICS_MODULE = "Agnostic Logics Module";
|
|
@@ -64,10 +71,30 @@ export const SERVER_COMPONENTS_MODULE = "Server Components Module";
|
|
|
64
71
|
export const CLIENT_COMPONENTS_MODULE = "Client Components Module";
|
|
65
72
|
export const AGNOSTIC_COMPONENTS_MODULE = "Agnostic Components Module";
|
|
66
73
|
export const SERVER_FUNCTIONS_MODULE = "Server Functions Module";
|
|
74
|
+
// and directive21
|
|
67
75
|
export const CLIENT_CONTEXTS_MODULE = "Client Contexts Module";
|
|
68
76
|
export const AGNOSTIC_CONDITIONS_MODULE = "Agnostic Conditions Module";
|
|
69
77
|
export const AGNOSTIC_STRATEGIES_MODULE = "Agnostic Strategies Module";
|
|
70
78
|
|
|
79
|
+
// all mappings of "resolved" directives with "resolved" modules
|
|
80
|
+
// agnostic20
|
|
81
|
+
export const effectiveDirectives_effectiveModules = Object.freeze({
|
|
82
|
+
[USE_SERVER_LOGICS]: SERVER_LOGICS_MODULE,
|
|
83
|
+
[USE_SERVER_COMPONENTS]: SERVER_COMPONENTS_MODULE,
|
|
84
|
+
[USE_SERVER_FUNCTIONS]: SERVER_FUNCTIONS_MODULE,
|
|
85
|
+
[USE_CLIENT_LOGICS]: CLIENT_LOGICS_MODULE,
|
|
86
|
+
[USE_CLIENT_COMPONENTS]: CLIENT_COMPONENTS_MODULE,
|
|
87
|
+
[USE_AGNOSTIC_LOGICS]: AGNOSTIC_LOGICS_MODULE,
|
|
88
|
+
[USE_AGNOSTIC_COMPONENTS]: AGNOSTIC_COMPONENTS_MODULE,
|
|
89
|
+
});
|
|
90
|
+
// and directive21
|
|
91
|
+
export const commentedDirectives_commentedModules = Object.freeze({
|
|
92
|
+
[USE_CLIENT_CONTEXTS]: CLIENT_CONTEXTS_MODULE,
|
|
93
|
+
[USE_AGNOSTIC_CONDITIONS]: AGNOSTIC_CONDITIONS_MODULE,
|
|
94
|
+
[USE_AGNOSTIC_STRATEGIES]: AGNOSTIC_STRATEGIES_MODULE,
|
|
95
|
+
...effectiveDirectives_effectiveModules,
|
|
96
|
+
});
|
|
97
|
+
|
|
71
98
|
// JavaScript/TypeScript extensions
|
|
72
99
|
export const TSX = ".tsx";
|
|
73
100
|
export const TS = ".ts";
|
|
@@ -77,8 +104,13 @@ export const MJS = ".mjs";
|
|
|
77
104
|
export const CJS = ".cjs";
|
|
78
105
|
|
|
79
106
|
// JavaScript/TypeScript extensions array
|
|
80
|
-
/** @type {
|
|
107
|
+
/** @type {Extensions} */
|
|
81
108
|
export const EXTENSIONS = [TSX, TS, JSX, JS, MJS, CJS]; // In priority order
|
|
82
109
|
|
|
83
110
|
// message strings
|
|
84
111
|
export const ARE_NOT_ALLOWED_TO_IMPORT = "are not allowed to import";
|
|
112
|
+
|
|
113
|
+
// skipping object for flows
|
|
114
|
+
export const skip = Object.freeze({
|
|
115
|
+
skip: true,
|
|
116
|
+
});
|
|
@@ -3,30 +3,22 @@ import path from "path";
|
|
|
3
3
|
|
|
4
4
|
import { loadConfig, createMatchPath } from "tsconfig-paths";
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
AGNOSTIC_COMPONENTS_MODULE,
|
|
23
|
-
SERVER_FUNCTIONS_MODULE,
|
|
24
|
-
CLIENT_CONTEXTS_MODULE,
|
|
25
|
-
AGNOSTIC_CONDITIONS_MODULE,
|
|
26
|
-
AGNOSTIC_STRATEGIES_MODULE,
|
|
27
|
-
EXTENSIONS,
|
|
28
|
-
ARE_NOT_ALLOWED_TO_IMPORT,
|
|
29
|
-
} from "../constants/bases.js";
|
|
6
|
+
import { EXTENSIONS, ARE_NOT_ALLOWED_TO_IMPORT } from "../constants/bases.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {import('../../../types/_commons/typedefs').ResolvedDirectives_ResolvedModules} ResolvedDirectives_ResolvedModules
|
|
10
|
+
* @typedef {import('../../../types/_commons/typedefs').CurrentFileResolvedDirective} CurrentFileResolvedDirective
|
|
11
|
+
* @typedef {import('../../../types/_commons/typedefs').Context<string, readonly unknown[]>} Context
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @template {CurrentFileResolvedDirective} T
|
|
16
|
+
* @typedef {import('../../../types/_commons/typedefs').ImportedFileResolvedDirective<T>} ImportedFileResolvedDirective
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* @template {CurrentFileResolvedDirective} T
|
|
20
|
+
* @typedef {import('../../../types/_commons/typedefs').ResolvedDirectives_BlockedImports<T>} ResolvedDirectives_BlockedImports
|
|
21
|
+
*/
|
|
30
22
|
|
|
31
23
|
/* resolveImportPath */
|
|
32
24
|
|
|
@@ -110,7 +102,7 @@ export const getImportedFileFirstLine = (resolvedImportPath) => {
|
|
|
110
102
|
|
|
111
103
|
/**
|
|
112
104
|
* Gets the coordinates for the first line of code of a file.
|
|
113
|
-
* @param {
|
|
105
|
+
* @param {Context} context An ESLint rule's `context` object.
|
|
114
106
|
* @returns The `context.report` `loc`-compatible coordinates for the first line of code of a file.
|
|
115
107
|
*/
|
|
116
108
|
export const highlightFirstLineOfCode = (context) => ({
|
|
@@ -122,10 +114,11 @@ export const highlightFirstLineOfCode = (context) => ({
|
|
|
122
114
|
|
|
123
115
|
/**
|
|
124
116
|
* Returns a boolean deciding if an imported file's "resolved" directive is incompatible with the current file's "resolved" directive.
|
|
125
|
-
* @
|
|
126
|
-
* @param {
|
|
127
|
-
* @param {
|
|
128
|
-
* @
|
|
117
|
+
* @template {CurrentFileResolvedDirective} T
|
|
118
|
+
* @param {ResolvedDirectives_BlockedImports<T>} resolvedDirectives_blockedImports The blocked imports object, either for agnostic20 or for directive21.
|
|
119
|
+
* @param {T} currentFileResolvedDirective The current file's "resolved" directive.
|
|
120
|
+
* @param {ImportedFileResolvedDirective<T>} importedFileResolvedDirective The imported file's "resolved" directive.
|
|
121
|
+
* @returns `true` if the import is blocked, as established in respective `resolvedDirectives_blockedImports`.
|
|
129
122
|
*/
|
|
130
123
|
export const isImportBlocked = (
|
|
131
124
|
// Note: "Blocked" here is preferred over "not allowed" because a specific message will be shared for each of the blocked situations, explaining their reasons and the solutions needed.
|
|
@@ -141,37 +134,40 @@ export const isImportBlocked = (
|
|
|
141
134
|
|
|
142
135
|
/**
|
|
143
136
|
* Makes the intro for each specific import rule violation messages.
|
|
144
|
-
* @
|
|
145
|
-
* @param {
|
|
146
|
-
* @param {
|
|
147
|
-
* @
|
|
137
|
+
* @template {CurrentFileResolvedDirective} T
|
|
138
|
+
* @param {ResolvedDirectives_ResolvedModules} resolvedDirectives_resolvedModules The resolved modules object, either for agnostic20 or for directive21.
|
|
139
|
+
* @param {CurrentFileResolvedDirective} currentFileResolvedDirective The current file's "resolved" directive.
|
|
140
|
+
* @param {ImportedFileResolvedDirective<T>} importedFileResolvedDirective The imported file's "resolved" directive.
|
|
141
|
+
* @returns "[Current file 'resolved' modules] are not allowed to import [imported file 'resolved' modules]."
|
|
148
142
|
*/
|
|
149
143
|
export const makeIntroForSpecificViolationMessage = (
|
|
150
|
-
|
|
144
|
+
resolvedDirectives_resolvedModules,
|
|
151
145
|
currentFileResolvedDirective,
|
|
152
146
|
importedFileResolvedDirective
|
|
153
147
|
) =>
|
|
154
|
-
`${
|
|
148
|
+
`${resolvedDirectives_resolvedModules[currentFileResolvedDirective]}s ${ARE_NOT_ALLOWED_TO_IMPORT} ${resolvedDirectives_resolvedModules[importedFileResolvedDirective]}s.`;
|
|
155
149
|
|
|
156
|
-
/*
|
|
150
|
+
/* makeMessageFromCurrentFileResolvedDirective */
|
|
157
151
|
|
|
158
152
|
/**
|
|
159
153
|
* Lists in an message the "resolved" modules incompatible with a "resolved" module based on its "resolved" directive.
|
|
160
|
-
* @
|
|
161
|
-
* @param {
|
|
162
|
-
* @param {
|
|
154
|
+
* @template {CurrentFileResolvedDirective} T
|
|
155
|
+
* @param {ResolvedDirectives_ResolvedModules} resolvedDirectives_resolvedModules The resolved modules object, either for agnostic20 or for directive21.
|
|
156
|
+
* @param {ResolvedDirectives_BlockedImports<T>} resolvedDirectives_blockedImports The blocked imports object, either for agnostic20 or for directive21.
|
|
157
|
+
* @param {T} currentFileResolvedDirective The "resolved" directive of the "resolved" module.
|
|
163
158
|
* @returns The message listing the incompatible "resolved" modules.
|
|
164
159
|
*/
|
|
165
|
-
export const
|
|
166
|
-
|
|
160
|
+
export const makeMessageFromCurrentFileResolvedDirective = (
|
|
161
|
+
resolvedDirectives_resolvedModules,
|
|
167
162
|
resolvedDirectives_blockedImports,
|
|
168
|
-
|
|
163
|
+
currentFileResolvedDirective
|
|
169
164
|
) => {
|
|
170
|
-
const effectiveModule =
|
|
165
|
+
const effectiveModule =
|
|
166
|
+
resolvedDirectives_resolvedModules[currentFileResolvedDirective];
|
|
171
167
|
const effectiveModulesString = effectiveModule + "s"; // plural
|
|
172
168
|
|
|
173
169
|
const blockedImports =
|
|
174
|
-
resolvedDirectives_blockedImports[
|
|
170
|
+
resolvedDirectives_blockedImports[currentFileResolvedDirective].map(
|
|
175
171
|
(e) => e.blockedImport
|
|
176
172
|
) || [];
|
|
177
173
|
|
|
@@ -180,7 +176,7 @@ export const makeMessageFromResolvedDirective = (
|
|
|
180
176
|
}
|
|
181
177
|
|
|
182
178
|
const blockedEffectiveModules = blockedImports.map(
|
|
183
|
-
(e) =>
|
|
179
|
+
(e) => resolvedDirectives_resolvedModules[e] + "s" // plural
|
|
184
180
|
);
|
|
185
181
|
|
|
186
182
|
const blockedEffectiveModulesString =
|
|
@@ -197,9 +193,10 @@ export const makeMessageFromResolvedDirective = (
|
|
|
197
193
|
|
|
198
194
|
/**
|
|
199
195
|
* Finds the `message` for the specific violation of "resolved" directives import rules based on `resolvedDirectives_blockedImports`.
|
|
200
|
-
* @
|
|
201
|
-
* @param {
|
|
202
|
-
* @param {
|
|
196
|
+
* @template {CurrentFileResolvedDirective} T
|
|
197
|
+
* @param {ResolvedDirectives_BlockedImports<T>} resolvedDirectives_blockedImports The blocked imports object, either for agnostic20 or for directive21.
|
|
198
|
+
* @param {T} currentFileResolvedDirective The current file's "resolved" directive.
|
|
199
|
+
* @param {ImportedFileResolvedDirective<T>} importedFileResolvedDirective The imported file's "resolved" directive.
|
|
203
200
|
* @returns The corresponding `message`.
|
|
204
201
|
*/
|
|
205
202
|
export const findSpecificViolationMessage = (
|
|
@@ -6,29 +6,30 @@ import {
|
|
|
6
6
|
USE_CLIENT_COMPONENTS as COMMONS_USE_CLIENT_COMPONENTS,
|
|
7
7
|
USE_AGNOSTIC_LOGICS as COMMONS_USE_AGNOSTIC_LOGICS,
|
|
8
8
|
USE_AGNOSTIC_COMPONENTS as COMMONS_USE_AGNOSTIC_COMPONENTS,
|
|
9
|
-
|
|
10
|
-
SERVER_COMPONENTS_MODULE as COMMONS_SERVER_COMPONENTS_MODULE,
|
|
11
|
-
SERVER_FUNCTIONS_MODULE as COMMONS_SERVER_FUNCTIONS_MODULE,
|
|
12
|
-
CLIENT_LOGICS_MODULE as COMMONS_CLIENT_LOGICS_MODULE,
|
|
13
|
-
CLIENT_COMPONENTS_MODULE as COMMONS_CLIENT_COMPONENTS_MODULE,
|
|
14
|
-
AGNOSTIC_LOGICS_MODULE as COMMONS_AGNOSTIC_LOGICS_MODULE,
|
|
15
|
-
AGNOSTIC_COMPONENTS_MODULE as COMMONS_AGNOSTIC_COMPONENTS_MODULE,
|
|
9
|
+
effectiveDirectives_effectiveModules,
|
|
16
10
|
} from "../../../_commons/constants/bases.js";
|
|
17
11
|
|
|
18
12
|
import { makeIntroForSpecificViolationMessage as commonsMakeIntroForSpecificViolationMessage } from "../../../_commons/utilities/helpers.js";
|
|
19
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').Directive} Directive
|
|
16
|
+
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').Directives} Directives
|
|
17
|
+
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').EffectiveDirective} EffectiveDirective
|
|
18
|
+
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').Directives_EffectiveDirectives} Directives_EffectiveDirectives
|
|
19
|
+
*/
|
|
20
|
+
|
|
20
21
|
// directives
|
|
21
|
-
export const NO_DIRECTIVE =
|
|
22
|
+
export const NO_DIRECTIVE = "no directive";
|
|
22
23
|
export const USE_SERVER = "use server";
|
|
23
24
|
export const USE_CLIENT = "use client";
|
|
24
25
|
export const USE_AGNOSTIC = "use agnostic";
|
|
25
26
|
|
|
26
27
|
// directives array
|
|
27
|
-
/** @type {
|
|
28
|
+
/** @type {Directives} */
|
|
28
29
|
export const directivesArray = [USE_SERVER, USE_CLIENT, USE_AGNOSTIC];
|
|
29
30
|
|
|
30
31
|
// directives set
|
|
31
|
-
/** @type {ReadonlySet<
|
|
32
|
+
/** @type {ReadonlySet<Directive>} */
|
|
32
33
|
export const directivesSet = new Set(directivesArray); // no longer used exported to satisfy static type inference
|
|
33
34
|
|
|
34
35
|
// effective directives
|
|
@@ -40,24 +41,34 @@ export const USE_CLIENT_COMPONENTS = COMMONS_USE_CLIENT_COMPONENTS;
|
|
|
40
41
|
export const USE_AGNOSTIC_LOGICS = COMMONS_USE_AGNOSTIC_LOGICS;
|
|
41
42
|
export const USE_AGNOSTIC_COMPONENTS = COMMONS_USE_AGNOSTIC_COMPONENTS;
|
|
42
43
|
|
|
43
|
-
//
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const CLIENT_LOGICS_MODULE = COMMONS_CLIENT_LOGICS_MODULE;
|
|
48
|
-
const CLIENT_COMPONENTS_MODULE = COMMONS_CLIENT_COMPONENTS_MODULE;
|
|
49
|
-
const AGNOSTIC_LOGICS_MODULE = COMMONS_AGNOSTIC_LOGICS_MODULE;
|
|
50
|
-
const AGNOSTIC_COMPONENTS_MODULE = COMMONS_AGNOSTIC_COMPONENTS_MODULE;
|
|
44
|
+
// module kinds
|
|
45
|
+
export const LOGICS = "logics";
|
|
46
|
+
export const COMPONENTS = "components";
|
|
47
|
+
export const FUNCTIONS = "functions";
|
|
51
48
|
|
|
52
|
-
// mapping
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
[
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
[
|
|
49
|
+
// mapping directives with effective directives
|
|
50
|
+
/** @type {Directives_EffectiveDirectives} */
|
|
51
|
+
export const directives_effectiveDirectives = Object.freeze({
|
|
52
|
+
[NO_DIRECTIVE]: {
|
|
53
|
+
[LOGICS]: USE_SERVER_LOGICS,
|
|
54
|
+
[COMPONENTS]: USE_SERVER_COMPONENTS,
|
|
55
|
+
[FUNCTIONS]: null,
|
|
56
|
+
},
|
|
57
|
+
[USE_SERVER]: {
|
|
58
|
+
[LOGICS]: null,
|
|
59
|
+
[COMPONENTS]: null,
|
|
60
|
+
[FUNCTIONS]: USE_SERVER_FUNCTIONS,
|
|
61
|
+
},
|
|
62
|
+
[USE_CLIENT]: {
|
|
63
|
+
[LOGICS]: USE_CLIENT_LOGICS,
|
|
64
|
+
[COMPONENTS]: USE_CLIENT_COMPONENTS,
|
|
65
|
+
[FUNCTIONS]: null,
|
|
66
|
+
},
|
|
67
|
+
[USE_AGNOSTIC]: {
|
|
68
|
+
[LOGICS]: USE_AGNOSTIC_LOGICS,
|
|
69
|
+
[COMPONENTS]: USE_AGNOSTIC_COMPONENTS,
|
|
70
|
+
[FUNCTIONS]: null,
|
|
71
|
+
},
|
|
61
72
|
});
|
|
62
73
|
|
|
63
74
|
// message placeholders
|
|
@@ -70,8 +81,8 @@ export const specificViolationMessage = "specificViolationMessage";
|
|
|
70
81
|
|
|
71
82
|
/**
|
|
72
83
|
* Makes the intro for each specific import rule violation messages.
|
|
73
|
-
* @param {
|
|
74
|
-
* @param {
|
|
84
|
+
* @param {EffectiveDirective} currentFileEffectiveDirective The current file's effective directive.
|
|
85
|
+
* @param {EffectiveDirective} importedFileEffectiveDirective The imported file's effective directive.
|
|
75
86
|
* @returns "[Current file effective modules] are not allowed to import [imported file effective modules]."
|
|
76
87
|
*/
|
|
77
88
|
const makeIntroForSpecificViolationMessage = (
|
|
@@ -79,7 +90,7 @@ const makeIntroForSpecificViolationMessage = (
|
|
|
79
90
|
importedFileEffectiveDirective
|
|
80
91
|
) =>
|
|
81
92
|
commonsMakeIntroForSpecificViolationMessage(
|
|
82
|
-
|
|
93
|
+
effectiveDirectives_effectiveModules,
|
|
83
94
|
currentFileEffectiveDirective,
|
|
84
95
|
importedFileEffectiveDirective
|
|
85
96
|
);
|
|
@@ -87,7 +98,7 @@ const makeIntroForSpecificViolationMessage = (
|
|
|
87
98
|
const SUGGEST_USE_AGNOSTIC =
|
|
88
99
|
"If the module you're trying to import does not possess any server-side code however, please mark it with this plugin's own and eponymous 'use agnostic' directive to signal its compatibility across all environments.";
|
|
89
100
|
|
|
90
|
-
export const
|
|
101
|
+
export const effectiveDirectives_blockedImports = Object.freeze({
|
|
91
102
|
[USE_SERVER_LOGICS]: [
|
|
92
103
|
// USE_SERVER_LOGICS allowed, because Server Logics can compose with one another.
|
|
93
104
|
// USE_SERVER_COMPONENTS allowed, because Server Components are OK to be composed with Server Logics as long as the Server Logics Module, by convention, does not export React components.
|
|
@@ -16,7 +16,11 @@ import {
|
|
|
16
16
|
reExportsFlow,
|
|
17
17
|
} from "../utilities/flows.js";
|
|
18
18
|
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').Rule} Rule
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** @type {Rule} */
|
|
20
24
|
const rule = {
|
|
21
25
|
meta: {
|
|
22
26
|
type: "problem",
|
|
@@ -5,16 +5,11 @@ import {
|
|
|
5
5
|
useServerJSXMessageId,
|
|
6
6
|
importBreaksEffectiveImportRulesMessageId,
|
|
7
7
|
reExportNotSameMessageId,
|
|
8
|
+
skip,
|
|
8
9
|
} from "../../../_commons/constants/bases.js";
|
|
9
10
|
import {
|
|
11
|
+
NO_DIRECTIVE,
|
|
10
12
|
USE_SERVER,
|
|
11
|
-
USE_SERVER_LOGICS,
|
|
12
|
-
USE_SERVER_COMPONENTS,
|
|
13
|
-
USE_SERVER_FUNCTIONS,
|
|
14
|
-
USE_CLIENT_LOGICS,
|
|
15
|
-
USE_CLIENT_COMPONENTS,
|
|
16
|
-
USE_AGNOSTIC_LOGICS,
|
|
17
|
-
USE_AGNOSTIC_COMPONENTS,
|
|
18
13
|
// currentFileEffectiveDirective,
|
|
19
14
|
// importedFileEffectiveDirective,
|
|
20
15
|
effectiveDirectiveMessage,
|
|
@@ -30,18 +25,28 @@ import {
|
|
|
30
25
|
getDirectiveFromImportedModule,
|
|
31
26
|
getEffectiveDirective,
|
|
32
27
|
isImportBlocked,
|
|
33
|
-
|
|
28
|
+
makeMessageFromCurrentFileEffectiveDirective,
|
|
34
29
|
findSpecificViolationMessage,
|
|
35
30
|
} from "./helpers.js";
|
|
36
31
|
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').Context} Context
|
|
34
|
+
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').EffectiveDirective} EffectiveDirective
|
|
35
|
+
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').ImportDeclaration} ImportDeclaration
|
|
36
|
+
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').ExportNamedDeclaration} ExportNamedDeclaration
|
|
37
|
+
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').ExportAllDeclaration} ExportAllDeclaration
|
|
38
|
+
*/
|
|
39
|
+
|
|
37
40
|
/* currentFileFlow */
|
|
38
41
|
|
|
39
42
|
/**
|
|
40
43
|
* The flow that begins the import rules enforcement rule, retrieving the valid directive of the current file before comparing it to upcoming valid directives of the files it imports.
|
|
41
|
-
* @param {
|
|
42
|
-
* @returns
|
|
44
|
+
* @param {Context} context The ESLint rule's `context` object.
|
|
45
|
+
* @returns Either an object with `skip: true` to disregard or one with the non-null `currentFileEffectiveDirective`.
|
|
43
46
|
*/
|
|
44
47
|
export const currentFileFlow = (context) => {
|
|
48
|
+
const skipTrue = { ...skip, currentFileEffectiveDirective: undefined };
|
|
49
|
+
|
|
45
50
|
// GETTING THE EXTENSION OF THE CURRENT FILE
|
|
46
51
|
const currentFileExtension = path.extname(context.filename);
|
|
47
52
|
|
|
@@ -53,11 +58,12 @@ export const currentFileFlow = (context) => {
|
|
|
53
58
|
console.error(
|
|
54
59
|
"ERROR. Linted files for this rule should only be in JavaScript (TypeScript)."
|
|
55
60
|
);
|
|
56
|
-
return
|
|
61
|
+
return skipTrue;
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
|
|
60
|
-
const currentFileDirective =
|
|
64
|
+
// GETTING THE DIRECTIVE (or lack thereof) OF THE CURRENT FILE
|
|
65
|
+
const currentFileDirective =
|
|
66
|
+
getDirectiveFromCurrentModule(context) ?? NO_DIRECTIVE;
|
|
61
67
|
|
|
62
68
|
// reports if a file marked "use server" has a JSX extension
|
|
63
69
|
if (
|
|
@@ -68,10 +74,10 @@ export const currentFileFlow = (context) => {
|
|
|
68
74
|
loc: highlightFirstLineOfCode(context),
|
|
69
75
|
messageId: useServerJSXMessageId,
|
|
70
76
|
});
|
|
71
|
-
return
|
|
77
|
+
return skipTrue;
|
|
72
78
|
}
|
|
73
79
|
|
|
74
|
-
// GETTING THE EFFECTIVE DIRECTIVE OF THE CURRENT FILE
|
|
80
|
+
// GETTING THE EFFECTIVE DIRECTIVE (no lack thereof) OF THE CURRENT FILE
|
|
75
81
|
const currentFileEffectiveDirective = getEffectiveDirective(
|
|
76
82
|
currentFileDirective,
|
|
77
83
|
currentFileExtension
|
|
@@ -80,21 +86,23 @@ export const currentFileFlow = (context) => {
|
|
|
80
86
|
// fails if one of the seven effective directives has not been obtained
|
|
81
87
|
if (currentFileEffectiveDirective === null) {
|
|
82
88
|
console.error("ERROR. Effective directive should never be null.");
|
|
83
|
-
return
|
|
89
|
+
return skipTrue;
|
|
84
90
|
}
|
|
85
91
|
|
|
86
|
-
return { currentFileEffectiveDirective };
|
|
92
|
+
return { skip: undefined, currentFileEffectiveDirective };
|
|
87
93
|
};
|
|
88
94
|
|
|
89
95
|
/* importedFileFlow */
|
|
90
96
|
|
|
91
97
|
/**
|
|
92
98
|
* The flow that is shared between import and re-export traversals to obtain the import file's effective directive.
|
|
93
|
-
* @param {
|
|
94
|
-
* @param {
|
|
95
|
-
* @returns
|
|
99
|
+
* @param {Context} context The ESLint rule's `context` object.
|
|
100
|
+
* @param {ImportDeclaration} node The ESLint `node` of the rule's current traversal.
|
|
101
|
+
* @returns Either an object with `skip: true` to disregard or one with the non-null `importedFileEffectiveDirective`.
|
|
96
102
|
*/
|
|
97
103
|
const importedFileFlow = (context, node) => {
|
|
104
|
+
const skipTrue = { ...skip, importedFileEffectiveDirective: undefined };
|
|
105
|
+
|
|
98
106
|
// finds the full path of the import
|
|
99
107
|
const resolvedImportPath = resolveImportPath(
|
|
100
108
|
path.dirname(context.filename),
|
|
@@ -103,19 +111,21 @@ const importedFileFlow = (context, node) => {
|
|
|
103
111
|
);
|
|
104
112
|
|
|
105
113
|
// does not operate on paths it did not resolve
|
|
106
|
-
if (resolvedImportPath === null) return
|
|
114
|
+
if (resolvedImportPath === null) return skipTrue;
|
|
107
115
|
// does not operate on non-JS files
|
|
108
116
|
const isImportedFileJS = EXTENSIONS.some((ext) =>
|
|
109
117
|
resolvedImportPath.endsWith(ext)
|
|
110
118
|
);
|
|
111
|
-
if (!isImportedFileJS) return
|
|
119
|
+
if (!isImportedFileJS) return skipTrue;
|
|
112
120
|
|
|
113
|
-
|
|
121
|
+
// GETTING THE DIRECTIVE (or lack thereof) OF THE IMPORTED FILE
|
|
114
122
|
const importedFileDirective =
|
|
115
|
-
getDirectiveFromImportedModule(resolvedImportPath);
|
|
123
|
+
getDirectiveFromImportedModule(resolvedImportPath) ?? NO_DIRECTIVE;
|
|
124
|
+
|
|
116
125
|
// GETTING THE EXTENSION OF THE IMPORTED FILE
|
|
117
126
|
const importedFileFileExtension = path.extname(resolvedImportPath);
|
|
118
|
-
|
|
127
|
+
|
|
128
|
+
// GETTING THE EFFECTIVE DIRECTIVE (no lack thereof) OF THE IMPORTED FILE
|
|
119
129
|
const importedFileEffectiveDirective = getEffectiveDirective(
|
|
120
130
|
importedFileDirective,
|
|
121
131
|
importedFileFileExtension
|
|
@@ -124,20 +134,20 @@ const importedFileFlow = (context, node) => {
|
|
|
124
134
|
// also fails if one of the seven effective directives has not been obtained
|
|
125
135
|
if (importedFileEffectiveDirective === null) {
|
|
126
136
|
console.error("ERROR. Effective directive should never be null.");
|
|
127
|
-
return
|
|
137
|
+
return skipTrue;
|
|
128
138
|
}
|
|
129
139
|
|
|
130
|
-
// For now skipping on both "does not operate" (which should ignore) and "fails"
|
|
140
|
+
// For now skipping on both "does not operate" (which should ignore) and "fails" (which should crash) albeit with console.error.
|
|
131
141
|
|
|
132
|
-
return { importedFileEffectiveDirective };
|
|
142
|
+
return { skip: undefined, importedFileEffectiveDirective };
|
|
133
143
|
};
|
|
134
144
|
|
|
135
145
|
/* importsFlow */
|
|
136
146
|
|
|
137
147
|
/** The full flow for import traversals to enforce effective directives import rules.
|
|
138
|
-
* @param {
|
|
139
|
-
* @param {
|
|
140
|
-
* @param {
|
|
148
|
+
* @param {Context} context The ESLint rule's `context` object.
|
|
149
|
+
* @param {ImportDeclaration} node The ESLint `node` of the rule's current traversal.
|
|
150
|
+
* @param {EffectiveDirective} currentFileEffectiveDirective The current file's effective directive.
|
|
141
151
|
* @returns Early if the flow needs to be interrupted.
|
|
142
152
|
*/
|
|
143
153
|
export const importsFlow = (context, node, currentFileEffectiveDirective) => {
|
|
@@ -159,9 +169,10 @@ export const importsFlow = (context, node, currentFileEffectiveDirective) => {
|
|
|
159
169
|
node,
|
|
160
170
|
messageId: importBreaksEffectiveImportRulesMessageId,
|
|
161
171
|
data: {
|
|
162
|
-
[effectiveDirectiveMessage]:
|
|
163
|
-
|
|
164
|
-
|
|
172
|
+
[effectiveDirectiveMessage]:
|
|
173
|
+
makeMessageFromCurrentFileEffectiveDirective(
|
|
174
|
+
currentFileEffectiveDirective
|
|
175
|
+
),
|
|
165
176
|
[specificViolationMessage]: findSpecificViolationMessage(
|
|
166
177
|
currentFileEffectiveDirective,
|
|
167
178
|
importedFileEffectiveDirective
|
|
@@ -174,9 +185,9 @@ export const importsFlow = (context, node, currentFileEffectiveDirective) => {
|
|
|
174
185
|
/* reExportsFlow */
|
|
175
186
|
|
|
176
187
|
/** The full flow for export traversals, shared between `ExportNamedDeclaration` and `ExportAllDeclaration`, to ensure same effective directive re-exports.
|
|
177
|
-
* @param {
|
|
178
|
-
* @param {
|
|
179
|
-
* @param {
|
|
188
|
+
* @param {Context} context The ESLint rule's `context` object.
|
|
189
|
+
* @param {ExportNamedDeclaration | ExportAllDeclaration} node The ESLint `node` of the rule's current traversal.
|
|
190
|
+
* @param {EffectiveDirective} currentFileEffectiveDirective The current file's effective directive.
|
|
180
191
|
* @returns Early if the flow needs to be interrupted.
|
|
181
192
|
*/
|
|
182
193
|
export const reExportsFlow = (context, node, currentFileEffectiveDirective) => {
|