eslint-plugin-use-agnostic 1.7.5 → 1.7.9
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/comments.config.json +20 -0
- package/comments.config.mjs +21 -1
- package/jscomments/jsdoc/alias-variables.js +6 -0
- package/jscomments/jsdoc/comments.js +18 -7
- package/library/agnostic20/_commons/rules/import-rules.js +3 -1
- package/library/agnostic20/_commons/utilities/flows.js +103 -0
- package/library/directive21/_commons/rules/import-rules.js +3 -0
- package/library/directive21/_commons/utilities/flows.js +270 -13
- package/library/directive21/_commons/utilities/helpers.js +64 -1
- package/package.json +1 -1
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export const forAliasVariables = Object.freeze({
|
|
2
2
|
importsFlow:
|
|
3
3
|
"The full flow for import traversals to enforce effective directives import rules." /* $COMMENT#JSDOC#FORALIASVARIABLES#IMPORTSFLOW */,
|
|
4
|
+
importsFlowRequire:
|
|
5
|
+
"The `importsFlow` adapted for `require` calls to enforce effective directives import rules." /* $COMMENT#JSDOC#FORALIASVARIABLES#IMPORTSFLOWREQUIRE */,
|
|
6
|
+
importsFlowCommented:
|
|
7
|
+
"The full flow for import traversals to enforce commented directives import rules." /* $COMMENT#JSDOC#FORALIASVARIABLES#IMPORTSFLOWCOMMENTED */,
|
|
8
|
+
importsFlowCommentedRequire:
|
|
9
|
+
"The `importsFlow` adapted for `require` calls to enforce commented directives import rules." /* $COMMENT#JSDOC#FORALIASVARIABLES#IMPORTSFLOWCOMMENTEDREQUIRE */,
|
|
4
10
|
flowReturnsEarly:
|
|
5
11
|
"Early if the flow needs to be interrupted." /* $COMMENT#JSDOC#FORALIASVARIABLES#FLOWRETURNSEARLY */,
|
|
6
12
|
});
|
|
@@ -15,8 +15,10 @@ export const jsDocComments = Object.freeze({
|
|
|
15
15
|
"$COMMENT#JSDOC#FORCOMPOSEDVARIABLES#LISTSINMESSAGE $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#RESOLVED $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#MODULESINCOMPATIBLE $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#RESOLVED $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#MODULEBASEDON $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#RESOLVED $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#DIRECTIVEPERIOD" /* $COMMENT#JSDOC#DEFINITIONS#MAKEMESSAGEFROMCURRENTFILERESOLVEDDIRECTIVE */,
|
|
16
16
|
findSpecificViolationMessage:
|
|
17
17
|
"$COMMENT#JSDOC#FORCOMPOSEDVARIABLES#FINDTHEMESSAGE $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#RESOLVED $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#RULESBASEDON $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#RDBIPERIOD" /* $COMMENT#JSDOC#DEFINITIONS#FINDSPECIFICVIOLATIONMESSAGE */,
|
|
18
|
-
walkAST:
|
|
19
|
-
|
|
18
|
+
walkAST:
|
|
19
|
+
"Walks an AST with a given callback." /* $COMMENT#JSDOC#DEFINITIONS#WALKAST */,
|
|
20
|
+
visitNode:
|
|
21
|
+
"Recursively visits an AST node with a given callback." /* $COMMENT#JSDOC#DEFINITIONS#VISITNODE */,
|
|
20
22
|
analyzeExportsForReExports:
|
|
21
23
|
"Analyzes a source code's exports to detect re-exports." /* $COMMENT#JSDOC#DEFINITIONS#ANALYZEEXPORTSFORREEXPORTS */,
|
|
22
24
|
agnostic20: Object.freeze({
|
|
@@ -44,6 +46,8 @@ export const jsDocComments = Object.freeze({
|
|
|
44
46
|
"$COMMENT#JSDOC#FORCOMPOSEDVARIABLES#FLOWTHATBEGINS $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#EFFECTIVE $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#DIRECTIVE $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#OFCURRENTFILE $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#EFFECTIVE $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#DIRECTIVES $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#OFFILESITIMPORTS" /* $COMMENT#JSDOC#DEFINITIONS#AGNOSTIC20#CURRENTFILEFLOW */,
|
|
45
47
|
importedFileFlow:
|
|
46
48
|
"$COMMENT#JSDOC#FORCOMPOSEDVARIABLES#FLOWIMPORTREEXPORT $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#EFFECTIVE $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#DIRECTIVEPERIOD" /* $COMMENT#JSDOC#DEFINITIONS#AGNOSTIC20#IMPORTEDFILEFLOW */,
|
|
49
|
+
importedFileFlowRequire:
|
|
50
|
+
"The `importedFileFlow` adapted for `require` calls to obtain the import file's effective directive." /* $COMMENT#JSDOC#DEFINITIONS#AGNOSTIC20#IMPORTEDFILEFLOWREQUIRE */,
|
|
47
51
|
importsFlow:
|
|
48
52
|
"JSDOC#FORALIASVARIABLES#IMPORTSFLOW" /* $COMMENT#JSDOC#DEFINITIONS#AGNOSTIC20#IMPORTSFLOW */,
|
|
49
53
|
reExportsFlow:
|
|
@@ -88,6 +92,8 @@ export const jsDocComments = Object.freeze({
|
|
|
88
92
|
"$COMMENT#JSDOC#FORCOMPOSEDVARIABLES#FLOWTHATBEGINS $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#VERIFIEDCOMMENTED $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#DIRECTIVE $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#OFCURRENTFILE $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#VERIFIEDCOMMENTED $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#DIRECTIVES $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#OFFILESITIMPORTS" /* $COMMENT#JSDOC#DEFINITIONS#DIRECTIVE21#CURRENTFILEFLOW */,
|
|
89
93
|
importedFileFlow:
|
|
90
94
|
"$COMMENT#JSDOC#FORCOMPOSEDVARIABLES#FLOWIMPORTREEXPORT $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#COMMENTED $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#DIRECTIVEPERIOD" /* $COMMENT#JSDOC#DEFINITIONS#DIRECTIVE21#IMPORTEDFILEFLOW */,
|
|
95
|
+
importedFileFlowRequire:
|
|
96
|
+
"The `importedFileFlow` adapted for `require` calls to obtain the import file's commented directive." /* $COMMENT#JSDOC#DEFINITIONS#DIRECTIVE21#IMPORTEDFILEFLOWREQUIRE */,
|
|
91
97
|
importsFlow:
|
|
92
98
|
"JSDOC#FORALIASVARIABLES#IMPORTSFLOW" /* $COMMENT#JSDOC#DEFINITIONS#DIRECTIVE21#IMPORTSFLOW */,
|
|
93
99
|
allExportsFlow:
|
|
@@ -130,12 +136,17 @@ export const jsDocComments = Object.freeze({
|
|
|
130
136
|
extension:
|
|
131
137
|
"The JavaScript (TypeScript) extension of the file." /* $COMMENT#JSDOC#PARAMS#EXTENSION */,
|
|
132
138
|
node: "The ESLint `node` of the rule's current traversal." /* $COMMENT#JSDOC#PARAMS#NODE */,
|
|
133
|
-
sourceCodeA:
|
|
134
|
-
|
|
139
|
+
sourceCodeA:
|
|
140
|
+
"The `SourceCode` where the AST comes from." /* $COMMENT#JSDOC#PARAMS#SOURCECODEA */,
|
|
141
|
+
callback:
|
|
142
|
+
"The callback that runs during the walk." /* $COMMENT#JSDOC#PARAMS#CALLBACK */,
|
|
135
143
|
nodeB: "The node being visited." /* $COMMENT#JSDOC#PARAMS#NODEB */,
|
|
136
|
-
parent:
|
|
137
|
-
|
|
138
|
-
|
|
144
|
+
parent:
|
|
145
|
+
"The parent of the node being visited." /* $COMMENT#JSDOC#PARAMS#PARENT */,
|
|
146
|
+
visitorKeys:
|
|
147
|
+
"The visitor keys of the node being visited." /* $COMMENT#JSDOC#PARAMS#VISITORKEYS */,
|
|
148
|
+
sourceCodeB:
|
|
149
|
+
"The `SourceCode` to analyze." /* $COMMENT#JSDOC#PARAMS#SOURCECODEB */,
|
|
139
150
|
agnostic20: Object.freeze({
|
|
140
151
|
currentFileEffectiveDirective:
|
|
141
152
|
"$COMMENT#JSDOC#FORCOMPOSEDVARIABLES#THECURRENTFILE $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#EFFECTIVE $COMMENT#JSDOC#FORCOMPOSEDVARIABLES#DIRECTIVEPERIOD" /* $COMMENT#JSDOC#PARAMS#AGNOSTIC20#CURRENTFILEEFFECTIVEDIRECTIVE */,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
currentFileFlow,
|
|
15
15
|
importsFlow,
|
|
16
16
|
reExportsFlow,
|
|
17
|
+
importsFlowRequire,
|
|
17
18
|
} from "../utilities/flows.js";
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -54,7 +55,8 @@ Indeed, Server Functions Modules have no business exporting JSX. `,
|
|
|
54
55
|
ExportAllDeclaration: (node) =>
|
|
55
56
|
reExportsFlow(context, node, currentFileEffectiveDirective),
|
|
56
57
|
// Unlike directive21, no ExportDefaultDeclaration because ExportDefaultDeclaration don't have source. The reason they're addressed in directive21 is specifically for Agnostic Strategies.
|
|
57
|
-
|
|
58
|
+
CallExpression: (node) =>
|
|
59
|
+
importsFlowRequire(context, node, currentFileEffectiveDirective),
|
|
58
60
|
};
|
|
59
61
|
},
|
|
60
62
|
};
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').ImportDeclaration} ImportDeclaration
|
|
36
36
|
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').ExportNamedDeclaration} ExportNamedDeclaration
|
|
37
37
|
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').ExportAllDeclaration} ExportAllDeclaration
|
|
38
|
+
* @typedef {import('../../../../types/agnostic20/_commons/typedefs.js').CallExpression} CallExpression
|
|
38
39
|
*/
|
|
39
40
|
|
|
40
41
|
/* currentFileFlow */
|
|
@@ -144,6 +145,68 @@ const importedFileFlow = (context, node) => {
|
|
|
144
145
|
return { skip: undefined, importedFileEffectiveDirective };
|
|
145
146
|
};
|
|
146
147
|
|
|
148
|
+
// NEW!! Currently strictly adapted from importedFileFlow
|
|
149
|
+
/**
|
|
150
|
+
* The `importedFileFlow` adapted for `require` calls to obtain the import file's effective directive.
|
|
151
|
+
* @param {Context} context The ESLint rule's `context` object.
|
|
152
|
+
* @param {CallExpression} node The ESLint `node` of the rule's current traversal.
|
|
153
|
+
* @returns Either an object with `skip: true` to disregard or one with the non-null `importedFileEffectiveDirective`.
|
|
154
|
+
*/
|
|
155
|
+
const importedFileFlowRequire = (context, node) => {
|
|
156
|
+
const skipTrue = { ...skip, importedFileEffectiveDirective: undefined };
|
|
157
|
+
|
|
158
|
+
if (
|
|
159
|
+
node.callee.type === "Identifier" &&
|
|
160
|
+
node.callee.name === "require" &&
|
|
161
|
+
node.arguments.length === 1 &&
|
|
162
|
+
node.arguments[0].type === "Literal"
|
|
163
|
+
) {
|
|
164
|
+
const importPath = node.arguments[0].value;
|
|
165
|
+
|
|
166
|
+
if (typeof importPath !== "string") return skipTrue;
|
|
167
|
+
|
|
168
|
+
// finds the full path of the import
|
|
169
|
+
const resolvedImportPath = resolveImportingPath(
|
|
170
|
+
path.dirname(context.filename),
|
|
171
|
+
importPath,
|
|
172
|
+
findUpSync("tsconfig.json", {
|
|
173
|
+
cwd: path.dirname(context.filename),
|
|
174
|
+
}) ?? context.cwd
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// does not operate on paths it did not resolve
|
|
178
|
+
if (resolvedImportPath === null) return skipTrue;
|
|
179
|
+
// does not operate on non-JS files
|
|
180
|
+
const isImportedFileJS = EXTENSIONS.some((ext) =>
|
|
181
|
+
resolvedImportPath.endsWith(ext)
|
|
182
|
+
);
|
|
183
|
+
if (!isImportedFileJS) return skipTrue;
|
|
184
|
+
|
|
185
|
+
// GETTING THE DIRECTIVE (or lack thereof) OF THE IMPORTED FILE
|
|
186
|
+
const importedFileDirective =
|
|
187
|
+
getDirectiveFromImportedModule(resolvedImportPath) ?? NO_DIRECTIVE;
|
|
188
|
+
|
|
189
|
+
// GETTING THE EXTENSION OF THE IMPORTED FILE
|
|
190
|
+
const importedFileFileExtension = path.extname(resolvedImportPath);
|
|
191
|
+
|
|
192
|
+
// GETTING THE EFFECTIVE DIRECTIVE (no lack thereof) OF THE IMPORTED FILE
|
|
193
|
+
const importedFileEffectiveDirective = getEffectiveDirective(
|
|
194
|
+
importedFileDirective,
|
|
195
|
+
importedFileFileExtension
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
// also fails if one of the seven effective directives has not been obtained
|
|
199
|
+
if (importedFileEffectiveDirective === null) {
|
|
200
|
+
console.error("ERROR. Effective directive should never be null.");
|
|
201
|
+
return skipTrue;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// For now skipping on both "does not operate" (which should ignore) and "fails" (which should crash) albeit with console.error.
|
|
205
|
+
|
|
206
|
+
return { skip: undefined, importedFileEffectiveDirective };
|
|
207
|
+
} else return skipTrue;
|
|
208
|
+
};
|
|
209
|
+
|
|
147
210
|
/* importsFlow */
|
|
148
211
|
|
|
149
212
|
/** The full flow for import traversals to enforce effective directives import rules.
|
|
@@ -184,6 +247,46 @@ export const importsFlow = (context, node, currentFileEffectiveDirective) => {
|
|
|
184
247
|
}
|
|
185
248
|
};
|
|
186
249
|
|
|
250
|
+
// NEW!! Currently strictly adapted from importsFlow
|
|
251
|
+
/** The `importsFlow` adapted for `require` calls to enforce effective directives import rules.
|
|
252
|
+
* @param {Context} context The ESLint rule's `context` object.
|
|
253
|
+
* @param {CallExpression} node The ESLint `node` of the rule's current traversal.
|
|
254
|
+
* @param {EffectiveDirective} currentFileEffectiveDirective The current file's effective directive.
|
|
255
|
+
* @returns Early if the flow needs to be interrupted.
|
|
256
|
+
*/
|
|
257
|
+
export const importsFlowRequire = (
|
|
258
|
+
context,
|
|
259
|
+
node,
|
|
260
|
+
currentFileEffectiveDirective
|
|
261
|
+
) => {
|
|
262
|
+
const result = importedFileFlowRequire(context, node);
|
|
263
|
+
|
|
264
|
+
if (result.skip) return;
|
|
265
|
+
const { importedFileEffectiveDirective } = result;
|
|
266
|
+
|
|
267
|
+
if (
|
|
268
|
+
isImportBlocked(
|
|
269
|
+
currentFileEffectiveDirective,
|
|
270
|
+
importedFileEffectiveDirective
|
|
271
|
+
)
|
|
272
|
+
) {
|
|
273
|
+
context.report({
|
|
274
|
+
node,
|
|
275
|
+
messageId: importBreaksEffectiveImportRulesMessageId,
|
|
276
|
+
data: {
|
|
277
|
+
[effectiveDirectiveMessage]:
|
|
278
|
+
makeMessageFromCurrentFileEffectiveDirective(
|
|
279
|
+
currentFileEffectiveDirective
|
|
280
|
+
),
|
|
281
|
+
[specificViolationMessage]: findSpecificViolationMessage(
|
|
282
|
+
currentFileEffectiveDirective,
|
|
283
|
+
importedFileEffectiveDirective
|
|
284
|
+
),
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
187
290
|
/* reExportsFlow */
|
|
188
291
|
|
|
189
292
|
/** The full flow for export traversals, shared between `ExportNamedDeclaration` and `ExportAllDeclaration`, to ensure same effective directive re-exports.
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
currentFileFlow,
|
|
25
25
|
importsFlow,
|
|
26
26
|
allExportsFlow,
|
|
27
|
+
importsFlowRequire,
|
|
27
28
|
} from "../utilities/flows.js";
|
|
28
29
|
|
|
29
30
|
/**
|
|
@@ -73,6 +74,8 @@ Please include a Strategy that corresponds to the kind of module this export wou
|
|
|
73
74
|
allExportsFlow(context, node, verifiedCommentedDirective),
|
|
74
75
|
ExportDefaultDeclaration: (node) =>
|
|
75
76
|
allExportsFlow(context, node, verifiedCommentedDirective),
|
|
77
|
+
CallExpression: (node) =>
|
|
78
|
+
importsFlowRequire(context, node, verifiedCommentedDirective),
|
|
76
79
|
};
|
|
77
80
|
},
|
|
78
81
|
};
|
|
@@ -52,11 +52,72 @@ import { analyzeExportsForReExports } from "./analyze-exports-re.js";
|
|
|
52
52
|
* @typedef {import('../../../../types/directive21/_commons/typedefs.js').ExportNamedDeclaration} ExportNamedDeclaration
|
|
53
53
|
* @typedef {import('../../../../types/directive21/_commons/typedefs.js').ExportAllDeclaration} ExportAllDeclaration
|
|
54
54
|
* @typedef {import('../../../../types/directive21/_commons/typedefs.js').ExportDefaultDeclaration} ExportDefaultDeclaration
|
|
55
|
+
* @typedef {import('../../../../types/directive21/_commons/typedefs.js').CallExpression} CallExpression
|
|
55
56
|
* @typedef {import('../../../../types/directive21/_commons/typedefs.js').Environment} Environment
|
|
56
57
|
*/
|
|
57
58
|
|
|
58
59
|
/* currentFileFlow */
|
|
59
60
|
|
|
61
|
+
// copied from eXtra JSX (further proving that all core constants and utilities from eXtra JSX should live inside use-agnostic in v2)
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @type {readonly [".x.jsx", ".x.cjsx", ".x.mjsx", ".x.tsx", ".x.ctsx", ".x.mtsx"]}
|
|
65
|
+
*/
|
|
66
|
+
export const eXtraJsxExtensions = Object.freeze([
|
|
67
|
+
".x.jsx",
|
|
68
|
+
".x.cjsx",
|
|
69
|
+
".x.mjsx",
|
|
70
|
+
".x.tsx",
|
|
71
|
+
".x.ctsx",
|
|
72
|
+
".x.mtsx",
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @type {readonly [".x.js", ".x.cjs", ".x.mjs", ".x.ts", ".x.cts", ".x.mts"]}
|
|
77
|
+
*/
|
|
78
|
+
export const eXtraJsExtensions = Object.freeze([
|
|
79
|
+
".x.js",
|
|
80
|
+
".x.cjs",
|
|
81
|
+
".x.mjs",
|
|
82
|
+
".x.ts",
|
|
83
|
+
".x.cts",
|
|
84
|
+
".x.mts",
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @type {readonly [".x.jsx", ".x.cjsx", ".x.mjsx", ".x.tsx", ".x.ctsx", ".x.mtsx", ".x.js", ".x.cjs", ".x.mjs", ".x.ts", ".x.cts", ".x.mts"]}
|
|
89
|
+
*/
|
|
90
|
+
export const extraJavaScriptExtensions = Object.freeze([
|
|
91
|
+
...eXtraJsxExtensions,
|
|
92
|
+
...eXtraJsExtensions,
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* $COMMENT#JSDOC#CORE#DEFS#FILEISANYJAVASCRIPT
|
|
97
|
+
* @param {string} filePath $COMMENT#JSDOC#CORE#PARAMS#FILEPATH
|
|
98
|
+
* @returns $COMMENT#JSDOC#CORE#RETURNS#FILEISANYJAVASCRIPT
|
|
99
|
+
*/
|
|
100
|
+
export const fileIsAnyJavaScript = (filePath) =>
|
|
101
|
+
EXTENSIONS.some((e) => filePath.endsWith(e));
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* $COMMENT#JSDOC#CORE#DEFS#FILEISEXTRAJAVASCRIPT
|
|
105
|
+
* @param {string} filePath $COMMENT#JSDOC#CORE#PARAMS#FILEPATH
|
|
106
|
+
* @returns $COMMENT#JSDOC#CORE#RETURNS#FILEISEXTRAJAVASCRIPT
|
|
107
|
+
*/
|
|
108
|
+
export const fileIsExtraJavaScript = (filePath) =>
|
|
109
|
+
extraJavaScriptExtensions.some((e) => filePath.endsWith(e));
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* $COMMENT#JSDOC#CORE#DEFS#FILEISREGULARJAVASCRIPT
|
|
113
|
+
* @param {string} filePath $COMMENT#JSDOC#CORE#PARAMS#FILEPATH
|
|
114
|
+
* @returns $COMMENT#JSDOC#CORE#RETURNS#FILEISREGULARJAVASCRIPT
|
|
115
|
+
*/
|
|
116
|
+
export const fileIsRegularJavaScript = (filePath) =>
|
|
117
|
+
fileIsAnyJavaScript(filePath) && !fileIsExtraJavaScript(filePath);
|
|
118
|
+
|
|
119
|
+
//
|
|
120
|
+
|
|
60
121
|
/**
|
|
61
122
|
* The flow that begins the import rules enforcement rule, retrieving the verified commented directive of the current file before comparing it to upcoming verified commented directives of the files it imports.
|
|
62
123
|
* @param {Context} context The ESLint rule's `context` object.
|
|
@@ -130,16 +191,24 @@ export const currentFileFlow = (context) => {
|
|
|
130
191
|
return { skip: undefined, verifiedCommentedDirective }; // at this time, behaves as if the new implementation didn't exist yet
|
|
131
192
|
}
|
|
132
193
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
verifiedCommentedDirective
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
194
|
+
// NEW
|
|
195
|
+
// do not report if the module is a non-Extra JavaScript Agnostic Strategies Module, in order to allow them the freedom of doing whatever they want so they can behave in any which way they need to as convention files
|
|
196
|
+
if (
|
|
197
|
+
!(
|
|
198
|
+
fileIsRegularJavaScript(context.filename) &&
|
|
199
|
+
verifiedCommentedDirective === USE_AGNOSTIC_STRATEGIES
|
|
200
|
+
)
|
|
201
|
+
)
|
|
202
|
+
context.report({
|
|
203
|
+
loc: highlightFirstLineOfCode(context),
|
|
204
|
+
messageId: commentedDirectiveReactDirectiveFailedMessageId,
|
|
205
|
+
data: {
|
|
206
|
+
// verifiedCommentedDirective
|
|
207
|
+
verifiedCommentedDirective,
|
|
208
|
+
// expectedReactDirectiveAsText
|
|
209
|
+
expectedReactDirectiveAsText,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
143
212
|
return skipTrue;
|
|
144
213
|
}
|
|
145
214
|
|
|
@@ -208,7 +277,10 @@ const importedFileFlow = (context, node) => {
|
|
|
208
277
|
node
|
|
209
278
|
);
|
|
210
279
|
|
|
211
|
-
if (
|
|
280
|
+
if (
|
|
281
|
+
importingFileCommentedDirective === null &&
|
|
282
|
+
fileIsExtraJavaScript(context.filename)
|
|
283
|
+
) {
|
|
212
284
|
context.report({
|
|
213
285
|
node,
|
|
214
286
|
messageId: importNotStrategizedMessageId,
|
|
@@ -243,10 +315,120 @@ const importedFileFlow = (context, node) => {
|
|
|
243
315
|
};
|
|
244
316
|
};
|
|
245
317
|
|
|
318
|
+
// NEW!! Currently strictly adapted from importedFileFlow
|
|
319
|
+
/**
|
|
320
|
+
* The `importedFileFlow` adapted for `require` calls to obtain the import file's commented directive.
|
|
321
|
+
* @param {Context} context The ESLint rule's `context` object.
|
|
322
|
+
* @param {CallExpression} node The ESLint `node` of the rule's current traversal.
|
|
323
|
+
* @returns Either an object with `skip: true` to disregard or one with the non-null `importedFileCommentedDirective`. And now with the added results of `analyzeExportsForReExports`.
|
|
324
|
+
*/
|
|
325
|
+
const importedFileFlowRequire = (context, node) => {
|
|
326
|
+
const skipTrue = {
|
|
327
|
+
...skip,
|
|
328
|
+
importedFileCommentedDirective: undefined,
|
|
329
|
+
analyzeExportsForReExportsResults: undefined,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
if (
|
|
333
|
+
node.callee.type === "Identifier" &&
|
|
334
|
+
node.callee.name === "require" &&
|
|
335
|
+
node.arguments.length === 1 &&
|
|
336
|
+
node.arguments[0].type === "Literal"
|
|
337
|
+
) {
|
|
338
|
+
const importPath = node.arguments[0].value;
|
|
339
|
+
|
|
340
|
+
if (typeof importPath !== "string") return skipTrue;
|
|
341
|
+
|
|
342
|
+
// finds the full path of the import
|
|
343
|
+
const resolvedImportPath = resolveImportingPath(
|
|
344
|
+
path.dirname(context.filename),
|
|
345
|
+
importPath,
|
|
346
|
+
findUpSync("tsconfig.json", {
|
|
347
|
+
cwd: path.dirname(context.filename),
|
|
348
|
+
}) ?? context.cwd
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
// does not operate on paths it did not resolve
|
|
352
|
+
if (resolvedImportPath === null) return skipTrue;
|
|
353
|
+
// does not operate on non-JS files
|
|
354
|
+
const isImportedFileJS = EXTENSIONS.some((ext) =>
|
|
355
|
+
resolvedImportPath.endsWith(ext)
|
|
356
|
+
);
|
|
357
|
+
if (!isImportedFileJS) return skipTrue;
|
|
358
|
+
|
|
359
|
+
// GETTING THE DIRECTIVE (or lack thereof) OF THE IMPORTED FILE
|
|
360
|
+
let {
|
|
361
|
+
commentedDirective: importedFileCommentedDirective,
|
|
362
|
+
sourceCode: importedFileSourceCode,
|
|
363
|
+
} = getCommentedDirectiveFromImportedModule(resolvedImportPath);
|
|
364
|
+
|
|
365
|
+
// returns early if there is no directive or no valid directive (same, but eventually no directive could have defaults)
|
|
366
|
+
if (!importedFileCommentedDirective) {
|
|
367
|
+
// Now silencing the warning as superfluous, in order to not warn on imports of files without a commented directive that are outside of linting range.
|
|
368
|
+
|
|
369
|
+
// console.warn(
|
|
370
|
+
// `WARNING. The imported file ${resolvedImportPath}, whose path has been resolved from ${context.filename}, has no commented directive. It is thus ignored since the report on that circumstance would be available on the imported file itself.`
|
|
371
|
+
// ); // The decision not to report has been taken to not inflate the number of warnings.
|
|
372
|
+
return skipTrue;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/* GETTING THE CORRECT DIRECTIVE INTERPRETATION OF STRATEGY FOR AGNOSTIC STRATEGIES MODULES IMPORTS.
|
|
376
|
+
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.
|
|
377
|
+
|
|
378
|
+
After a short attempt, the feature to address this (crossingStrategies) is currently canceled, mainly due to the exponential complexity provided by the different ways in which exports can be made in JavaScript.
|
|
379
|
+
|
|
380
|
+
(Consequently, details below are currently at the stage of wishful thinking.)
|
|
381
|
+
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. */
|
|
382
|
+
|
|
383
|
+
if (importedFileCommentedDirective === USE_AGNOSTIC_STRATEGIES) {
|
|
384
|
+
const importingFileCommentedDirective = getStrategizedDirective(
|
|
385
|
+
context,
|
|
386
|
+
node
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
if (
|
|
390
|
+
importingFileCommentedDirective === null &&
|
|
391
|
+
fileIsExtraJavaScript(context.filename)
|
|
392
|
+
) {
|
|
393
|
+
context.report({
|
|
394
|
+
node,
|
|
395
|
+
messageId: importNotStrategizedMessageId,
|
|
396
|
+
});
|
|
397
|
+
return skipTrue;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// 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.
|
|
401
|
+
importedFileCommentedDirective = importingFileCommentedDirective;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// you never know
|
|
405
|
+
if (!importedFileSourceCode) {
|
|
406
|
+
console.warn(
|
|
407
|
+
`Somehow, file "${resolvedImportPath}" does not have a SourceCode object obtainable.`
|
|
408
|
+
);
|
|
409
|
+
return {
|
|
410
|
+
skip: undefined,
|
|
411
|
+
importedFileCommentedDirective,
|
|
412
|
+
analyzeExportsForReExportsResults: undefined,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const analyzeExportsForReExportsResults = analyzeExportsForReExports(
|
|
417
|
+
importedFileSourceCode
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
return {
|
|
421
|
+
skip: undefined,
|
|
422
|
+
importedFileCommentedDirective,
|
|
423
|
+
analyzeExportsForReExportsResults,
|
|
424
|
+
};
|
|
425
|
+
} else return skipTrue;
|
|
426
|
+
};
|
|
427
|
+
|
|
246
428
|
/* importsFlow */
|
|
247
429
|
|
|
248
430
|
/**
|
|
249
|
-
* The full flow for import traversals to enforce
|
|
431
|
+
* The full flow for import traversals to enforce commented directives import rules.
|
|
250
432
|
* @param {Context} context The ESLint rule's `context` object.
|
|
251
433
|
* @param {ImportDeclaration} node The ESLint `node` of the rule's current traversal.
|
|
252
434
|
* @param {CommentedDirective} currentFileCommentedDirective The current file's commented directive.
|
|
@@ -286,7 +468,82 @@ export const importsFlow = (context, node, currentFileCommentedDirective) => {
|
|
|
286
468
|
});
|
|
287
469
|
}
|
|
288
470
|
|
|
289
|
-
|
|
471
|
+
if (result.analyzeExportsForReExportsResults) {
|
|
472
|
+
const { reExportsWithSource, reExportsViaLocal } =
|
|
473
|
+
result.analyzeExportsForReExportsResults;
|
|
474
|
+
|
|
475
|
+
// immediately returns if no re-exports are found
|
|
476
|
+
if (reExportsWithSource.length === 0 && reExportsViaLocal.length === 0)
|
|
477
|
+
return;
|
|
478
|
+
|
|
479
|
+
/** @type {Environment} */
|
|
480
|
+
const currentFileEnvironment = currentFileCommentedDirective.split(" ")[1];
|
|
481
|
+
/** @type {Environment} */
|
|
482
|
+
const importedFileEnvironment =
|
|
483
|
+
importedFileCommentedDirective.split(" ")[1];
|
|
484
|
+
|
|
485
|
+
if (
|
|
486
|
+
!environments_allowedChainImportEnvironments[
|
|
487
|
+
currentFileEnvironment
|
|
488
|
+
].includes(importedFileEnvironment)
|
|
489
|
+
) {
|
|
490
|
+
context.report({
|
|
491
|
+
node,
|
|
492
|
+
messageId: cantChainImportAcrossEnvironmentsMessageId,
|
|
493
|
+
data: {
|
|
494
|
+
// currentFileEnvironment:
|
|
495
|
+
currentFileEnvironment,
|
|
496
|
+
// importedFileEnvironment:
|
|
497
|
+
importedFileEnvironment,
|
|
498
|
+
},
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
// NEW!! Currently strictly adapted from importsFlow
|
|
505
|
+
/**
|
|
506
|
+
* The `importsFlow` adapted for `require` calls to enforce commented directives import rules.
|
|
507
|
+
* @param {Context} context The ESLint rule's `context` object.
|
|
508
|
+
* @param {CallExpression} node The ESLint `node` of the rule's current traversal.
|
|
509
|
+
* @param {CommentedDirective} currentFileCommentedDirective The current file's commented directive.
|
|
510
|
+
* @returns Early if the flow needs to be interrupted.
|
|
511
|
+
*/
|
|
512
|
+
export const importsFlowRequire = (
|
|
513
|
+
context,
|
|
514
|
+
node,
|
|
515
|
+
currentFileCommentedDirective
|
|
516
|
+
) => {
|
|
517
|
+
const result = importedFileFlowRequire(context, node);
|
|
518
|
+
|
|
519
|
+
if (result.skip) return;
|
|
520
|
+
const { importedFileCommentedDirective } = result;
|
|
521
|
+
|
|
522
|
+
// returns early is the current file is an Agnostic Strategies Module
|
|
523
|
+
if (currentFileCommentedDirective === USE_AGNOSTIC_STRATEGIES) return;
|
|
524
|
+
|
|
525
|
+
if (
|
|
526
|
+
isImportBlocked(
|
|
527
|
+
currentFileCommentedDirective,
|
|
528
|
+
importedFileCommentedDirective
|
|
529
|
+
)
|
|
530
|
+
) {
|
|
531
|
+
context.report({
|
|
532
|
+
node,
|
|
533
|
+
messageId: importBreaksCommentedImportRulesMessageId,
|
|
534
|
+
data: {
|
|
535
|
+
[commentedDirectiveMessage]:
|
|
536
|
+
makeMessageFromCurrentFileCommentedDirective(
|
|
537
|
+
currentFileCommentedDirective
|
|
538
|
+
),
|
|
539
|
+
[specificViolationMessage]: findSpecificViolationMessage(
|
|
540
|
+
currentFileCommentedDirective,
|
|
541
|
+
importedFileCommentedDirective
|
|
542
|
+
),
|
|
543
|
+
},
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
|
|
290
547
|
if (result.analyzeExportsForReExportsResults) {
|
|
291
548
|
const { reExportsWithSource, reExportsViaLocal } =
|
|
292
549
|
result.analyzeExportsForReExportsResults;
|
|
@@ -245,6 +245,66 @@ export const getStrategizedDirective = (context, node) => {
|
|
|
245
245
|
|
|
246
246
|
/* addressDirectiveIfAgnosticStrategies */
|
|
247
247
|
|
|
248
|
+
// copied from eXtra JSX (further proving that all core constants and utilities from eXtra JSX should live inside use-agnostic in v2)
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* @type {readonly [".x.jsx", ".x.cjsx", ".x.mjsx", ".x.tsx", ".x.ctsx", ".x.mtsx"]}
|
|
252
|
+
*/
|
|
253
|
+
export const eXtraJsxExtensions = Object.freeze([
|
|
254
|
+
".x.jsx",
|
|
255
|
+
".x.cjsx",
|
|
256
|
+
".x.mjsx",
|
|
257
|
+
".x.tsx",
|
|
258
|
+
".x.ctsx",
|
|
259
|
+
".x.mtsx",
|
|
260
|
+
]);
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* @type {readonly [".x.js", ".x.cjs", ".x.mjs", ".x.ts", ".x.cts", ".x.mts"]}
|
|
264
|
+
*/
|
|
265
|
+
export const eXtraJsExtensions = Object.freeze([
|
|
266
|
+
".x.js",
|
|
267
|
+
".x.cjs",
|
|
268
|
+
".x.mjs",
|
|
269
|
+
".x.ts",
|
|
270
|
+
".x.cts",
|
|
271
|
+
".x.mts",
|
|
272
|
+
]);
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* @type {readonly [".x.jsx", ".x.cjsx", ".x.mjsx", ".x.tsx", ".x.ctsx", ".x.mtsx", ".x.js", ".x.cjs", ".x.mjs", ".x.ts", ".x.cts", ".x.mts"]}
|
|
276
|
+
*/
|
|
277
|
+
export const extraJavaScriptExtensions = Object.freeze([
|
|
278
|
+
...eXtraJsxExtensions,
|
|
279
|
+
...eXtraJsExtensions,
|
|
280
|
+
]);
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* $COMMENT#JSDOC#CORE#DEFS#FILEISANYJAVASCRIPT
|
|
284
|
+
* @param {string} filePath $COMMENT#JSDOC#CORE#PARAMS#FILEPATH
|
|
285
|
+
* @returns $COMMENT#JSDOC#CORE#RETURNS#FILEISANYJAVASCRIPT
|
|
286
|
+
*/
|
|
287
|
+
export const fileIsAnyJavaScript = (filePath) =>
|
|
288
|
+
EXTENSIONS.some((e) => filePath.endsWith(e));
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* $COMMENT#JSDOC#CORE#DEFS#FILEISEXTRAJAVASCRIPT
|
|
292
|
+
* @param {string} filePath $COMMENT#JSDOC#CORE#PARAMS#FILEPATH
|
|
293
|
+
* @returns $COMMENT#JSDOC#CORE#RETURNS#FILEISEXTRAJAVASCRIPT
|
|
294
|
+
*/
|
|
295
|
+
export const fileIsExtraJavaScript = (filePath) =>
|
|
296
|
+
extraJavaScriptExtensions.some((e) => filePath.endsWith(e));
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* $COMMENT#JSDOC#CORE#DEFS#FILEISREGULARJAVASCRIPT
|
|
300
|
+
* @param {string} filePath $COMMENT#JSDOC#CORE#PARAMS#FILEPATH
|
|
301
|
+
* @returns $COMMENT#JSDOC#CORE#RETURNS#FILEISREGULARJAVASCRIPT
|
|
302
|
+
*/
|
|
303
|
+
export const fileIsRegularJavaScript = (filePath) =>
|
|
304
|
+
fileIsAnyJavaScript(filePath) && !fileIsExtraJavaScript(filePath);
|
|
305
|
+
|
|
306
|
+
//
|
|
307
|
+
|
|
248
308
|
/**
|
|
249
309
|
* Verifies the current node's export strategy if the current commented directive is `"use agnostic strategies"` by reporting `exportNotStrategized` in case an export is not strategized in an Agnostic Strategies Module.
|
|
250
310
|
* @param {Context} context The ESLint rule's `context` object.
|
|
@@ -263,7 +323,10 @@ export const addressDirectiveIfAgnosticStrategies = (
|
|
|
263
323
|
|
|
264
324
|
const exportStrategizedDirective = getStrategizedDirective(context, node);
|
|
265
325
|
|
|
266
|
-
if (
|
|
326
|
+
if (
|
|
327
|
+
exportStrategizedDirective === null &&
|
|
328
|
+
fileIsExtraJavaScript(context.filename)
|
|
329
|
+
) {
|
|
267
330
|
context.report({
|
|
268
331
|
node,
|
|
269
332
|
messageId: exportNotStrategizedMessageId,
|