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