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 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 (using TypeScript and the Flat Config)
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
- } from "eslint-plugin-use-agnostic"; // no declaration file at this time
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
- // files: ['**/*.js', '**/*.jsx'], // if you're using vanilla JavaScript
29
- files: ["**/*.ts", "**/*.tsx"],
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 {readonly [TSX, TS, JSX, JS, MJS, CJS]} */
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
- USE_SERVER_LOGICS,
8
- USE_CLIENT_LOGICS,
9
- USE_AGNOSTIC_LOGICS,
10
- USE_SERVER_COMPONENTS,
11
- USE_CLIENT_COMPONENTS,
12
- USE_AGNOSTIC_COMPONENTS,
13
- USE_SERVER_FUNCTIONS,
14
- USE_CLIENT_CONTEXTS,
15
- USE_AGNOSTIC_CONDITIONS,
16
- USE_AGNOSTIC_STRATEGIES,
17
- SERVER_LOGICS_MODULE,
18
- CLIENT_LOGICS_MODULE,
19
- AGNOSTIC_LOGICS_MODULE,
20
- SERVER_COMPONENTS_MODULE,
21
- CLIENT_COMPONENTS_MODULE,
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 {import('@typescript-eslint/utils').TSESLint.RuleContext} context An ESLint rule's `context` object.
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
- * @param {Readonly<{"use server logics": {blockedImport: string; message: string;}[]; "use client logics": {blockedImport: string; message: string;}[]; "use agnostic logics": {blockedImport: string; message: string;}[]; "use server components": {blockedImport: string; message: string;}[]; "use client components": {blockedImport: string; message: string;}[]; "use agnostic components": {blockedImport: string; message: string;}[]; "use server functions": {blockedImport: string; message: string;}[]; "use client contexts"?: {blockedImport: string; message: string;}[]; "use agnostic conditions"?: {blockedImport: string; message: string;}[]; "use agnostic strategies"?: {blockedImport: string; message: string;}[];}>} resolvedDirectives_blockedImports The blocked imports object, either for agnostic20 or for directive21.
126
- * @param {USE_SERVER_LOGICS | USE_CLIENT_LOGICS | USE_AGNOSTIC_LOGICS | USE_SERVER_COMPONENTS | USE_CLIENT_COMPONENTS | USE_AGNOSTIC_COMPONENTS | USE_SERVER_FUNCTIONS | USE_CLIENT_CONTEXTS | USE_AGNOSTIC_CONDITIONS | USE_AGNOSTIC_STRATEGIES} currentFileResolvedDirective The current file's "resolved" directive.
127
- * @param {USE_SERVER_LOGICS | USE_CLIENT_LOGICS | USE_AGNOSTIC_LOGICS | USE_SERVER_COMPONENTS | USE_CLIENT_COMPONENTS | USE_AGNOSTIC_COMPONENTS | USE_SERVER_FUNCTIONS | USE_CLIENT_CONTEXTS | USE_AGNOSTIC_CONDITIONS} importedFileResolvedDirective The imported file's "resolved" directive.
128
- * @returns Returns `true` if the import is blocked, as established in respective `resolvedDirectives_blockedImports`.
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
- * @param {Readonly<{"use server logics": SERVER_LOGICS_MODULE; "use client logics": CLIENT_LOGICS_MODULE; "use agnostic logics": AGNOSTIC_LOGICS_MODULE; "use server components": SERVER_COMPONENTS_MODULE; "use client components": CLIENT_COMPONENTS_MODULE; "use agnostic components": AGNOSTIC_COMPONENTS_MODULE; "use server functions": SERVER_FUNCTIONS_MODULE; "use client contexts": CLIENT_CONTEXTS_MODULE; "use agnostic conditions": AGNOSTIC_CONDITIONS_MODULE; "use agnostic strategies": AGNOSTIC_STRATEGIES_MODULE;}>} resolvedDirectives_ResolvedModules The resolved modules object, either for agnostic20 or for directive21.
145
- * @param {USE_SERVER_LOGICS | USE_CLIENT_LOGICS | USE_AGNOSTIC_LOGICS | USE_SERVER_COMPONENTS | USE_CLIENT_COMPONENTS | USE_AGNOSTIC_COMPONENTS | USE_SERVER_FUNCTIONS | USE_CLIENT_CONTEXTS | USE_AGNOSTIC_CONDITIONS | USE_AGNOSTIC_STRATEGIES} currentFileResolvedDirective The current file's "resolved" directive.
146
- * @param {USE_SERVER_LOGICS | USE_CLIENT_LOGICS | USE_AGNOSTIC_LOGICS | USE_SERVER_COMPONENTS | USE_CLIENT_COMPONENTS | USE_AGNOSTIC_COMPONENTS | USE_SERVER_FUNCTIONS | USE_CLIENT_CONTEXTS | USE_AGNOSTIC_CONDITIONS} importedFileResolvedDirective The imported file's "resolved" directive.
147
- * @returns Returns "[Current file 'resolved' modules] are not allowed to import [imported file 'resolved' modules]."
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
- resolvedDirectives_ResolvedModules,
144
+ resolvedDirectives_resolvedModules,
151
145
  currentFileResolvedDirective,
