1/** 2 * @fileoverview Require or disallow newline at the end of files 3 * @author Nodeca Team <https://github.com/nodeca> 4 */ 5"use strict"; 6 7//------------------------------------------------------------------------------ 8// Requirements 9//------------------------------------------------------------------------------ 10 11const lodash = require("lodash"); 12 13//------------------------------------------------------------------------------ 14// Rule Definition 15//------------------------------------------------------------------------------ 16 17module.exports = { 18 meta: { 19 type: "layout", 20 21 docs: { 22 description: "require or disallow newline at the end of files", 23 category: "Stylistic Issues", 24 recommended: false, 25 url: "https://eslint.org/docs/rules/eol-last" 26 }, 27 28 fixable: "whitespace", 29 30 schema: [ 31 { 32 enum: ["always", "never", "unix", "windows"] 33 } 34 ], 35 36 messages: { 37 missing: "Newline required at end of file but not found.", 38 unexpected: "Newline not allowed at end of file." 39 } 40 }, 41 create(context) { 42 43 //-------------------------------------------------------------------------- 44 // Public 45 //-------------------------------------------------------------------------- 46 47 return { 48 Program: function checkBadEOF(node) { 49 const sourceCode = context.getSourceCode(), 50 src = sourceCode.getText(), 51 location = { 52 column: lodash.last(sourceCode.lines).length, 53 line: sourceCode.lines.length 54 }, 55 LF = "\n", 56 CRLF = `\r${LF}`, 57 endsWithNewline = lodash.endsWith(src, LF); 58 59 /* 60 * Empty source is always valid: No content in file so we don't 61 * need to lint for a newline on the last line of content. 62 */ 63 if (!src.length) { 64 return; 65 } 66 67 let mode = context.options[0] || "always", 68 appendCRLF = false; 69 70 if (mode === "unix") { 71 72 // `"unix"` should behave exactly as `"always"` 73 mode = "always"; 74 } 75 if (mode === "windows") { 76 77 // `"windows"` should behave exactly as `"always"`, but append CRLF in the fixer for backwards compatibility 78 mode = "always"; 79 appendCRLF = true; 80 } 81 if (mode === "always" && !endsWithNewline) { 82 83 // File is not newline-terminated, but should be 84 context.report({ 85 node, 86 loc: location, 87 messageId: "missing", 88 fix(fixer) { 89 return fixer.insertTextAfterRange([0, src.length], appendCRLF ? CRLF : LF); 90 } 91 }); 92 } else if (mode === "never" && endsWithNewline) { 93 94 // File is newline-terminated, but shouldn't be 95 context.report({ 96 node, 97 loc: location, 98 messageId: "unexpected", 99 fix(fixer) { 100 const finalEOLs = /(?:\r?\n)+$/u, 101 match = finalEOLs.exec(sourceCode.text), 102 start = match.index, 103 end = sourceCode.text.length; 104 105 return fixer.replaceTextRange([start, end], ""); 106 } 107 }); 108 } 109 } 110 }; 111 } 112}; 113