Skip to content

Commit

Permalink
New: prefer-single-export rule
Browse files Browse the repository at this point in the history
  • Loading branch information
robertrossmann committed Jan 16, 2017
1 parent c975742 commit c3fa502
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 0 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
- Add [`prefer-single-export`] rule: style-guide rule to report use of multiple named exports ([#721], thanks [@Alaneor])

### Changed
- [`no-extraneous-dependencies`]: use `read-pkg-up` to simplify finding + loading `package.json` ([#680], thanks [@wtgtybhertgeghgtwtg])

Expand Down Expand Up @@ -376,7 +379,9 @@ for info on changes for earlier releases.
[`no-webpack-loader-syntax`]: ./docs/rules/no-webpack-loader-syntax.md
[`no-unassigned-import`]: ./docs/rules/no-unassigned-import.md
[`unambiguous`]: ./docs/rules/unambiguous.md
[`prefer-single-export`]: ./docs/rules/prefer-single-export.md

[#721]: https://github.com/benmosher/eslint-plugin-import/pull/721
[#680]: https://github.com/benmosher/eslint-plugin-import/pull/680
[#654]: https://github.com/benmosher/eslint-plugin-import/pull/654
[#639]: https://github.com/benmosher/eslint-plugin-import/pull/639
Expand Down Expand Up @@ -561,3 +566,4 @@ for info on changes for earlier releases.
[@ntdb]: https://github.com/ntdb
[@jakubsta]: https://github.com/jakubsta
[@wtgtybhertgeghgtwtg]: https://github.com/wtgtybhertgeghgtwtg
[@Alaneor]: https://github.com/Alaneor
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`])
* Prefer single named export declaration ([`prefer-single-export`])

[`first`]: ./docs/rules/first.md
[`no-duplicates`]: ./docs/rules/no-duplicates.md
Expand All @@ -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
[`prefer-single-export`]: ./docs/rules/prefer-single-export.md

## Installation

Expand Down
54 changes: 54 additions & 0 deletions docs/rules/prefer-single-export.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# prefer-single-export

Reports when multiple named exports or CommonJS assignments are present in a single file.

**Rationale:** An `export` declaration or `module.exports` assignment can appear anywhere in the code. By requiring a single export declaration, your exports will remain at one place, making it easier to see what named exports a module provides.

## Rule Details

This rule warns whenever a single file contains multiple named exports or assignments to `module.exports` (or `exports`).

### Valid

```js
export default function test() {}
// A single named export -> ok
export const valid = true
```

```js
const first = true
const second = true

// A single named export -> ok
export {
first,
second,
}
```

```js
// A single exports assignment -> ok
module.exports = {
first: true,
second: true
}
```

### Invalid

```js
// Multiple named exports -> not ok!
export const first = true
export const second = true
```

```js
// Multiple exports assignments -> not ok!
exports.first = true
exports.second = true
```

## When Not To Use It

If you do not mind having multiple named exports in a single module, you can safely turn this rule off.
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const rules = {
'extensions': require('./rules/extensions'),
'no-restricted-paths': require('./rules/no-restricted-paths'),
'no-internal-modules': require('./rules/no-internal-modules'),
'prefer-single-export': require('./rules/prefer-single-export'),

'no-named-default': require('./rules/no-named-default'),
'no-named-as-default': require('./rules/no-named-as-default'),
Expand Down
37 changes: 37 additions & 0 deletions src/rules/prefer-single-export.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const MULTI_EXPORT = 'Multiple named export declarations'
const MULTI_MODULE_EXPORTS = 'Multiple CommonJS exports'

const meta = {}

function create(context) {
const issues = new Map()

return {
ExportNamedDeclaration(node) {
issues.set(node, MULTI_EXPORT)
},

MemberExpression(node) {
if (node.object.name === 'module' && node.property.name === 'exports') {
issues.set(node, MULTI_MODULE_EXPORTS)
}

if (node.object.name === 'exports') {
issues.set(node, MULTI_MODULE_EXPORTS)
}
},

'Program:exit': function onExit() {
if (issues.size > 1) {
for (const [node, message] of issues) {
context.report({ node, message })
}
}
},
}
}

export default {
meta,
create,
}
54 changes: 54 additions & 0 deletions tests/src/rules/prefer-single-export.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { test } from '../utils'
import { RuleTester } from 'eslint'
import rule from 'rules/prefer-single-export'

const ruleTester = new RuleTester()

ruleTester.run('prefer-single-export', rule, {
valid: [
test({ code: 'export const test = true' }),
test({ code: 'export default {}\nexport const test = true' }),
test({ code: [
'const first = true',
'const second = true',
'export { first,\nsecond }',
].join('\n') }),
test({ code: 'module.exports = {} '}),
test({ code: 'module.exports = { test: true,\nanother: false }' }),
test({ code: 'exports.test = true' }),
],
invalid: [
test({
code: [
'export const test = true',
'export const another = true',
].join('\n'),
errors: [
'Multiple named export declarations',
'Multiple named export declarations',
],
}),
test({
code: [
'module.exports = {}',
'module.exports.test = true',
'module.exports.another = true',
].join('\n'),
errors: [
'Multiple CommonJS exports',
'Multiple CommonJS exports',
'Multiple CommonJS exports',
],
}),
test({
code: [
'module.exports = {}',
'module.exports = {}',
].join('\n'),
errors: [
'Multiple CommonJS exports',
'Multiple CommonJS exports',
],
}),
],
})

0 comments on commit c3fa502

Please sign in to comment.