]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
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 | ||
11 | const lodash = require("lodash"); | |
12 | ||
13 | //------------------------------------------------------------------------------ | |
14 | // Rule Definition | |
15 | //------------------------------------------------------------------------------ | |
16 | ||
17 | module.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 | }; |