eslint-plugin-use-agnostic 0.8.0 → 0.9.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/README.md +2 -2
- package/library/_commons/constants/bases.js +4 -2
- package/library/_commons/utilities/helpers.js +20 -8
- package/library/agnostic20/_commons/constants/bases.js +2 -1
- package/library/agnostic20/_commons/utilities/flows.js +7 -10
- package/library/agnostic20/config.js +2 -0
- package/library/directive21/_commons/constants/bases.js +16 -14
- package/library/directive21/_commons/utilities/flows.js +27 -44
- package/library/directive21/_commons/utilities/helpers.js +41 -7
- package/library/directive21/config.js +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ Only the first line of code in a file is observed for the presence of a directiv
|
|
|
85
85
|
|
|
86
86
|
Aliased import paths are resolved only if your ESLint config file and your `tsconfig.json` file are in the same directory. (At least to my knowledge.)
|
|
87
87
|
|
|
88
|
-
It is up to you to confirm that your Agnostic Modules are indeed agnostic, meaning that they do
|
|
88
|
+
It is up to you to confirm that your Agnostic Modules are indeed agnostic, meaning that they do have neither server- nor client-side code. `eslint-plugin-use-agnostic`, at least at this time, does not do this verification for you.
|
|
89
89
|
|
|
90
90
|
It is also up to you to ensure, as outlined above, that **you do not mix** exporting React components with exporting other logics within the same module. Separating exporting React components from their own modules ending with a JSX extension, from other logics from modules that don't end with a JSX extension, is crucial for distinguishing between Logics Modules and Components Modules.
|
|
91
91
|
|
|
@@ -109,4 +109,4 @@ But not having a directive to distinguish between 1. non-special Server Modules
|
|
|
109
109
|
|
|
110
110
|
This is what the 'use agnostic' directive solves. It clearly marks a module to be an Agnostic Module. And if a module that used to lack a directive can now be marked as an Agnostic Module, this allows modules without a directive to finally, truly be Server Modules by default. And eslint-plugin-use-agnostic can work from there.
|
|
111
111
|
|
|
112
|
-
A lot more work
|
|
112
|
+
A lot more work has to be done, and a lot of it unfortunately can only be optimized deeper into React's innerworkings. But if the introduction of 'use agnostic' can already create such powerful static analysis, imagine what it could produce if only it were incorporated into React as an official directive of the Fullstack React Architecture.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* plugin names */
|
|
2
2
|
// use-agnostic
|
|
3
3
|
export const useAgnosticPluginName = "use-agnostic";
|
|
4
|
-
// crossingStrategies
|
|
4
|
+
// crossingStrategies (canceled)
|
|
5
5
|
export const strategiesPluginName = "strategies";
|
|
6
6
|
|
|
7
7
|
/* config names */
|
|
@@ -18,7 +18,7 @@ export const enforceEffectiveDirectivesRuleName =
|
|
|
18
18
|
export const enforceCommentedDirectivesRuleName =
|
|
19
19
|
"enforce-commented-directives-import-rules";
|
|
20
20
|
|
|
21
|
-
// crossingStrategies
|
|
21
|
+
// crossingStrategies (canceled)
|
|
22
22
|
export const verifySpecifierImportRuleName =
|
|
23
23
|
"verify-specifier-import-export-same-strategy";
|
|
24
24
|
export const verifyDefaultImportRuleName =
|
|
@@ -44,6 +44,8 @@ export const exportNotStrategized =
|
|
|
44
44
|
"export-from-use-agnostic-strategies-not-strategized";
|
|
45
45
|
|
|
46
46
|
// all "resolved" directives (from AIA/agnostic20 & DFA/directive21)
|
|
47
|
+
// - AIA: Agnostic-Included Architecture (agnostic20)
|
|
48
|
+
// - DFA: Directive-First Architecture (directive21)
|
|
47
49
|
export const USE_SERVER_LOGICS = "use server logics";
|
|
48
50
|
export const USE_CLIENT_LOGICS = "use client logics";
|
|
49
51
|
export const USE_AGNOSTIC_LOGICS = "use agnostic logics";
|
|
@@ -97,6 +97,18 @@ export const getImportedFileFirstLine = (resolvedImportPath) => {
|
|
|
97
97
|
return importedFileFirstLine;
|
|
98
98
|
};
|
|
99
99
|
|
|
100
|
+
/* highlightFirstLineOfCode */
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Gets the coordinates for the first line of code of a file.
|
|
104
|
+
* @param {import('@typescript-eslint/utils').TSESLint.RuleContext} context An ESLint rule's `context` object.
|
|
105
|
+
* @returns The `context.report` `loc`-compatible coordinates for the first line of code of a file.
|
|
106
|
+
*/
|
|
107
|
+
export const highlightFirstLineOfCode = (context) => ({
|
|
108
|
+
start: { line: 1, column: 0 },
|
|
109
|
+
end: { line: 1, column: context.sourceCode.lines[0].length },
|
|
110
|
+
});
|
|
111
|
+
|
|
100
112
|
/* isImportBlocked */
|
|
101
113
|
|
|
102
114
|
/**
|
|
@@ -110,7 +122,7 @@ export const isImportBlocked = (
|
|
|
110
122
|
// 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.
|
|
111
123
|
resolvedDirectives_blockedImports,
|
|
112
124
|
currentFileResolvedDirective,
|
|
113
|
-
importedFileResolvedDirective
|
|
125
|
+
importedFileResolvedDirective
|
|
114
126
|
) =>
|
|
115
127
|
resolvedDirectives_blockedImports[currentFileResolvedDirective]
|
|
116
128
|
.map((e) => e.blockedImport)
|
|
@@ -123,12 +135,12 @@ export const isImportBlocked = (
|
|
|
123
135
|
* @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.
|
|
124
136
|
* @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.
|
|
125
137
|
* @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.
|
|
126
|
-
* @returns {string} Returns "[Current file 'resolved' modules] are not allowed to import [imported file 'resolved' modules]"
|
|
138
|
+
* @returns {string} Returns "[Current file 'resolved' modules] are not allowed to import [imported file 'resolved' modules]."
|
|
127
139
|
*/
|
|
128
140
|
export const makeIntroForSpecificViolationMessage = (
|
|
129
141
|
resolvedDirectives_ResolvedModules,
|
|
130
142
|
currentFileResolvedDirective,
|
|
131
|
-
importedFileResolvedDirective
|
|
143
|
+
importedFileResolvedDirective
|
|
132
144
|
) =>
|
|
133
145
|
`${resolvedDirectives_ResolvedModules[currentFileResolvedDirective]}s ${ARE_NOT_ALLOWED_TO_IMPORT} ${resolvedDirectives_ResolvedModules[importedFileResolvedDirective]}s.`;
|
|
134
146
|
|
|
@@ -144,14 +156,14 @@ export const makeIntroForSpecificViolationMessage = (
|
|
|
144
156
|
export const makeMessageFromResolvedDirective = (
|
|
145
157
|
resolvedDirectives_ResolvedModules,
|
|
146
158
|
resolvedDirectives_blockedImports,
|
|
147
|
-
resolvedDirective
|
|
159
|
+
resolvedDirective
|
|
148
160
|
) => {
|
|
149
161
|
const effectiveModule = resolvedDirectives_ResolvedModules[resolvedDirective];
|
|
150
162
|
const effectiveModulesString = effectiveModule + "s"; // plural
|
|
151
163
|
|
|
152
164
|
const blockedImports =
|
|
153
165
|
resolvedDirectives_blockedImports[resolvedDirective].map(
|
|
154
|
-
(e) => e.blockedImport
|
|
166
|
+
(e) => e.blockedImport
|
|
155
167
|
) || [];
|
|
156
168
|
|
|
157
169
|
if (blockedImports.length === 0) {
|
|
@@ -159,7 +171,7 @@ export const makeMessageFromResolvedDirective = (
|
|
|
159
171
|
}
|
|
160
172
|
|
|
161
173
|
const blockedEffectiveModules = blockedImports.map(
|
|
162
|
-
(e) => resolvedDirectives_ResolvedModules[e] + "s"
|
|
174
|
+
(e) => resolvedDirectives_ResolvedModules[e] + "s" // plural
|
|
163
175
|
);
|
|
164
176
|
|
|
165
177
|
const blockedEffectiveModulesString =
|
|
@@ -184,8 +196,8 @@ export const makeMessageFromResolvedDirective = (
|
|
|
184
196
|
export const findSpecificViolationMessage = (
|
|
185
197
|
resolvedDirectives_blockedImports,
|
|
186
198
|
currentFileResolvedDirective,
|
|
187
|
-
importedFileResolvedDirective
|
|
199
|
+
importedFileResolvedDirective
|
|
188
200
|
) =>
|
|
189
201
|
resolvedDirectives_blockedImports[currentFileResolvedDirective].find(
|
|
190
|
-
(e) => e.blockedImport === importedFileResolvedDirective
|
|
202
|
+
(e) => e.blockedImport === importedFileResolvedDirective
|
|
191
203
|
).message;
|
|
@@ -65,6 +65,7 @@ export const directivesSet = new Set([USE_SERVER, USE_CLIENT, USE_AGNOSTIC]);
|
|
|
65
65
|
|
|
66
66
|
/* from the getDirectiveFromImportedModule utility */
|
|
67
67
|
|
|
68
|
+
/** @type {readonly [USE_SERVER, USE_CLIENT, USE_AGNOSTIC]} */
|
|
68
69
|
export const directivesArray = Array.from(directivesSet);
|
|
69
70
|
|
|
70
71
|
/* from the isImportBlocked utility */
|
|
@@ -75,7 +76,7 @@ export const directivesArray = Array.from(directivesSet);
|
|
|
75
76
|
* Makes the intro for each specific import rule violation messages.
|
|
76
77
|
* @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.
|
|
77
78
|
* @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.
|
|
78
|
-
* @returns {string} Returns "[Current file effective modules] are not allowed to import [imported file effective modules]"
|
|
79
|
+
* @returns {string} Returns "[Current file effective modules] are not allowed to import [imported file effective modules]."
|
|
79
80
|
*/
|
|
80
81
|
const makeIntroForSpecificViolationMessage = (
|
|
81
82
|
currentFileEffectiveDirective,
|
|
@@ -20,7 +20,10 @@ import {
|
|
|
20
20
|
specificViolationMessage,
|
|
21
21
|
} from "../constants/bases.js";
|
|
22
22
|
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
resolveImportPath,
|
|
25
|
+
highlightFirstLineOfCode,
|
|
26
|
+
} from "../../../_commons/utilities/helpers.js";
|
|
24
27
|
import {
|
|
25
28
|
getDirectiveFromCurrentModule,
|
|
26
29
|
getDirectiveFromImportedModule,
|
|
@@ -61,10 +64,7 @@ export const currentFileFlow = (context) => {
|
|
|
61
64
|
currentFileExtension.endsWith("x")
|
|
62
65
|
) {
|
|
63
66
|
context.report({
|
|
64
|
-
loc:
|
|
65
|
-
start: { line: 1, column: 0 },
|
|
66
|
-
end: { line: 1, column: context.sourceCode.lines[0].length },
|
|
67
|
-
},
|
|
67
|
+
loc: highlightFirstLineOfCode(context),
|
|
68
68
|
messageId: useServerJSXMessageId,
|
|
69
69
|
});
|
|
70
70
|
return { skip: true };
|
|
@@ -91,9 +91,6 @@ export const currentFileFlow = (context) => {
|
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* The flow that is shared between import and re-export traversals to obtain the import file's effective directive.
|
|
94
|
-
* @param {string} currentDir Directory of the file containing the import (from `path.dirname(context.filename)`).
|
|
95
|
-
* @param {string} importPath The import specifier (e.g., `@/components/Button` or `./utils`).
|
|
96
|
-
* @param {string} cwd Project root (from `context.cwd`). Caveat: only as an assumption currently.
|
|
97
94
|
* @param {Readonly<import('@typescript-eslint/utils').TSESLint.RuleContext<typeof reExportNotSameMessageId | typeof importBreaksEffectiveImportRulesMessageId | typeof useServerJSXMessageId, []>>} context The ESLint rule's `context` object.
|
|
98
95
|
* @param {import('@typescript-eslint/types').TSESTree.ImportDeclaration} node The ESLint `node` of the rule's current traversal.
|
|
99
96
|
* @returns {{skip: true; importedFileEffectiveDirective: undefined; resolvedImportPath: 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; resolvedImportPath: string;}} Returns either an object with `skip: true` to disregard or one with the non-null `importedFileEffectiveDirective`.
|
|
@@ -202,9 +199,9 @@ export const reExportsFlow = (context, node, currentFileEffectiveDirective) => {
|
|
|
202
199
|
node,
|
|
203
200
|
messageId: reExportNotSameMessageId,
|
|
204
201
|
data: {
|
|
205
|
-
// currentFileEffectiveDirective
|
|
202
|
+
// currentFileEffectiveDirective:
|
|
206
203
|
currentFileEffectiveDirective,
|
|
207
|
-
// importedFileEffectiveDirective
|
|
204
|
+
// importedFileEffectiveDirective:
|
|
208
205
|
importedFileEffectiveDirective,
|
|
209
206
|
},
|
|
210
207
|
});
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Makes the agnostic20 config for the use-agnostic ESLint plugin.
|
|
12
|
+
* @param {import('eslint').ESLint.Plugin} plugin The use-agnostic ESLint plugin itself.
|
|
13
|
+
* @returns The agnostic20 config's name as a key and its config as its value.
|
|
12
14
|
*/
|
|
13
15
|
export const makeAgnostic20Config = (plugin) => ({
|
|
14
16
|
[agnostic20ConfigName]: defineConfig([
|
|
@@ -86,27 +86,29 @@ export const directivesSet = new Set([
|
|
|
86
86
|
|
|
87
87
|
/* from the getCommentedDirectiveFromImportedModule utility */
|
|
88
88
|
|
|
89
|
+
/** @type {readonly [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]} */
|
|
89
90
|
export const directivesArray = Array.from(directivesSet);
|
|
90
91
|
|
|
91
92
|
/* commentedDirectives_4RawImplementations */
|
|
92
93
|
|
|
93
|
-
//
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
// all formatting styles as an array of [prefix, quote, suffix]
|
|
95
|
+
const commentStyles = [
|
|
96
|
+
[`// `, `'`, ``], // V1: `// 'directive'`
|
|
97
|
+
[`// `, `"`, ``], // V2: `// "directive"`
|
|
98
|
+
[`/* `, `'`, ` */`], // V3: `/* 'directive' */`
|
|
99
|
+
[`/* `, `"`, ` */`], // V4: `/* "directive" */`
|
|
100
|
+
];
|
|
98
101
|
|
|
99
102
|
/**
|
|
100
103
|
* Makes the array of all four accepted commented directive implementations on a directive basis.
|
|
101
|
-
* @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} directive
|
|
102
|
-
* @returns
|
|
104
|
+
* @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} directive The commented directive.
|
|
105
|
+
* @returns {string[]} The array of formatted commented directives.
|
|
103
106
|
*/
|
|
104
|
-
const make4RawImplementations = (directive) =>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
];
|
|
107
|
+
const make4RawImplementations = (directive) =>
|
|
108
|
+
commentStyles.map(
|
|
109
|
+
([prefix, quote, suffix]) =>
|
|
110
|
+
`${prefix}${quote}${directive}${quote}${suffix}`
|
|
111
|
+
);
|
|
110
112
|
|
|
111
113
|
// mapped commented directives to their 4 raw implementations
|
|
112
114
|
export const commentedDirectives_4RawImplementations = Object.freeze({
|
|
@@ -152,7 +154,7 @@ export const commentedStrategies_CommentedDirectives = Object.freeze({
|
|
|
152
154
|
* Makes the intro for each specific import rule violation messages.
|
|
153
155
|
* @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} currentFileCommentedDirective The current file's commented directive.
|
|
154
156
|
* @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} importedFileCommentedDirective The imported file's commented directive.
|
|
155
|
-
* @returns {string} Returns "[Current file commented modules] are not allowed to import [imported file commented modules]"
|
|
157
|
+
* @returns {string} Returns "[Current file commented modules] are not allowed to import [imported file commented modules]."
|
|
156
158
|
*/
|
|
157
159
|
const makeIntroForSpecificViolationMessage = (
|
|
158
160
|
currentFileCommentedDirective,
|
|
@@ -28,7 +28,10 @@ import {
|
|
|
28
28
|
specificFailure,
|
|
29
29
|
} from "../constants/bases.js";
|
|
30
30
|
|
|
31
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
resolveImportPath,
|
|
33
|
+
highlightFirstLineOfCode,
|
|
34
|
+
} from "../../../_commons/utilities/helpers.js";
|
|
32
35
|
import {
|
|
33
36
|
getCommentedDirectiveFromCurrentModule,
|
|
34
37
|
getVerifiedCommentedDirective,
|
|
@@ -37,6 +40,7 @@ import {
|
|
|
37
40
|
makeMessageFromCommentedDirective,
|
|
38
41
|
findSpecificViolationMessage,
|
|
39
42
|
getStrategizedDirective,
|
|
43
|
+
addressDirectiveIfAgnosticStrategies,
|
|
40
44
|
} from "./helpers.js";
|
|
41
45
|
|
|
42
46
|
/* currentFileFlow */
|
|
@@ -67,10 +71,7 @@ export const currentFileFlow = (context) => {
|
|
|
67
71
|
// reports if there is no directive or no valid directive (same, but eventually no directive could have defaults)
|
|
68
72
|
if (!commentedDirective) {
|
|
69
73
|
context.report({
|
|
70
|
-
loc:
|
|
71
|
-
start: { line: 1, column: 0 },
|
|
72
|
-
end: { line: 1, column: context.sourceCode.lines[0].length },
|
|
73
|
-
},
|
|
74
|
+
loc: highlightFirstLineOfCode(context),
|
|
74
75
|
messageId: noCommentedDirective,
|
|
75
76
|
});
|
|
76
77
|
return { skip: true };
|
|
@@ -84,10 +85,7 @@ export const currentFileFlow = (context) => {
|
|
|
84
85
|
// reports if the verification failed
|
|
85
86
|
if (!verifiedCommentedDirective) {
|
|
86
87
|
context.report({
|
|
87
|
-
loc:
|
|
88
|
-
start: { line: 1, column: 0 },
|
|
89
|
-
end: { line: 1, column: context.sourceCode.lines[0].length },
|
|
90
|
-
},
|
|
88
|
+
loc: highlightFirstLineOfCode(context),
|
|
91
89
|
messageId: commentedDirectiveVerificationFailed,
|
|
92
90
|
data: {
|
|
93
91
|
[specificFailure]:
|
|
@@ -139,10 +137,11 @@ const importedFileFlow = (context, node) => {
|
|
|
139
137
|
}
|
|
140
138
|
|
|
141
139
|
/* GETTING THE CORRECT DIRECTIVE INTERPRETATION OF STRATEGY FOR AGNOSTIC STRATEGIES MODULES IMPORTS.
|
|
142
|
-
(The Directive-First Architecture does not check whether the export and import Strategies are the same at this time, meaning
|
|
140
|
+
(The Directive-First Architecture does not check whether the export and import Strategies are the same at this time, meaning an @clientLogics strategy could be wrongly imported and interpreted as an @serverLogics strategy.
|
|
143
141
|
|
|
144
|
-
After a short attempt, this feature is currently
|
|
142
|
+
After a short attempt, this feature is currently canceled, mainly since the amount of work it will require will not be able to be transferred in a future where commented strategies will actually be real syntax.
|
|
145
143
|
|
|
144
|
+
// (Consequently, details below are currently at the stage of wishful thinking.)
|
|
146
145
|
However, Strategy exports are planned to be linting in the future within their own Agnostic Strategies Modules to ensure they respect import rules within their own scopes. It may also become possible to check whether the export and import Strategies are the same in the future when identifiers are defined and the same, especially for components modules where a convention could be to for all non-type export to be named and PascalCase.) */
|
|
147
146
|
if (importedFileCommentedDirective === USE_AGNOSTIC_STRATEGIES) {
|
|
148
147
|
const importingFileCommentedDirective = getStrategizedDirective(
|
|
@@ -222,27 +221,29 @@ export const allExportsFlow = (
|
|
|
222
221
|
// does not operate on `export type`
|
|
223
222
|
if (node.exportKind === "type") return;
|
|
224
223
|
|
|
225
|
-
|
|
224
|
+
// regular exports scenarios
|
|
225
|
+
if (!node.source) {
|
|
226
|
+
addressDirectiveIfAgnosticStrategies(
|
|
227
|
+
context,
|
|
228
|
+
node,
|
|
229
|
+
currentFileCommentedDirective
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
// re-exports scenarios
|
|
233
|
+
else {
|
|
226
234
|
const result = importedFileFlow(context, node);
|
|
227
235
|
|
|
228
236
|
if (result.skip) return;
|
|
229
237
|
const { importedFileCommentedDirective } = result;
|
|
230
238
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (exportStrategizedDirective === null) {
|
|
237
|
-
context.report({
|
|
238
|
-
node,
|
|
239
|
-
messageId: exportNotStrategized,
|
|
240
|
-
});
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
239
|
+
const addressedDirective = addressDirectiveIfAgnosticStrategies(
|
|
240
|
+
context,
|
|
241
|
+
node,
|
|
242
|
+
currentFileCommentedDirective
|
|
243
|
+
);
|
|
243
244
|
|
|
244
|
-
|
|
245
|
-
|
|
245
|
+
if (!addressedDirective) return;
|
|
246
|
+
else currentFileCommentedDirective = addressedDirective;
|
|
246
247
|
|
|
247
248
|
if (currentFileCommentedDirective !== importedFileCommentedDirective) {
|
|
248
249
|
context.report({
|
|
@@ -255,24 +256,6 @@ export const allExportsFlow = (
|
|
|
255
256
|
importedFileCommentedDirective,
|
|
256
257
|
},
|
|
257
258
|
});
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
} else {
|
|
261
|
-
// ignores if this is NOT an Agnostic Strategies Module
|
|
262
|
-
// verifies current node export strategy if "use agnostic strategies"
|
|
263
|
-
if (currentFileCommentedDirective === USE_AGNOSTIC_STRATEGIES) {
|
|
264
|
-
const exportStrategizedDirective = getStrategizedDirective(context, node);
|
|
265
|
-
|
|
266
|
-
if (exportStrategizedDirective === null) {
|
|
267
|
-
context.report({
|
|
268
|
-
node,
|
|
269
|
-
messageId: exportNotStrategized,
|
|
270
|
-
});
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// just to emphasize that this is the same short flow from above
|
|
275
|
-
currentFileCommentedDirective = exportStrategizedDirective;
|
|
276
259
|
}
|
|
277
260
|
}
|
|
278
261
|
};
|
|
@@ -190,9 +190,11 @@ export const getCommentedDirectiveFromImportedModule = (resolvedImportPath) => {
|
|
|
190
190
|
const importedFileFirstLine = getImportedFileFirstLine(resolvedImportPath);
|
|
191
191
|
|
|
192
192
|
// sees if the first line includes any of the directives and finds the directive that it includes
|
|
193
|
+
/** @type {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 | ""} */
|
|
193
194
|
let includedDirective = "";
|
|
194
|
-
const
|
|
195
|
-
|
|
195
|
+
const firstLength = directivesArray.length;
|
|
196
|
+
|
|
197
|
+
for (let i = 0; i < firstLength; i++) {
|
|
196
198
|
const directive = directivesArray[i];
|
|
197
199
|
if (importedFileFirstLine.includes(directive)) {
|
|
198
200
|
includedDirective = directive;
|
|
@@ -203,12 +205,14 @@ export const getCommentedDirectiveFromImportedModule = (resolvedImportPath) => {
|
|
|
203
205
|
// returns null early if there is none of the directives in the first line
|
|
204
206
|
if (includedDirective === "") return null;
|
|
205
207
|
|
|
208
|
+
/** @type {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 | ""} */
|
|
206
209
|
let importFileDirective = "";
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
const rawImplementations =
|
|
211
|
+
commentedDirectives_4RawImplementations[includedDirective];
|
|
212
|
+
const secondLength = rawImplementations.length;
|
|
213
|
+
|
|
214
|
+
for (let i = 0; i < secondLength; i++) {
|
|
215
|
+
const raw = rawImplementations[i];
|
|
212
216
|
if (raw === importedFileFirstLine) {
|
|
213
217
|
importFileDirective = includedDirective;
|
|
214
218
|
break;
|
|
@@ -289,3 +293,33 @@ export const findSpecificViolationMessage = (
|
|
|
289
293
|
currentFileCommentedDirective,
|
|
290
294
|
importedFileCommentedDirective
|
|
291
295
|
);
|
|
296
|
+
|
|
297
|
+
/* addressDirectiveIfAgnosticStrategies */
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Verifies the current node's export strategy if `"use agnostic strategies"` by reporting `exportNotStrategized` in case an export is not strategized in an Agnostic Strategies Module.
|
|
301
|
+
* @param {Readonly<import('@typescript-eslint/utils').TSESLint.RuleContext<typeof reExportNotSameMessageId | typeof importBreaksCommentedImportRulesMessageId | typeof noCommentedDirective | typeof commentedDirectiveVerificationFailed | typeof importNotStrategized | typeof exportNotStrategized, []>>} context The ESLint rule's `context` object.
|
|
302
|
+
* @param {import('@typescript-eslint/types').TSESTree.ExportNamedDeclaration | import('@typescript-eslint/types').TSESTree.ExportAllDeclaration | import('@typescript-eslint/types').TSESTree.ExportDefaultDeclaration} node The ESLint `node` of the rule's current traversal.
|
|
303
|
+
* @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} currentFileCommentedDirective The current file's commented directive.
|
|
304
|
+
* @returns The commented directive, the addressed strategy (as a commented directive) or null in case of failure.
|
|
305
|
+
*/
|
|
306
|
+
export const addressDirectiveIfAgnosticStrategies = (
|
|
307
|
+
context,
|
|
308
|
+
node,
|
|
309
|
+
currentFileCommentedDirective
|
|
310
|
+
) => {
|
|
311
|
+
// ignores if not addressing an Agnostic Strategies Module
|
|
312
|
+
if (currentFileCommentedDirective !== USE_AGNOSTIC_STRATEGIES)
|
|
313
|
+
return currentFileCommentedDirective;
|
|
314
|
+
|
|
315
|
+
const exportStrategizedDirective = getStrategizedDirective(context, node);
|
|
316
|
+
|
|
317
|
+
if (exportStrategizedDirective === null) {
|
|
318
|
+
context.report({
|
|
319
|
+
node,
|
|
320
|
+
messageId: exportNotStrategized,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return exportStrategizedDirective; // null indicates failure
|
|
325
|
+
};
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Makes the directive21 config for the use-agnostic ESLint plugin.
|
|
12
|
+
* @param {import('eslint').ESLint.Plugin} plugin The use-agnostic ESLint plugin itself.
|
|
13
|
+
* @returns The directive21 config's name as a key and its config as its value.
|
|
12
14
|
*/
|
|
13
15
|
export const makeDirective21Config = (plugin) => ({
|
|
14
16
|
[directive21ConfigName]: defineConfig([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-use-agnostic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Highlights problematic server-client imports in projects made with the Fullstack React Architecture.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"url": "git+https://github.com/LutherTS/eslint-plugin-use-agnostic.git"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
|
-
"test": "node tests/agnostic20/import-rules.test.js"
|
|
34
|
+
"test": "node tests/agnostic20/import-rules.test.js && node tests/directive21/import-rules.test.js"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"tsconfig-paths": "^4.2.0",
|