-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[New]
no-rename-default
: Forbid importing a default export by a dif…
…ferent name
- Loading branch information
Showing
17 changed files
with
615 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# import/no-rename-default | ||
|
||
⚠️ This rule _warns_ in the 🚸 `warnings` config. | ||
|
||
<!-- end auto-generated rule header --> | ||
|
||
Prohibit importing a default export by another name. | ||
|
||
## Rule Details | ||
|
||
Given: | ||
|
||
```js | ||
// api/get-users.js | ||
export default async function getUsers() {} | ||
``` | ||
|
||
...this would be valid: | ||
|
||
```js | ||
import getUsers from './api/get-users.js'; | ||
``` | ||
|
||
...and the following would be reported: | ||
|
||
```js | ||
// Caution: `get-users.js` has a default export `getUsers`. | ||
// This imports `getUsers` as `findUsers`. | ||
// Check if you meant to write `import getUsers from './api/get-users'` instead. | ||
import findUsers from './get-users'; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
/** | ||
* @fileOverview Rule to warn about importing a default export by different name | ||
* @author James Whitney | ||
*/ | ||
|
||
import docsUrl from '../docsUrl'; | ||
import ExportMapBuilder from '../exportMap/builder'; | ||
import path from 'path'; | ||
|
||
//------------------------------------------------------------------------------ | ||
// Rule Definition | ||
//------------------------------------------------------------------------------ | ||
|
||
/** @type {import('@typescript-eslint/utils').TSESLint.RuleModule} */ | ||
const rule = { | ||
meta: { | ||
type: 'suggestion', | ||
docs: { | ||
category: 'Helpful warnings', | ||
description: 'Forbid importing a default export by a different name.', | ||
recommended: false, | ||
url: docsUrl('no-named-as-default'), | ||
}, | ||
schema: [ | ||
{ | ||
type: 'object', | ||
properties: { | ||
commonjs: { | ||
type: 'boolean', | ||
}, | ||
}, | ||
additionalProperties: false, | ||
}, | ||
], | ||
}, | ||
|
||
create(context) { | ||
function findDefaultDestructure(properties) { | ||
const found = properties.find((property) => { | ||
if (property.key.name === 'default') { | ||
return property; | ||
} | ||
}); | ||
return found; | ||
} | ||
|
||
function getDefaultExportName(targetNode) { | ||
if (targetNode.type === 'CallExpression') { | ||
const [argumentNode] = targetNode.arguments; | ||
return getDefaultExportName(argumentNode); | ||
} | ||
if (targetNode.type === 'FunctionDeclaration') { | ||
return targetNode.id.name; | ||
} | ||
if (targetNode.type === 'Identifier') { | ||
return targetNode.name; | ||
} | ||
} | ||
|
||
function getDefaultExportNode(exportMap) { | ||
const defaultExportNode = exportMap.exports.get('default'); | ||
if (defaultExportNode == null) { | ||
return; | ||
} | ||
return defaultExportNode; | ||
} | ||
|
||
function getExportMap(source, context) { | ||
const exportMap = ExportMapBuilder.get(source.value, context); | ||
if (exportMap == null) { | ||
return; | ||
} | ||
if (exportMap.errors.length > 0) { | ||
exportMap.reportErrors(context, source.value); | ||
return; | ||
} | ||
return exportMap; | ||
} | ||
|
||
function handleImport(node) { | ||
|
||
const exportMap = getExportMap(node.parent.source, context); | ||
if (exportMap == null) { | ||
return; | ||
} | ||
|
||
const defaultExportNode = getDefaultExportNode(exportMap); | ||
if (defaultExportNode == null) { | ||
return; | ||
|
||
} | ||
|
||
const defaultExportName = getDefaultExportName(defaultExportNode.declaration); | ||
if (defaultExportName === undefined) { | ||
return; | ||
} | ||
|
||
const importTarget = node.parent.source.value; | ||
const importBasename = path.basename(exportMap.path); | ||
|
||
if (node.type === 'ImportDefaultSpecifier') { | ||
const importName = node.local.name; | ||
|
||
if (importName === defaultExportName) { | ||
return; | ||
} | ||
|
||
context.report({ | ||
node, | ||
message: `Caution: \`${importBasename}\` has a default export \`${defaultExportName}\`. This imports \`${defaultExportName}\` as \`${importName}\`. Check if you meant to write \`import ${defaultExportName} from '${importTarget}'\` instead.`, | ||
}); | ||
|
||
return; | ||
} | ||
|
||
if (node.type !== 'ImportSpecifier') { | ||
return; | ||
} | ||
|
||
if (node.imported.name !== 'default') { | ||
return; | ||
} | ||
|
||
const actualImportedName = node.local.name; | ||
|
||
if (actualImportedName === defaultExportName) { | ||
return; | ||
} | ||
|
||
context.report({ | ||
node, | ||
message: `Caution: \`${importBasename}\` has a default export \`${defaultExportName}\`. This imports \`${defaultExportName}\` as \`${actualImportedName}\`. Check if you meant to write \`import { default as ${defaultExportName} } from '${importTarget}'\` instead.`, | ||
}); | ||
} | ||
|
||
function handleRequire(node) { | ||
const options = context.options[0] || {}; | ||
|
||
if ( | ||
!options.commonjs | ||
|| node.type !== 'VariableDeclarator' | ||
|| !node.id || !(node.id.type === 'Identifier' || node.id.type === 'ObjectPattern') | ||
|| !node.init || node.init.type !== 'CallExpression' | ||
) { | ||
return; | ||
} | ||
|
||
let defaultDestructure; | ||
if (node.id.type === 'ObjectPattern') { | ||
defaultDestructure = findDefaultDestructure(node.id.properties); | ||
if (defaultDestructure === undefined) { | ||
return; | ||
} | ||
} | ||
|
||
const call = node.init; | ||
const [source] = call.arguments; | ||
|
||
if ( | ||
call.callee.type !== 'Identifier' || call.callee.name !== 'require' || call.arguments.length !== 1 | ||
|| source.type !== 'Literal' | ||
) { | ||
return; | ||
} | ||
|
||
const exportMap = getExportMap(source, context); | ||
if (exportMap == null) { | ||
return; | ||
} | ||
|
||
const defaultExportNode = getDefaultExportNode(exportMap); | ||
if (defaultExportNode == null) { | ||
return; | ||
} | ||
|
||
const defaultExportName = getDefaultExportName(defaultExportNode.declaration); | ||
const requireTarget = source.value; | ||
const requireBasename = path.basename(exportMap.path); | ||
const requireName = node.id.type === 'Identifier' ? node.id.name : defaultDestructure.value.name; | ||
|
||
if (defaultExportName === undefined) { | ||
return; | ||
} | ||
|
||
if (requireName === defaultExportName) { | ||
return; | ||
} | ||
|
||
if (node.id.type === 'Identifier') { | ||
context.report({ | ||
node, | ||
message: `Caution: \`${requireBasename}\` has a default export \`${defaultExportName}\`. This requires \`${defaultExportName}\` as \`${requireName}\`. Check if you meant to write \`const ${defaultExportName} = require('${requireTarget}')\` instead.`, | ||
}); | ||
return; | ||
} | ||
|
||
context.report({ | ||
node, | ||
message: `Caution: \`${requireBasename}\` has a default export \`${defaultExportName}\`. This requires \`${defaultExportName}\` as \`${requireName}\`. Check if you meant to write \`const { default: ${defaultExportName} } = require('${requireTarget}')\` instead.`, | ||
}); | ||
} | ||
|
||
return { | ||
ImportDefaultSpecifier(node) { | ||
handleImport(node); | ||
}, | ||
ImportSpecifier(node) { | ||
handleImport(node); | ||
}, | ||
VariableDeclarator(node) { | ||
handleRequire(node); | ||
}, | ||
}; | ||
}, | ||
}; | ||
|
||
module.exports = rule; |
5 changes: 5 additions & 0 deletions
5
tests/files/no-rename-default/binding-fn-get-users-with-logger-and-auth.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import getUsers from './default-fn-get-users'; | ||
import withAuth from './hoc-with-auth'; | ||
import withLogger from './hoc-with-logger'; | ||
|
||
export default withLogger(withAuth(getUsers)); |
4 changes: 4 additions & 0 deletions
4
tests/files/no-rename-default/binding-fn-get-users-with-logger.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import getUsers from './default-fn-get-users'; | ||
import withLogger from './hoc-with-logger'; | ||
|
||
export default withLogger(getUsers); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export default {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export const barNamed1 = 'bar-named-1'; | ||
export const barNamed2 = 'bar-named-2'; | ||
|
||
const bar = 'bar'; | ||
|
||
export default bar; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export const fooNamed1 = 'foo-named-1'; | ||
export const fooNamed2 = 'foo-named-2'; | ||
|
||
const foo = 'foo'; | ||
|
||
export default foo; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export default function getUsersSync() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export default async function getUsers() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export default 123; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export default function withAuth(fn) { | ||
return function innerAuth(...args) { | ||
const auth = {}; | ||
return fn.call(null, auth, ...args); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export default function withLogger(fn) { | ||
return function innerLogger(...args) { | ||
console.log(`${fn.name} called`); | ||
return fn.apply(null, args); | ||
} | ||
} |
Oops, something went wrong.