diff --git a/CHANGELOG.md b/CHANGELOG.md index e1ff0c4e70..6e4d2ac465 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com). ## [Unreleased] +### Added +- [`no-anonymous-default-export`] rule: report anonymous default exports; literals or anonymous functions. + ### Changed - [`no-extraneous-dependencies`]: use `read-pkg-up` to simplify finding + loading `package.json` ([#680], thanks [@wtgtybhertgeghgtwtg]) diff --git a/README.md b/README.md index 519e707d81..2877dbeee5 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a * Limit the maximum number of dependencies a module can have ([`max-dependencies`]) * Forbid unassigned imports ([`no-unassigned-import`]) * Forbid named default exports ([`no-named-default`]) +* Forbid literals and anonymous functions as default exports ([`no-anonymous-default-export`]) [`first`]: ./docs/rules/first.md [`no-duplicates`]: ./docs/rules/no-duplicates.md @@ -87,6 +88,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a [`max-dependencies`]: ./docs/rules/max-dependencies.md [`no-unassigned-import`]: ./docs/rules/no-unassigned-import.md [`no-named-default`]: ./docs/rules/no-named-default.md +[`no-anonymous-default-export`]: ./docs/rules/no-anonymous-default-export.md ## Installation diff --git a/docs/rules/no-anonymous-default-export.md b/docs/rules/no-anonymous-default-export.md new file mode 100644 index 0000000000..1a5ca717f8 --- /dev/null +++ b/docs/rules/no-anonymous-default-export.md @@ -0,0 +1,37 @@ +# no-anonymous-default-export + +Reports if an unnamed literal or anonymous function is exported as a module's default. +This helps improve the grepability of the codebase by encouraging the re-use of the same identifier for the module's default export at its declaration site and at its import sites. + +## Rule Details + +### Fail +```js +export default 123 +``` + +```js +export default {} +``` + +```js +export default function () {} +``` + +```js +export default () => {} +``` + +```js +export default class {} +``` + +### Pass +```js +const foo = 123 +export default foo +``` + +```js +export default function foo() {} +``` diff --git a/docs/rules/no-conditional-export.md b/docs/rules/no-conditional-export.md new file mode 100644 index 0000000000..43403b9bef --- /dev/null +++ b/docs/rules/no-conditional-export.md @@ -0,0 +1,25 @@ +# no-conditional-export + +Reports if a conditional is used as an export. +This helps improve the readability of the codebase by forbidding potentially complex sub-expressions. + +## Rule Details + +### Fail +```js +export default 123 +``` + +```js +export default function () {} +``` + +### Pass +```js +const foo = 123 +export default foo +``` + +```js +export default function foo() {} +``` diff --git a/src/index.js b/src/index.js index 67fdb13261..69cbc2f5e6 100644 --- a/src/index.js +++ b/src/index.js @@ -13,6 +13,7 @@ export const rules = { 'no-named-default': require('./rules/no-named-default'), 'no-named-as-default': require('./rules/no-named-as-default'), 'no-named-as-default-member': require('./rules/no-named-as-default-member'), + 'no-anonymous-default-export': require('./rules/no-anonymous-default-export'), 'no-commonjs': require('./rules/no-commonjs'), 'no-amd': require('./rules/no-amd'), diff --git a/src/rules/no-anonymous-default-export.js b/src/rules/no-anonymous-default-export.js new file mode 100644 index 0000000000..5cc18bcd59 --- /dev/null +++ b/src/rules/no-anonymous-default-export.js @@ -0,0 +1,49 @@ +/** + * @fileoverview Rule to disallow anonymous default exports. + * @author Duncan Beevers + */ + +module.exports = { + meta: {}, + + create: function (context) { + + return { + 'ExportDefaultDeclaration': (node) => { + // Export function + if (node.declaration.type === 'Literal' || node.declaration.type === 'ObjectExpression') { + context.report({ + node: node, + message: 'Unexpected default export of literal', + }) + return + } + + if (node.declaration.type === 'FunctionDeclaration' && !node.declaration.id) { + context.report({ + node: node, + message: 'Unexpected default export of anonymous function', + }) + return + } + + if (node.declaration.type === 'ClassDeclaration' && !node.declaration.id) { + context.report({ + node: node, + message: 'Unexpected default export of anonymous class', + }) + return + } + + if (node.declaration.type === 'ArrowFunctionExpression') { + context.report({ + node: node, + message: 'Unexpected default export of arrow function', + }) + return + } + }, + } + + }, +} diff --git a/tests/src/rules/no-anonymous-default-export.js b/tests/src/rules/no-anonymous-default-export.js new file mode 100644 index 0000000000..fc7e085547 --- /dev/null +++ b/tests/src/rules/no-anonymous-default-export.js @@ -0,0 +1,24 @@ +import { test, SYNTAX_CASES } from '../utils' + +import { RuleTester } from 'eslint' + +var ruleTester = new RuleTester() +var rule = require('rules/no-anonymous-default-export') + +ruleTester.run('no-anonymous-default-export', rule, { + valid: [ + test({ code: 'const foo = 123\nexport default foo' }), + test({ code: 'export default function foo() {}'}), + test({ code: 'export default class MyClass {}'}), + + ...SYNTAX_CASES, + ], + + invalid: [ + test({ code: 'export default 123', errors: [{ message: 'Unexpected default export of literal' }] }), + test({ code: 'export default {}', errors: [{ message: 'Unexpected default export of literal' }] }), + test({ code: 'export default class {}', errors: [{ message: 'Unexpected default export of anonymous class' }] }), + test({ code: 'export default function() {}', errors: [{ message: 'Unexpected default export of anonymous function' }] }), + test({ code: 'export default () => {}', errors: [{ message: 'Unexpected default export of arrow function' }] }), + ], +})