From 31f26463ec05a756d6b2f65283676d741169dc74 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Tue, 27 Aug 2024 05:48:26 +0800 Subject: [PATCH 1/6] chore: make eslint happy --- src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 2d28d25c..49152927 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,7 +18,6 @@ import recommended from './config/recommended' import stage0 from './config/stage-0' import typescript from './config/typescript' import warnings from './config/warnings' - // rules import consistentTypeSpecifierStyle from './rules/consistent-type-specifier-style' import default_ from './rules/default' From f547d4faf782f842dd46c63dcd298421bc39ecf7 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Tue, 27 Aug 2024 06:31:28 +0800 Subject: [PATCH 2/6] feat: implement `no-rename-default` --- src/config/flat/warnings.ts | 1 + src/config/warnings.ts | 1 + src/index.ts | 2 + src/rules/no-rename-default.ts | 305 +++++++++++++++++++++++++++++++++ src/utils/export-map.ts | 53 ++++-- 5 files changed, 343 insertions(+), 19 deletions(-) create mode 100644 src/rules/no-rename-default.ts diff --git a/src/config/flat/warnings.ts b/src/config/flat/warnings.ts index ca9dde2d..9b19db42 100644 --- a/src/config/flat/warnings.ts +++ b/src/config/flat/warnings.ts @@ -7,6 +7,7 @@ export default { rules: { 'import-x/no-named-as-default': 1, 'import-x/no-named-as-default-member': 1, + 'import/no-rename-default': 1, 'import-x/no-duplicates': 1, }, } satisfies PluginFlatBaseConfig diff --git a/src/config/warnings.ts b/src/config/warnings.ts index fe111e84..65d68050 100644 --- a/src/config/warnings.ts +++ b/src/config/warnings.ts @@ -8,6 +8,7 @@ export = { rules: { 'import-x/no-named-as-default': 1, 'import-x/no-named-as-default-member': 1, + 'import/no-rename-default': 1, 'import-x/no-duplicates': 1, }, } satisfies PluginConfig diff --git a/src/index.ts b/src/index.ts index 49152927..0c19a94f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -54,6 +54,7 @@ import noNamespace from './rules/no-namespace' import noNodejsModules from './rules/no-nodejs-modules' import noRelativePackages from './rules/no-relative-packages' import noRelativeParentImports from './rules/no-relative-parent-imports' +import noRenameDefault from './rules/no-rename-default' import noRestrictedPaths from './rules/no-restricted-paths' import noSelfImport from './rules/no-self-import' import noUnassignedImport from './rules/no-unassigned-import' @@ -93,6 +94,7 @@ const rules = { 'no-named-as-default': noNamedAsDefault, 'no-named-as-default-member': noNamedAsDefaultMember, 'no-anonymous-default-export': noAnonymousDefaultExport, + 'no-rename-default': noRenameDefault, 'no-unused-modules': noUnusedModules, 'no-commonjs': noCommonjs, diff --git a/src/rules/no-rename-default.ts b/src/rules/no-rename-default.ts new file mode 100644 index 00000000..5fe195ad --- /dev/null +++ b/src/rules/no-rename-default.ts @@ -0,0 +1,305 @@ +/** + * @fileOverview Rule to warn about importing a default export by different name + * @author James Whitney + * @author Sukka - Port to TypeScript + */ + +import path from 'path'; +import { createRule, ExportMap } from '../utils'; +import type { ModuleOptions } from '../utils'; +import type { TSESTree } from '@typescript-eslint/typescript-estree'; +import type { VariableDeclaration } from 'typescript'; + +type Options = ModuleOptions & { + preventRenamingBindings?: boolean +} + +type MessageId = 'renameDefault' + +export = createRule<[Options?], MessageId>({ + name: 'no-rename-default', + meta: { + type: 'suggestion', + docs: { + category: 'Helpful warnings', + description: 'Forbid importing a default export by a different name.', + }, + schema: [ + { + type: 'object', + properties: { + commonjs: { + default: false, + type: 'boolean', + }, + preventRenamingBindings: { + default: true, + type: 'boolean', + }, + }, + additionalProperties: false, + }, + ], + messages: { + renameDefault: 'Caution: `{{importBasename}}` has a default export `{{defaultExportName}}`. This imports `{{defaultExportName}}` as `${{importName}}`. Check if you meant to write {{suggestion}} instead.', + }, + }, + defaultOptions: [], + create(context) { + const { + commonjs = false, + preventRenamingBindings = true, + } = context.options[0] || {}; + + function findDefaultDestructure(properties: (TSESTree.Property | TSESTree.RestElement)[]) { + const found = properties.find((property) => { + if ('key' in property && 'name' in property.key && property.key.name === 'default') { + return property; + } + }); + return found; + } + + function getDefaultExportName(targetNode: TSESTree.ExportSpecifier | TSESTree.DefaultExportDeclarations | TSESTree.CallExpressionArgument) { + if (targetNode == null) { + return; + } + switch (targetNode.type) { + case 'AssignmentExpression': { + if (!preventRenamingBindings) { + // Allow assignments to be renamed when the `preventRenamingBindings` + // option is set to `false`. + // + // export default Foo = 1; + return; + } + if (targetNode.left.type !== 'Identifier') { + return; + } + return targetNode.left.name; + } + case 'CallExpression': { + const [argumentNode] = targetNode.arguments; + return getDefaultExportName(argumentNode); + } + case 'ClassDeclaration': { + if (targetNode.id && typeof targetNode.id.name === 'string') { + return targetNode.id.name; + } + // Here we have an anonymous class. We can skip here. + return; + } + case 'ExportSpecifier': { + return targetNode.local.name; + } + case 'FunctionDeclaration': { + return targetNode.id?.name; + } + case 'Identifier': { + if (!preventRenamingBindings) { + // Allow identifier to be renamed when the `preventRenamingBindings` + // option is set to `false`. + // + // const foo = 'foo'; + // export default foo; + return; + } + return targetNode.name; + } + default: + // This type of node is not handled. + // Returning `undefined` here signifies this and causes the check to + // exit early. + } + } + + function getDefaultExportNode(exportMap: ExportMap): TSESTree.DefaultExportDeclarations | TSESTree.ExportSpecifier | undefined { + const defaultExportNode = exportMap.exports.get('default'); + if (defaultExportNode == null) { + return; + } + switch (defaultExportNode.type) { + case 'ExportDefaultDeclaration': { + return defaultExportNode.declaration; + } + case 'ExportNamedDeclaration': { + return defaultExportNode.specifiers.find((specifier) => specifier.exported.name === 'default'); + } + default: + return; + } + } + + function getExportMap(source: TSESTree.StringLiteral | null) { + if (!source) { + return; + } + const exportMap = ExportMap.get(source.value, context); + if (exportMap == null) { + return; + } + if (exportMap.errors.length > 0) { + exportMap.reportErrors(context, { source }); + return; + } + return exportMap; + } + + function handleImport(node: TSESTree.ImportDefaultSpecifier | TSESTree.ImportSpecifier) { + const exportMap = getExportMap(node.parent.source); + if (exportMap == null) { + return; + } + + const defaultExportNode = getDefaultExportNode(exportMap); + if (defaultExportNode == null) { + return; + } + + const defaultExportName = getDefaultExportName(defaultExportNode); + 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, + messageId: 'renameDefault', + data: { + importBasename, + defaultExportName, + importName, + suggestion: `import ${defaultExportName} from '${importTarget}'`, + }, + }); + + return; + } + + if (node.type !== 'ImportSpecifier') { + return; + } + + if (node.imported.name !== 'default') { + return; + } + + const actualImportedName = node.local.name; + + if (actualImportedName === defaultExportName) { + return; + } + + context.report({ + node, + messageId: 'renameDefault', + data: { + importBasename, + defaultExportName, + importName: actualImportedName, + suggestion: `import { default as ${defaultExportName} } from '${importTarget}'`, + } + }); + } + + function handleRequire(node: TSESTree.VariableDeclarator) { + if ( + !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' || typeof source.value !== 'string' + ) { + return; + } + + const exportMap = getExportMap(source); + if (exportMap == null) { + return; + } + + const defaultExportNode = getDefaultExportNode(exportMap); + if (defaultExportNode == null) { + return; + } + + const defaultExportName = getDefaultExportName(defaultExportNode); + const requireTarget = source.value; + const requireBasename = path.basename(exportMap.path); + + let requireName; + if (node.id.type === 'Identifier') { + requireName = node.id.name; + } else if (defaultDestructure?.value?.type === 'Identifier') { + requireName = defaultDestructure.value.name; + } else { + requireName = ''; + } + + if (defaultExportName === undefined) { + return; + } + + if (requireName === defaultExportName) { + return; + } + + if (node.id.type === 'Identifier') { + context.report({ + node, + messageId: 'renameDefault', + data: { + importBasename: requireBasename, + defaultExportName, + importName: requireName, + suggestion: `const ${defaultExportName} = require('${requireTarget}')` + } + }); + return; + } + + context.report({ + node, + messageId: 'renameDefault', + data: { + importBasename: requireBasename, + defaultExportName, + importName: requireName, + suggestion: `const { default: ${defaultExportName} } = require('${requireTarget}')` + } + }); + } + + return { + ImportDefaultSpecifier: handleImport, + ImportSpecifier: handleImport, + VariableDeclarator: handleRequire, + }; + }, +}); diff --git a/src/utils/export-map.ts b/src/utils/export-map.ts index 2153458e..8e3c9276 100644 --- a/src/utils/export-map.ts +++ b/src/utils/export-map.ts @@ -149,7 +149,7 @@ export class ExportMap { let ast: TSESTree.Program let visitorKeys: TSESLint.SourceCode.VisitorKeys | null try { - ;({ ast, visitorKeys } = parse(filepath, content, context)) + ; ({ ast, visitorKeys } = parse(filepath, content, context)) } catch (error) { m.errors.push(error as ParseError) return m // can't continue @@ -271,6 +271,7 @@ export class ExportMap { break } case 'ExportNamespaceSpecifier': { + m.exports.set(s.exported.name, n); m.namespace.set( s.exported.name, Object.defineProperty(exportMeta, 'namespace', { @@ -282,6 +283,7 @@ export class ExportMap { return } case 'ExportAllDeclaration': { + m.exports.set(s.exported!.name || (s.exported as any).value, n); m.namespace.set( getValue(s.exported!), addNamespace(exportMeta, s.exported!), @@ -291,6 +293,7 @@ export class ExportMap { } case 'ExportSpecifier': { if (!('source' in n && n.source)) { + m.exports.set(s.exported!.name || (s.exported as any).value, n); m.namespace.set( getValue(s.exported), addNamespace(exportMeta, s.local), @@ -435,6 +438,7 @@ export class ExportMap { if (n.declaration.type === 'Identifier') { addNamespace(exportMeta, n.declaration) } + m.exports.set('default', n) m.namespace.set('default', exportMeta) continue } @@ -485,6 +489,7 @@ export class ExportMap { // @ts-expect-error - legacy parser type case 'TSAbstractClassDeclaration': case 'TSModuleDeclaration': { + m.exports.set((n.declaration.id as TSESTree.Identifier).name, n); m.namespace.set( (n.declaration.id as TSESTree.Identifier).name, captureDoc(source, docStyleParsers, n), @@ -494,12 +499,13 @@ export class ExportMap { /* eslint-enable no-fallthrough */ case 'VariableDeclaration': { for (const d of n.declaration.declarations) { - recursivePatternCapture(d.id, id => + recursivePatternCapture(d.id, id => { + m.exports.set((id as TSESTree.Identifier).name, n); m.namespace.set( (id as TSESTree.Identifier).name, captureDoc(source, docStyleParsers, d, n), - ), - ) + ) + }) } break } @@ -522,17 +528,17 @@ export class ExportMap { const exportedName = n.type === 'TSNamespaceExportDeclaration' ? ( - n.id || - // @ts-expect-error - legacy parser type - n.name - ).name + n.id || + // @ts-expect-error - legacy parser type + n.name + ).name : ('expression' in n && - n.expression && - (('name' in n.expression && n.expression.name) || - ('id' in n.expression && - n.expression.id && - n.expression.id.name))) || - null + n.expression && + (('name' in n.expression && n.expression.name) || + ('id' in n.expression && + n.expression.id && + n.expression.id.name))) || + null const getRoot = ( node: TSESTree.TSQualifiedName, @@ -551,7 +557,7 @@ export class ExportMap { ('name' in node.id ? node.id.name === exportedName : 'left' in node.id && - getRoot(node.id).name === exportedName)) || + getRoot(node.id).name === exportedName)) || ('declarations' in node && node.declarations.find( d => 'name' in d.id && d.id.name === exportedName, @@ -560,6 +566,7 @@ export class ExportMap { }) if (exportedDecls.length === 0) { + m.exports.set('default', n); // Export is not referencing any local declaration, must be re-exporting m.namespace.set('default', captureDoc(source, docStyleParsers, n)) continue @@ -569,6 +576,7 @@ export class ExportMap { isEsModuleInteropTrue() && // esModuleInterop is on in tsconfig !m.namespace.has('default') // and default isn't added already ) { + m.exports.set('default', n); m.namespace.set('default', {}) // add default export } @@ -578,6 +586,8 @@ export class ExportMap { // @ts-expect-error - legacy parser type if (type === 'TSModuleDeclaration') { + // @ts-expect-error - legacy parser type + m.exports.set((decl.body.id as TSESTree.Identifier).name, n) m.namespace.set( // @ts-expect-error - legacy parser type (decl.body.id as TSESTree.Identifier).name, @@ -607,7 +617,8 @@ export class ExportMap { // TypeScript can check this for us; we needn't } else if (namespaceDecl.type === 'VariableDeclaration') { for (const d of namespaceDecl.declarations) - recursivePatternCapture(d.id, id => + recursivePatternCapture(d.id, id => { + m.exports.set((id as TSESTree.Identifier).name, n); m.namespace.set( (id as TSESTree.Identifier).name, captureDoc( @@ -617,9 +628,10 @@ export class ExportMap { namespaceDecl, moduleBlockNode, ), - ), - ) + ) + }) } else if ('id' in namespaceDecl) { + m.exports.set((namespaceDecl.id as TSESTree.Identifier).name, n); m.namespace.set( (namespaceDecl.id as TSESTree.Identifier).name, captureDoc(source, docStyleParsers, moduleBlockNode), @@ -629,6 +641,7 @@ export class ExportMap { } } else { // Export as default + m.exports.set('default', n); m.namespace.set( 'default', captureDoc(source, docStyleParsers, decl), @@ -663,6 +676,7 @@ export class ExportMap { m.namespace.size > 0 && // anything is exported !m.namespace.has('default') // and default isn't added already ) { + m.exports.set('default', ast.body[0]) // add default export m.namespace.set('default', {}) // add default export } @@ -697,6 +711,7 @@ export class ExportMap { * dependencies of this module that are not explicitly re-exported */ imports = new Map() + exports = new Map(); errors: ParseError[] = [] @@ -708,7 +723,7 @@ export class ExportMap { declare doc: Annotation | undefined - constructor(public path: string) {} + constructor(public path: string) { } get hasDefault() { return this.get('default') != null From 2c64729c48e7e59584545fd74726ff2b67845e4b Mon Sep 17 00:00:00 2001 From: SukkaW Date: Tue, 27 Aug 2024 06:52:31 +0800 Subject: [PATCH 3/6] test: `no-rename-default` --- docs/rules/no-rename-default.md | 29 + src/config/flat/warnings.ts | 2 +- src/config/warnings.ts | 2 +- src/rules/no-rename-default.ts | 253 ++--- src/utils/export-map.ts | 51 +- .../anonymous-arrow-async.js | 1 + .../no-rename-default/anonymous-arrow.js | 1 + .../no-rename-default/anonymous-class.js | 1 + .../no-rename-default/anonymous-object.js | 1 + .../no-rename-default/anonymous-primitive.js | 1 + .../no-rename-default/assign-arrow-async.js | 1 + .../no-rename-default/assign-arrow.js | 1 + .../no-rename-default/assign-class-named.js | 1 + .../no-rename-default/assign-class.js | 1 + .../no-rename-default/assign-fn-named.js | 1 + test/fixtures/no-rename-default/assign-fn.js | 1 + .../assign-generator-named.js | 1 + .../no-rename-default/assign-generator.js | 1 + test/fixtures/no-rename-default/class-user.js | 1 + test/fixtures/no-rename-default/const-bar.js | 6 + test/fixtures/no-rename-default/const-foo.js | 6 + .../no-rename-default/fn-get-users-sync.js | 1 + .../no-rename-default/fn-get-users.js | 1 + .../no-rename-default/generator-reader.js | 1 + .../binding-const-rename-fn.js | 3 + .../pr-3006-feedback/binding-fn-rename.js | 3 + .../binding-hoc-with-logger-for-foo.js | 4 + .../binding-hoc-with-logger-for-get-users.js | 4 + ...hoc-with-logger-with-auth-for-get-users.js | 5 + .../pr-3006-feedback/hoc-with-auth.js | 6 + .../pr-3006-feedback/hoc-with-logger.js | 6 + .../no-rename-default/typescript-default.d.ts | 3 + test/package.spec.ts | 1 + test/rules/no-rename-default.spec.ts | 898 ++++++++++++++++++ 34 files changed, 1160 insertions(+), 139 deletions(-) create mode 100644 docs/rules/no-rename-default.md create mode 100644 test/fixtures/no-rename-default/anonymous-arrow-async.js create mode 100644 test/fixtures/no-rename-default/anonymous-arrow.js create mode 100644 test/fixtures/no-rename-default/anonymous-class.js create mode 100644 test/fixtures/no-rename-default/anonymous-object.js create mode 100644 test/fixtures/no-rename-default/anonymous-primitive.js create mode 100644 test/fixtures/no-rename-default/assign-arrow-async.js create mode 100644 test/fixtures/no-rename-default/assign-arrow.js create mode 100644 test/fixtures/no-rename-default/assign-class-named.js create mode 100644 test/fixtures/no-rename-default/assign-class.js create mode 100644 test/fixtures/no-rename-default/assign-fn-named.js create mode 100644 test/fixtures/no-rename-default/assign-fn.js create mode 100644 test/fixtures/no-rename-default/assign-generator-named.js create mode 100644 test/fixtures/no-rename-default/assign-generator.js create mode 100644 test/fixtures/no-rename-default/class-user.js create mode 100644 test/fixtures/no-rename-default/const-bar.js create mode 100644 test/fixtures/no-rename-default/const-foo.js create mode 100644 test/fixtures/no-rename-default/fn-get-users-sync.js create mode 100644 test/fixtures/no-rename-default/fn-get-users.js create mode 100644 test/fixtures/no-rename-default/generator-reader.js create mode 100644 test/fixtures/no-rename-default/pr-3006-feedback/binding-const-rename-fn.js create mode 100644 test/fixtures/no-rename-default/pr-3006-feedback/binding-fn-rename.js create mode 100644 test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-foo.js create mode 100644 test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-get-users.js create mode 100644 test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-with-auth-for-get-users.js create mode 100644 test/fixtures/no-rename-default/pr-3006-feedback/hoc-with-auth.js create mode 100644 test/fixtures/no-rename-default/pr-3006-feedback/hoc-with-logger.js create mode 100644 test/fixtures/no-rename-default/typescript-default.d.ts create mode 100644 test/rules/no-rename-default.spec.ts diff --git a/docs/rules/no-rename-default.md b/docs/rules/no-rename-default.md new file mode 100644 index 00000000..f101850d --- /dev/null +++ b/docs/rules/no-rename-default.md @@ -0,0 +1,29 @@ +# import/no-rename-default + + + +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'; +``` diff --git a/src/config/flat/warnings.ts b/src/config/flat/warnings.ts index 9b19db42..c9c20995 100644 --- a/src/config/flat/warnings.ts +++ b/src/config/flat/warnings.ts @@ -7,7 +7,7 @@ export default { rules: { 'import-x/no-named-as-default': 1, 'import-x/no-named-as-default-member': 1, - 'import/no-rename-default': 1, + 'import-x/no-rename-default': 1, 'import-x/no-duplicates': 1, }, } satisfies PluginFlatBaseConfig diff --git a/src/config/warnings.ts b/src/config/warnings.ts index 65d68050..56fc93e1 100644 --- a/src/config/warnings.ts +++ b/src/config/warnings.ts @@ -8,7 +8,7 @@ export = { rules: { 'import-x/no-named-as-default': 1, 'import-x/no-named-as-default-member': 1, - 'import/no-rename-default': 1, + 'import-x/no-rename-default': 1, 'import-x/no-duplicates': 1, }, } satisfies PluginConfig diff --git a/src/rules/no-rename-default.ts b/src/rules/no-rename-default.ts index 5fe195ad..08489854 100644 --- a/src/rules/no-rename-default.ts +++ b/src/rules/no-rename-default.ts @@ -4,11 +4,12 @@ * @author Sukka - Port to TypeScript */ -import path from 'path'; -import { createRule, ExportMap } from '../utils'; -import type { ModuleOptions } from '../utils'; -import type { TSESTree } from '@typescript-eslint/typescript-estree'; -import type { VariableDeclaration } from 'typescript'; +import path from 'node:path' + +import type { TSESTree } from '@typescript-eslint/typescript-estree' + +import { createRule, ExportMap } from '../utils' +import type { ModuleOptions } from '../utils' type Options = ModuleOptions & { preventRenamingBindings?: boolean @@ -41,28 +42,23 @@ export = createRule<[Options?], MessageId>({ }, ], messages: { - renameDefault: 'Caution: `{{importBasename}}` has a default export `{{defaultExportName}}`. This imports `{{defaultExportName}}` as `${{importName}}`. Check if you meant to write {{suggestion}} instead.', + renameDefault: + 'Caution: `{{importBasename}}` has a default export `{{defaultExportName}}`. This {{requiresOrImports}} `{{defaultExportName}}` as `{{importName}}`. Check if you meant to write `{{suggestion}}` instead.', }, }, defaultOptions: [], create(context) { - const { - commonjs = false, - preventRenamingBindings = true, - } = context.options[0] || {}; - - function findDefaultDestructure(properties: (TSESTree.Property | TSESTree.RestElement)[]) { - const found = properties.find((property) => { - if ('key' in property && 'name' in property.key && property.key.name === 'default') { - return property; - } - }); - return found; - } - - function getDefaultExportName(targetNode: TSESTree.ExportSpecifier | TSESTree.DefaultExportDeclarations | TSESTree.CallExpressionArgument) { + const { commonjs = false, preventRenamingBindings = true } = + context.options[0] || {} + + function getDefaultExportName( + targetNode: + | TSESTree.ExportSpecifier + | TSESTree.DefaultExportDeclarations + | TSESTree.CallExpressionArgument, + ) { if (targetNode == null) { - return; + return } switch (targetNode.type) { case 'AssignmentExpression': { @@ -71,29 +67,29 @@ export = createRule<[Options?], MessageId>({ // option is set to `false`. // // export default Foo = 1; - return; + return } if (targetNode.left.type !== 'Identifier') { - return; + return } - return targetNode.left.name; + return targetNode.left.name } case 'CallExpression': { - const [argumentNode] = targetNode.arguments; - return getDefaultExportName(argumentNode); + const [argumentNode] = targetNode.arguments + return getDefaultExportName(argumentNode) } case 'ClassDeclaration': { if (targetNode.id && typeof targetNode.id.name === 'string') { - return targetNode.id.name; + return targetNode.id.name } // Here we have an anonymous class. We can skip here. - return; + return } case 'ExportSpecifier': { - return targetNode.local.name; + return targetNode.local.name } case 'FunctionDeclaration': { - return targetNode.id?.name; + return targetNode.id?.name } case 'Identifier': { if (!preventRenamingBindings) { @@ -102,73 +98,58 @@ export = createRule<[Options?], MessageId>({ // // const foo = 'foo'; // export default foo; - return; + return } - return targetNode.name; - } - default: - // This type of node is not handled. - // Returning `undefined` here signifies this and causes the check to - // exit early. - } - } - - function getDefaultExportNode(exportMap: ExportMap): TSESTree.DefaultExportDeclarations | TSESTree.ExportSpecifier | undefined { - const defaultExportNode = exportMap.exports.get('default'); - if (defaultExportNode == null) { - return; - } - switch (defaultExportNode.type) { - case 'ExportDefaultDeclaration': { - return defaultExportNode.declaration; - } - case 'ExportNamedDeclaration': { - return defaultExportNode.specifiers.find((specifier) => specifier.exported.name === 'default'); + return targetNode.name } default: - return; + // This type of node is not handled. + // Returning `undefined` here signifies this and causes the check to + // exit early. } } function getExportMap(source: TSESTree.StringLiteral | null) { if (!source) { - return; + return } - const exportMap = ExportMap.get(source.value, context); + const exportMap = ExportMap.get(source.value, context) if (exportMap == null) { - return; + return } if (exportMap.errors.length > 0) { - exportMap.reportErrors(context, { source }); - return; + exportMap.reportErrors(context, { source }) + return } - return exportMap; + return exportMap } - function handleImport(node: TSESTree.ImportDefaultSpecifier | TSESTree.ImportSpecifier) { - const exportMap = getExportMap(node.parent.source); + function handleImport( + node: TSESTree.ImportDefaultSpecifier | TSESTree.ImportSpecifier, + ) { + const exportMap = getExportMap(node.parent.source) if (exportMap == null) { - return; + return } - const defaultExportNode = getDefaultExportNode(exportMap); + const defaultExportNode = getDefaultExportNode(exportMap) if (defaultExportNode == null) { - return; + return } - const defaultExportName = getDefaultExportName(defaultExportNode); + const defaultExportName = getDefaultExportName(defaultExportNode) if (defaultExportName === undefined) { - return; + return } - const importTarget = node.parent.source?.value; - const importBasename = path.basename(exportMap.path); + const importTarget = node.parent.source?.value + const importBasename = path.basename(exportMap.path) if (node.type === 'ImportDefaultSpecifier') { - const importName = node.local.name; + const importName = node.local.name if (importName === defaultExportName) { - return; + return } context.report({ @@ -178,25 +159,26 @@ export = createRule<[Options?], MessageId>({ importBasename, defaultExportName, importName, + requiresOrImports: 'imports', suggestion: `import ${defaultExportName} from '${importTarget}'`, }, - }); + }) - return; + return } if (node.type !== 'ImportSpecifier') { - return; + return } if (node.imported.name !== 'default') { - return; + return } - const actualImportedName = node.local.name; + const actualImportedName = node.local.name if (actualImportedName === defaultExportName) { - return; + return } context.report({ @@ -206,68 +188,74 @@ export = createRule<[Options?], MessageId>({ importBasename, defaultExportName, importName: actualImportedName, + requiresOrImports: 'imports', suggestion: `import { default as ${defaultExportName} } from '${importTarget}'`, - } - }); + }, + }) } function handleRequire(node: TSESTree.VariableDeclarator) { if ( - !commonjs - || node.type !== 'VariableDeclarator' - || !node.id || !(node.id.type === 'Identifier' || node.id.type === 'ObjectPattern') - || !node.init || node.init.type !== 'CallExpression' + !commonjs || + node.type !== 'VariableDeclarator' || + !node.id || + !(node.id.type === 'Identifier' || node.id.type === 'ObjectPattern') || + !node.init || + node.init.type !== 'CallExpression' ) { - return; + return } - let defaultDestructure; + let defaultDestructure if (node.id.type === 'ObjectPattern') { - defaultDestructure = findDefaultDestructure(node.id.properties); + defaultDestructure = findDefaultDestructure(node.id.properties) if (defaultDestructure === undefined) { - return; + return } } - const call = node.init; - const [source] = call.arguments; + const call = node.init + const [source] = call.arguments if ( - call.callee.type !== 'Identifier' || call.callee.name !== 'require' || call.arguments.length !== 1 - || source.type !== 'Literal' || typeof source.value !== 'string' + call.callee.type !== 'Identifier' || + call.callee.name !== 'require' || + call.arguments.length !== 1 || + source.type !== 'Literal' || + typeof source.value !== 'string' ) { - return; + return } - const exportMap = getExportMap(source); + const exportMap = getExportMap(source) if (exportMap == null) { - return; + return } - const defaultExportNode = getDefaultExportNode(exportMap); + const defaultExportNode = getDefaultExportNode(exportMap) if (defaultExportNode == null) { - return; + return } - const defaultExportName = getDefaultExportName(defaultExportNode); - const requireTarget = source.value; - const requireBasename = path.basename(exportMap.path); + const defaultExportName = getDefaultExportName(defaultExportNode) + const requireTarget = source.value + const requireBasename = path.basename(exportMap.path) - let requireName; + let requireName if (node.id.type === 'Identifier') { - requireName = node.id.name; + requireName = node.id.name } else if (defaultDestructure?.value?.type === 'Identifier') { - requireName = defaultDestructure.value.name; + requireName = defaultDestructure.value.name } else { - requireName = ''; + requireName = '' } if (defaultExportName === undefined) { - return; + return } if (requireName === defaultExportName) { - return; + return } if (node.id.type === 'Identifier') { @@ -278,10 +266,11 @@ export = createRule<[Options?], MessageId>({ importBasename: requireBasename, defaultExportName, importName: requireName, - suggestion: `const ${defaultExportName} = require('${requireTarget}')` - } - }); - return; + requiresOrImports: 'requires', + suggestion: `const ${defaultExportName} = require('${requireTarget}')`, + }, + }) + return } context.report({ @@ -291,15 +280,53 @@ export = createRule<[Options?], MessageId>({ importBasename: requireBasename, defaultExportName, importName: requireName, - suggestion: `const { default: ${defaultExportName} } = require('${requireTarget}')` - } - }); + requiresOrImports: 'requires', + suggestion: `const { default: ${defaultExportName} } = require('${requireTarget}')`, + }, + }) } return { ImportDefaultSpecifier: handleImport, ImportSpecifier: handleImport, VariableDeclarator: handleRequire, - }; + } }, -}); +}) + +function findDefaultDestructure( + properties: Array, +) { + const found = properties.find(property => { + if ( + 'key' in property && + 'name' in property.key && + property.key.name === 'default' + ) { + return property + } + }) + return found +} + +function getDefaultExportNode( + exportMap: ExportMap, +): TSESTree.DefaultExportDeclarations | TSESTree.ExportSpecifier | undefined { + const defaultExportNode = exportMap.exports.get('default') + if (defaultExportNode == null) { + return + } + switch (defaultExportNode.type) { + case 'ExportDefaultDeclaration': { + return defaultExportNode.declaration + } + case 'ExportNamedDeclaration': { + return defaultExportNode.specifiers.find( + specifier => specifier.exported.name === 'default', + ) + } + default: { + return + } + } +} diff --git a/src/utils/export-map.ts b/src/utils/export-map.ts index 8e3c9276..84e548f5 100644 --- a/src/utils/export-map.ts +++ b/src/utils/export-map.ts @@ -149,7 +149,7 @@ export class ExportMap { let ast: TSESTree.Program let visitorKeys: TSESLint.SourceCode.VisitorKeys | null try { - ; ({ ast, visitorKeys } = parse(filepath, content, context)) + ;({ ast, visitorKeys } = parse(filepath, content, context)) } catch (error) { m.errors.push(error as ParseError) return m // can't continue @@ -271,7 +271,7 @@ export class ExportMap { break } case 'ExportNamespaceSpecifier': { - m.exports.set(s.exported.name, n); + m.exports.set(s.exported.name, n) m.namespace.set( s.exported.name, Object.defineProperty(exportMeta, 'namespace', { @@ -283,7 +283,7 @@ export class ExportMap { return } case 'ExportAllDeclaration': { - m.exports.set(s.exported!.name || (s.exported as any).value, n); + m.exports.set(s.exported!.name, n) m.namespace.set( getValue(s.exported!), addNamespace(exportMeta, s.exported!), @@ -293,7 +293,7 @@ export class ExportMap { } case 'ExportSpecifier': { if (!('source' in n && n.source)) { - m.exports.set(s.exported!.name || (s.exported as any).value, n); + m.exports.set(s.exported!.name, n) m.namespace.set( getValue(s.exported), addNamespace(exportMeta, s.local), @@ -489,7 +489,7 @@ export class ExportMap { // @ts-expect-error - legacy parser type case 'TSAbstractClassDeclaration': case 'TSModuleDeclaration': { - m.exports.set((n.declaration.id as TSESTree.Identifier).name, n); + m.exports.set((n.declaration.id as TSESTree.Identifier).name, n) m.namespace.set( (n.declaration.id as TSESTree.Identifier).name, captureDoc(source, docStyleParsers, n), @@ -500,7 +500,7 @@ export class ExportMap { case 'VariableDeclaration': { for (const d of n.declaration.declarations) { recursivePatternCapture(d.id, id => { - m.exports.set((id as TSESTree.Identifier).name, n); + m.exports.set((id as TSESTree.Identifier).name, n) m.namespace.set( (id as TSESTree.Identifier).name, captureDoc(source, docStyleParsers, d, n), @@ -528,17 +528,17 @@ export class ExportMap { const exportedName = n.type === 'TSNamespaceExportDeclaration' ? ( - n.id || - // @ts-expect-error - legacy parser type - n.name - ).name + n.id || + // @ts-expect-error - legacy parser type + n.name + ).name : ('expression' in n && - n.expression && - (('name' in n.expression && n.expression.name) || - ('id' in n.expression && - n.expression.id && - n.expression.id.name))) || - null + n.expression && + (('name' in n.expression && n.expression.name) || + ('id' in n.expression && + n.expression.id && + n.expression.id.name))) || + null const getRoot = ( node: TSESTree.TSQualifiedName, @@ -557,7 +557,7 @@ export class ExportMap { ('name' in node.id ? node.id.name === exportedName : 'left' in node.id && - getRoot(node.id).name === exportedName)) || + getRoot(node.id).name === exportedName)) || ('declarations' in node && node.declarations.find( d => 'name' in d.id && d.id.name === exportedName, @@ -566,7 +566,7 @@ export class ExportMap { }) if (exportedDecls.length === 0) { - m.exports.set('default', n); + m.exports.set('default', n) // Export is not referencing any local declaration, must be re-exporting m.namespace.set('default', captureDoc(source, docStyleParsers, n)) continue @@ -576,7 +576,7 @@ export class ExportMap { isEsModuleInteropTrue() && // esModuleInterop is on in tsconfig !m.namespace.has('default') // and default isn't added already ) { - m.exports.set('default', n); + m.exports.set('default', n) m.namespace.set('default', {}) // add default export } @@ -618,7 +618,7 @@ export class ExportMap { } else if (namespaceDecl.type === 'VariableDeclaration') { for (const d of namespaceDecl.declarations) recursivePatternCapture(d.id, id => { - m.exports.set((id as TSESTree.Identifier).name, n); + m.exports.set((id as TSESTree.Identifier).name, n) m.namespace.set( (id as TSESTree.Identifier).name, captureDoc( @@ -631,7 +631,10 @@ export class ExportMap { ) }) } else if ('id' in namespaceDecl) { - m.exports.set((namespaceDecl.id as TSESTree.Identifier).name, n); + m.exports.set( + (namespaceDecl.id as TSESTree.Identifier).name, + n, + ) m.namespace.set( (namespaceDecl.id as TSESTree.Identifier).name, captureDoc(source, docStyleParsers, moduleBlockNode), @@ -641,7 +644,7 @@ export class ExportMap { } } else { // Export as default - m.exports.set('default', n); + m.exports.set('default', n) m.namespace.set( 'default', captureDoc(source, docStyleParsers, decl), @@ -711,7 +714,7 @@ export class ExportMap { * dependencies of this module that are not explicitly re-exported */ imports = new Map() - exports = new Map(); + exports = new Map() errors: ParseError[] = [] @@ -723,7 +726,7 @@ export class ExportMap { declare doc: Annotation | undefined - constructor(public path: string) { } + constructor(public path: string) {} get hasDefault() { return this.get('default') != null diff --git a/test/fixtures/no-rename-default/anonymous-arrow-async.js b/test/fixtures/no-rename-default/anonymous-arrow-async.js new file mode 100644 index 00000000..18554e89 --- /dev/null +++ b/test/fixtures/no-rename-default/anonymous-arrow-async.js @@ -0,0 +1 @@ +export default async () => {}; diff --git a/test/fixtures/no-rename-default/anonymous-arrow.js b/test/fixtures/no-rename-default/anonymous-arrow.js new file mode 100644 index 00000000..2d1ec238 --- /dev/null +++ b/test/fixtures/no-rename-default/anonymous-arrow.js @@ -0,0 +1 @@ +export default () => {}; diff --git a/test/fixtures/no-rename-default/anonymous-class.js b/test/fixtures/no-rename-default/anonymous-class.js new file mode 100644 index 00000000..0a6916a6 --- /dev/null +++ b/test/fixtures/no-rename-default/anonymous-class.js @@ -0,0 +1 @@ +export default class {}; diff --git a/test/fixtures/no-rename-default/anonymous-object.js b/test/fixtures/no-rename-default/anonymous-object.js new file mode 100644 index 00000000..ff8b4c56 --- /dev/null +++ b/test/fixtures/no-rename-default/anonymous-object.js @@ -0,0 +1 @@ +export default {}; diff --git a/test/fixtures/no-rename-default/anonymous-primitive.js b/test/fixtures/no-rename-default/anonymous-primitive.js new file mode 100644 index 00000000..05e08712 --- /dev/null +++ b/test/fixtures/no-rename-default/anonymous-primitive.js @@ -0,0 +1 @@ +export default 123; diff --git a/test/fixtures/no-rename-default/assign-arrow-async.js b/test/fixtures/no-rename-default/assign-arrow-async.js new file mode 100644 index 00000000..85c31b47 --- /dev/null +++ b/test/fixtures/no-rename-default/assign-arrow-async.js @@ -0,0 +1 @@ +export default arrowAsync = async () => {}; diff --git a/test/fixtures/no-rename-default/assign-arrow.js b/test/fixtures/no-rename-default/assign-arrow.js new file mode 100644 index 00000000..970ba63d --- /dev/null +++ b/test/fixtures/no-rename-default/assign-arrow.js @@ -0,0 +1 @@ +export default arrow = () => {}; diff --git a/test/fixtures/no-rename-default/assign-class-named.js b/test/fixtures/no-rename-default/assign-class-named.js new file mode 100644 index 00000000..90f15bb0 --- /dev/null +++ b/test/fixtures/no-rename-default/assign-class-named.js @@ -0,0 +1 @@ +export default User = class MyUser {} diff --git a/test/fixtures/no-rename-default/assign-class.js b/test/fixtures/no-rename-default/assign-class.js new file mode 100644 index 00000000..3b46709f --- /dev/null +++ b/test/fixtures/no-rename-default/assign-class.js @@ -0,0 +1 @@ +export default User = class {} diff --git a/test/fixtures/no-rename-default/assign-fn-named.js b/test/fixtures/no-rename-default/assign-fn-named.js new file mode 100644 index 00000000..ccbf54a5 --- /dev/null +++ b/test/fixtures/no-rename-default/assign-fn-named.js @@ -0,0 +1 @@ +export default fn = function myFn() {}; diff --git a/test/fixtures/no-rename-default/assign-fn.js b/test/fixtures/no-rename-default/assign-fn.js new file mode 100644 index 00000000..f3da45e3 --- /dev/null +++ b/test/fixtures/no-rename-default/assign-fn.js @@ -0,0 +1 @@ +export default fn = function () {}; diff --git a/test/fixtures/no-rename-default/assign-generator-named.js b/test/fixtures/no-rename-default/assign-generator-named.js new file mode 100644 index 00000000..737411e0 --- /dev/null +++ b/test/fixtures/no-rename-default/assign-generator-named.js @@ -0,0 +1 @@ +export default generator = function* myGenerator() {}; diff --git a/test/fixtures/no-rename-default/assign-generator.js b/test/fixtures/no-rename-default/assign-generator.js new file mode 100644 index 00000000..68e256de --- /dev/null +++ b/test/fixtures/no-rename-default/assign-generator.js @@ -0,0 +1 @@ +export default generator = function* () {}; diff --git a/test/fixtures/no-rename-default/class-user.js b/test/fixtures/no-rename-default/class-user.js new file mode 100644 index 00000000..e6c5fb05 --- /dev/null +++ b/test/fixtures/no-rename-default/class-user.js @@ -0,0 +1 @@ +export default class User {}; diff --git a/test/fixtures/no-rename-default/const-bar.js b/test/fixtures/no-rename-default/const-bar.js new file mode 100644 index 00000000..da071dcc --- /dev/null +++ b/test/fixtures/no-rename-default/const-bar.js @@ -0,0 +1,6 @@ +export const barNamed1 = 'bar-named-1'; +export const barNamed2 = 'bar-named-2'; + +const bar = 'bar'; + +export default bar; diff --git a/test/fixtures/no-rename-default/const-foo.js b/test/fixtures/no-rename-default/const-foo.js new file mode 100644 index 00000000..a85147d8 --- /dev/null +++ b/test/fixtures/no-rename-default/const-foo.js @@ -0,0 +1,6 @@ +export const fooNamed1 = 'foo-named-1'; +export const fooNamed2 = 'foo-named-2'; + +const foo = 'foo'; + +export default foo; diff --git a/test/fixtures/no-rename-default/fn-get-users-sync.js b/test/fixtures/no-rename-default/fn-get-users-sync.js new file mode 100644 index 00000000..c1f60a47 --- /dev/null +++ b/test/fixtures/no-rename-default/fn-get-users-sync.js @@ -0,0 +1 @@ +export default function getUsersSync() {} diff --git a/test/fixtures/no-rename-default/fn-get-users.js b/test/fixtures/no-rename-default/fn-get-users.js new file mode 100644 index 00000000..9b77ba19 --- /dev/null +++ b/test/fixtures/no-rename-default/fn-get-users.js @@ -0,0 +1 @@ +export default async function getUsers() {} diff --git a/test/fixtures/no-rename-default/generator-reader.js b/test/fixtures/no-rename-default/generator-reader.js new file mode 100644 index 00000000..358f1186 --- /dev/null +++ b/test/fixtures/no-rename-default/generator-reader.js @@ -0,0 +1 @@ +export default function* reader() {} diff --git a/test/fixtures/no-rename-default/pr-3006-feedback/binding-const-rename-fn.js b/test/fixtures/no-rename-default/pr-3006-feedback/binding-const-rename-fn.js new file mode 100644 index 00000000..139b0492 --- /dev/null +++ b/test/fixtures/no-rename-default/pr-3006-feedback/binding-const-rename-fn.js @@ -0,0 +1,3 @@ +const foo = function bar() {}; + +export default foo; diff --git a/test/fixtures/no-rename-default/pr-3006-feedback/binding-fn-rename.js b/test/fixtures/no-rename-default/pr-3006-feedback/binding-fn-rename.js new file mode 100644 index 00000000..e16264a2 --- /dev/null +++ b/test/fixtures/no-rename-default/pr-3006-feedback/binding-fn-rename.js @@ -0,0 +1,3 @@ +function bar() {} + +export default bar; diff --git a/test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-foo.js b/test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-foo.js new file mode 100644 index 00000000..36904cdf --- /dev/null +++ b/test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-foo.js @@ -0,0 +1,4 @@ +import foo from '../default-const-foo'; +import withLogger from './hoc-with-logger'; + +export default withLogger(foo); diff --git a/test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-get-users.js b/test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-get-users.js new file mode 100644 index 00000000..6d16feef --- /dev/null +++ b/test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-get-users.js @@ -0,0 +1,4 @@ +import getUsers from '../default-fn-get-users'; +import withLogger from './hoc-with-logger'; + +export default withLogger(getUsers); diff --git a/test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-with-auth-for-get-users.js b/test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-with-auth-for-get-users.js new file mode 100644 index 00000000..53b24539 --- /dev/null +++ b/test/fixtures/no-rename-default/pr-3006-feedback/binding-hoc-with-logger-with-auth-for-get-users.js @@ -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)); diff --git a/test/fixtures/no-rename-default/pr-3006-feedback/hoc-with-auth.js b/test/fixtures/no-rename-default/pr-3006-feedback/hoc-with-auth.js new file mode 100644 index 00000000..03c9006a --- /dev/null +++ b/test/fixtures/no-rename-default/pr-3006-feedback/hoc-with-auth.js @@ -0,0 +1,6 @@ +export default function withAuth(fn) { + return function innerAuth(...args) { + const auth = {}; + return fn.call(null, auth, ...args); + } +} diff --git a/test/fixtures/no-rename-default/pr-3006-feedback/hoc-with-logger.js b/test/fixtures/no-rename-default/pr-3006-feedback/hoc-with-logger.js new file mode 100644 index 00000000..bead4362 --- /dev/null +++ b/test/fixtures/no-rename-default/pr-3006-feedback/hoc-with-logger.js @@ -0,0 +1,6 @@ +export default function withLogger(fn) { + return function innerLogger(...args) { + console.log(`${fn.name} called`); + return fn.apply(null, args); + } +} diff --git a/test/fixtures/no-rename-default/typescript-default.d.ts b/test/fixtures/no-rename-default/typescript-default.d.ts new file mode 100644 index 00000000..deac53f8 --- /dev/null +++ b/test/fixtures/no-rename-default/typescript-default.d.ts @@ -0,0 +1,3 @@ +declare const foo: {}; + +export default foo; diff --git a/test/package.spec.ts b/test/package.spec.ts index 9488e479..33c63dc2 100644 --- a/test/package.spec.ts +++ b/test/package.spec.ts @@ -52,6 +52,7 @@ describe('package', () => { continue } for (const rule of Object.keys(config.rules)) { + console.log({ rule, preamble }) expect(() => require(getRulePath(rule.slice(preamble.length))), ).not.toThrow() diff --git a/test/rules/no-rename-default.spec.ts b/test/rules/no-rename-default.spec.ts new file mode 100644 index 00000000..c7e04097 --- /dev/null +++ b/test/rules/no-rename-default.spec.ts @@ -0,0 +1,898 @@ +import { RuleTester } from '@typescript-eslint/rule-tester' + +import { parsers, test } from '../utils' + +import rule from 'eslint-plugin-import-x/rules/no-rename-default' + +const ruleTester = new RuleTester() + +// IMPORT +// anonymous-arrow.js +// anonymous-arrow-async.js +// anonymous-class.js +// anonymous-object.js +// anonymous-primitive.js +ruleTester.run('no-rename-default', rule, { + valid: [ + `import _ from './no-rename-default/anonymous-arrow'`, + `import _ from './no-rename-default/anonymous-arrow-async'`, + `import _ from './no-rename-default/anonymous-class'`, + `import _ from './no-rename-default/anonymous-object'`, + `import _ from './no-rename-default/anonymous-primitive'`, + ], + invalid: [], +}) + +// REQUIRE { commonjs: true } +// anonymous-arrow.js +// anonymous-arrow-async.js +// anonymous-class.js +// anonymous-object.js +// anonymous-primitive.js +ruleTester.run('no-rename-default', rule, { + valid: [ + test({ + code: `const _ = require('./no-rename-default/anonymous-arrow')`, + options: [{ commonjs: true }], + }), + test({ + code: `const _ = require('./no-rename-default/anonymous-arrow-async')`, + options: [{ commonjs: true }], + }), + test({ + code: `const _ = require('./no-rename-default/anonymous-class')`, + options: [{ commonjs: true }], + }), + test({ + code: `const _ = require('./no-rename-default/anonymous-object')`, + options: [{ commonjs: true }], + }), + test({ + code: `const _ = require('./no-rename-default/anonymous-primitive')`, + options: [{ commonjs: true }], + }), + ], + invalid: [], +}) + +// IMPORT +// assign-arrow.js +// assign-arrow-async.js +// assign-class.js +// assign-class-named.js +// assign-fn.js +// assign-fn-named.js +// assign-generator.js +// assign-generator-named.js +ruleTester.run('no-rename-default', rule, { + valid: [ + `import arrow from './no-rename-default/assign-arrow'`, + `import arrowAsync from './no-rename-default/assign-arrow-async'`, + `import User from './no-rename-default/assign-class'`, + `import User from './no-rename-default/assign-class-named'`, + `import fn from './no-rename-default/assign-fn'`, + `import fn from './no-rename-default/assign-fn-named'`, + `import generator from './no-rename-default/assign-generator'`, + `import generator from './no-rename-default/assign-generator-named'`, + ], + invalid: [ + test({ + code: `import myArrow from './no-rename-default/assign-arrow'`, + errors: [ + { + message: + "Caution: `assign-arrow.js` has a default export `arrow`. This imports `arrow` as `myArrow`. Check if you meant to write `import arrow from './no-rename-default/assign-arrow'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + test({ + code: `import myArrowAsync from './no-rename-default/assign-arrow-async'`, + errors: [ + { + message: + "Caution: `assign-arrow-async.js` has a default export `arrowAsync`. This imports `arrowAsync` as `myArrowAsync`. Check if you meant to write `import arrowAsync from './no-rename-default/assign-arrow-async'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + test({ + code: `import MyUser from './no-rename-default/assign-class'`, + errors: [ + { + message: + "Caution: `assign-class.js` has a default export `User`. This imports `User` as `MyUser`. Check if you meant to write `import User from './no-rename-default/assign-class'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + test({ + code: `import MyUser from './no-rename-default/assign-class-named'`, + errors: [ + { + message: + "Caution: `assign-class-named.js` has a default export `User`. This imports `User` as `MyUser`. Check if you meant to write `import User from './no-rename-default/assign-class-named'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + test({ + code: `import myFn from './no-rename-default/assign-fn'`, + errors: [ + { + message: + "Caution: `assign-fn.js` has a default export `fn`. This imports `fn` as `myFn`. Check if you meant to write `import fn from './no-rename-default/assign-fn'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + test({ + code: `import myFn from './no-rename-default/assign-fn-named'`, + errors: [ + { + message: + "Caution: `assign-fn-named.js` has a default export `fn`. This imports `fn` as `myFn`. Check if you meant to write `import fn from './no-rename-default/assign-fn-named'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + test({ + code: `import myGenerator from './no-rename-default/assign-generator'`, + errors: [ + { + message: + "Caution: `assign-generator.js` has a default export `generator`. This imports `generator` as `myGenerator`. Check if you meant to write `import generator from './no-rename-default/assign-generator'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + test({ + code: `import myGenerator from './no-rename-default/assign-generator-named'`, + errors: [ + { + message: + "Caution: `assign-generator-named.js` has a default export `generator`. This imports `generator` as `myGenerator`. Check if you meant to write `import generator from './no-rename-default/assign-generator-named'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + ], +}) + +// IMPORT { preventRenamingBindings: false } +// assign-arrow.js +// assign-arrow-async.js +// assign-class.js +// assign-class-named.js +// assign-fn.js +// assign-fn-named.js +// assign-generator.js +// assign-generator-named.js +ruleTester.run('no-rename-default', rule, { + valid: [ + test({ + code: `import myArrow from './no-rename-default/assign-arrow'`, + options: [{ preventRenamingBindings: false }], + }), + test({ + code: `import myArrowAsync from './no-rename-default/assign-arrow-async'`, + options: [{ preventRenamingBindings: false }], + }), + test({ + code: `import MyUser from './no-rename-default/assign-class'`, + options: [{ preventRenamingBindings: false }], + }), + test({ + code: `import MyUser from './no-rename-default/assign-class-named'`, + options: [{ preventRenamingBindings: false }], + }), + test({ + code: `import myFn from './no-rename-default/assign-fn'`, + options: [{ preventRenamingBindings: false }], + }), + test({ + code: `import myFn from './no-rename-default/assign-fn-named'`, + options: [{ preventRenamingBindings: false }], + }), + test({ + code: `import myGenerator from './no-rename-default/assign-generator'`, + options: [{ preventRenamingBindings: false }], + }), + test({ + code: `import myGenerator from './no-rename-default/assign-generator-named'`, + options: [{ preventRenamingBindings: false }], + }), + ], + invalid: [], +}) + +// REQUIRE { commonjs: true } +// assign-arrow.js +// assign-arrow-async.js +// assign-class.js +// assign-class-named.js +// assign-fn.js +// assign-fn-named.js +// assign-generator.js +// assign-generator-named.js +ruleTester.run('no-rename-default', rule, { + valid: [ + test({ + code: `const arrow = require('./no-rename-default/assign-arrow')`, + options: [{ commonjs: true }], + }), + test({ + code: `const arrowAsync = require('./no-rename-default/assign-arrow-async')`, + options: [{ commonjs: true }], + }), + test({ + code: `const User = require('./no-rename-default/assign-class')`, + options: [{ commonjs: true }], + }), + test({ + code: `const User = require('./no-rename-default/assign-class-named')`, + options: [{ commonjs: true }], + }), + test({ + code: `const fn = require('./no-rename-default/assign-fn')`, + options: [{ commonjs: true }], + }), + test({ + code: `const fn = require('./no-rename-default/assign-fn-named')`, + options: [{ commonjs: true }], + }), + test({ + code: `const generator = require('./no-rename-default/assign-generator')`, + options: [{ commonjs: true }], + }), + test({ + code: `const generator = require('./no-rename-default/assign-generator-named')`, + options: [{ commonjs: true }], + }), + ], + invalid: [ + test({ + code: `const myArrow = require('./no-rename-default/assign-arrow')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `assign-arrow.js` has a default export `arrow`. This requires `arrow` as `myArrow`. Check if you meant to write `const arrow = require('./no-rename-default/assign-arrow')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + test({ + code: `const myArrowAsync = require('./no-rename-default/assign-arrow-async')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `assign-arrow-async.js` has a default export `arrowAsync`. This requires `arrowAsync` as `myArrowAsync`. Check if you meant to write `const arrowAsync = require('./no-rename-default/assign-arrow-async')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + test({ + code: `const MyUser = require('./no-rename-default/assign-class')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `assign-class.js` has a default export `User`. This requires `User` as `MyUser`. Check if you meant to write `const User = require('./no-rename-default/assign-class')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + test({ + code: `const MyUser = require('./no-rename-default/assign-class-named')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `assign-class-named.js` has a default export `User`. This requires `User` as `MyUser`. Check if you meant to write `const User = require('./no-rename-default/assign-class-named')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + test({ + code: `const myFn = require('./no-rename-default/assign-fn')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `assign-fn.js` has a default export `fn`. This requires `fn` as `myFn`. Check if you meant to write `const fn = require('./no-rename-default/assign-fn')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + test({ + code: `const myFn = require('./no-rename-default/assign-fn-named')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `assign-fn-named.js` has a default export `fn`. This requires `fn` as `myFn`. Check if you meant to write `const fn = require('./no-rename-default/assign-fn-named')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + test({ + code: `const myGenerator = require('./no-rename-default/assign-generator')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `assign-generator.js` has a default export `generator`. This requires `generator` as `myGenerator`. Check if you meant to write `const generator = require('./no-rename-default/assign-generator')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + test({ + code: `const myGenerator = require('./no-rename-default/assign-generator-named')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `assign-generator-named.js` has a default export `generator`. This requires `generator` as `myGenerator`. Check if you meant to write `const generator = require('./no-rename-default/assign-generator-named')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + ], +}) + +// REQUIRE { commonjs: true, preventRenamingBindings: false } +// assign-arrow.js +// assign-arrow-async.js +// assign-class.js +// assign-class-named.js +// assign-fn.js +// assign-fn-named.js +// assign-generator.js +// assign-generator-named.js +ruleTester.run('no-renamed-default', rule, { + valid: [ + test({ + code: `const myArrow = require('./no-rename-default/assign-arrow')`, + options: [{ commonjs: true, preventRenamingBindings: false }], + }), + test({ + code: `const myArrowAsync = require('./no-rename-default/assign-arrow-async')`, + options: [{ commonjs: true, preventRenamingBindings: false }], + }), + test({ + code: `const MyUser = require('./no-rename-default/assign-class')`, + options: [{ commonjs: true, preventRenamingBindings: false }], + }), + test({ + code: `const MyUser = require('./no-rename-default/assign-class-named')`, + options: [{ commonjs: true, preventRenamingBindings: false }], + }), + test({ + code: `const myFn = require('./no-rename-default/assign-fn')`, + options: [{ commonjs: true, preventRenamingBindings: false }], + }), + test({ + code: `const myFn = require('./no-rename-default/assign-fn-named')`, + options: [{ commonjs: true, preventRenamingBindings: false }], + }), + test({ + code: `const myGenerator = require('./no-rename-default/assign-generator')`, + options: [{ commonjs: true, preventRenamingBindings: false }], + }), + test({ + code: `const myGenerator = require('./no-rename-default/assign-generator-named')`, + options: [{ commonjs: true, preventRenamingBindings: false }], + }), + ], + invalid: [], +}) + +// IMPORT +// class-user.js +ruleTester.run('no-rename-default', rule, { + valid: [`import User from './no-rename-default/class-user'`], + invalid: [ + test({ + code: `import MyUser from './no-rename-default/class-user'`, + errors: [ + { + message: + "Caution: `class-user.js` has a default export `User`. This imports `User` as `MyUser`. Check if you meant to write `import User from './no-rename-default/class-user'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + ], +}) + +// REQUIRE { commonjs: true } +// class-user.js +ruleTester.run('no-rename-default', rule, { + valid: [ + test({ + code: `const User = require('./no-rename-default/class-user')`, + options: [{ commonjs: true }], + }), + ], + invalid: [ + test({ + code: `const MyUser = require('./no-rename-default/class-user')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `class-user.js` has a default export `User`. This requires `User` as `MyUser`. Check if you meant to write `const User = require('./no-rename-default/class-user')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + ], +}) + +// IMPORT +// const-bar.js +// const-foo.js +ruleTester.run('no-rename-default', rule, { + valid: [ + `import foo from './no-rename-default/const-foo'`, + `import { fooNamed1 } from './no-rename-default/const-foo'`, + `import { fooNamed1, fooNamed2 } from './no-rename-default/const-foo'`, + `import { default as foo } from './no-rename-default/const-foo'`, + `import { default as foo, fooNamed1 } from './no-rename-default/const-foo'`, + `import foo, { fooNamed1 } from './no-rename-default/const-foo'`, + ` + import bar from './no-rename-default/const-bar' + import foo from './no-rename-default/const-foo' + `, + ` + import bar, { barNamed1 } from './no-rename-default/const-bar' + import foo, { fooNamed1 } from './no-rename-default/const-foo' + `, + ], + invalid: [ + test({ + code: `import bar from './no-rename-default/const-foo'`, + errors: [ + { + message: + "Caution: `const-foo.js` has a default export `foo`. This imports `foo` as `bar`. Check if you meant to write `import foo from './no-rename-default/const-foo'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + test({ + code: `import { default as bar } from './no-rename-default/const-foo'`, + errors: [ + { + message: + "Caution: `const-foo.js` has a default export `foo`. This imports `foo` as `bar`. Check if you meant to write `import { default as foo } from './no-rename-default/const-foo'` instead.", + type: 'ImportSpecifier', + }, + ], + }), + test({ + code: `import { default as bar, fooNamed1 } from './no-rename-default/const-foo'`, + errors: [ + { + message: + "Caution: `const-foo.js` has a default export `foo`. This imports `foo` as `bar`. Check if you meant to write `import { default as foo } from './no-rename-default/const-foo'` instead.", + type: 'ImportSpecifier', + }, + ], + }), + test({ + code: `import bar, { fooNamed1 } from './no-rename-default/const-foo'`, + errors: [ + { + message: + "Caution: `const-foo.js` has a default export `foo`. This imports `foo` as `bar`. Check if you meant to write `import foo from './no-rename-default/const-foo'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + test({ + code: `import foo from './no-rename-default/const-bar' + import bar from './no-rename-default/const-foo'`, + errors: [ + { + message: + "Caution: `const-bar.js` has a default export `bar`. This imports `bar` as `foo`. Check if you meant to write `import bar from './no-rename-default/const-bar'` instead.", + type: 'ImportDefaultSpecifier', + }, + { + message: + "Caution: `const-foo.js` has a default export `foo`. This imports `foo` as `bar`. Check if you meant to write `import foo from './no-rename-default/const-foo'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + test({ + code: `import findUsers from './no-rename-default/fn-get-users'`, + errors: [ + { + message: + "Caution: `fn-get-users.js` has a default export `getUsers`. This imports `getUsers` as `findUsers`. Check if you meant to write `import getUsers from './no-rename-default/fn-get-users'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + test({ + code: `import findUsersSync from './no-rename-default/fn-get-users-sync'`, + errors: [ + { + message: + "Caution: `fn-get-users-sync.js` has a default export `getUsersSync`. This imports `getUsersSync` as `findUsersSync`. Check if you meant to write `import getUsersSync from './no-rename-default/fn-get-users-sync'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + test({ + code: `import foo, { barNamed1 } from './no-rename-default/const-bar' + import bar, { fooNamed1 } from './no-rename-default/const-foo'`, + errors: [ + { + message: + "Caution: `const-bar.js` has a default export `bar`. This imports `bar` as `foo`. Check if you meant to write `import bar from './no-rename-default/const-bar'` instead.", + type: 'ImportDefaultSpecifier', + }, + { + message: + "Caution: `const-foo.js` has a default export `foo`. This imports `foo` as `bar`. Check if you meant to write `import foo from './no-rename-default/const-foo'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + ], +}) + +// REQUIRE { commonjs: true } +// const-bar.js +// const-foo.js +ruleTester.run('no-rename-default', rule, { + valid: [ + test({ + code: `const foo = require('./no-rename-default/const-foo')`, + options: [{ commonjs: true }], + }), + test({ + code: `const { fooNamed1 } = require('./no-rename-default/const-foo')`, + options: [{ commonjs: true }], + }), + test({ + code: `const { fooNamed1, fooNamed2 } = require('./no-rename-default/const-foo')`, + options: [{ commonjs: true }], + }), + test({ + code: `const { default: foo } = require('./no-rename-default/const-foo')`, + options: [{ commonjs: true }], + }), + test({ + code: `const { default: foo, fooNamed1 } = require('./no-rename-default/const-foo')`, + options: [{ commonjs: true }], + }), + test({ + code: `const foo = require('./no-rename-default/const-foo') + const { fooNamed1 } = require('./no-rename-default/const-foo')`, + options: [{ commonjs: true }], + }), + test({ + code: `const getUsers = require('./no-rename-default/fn-get-users')`, + options: [{ commonjs: true }], + }), + test({ + code: `const getUsersSync = require('./no-rename-default/fn-get-users-sync')`, + options: [{ commonjs: true }], + }), + test({ + code: ` + const bar = require('./no-rename-default/const-bar') + const { barNamed1 } = require('./no-rename-default/const-bar') + const foo = require('./no-rename-default/const-foo') + const { fooNamed1 } = require('./no-rename-default/const-foo') + `, + options: [{ commonjs: true }], + }), + ], + invalid: [ + test({ + code: `const bar = require('./no-rename-default/const-foo')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `const-foo.js` has a default export `foo`. This requires `foo` as `bar`. Check if you meant to write `const foo = require('./no-rename-default/const-foo')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + test({ + code: `const bar = require('./no-rename-default/const-foo') + const { fooNamed1 } = require('./no-rename-default/const-foo')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `const-foo.js` has a default export `foo`. This requires `foo` as `bar`. Check if you meant to write `const foo = require('./no-rename-default/const-foo')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + test({ + code: `const { default: bar } = require('./no-rename-default/const-foo')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `const-foo.js` has a default export `foo`. This requires `foo` as `bar`. Check if you meant to write `const { default: foo } = require('./no-rename-default/const-foo')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + test({ + code: `const { default: bar, fooNamed1 } = require('./no-rename-default/const-foo')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `const-foo.js` has a default export `foo`. This requires `foo` as `bar`. Check if you meant to write `const { default: foo } = require('./no-rename-default/const-foo')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + test({ + code: ` + const foo = require('./no-rename-default/const-bar') + const bar = require('./no-rename-default/const-foo') + `, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `const-bar.js` has a default export `bar`. This requires `bar` as `foo`. Check if you meant to write `const bar = require('./no-rename-default/const-bar')` instead.", + type: 'VariableDeclarator', + }, + { + message: + "Caution: `const-foo.js` has a default export `foo`. This requires `foo` as `bar`. Check if you meant to write `const foo = require('./no-rename-default/const-foo')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + test({ + code: ` + const foo = require('./no-rename-default/const-bar') + const { barNamed1 } = require('./no-rename-default/const-bar') + const bar = require('./no-rename-default/const-foo') + const { fooNamed1 } = require('./no-rename-default/const-foo') + `, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `const-bar.js` has a default export `bar`. This requires `bar` as `foo`. Check if you meant to write `const bar = require('./no-rename-default/const-bar')` instead.", + type: 'VariableDeclarator', + }, + { + message: + "Caution: `const-foo.js` has a default export `foo`. This requires `foo` as `bar`. Check if you meant to write `const foo = require('./no-rename-default/const-foo')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + ], +}) + +// IMPORT +// fn-get-users.js +// fn-get-users-sync.js +ruleTester.run('no-rename-default', rule, { + valid: [ + `import getUsers from './no-rename-default/fn-get-users'`, + `import getUsersSync from './no-rename-default/fn-get-users-sync'`, + ], + invalid: [ + test({ + code: `import findUsers from './no-rename-default/fn-get-users'`, + errors: [ + { + message: + "Caution: `fn-get-users.js` has a default export `getUsers`. This imports `getUsers` as `findUsers`. Check if you meant to write `import getUsers from './no-rename-default/fn-get-users'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + test({ + code: `import findUsersSync from './no-rename-default/fn-get-users-sync'`, + errors: [ + { + message: + "Caution: `fn-get-users-sync.js` has a default export `getUsersSync`. This imports `getUsersSync` as `findUsersSync`. Check if you meant to write `import getUsersSync from './no-rename-default/fn-get-users-sync'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + ], +}) + +// REQUIRE { commonjs: true } +// fn-get-users.js +// fn-get-users-sync.js +ruleTester.run('no-rename-default', rule, { + valid: [ + test({ + code: `const getUsers = require('./no-rename-default/fn-get-users')`, + options: [{ commonjs: true }], + }), + test({ + code: `const getUsersSync = require('./no-rename-default/fn-get-users-sync')`, + options: [{ commonjs: true }], + }), + ], + invalid: [ + test({ + code: `const findUsers = require('./no-rename-default/fn-get-users')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `fn-get-users.js` has a default export `getUsers`. This requires `getUsers` as `findUsers`. Check if you meant to write `const getUsers = require('./no-rename-default/fn-get-users')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + test({ + code: `const findUsersSync = require('./no-rename-default/fn-get-users-sync')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `fn-get-users-sync.js` has a default export `getUsersSync`. This requires `getUsersSync` as `findUsersSync`. Check if you meant to write `const getUsersSync = require('./no-rename-default/fn-get-users-sync')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + ], +}) + +// IMPORT +// generator-reader.js +ruleTester.run('no-rename-default', rule, { + valid: [`import reader from './no-rename-default/generator-reader'`], + invalid: [ + test({ + code: `import myReader from './no-rename-default/generator-reader'`, + errors: [ + { + message: + "Caution: `generator-reader.js` has a default export `reader`. This imports `reader` as `myReader`. Check if you meant to write `import reader from './no-rename-default/generator-reader'` instead.", + type: 'ImportDefaultSpecifier', + }, + ], + }), + ], +}) + +// REQUIRE { commonjs: true } +// generator-reader.js +ruleTester.run('no-rename-default', rule, { + valid: [ + test({ + code: `const reader = require('./no-rename-default/generator-reader')`, + options: [{ commonjs: true }], + }), + ], + invalid: [ + test({ + code: `const myReader = require('./no-rename-default/generator-reader')`, + options: [{ commonjs: true }], + errors: [ + { + message: + "Caution: `generator-reader.js` has a default export `reader`. This requires `reader` as `myReader`. Check if you meant to write `const reader = require('./no-rename-default/generator-reader')` instead.", + type: 'VariableDeclarator', + }, + ], + }), + ], +}) + +//------------------------------------------------------------------------------ +// PR_FEEDBACK +//------------------------------------------------------------------------------ + +// IMPORT +// binding-const-rename-fn.js +ruleTester.run('no-rename-default', rule, { + valid: [ + `import foo from './no-rename-default/pr-3006-feedback/binding-const-rename-fn'`, + ], + invalid: [], +}) + +// REQUIRE { commonjs: true } +// binding-const-rename-fn.js +ruleTester.run('no-rename-default', rule, { + valid: [ + test({ + code: `const foo = require('./no-rename-default/pr-3006-feedback/binding-const-rename-fn')`, + options: [{ commonjs: true, preventRenamingBindings: false }], + }), + ], + invalid: [], +}) + +// IMPORT +// binding-hoc-with-logger-for-foo.js +// binding-hoc-with-logger-for-get-users.js +// binding-hoc-with-logger-with-auth-for-get-users.js +ruleTester.run('no-rename-default', rule, { + valid: [ + `import foo from './no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-foo'`, + `import getUsers from './no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-get-users'`, + `import getUsers from './no-rename-default/pr-3006-feedback/binding-hoc-with-logger-with-auth-for-get-users'`, + ], + invalid: [], +}) + +// REQUIRE { commonjs: true } +// binding-hoc-with-logger-for-foo.js +// binding-hoc-with-logger-for-get-users.js +// binding-hoc-with-logger-with-auth-for-get-users.js +ruleTester.run('no-rename-default', rule, { + valid: [ + test({ + code: `const foo = require('./no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-foo')`, + options: [{ commonjs: true }], + }), + test({ + code: `const getUsers = require('./no-rename-default/pr-3006-feedback/binding-hoc-with-logger-for-get-users')`, + options: [{ commonjs: true }], + }), + test({ + code: `const getUsers = require('./no-rename-default/pr-3006-feedback/binding-hoc-with-logger-with-auth-for-get-users')`, + options: [{ commonjs: true }], + }), + ], + invalid: [], +}) + +// IMPORT { preventRenamingBindings: false } +// binding-fn-rename.js +ruleTester.run('no-rename-default', rule, { + valid: [ + test({ + code: `import _ from './no-rename-default/pr-3006-feedback/binding-fn-rename'`, + options: [{ preventRenamingBindings: false }], + }), + ], + invalid: [], +}) + +// REQUIRE { commonjs: true, preventRenamingBindings: false } +// binding-fn-rename.js +ruleTester.run('no-rename-default', rule, { + valid: [ + test({ + code: `const _ = require('./no-rename-default/pr-3006-feedback/binding-fn-rename')`, + options: [{ commonjs: true, preventRenamingBindings: false }], + }), + ], + invalid: [], +}) + +describe('TypeScript', function () { + ruleTester.run('no-rename-default', rule, { + valid: [ + test({ + code: `import foo from './no-rename-default/typescript-default'`, + settings: { + 'import-x/parsers': { [parsers.TS]: ['.ts'] }, + 'import-x/resolver': { 'eslint-import-resolver-typescript': true }, + }, + }), + ], + invalid: [], + }) +}) From 96e00eaea7c6257caaff6bf6d069ccf2b26ab4a6 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Tue, 27 Aug 2024 07:00:55 +0800 Subject: [PATCH 4/6] docs: update docs --- README.md | 1 + docs/rules/no-rename-default.md | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d3967509..76943aae 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a | [no-mutable-exports](docs/rules/no-mutable-exports.md) | Forbid the use of mutable exports with `var` or `let`. | | | | | | | | [no-named-as-default](docs/rules/no-named-as-default.md) | Forbid use of exported name as identifier of default export. | | ☑️ 🚸 | | | | | | [no-named-as-default-member](docs/rules/no-named-as-default-member.md) | Forbid use of exported name as property of default export. | | ☑️ 🚸 | | | | | +| [no-rename-default](docs/rules/no-rename-default.md) | Forbid importing a default export by a different name. | | | | | | | | [no-unused-modules](docs/rules/no-unused-modules.md) | Forbid modules without exports, or exports without matching import in another module. | | | | | | | ### Module systems diff --git a/docs/rules/no-rename-default.md b/docs/rules/no-rename-default.md index f101850d..58a04468 100644 --- a/docs/rules/no-rename-default.md +++ b/docs/rules/no-rename-default.md @@ -1,4 +1,4 @@ -# import/no-rename-default +# import-x/no-rename-default @@ -16,7 +16,7 @@ export default async function getUsers() {} ...this would be valid: ```js -import getUsers from './api/get-users.js'; +import getUsers from './api/get-users.js' ``` ...and the following would be reported: @@ -25,5 +25,5 @@ import getUsers from './api/get-users.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'; +import findUsers from './get-users' ``` From ba490a4baaf0f2150af4f81b23c279f3f3db6e89 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Tue, 27 Aug 2024 07:03:32 +0800 Subject: [PATCH 5/6] chore: add changeset --- .changeset/tasty-colts-cover.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tasty-colts-cover.md diff --git a/.changeset/tasty-colts-cover.md b/.changeset/tasty-colts-cover.md new file mode 100644 index 00000000..0fd37489 --- /dev/null +++ b/.changeset/tasty-colts-cover.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-import-x": minor +--- + +Added `no-rename-default` that forbid importing a default export by a different name. Originally created by @whitneytit, ported by @SukkaW From d00aa28e1bd37be8c0e49a1b6e0815f076fbffb0 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Tue, 27 Aug 2024 07:05:50 +0800 Subject: [PATCH 6/6] docs: update docs --- README.md | 2 +- docs/rules/no-rename-default.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 76943aae..1d7c5a4f 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a | [no-mutable-exports](docs/rules/no-mutable-exports.md) | Forbid the use of mutable exports with `var` or `let`. | | | | | | | | [no-named-as-default](docs/rules/no-named-as-default.md) | Forbid use of exported name as identifier of default export. | | ☑️ 🚸 | | | | | | [no-named-as-default-member](docs/rules/no-named-as-default-member.md) | Forbid use of exported name as property of default export. | | ☑️ 🚸 | | | | | -| [no-rename-default](docs/rules/no-rename-default.md) | Forbid importing a default export by a different name. | | | | | | | +| [no-rename-default](docs/rules/no-rename-default.md) | Forbid importing a default export by a different name. | | 🚸 | | | | | | [no-unused-modules](docs/rules/no-unused-modules.md) | Forbid modules without exports, or exports without matching import in another module. | | | | | | | ### Module systems diff --git a/docs/rules/no-rename-default.md b/docs/rules/no-rename-default.md index 58a04468..4fbeb507 100644 --- a/docs/rules/no-rename-default.md +++ b/docs/rules/no-rename-default.md @@ -1,5 +1,7 @@ # import-x/no-rename-default +⚠️ This rule _warns_ in the 🚸 `warnings` config. + Prohibit importing a default export by another name.