-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'no-mutable-exports': merges + closes #290
- Loading branch information
Showing
6 changed files
with
165 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# no-mutable-exports | ||
|
||
Forbids the use of mutable exports with `var` or `let`. | ||
|
||
## Rule Details | ||
|
||
Valid: | ||
|
||
```js | ||
export const count = 1 | ||
export function getCount() {} | ||
export class Counter {} | ||
``` | ||
|
||
...whereas here exports will be reported: | ||
|
||
```js | ||
export let count = 2 | ||
export var count = 3 | ||
|
||
let count = 4 | ||
export { count } // reported here | ||
``` | ||
|
||
## Functions/Classes | ||
|
||
Note that exported function/class declaration identifiers may be reassigned, | ||
but are not flagged by this rule at this time. They may be in the future, if a | ||
reassignment is detected, i.e. | ||
|
||
```js | ||
// possible future behavior! | ||
export class Counter {} // reported here: exported class is reassigned on line [x]. | ||
Counter = KitchenSink // not reported here unless you enable no-class-assign | ||
|
||
// this pre-declaration reassignment is valid on account of function hoisting | ||
getCount = function getDuke() {} // not reported here without no-func-assign | ||
export function getCount() {} // reported here: exported function is reassigned on line [x]. | ||
``` | ||
|
||
To prevent general reassignment of these identifiers, exported or not, you may | ||
want to enable the following core ESLint rules: | ||
|
||
- [no-func-assign] | ||
- [no-class-assign] | ||
|
||
[no-func-assign]: http://eslint.org/docs/rules/no-func-assign | ||
[no-class-assign]: http://eslint.org/docs/rules/no-class-assign | ||
|
||
## When Not To Use It | ||
|
||
If your environment correctly implements mutable export bindings. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
module.exports = function (context) { | ||
function checkDeclaration(node) { | ||
const {kind} = node | ||
if (kind === 'var' || kind === 'let') { | ||
context.report(node, `Exporting mutable '${kind}' binding, use 'const' instead.`) | ||
} | ||
} | ||
|
||
function checkDeclarationsInScope({variables}, name) { | ||
for (let variable of variables) { | ||
if (variable.name === name) { | ||
for (let def of variable.defs) { | ||
if (def.type === 'Variable') { | ||
checkDeclaration(def.parent) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
function handleExportDefault(node) { | ||
const scope = context.getScope() | ||
|
||
if (node.declaration.name) { | ||
checkDeclarationsInScope(scope, node.declaration.name) | ||
} | ||
} | ||
|
||
function handleExportNamed(node) { | ||
const scope = context.getScope() | ||
|
||
if (node.declaration) { | ||
checkDeclaration(node.declaration) | ||
} else { | ||
for (let specifier of node.specifiers) { | ||
checkDeclarationsInScope(scope, specifier.local.name) | ||
} | ||
} | ||
} | ||
|
||
return { | ||
'ExportDefaultDeclaration': handleExportDefault, | ||
'ExportNamedDeclaration': handleExportNamed, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import {test} from '../utils' | ||
import {RuleTester} from 'eslint' | ||
import rule from 'rules/no-mutable-exports' | ||
|
||
const ruleTester = new RuleTester() | ||
|
||
ruleTester.run('no-mutable-exports', rule, { | ||
valid: [ | ||
test({ code: 'export const count = 1'}), | ||
test({ code: 'export function getCount() {}'}), | ||
test({ code: 'export class Counter {}'}), | ||
test({ code: 'export default count = 1'}), | ||
test({ code: 'export default function getCount() {}'}), | ||
test({ code: 'export default class Counter {}'}), | ||
test({ code: 'const count = 1\nexport { count }'}), | ||
test({ code: 'const count = 1\nexport { count as counter }'}), | ||
test({ code: 'const count = 1\nexport default count'}), | ||
test({ code: 'const count = 1\nexport { count as default }'}), | ||
test({ code: 'function getCount() {}\nexport { getCount }'}), | ||
test({ code: 'function getCount() {}\nexport { getCount as getCounter }'}), | ||
test({ code: 'function getCount() {}\nexport default getCount'}), | ||
test({ code: 'function getCount() {}\nexport { getCount as default }'}), | ||
test({ code: 'class Counter {}\nexport { Counter }'}), | ||
test({ code: 'class Counter {}\nexport { Counter as Count }'}), | ||
test({ code: 'class Counter {}\nexport default Counter'}), | ||
test({ code: 'class Counter {}\nexport { Counter as default }'}), | ||
], | ||
invalid: [ | ||
test({ | ||
code: 'export let count = 1', | ||
errors: ['Exporting mutable \'let\' binding, use \'const\' instead.'], | ||
}), | ||
test({ | ||
code: 'export var count = 1', | ||
errors: ['Exporting mutable \'var\' binding, use \'const\' instead.'], | ||
}), | ||
test({ | ||
code: 'let count = 1\nexport { count }', | ||
errors: ['Exporting mutable \'let\' binding, use \'const\' instead.'], | ||
}), | ||
test({ | ||
code: 'var count = 1\nexport { count }', | ||
errors: ['Exporting mutable \'var\' binding, use \'const\' instead.'], | ||
}), | ||
test({ | ||
code: 'let count = 1\nexport { count as counter }', | ||
errors: ['Exporting mutable \'let\' binding, use \'const\' instead.'], | ||
}), | ||
test({ | ||
code: 'var count = 1\nexport { count as counter }', | ||
errors: ['Exporting mutable \'var\' binding, use \'const\' instead.'], | ||
}), | ||
test({ | ||
code: 'let count = 1\nexport default count', | ||
errors: ['Exporting mutable \'let\' binding, use \'const\' instead.'], | ||
}), | ||
test({ | ||
code: 'var count = 1\nexport default count', | ||
errors: ['Exporting mutable \'var\' binding, use \'const\' instead.'], | ||
}), | ||
], | ||
}) |