152
146
  importedFileResolvedDirective
153
147
  ) =>
154
- `${resolvedDirectives_ResolvedModules[currentFileResolvedDirective]}s ${ARE_NOT_ALLOWED_TO_IMPORT} ${resolvedDirectives_ResolvedModules[importedFileResolvedDirective]}s.`;
148
+ `${resolvedDirectives_resolvedModules[currentFileResolvedDirective]}s ${ARE_NOT_ALLOWED_TO_IMPORT} ${resolvedDirectives_resolvedModules[importedFileResolvedDirective]}s.`;
155
149
 
156
- /* makeMessageFromResolvedDirective */
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
- * @param {Readonly<{"use server logics": SERVER_LOGICS_MODULE; "use client logics": CLIENT_LOGICS_MODULE; "use agnostic logics": AGNOSTIC_LOGICS_MODULE; "use server components": SERVER_COMPONENTS_MODULE; "use client components": CLIENT_COMPONENTS_MODULE; "use agnostic components": AGNOSTIC_COMPONENTS_MODULE; "use server functions": SERVER_FUNCTIONS_MODULE; "use client contexts": CLIENT_CONTEXTS_MODULE; "use agnostic conditions": AGNOSTIC_CONDITIONS_MODULE; "use agnostic strategies": AGNOSTIC_STRATEGIES_MODULE;}>} resolvedDirectives_ResolvedModules The resolved modules object, either for agnostic20 or for directive21.
161
- * @param {Readonly<{"use server logics": {blockedImport: string; message: string;}[]; "use client logics": {blockedImport: string; message: string;}[]; "use agnostic logics": {blockedImport: string; message: string;}[]; "use server components": {blockedImport: string; message: string;}[]; "use client components": {blockedImport: string; message: string;}[]; "use agnostic components": {blockedImport: string; message: string;}[]; "use server functions": {blockedImport: string; message: string;}[]; "use client contexts"?: {blockedImport: string; message: string;}[]; "use agnostic conditions"?: {blockedImport: string; message: string;}[]; "use agnostic strategies"?: {blockedImport: string; message: string;}[];}>} resolvedDirectives_blockedImports The blocked imports object, either for agnostic20 or for directive21.
162
- * @param {USE_SERVER_LOGICS | USE_CLIENT_LOGICS | USE_AGNOSTIC_LOGICS | USE_SERVER_COMPONENTS | USE_CLIENT_COMPONENTS | USE_AGNOSTIC_COMPONENTS | USE_SERVER_FUNCTIONS | USE_CLIENT_CONTEXTS | USE_AGNOSTIC_CONDITIONS | USE_AGNOSTIC_STRATEGIES} resolvedDirective The "resolved" directive of the "resolved" module.
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 makeMessageFromResolvedDirective = (
166
- resolvedDirectives_ResolvedModules,
160
+ export const makeMessageFromCurrentFileResolvedDirective = (
161
+ resolvedDirectives_resolvedModules,
167
162
  resolvedDirectives_blockedImports,
168
- resolvedDirective
163
+ currentFileResolvedDirective
169
164
  ) => {
170
- const effectiveModule = resolvedDirectives_ResolvedModules[resolvedDirective];
165
+ const effectiveModule =
166
+ resolvedDirectives_resolvedModules[currentFileResolvedDirective];
171
167
  const effectiveModulesString = effectiveModule + "s"; // plural
172
168
 
173
169
  const blockedImports =
174
- resolvedDirectives_blockedImports[resolvedDirective].map(
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) => resolvedDirectives_ResolvedModules[e] + "s" // plural
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
- * @param {Readonly<{"use server logics": {blockedImport: string; message: string;}[]; "use client logics": {blockedImport: string; message: string;}[]; "use agnostic logics": {blockedImport: string; message: string;}[]; "use server components": {blockedImport: string; message: string;}[]; "use client components": {blockedImport: string; message: string;}[]; "use agnostic components": {blockedImport: string; message: string;}[]; "use server functions": {blockedImport: string; message: string;}[]; "use client contexts"?: {blockedImport: string; message: string;}[]; "use agnostic conditions"?: {blockedImport: string; message: string;}[]; "use agnostic strategies"?: {blockedImport: string; message: string;}[];}>} resolvedDirectives_blockedImports The blocked imports object, either for agnostic20 or for directive21.
201
- * @param {USE_SERVER_LOGICS | USE_CLIENT_LOGICS | USE_AGNOSTIC_LOGICS | USE_SERVER_COMPONENTS | USE_CLIENT_COMPONENTS | USE_AGNOSTIC_COMPONENTS | USE_SERVER_FUNCTIONS | USE_CLIENT_CONTEXTS | USE_AGNOSTIC_CONDITIONS | USE_AGNOSTIC_STRATEGIES} currentFileResolvedDirective The current file's "resolved" directive.
202
- * @param {USE_SERVER_LOGICS | USE_CLIENT_LOGICS | USE_AGNOSTIC_LOGICS | USE_SERVER_COMPONENTS | USE_CLIENT_COMPONENTS | USE_AGNOSTIC_COMPONENTS | USE_SERVER_FUNCTIONS | USE_CLIENT_CONTEXTS | USE_AGNOSTIC_CONDITIONS} importedFileResolvedDirective The imported file's "resolved" directive.
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
- SERVER_LOGICS_MODULE as COMMONS_SERVER_LOGICS_MODULE,
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 = null;
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 {readonly [USE_SERVER, USE_CLIENT, USE_AGNOSTIC]} */
28
+ /** @type {Directives} */
28
29
  export const directivesArray = [USE_SERVER, USE_CLIENT, USE_AGNOSTIC];
29
30
 
30
31
  // directives set
31
- /** @type {ReadonlySet<USE_SERVER | USE_CLIENT | USE_AGNOSTIC>} */
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
- // effective modules
44
- const SERVER_LOGICS_MODULE = COMMONS_SERVER_LOGICS_MODULE;
45
- const SERVER_COMPONENTS_MODULE = COMMONS_SERVER_COMPONENTS_MODULE;
46
- const SERVER_FUNCTIONS_MODULE = COMMONS_SERVER_FUNCTIONS_MODULE;
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 effective directives with effective modules
53
- export const effectiveDirectives_EffectiveModules = Object.freeze({
54
- [USE_SERVER_LOGICS]: SERVER_LOGICS_MODULE,
55
- [USE_SERVER_COMPONENTS]: SERVER_COMPONENTS_MODULE,
56
- [USE_SERVER_FUNCTIONS]: SERVER_FUNCTIONS_MODULE,
57
- [USE_CLIENT_LOGICS]: CLIENT_LOGICS_MODULE,
58
- [USE_CLIENT_COMPONENTS]: CLIENT_COMPONENTS_MODULE,
59
- [USE_AGNOSTIC_LOGICS]: AGNOSTIC_LOGICS_MODULE,
60
- [USE_AGNOSTIC_COMPONENTS]: AGNOSTIC_COMPONENTS_MODULE,
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 {USE_SERVER_LOGICS | USE_SERVER_COMPONENTS | USE_SERVER_FUNCTIONS | USE_CLIENT_LOGICS | USE_CLIENT_COMPONENTS | USE_AGNOSTIC_LOGICS | USE_AGNOSTIC_COMPONENTS} currentFileEffectiveDirective The current file's effective directive.
74
- * @param {USE_SERVER_LOGICS | USE_SERVER_COMPONENTS | USE_SERVER_FUNCTIONS | USE_CLIENT_LOGICS | USE_CLIENT_COMPONENTS | USE_AGNOSTIC_LOGICS | USE_AGNOSTIC_COMPONENTS} importedFileEffectiveDirective The imported file's effective directive.
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
- effectiveDirectives_EffectiveModules,
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 effectiveDirectives_BlockedImports = Object.freeze({
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
- /** @type {import('@typescript-eslint/utils').TSESLint.RuleModule<typeof reExportNotSameMessageId | typeof importBreaksEffectiveImportRulesMessageId | typeof useServerJSXMessageId, []>} */
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
- makeMessageFromEffectiveDirective,
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 {Readonly<import('@typescript-eslint/utils').TSESLint.RuleContext<typeof reExportNotSameMessageId | typeof importBreaksEffectiveImportRulesMessageId | typeof useServerJSXMessageId, []>>} context The ESLint rule's `context` object.
42
- * @returns {{skip: true; currentFileEffectiveDirective: undefined;} | {skip: undefined; currentFileEffectiveDirective: USE_SERVER_LOGICS | USE_SERVER_COMPONENTS | USE_SERVER_FUNCTIONS | USE_CLIENT_LOGICS | USE_CLIENT_COMPONENTS | USE_AGNOSTIC_LOGICS | USE_AGNOSTIC_COMPONENTS;}} Either an object with `skip: true` to disregard or one with the non-null `currentFileEffectiveDirective`.
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 { skip: true };
61
+ return skipTrue;
57
62
  }
58
63
 
59
- /* GETTING THE DIRECTIVE (or lack thereof) OF THE CURRENT FILE */
60
- const currentFileDirective = getDirectiveFromCurrentModule(context);
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 { skip: true };
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 { skip: true };
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 {Readonly<import('@typescript-eslint/utils').TSESLint.RuleContext<typeof reExportNotSameMessageId | typeof importBreaksEffectiveImportRulesMessageId | typeof useServerJSXMessageId, []>>} context The ESLint rule's `context` object.
94
- * @param {import('@typescript-eslint/types').TSESTree.ImportDeclaration} node The ESLint `node` of the rule's current traversal.
95
- * @returns {{skip: true; importedFileEffectiveDirective: undefined;} | {skip: undefined; importedFileEffectiveDirective: USE_SERVER_LOGICS | USE_SERVER_COMPONENTS | USE_SERVER_FUNCTIONS | USE_CLIENT_LOGICS | USE_CLIENT_COMPONENTS | USE_AGNOSTIC_LOGICS | USE_AGNOSTIC_COMPONENTS;}} Either an object with `skip: true` to disregard or one with the non-null `importedFileEffectiveDirective`.
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 { skip: true };
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 { skip: true };
119
+ if (!isImportedFileJS) return skipTrue;
112
120
 
113
- /* GETTING THE DIRECTIVE (or lack thereof) OF THE IMPORTED FILE */
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
- // GETTING THE EFFECTIVE DIRECTIVE OF THE IMPORTED FILE
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 { skip: true };
137
+ return skipTrue;
128
138
  }
129
139
 
130
- // For now skipping on both "does not operate" (which should ignore) and "fails" albeit with console.error (which should crash).
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 {Readonly<import('@typescript-eslint/utils').TSESLint.RuleContext<typeof reExportNotSameMessageId | typeof importBreaksEffectiveImportRulesMessageId | typeof useServerJSXMessageId, []>>} context The ESLint rule's `context` object.
139
- * @param {import('@typescript-eslint/types').TSESTree.ImportDeclaration} node The ESLint `node` of the rule's current traversal.
140
- * @param {USE_SERVER_LOGICS | USE_SERVER_COMPONENTS | USE_SERVER_FUNCTIONS | USE_CLIENT_LOGICS | USE_CLIENT_COMPONENTS | USE_AGNOSTIC_LOGICS | USE_AGNOSTIC_COMPONENTS} currentFileEffectiveDirective The current file's effective directive.
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]: makeMessageFromEffectiveDirective(
163
- currentFileEffectiveDirective
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 {Readonly<import('@typescript-eslint/utils').TSESLint.RuleContext<typeof reExportNotSameMessageId | typeof importBreaksEffectiveImportRulesMessageId | typeof useServerJSXMessageId, []>>} context The ESLint rule's `context` object.
178
- * @param {import('@typescript-eslint/types').TSESTree.ExportNamedDeclaration | import('@typescript-eslint/types').TSESTree.ExportAllDeclaration} node The ESLint `node` of the rule's current traversal.
179
- * @param {USE_SERVER_LOGICS | USE_SERVER_COMPONENTS | USE_SERVER_FUNCTIONS | USE_CLIENT_LOGICS | USE_CLIENT_COMPONENTS | USE_AGNOSTIC_LOGICS | USE_AGNOSTIC_COMPONENTS} currentFileEffectiveDirective The current file's effective directive.
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) => {