]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-duplicate-imports.js
947bb30c2e175a887f38ab7feae5ad2518183257
2 * @fileoverview Restrict usage of duplicate imports.
3 * @author Simen Bekkhus
7 //------------------------------------------------------------------------------
9 //------------------------------------------------------------------------------
11 const NAMED_TYPES
= [ "ImportSpecifier" , "ExportSpecifier" ];
12 const NAMESPACE_TYPES
= [
13 "ImportNamespaceSpecifier" ,
14 "ExportNamespaceSpecifier"
17 //------------------------------------------------------------------------------
19 //------------------------------------------------------------------------------
22 * Check if an import/export type belongs to (ImportSpecifier|ExportSpecifier) or (ImportNamespaceSpecifier|ExportNamespaceSpecifier).
23 * @param {string} importExportType An import/export type to check.
24 * @param {string} type Can be "named" or "namespace"
25 * @returns {boolean} True if import/export type belongs to (ImportSpecifier|ExportSpecifier) or (ImportNamespaceSpecifier|ExportNamespaceSpecifier) and false if it doesn't.
27 function isImportExportSpecifier ( importExportType
, type
) {
28 const arrayToCheck
= type
=== "named" ? NAMED_TYPES
: NAMESPACE_TYPES
;
30 return arrayToCheck
. includes ( importExportType
);
34 * Return the type of (import|export).
35 * @param {ASTNode} node A node to get.
36 * @returns {string} The type of the (import|export).
38 function getImportExportType ( node
) {
39 if ( node
. specifiers
&& node
. specifiers
. length
> 0 ) {
40 const nodeSpecifiers
= node
. specifiers
;
41 const index
= nodeSpecifiers
. findIndex (
43 isImportExportSpecifier ( type
, "named" ) ||
44 isImportExportSpecifier ( type
, "namespace" )
46 const i
= index
> - 1 ? index
: 0 ;
48 return nodeSpecifiers
[ i
]. type
;
50 if ( node
. type
=== "ExportAllDeclaration" ) {
52 return "ExportNamespaceSpecifier" ;
56 return "SideEffectImport" ;
60 * Returns a boolean indicates if two (import|export) can be merged
61 * @param {ASTNode} node1 A node to check.
62 * @param {ASTNode} node2 A node to check.
63 * @returns {boolean} True if two (import|export) can be merged, false if they can't.
65 function isImportExportCanBeMerged ( node1
, node2
) {
66 const importExportType1
= getImportExportType ( node1
);
67 const importExportType2
= getImportExportType ( node2
);
70 ( importExportType1
=== "ExportAll" &&
71 importExportType2
!== "ExportAll" &&
72 importExportType2
!== "SideEffectImport" ) ||
73 ( importExportType1
!== "ExportAll" &&
74 importExportType1
!== "SideEffectImport" &&
75 importExportType2
=== "ExportAll" )
80 ( isImportExportSpecifier ( importExportType1
, "namespace" ) &&
81 isImportExportSpecifier ( importExportType2
, "named" )) ||
82 ( isImportExportSpecifier ( importExportType2
, "namespace" ) &&
83 isImportExportSpecifier ( importExportType1
, "named" ))
91 * Returns a boolean if we should report (import|export).
92 * @param {ASTNode} node A node to be reported or not.
93 * @param {[ASTNode]} previousNodes An array contains previous nodes of the module imported or exported.
94 * @returns {boolean} True if the (import|export) should be reported.
96 function shouldReportImportExport ( node
, previousNodes
) {
99 while ( i
< previousNodes
. length
) {
100 if ( isImportExportCanBeMerged ( node
, previousNodes
[ i
])) {
109 * Returns array contains only nodes with declarations types equal to type.
110 * @param {[{node: ASTNode, declarationType: string}]} nodes An array contains objects, each object contains a node and a declaration type.
111 * @param {string} type Declaration type.
112 * @returns {[ASTNode]} An array contains only nodes with declarations types equal to type.
114 function getNodesByDeclarationType ( nodes
, type
) {
116 . filter (({ declarationType
}) => declarationType
=== type
)
117 . map (({ node
}) => node
);
121 * Returns the name of the module imported or re-exported.
122 * @param {ASTNode} node A node to get.
123 * @returns {string} The name of the module, or empty string if no name.
125 function getModule ( node
) {
126 if ( node
&& node
. source
&& node
. source
. value
) {
127 return node
. source
. value
. trim ();
133 * Checks if the (import|export) can be merged with at least one import or one export, and reports if so.
134 * @param {RuleContext} context The ESLint rule context object.
135 * @param {ASTNode} node A node to get.
136 * @param {Map} modules A Map object contains as a key a module name and as value an array contains objects, each object contains a node and a declaration type.
137 * @param {string} declarationType A declaration type can be an import or export.
138 * @param {boolean} includeExports Whether or not to check for exports in addition to imports.
139 * @returns {void} No return value.
141 function checkAndReport (
148 const module
= getModule ( node
);
150 if ( modules
. has ( module
)) {
151 const previousNodes
= modules
. get ( module
);
152 const messagesIds
= [];
153 const importNodes
= getNodesByDeclarationType ( previousNodes
, "import" );
156 if ( includeExports
) {
157 exportNodes
= getNodesByDeclarationType ( previousNodes
, "export" );
159 if ( declarationType
=== "import" ) {
160 if ( shouldReportImportExport ( node
, importNodes
)) {
161 messagesIds
. push ( "import" );
163 if ( includeExports
) {
164 if ( shouldReportImportExport ( node
, exportNodes
)) {
165 messagesIds
. push ( "importAs" );
168 } else if ( declarationType
=== "export" ) {
169 if ( shouldReportImportExport ( node
, exportNodes
)) {
170 messagesIds
. push ( "export" );
172 if ( shouldReportImportExport ( node
, importNodes
)) {
173 messagesIds
. push ( "exportAs" );
176 messagesIds
. forEach ( messageId
=>
188 * @callback nodeCallback
189 * @param {ASTNode} node A node to handle.
193 * Returns a function handling the (imports|exports) of a given file
194 * @param {RuleContext} context The ESLint rule context object.
195 * @param {Map} modules A Map object contains as a key a module name and as value an array contains objects, each object contains a node and a declaration type.
196 * @param {string} declarationType A declaration type can be an import or export.
197 * @param {boolean} includeExports Whether or not to check for exports in addition to imports.
198 * @returns {nodeCallback} A function passed to ESLint to handle the statement.
200 function handleImportsExports (
206 return function ( node
) {
207 const module
= getModule ( node
);
217 const currentNode
= { node
, declarationType
};
218 let nodes
= [ currentNode
];
220 if ( modules
. has ( module
)) {
221 const previousNodes
= modules
. get ( module
);
223 nodes
= [... previousNodes
, currentNode
];
225 modules
. set ( module
, nodes
);
230 /** @type {import('../shared/types').Rule} */
236 description
: "disallow duplicate module imports" ,
238 url
: "https://eslint.org/docs/rules/no-duplicate-imports"
250 additionalProperties
: false
255 import : "'{{module}}' import is duplicated." ,
256 importAs
: "'{{module}}' import is duplicated as export." ,
257 export : "'{{module}}' export is duplicated." ,
258 exportAs
: "'{{module}}' export is duplicated as import."
263 const includeExports
= ( context
. options
[ 0 ] || {}). includeExports
,
266 ImportDeclaration
: handleImportsExports (
274 if ( includeExports
) {
275 handlers
. ExportNamedDeclaration
= handleImportsExports (
281 handlers
. ExportAllDeclaration
= handleImportsExports (