3 const _ = require("lodash");
4 const execall = require("execall");
5 const optionsMatches = require("../../utils/optionsMatches");
6 const report = require("../../utils/report");
7 const ruleMessages = require("../../utils/ruleMessages");
8 const styleSearch = require("style-search");
9 const validateOptions = require("../../utils/validateOptions");
11 const ruleName = "max-line-length";
13 const messages = ruleMessages(ruleName, {
15 `Expected line length to be no more than ${max} ${
16 max === 1 ? "character" : "characters"
20 const rule = function(maxLength, options) {
21 return (root, result) => {
22 const validOptions = validateOptions(
32 ignore: ["non-comments", "comments"],
33 ignorePattern: [_.isString]
42 const rootString = root.source.input.css;
44 const ignoreNonComments = optionsMatches(options, "ignore", "non-comments");
45 const ignoreComments = optionsMatches(options, "ignore", "comments");
48 checkNewline({ endIndex: 0 });
50 // Check subsequent lines
52 { source: rootString, target: ["\n"], comments: "check" },
56 function complain(index) {
61 message: messages.expected(maxLength),
66 function checkNewline(match) {
67 let nextNewlineIndex = rootString.indexOf("\n", match.endIndex);
68 if (rootString[nextNewlineIndex - 1] === "\r") {
69 nextNewlineIndex -= 1;
72 // Accommodate last line
73 if (nextNewlineIndex === -1) {
74 nextNewlineIndex = rootString.length;
77 const rawLineLength = nextNewlineIndex - match.endIndex;
78 const lineText = rootString.slice(match.endIndex, nextNewlineIndex);
80 // Case sensitive ignorePattern match
81 if (optionsMatches(options, "ignorePattern", lineText)) {
85 const urlArgumentsLength = execall(/url\((.*)\)/gi, lineText).reduce(
87 return result + _.get(match, "sub[0].length", 0);
92 const importUrlsLength = execall(
93 /@import\s+(['"].*['"])/gi,
95 ).reduce((result, match) => {
96 return result + _.get(match, "sub[0].length", 0);
99 // If the line's length is less than or equal to the specified
100 // max, ignore it ... So anything below is liable to be complained about.
101 // **Note that the length of any url arguments or import urls
102 // are excluded from the calculation.**
103 if (rawLineLength - urlArgumentsLength - importUrlsLength <= maxLength) {
107 const complaintIndex = nextNewlineIndex - 1;
109 if (ignoreComments) {
110 if (match.insideComment) {
114 // This trimming business is to notice when the line starts a
115 // comment but that comment is indented, e.g.
116 // /* something here */
117 const nextTwoChars = rootString
118 .slice(match.endIndex)
121 if (nextTwoChars === "/*" || nextTwoChars === "//") {
126 if (ignoreNonComments) {
127 if (match.insideComment) {
128 return complain(complaintIndex);
131 // This trimming business is to notice when the line starts a
132 // comment but that comment is indented, e.g.
133 // /* something here */
134 const nextTwoChars = rootString
135 .slice(match.endIndex)
138 if (nextTwoChars !== "/*" && nextTwoChars !== "//") {
141 return complain(complaintIndex);
144 // If there are no spaces besides initial (indent) spaces, ignore it
145 const lineString = rootString.slice(match.endIndex, nextNewlineIndex);
146 if (lineString.replace(/^\s+/, "").indexOf(" ") === -1) {
150 return complain(complaintIndex);
155 rule.ruleName = ruleName;
156 rule.messages = messages;
157 module.exports = rule;