eslint-plugin-use-agnostic 0.9.7 → 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 +10 -3
- package/library/_commons/constants/bases.js +0 -1
- package/library/_commons/utilities/helpers.js +7 -7
- package/library/agnostic20/_commons/constants/bases.js +20 -14
- package/library/agnostic20/_commons/utilities/flows.js +16 -13
- package/library/agnostic20/_commons/utilities/helpers.js +16 -45
- package/library/directive21/_commons/constants/bases.js +16 -3
- package/library/directive21/_commons/utilities/flows.js +15 -10
- package/library/directive21/_commons/utilities/helpers.js +23 -38
- 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
|
+
// enforceEffectiveDirectivesRuleName
|
|
23
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
|
},
|
|
@@ -104,7 +104,6 @@ export const MJS = ".mjs";
|
|
|
104
104
|
export const CJS = ".cjs";
|
|
105
105
|
|
|
106
106
|
// JavaScript/TypeScript extensions array
|
|
107
|
-
// /** @type {readonly [TSX, TS, JSX, JS, MJS, CJS]} */
|
|
108
107
|
/** @type {Extensions} */
|
|
109
108
|
export const EXTENSIONS = [TSX, TS, JSX, JS, MJS, CJS]; // In priority order
|
|
110
109
|
|
|
@@ -6,7 +6,6 @@ import { loadConfig, createMatchPath } from "tsconfig-paths";
|
|
|
6
6
|
import { EXTENSIONS, ARE_NOT_ALLOWED_TO_IMPORT } from "../constants/bases.js";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* @typedef {import('../../../types/_commons/typedefs').ResolvedDirective} ResolvedDirective
|
|
10
9
|
* @typedef {import('../../../types/_commons/typedefs').ResolvedDirectives_ResolvedModules} ResolvedDirectives_ResolvedModules
|
|
11
10
|
* @typedef {import('../../../types/_commons/typedefs').CurrentFileResolvedDirective} CurrentFileResolvedDirective
|
|
12
11
|
* @typedef {import('../../../types/_commons/typedefs').Context<string, readonly unknown[]>} Context
|
|
@@ -148,26 +147,27 @@ export const makeIntroForSpecificViolationMessage = (
|
|
|
148
147
|
) =>
|
|
149
148
|
`${resolvedDirectives_resolvedModules[currentFileResolvedDirective]}s ${ARE_NOT_ALLOWED_TO_IMPORT} ${resolvedDirectives_resolvedModules[importedFileResolvedDirective]}s.`;
|
|
150
149
|
|
|
151
|
-
/*
|
|
150
|
+
/* makeMessageFromCurrentFileResolvedDirective */
|
|
152
151
|
|
|
153
152
|
/**
|
|
154
153
|
* Lists in an message the "resolved" modules incompatible with a "resolved" module based on its "resolved" directive.
|
|
155
154
|
* @template {CurrentFileResolvedDirective} T
|
|
156
155
|
* @param {ResolvedDirectives_ResolvedModules} resolvedDirectives_resolvedModules The resolved modules object, either for agnostic20 or for directive21.
|
|
157
156
|
* @param {ResolvedDirectives_BlockedImports<T>} resolvedDirectives_blockedImports The blocked imports object, either for agnostic20 or for directive21.
|
|
158
|
-
* @param {T}
|
|
157
|
+
* @param {T} currentFileResolvedDirective The "resolved" directive of the "resolved" module.
|
|
159
158
|
* @returns The message listing the incompatible "resolved" modules.
|
|
160
159
|
*/
|
|
161
|
-
export const
|
|
160
|
+
export const makeMessageFromCurrentFileResolvedDirective = (
|
|
162
161
|
resolvedDirectives_resolvedModules,
|
|
163
162
|
resolvedDirectives_blockedImports,
|
|
164
|
-
|
|
163
|
+
currentFileResolvedDirective
|
|
165
164
|
) => {
|
|
166
|
-
const effectiveModule =
|
|
165
|
+
const effectiveModule =
|
|
166
|
+
resolvedDirectives_resolvedModules[currentFileResolvedDirective];
|
|
167
167
|
const effectiveModulesString = effectiveModule + "s"; // plural
|
|
168
168
|
|
|
169
169
|
const blockedImports =
|
|
170
|
-
resolvedDirectives_blockedImports[
|
|
170
|
+
resolvedDirectives_blockedImports[currentFileResolvedDirective].map(
|
|
171
171
|
(e) => e.blockedImport
|
|
172
172
|
) || [];
|
|
173
173
|
|
|
@@ -15,10 +15,11 @@ import { makeIntroForSpecificViolationMessage as commonsMakeIntroForSpecificViol
|
|
|
15
15
|
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').Directive} Directive
|
|
16
16
|
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').Directives} Directives
|
|
17
17
|
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').EffectiveDirective} EffectiveDirective
|
|
18
|
+
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').Directives_EffectiveDirectives} Directives_EffectiveDirectives
|
|
18
19
|
*/
|
|
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";
|
|
@@ -40,28 +41,33 @@ 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
|
|
|
44
|
+
// module kinds
|
|
45
|
+
export const LOGICS = "logics";
|
|
46
|
+
export const COMPONENTS = "components";
|
|
47
|
+
export const FUNCTIONS = "functions";
|
|
48
|
+
|
|
43
49
|
// mapping directives with effective directives
|
|
44
|
-
/** @type {
|
|
50
|
+
/** @type {Directives_EffectiveDirectives} */
|
|
45
51
|
export const directives_effectiveDirectives = Object.freeze({
|
|
46
52
|
[NO_DIRECTIVE]: {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
[LOGICS]: USE_SERVER_LOGICS,
|
|
54
|
+
[COMPONENTS]: USE_SERVER_COMPONENTS,
|
|
55
|
+
[FUNCTIONS]: null,
|
|
50
56
|
},
|
|
51
57
|
[USE_SERVER]: {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
[LOGICS]: null,
|
|
59
|
+
[COMPONENTS]: null,
|
|
60
|
+
[FUNCTIONS]: USE_SERVER_FUNCTIONS,
|
|
55
61
|
},
|
|
56
62
|
[USE_CLIENT]: {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
[LOGICS]: USE_CLIENT_LOGICS,
|
|
64
|
+
[COMPONENTS]: USE_CLIENT_COMPONENTS,
|
|
65
|
+
[FUNCTIONS]: null,
|
|
60
66
|
},
|
|
61
67
|
[USE_AGNOSTIC]: {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
68
|
+
[LOGICS]: USE_AGNOSTIC_LOGICS,
|
|
69
|
+
[COMPONENTS]: USE_AGNOSTIC_COMPONENTS,
|
|
70
|
+
[FUNCTIONS]: null,
|
|
65
71
|
},
|
|
66
72
|
});
|
|
67
73
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
skip,
|
|
9
9
|
} from "../../../_commons/constants/bases.js";
|
|
10
10
|
import {
|
|
11
|
+
NO_DIRECTIVE,
|
|
11
12
|
USE_SERVER,
|
|
12
13
|
// currentFileEffectiveDirective,
|
|
13
14
|
// importedFileEffectiveDirective,
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
getDirectiveFromImportedModule,
|
|
25
26
|
getEffectiveDirective,
|
|
26
27
|
isImportBlocked,
|
|
27
|
-
|
|
28
|
+
makeMessageFromCurrentFileEffectiveDirective,
|
|
28
29
|
findSpecificViolationMessage,
|
|
29
30
|
} from "./helpers.js";
|
|
30
31
|
|
|
@@ -38,7 +39,6 @@ import {
|
|
|
38
39
|
|
|
39
40
|
/* currentFileFlow */
|
|
40
41
|
|
|
41
|
-
// {{skip: true; currentFileEffectiveDirective: undefined;} | {skip: undefined; currentFileEffectiveDirective: EffectiveDirective;}}
|
|
42
42
|
/**
|
|
43
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.
|
|
44
44
|
* @param {Context} context The ESLint rule's `context` object.
|
|
@@ -61,8 +61,9 @@ export const currentFileFlow = (context) => {
|
|
|
61
61
|
return skipTrue;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
const currentFileDirective =
|
|
64
|
+
// GETTING THE DIRECTIVE (or lack thereof) OF THE CURRENT FILE
|
|
65
|
+
const currentFileDirective =
|
|
66
|
+
getDirectiveFromCurrentModule(context) ?? NO_DIRECTIVE;
|
|
66
67
|
|
|
67
68
|
// reports if a file marked "use server" has a JSX extension
|
|
68
69
|
if (
|
|
@@ -76,7 +77,7 @@ export const currentFileFlow = (context) => {
|
|
|
76
77
|
return skipTrue;
|
|
77
78
|
}
|
|
78
79
|
|
|
79
|
-
// GETTING THE EFFECTIVE DIRECTIVE OF THE CURRENT FILE
|
|
80
|
+
// GETTING THE EFFECTIVE DIRECTIVE (no lack thereof) OF THE CURRENT FILE
|
|
80
81
|
const currentFileEffectiveDirective = getEffectiveDirective(
|
|
81
82
|
currentFileDirective,
|
|
82
83
|
currentFileExtension
|
|
@@ -93,7 +94,6 @@ export const currentFileFlow = (context) => {
|
|
|
93
94
|
|
|
94
95
|
/* importedFileFlow */
|
|
95
96
|
|
|
96
|
-
// {{skip: true; importedFileEffectiveDirective: undefined;} | {skip: undefined; importedFileEffectiveDirective: EffectiveDirective;}}
|
|
97
97
|
/**
|
|
98
98
|
* The flow that is shared between import and re-export traversals to obtain the import file's effective directive.
|
|
99
99
|
* @param {Context} context The ESLint rule's `context` object.
|
|
@@ -118,12 +118,14 @@ const importedFileFlow = (context, node) => {
|
|
|
118
118
|
);
|
|
119
119
|
if (!isImportedFileJS) return skipTrue;
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
// GETTING THE DIRECTIVE (or lack thereof) OF THE IMPORTED FILE
|
|
122
122
|
const importedFileDirective =
|
|
123
|
-
getDirectiveFromImportedModule(resolvedImportPath);
|
|
123
|
+
getDirectiveFromImportedModule(resolvedImportPath) ?? NO_DIRECTIVE;
|
|
124
|
+
|
|
124
125
|
// GETTING THE EXTENSION OF THE IMPORTED FILE
|
|
125
126
|
const importedFileFileExtension = path.extname(resolvedImportPath);
|
|
126
|
-
|
|
127
|
+
|
|
128
|
+
// GETTING THE EFFECTIVE DIRECTIVE (no lack thereof) OF THE IMPORTED FILE
|
|
127
129
|
const importedFileEffectiveDirective = getEffectiveDirective(
|
|
128
130
|
importedFileDirective,
|
|
129
131
|
importedFileFileExtension
|
|
@@ -135,7 +137,7 @@ const importedFileFlow = (context, node) => {
|
|
|
135
137
|
return skipTrue;
|
|
136
138
|
}
|
|
137
139
|
|
|
138
|
-
// 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.
|
|
139
141
|
|
|
140
142
|
return { skip: undefined, importedFileEffectiveDirective };
|
|
141
143
|
};
|
|
@@ -167,9 +169,10 @@ export const importsFlow = (context, node, currentFileEffectiveDirective) => {
|
|
|
167
169
|
node,
|
|
168
170
|
messageId: importBreaksEffectiveImportRulesMessageId,
|
|
169
171
|
data: {
|
|
170
|
-
[effectiveDirectiveMessage]:
|
|
171
|
-
|
|
172
|
-
|
|
172
|
+
[effectiveDirectiveMessage]:
|
|
173
|
+
makeMessageFromCurrentFileEffectiveDirective(
|
|
174
|
+
currentFileEffectiveDirective
|
|
175
|
+
),
|
|
173
176
|
[specificViolationMessage]: findSpecificViolationMessage(
|
|
174
177
|
currentFileEffectiveDirective,
|
|
175
178
|
importedFileEffectiveDirective
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import { effectiveDirectives_effectiveModules } from "../../../_commons/constants/bases.js";
|
|
2
2
|
import {
|
|
3
3
|
USE_SERVER,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
USE_SERVER_COMPONENTS,
|
|
8
|
-
USE_SERVER_FUNCTIONS,
|
|
9
|
-
USE_CLIENT_LOGICS,
|
|
10
|
-
USE_CLIENT_COMPONENTS,
|
|
11
|
-
USE_AGNOSTIC_LOGICS,
|
|
12
|
-
USE_AGNOSTIC_COMPONENTS,
|
|
4
|
+
LOGICS,
|
|
5
|
+
COMPONENTS,
|
|
6
|
+
FUNCTIONS,
|
|
13
7
|
directivesArray,
|
|
14
8
|
directives_effectiveDirectives,
|
|
15
9
|
effectiveDirectives_blockedImports,
|
|
@@ -18,13 +12,14 @@ import {
|
|
|
18
12
|
import {
|
|
19
13
|
getImportedFileFirstLine,
|
|
20
14
|
isImportBlocked as commonsIsImportBlocked,
|
|
21
|
-
|
|
15
|
+
makeMessageFromCurrentFileResolvedDirective,
|
|
22
16
|
findSpecificViolationMessage as commonsFindSpecificViolationMessage,
|
|
23
17
|
} from "../../../_commons/utilities/helpers.js";
|
|
24
18
|
|
|
25
19
|
/**
|
|
26
20
|
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').Context} Context
|
|
27
21
|
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').Directive} Directive
|
|
22
|
+
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').NoDirective} NoDirective
|
|
28
23
|
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').Extension} Extension
|
|
29
24
|
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').EffectiveDirective} EffectiveDirective
|
|
30
25
|
*/
|
|
@@ -75,44 +70,18 @@ export const getDirectiveFromCurrentModule = (context) => {
|
|
|
75
70
|
* - `'use client components'` denotes a Client Components Module.
|
|
76
71
|
* - `'use agnostic logics'` denotes an Agnostic Logics Module.
|
|
77
72
|
* - `'use agnostic components'` denotes an Agnostic Components Module.
|
|
78
|
-
* @param {Directive |
|
|
73
|
+
* @param {Directive | NoDirective} directive The directive as written on top of the file (`"no directive"` if no valid directive).
|
|
79
74
|
* @param {Extension} extension The JavaScript (TypeScript) extension of the file.
|
|
80
75
|
* @returns The effective directive, from which imports rules are applied.
|
|
81
76
|
*/
|
|
82
77
|
export const getEffectiveDirective = (directive, extension) => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (directive === null && extension.endsWith("x"))
|
|
86
|
-
return USE_SERVER_COMPONENTS;
|
|
87
|
-
if (directive === USE_SERVER && !extension.endsWith("x"))
|
|
88
|
-
return USE_SERVER_FUNCTIONS;
|
|
89
|
-
if (directive === USE_CLIENT && !extension.endsWith("x"))
|
|
90
|
-
return USE_CLIENT_LOGICS;
|
|
91
|
-
if (directive === USE_CLIENT && extension.endsWith("x"))
|
|
92
|
-
return USE_CLIENT_COMPONENTS;
|
|
93
|
-
if (directive === USE_AGNOSTIC && !extension.endsWith("x"))
|
|
94
|
-
return USE_AGNOSTIC_LOGICS;
|
|
95
|
-
if (directive === USE_AGNOSTIC && extension.endsWith("x"))
|
|
96
|
-
return USE_AGNOSTIC_COMPONENTS;
|
|
97
|
-
|
|
98
|
-
return null; // default error, should be unreachable
|
|
99
|
-
};
|
|
100
|
-
// directives_effectiveDirectives
|
|
101
|
-
/**
|
|
102
|
-
* @param {Directive | null} directive
|
|
103
|
-
* @param {Extension} extension
|
|
104
|
-
* @returns
|
|
105
|
-
*/
|
|
106
|
-
export const trueGetEffectiveDirective = (directive, extension) => {
|
|
107
|
-
const type = extension.endsWith("x")
|
|
108
|
-
? "components"
|
|
78
|
+
const moduleKind = extension.endsWith("x")
|
|
79
|
+
? COMPONENTS
|
|
109
80
|
: directive === USE_SERVER
|
|
110
|
-
?
|
|
111
|
-
:
|
|
81
|
+
? FUNCTIONS
|
|
82
|
+
: LOGICS;
|
|
112
83
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return directives_effectiveDirectives[directive]?.[type] ?? null;
|
|
84
|
+
return directives_effectiveDirectives[directive][moduleKind];
|
|
116
85
|
};
|
|
117
86
|
|
|
118
87
|
/* getDirectiveFromImportedModule */
|
|
@@ -165,15 +134,17 @@ export const isImportBlocked = (
|
|
|
165
134
|
importedFileEffectiveDirective
|
|
166
135
|
);
|
|
167
136
|
|
|
168
|
-
/*
|
|
137
|
+
/* makeMessageFromCurrentFileEffectiveDirective */
|
|
169
138
|
|
|
170
139
|
/**
|
|
171
140
|
* Lists in an message the effective modules incompatible with an effective module based on its effective directive.
|
|
172
141
|
* @param {EffectiveDirective} effectiveDirective The effective directive of the effective module.
|
|
173
142
|
* @returns The message listing the incompatible effective modules.
|
|
174
143
|
*/
|
|
175
|
-
export const
|
|
176
|
-
|
|
144
|
+
export const makeMessageFromCurrentFileEffectiveDirective = (
|
|
145
|
+
effectiveDirective
|
|
146
|
+
) =>
|
|
147
|
+
makeMessageFromCurrentFileResolvedDirective(
|
|
177
148
|
effectiveDirectives_effectiveModules,
|
|
178
149
|
effectiveDirectives_blockedImports,
|
|
179
150
|
effectiveDirective
|
|
@@ -36,7 +36,7 @@ export const USE_AGNOSTIC_STRATEGIES = COMMONS_USE_AGNOSTIC_STRATEGIES;
|
|
|
36
36
|
|
|
37
37
|
// commented directives array
|
|
38
38
|
/** @type {CommentedDirectives} */
|
|
39
|
-
export const
|
|
39
|
+
export const commentedDirectivesArray = [
|
|
40
40
|
USE_SERVER_LOGICS,
|
|
41
41
|
USE_CLIENT_LOGICS,
|
|
42
42
|
USE_AGNOSTIC_LOGICS,
|
|
@@ -51,7 +51,21 @@ export const directivesArray = [
|
|
|
51
51
|
|
|
52
52
|
// commented directives set
|
|
53
53
|
/** @type {ReadonlySet<CommentedDirective>} */
|
|
54
|
-
export const
|
|
54
|
+
export const commentedDirectivesSet = new Set(commentedDirectivesArray); // no longer used exported to satisfy static type inference
|
|
55
|
+
|
|
56
|
+
// mapped commented directives to their extension rules
|
|
57
|
+
export const commentedDirectives_extensionRules = {
|
|
58
|
+
[USE_SERVER_LOGICS]: false, // Must not end with 'x'
|
|
59
|
+
[USE_CLIENT_LOGICS]: false,
|
|
60
|
+
[USE_AGNOSTIC_LOGICS]: false,
|
|
61
|
+
[USE_SERVER_COMPONENTS]: true, // Must end with 'x'
|
|
62
|
+
[USE_CLIENT_COMPONENTS]: true,
|
|
63
|
+
[USE_AGNOSTIC_COMPONENTS]: true,
|
|
64
|
+
[USE_SERVER_FUNCTIONS]: false,
|
|
65
|
+
[USE_CLIENT_CONTEXTS]: true,
|
|
66
|
+
[USE_AGNOSTIC_CONDITIONS]: true,
|
|
67
|
+
[USE_AGNOSTIC_STRATEGIES]: null, // Any extension allowed
|
|
68
|
+
};
|
|
55
69
|
|
|
56
70
|
// commented strategies
|
|
57
71
|
export const AT_SERVER_LOGICS = "@serverLogics";
|
|
@@ -145,7 +159,6 @@ const _DIRECTIVE_MUST_HAVE_A_JSX_FILE_EXTENSION =
|
|
|
145
159
|
"directive must have a JSX file extension";
|
|
146
160
|
|
|
147
161
|
export const commentedDirectives_verificationReports = Object.freeze({
|
|
148
|
-
// somehow doing it by hand is better for type inference in raw JS
|
|
149
162
|
[USE_SERVER_LOGICS]: `${MODULES_MARKED_WITH_THE_} "${USE_SERVER_LOGICS}" ${_DIRECTIVE_MUST_HAVE_A_NON_JSX_FILE_EXTENSION}.`,
|
|
150
163
|
[USE_CLIENT_LOGICS]: `${MODULES_MARKED_WITH_THE_} "${USE_CLIENT_LOGICS}" ${_DIRECTIVE_MUST_HAVE_A_NON_JSX_FILE_EXTENSION}.`,
|
|
151
164
|
[USE_AGNOSTIC_LOGICS]: `${MODULES_MARKED_WITH_THE_} "${USE_AGNOSTIC_LOGICS}" ${_DIRECTIVE_MUST_HAVE_A_NON_JSX_FILE_EXTENSION}.`,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
getVerifiedCommentedDirective,
|
|
29
29
|
getCommentedDirectiveFromImportedModule,
|
|
30
30
|
isImportBlocked,
|
|
31
|
-
|
|
31
|
+
makeMessageFromCurrentFileCommentedDirective,
|
|
32
32
|
findSpecificViolationMessage,
|
|
33
33
|
getStrategizedDirective,
|
|
34
34
|
addressDirectiveIfAgnosticStrategies,
|
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
export const currentFileFlow = (context) => {
|
|
54
54
|
const skipTrue = { ...skip, verifiedCommentedDirective: undefined };
|
|
55
55
|
|
|
56
|
-
//
|
|
56
|
+
// gets the extension of the current file
|
|
57
57
|
const currentFileExtension = path.extname(context.filename);
|
|
58
58
|
|
|
59
59
|
// fails if the file is not JavaScript (TypeScript)
|
|
@@ -79,6 +79,7 @@ export const currentFileFlow = (context) => {
|
|
|
79
79
|
return skipTrue;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
// verifies the commented directive from the current file
|
|
82
83
|
const verifiedCommentedDirective = getVerifiedCommentedDirective(
|
|
83
84
|
commentedDirective,
|
|
84
85
|
currentFileExtension
|
|
@@ -126,7 +127,7 @@ const importedFileFlow = (context, node) => {
|
|
|
126
127
|
);
|
|
127
128
|
if (!isImportedFileJS) return skipTrue;
|
|
128
129
|
|
|
129
|
-
|
|
130
|
+
// GETTING THE DIRECTIVE (or lack thereof) OF THE IMPORTED FILE
|
|
130
131
|
let importedFileCommentedDirective =
|
|
131
132
|
getCommentedDirectiveFromImportedModule(resolvedImportPath);
|
|
132
133
|
|
|
@@ -141,19 +142,17 @@ const importedFileFlow = (context, node) => {
|
|
|
141
142
|
/* GETTING THE CORRECT DIRECTIVE INTERPRETATION OF STRATEGY FOR AGNOSTIC STRATEGIES MODULES IMPORTS.
|
|
142
143
|
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
144
|
|
|
144
|
-
After a short attempt,
|
|
145
|
+
After a short attempt, the feature to address this (crossingStrategies) 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
146
|
|
|
146
147
|
(Consequently, details below are currently at the stage of wishful thinking.)
|
|
147
148
|
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 for all non-type exports to be named and PascalCase. */
|
|
149
|
+
|
|
148
150
|
if (importedFileCommentedDirective === USE_AGNOSTIC_STRATEGIES) {
|
|
149
151
|
const importingFileCommentedDirective = getStrategizedDirective(
|
|
150
152
|
context,
|
|
151
153
|
node
|
|
152
154
|
);
|
|
153
155
|
|
|
154
|
-
// FOR NOW, we consider the importingFileCommentedDirective (which is strategized) and the importedFileCommentedDirective (which should be strategized on its own imported file) to be same, given the limitation highlighted above.
|
|
155
|
-
importedFileCommentedDirective = importingFileCommentedDirective;
|
|
156
|
-
|
|
157
156
|
if (importingFileCommentedDirective === null) {
|
|
158
157
|
context.report({
|
|
159
158
|
node,
|
|
@@ -161,6 +160,9 @@ const importedFileFlow = (context, node) => {
|
|
|
161
160
|
});
|
|
162
161
|
return skipTrue;
|
|
163
162
|
}
|
|
163
|
+
|
|
164
|
+
// FOR NOW, we consider the importingFileCommentedDirective (which is strategized) and the importedFileCommentedDirective (which should be strategized on its own imported file) to be same, given the limitation highlighted above.
|
|
165
|
+
importedFileCommentedDirective = importingFileCommentedDirective;
|
|
164
166
|
}
|
|
165
167
|
|
|
166
168
|
return { skip: undefined, importedFileCommentedDirective };
|
|
@@ -194,9 +196,10 @@ export const importsFlow = (context, node, currentFileCommentedDirective) => {
|
|
|
194
196
|
node,
|
|
195
197
|
messageId: importBreaksCommentedImportRulesMessageId,
|
|
196
198
|
data: {
|
|
197
|
-
[commentedDirectiveMessage]:
|
|
198
|
-
|
|
199
|
-
|
|
199
|
+
[commentedDirectiveMessage]:
|
|
200
|
+
makeMessageFromCurrentFileCommentedDirective(
|
|
201
|
+
currentFileCommentedDirective
|
|
202
|
+
),
|
|
200
203
|
[specificViolationMessage]: findSpecificViolationMessage(
|
|
201
204
|
currentFileCommentedDirective,
|
|
202
205
|
importedFileCommentedDirective
|
|
@@ -244,7 +247,9 @@ export const allExportsFlow = (
|
|
|
244
247
|
currentFileCommentedDirective
|
|
245
248
|
);
|
|
246
249
|
|
|
250
|
+
// returns early if an address has been made
|
|
247
251
|
if (!addressedDirective) return;
|
|
252
|
+
// moves on to the re-export check otherwise
|
|
248
253
|
else currentFileCommentedDirective = addressedDirective;
|
|
249
254
|
|
|
250
255
|
if (currentFileCommentedDirective !== importedFileCommentedDirective) {
|
|
@@ -3,18 +3,11 @@ import {
|
|
|
3
3
|
commentedDirectives_commentedModules,
|
|
4
4
|
} from "../../../_commons/constants/bases.js";
|
|
5
5
|
import {
|
|
6
|
-
USE_SERVER_LOGICS,
|
|
7
|
-
USE_CLIENT_LOGICS,
|
|
8
6
|
USE_AGNOSTIC_LOGICS,
|
|
9
|
-
USE_SERVER_COMPONENTS,
|
|
10
|
-
USE_CLIENT_COMPONENTS,
|
|
11
|
-
USE_AGNOSTIC_COMPONENTS,
|
|
12
|
-
USE_SERVER_FUNCTIONS,
|
|
13
|
-
USE_CLIENT_CONTEXTS,
|
|
14
|
-
USE_AGNOSTIC_CONDITIONS,
|
|
15
7
|
USE_AGNOSTIC_STRATEGIES,
|
|
16
|
-
|
|
8
|
+
commentedDirectivesArray,
|
|
17
9
|
strategiesArray,
|
|
10
|
+
commentedDirectives_extensionRules,
|
|
18
11
|
commentedDirectives_4RawImplementations,
|
|
19
12
|
commentedStrategies_commentedDirectives,
|
|
20
13
|
commentedDirectives_blockedImports,
|
|
@@ -23,7 +16,7 @@ import {
|
|
|
23
16
|
import {
|
|
24
17
|
getImportedFileFirstLine,
|
|
25
18
|
isImportBlocked as commonsIsImportBlocked,
|
|
26
|
-
|
|
19
|
+
makeMessageFromCurrentFileResolvedDirective,
|
|
27
20
|
findSpecificViolationMessage as commonsFindSpecificViolationMessage,
|
|
28
21
|
} from "../../../_commons/utilities/helpers.js";
|
|
29
22
|
|
|
@@ -124,7 +117,7 @@ export const getCommentedDirectiveFromCurrentModule = (context) => {
|
|
|
124
117
|
|
|
125
118
|
// certifies the directive or lack thereof from the obtained value
|
|
126
119
|
const commentedDirective =
|
|
127
|
-
|
|
120
|
+
commentedDirectivesArray.find((directive) => directive === value) ?? null;
|
|
128
121
|
|
|
129
122
|
return commentedDirective;
|
|
130
123
|
};
|
|
@@ -145,31 +138,17 @@ export const getCommentedDirectiveFromCurrentModule = (context) => {
|
|
|
145
138
|
* - `'use agnostic strategies'`: Agnostic Strategies Modules may export JSX.
|
|
146
139
|
* @param {CommentedDirective} directive The commented directive as written on top of the file (cannot be `null` at that stage).
|
|
147
140
|
* @param {Extension} extension The JavaScript (TypeScript) extension of the file.
|
|
148
|
-
* @returns The verified commented directive, from which imports rules are applied. Returns `null` if the verification failed, upon which an error will be reported depending on the commented directive, since the error logic here is strictly binary.
|
|
141
|
+
* @returns {CommentedDirective | null} The verified commented directive, from which imports rules are applied. Returns `null` if the verification failed, upon which an error will be reported depending on the commented directive, since the error logic here is strictly binary.
|
|
149
142
|
*/
|
|
150
143
|
export const getVerifiedCommentedDirective = (directive, extension) => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
return directive;
|
|
160
|
-
if (directive === USE_CLIENT_COMPONENTS && extension.endsWith("x"))
|
|
161
|
-
return directive;
|
|
162
|
-
if (directive === USE_AGNOSTIC_COMPONENTS && extension.endsWith("x"))
|
|
163
|
-
return directive;
|
|
164
|
-
if (directive === USE_SERVER_FUNCTIONS && !extension.endsWith("x"))
|
|
165
|
-
return directive;
|
|
166
|
-
if (directive === USE_CLIENT_CONTEXTS && extension.endsWith("x"))
|
|
167
|
-
return directive;
|
|
168
|
-
if (directive === USE_AGNOSTIC_CONDITIONS && extension.endsWith("x"))
|
|
169
|
-
return directive;
|
|
170
|
-
if (directive === USE_AGNOSTIC_STRATEGIES) return directive;
|
|
171
|
-
|
|
172
|
-
return null; // verification error
|
|
144
|
+
const rule = commentedDirectives_extensionRules[directive];
|
|
145
|
+
const isExtensionJSX = extension.endsWith("x");
|
|
146
|
+
|
|
147
|
+
if (rule === true && isExtensionJSX) return directive; // requires JSX extension
|
|
148
|
+
if (rule === false && !isExtensionJSX) return directive; // forbids JSX extension
|
|
149
|
+
if (rule === null) return directive; // no extension constraint, specifically for "use agnostic strategies"
|
|
150
|
+
|
|
151
|
+
return null; // verification failed
|
|
173
152
|
};
|
|
174
153
|
|
|
175
154
|
/* getCommentedDirectiveFromImportedModule */
|
|
@@ -197,7 +176,7 @@ export const getCommentedDirectiveFromImportedModule = (resolvedImportPath) => {
|
|
|
197
176
|
const importedFileFirstLine = getImportedFileFirstLine(resolvedImportPath);
|
|
198
177
|
|
|
199
178
|
// sees if the first line includes any of the directives and finds the directive that it includes, with USE_AGNOSTIC_LOGICS as a default
|
|
200
|
-
const includedDirective =
|
|
179
|
+
const includedDirective = commentedDirectivesArray.reduce((acc, curr) => {
|
|
201
180
|
if (importedFileFirstLine.includes(curr)) return curr;
|
|
202
181
|
else return acc;
|
|
203
182
|
}, USE_AGNOSTIC_LOGICS);
|
|
@@ -221,19 +200,23 @@ export const getCommentedDirectiveFromImportedModule = (resolvedImportPath) => {
|
|
|
221
200
|
* @returns The interpreted directive, a.k.a. strategized directive, or lack thereof via `null`.
|
|
222
201
|
*/
|
|
223
202
|
export const getStrategizedDirective = (context, node) => {
|
|
203
|
+
// gets the first nested `/* */` comment inside the node
|
|
224
204
|
const firstNestedComment = context.sourceCode.getCommentsInside(node)[0];
|
|
225
205
|
|
|
226
206
|
// returns null early if there is no nested comments
|
|
227
207
|
if (!firstNestedComment) return null;
|
|
228
208
|
|
|
209
|
+
// gets and trims the first nested comment raw
|
|
229
210
|
const rawStrategy = firstNestedComment.value.trim() || "";
|
|
230
211
|
|
|
212
|
+
// asserts whether that first nested comment is or isn't a Strategy
|
|
231
213
|
const strategy =
|
|
232
214
|
strategiesArray.find((strategy) => strategy === rawStrategy) ?? null;
|
|
233
215
|
|
|
234
216
|
// returns null early if no strategy was identified
|
|
235
217
|
if (!strategy) return null;
|
|
236
218
|
|
|
219
|
+
// maps the strategy to the its relevant directive
|
|
237
220
|
const commentedDirective = commentedStrategies_commentedDirectives[strategy];
|
|
238
221
|
|
|
239
222
|
return commentedDirective;
|
|
@@ -257,15 +240,17 @@ export const isImportBlocked = (
|
|
|
257
240
|
importedFileCommentedDirective
|
|
258
241
|
);
|
|
259
242
|
|
|
260
|
-
/*
|
|
243
|
+
/* makeMessageFromCurrentFileCommentedDirective */
|
|
261
244
|
|
|
262
245
|
/**
|
|
263
246
|
* Lists in an message the commented modules incompatible with a commented module based on its commented directive.
|
|
264
247
|
* @param {CommentedDirective} commentedDirective The commented directive of the commented module.
|
|
265
248
|
* @returns The message listing the incompatible commented modules.
|
|
266
249
|
*/
|
|
267
|
-
export const
|
|
268
|
-
|
|
250
|
+
export const makeMessageFromCurrentFileCommentedDirective = (
|
|
251
|
+
commentedDirective
|
|
252
|
+
) =>
|
|
253
|
+
makeMessageFromCurrentFileResolvedDirective(
|
|
269
254
|
commentedDirectives_commentedModules,
|
|
270
255
|
commentedDirectives_blockedImports,
|
|
271
256
|
commentedDirective
|