diff --git a/README.md b/README.md index 53b264062..6f826a85b 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a * Forbid unassigned imports ([`no-unassigned-import`]) * Forbid named default exports ([`no-named-default`]) * Forbid default exports ([`no-default-export`]) +* Forbid named exports ([`no-named-export`]) * Forbid anonymous values as default exports ([`no-anonymous-default-export`]) * Prefer named exports to be grouped together in a single export declaration ([`group-exports`]) * Enforce a leading comment with the webpackChunkName for dynamic imports ([`dynamic-import-chunkname`]) @@ -104,6 +105,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a [`no-anonymous-default-export`]: ./docs/rules/no-anonymous-default-export.md [`group-exports`]: ./docs/rules/group-exports.md [`no-default-export`]: ./docs/rules/no-default-export.md +[`no-named-export`]: ./docs/rules/no-named-export.md [`dynamic-import-chunkname`]: ./docs/rules/dynamic-import-chunkname.md ## Installation diff --git a/docs/rules/no-named-export.md b/docs/rules/no-named-export.md new file mode 100644 index 000000000..b3a0ef8d6 --- /dev/null +++ b/docs/rules/no-named-export.md @@ -0,0 +1,77 @@ +# no-named-export + +Prohibit named exports. Mostly an inverse of [`no-default-export`]. + +[`no-default-export`]: ./no-default-export.md + +## Rule Details + +The following patterns are considered warnings: + +```javascript +// bad1.js + +// There is only a single module export and it's a named export. +export const foo = 'foo'; +``` + +```javascript +// bad2.js + +// There is more than one named export in the module. +export const foo = 'foo'; +export const bar = 'bar'; +``` + +```javascript +// bad3.js + +// There is more than one named export in the module. +const foo = 'foo'; +const bar = 'bar'; +export { foo, bar } +``` + +```javascript +// bad4.js + +// There is more than one named export in the module. +export * from './other-module' +``` + +```javascript +// bad5.js + +// There is a default and a named export. +export const foo = 'foo'; +const bar = 'bar'; +export default 'bar'; +``` + +The following patterns are not warnings: + +```javascript +// good1.js + +// There is only a single module export and it's a default export. +export default 'bar'; +``` + +```javascript +// good2.js + +// There is only a single module export and it's a default export. +const foo = 'foo'; +export { foo as default } +``` + +```javascript +// good3.js + +// There is only a single module export and it's a default export. +export default from './other-module'; +``` + +## When Not To Use It + +If you don't care if named imports are used, or if you prefer named imports over default imports. diff --git a/src/index.js b/src/index.js index 7df67867f..f5794595d 100644 --- a/src/index.js +++ b/src/index.js @@ -32,6 +32,7 @@ export const rules = { 'newline-after-import': require('./rules/newline-after-import'), 'prefer-default-export': require('./rules/prefer-default-export'), 'no-default-export': require('./rules/no-default-export'), + 'no-named-export': require('./rules/no-named-export'), 'no-dynamic-require': require('./rules/no-dynamic-require'), 'unambiguous': require('./rules/unambiguous'), 'no-unassigned-import': require('./rules/no-unassigned-import'), diff --git a/src/rules/no-named-export.js b/src/rules/no-named-export.js new file mode 100644 index 000000000..2c9f68599 --- /dev/null +++ b/src/rules/no-named-export.js @@ -0,0 +1,33 @@ +import docsUrl from '../docsUrl' + +module.exports = { + meta: { + docs: { url: docsUrl('no-named-export') }, + }, + + create(context) { + // ignore non-modules + if (context.parserOptions.sourceType !== 'module') { + return {} + } + + const message = 'Named exports are not allowed.' + + return { + ExportAllDeclaration(node) { + context.report({node, message}) + }, + + ExportNamedDeclaration(node) { + if (node.specifiers.length === 0) { + return context.report({node, message}) + } + + const someNamed = node.specifiers.some(specifier => specifier.exported.name !== 'default') + if (someNamed) { + context.report({node, message}) + } + }, + } + }, +} diff --git a/tests/src/rules/no-named-export.js b/tests/src/rules/no-named-export.js new file mode 100644 index 000000000..d8748d3f6 --- /dev/null +++ b/tests/src/rules/no-named-export.js @@ -0,0 +1,179 @@ +import { RuleTester } from 'eslint' +import { test } from '../utils' + +const ruleTester = new RuleTester() + , rule = require('rules/no-named-export') + +ruleTester.run('no-named-export', rule, { + valid: [ + test({ + code: 'export default function bar() {};', + }), + test({ + code: 'export { foo as default }', + }), + test({ + code: 'export default from "foo.js"', + parser: 'babel-eslint', + }), + + // no exports at all + test({ + code: `import * as foo from './foo';`, + }), + test({ + code: `import foo from './foo';`, + }), + test({ + code: `import {default as foo} from './foo';`, + }), + ], + invalid: [ + test({ + code: ` + export const foo = 'foo'; + export const bar = 'bar'; + `, + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }, { + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: ` + export const foo = 'foo'; + export default bar;`, + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: ` + export const foo = 'foo'; + export function bar() {}; + `, + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }, { + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: `export const foo = 'foo';`, + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: ` + const foo = 'foo'; + export { foo }; + `, + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: `export { foo, bar }`, + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: `export const { foo, bar } = item;`, + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: `export const { foo, bar: baz } = item;`, + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: `export const { foo: { bar, baz } } = item;`, + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: ` + export const foo = item; + export { item }; + `, + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }, { + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: `export * from './foo';`, + errors: [{ + ruleId: 'ExportAllDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: `export const { foo } = { foo: "bar" };`, + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: `export const { foo: { bar } } = { foo: { bar: "baz" } };`, + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: 'export { a, b } from "foo.js"', + parser: 'babel-eslint', + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: `export type UserId = number;`, + parser: 'babel-eslint', + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: 'export foo from "foo.js"', + parser: 'babel-eslint', + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + test({ + code: `export Memory, { MemoryValue } from './Memory'`, + parser: 'babel-eslint', + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Named exports are not allowed.', + }], + }), + ], +})