From cf45512a1bc3fc476745269fee54713dc4a4ea71 Mon Sep 17 00:00:00 2001 From: "Gavri (Gabriel) Guy" Date: Thu, 5 May 2016 11:48:36 -0400 Subject: [PATCH] Add `prefer-default-export` rule --- CHANGELOG.md | 6 +++ README.md | 1 + docs/rules/prefer-default-export.md | 51 ++++++++++++++++++++++++ src/index.js | 1 + src/rules/prefer-default-export.js | 31 ++++++++++++++ tests/src/rules/prefer-default-export.js | 48 ++++++++++++++++++++++ 6 files changed, 138 insertions(+) create mode 100644 docs/rules/prefer-default-export.md create mode 100644 src/rules/prefer-default-export.js create mode 100644 tests/src/rules/prefer-default-export.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 12c699911..cef8d8e53 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 +- [`prefer-default-export`], new rule. ([#308], thanks [@gavriguy]) + ### Fixed - ignore namespace / ES7 re-exports in [`no-mutable-exports`]. ([#317], fixed by [#322]. thanks [@borisyankov] + [@jfmengels]) @@ -204,8 +207,10 @@ for info on changes for earlier releases. [`named`]: ./docs/rules/named.md [`newline-after-import`]: ./docs/rules/newline-after-import.md [`no-mutable-exports`]: ./docs/rules/no-mutable-exports.md +[`prefer-default-export`]: ./docs/rules/prefer-default-export.md [#322]: https://github.com/benmosher/eslint-plugin-import/pull/322 +[#308]: https://github.com/benmosher/eslint-plugin-import/pull/308 [#298]: https://github.com/benmosher/eslint-plugin-import/pull/298 [#297]: https://github.com/benmosher/eslint-plugin-import/pull/297 [#296]: https://github.com/benmosher/eslint-plugin-import/pull/296 @@ -281,3 +286,4 @@ for info on changes for earlier releases. [@SimenB]: https://github.com/SimenB [@josh]: https://github.com/josh [@borisyankov]: https://github.com/borisyankov +[@gavriguy]: https://github.com/gavriguy diff --git a/README.md b/README.md index bf530fb99..fc15ac7cf 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a [`extensions`]: ./docs/rules/extensions.md [`order`]: ./docs/rules/order.md [`newline-after-import`]: ./docs/rules/newline-after-import.md +[`prefer-default-export`]: ./docs/rules/prefer-default-export.md ## Installation diff --git a/docs/rules/prefer-default-export.md b/docs/rules/prefer-default-export.md new file mode 100644 index 000000000..892abfa38 --- /dev/null +++ b/docs/rules/prefer-default-export.md @@ -0,0 +1,51 @@ +# prefer-default-export + +When there is only a single export from a module prefer using default export over named export. + +## Rule Details + +The following patterns are considered warnings: + +```javascript +// bad.js + +// There is only a single module export and its a named export. +export const foo = 'foo'; + +``` + +The following patterns are not warnings: + +```javascript +// good1.js + +// There is a default export. +export const foo = 'foo'; +const bar = 'bar'; +export default 'bar'; +``` + +```javascript +// good2.js + +// There is more thank one named exports in the module. +export const foo = 'foo'; +export const bar = 'bar'; +``` + +```javascript +// good3.js + +// There is more thank one named exports in the module +const foo = 'foo'; +const bar = 'bar'; +export { foo, bar } +``` + +```javascript +// good4.js + +// There is a default export. +const foo = 'foo'; +export { foo as default } +``` diff --git a/src/index.js b/src/index.js index 0d1eb15cf..f9da72a22 100644 --- a/src/index.js +++ b/src/index.js @@ -19,6 +19,7 @@ export const rules = { 'no-nodejs-modules': require('./rules/no-nodejs-modules'), 'order': require('./rules/order'), 'newline-after-import': require('./rules/newline-after-import'), + 'prefer-default-export': require('./rules/prefer-default-export'), // metadata-based 'no-deprecated': require('./rules/no-deprecated'), diff --git a/src/rules/prefer-default-export.js b/src/rules/prefer-default-export.js new file mode 100644 index 000000000..871cd8187 --- /dev/null +++ b/src/rules/prefer-default-export.js @@ -0,0 +1,31 @@ +'use strict' + +module.exports = function(context) { + let namedExportCount = 0 + let specifierExportCount = 0 + let hasDefaultExport = false + let namedExportNode = null + return { + 'ExportSpecifier': function(node) { + if (node.exported.name === 'default') { + hasDefaultExport = true + } else { + specifierExportCount++ + namedExportNode = node + } + }, + 'ExportNamedDeclaration': function(node) { + namedExportCount++ + namedExportNode = node + }, + 'ExportDefaultDeclaration': function() { + hasDefaultExport = true + }, + + 'Program:exit': function() { + if (namedExportCount === 1 && specifierExportCount < 2 && !hasDefaultExport) { + context.report(namedExportNode, 'Prefer default export.') + } + }, + } +} diff --git a/tests/src/rules/prefer-default-export.js b/tests/src/rules/prefer-default-export.js new file mode 100644 index 000000000..c3827eeba --- /dev/null +++ b/tests/src/rules/prefer-default-export.js @@ -0,0 +1,48 @@ +import { test } from '../utils' + +import { RuleTester } from 'eslint' + +const ruleTester = new RuleTester() + , rule = require('rules/prefer-default-export') + +ruleTester.run('prefer-default-export', rule, { + valid: [ + test({ + code: ` + export const foo = 'foo'; + export const bar = 'bar';`, + }), + test({ + code: ` + export const foo = 'foo'; + export default bar;`, + }), + test({ + code: ` + export { foo, bar }`, + }), + test({ + code: ` + export { foo as default }`, + }), + ], + invalid: [ + test({ + code: ` + export const foo = 'foo';`, + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Prefer default export.', + }], + }), + test({ + code: ` + const foo = 'foo'; + export { foo };`, + errors: [{ + ruleId: 'ExportNamedDeclaration', + message: 'Prefer default export.', + }], + }), + ], +})