diff --git a/CHANGELOG.md b/CHANGELOG.md index d047282d5f..dc228dd59b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ### Added - [`no-anonymous-default-export`] rule: report anonymous default exports ([#712], thanks [@duncanbeevers]). - Add new value to `order`'s `newlines-between` option to allow newlines inside import groups ([#627], [#628], thanks [@giodamelio]) +- [`exports-last`] lints that export statements are at the end of the file ([#620] + [#632]) ### Changed - [`no-extraneous-dependencies`]: use `read-pkg-up` to simplify finding + loading `package.json` ([#680], thanks [@wtgtybhertgeghgtwtg]) @@ -35,9 +36,6 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - Properly report [`newline-after-import`] when next line is a decorator - Fixed documentation for the default values for the [`order`] rule ([#601]) -### Added -- [`exports-last`] lints that export statements are at the end of the file ([#620] + [#632]) - ## [2.0.1] - 2016-10-06 ### Fixed - Fixed code that relied on removed dependencies. ([#604]) diff --git a/docs/rules/exports-last.md b/docs/rules/exports-last.md index 9c7b0dd2d4..22b654d2ea 100644 --- a/docs/rules/exports-last.md +++ b/docs/rules/exports-last.md @@ -1,6 +1,6 @@ # exports-last -This rule reports all export declaration which come before any non-export statements. +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 @@ -26,6 +26,8 @@ const str = 'foo' ## This will not be reported ```JS +const arr = ['bar'] + export const bool = true export default bool diff --git a/src/rules/exports-last.js b/src/rules/exports-last.js index 1cc0334699..91af6b421d 100644 --- a/src/rules/exports-last.js +++ b/src/rules/exports-last.js @@ -1,17 +1,23 @@ -const isExportStatement = ({ type }) => - type === 'ExportDefaultDeclaration' - || type === 'ExportNamedDeclaration' - || type === 'ExportAllDeclaration' +function isNonExportStatement({ type }) { + return type !== 'ExportDefaultDeclaration' && + type !== 'ExportNamedDeclaration' && + type !== 'ExportAllDeclaration' +} -const rule = { - create(context) { +module.exports = { + create: function (context) { return { - Program({ body }) { - const firstExportStatementIndex = body.findIndex(isExportStatement) + Program: function ({ body }) { + const lastNonExportStatementIndex = body.reduce(function findLastIndex(acc, item, index) { + if (isNonExportStatement(item)) { + return index + } + return acc + }, -1) - if (firstExportStatementIndex !== -1) { - body.slice(firstExportStatementIndex).forEach((node) => { - if (!isExportStatement(node)) { + 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', @@ -23,5 +29,3 @@ const rule = { } }, } - -export default rule diff --git a/tests/src/rules/exports-last.js b/tests/src/rules/exports-last.js index 4d8fab5ce4..c3c26fdfc7 100644 --- a/tests/src/rules/exports-last.js +++ b/tests/src/rules/exports-last.js @@ -5,13 +5,17 @@ import rule from 'rules/exports-last' const ruleTester = new RuleTester() -const errors = ['Export statements should appear at the end of the file'] +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: '', + code: '// comment', }), test({ // No exports @@ -82,7 +86,7 @@ ruleTester.run('exports-last', rule, { export default 'bar' const bar = true `, - errors, + errors: [error('ExportDefaultDeclaration')], }), // Named export before variable declaration test({ @@ -90,7 +94,7 @@ ruleTester.run('exports-last', rule, { export const foo = 'bar' const bar = true `, - errors, + errors: [error('ExportNamedDeclaration')], }), // Export all before variable declaration test({ @@ -98,7 +102,7 @@ ruleTester.run('exports-last', rule, { export * from './foo' const bar = true `, - errors, + errors: [error('ExportAllDeclaration')], }), // Many exports arround variable declaration test({ @@ -111,7 +115,10 @@ ruleTester.run('exports-last', rule, { export const even = 'count' export const how = 'many' `, - errors, + errors: [ + error('ExportDefaultDeclaration'), + error('ExportNamedDeclaration'), + ], }), ], })