eol-last.js 3.8 KB

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