| "use strict"; |
| |
| const _ = require("lodash"); |
| const execall = require("execall"); |
| const optionsMatches = require("../../utils/optionsMatches"); |
| const report = require("../../utils/report"); |
| const ruleMessages = require("../../utils/ruleMessages"); |
| const styleSearch = require("style-search"); |
| const validateOptions = require("../../utils/validateOptions"); |
| |
| const ruleName = "max-line-length"; |
| |
| const messages = ruleMessages(ruleName, { |
| expected: max => |
| `Expected line length to be no more than ${max} ${ |
| max === 1 ? "character" : "characters" |
| }` |
| }); |
| |
| const rule = function(maxLength, options, context) { |
| return (root, result) => { |
| const validOptions = validateOptions( |
| result, |
| ruleName, |
| { |
| actual: maxLength, |
| possible: _.isNumber |
| }, |
| { |
| actual: options, |
| possible: { |
| ignore: ["non-comments", "comments"], |
| ignorePattern: [_.isString, _.isRegExp] |
| }, |
| optional: true |
| } |
| ); |
| |
| if (!validOptions) { |
| return; |
| } |
| |
| const ignoreNonComments = optionsMatches(options, "ignore", "non-comments"); |
| const ignoreComments = optionsMatches(options, "ignore", "comments"); |
| const rootString = context.fix ? root.toString() : root.source.input.css; |
| |
| // Check first line |
| checkNewline(rootString, { endIndex: 0 }, root); |
| // Check subsequent lines |
| styleSearch( |
| { source: rootString, target: ["\n"], comments: "check" }, |
| match => checkNewline(rootString, match, root) |
| ); |
| |
| function complain(index, root) { |
| report({ |
| index, |
| result, |
| ruleName, |
| message: messages.expected(maxLength), |
| node: root |
| }); |
| } |
| |
| function checkNewline(rootString, match, root) { |
| let nextNewlineIndex = rootString.indexOf("\n", match.endIndex); |
| |
| if (rootString[nextNewlineIndex - 1] === "\r") { |
| nextNewlineIndex -= 1; |
| } |
| |
| // Accommodate last line |
| if (nextNewlineIndex === -1) { |
| nextNewlineIndex = rootString.length; |
| } |
| |
| const rawLineLength = nextNewlineIndex - match.endIndex; |
| const lineText = rootString.slice(match.endIndex, nextNewlineIndex); |
| |
| // Case sensitive ignorePattern match |
| if (optionsMatches(options, "ignorePattern", lineText)) { |
| return; |
| } |
| |
| const urlArgumentsLength = execall(/url\((.*)\)/gi, lineText).reduce( |
| (result, match) => { |
| return result + _.get(match, "subMatches[0].length", 0); |
| }, |
| 0 |
| ); |
| |
| const importUrlsLength = execall( |
| /@import\s+(['"].*['"])/gi, |
| lineText |
| ).reduce((result, match) => { |
| return result + _.get(match, "subMatches[0].length", 0); |
| }, 0); |
| |
| // If the line's length is less than or equal to the specified |
| // max, ignore it ... So anything below is liable to be complained about. |
| // **Note that the length of any url arguments or import urls |
| // are excluded from the calculation.** |
| if (rawLineLength - urlArgumentsLength - importUrlsLength <= maxLength) { |
| return; |
| } |
| |
| const complaintIndex = nextNewlineIndex - 1; |
| |
| if (ignoreComments) { |
| if (match.insideComment) { |
| return; |
| } |
| |
| // This trimming business is to notice when the line starts a |
| // comment but that comment is indented, e.g. |
| // /* something here */ |
| const nextTwoChars = rootString |
| .slice(match.endIndex) |
| .trim() |
| .slice(0, 2); |
| |
| if (nextTwoChars === "/*" || nextTwoChars === "//") { |
| return; |
| } |
| } |
| |
| if (ignoreNonComments) { |
| if (match.insideComment) { |
| return complain(complaintIndex, root); |
| } |
| |
| // This trimming business is to notice when the line starts a |
| // comment but that comment is indented, e.g. |
| // /* something here */ |
| const nextTwoChars = rootString |
| .slice(match.endIndex) |
| .trim() |
| .slice(0, 2); |
| |
| if (nextTwoChars !== "/*" && nextTwoChars !== "//") { |
| return; |
| } |
| |
| return complain(complaintIndex, root); |
| } |
| |
| // If there are no spaces besides initial (indent) spaces, ignore it |
| const lineString = rootString.slice(match.endIndex, nextNewlineIndex); |
| |
| if (lineString.replace(/^\s+/, "").indexOf(" ") === -1) { |
| return; |
| } |
| |
| return complain(complaintIndex, root); |
| } |
| }; |
| }; |
| |
| rule.ruleName = ruleName; |
| rule.messages = messages; |
| module.exports = rule; |