eslint-plugin-use-agnostic 0.9.3 → 0.9.5
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/library/_commons/constants/bases.js +4 -5
- package/library/_commons/utilities/helpers.js +26 -17
- package/library/agnostic20/_commons/constants/bases.js +9 -13
- package/library/agnostic20/_commons/utilities/flows.js +8 -11
- package/library/agnostic20/_commons/utilities/helpers.js +11 -11
- package/library/directive21/_commons/constants/bases.js +88 -73
- package/library/directive21/_commons/utilities/flows.js +11 -13
- package/library/directive21/_commons/utilities/helpers.js +56 -47
- package/package.json +5 -3
- package/types/index.d.ts +12 -0
|
@@ -17,7 +17,6 @@ export const enforceEffectiveDirectivesRuleName =
|
|
|
17
17
|
// directive21
|
|
18
18
|
export const enforceCommentedDirectivesRuleName =
|
|
19
19
|
"enforce-commented-directives-import-rules";
|
|
20
|
-
|
|
21
20
|
// crossingStrategies (canceled)
|
|
22
21
|
export const verifySpecifierImportRuleName =
|
|
23
22
|
"verify-specifier-import-export-same-strategy";
|
|
@@ -69,8 +68,7 @@ export const CLIENT_CONTEXTS_MODULE = "Client Contexts Module";
|
|
|
69
68
|
export const AGNOSTIC_CONDITIONS_MODULE = "Agnostic Conditions Module";
|
|
70
69
|
export const AGNOSTIC_STRATEGIES_MODULE = "Agnostic Strategies Module";
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
// JavaScript/TypeScript extensions
|
|
74
72
|
export const TSX = ".tsx";
|
|
75
73
|
export const TS = ".ts";
|
|
76
74
|
export const JSX = ".jsx";
|
|
@@ -78,8 +76,9 @@ export const JS = ".js";
|
|
|
78
76
|
export const MJS = ".mjs";
|
|
79
77
|
export const CJS = ".cjs";
|
|
80
78
|
|
|
79
|
+
// JavaScript/TypeScript extensions array
|
|
80
|
+
/** @type {readonly [TSX, TS, JSX, JS, MJS, CJS]} */
|
|
81
81
|
export const EXTENSIONS = [TSX, TS, JSX, JS, MJS, CJS]; // In priority order
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
// message strings
|
|
85
84
|
export const ARE_NOT_ALLOWED_TO_IMPORT = "are not allowed to import";
|
|
@@ -30,15 +30,28 @@ import {
|
|
|
30
30
|
|
|
31
31
|
/* resolveImportPath */
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Finds the existing path of an import that does not have an extension specified.
|
|
35
|
+
* @param {string} basePath The absolute import path with extension yet resolved.
|
|
36
|
+
* @returns The absolute path with its extension or `null` if no path is found.
|
|
37
|
+
*/
|
|
38
|
+
const findExistingPath = (basePath) => {
|
|
39
|
+
for (const ext of EXTENSIONS) {
|
|
40
|
+
const fullPath = `${basePath}${ext}`;
|
|
41
|
+
if (fs.existsSync(fullPath)) return fullPath;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
};
|
|
45
|
+
|
|
33
46
|
/**
|
|
34
47
|
* Resolves an import path to a filesystem path, handling:
|
|
35
48
|
* - Aliases (via tsconfig.json `paths`)
|
|
36
49
|
* - Missing extensions (appends .ts, .tsx, etc.)
|
|
37
50
|
* - Directory imports (e.g., `./components` → `./components/index.ts`)
|
|
38
|
-
* @param {string} currentDir
|
|
51
|
+
* @param {string} currentDir The directory of the file containing the import (from `path.dirname(context.filename)`).
|
|
39
52
|
* @param {string} importPath The import specifier (e.g., `@/components/Button` or `./utils`), from the current node.
|
|
40
|
-
* @param {string} cwd
|
|
41
|
-
* @returns
|
|
53
|
+
* @param {string} cwd The project root (from `context.cwd`). Caveat: only as an assumption currently.
|
|
54
|
+
* @returns The absolute resolved path or `null` if no path is found.
|
|
42
55
|
*/
|
|
43
56
|
export const resolveImportPath = (currentDir, importPath, cwd) => {
|
|
44
57
|
// --- Step 1: Resolve aliases (if tsconfig.json `paths` exists) ---
|
|
@@ -63,19 +76,15 @@ export const resolveImportPath = (currentDir, importPath, cwd) => {
|
|
|
63
76
|
if (path.extname(importPath) && fs.existsSync(basePath)) return basePath;
|
|
64
77
|
|
|
65
78
|
// Case 2: Try appending extensions
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (fs.existsSync(fullPath)) return fullPath;
|
|
69
|
-
}
|
|
79
|
+
const extensionlessImportPath = findExistingPath(basePath);
|
|
80
|
+
if (extensionlessImportPath) return extensionlessImportPath;
|
|
70
81
|
|
|
71
82
|
// Case 3: Directory import (e.g., `./components` → `./components/index.ts`)
|
|
72
83
|
const indexPath = path.join(basePath, "index");
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (fs.existsSync(fullPath)) return fullPath;
|
|
76
|
-
}
|
|
84
|
+
const directoryImportPath = findExistingPath(indexPath);
|
|
85
|
+
if (directoryImportPath) return extensionlessImportPath;
|
|
77
86
|
|
|
78
|
-
return null; //
|
|
87
|
+
return null; // not found
|
|
79
88
|
};
|
|
80
89
|
|
|
81
90
|
/* getImportedFileFirstLine */
|
|
@@ -83,7 +92,7 @@ export const resolveImportPath = (currentDir, importPath, cwd) => {
|
|
|
83
92
|
/**
|
|
84
93
|
* Gets the first line of code of the imported module.
|
|
85
94
|
* @param {string} resolvedImportPath The resolved path of the imported module.
|
|
86
|
-
* @returns
|
|
95
|
+
* @returns The first line of the imported module.
|
|
87
96
|
*/
|
|
88
97
|
export const getImportedFileFirstLine = (resolvedImportPath) => {
|
|
89
98
|
// gets the code of the import
|
|
@@ -116,7 +125,7 @@ export const highlightFirstLineOfCode = (context) => ({
|
|
|
116
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.
|
|
117
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.
|
|
118
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.
|
|
119
|
-
* @returns
|
|
128
|
+
* @returns Returns `true` if the import is blocked, as established in respective `resolvedDirectives_blockedImports`.
|
|
120
129
|
*/
|
|
121
130
|
export const isImportBlocked = (
|
|
122
131
|
// 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.
|
|
@@ -135,7 +144,7 @@ export const isImportBlocked = (
|
|
|
135
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.
|
|
136
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.
|
|
137
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.
|
|
138
|
-
* @returns
|
|
147
|
+
* @returns Returns "[Current file 'resolved' modules] are not allowed to import [imported file 'resolved' modules]."
|
|
139
148
|
*/
|
|
140
149
|
export const makeIntroForSpecificViolationMessage = (
|
|
141
150
|
resolvedDirectives_ResolvedModules,
|
|
@@ -151,7 +160,7 @@ export const makeIntroForSpecificViolationMessage = (
|
|
|
151
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.
|
|
152
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.
|
|
153
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
|
-
* @returns
|
|
163
|
+
* @returns The message listing the incompatible "resolved" modules.
|
|
155
164
|
*/
|
|
156
165
|
export const makeMessageFromResolvedDirective = (
|
|
157
166
|
resolvedDirectives_ResolvedModules,
|
|
@@ -191,7 +200,7 @@ export const makeMessageFromResolvedDirective = (
|
|
|
191
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.
|
|
192
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.
|
|
193
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.
|
|
194
|
-
* @returns
|
|
203
|
+
* @returns The corresponding `message`.
|
|
195
204
|
*/
|
|
196
205
|
export const findSpecificViolationMessage = (
|
|
197
206
|
resolvedDirectives_blockedImports,
|
|
@@ -23,6 +23,14 @@ export const USE_SERVER = "use server";
|
|
|
23
23
|
export const USE_CLIENT = "use client";
|
|
24
24
|
export const USE_AGNOSTIC = "use agnostic";
|
|
25
25
|
|
|
26
|
+
// directives array
|
|
27
|
+
/** @type {readonly [USE_SERVER, USE_CLIENT, USE_AGNOSTIC]} */
|
|
28
|
+
export const directivesArray = [USE_SERVER, USE_CLIENT, USE_AGNOSTIC];
|
|
29
|
+
|
|
30
|
+
// directives set
|
|
31
|
+
/** @type {ReadonlySet<USE_SERVER | USE_CLIENT | USE_AGNOSTIC>} */
|
|
32
|
+
export const directivesSet = new Set(directivesArray); // no longer used exported to satisfy static type inference
|
|
33
|
+
|
|
26
34
|
// effective directives
|
|
27
35
|
export const USE_SERVER_LOGICS = COMMONS_USE_SERVER_LOGICS;
|
|
28
36
|
export const USE_SERVER_COMPONENTS = COMMONS_USE_SERVER_COMPONENTS;
|
|
@@ -53,30 +61,18 @@ export const effectiveDirectives_EffectiveModules = Object.freeze({
|
|
|
53
61
|
});
|
|
54
62
|
|
|
55
63
|
// message placeholders
|
|
56
|
-
|
|
57
64
|
export const currentFileEffectiveDirective = "currentFileEffectiveDirective";
|
|
58
65
|
export const importedFileEffectiveDirective = "importedFileEffectiveDirective";
|
|
59
66
|
export const effectiveDirectiveMessage = "effectiveDirectiveMessage";
|
|
60
67
|
export const specificViolationMessage = "specificViolationMessage";
|
|
61
68
|
|
|
62
|
-
/* from the getDirectiveFromCurrentModule utility */
|
|
63
|
-
|
|
64
|
-
export const directivesSet = new Set([USE_SERVER, USE_CLIENT, USE_AGNOSTIC]);
|
|
65
|
-
|
|
66
|
-
/* from the getDirectiveFromImportedModule utility */
|
|
67
|
-
|
|
68
|
-
/** @type {readonly [USE_SERVER, USE_CLIENT, USE_AGNOSTIC]} */
|
|
69
|
-
export const directivesArray = Array.from(directivesSet);
|
|
70
|
-
|
|
71
|
-
/* from the isImportBlocked utility */
|
|
72
|
-
|
|
73
69
|
/* effectiveDirectives_BlockedImports */
|
|
74
70
|
|
|
75
71
|
/**
|
|
76
72
|
* Makes the intro for each specific import rule violation messages.
|
|
77
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.
|
|
78
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.
|
|
79
|
-
* @returns
|
|
75
|
+
* @returns "[Current file effective modules] are not allowed to import [imported file effective modules]."
|
|
80
76
|
*/
|
|
81
77
|
const makeIntroForSpecificViolationMessage = (
|
|
82
78
|
currentFileEffectiveDirective,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
reExportNotSameMessageId,
|
|
8
8
|
} from "../../../_commons/constants/bases.js";
|
|
9
9
|
import {
|
|
10
|
+
USE_SERVER,
|
|
10
11
|
USE_SERVER_LOGICS,
|
|
11
12
|
USE_SERVER_COMPONENTS,
|
|
12
13
|
USE_SERVER_FUNCTIONS,
|
|
@@ -38,7 +39,7 @@ import {
|
|
|
38
39
|
/**
|
|
39
40
|
* 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.
|
|
40
41
|
* @param {Readonly<import('@typescript-eslint/utils').TSESLint.RuleContext<typeof reExportNotSameMessageId | typeof importBreaksEffectiveImportRulesMessageId | typeof useServerJSXMessageId, []>>} context The ESLint rule's `context` object.
|
|
41
|
-
* @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;}}
|
|
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`.
|
|
42
43
|
*/
|
|
43
44
|
export const currentFileFlow = (context) => {
|
|
44
45
|
// GETTING THE EXTENSION OF THE CURRENT FILE
|
|
@@ -60,7 +61,7 @@ export const currentFileFlow = (context) => {
|
|
|
60
61
|
|
|
61
62
|
// reports if a file marked "use server" has a JSX extension
|
|
62
63
|
if (
|
|
63
|
-
currentFileDirective ===
|
|
64
|
+
currentFileDirective === USE_SERVER &&
|
|
64
65
|
currentFileExtension.endsWith("x")
|
|
65
66
|
) {
|
|
66
67
|
context.report({
|
|
@@ -82,9 +83,7 @@ export const currentFileFlow = (context) => {
|
|
|
82
83
|
return { skip: true };
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
return {
|
|
86
|
-
currentFileEffectiveDirective,
|
|
87
|
-
};
|
|
86
|
+
return { currentFileEffectiveDirective };
|
|
88
87
|
};
|
|
89
88
|
|
|
90
89
|
/* importedFileFlow */
|
|
@@ -93,7 +92,7 @@ export const currentFileFlow = (context) => {
|
|
|
93
92
|
* The flow that is shared between import and re-export traversals to obtain the import file's effective directive.
|
|
94
93
|
* @param {Readonly<import('@typescript-eslint/utils').TSESLint.RuleContext<typeof reExportNotSameMessageId | typeof importBreaksEffectiveImportRulesMessageId | typeof useServerJSXMessageId, []>>} context The ESLint rule's `context` object.
|
|
95
94
|
* @param {import('@typescript-eslint/types').TSESTree.ImportDeclaration} node The ESLint `node` of the rule's current traversal.
|
|
96
|
-
* @returns {{skip: true; importedFileEffectiveDirective: undefined;
|
|
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`.
|
|
97
96
|
*/
|
|
98
97
|
const importedFileFlow = (context, node) => {
|
|
99
98
|
// finds the full path of the import
|
|
@@ -130,9 +129,7 @@ const importedFileFlow = (context, node) => {
|
|
|
130
129
|
|
|
131
130
|
// For now skipping on both "does not operate" (which should ignore) and "fails" albeit with console.error (which should crash).
|
|
132
131
|
|
|
133
|
-
return {
|
|
134
|
-
importedFileEffectiveDirective,
|
|
135
|
-
};
|
|
132
|
+
return { importedFileEffectiveDirective };
|
|
136
133
|
};
|
|
137
134
|
|
|
138
135
|
/* importsFlow */
|
|
@@ -141,7 +138,7 @@ const importedFileFlow = (context, node) => {
|
|
|
141
138
|
* @param {Readonly<import('@typescript-eslint/utils').TSESLint.RuleContext<typeof reExportNotSameMessageId | typeof importBreaksEffectiveImportRulesMessageId | typeof useServerJSXMessageId, []>>} context The ESLint rule's `context` object.
|
|
142
139
|
* @param {import('@typescript-eslint/types').TSESTree.ImportDeclaration} node The ESLint `node` of the rule's current traversal.
|
|
143
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.
|
|
144
|
-
* @returns
|
|
141
|
+
* @returns Early if the flow needs to be interrupted.
|
|
145
142
|
*/
|
|
146
143
|
export const importsFlow = (context, node, currentFileEffectiveDirective) => {
|
|
147
144
|
// does not operate on `import type`
|
|
@@ -180,7 +177,7 @@ export const importsFlow = (context, node, currentFileEffectiveDirective) => {
|
|
|
180
177
|
* @param {Readonly<import('@typescript-eslint/utils').TSESLint.RuleContext<typeof reExportNotSameMessageId | typeof importBreaksEffectiveImportRulesMessageId | typeof useServerJSXMessageId, []>>} context The ESLint rule's `context` object.
|
|
181
178
|
* @param {import('@typescript-eslint/types').TSESTree.ExportNamedDeclaration | import('@typescript-eslint/types').TSESTree.ExportAllDeclaration} node The ESLint `node` of the rule's current traversal.
|
|
182
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.
|
|
183
|
-
* @returns
|
|
180
|
+
* @returns Early if the flow needs to be interrupted.
|
|
184
181
|
*/
|
|
185
182
|
export const reExportsFlow = (context, node, currentFileEffectiveDirective) => {
|
|
186
183
|
// does not operate on `export type`
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
useServerJSXMessageId,
|
|
3
|
-
importBreaksEffectiveImportRulesMessageId,
|
|
4
|
-
reExportNotSameMessageId,
|
|
5
2
|
TSX,
|
|
6
3
|
TS,
|
|
7
4
|
JSX,
|
|
8
5
|
JS,
|
|
9
6
|
MJS,
|
|
10
7
|
CJS,
|
|
8
|
+
useServerJSXMessageId,
|
|
9
|
+
importBreaksEffectiveImportRulesMessageId,
|
|
10
|
+
reExportNotSameMessageId,
|
|
11
11
|
} from "../../../_commons/constants/bases.js";
|
|
12
12
|
import {
|
|
13
13
|
NO_DIRECTIVE,
|
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
USE_AGNOSTIC_LOGICS,
|
|
23
23
|
USE_AGNOSTIC_COMPONENTS,
|
|
24
24
|
effectiveDirectives_EffectiveModules,
|
|
25
|
-
directivesSet,
|
|
26
25
|
directivesArray,
|
|
27
26
|
effectiveDirectives_BlockedImports,
|
|
28
27
|
} from "../constants/bases.js";
|
|
@@ -43,7 +42,7 @@ import {
|
|
|
43
42
|
* - `'use server'` denotes a Server Functions Module.
|
|
44
43
|
* - `'use agnostic'` denotes an Agnostic Module (formerly Shared Module).
|
|
45
44
|
* @param {Readonly<import('@typescript-eslint/utils').TSESLint.RuleContext<typeof reExportNotSameMessageId | typeof importBreaksEffectiveImportRulesMessageId | typeof useServerJSXMessageId, []>>} context The ESLint rule's `context` object.
|
|
46
|
-
* @returns
|
|
45
|
+
* @returns The directive, or lack thereof via `null`. The lack of a directive is considered server-by-default.
|
|
47
46
|
*/
|
|
48
47
|
export const getDirectiveFromCurrentModule = (context) => {
|
|
49
48
|
// the AST body to check for the top-of-the-file directive
|
|
@@ -63,7 +62,8 @@ export const getDirectiveFromCurrentModule = (context) => {
|
|
|
63
62
|
if (value === null) return value;
|
|
64
63
|
|
|
65
64
|
// the value to be exactly 'use client', 'use server' or 'use agnostic' in order not to be considered null by default, or server-by-default
|
|
66
|
-
const currentFileDirective =
|
|
65
|
+
const currentFileDirective =
|
|
66
|
+
directivesArray.find((directive) => directive === value) ?? null;
|
|
67
67
|
|
|
68
68
|
return currentFileDirective;
|
|
69
69
|
};
|
|
@@ -81,7 +81,7 @@ export const getDirectiveFromCurrentModule = (context) => {
|
|
|
81
81
|
* - `'use agnostic components'` denotes an Agnostic Components Module.
|
|
82
82
|
* @param {NO_DIRECTIVE | USE_SERVER | USE_CLIENT | USE_AGNOSTIC} directive The directive as written on top of the file (`null` if no valid directive).
|
|
83
83
|
* @param {TSX | TS | JSX | JS | MJS | CJS} extension The JavaScript (TypeScript) extension of the file.
|
|
84
|
-
* @returns
|
|
84
|
+
* @returns The effective directive, from which imports rules are applied.
|
|
85
85
|
*/
|
|
86
86
|
export const getEffectiveDirective = (directive, extension) => {
|
|
87
87
|
// I could use a map, but because this is in JS with JSDoc, a manual solution is peculiarly more typesafe.
|
|
@@ -112,7 +112,7 @@ export const getEffectiveDirective = (directive, extension) => {
|
|
|
112
112
|
* - `'use agnostic'` denotes an Agnostic Module (formerly Shared Module).
|
|
113
113
|
* - `null` denotes a server-by-default module, ideally a Server Module.
|
|
114
114
|
* @param {string} resolvedImportPath The resolved path of the import.
|
|
115
|
-
* @returns
|
|
115
|
+
* @returns The directive, or lack thereof via `null`. The lack of a directive is considered server-by-default.
|
|
116
116
|
*/
|
|
117
117
|
export const getDirectiveFromImportedModule = (resolvedImportPath) => {
|
|
118
118
|
// gets the first line of the code of the import
|
|
@@ -141,7 +141,7 @@ export const getDirectiveFromImportedModule = (resolvedImportPath) => {
|
|
|
141
141
|
* Returns a boolean deciding if an imported file's effective directive is incompatible with the current file's effective directive.
|
|
142
142
|
* @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.
|
|
143
143
|
* @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.
|
|
144
|
-
* @returns
|
|
144
|
+
* @returns `true` if the import is blocked, as established in `effectiveDirectives_BlockedImports`.
|
|
145
145
|
*/
|
|
146
146
|
export const isImportBlocked = (
|
|
147
147
|
currentFileEffectiveDirective,
|
|
@@ -158,7 +158,7 @@ export const isImportBlocked = (
|
|
|
158
158
|
/**
|
|
159
159
|
* Lists in an message the effective modules incompatible with an effective module based on its effective directive.
|
|
160
160
|
* @param {USE_SERVER_LOGICS | USE_SERVER_COMPONENTS | USE_SERVER_FUNCTIONS | USE_CLIENT_LOGICS | USE_CLIENT_COMPONENTS | USE_AGNOSTIC_LOGICS | USE_AGNOSTIC_COMPONENTS} effectiveDirective The effective directive of the effective module.
|
|
161
|
-
* @returns
|
|
161
|
+
* @returns The message listing the incompatible effective modules.
|
|
162
162
|
*/
|
|
163
163
|
export const makeMessageFromEffectiveDirective = (effectiveDirective) =>
|
|
164
164
|
makeMessageFromResolvedDirective(
|
|
@@ -173,7 +173,7 @@ export const makeMessageFromEffectiveDirective = (effectiveDirective) =>
|
|
|
173
173
|
* Finds the `message` for the specific violation of effective directives import rules based on `effectiveDirectives_BlockedImports`.
|
|
174
174
|
* @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.
|
|
175
175
|
* @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.
|
|
176
|
-
* @returns
|
|
176
|
+
* @returns The corresponding `message`.
|
|
177
177
|
*/
|
|
178
178
|
export const findSpecificViolationMessage = (
|
|
179
179
|
currentFileEffectiveDirective,
|
|
@@ -35,6 +35,25 @@ export const USE_CLIENT_CONTEXTS = COMMONS_USE_CLIENT_CONTEXTS;
|
|
|
35
35
|
export const USE_AGNOSTIC_CONDITIONS = COMMONS_USE_AGNOSTIC_CONDITIONS;
|
|
36
36
|
export const USE_AGNOSTIC_STRATEGIES = COMMONS_USE_AGNOSTIC_STRATEGIES;
|
|
37
37
|
|
|
38
|
+
// commented directives array
|
|
39
|
+
/** @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]} */
|
|
40
|
+
export const directivesArray = [
|
|
41
|
+
USE_SERVER_LOGICS,
|
|
42
|
+
USE_CLIENT_LOGICS,
|
|
43
|
+
USE_AGNOSTIC_LOGICS,
|
|
44
|
+
USE_SERVER_COMPONENTS,
|
|
45
|
+
USE_CLIENT_COMPONENTS,
|
|
46
|
+
USE_AGNOSTIC_COMPONENTS,
|
|
47
|
+
USE_SERVER_FUNCTIONS,
|
|
48
|
+
USE_CLIENT_CONTEXTS,
|
|
49
|
+
USE_AGNOSTIC_CONDITIONS,
|
|
50
|
+
USE_AGNOSTIC_STRATEGIES,
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// commented directives set
|
|
54
|
+
/** @type {ReadonlySet<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>} */
|
|
55
|
+
export const directivesSet = new Set(directivesArray); // no longer used exported to satisfy static type inference
|
|
56
|
+
|
|
38
57
|
// commented modules
|
|
39
58
|
const SERVER_LOGICS_MODULE = COMMONS_SERVER_LOGICS_MODULE;
|
|
40
59
|
const CLIENT_LOGICS_MODULE = COMMONS_CLIENT_LOGICS_MODULE;
|
|
@@ -61,34 +80,55 @@ export const commentedDirectives_CommentedModules = Object.freeze({
|
|
|
61
80
|
[USE_AGNOSTIC_STRATEGIES]: AGNOSTIC_STRATEGIES_MODULE,
|
|
62
81
|
});
|
|
63
82
|
|
|
64
|
-
//
|
|
83
|
+
// commented strategies
|
|
84
|
+
const AT_SERVER_LOGICS = "@serverLogics";
|
|
85
|
+
const AT_CLIENT_LOGICS = "@clientLogics";
|
|
86
|
+
const AT_AGNOSTIC_LOGICS = "@agnosticLogics";
|
|
87
|
+
const AT_SERVER_COMPONENTS = "@serverComponents";
|
|
88
|
+
const AT_CLIENT_COMPONENTS = "@clientComponents";
|
|
89
|
+
const AT_AGNOSTIC_COMPONENTS = "@agnosticComponents";
|
|
90
|
+
const AT_SERVER_FUNCTIONS = "@serverFunctions";
|
|
91
|
+
const AT_CLIENT_CONTEXTS = "@clientContexts";
|
|
92
|
+
const AT_AGNOSTIC_CONDITIONS = "@agnosticConditions";
|
|
65
93
|
|
|
94
|
+
// commented strategies array
|
|
95
|
+
/** @type {readonly [AT_SERVER_LOGICS, AT_CLIENT_LOGICS, AT_AGNOSTIC_LOGICS, AT_SERVER_COMPONENTS, AT_CLIENT_COMPONENTS, AT_AGNOSTIC_COMPONENTS, AT_SERVER_FUNCTIONS, AT_CLIENT_CONTEXTS, AT_AGNOSTIC_CONDITIONS]} */
|
|
96
|
+
export const strategiesArray = [
|
|
97
|
+
AT_SERVER_LOGICS,
|
|
98
|
+
AT_CLIENT_LOGICS,
|
|
99
|
+
AT_AGNOSTIC_LOGICS,
|
|
100
|
+
AT_SERVER_COMPONENTS,
|
|
101
|
+
AT_CLIENT_COMPONENTS,
|
|
102
|
+
AT_AGNOSTIC_COMPONENTS,
|
|
103
|
+
AT_SERVER_FUNCTIONS,
|
|
104
|
+
AT_CLIENT_CONTEXTS,
|
|
105
|
+
AT_AGNOSTIC_CONDITIONS,
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
// commented strategies set
|
|
109
|
+
/** @type {ReadonlySet<AT_SERVER_LOGICS | AT_CLIENT_LOGICS | AT_AGNOSTIC_LOGICS | AT_SERVER_COMPONENTS | AT_CLIENT_COMPONENTS | AT_AGNOSTIC_COMPONENTS | AT_SERVER_FUNCTIONS | AT_CLIENT_CONTEXTS | AT_AGNOSTIC_CONDITIONS>} */
|
|
110
|
+
export const strategiesSet = new Set(strategiesArray); // no longer used exported to satisfy static type inference
|
|
111
|
+
|
|
112
|
+
// mapped commented strategies to their commented directives
|
|
113
|
+
export const commentedStrategies_CommentedDirectives = Object.freeze({
|
|
114
|
+
[AT_SERVER_LOGICS]: USE_SERVER_LOGICS,
|
|
115
|
+
[AT_CLIENT_LOGICS]: USE_CLIENT_LOGICS,
|
|
116
|
+
[AT_AGNOSTIC_LOGICS]: USE_AGNOSTIC_LOGICS,
|
|
117
|
+
[AT_SERVER_COMPONENTS]: USE_SERVER_COMPONENTS,
|
|
118
|
+
[AT_CLIENT_COMPONENTS]: USE_CLIENT_COMPONENTS,
|
|
119
|
+
[AT_AGNOSTIC_COMPONENTS]: USE_AGNOSTIC_COMPONENTS,
|
|
120
|
+
[AT_SERVER_FUNCTIONS]: USE_SERVER_FUNCTIONS,
|
|
121
|
+
[AT_CLIENT_CONTEXTS]: USE_CLIENT_CONTEXTS,
|
|
122
|
+
[AT_AGNOSTIC_CONDITIONS]: USE_AGNOSTIC_CONDITIONS,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// message placeholders
|
|
66
126
|
export const currentFileCommentedDirective = "currentFileCommentedDirective";
|
|
67
127
|
export const importedFileCommentedDirective = "importedFileCommentedDirective";
|
|
68
128
|
export const commentedDirectiveMessage = "commentedDirectiveMessage";
|
|
69
129
|
export const specificViolationMessage = "specificViolationMessage";
|
|
70
130
|
export const specificFailure = "specificFailure";
|
|
71
131
|
|
|
72
|
-
/* from the getCommentedDirectiveFromCurrentModule utility */
|
|
73
|
-
|
|
74
|
-
export const directivesSet = new Set([
|
|
75
|
-
USE_SERVER_LOGICS,
|
|
76
|
-
USE_CLIENT_LOGICS,
|
|
77
|
-
USE_AGNOSTIC_LOGICS,
|
|
78
|
-
USE_SERVER_COMPONENTS,
|
|
79
|
-
USE_CLIENT_COMPONENTS,
|
|
80
|
-
USE_AGNOSTIC_COMPONENTS,
|
|
81
|
-
USE_SERVER_FUNCTIONS,
|
|
82
|
-
USE_CLIENT_CONTEXTS,
|
|
83
|
-
USE_AGNOSTIC_CONDITIONS,
|
|
84
|
-
USE_AGNOSTIC_STRATEGIES,
|
|
85
|
-
]);
|
|
86
|
-
|
|
87
|
-
/* from the getCommentedDirectiveFromImportedModule utility */
|
|
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]} */
|
|
90
|
-
export const directivesArray = Array.from(directivesSet);
|
|
91
|
-
|
|
92
132
|
/* commentedDirectives_4RawImplementations */
|
|
93
133
|
|
|
94
134
|
// all formatting styles as an array of [prefix, quote, suffix]
|
|
@@ -97,20 +137,19 @@ const commentStyles = [
|
|
|
97
137
|
[`// `, `"`, ``], // V2: `// "directive"`
|
|
98
138
|
[`/* `, `'`, ` */`], // V3: `/* 'directive' */`
|
|
99
139
|
[`/* `, `"`, ` */`], // V4: `/* "directive" */`
|
|
100
|
-
];
|
|
140
|
+
]; // further inference optimation can be made but is overkill...
|
|
101
141
|
|
|
102
142
|
/**
|
|
103
143
|
* Makes the array of all four accepted commented directive implementations on a directive basis.
|
|
104
144
|
* @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
|
|
145
|
+
* @returns The array of formatted commented directives.
|
|
106
146
|
*/
|
|
107
147
|
const make4RawImplementations = (directive) =>
|
|
108
148
|
commentStyles.map(
|
|
109
149
|
([prefix, quote, suffix]) =>
|
|
110
150
|
`${prefix}${quote}${directive}${quote}${suffix}`
|
|
111
|
-
);
|
|
151
|
+
); // ...further inference optimation could be an extra challenge but would probably require TypeScript for comfort
|
|
112
152
|
|
|
113
|
-
// mapped commented directives to their 4 raw implementations
|
|
114
153
|
export const commentedDirectives_4RawImplementations = Object.freeze({
|
|
115
154
|
[USE_SERVER_LOGICS]: make4RawImplementations(USE_SERVER_LOGICS),
|
|
116
155
|
[USE_CLIENT_LOGICS]: make4RawImplementations(USE_CLIENT_LOGICS),
|
|
@@ -124,37 +163,35 @@ export const commentedDirectives_4RawImplementations = Object.freeze({
|
|
|
124
163
|
[USE_AGNOSTIC_STRATEGIES]: make4RawImplementations(USE_AGNOSTIC_STRATEGIES),
|
|
125
164
|
});
|
|
126
165
|
|
|
127
|
-
|
|
128
|
-
export const AT_SERVER_LOGICS = "@serverLogics";
|
|
129
|
-
export const AT_CLIENT_LOGICS = "@clientLogics";
|
|
130
|
-
export const AT_AGNOSTIC_LOGICS = "@agnosticLogics";
|
|
131
|
-
export const AT_SERVER_COMPONENTS = "@serverComponents";
|
|
132
|
-
export const AT_CLIENT_COMPONENTS = "@clientComponents";
|
|
133
|
-
export const AT_AGNOSTIC_COMPONENTS = "@agnosticComponents";
|
|
134
|
-
export const AT_SERVER_FUNCTIONS = "@serverFunctions";
|
|
135
|
-
export const AT_CLIENT_CONTEXTS = "@clientContexts";
|
|
136
|
-
export const AT_AGNOSTIC_CONDITIONS = "@agnosticConditions";
|
|
166
|
+
/* commentedDirectives_VerificationReports */
|
|
137
167
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
[
|
|
147
|
-
[
|
|
148
|
-
[
|
|
168
|
+
const MODULES_MARKED_WITH_THE_ = "modules marked with the";
|
|
169
|
+
const _DIRECTIVE_MUST_HAVE_A_NON_JSX_FILE_EXTENSION =
|
|
170
|
+
"directive must have a non-JSX file extension";
|
|
171
|
+
const _DIRECTIVE_MUST_HAVE_A_JSX_FILE_EXTENSION =
|
|
172
|
+
"directive must have a JSX file extension";
|
|
173
|
+
|
|
174
|
+
export const commentedDirectives_VerificationReports = Object.freeze({
|
|
175
|
+
// somehow doing it by hand is better for type inference in raw JS
|
|
176
|
+
[USE_SERVER_LOGICS]: `${MODULES_MARKED_WITH_THE_} "${USE_SERVER_LOGICS}" ${_DIRECTIVE_MUST_HAVE_A_NON_JSX_FILE_EXTENSION}.`,
|
|
177
|
+
[USE_CLIENT_LOGICS]: `${MODULES_MARKED_WITH_THE_} "${USE_CLIENT_LOGICS}" ${_DIRECTIVE_MUST_HAVE_A_NON_JSX_FILE_EXTENSION}.`,
|
|
178
|
+
[USE_AGNOSTIC_LOGICS]: `${MODULES_MARKED_WITH_THE_} "${USE_AGNOSTIC_LOGICS}" ${_DIRECTIVE_MUST_HAVE_A_NON_JSX_FILE_EXTENSION}.`,
|
|
179
|
+
[USE_SERVER_COMPONENTS]: `${MODULES_MARKED_WITH_THE_} "${USE_SERVER_COMPONENTS}" ${_DIRECTIVE_MUST_HAVE_A_JSX_FILE_EXTENSION}.`,
|
|
180
|
+
[USE_CLIENT_COMPONENTS]: `${MODULES_MARKED_WITH_THE_} "${USE_CLIENT_COMPONENTS}" ${_DIRECTIVE_MUST_HAVE_A_JSX_FILE_EXTENSION}.`,
|
|
181
|
+
[USE_AGNOSTIC_COMPONENTS]: `${MODULES_MARKED_WITH_THE_} "${USE_AGNOSTIC_COMPONENTS}" ${_DIRECTIVE_MUST_HAVE_A_JSX_FILE_EXTENSION}.`,
|
|
182
|
+
[USE_SERVER_FUNCTIONS]: `${MODULES_MARKED_WITH_THE_} "${USE_SERVER_FUNCTIONS}" ${_DIRECTIVE_MUST_HAVE_A_NON_JSX_FILE_EXTENSION}.`,
|
|
183
|
+
[USE_CLIENT_CONTEXTS]: `${MODULES_MARKED_WITH_THE_} "${USE_CLIENT_CONTEXTS}" ${_DIRECTIVE_MUST_HAVE_A_JSX_FILE_EXTENSION}.`,
|
|
184
|
+
[USE_AGNOSTIC_CONDITIONS]: `${MODULES_MARKED_WITH_THE_} "${USE_AGNOSTIC_CONDITIONS}" ${_DIRECTIVE_MUST_HAVE_A_JSX_FILE_EXTENSION}.`,
|
|
185
|
+
[USE_AGNOSTIC_STRATEGIES]: `${MODULES_MARKED_WITH_THE_} "${USE_AGNOSTIC_STRATEGIES}" directive are free to have the file extension of their choosing. (This is not a problem and should never surface.)`,
|
|
149
186
|
});
|
|
150
187
|
|
|
151
|
-
/*
|
|
188
|
+
/* commentedDirectives_BlockedImports */
|
|
152
189
|
|
|
153
190
|
/**
|
|
154
191
|
* Makes the intro for each specific import rule violation messages.
|
|
155
192
|
* @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.
|
|
156
193
|
* @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.
|
|
157
|
-
* @returns
|
|
194
|
+
* @returns "[Current file commented modules] are not allowed to import [imported file commented modules]."
|
|
158
195
|
*/
|
|
159
196
|
const makeIntroForSpecificViolationMessage = (
|
|
160
197
|
currentFileCommentedDirective,
|
|
@@ -379,7 +416,7 @@ export const commentedDirectives_BlockedImports = Object.freeze({
|
|
|
379
416
|
message: `${makeIntroForSpecificViolationMessage(
|
|
380
417
|
USE_SERVER_FUNCTIONS,
|
|
381
418
|
USE_AGNOSTIC_CONDITIONS
|
|
382
|
-
)} (Special) Agnostic Conditions Components aren't allowed (Special)
|
|
419
|
+
)} (Special) Agnostic Conditions Components aren't allowed because (Special) Server Functions have no business working with React Components.`,
|
|
383
420
|
},
|
|
384
421
|
],
|
|
385
422
|
[USE_CLIENT_CONTEXTS]: [
|
|
@@ -397,13 +434,13 @@ export const commentedDirectives_BlockedImports = Object.freeze({
|
|
|
397
434
|
message: `${makeIntroForSpecificViolationMessage(
|
|
398
435
|
USE_CLIENT_CONTEXTS,
|
|
399
436
|
USE_SERVER_COMPONENTS
|
|
400
|
-
)} Lineal Server Components may only pass through Client Contexts Components via the children prop within Server Components Modules.`,
|
|
437
|
+
)} Lineal Server Components may only pass through (Special) Client Contexts Components via the children prop within Server Components Modules.`,
|
|
401
438
|
},
|
|
402
439
|
// USE_CLIENT_COMPONENTS allowed, because Lineal Client Components can create client boundaries within (Special) Client Contexts Components.
|
|
403
440
|
// USE_AGNOSTIC_COMPONENTS allowed, because Lineal Agnostic Components can render safely on the client just like they can on the server.
|
|
404
441
|
// USE_SERVER_FUNCTIONS allowed, because (Special) Server Functions are specifically triggered by Client Components.
|
|
405
442
|
// USE_CLIENT_CONTEXTS allowed, because (Special) Client Contexts Components can compose with one another.
|
|
406
|
-
// USE_AGNOSTIC_CONDITIONS allowed, because (Special) Agnostic Conditions Components, as if they were Lineal Agnostic Components themselves, can render safely on the client just like they can on the server, in a mechanism that allows Client Contexts Components to safely and indirectly compose with child Server Components within Client Contexts Modules.
|
|
443
|
+
// USE_AGNOSTIC_CONDITIONS allowed, because (Special) Agnostic Conditions Components, as if they were Lineal Agnostic Components themselves, can render safely on the client just like they can on the server, in a mechanism that allows (Special) Client Contexts Components to safely and indirectly compose with child Server Components within Client Contexts Modules.
|
|
407
444
|
],
|
|
408
445
|
[USE_AGNOSTIC_CONDITIONS]: [
|
|
409
446
|
{
|
|
@@ -444,25 +481,3 @@ export const commentedDirectives_BlockedImports = Object.freeze({
|
|
|
444
481
|
// (Special) Agnostic Strategies Modules can import all known modules, except themselves since they cannot be imported as they are, only as and via Strategies. (Since Agnostic Strategies Modules cannot be imported as they are, there is no such things as a 'use agnostic strategies' importFileCommentedDirective.)
|
|
445
482
|
],
|
|
446
483
|
});
|
|
447
|
-
|
|
448
|
-
/* from the currentFileFlow flow */
|
|
449
|
-
|
|
450
|
-
const MODULES_MARKED_WITH_THE_ = "modules marked with the";
|
|
451
|
-
const _DIRECTIVE_MUST_HAVE_A_NON_JSX_FILE_EXTENSION =
|
|
452
|
-
"directive must have a non-JSX file extension";
|
|
453
|
-
const _DIRECTIVE_MUST_HAVE_A_JSX_FILE_EXTENSION =
|
|
454
|
-
"directive must have a JSX file extension";
|
|
455
|
-
|
|
456
|
-
export const commentedDirectives_VerificationReports = Object.freeze({
|
|
457
|
-
// somehow doing it by hand is better for type inference in raw JS
|
|
458
|
-
[USE_SERVER_LOGICS]: `${MODULES_MARKED_WITH_THE_} "${USE_SERVER_LOGICS}" ${_DIRECTIVE_MUST_HAVE_A_NON_JSX_FILE_EXTENSION}.`,
|
|
459
|
-
[USE_CLIENT_LOGICS]: `${MODULES_MARKED_WITH_THE_} "${USE_CLIENT_LOGICS}" ${_DIRECTIVE_MUST_HAVE_A_NON_JSX_FILE_EXTENSION}.`,
|
|
460
|
-
[USE_AGNOSTIC_LOGICS]: `${MODULES_MARKED_WITH_THE_} "${USE_AGNOSTIC_LOGICS}" ${_DIRECTIVE_MUST_HAVE_A_NON_JSX_FILE_EXTENSION}.`,
|
|
461
|
-
[USE_SERVER_COMPONENTS]: `${MODULES_MARKED_WITH_THE_} "${USE_SERVER_COMPONENTS}" ${_DIRECTIVE_MUST_HAVE_A_JSX_FILE_EXTENSION}.`,
|
|
462
|
-
[USE_CLIENT_COMPONENTS]: `${MODULES_MARKED_WITH_THE_} "${USE_CLIENT_COMPONENTS}" ${_DIRECTIVE_MUST_HAVE_A_JSX_FILE_EXTENSION}.`,
|
|
463
|
-
[USE_AGNOSTIC_COMPONENTS]: `${MODULES_MARKED_WITH_THE_} "${USE_AGNOSTIC_COMPONENTS}" ${_DIRECTIVE_MUST_HAVE_A_JSX_FILE_EXTENSION}.`,
|
|
464
|
-
[USE_SERVER_FUNCTIONS]: `${MODULES_MARKED_WITH_THE_} "${USE_SERVER_FUNCTIONS}" ${_DIRECTIVE_MUST_HAVE_A_NON_JSX_FILE_EXTENSION}.`,
|
|
465
|
-
[USE_CLIENT_CONTEXTS]: `${MODULES_MARKED_WITH_THE_} "${USE_CLIENT_CONTEXTS}" ${_DIRECTIVE_MUST_HAVE_A_JSX_FILE_EXTENSION}.`,
|
|
466
|
-
[USE_AGNOSTIC_CONDITIONS]: `${MODULES_MARKED_WITH_THE_} "${USE_AGNOSTIC_CONDITIONS}" ${_DIRECTIVE_MUST_HAVE_A_JSX_FILE_EXTENSION}.`,
|
|
467
|
-
[USE_AGNOSTIC_STRATEGIES]: `${MODULES_MARKED_WITH_THE_} "${USE_AGNOSTIC_STRATEGIES}" directive are free to have the file extension of their choosing. (This is not a problem and should never surface.)`,
|
|
468
|
-
});
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
/**
|
|
49
49
|
* 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.
|
|
50
50
|
* @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.
|
|
51
|
-
* @returns {{skip: true; verifiedCommentedDirective: undefined;} | {skip: undefined; verifiedCommentedDirective: 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;}}
|
|
51
|
+
* @returns {{skip: true; verifiedCommentedDirective: undefined;} | {skip: undefined; verifiedCommentedDirective: 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;}} Either an object with `skip: true` to disregard or one with the non-null `verifiedCommentedDirective`.
|
|
52
52
|
*/
|
|
53
53
|
export const currentFileFlow = (context) => {
|
|
54
54
|
// GETTING THE EXTENSION OF THE CURRENT FILE
|
|
@@ -95,9 +95,7 @@ export const currentFileFlow = (context) => {
|
|
|
95
95
|
return { skip: true };
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
return {
|
|
99
|
-
verifiedCommentedDirective,
|
|
100
|
-
};
|
|
98
|
+
return { verifiedCommentedDirective };
|
|
101
99
|
};
|
|
102
100
|
|
|
103
101
|
/* importedFileFlow */
|
|
@@ -106,7 +104,7 @@ export const currentFileFlow = (context) => {
|
|
|
106
104
|
* The flow that is shared between import and re-export traversals to obtain the import file's commented directive.
|
|
107
105
|
* @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.
|
|
108
106
|
* @param {import('@typescript-eslint/types').TSESTree.ImportDeclaration} node The ESLint `node` of the rule's current traversal.
|
|
109
|
-
* @returns {{skip: true; importedFileCommentedDirective: undefined;} | {skip: undefined; importedFileCommentedDirective: 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;}}
|
|
107
|
+
* @returns {{skip: true; importedFileCommentedDirective: undefined;} | {skip: undefined; importedFileCommentedDirective: 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;}} Either an object with `skip: true` to disregard or one with the non-null `importedFileCommentedDirective`.
|
|
110
108
|
*/
|
|
111
109
|
const importedFileFlow = (context, node) => {
|
|
112
110
|
// finds the full path of the import
|
|
@@ -131,18 +129,18 @@ const importedFileFlow = (context, node) => {
|
|
|
131
129
|
// returns early if there is no directive or no valid directive (same, but eventually no directive could have defaults)
|
|
132
130
|
if (!importedFileCommentedDirective) {
|
|
133
131
|
console.warn(
|
|
134
|
-
"The imported file, whose path has been resolved, has no directive. It is thus ignored since the report on that circumstance is available on the imported file itself."
|
|
132
|
+
"WARNING. The imported file, whose path has been resolved, has no directive. It is thus ignored since the report on that circumstance is available on the imported file itself."
|
|
135
133
|
);
|
|
136
134
|
return { skip: true };
|
|
137
135
|
}
|
|
138
136
|
|
|
139
137
|
/* GETTING THE CORRECT DIRECTIVE INTERPRETATION OF STRATEGY FOR AGNOSTIC STRATEGIES MODULES IMPORTS.
|
|
140
|
-
|
|
138
|
+
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.
|
|
141
139
|
|
|
142
140
|
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.
|
|
143
141
|
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
(Consequently, details below are currently at the stage of wishful thinking.)
|
|
143
|
+
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. */
|
|
146
144
|
if (importedFileCommentedDirective === USE_AGNOSTIC_STRATEGIES) {
|
|
147
145
|
const importingFileCommentedDirective = getStrategizedDirective(
|
|
148
146
|
context,
|
|
@@ -171,7 +169,7 @@ const importedFileFlow = (context, node) => {
|
|
|
171
169
|
* @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.
|
|
172
170
|
* @param {import('@typescript-eslint/types').TSESTree.ImportDeclaration} node The ESLint `node` of the rule's current traversal.
|
|
173
171
|
* @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.
|
|
174
|
-
* @returns
|
|
172
|
+
* @returns Early if the flow needs to be interrupted.
|
|
175
173
|
*/
|
|
176
174
|
export const importsFlow = (context, node, currentFileCommentedDirective) => {
|
|
177
175
|
// does not operate on `import type`
|
|
@@ -211,7 +209,7 @@ export const importsFlow = (context, node, currentFileCommentedDirective) => {
|
|
|
211
209
|
* @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.
|
|
212
210
|
* @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.
|
|
213
211
|
* @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.
|
|
214
|
-
* @returns
|
|
212
|
+
* @returns Early if the flow needs to be interrupted.
|
|
215
213
|
*/
|
|
216
214
|
export const allExportsFlow = (
|
|
217
215
|
context,
|
|
@@ -250,9 +248,9 @@ export const allExportsFlow = (
|
|
|
250
248
|
node,
|
|
251
249
|
messageId: reExportNotSameMessageId,
|
|
252
250
|
data: {
|
|
253
|
-
// currentFileCommentedDirective
|
|
251
|
+
// currentFileCommentedDirective:
|
|
254
252
|
currentFileCommentedDirective,
|
|
255
|
-
// importedFileCommentedDirective
|
|
253
|
+
// importedFileCommentedDirective:
|
|
256
254
|
importedFileCommentedDirective,
|
|
257
255
|
},
|
|
258
256
|
});
|
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
USE_CLIENT_CONTEXTS,
|
|
18
18
|
USE_AGNOSTIC_CONDITIONS,
|
|
19
19
|
USE_AGNOSTIC_STRATEGIES,
|
|
20
|
-
directivesSet,
|
|
21
20
|
directivesArray,
|
|
21
|
+
strategiesArray,
|
|
22
22
|
commentedDirectives_4RawImplementations,
|
|
23
23
|
commentedStrategies_CommentedDirectives,
|
|
24
24
|
commentedDirectives_BlockedImports,
|
|
@@ -34,6 +34,45 @@ import {
|
|
|
34
34
|
|
|
35
35
|
/* getCommentedDirectiveFromCurrentModule */
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Detects whether a string is single- or double-quoted.
|
|
39
|
+
* @param {string} string
|
|
40
|
+
* @returns
|
|
41
|
+
*/
|
|
42
|
+
const detectQuoteType = (string) => {
|
|
43
|
+
if (string.startsWith("'") && string.endsWith("'")) {
|
|
44
|
+
return true; // single quotes
|
|
45
|
+
} else if (string.startsWith('"') && string.endsWith('"')) {
|
|
46
|
+
return false; // double quotes
|
|
47
|
+
} else {
|
|
48
|
+
return null; // neither
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Removes single quotes from a string known to be single-quoted.
|
|
54
|
+
* @param {string} string
|
|
55
|
+
* @returns
|
|
56
|
+
*/
|
|
57
|
+
const stripSingleQuotes = (string) => {
|
|
58
|
+
if (string.startsWith("'") && string.endsWith("'")) {
|
|
59
|
+
return string.slice(1, -1);
|
|
60
|
+
}
|
|
61
|
+
return string;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Removes double quotes from a string known to be double-quoted.
|
|
66
|
+
* @param {string} string
|
|
67
|
+
* @returns
|
|
68
|
+
*/
|
|
69
|
+
const stripDoubleQuotes = (string) => {
|
|
70
|
+
if (string.startsWith('"') && string.endsWith('"')) {
|
|
71
|
+
return string.slice(1, -1);
|
|
72
|
+
}
|
|
73
|
+
return string;
|
|
74
|
+
};
|
|
75
|
+
|
|
37
76
|
/**
|
|
38
77
|
* Gets the commented directive of the current module.
|
|
39
78
|
*
|
|
@@ -50,7 +89,7 @@ import {
|
|
|
50
89
|
* - `'use agnostic conditions'`, `"use agnostic conditions"` denoting an Agnostic Conditions Module.
|
|
51
90
|
* - `'use agnostic strategies'`, `"use agnostic strategies"` denoting an Agnostic Strategies Module.
|
|
52
91
|
* @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.
|
|
53
|
-
* @returns
|
|
92
|
+
* @returns The commented directive, or lack thereof via `null`. Given the strictness of this architecture, the lack of a directive is considered a mistake. (Though rules may provide the opportunity to declare a default, and configs with preset defaults may become provided.)
|
|
54
93
|
*/
|
|
55
94
|
export const getCommentedDirectiveFromCurrentModule = (context) => {
|
|
56
95
|
// gets the first comment from the source code
|
|
@@ -78,50 +117,12 @@ export const getCommentedDirectiveFromCurrentModule = (context) => {
|
|
|
78
117
|
: stripDoubleQuotes(rawValue);
|
|
79
118
|
|
|
80
119
|
// certifies the directive or lack thereof from the obtained value
|
|
81
|
-
const commentedDirective =
|
|
120
|
+
const commentedDirective =
|
|
121
|
+
directivesArray.find((directive) => directive === value) ?? null;
|
|
82
122
|
|
|
83
123
|
return commentedDirective;
|
|
84
124
|
};
|
|
85
125
|
|
|
86
|
-
/**
|
|
87
|
-
* Detects whether a string is single- or double-quoted.
|
|
88
|
-
* @param {string} string
|
|
89
|
-
* @returns
|
|
90
|
-
*/
|
|
91
|
-
const detectQuoteType = (string) => {
|
|
92
|
-
if (string.startsWith("'") && string.endsWith("'")) {
|
|
93
|
-
return true; // single quotes
|
|
94
|
-
} else if (string.startsWith('"') && string.endsWith('"')) {
|
|
95
|
-
return false; // double quotes
|
|
96
|
-
} else {
|
|
97
|
-
return null; // neither
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Removes single quotes from a string known to be single-quoted.
|
|
103
|
-
* @param {string} string
|
|
104
|
-
* @returns
|
|
105
|
-
*/
|
|
106
|
-
const stripSingleQuotes = (string) => {
|
|
107
|
-
if (string.startsWith("'") && string.endsWith("'")) {
|
|
108
|
-
return string.slice(1, -1);
|
|
109
|
-
}
|
|
110
|
-
return string;
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Removes double quotes from a string known to be double-quoted.
|
|
115
|
-
* @param {string} string
|
|
116
|
-
* @returns
|
|
117
|
-
*/
|
|
118
|
-
const stripDoubleQuotes = (string) => {
|
|
119
|
-
if (string.startsWith('"') && string.endsWith('"')) {
|
|
120
|
-
return string.slice(1, -1);
|
|
121
|
-
}
|
|
122
|
-
return string;
|
|
123
|
-
};
|
|
124
|
-
|
|
125
126
|
/* getVerifiedCommentedDirective */
|
|
126
127
|
|
|
127
128
|
/**
|
|
@@ -138,7 +139,7 @@ const stripDoubleQuotes = (string) => {
|
|
|
138
139
|
* - `'use agnostic strategies'`: Agnostic Strategies Modules may export JSX.
|
|
139
140
|
* @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 as written on top of the file (cannot be `null` at that stage).
|
|
140
141
|
* @param {TSX | TS | JSX | JS | MJS | CJS} extension The JavaScript (TypeScript) extension of the file.
|
|
141
|
-
* @returns
|
|
142
|
+
* @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.
|
|
142
143
|
*/
|
|
143
144
|
export const getVerifiedCommentedDirective = (directive, extension) => {
|
|
144
145
|
// I could use a map, but because this is in JS with JSDoc, a manual solution is peculiarly more typesafe.
|
|
@@ -183,7 +184,7 @@ export const getVerifiedCommentedDirective = (directive, extension) => {
|
|
|
183
184
|
* - `'use agnostic conditions'`, `"use agnostic conditions"` denoting an Agnostic Conditions Module.
|
|
184
185
|
* - `'use agnostic strategies'`, `"use agnostic strategies"` denoting an Agnostic Strategies Module.
|
|
185
186
|
* @param {string} resolvedImportPath The resolved path of the import.
|
|
186
|
-
* @returns {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 | null} The commented directive, or lack thereof via `null`. Given the strictness of this architecture, the lack of a directive is considered a mistake. (Though rules may provide the opportunity to declare a default, and configs with preset defaults may
|
|
187
|
+
* @returns {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 | null} The commented directive, or lack thereof via `null`. Given the strictness of this architecture, the lack of a directive is considered a mistake. (Though rules may provide the opportunity to declare a default, and configs with preset defaults may become provided.)
|
|
187
188
|
*/
|
|
188
189
|
export const getCommentedDirectiveFromImportedModule = (resolvedImportPath) => {
|
|
189
190
|
// gets the first line of the code of the import
|
|
@@ -231,7 +232,7 @@ export const getCommentedDirectiveFromImportedModule = (resolvedImportPath) => {
|
|
|
231
232
|
* Gets the interpreted directive from a specified commented Strategy (such as `@serverLogics`) nested inside the import declaration for an import from an Agnostic Strategies Module.
|
|
232
233
|
* @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.
|
|
233
234
|
* @param {import('@typescript-eslint/types').TSESTree.ImportDeclaration} node The ESLint `node` of the rule's current traversal.
|
|
234
|
-
* @returns
|
|
235
|
+
* @returns The interpreted directive, a.k.a. strategized directive, or lack thereof via `null`.
|
|
235
236
|
*/
|
|
236
237
|
export const getStrategizedDirective = (context, node) => {
|
|
237
238
|
const firstNestedComment = context.sourceCode.getCommentsInside(node)[0];
|
|
@@ -239,9 +240,17 @@ export const getStrategizedDirective = (context, node) => {
|
|
|
239
240
|
// returns null early if there is no nested comments
|
|
240
241
|
if (!firstNestedComment) return null;
|
|
241
242
|
|
|
242
|
-
const
|
|
243
|
+
const rawStrategy = firstNestedComment.value.trim() || "";
|
|
244
|
+
|
|
245
|
+
const strategy =
|
|
246
|
+
strategiesArray.find((strategy) => strategy === rawStrategy) ?? null;
|
|
247
|
+
|
|
248
|
+
// returns null early if no strategy was identified
|
|
249
|
+
if (!strategy) return null;
|
|
243
250
|
|
|
244
|
-
|
|
251
|
+
const commentedDirective = commentedStrategies_CommentedDirectives[strategy];
|
|
252
|
+
|
|
253
|
+
return commentedDirective;
|
|
245
254
|
};
|
|
246
255
|
|
|
247
256
|
/* isImportBlocked */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-use-agnostic",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
4
4
|
"description": "Highlights problematic server-client imports in projects made with the Fullstack React Architecture.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"author": "Luther Tchofo Safo <luther@tchofo-safo-portfolio.me>",
|
|
22
22
|
"files": [
|
|
23
|
-
"library"
|
|
23
|
+
"library",
|
|
24
|
+
"types/index.d.ts"
|
|
24
25
|
],
|
|
25
26
|
"exports": {
|
|
26
27
|
".": "./library/index.js"
|
|
@@ -48,5 +49,6 @@
|
|
|
48
49
|
"engines": {
|
|
49
50
|
"node": ">=18.18.0 <18.99 || >=20.9.0 <21.0.0 || >=21.1.0"
|
|
50
51
|
},
|
|
51
|
-
"type": "module"
|
|
52
|
+
"type": "module",
|
|
53
|
+
"types": "types/index.d.ts"
|
|
52
54
|
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ESLint } from "eslint";
|
|
2
|
+
|
|
3
|
+
declare const plugin: ESLint.Plugin;
|
|
4
|
+
|
|
5
|
+
export default plugin;
|
|
6
|
+
|
|
7
|
+
// must be manually maintained
|
|
8
|
+
export const useAgnosticPluginName: "use-agnostic";
|
|
9
|
+
export const agnostic20ConfigName: "agnostic20";
|
|
10
|
+
export const directive21ConfigName: "directive21";
|
|
11
|
+
export const enforceEffectiveDirectivesRuleName: "enforce-effective-directives-import-rules";
|
|
12
|
+
export const enforceCommentedDirectivesRuleName: "enforce-commented-directives-import-rules";
|