eol-last.js 3.6 KB

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