-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
exports-last #632
exports-last #632
Changes from 7 commits
84fd4f2
76f1b84
0de28bd
0611e21
531d04d
baa585d
8c0e7d6
8d4e25e
1ba1c3a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# exports-last | ||
|
||
This rule enforces that all exports are declared at the bottom of the file. This rule will report any export declarations that comes before any non-export statements. | ||
|
||
|
||
## This will be reported | ||
|
||
```JS | ||
|
||
const bool = true | ||
|
||
export default bool | ||
|
||
const str = 'foo' | ||
|
||
``` | ||
|
||
```JS | ||
|
||
export const bool = true | ||
|
||
const str = 'foo' | ||
|
||
``` | ||
|
||
## This will not be reported | ||
|
||
```JS | ||
const arr = ['bar'] | ||
|
||
export const bool = true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a simple non-exported variable declaration up top, to show that that is allowed |
||
|
||
export default bool | ||
|
||
export function func() { | ||
console.log('Hello World 🌍') | ||
} | ||
|
||
export const str = 'foo' | ||
``` | ||
|
||
## When Not To Use It | ||
|
||
If you don't mind exports being sprinkled throughout a file, you may not want to enable this rule. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to indicate that this is only for the export statements using the |
||
|
||
#### ES6 exports only | ||
|
||
The exports-last rule is currently only working on ES6 exports. You may not want to enable this rule if you're using CommonJS exports. | ||
|
||
If you need CommonJS support feel free to open an issue or create a PR. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
function isNonExportStatement({ type }) { | ||
return type !== 'ExportDefaultDeclaration' && | ||
type !== 'ExportNamedDeclaration' && | ||
type !== 'ExportAllDeclaration' | ||
} | ||
|
||
module.exports = { | ||
create: function (context) { | ||
return { | ||
Program: function ({ body }) { | ||
const lastNonExportStatementIndex = body.reduce(function findLastIndex(acc, item, index) { | ||
if (isNonExportStatement(item)) { | ||
return index | ||
} | ||
return acc | ||
}, -1) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove this blank line (sorry about the nitpicking 😅) |
||
if (lastNonExportStatementIndex !== -1) { | ||
body.slice(0, lastNonExportStatementIndex).forEach(function checkNonExport(node) { | ||
if (!isNonExportStatement(node)) { | ||
context.report({ | ||
node, | ||
message: 'Export statements should appear at the end of the file', | ||
}) | ||
} | ||
}) | ||
} | ||
}, | ||
} | ||
}, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import { test } from '../utils' | ||
|
||
import { RuleTester } from 'eslint' | ||
import rule from 'rules/exports-last' | ||
|
||
const ruleTester = new RuleTester() | ||
|
||
const error = type => ({ | ||
ruleId: 'exports-last', | ||
message: 'Export statements should appear at the end of the file', | ||
type | ||
}); | ||
|
||
ruleTester.run('exports-last', rule, { | ||
valid: [ | ||
// Empty file | ||
test({ | ||
code: '// comment', | ||
}), | ||
test({ | ||
// No exports | ||
code: ` | ||
const foo = 'bar' | ||
const bar = 'baz' | ||
`, | ||
}), | ||
test({ | ||
code: ` | ||
const foo = 'bar' | ||
export {foo} | ||
`, | ||
}), | ||
test({ | ||
code: ` | ||
const foo = 'bar' | ||
export default foo | ||
`, | ||
}), | ||
// Only exports | ||
test({ | ||
code: ` | ||
export default foo | ||
export const bar = true | ||
`, | ||
}), | ||
test({ | ||
code: ` | ||
const foo = 'bar' | ||
export default foo | ||
export const bar = true | ||
`, | ||
}), | ||
// Multiline export | ||
test({ | ||
code: ` | ||
const foo = 'bar' | ||
export default function foo () { | ||
const very = 'multiline' | ||
} | ||
export const bar = true | ||
`, | ||
}), | ||
// Many exports | ||
test({ | ||
code: ` | ||
const foo = 'bar' | ||
export default foo | ||
export const so = 'many' | ||
export const exports = ':)' | ||
export const i = 'cant' | ||
export const even = 'count' | ||
export const how = 'many' | ||
`, | ||
}), | ||
// Export all | ||
test({ | ||
code: ` | ||
export * from './foo' | ||
`, | ||
}), | ||
], | ||
invalid: [ | ||
// Default export before variable declaration | ||
test({ | ||
code: ` | ||
export default 'bar' | ||
const bar = true | ||
`, | ||
errors: [error('ExportDefaultDeclaration')], | ||
}), | ||
// Named export before variable declaration | ||
test({ | ||
code: ` | ||
export const foo = 'bar' | ||
const bar = true | ||
`, | ||
errors: [error('ExportNamedDeclaration')], | ||
}), | ||
// Export all before variable declaration | ||
test({ | ||
code: ` | ||
export * from './foo' | ||
const bar = true | ||
`, | ||
errors: [error('ExportAllDeclaration')], | ||
}), | ||
// Many exports arround variable declaration | ||
test({ | ||
code: ` | ||
export default 'such foo many bar' | ||
export const so = 'many' | ||
const foo = 'bar' | ||
export const exports = ':)' | ||
export const i = 'cant' | ||
export const even = 'count' | ||
export const how = 'many' | ||
`, | ||
errors: [ | ||
error('ExportDefaultDeclaration'), | ||
error('ExportNamedDeclaration'), | ||
], | ||
}), | ||
], | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we lost one of the changelog entries - let's fix that :-)