• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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