| /** |
| * @fileoverview Require or disallow newline at the end of files |
| * @author Nodeca Team <https://github.com/nodeca> |
| */ |
| "use strict"; |
| |
| //------------------------------------------------------------------------------ |
| // Requirements |
| //------------------------------------------------------------------------------ |
| |
| const lodash = require("lodash"); |
| |
| //------------------------------------------------------------------------------ |
| // Rule Definition |
| //------------------------------------------------------------------------------ |
| |
| module.exports = { |
| meta: { |
| type: "layout", |
| |
| docs: { |
| description: "require or disallow newline at the end of files", |
| category: "Stylistic Issues", |
| recommended: false, |
| url: "https://eslint.org/docs/rules/eol-last" |
| }, |
| |
| fixable: "whitespace", |
| |
| schema: [ |
| { |
| enum: ["always", "never", "unix", "windows"] |
| } |
| ], |
| |
| messages: { |
| missing: "Newline required at end of file but not found.", |
| unexpected: "Newline not allowed at end of file." |
| } |
| }, |
| create(context) { |
| |
| //-------------------------------------------------------------------------- |
| // Public |
| //-------------------------------------------------------------------------- |
| |
| return { |
| Program: function checkBadEOF(node) { |
| const sourceCode = context.getSourceCode(), |
| src = sourceCode.getText(), |
| location = { |
| column: lodash.last(sourceCode.lines).length, |
| line: sourceCode.lines.length |
| }, |
| LF = "\n", |
| CRLF = `\r${LF}`, |
| endsWithNewline = lodash.endsWith(src, LF); |
| |
| /* |
| * Empty source is always valid: No content in file so we don't |
| * need to lint for a newline on the last line of content. |
| */ |
| if (!src.length) { |
| return; |
| } |
| |
| let mode = context.options[0] || "always", |
| appendCRLF = false; |
| |
| if (mode === "unix") { |
| |
| // `"unix"` should behave exactly as `"always"` |
| mode = "always"; |
| } |
| if (mode === "windows") { |
| |
| // `"windows"` should behave exactly as `"always"`, but append CRLF in the fixer for backwards compatibility |
| mode = "always"; |
| appendCRLF = true; |
| } |
| if (mode === "always" && !endsWithNewline) { |
| |
| // File is not newline-terminated, but should be |
| context.report({ |
| node, |
| loc: location, |
| messageId: "missing", |
| fix(fixer) { |
| return fixer.insertTextAfterRange([0, src.length], appendCRLF ? CRLF : LF); |
| } |
| }); |
| } else if (mode === "never" && endsWithNewline) { |
| |
| // File is newline-terminated, but shouldn't be |
| context.report({ |
| node, |
| loc: location, |
| messageId: "unexpected", |
| fix(fixer) { |
| const finalEOLs = /(?:\r?\n)+$/u, |
| match = finalEOLs.exec(sourceCode.text), |
| start = match.index, |
| end = sourceCode.text.length; |
| |
| return fixer.replaceTextRange([start, end], ""); |
| } |
| }); |
| } |
| } |
| }; |
| } |
| }; |