Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e15b13d

Browse files
committedDec 26, 2019
Add vue/max-len rule
1 parent ec94d6a commit e15b13d

File tree

6 files changed

+2067
-0
lines changed

6 files changed

+2067
-0
lines changed
 

‎docs/rules/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ For example:
153153
| [vue/key-spacing](./key-spacing.md) | enforce consistent spacing between keys and values in object literal properties | :wrench: |
154154
| [vue/keyword-spacing](./keyword-spacing.md) | enforce consistent spacing before and after keywords | :wrench: |
155155
| [vue/match-component-file-name](./match-component-file-name.md) | require component name property to match its file name | |
156+
| [vue/max-len](./max-len.md) | enforce a maximum line length | |
156157
| [vue/no-boolean-default](./no-boolean-default.md) | disallow boolean defaults | :wrench: |
157158
| [vue/no-deprecated-scope-attribute](./no-deprecated-scope-attribute.md) | disallow deprecated `scope` attribute (in Vue.js 2.5.0+) | :wrench: |
158159
| [vue/no-deprecated-slot-attribute](./no-deprecated-slot-attribute.md) | disallow deprecated `slot` attribute (in Vue.js 2.6.0+) | :wrench: |

‎docs/rules/max-len.md

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
---
2+
pageClass: rule-details
3+
sidebarDepth: 0
4+
title: vue/max-len
5+
description: enforce a maximum line length
6+
---
7+
# vue/max-len
8+
> enforce a maximum line length
9+
10+
## :book: Rule Details
11+
12+
This rule enforces a maximum line length to increase code readability and maintainability.
13+
This rule is the similar rule as core [max-len] rule but it applies to the source code in `.vue`.
14+
15+
<eslint-code-block :rules="{'vue/max-len': ['error']}">
16+
17+
```vue
18+
<template>
19+
<!-- ✓ GOOD -->
20+
<div>
21+
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
22+
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
23+
</div>
24+
25+
<!-- ✗ BAD -->
26+
<div>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </div>
27+
<div>
28+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
29+
</div>
30+
</template>
31+
32+
<script>
33+
/* ✓ GOOD */
34+
var foo = {
35+
"bar": "This is a bar.",
36+
"baz": { "qux": "This is a qux" },
37+
"easier": "to read"
38+
};
39+
40+
/* ✗ BAD */
41+
var foo = { "bar": "This is a bar.", "baz": { "qux": "This is a qux" }, "difficult": "to read" };
42+
</script>
43+
44+
<style>
45+
/* ignore */
46+
.ignore-stylesheet .blocks-other-than-script-and-template-are-ignored .this-line-has-a-length-of-100
47+
{}
48+
</style>
49+
```
50+
51+
</eslint-code-block>
52+
53+
## :wrench: Options
54+
55+
```js
56+
{
57+
"vue/max-len": ["error", {
58+
"code": 80,
59+
"template": 80,
60+
"tabWidth": 2,
61+
"comments": 80,
62+
"ignorePattern": "",
63+
"ignoreComments": false,
64+
"ignoreTrailingComments": false,
65+
"ignoreUrls": false,
66+
"ignoreStrings": false,
67+
"ignoreTemplateLiterals": false,
68+
"ignoreRegExpLiterals": false,
69+
"ignoreHTMLAttributeValues": false,
70+
"ignoreHTMLTextContents": false,
71+
}]
72+
}
73+
```
74+
75+
- `code` ... enforces a maximum line length. default `80`
76+
- `template` ... enforces a maximum line length for `<template>`. defaults to value of `code`
77+
- `tabWidth` ... specifies the character width for tab characters. default `2`
78+
- `comments` ... enforces a maximum line length for comments. defaults to value of `code`
79+
- `ignorePattern` ... ignores lines matching a regular expression. can only match a single line and need to be double escaped when written in YAML or JSON
80+
- `ignoreComments` ... if `true`, ignores all trailing comments and comments on their own line. default `false`
81+
- `ignoreTrailingComments` ... if `true`, ignores only trailing comments. default `false`
82+
- `ignoreUrls` ... if `true`, ignores lines that contain a URL. default `false`
83+
- `ignoreStrings` ... if `true`, ignores lines that contain a double-quoted or single-quoted string. default `false`
84+
- `ignoreTemplateLiterals` ... if `true`, ignores lines that contain a template literal. default `false`
85+
- `ignoreRegExpLiterals` ... if `true`, ignores lines that contain a RegExp literal. default `false`
86+
- `ignoreHTMLAttributeValues` ... if `true`, ignores lines that contain a HTML attribute value. default `false`
87+
- `ignoreHTMLTextContents` ... if `true`, ignores lines that contain a HTML text content. default `false`
88+
89+
### `"code": 40`
90+
91+
<eslint-code-block :rules="{'vue/max-len': ['error', {code: 40}]}">
92+
93+
```vue
94+
<template>
95+
<!-- ✓ GOOD -->
96+
<div>line length is 40 ........ </div>
97+
98+
<!-- ✗ BAD -->
99+
<div>line length is 50 .................. </div>
100+
</template>
101+
102+
<script>
103+
/* ✓ GOOD */
104+
var foo = ['line', 'length', 'is', '40']
105+
106+
/* ✗ BAD */
107+
var foo = ['line', 'length', 'is', '50', '......']
108+
</script>
109+
```
110+
111+
</eslint-code-block>
112+
113+
114+
### `"template": 120`
115+
116+
<eslint-code-block :rules="{'vue/max-len': ['error', {template: 120}]}">
117+
118+
```vue
119+
<template>
120+
<!-- ✓ GOOD -->
121+
<div>line length is 120 ....................................................................................... </div>
122+
123+
<!-- ✗ BAD -->
124+
<div>line length is 121 ........................................................................................ </div>
125+
</template>
126+
127+
<script>
128+
/* ✗ BAD */
129+
var foo = ['line', 'length', 'is', '81', '......', '......', '......', '......'];
130+
</script>
131+
```
132+
133+
</eslint-code-block>
134+
135+
### `"comments": 65`
136+
137+
<eslint-code-block :rules="{'vue/max-len': ['error', {comments: 65}]}">
138+
139+
```vue
140+
<template>
141+
<!-- ✓ GOOD -->
142+
<!--
143+
This is a comment that does not violates
144+
the maximum line length we have specified
145+
-->
146+
147+
<!-- ✗ BAD -->
148+
<!--
149+
This is a comment that violates the maximum line length we have specified
150+
-->
151+
</template>
152+
153+
<script>
154+
/* ✓ GOOD */
155+
/**
156+
* This is a comment that does not violates
157+
* the maximum line length we have specified
158+
*/
159+
160+
/* ✗ BAD */
161+
/**
162+
* This is a comment that violates the maximum line length we have specified
163+
*/
164+
</script>
165+
```
166+
167+
</eslint-code-block>
168+
169+
### `"ignoreComments": true`
170+
171+
<eslint-code-block :rules="{'vue/max-len': ['error', {ignoreComments: true}]}">
172+
173+
```vue
174+
<template>
175+
<!-- ✓ GOOD -->
176+
<!-- This is a really really really really really really really really really long comment -->
177+
</template>
178+
179+
<script>
180+
/* ✓ GOOD */
181+
/**
182+
* This is a really really really really really really really really really long comment
183+
*/
184+
</script>
185+
```
186+
187+
</eslint-code-block>
188+
189+
### `"ignoreTrailingComments": true`
190+
191+
<eslint-code-block :rules="{'vue/max-len': ['error', {ignoreTrailingComments: true}]}">
192+
193+
```vue
194+
<template>
195+
<!-- ✓ GOOD -->
196+
<div /><!-- This is a really really really really really really really really long comment -->
197+
198+
<!-- ✗ BAD -->
199+
<!-- This is a really really really really really really really really long comment -->
200+
</template>
201+
202+
<script>
203+
/* ✓ GOOD */
204+
var foo = 'bar'; // This is a really really really really really really really really long comment
205+
206+
/* ✗ BAD */
207+
// This is a really really really really really really really really long comment
208+
</script>
209+
```
210+
211+
</eslint-code-block>
212+
213+
### `"ignoreUrls": true`
214+
215+
<eslint-code-block :rules="{'vue/max-len': ['error', {ignoreUrls: true}]}">
216+
217+
```vue
218+
<template>
219+
<!-- ✓ GOOD -->
220+
<div style="background-image: url('https://www.example.com/really/really/really/really/really/really/really/long')" />
221+
</template>
222+
223+
<script>
224+
/* ✓ GOOD */
225+
var url = 'https://www.example.com/really/really/really/really/really/really/really/long';
226+
</script>
227+
```
228+
229+
</eslint-code-block>
230+
231+
### `"ignoreStrings": true`
232+
233+
<eslint-code-block :rules="{'vue/max-len': ['error', {ignoreStrings: true}]}">
234+
235+
```vue
236+
<template>
237+
<!-- ✓ GOOD -->
238+
<div :title="'this is a really really really really really really long string!'" />
239+
240+
<!-- ✗ BAD -->
241+
<div title="this is a really really really really really really long attribute value!" />
242+
<div>this is a really really really really really really really long text content!</div>
243+
</template>
244+
245+
<script>
246+
/* ✓ GOOD */
247+
var longString = 'this is a really really really really really really long string!';
248+
</script>
249+
```
250+
251+
</eslint-code-block>
252+
253+
### `"ignoreTemplateLiterals": true`
254+
255+
<eslint-code-block :rules="{'vue/max-len': ['error', {ignoreTemplateLiterals: true}]}">
256+
257+
```vue
258+
<template>
259+
<!-- ✓ GOOD -->
260+
<div :title="`this is a really really really really really long template literal!`" />
261+
</template>
262+
263+
<script>
264+
/* ✓ GOOD */
265+
var longTemplateLiteral = `this is a really really really really really long template literal!`;
266+
</script>
267+
```
268+
269+
</eslint-code-block>
270+
271+
### `"ignoreRegExpLiterals": true`
272+
273+
<eslint-code-block :rules="{'vue/max-len': ['error', {ignoreRegExpLiterals: true}]}">
274+
275+
```vue
276+
<template>
277+
<!-- ✓ GOOD -->
278+
<div :class="{
279+
foo: /this is a really really really really really long regular expression!/.test(bar)
280+
}" />
281+
</template>
282+
283+
<script>
284+
/* ✓ GOOD */
285+
var longRegExpLiteral = /this is a really really really really really long regular expression!/;
286+
</script>
287+
```
288+
289+
</eslint-code-block>
290+
291+
### `"ignoreHTMLAttributeValues": true`
292+
293+
<eslint-code-block :rules="{'vue/max-len': ['error', {ignoreHTMLAttributeValues: true}]}">
294+
295+
```vue
296+
<template>
297+
<!-- ✓ GOOD -->
298+
<div title="this is a really really really really really really long attribute value!" />
299+
300+
<!-- ✗ BAD -->
301+
<div :title="'this is a really really really really really really long string!'" />
302+
</template>
303+
```
304+
305+
</eslint-code-block>
306+
307+
### `"ignoreHTMLTextContents": true`
308+
309+
<eslint-code-block :rules="{'vue/max-len': ['error', {ignoreHTMLTextContents: true}]}">
310+
311+
```vue
312+
<template>
313+
<!-- ✓ GOOD -->
314+
<div>this is a really really really really really really really long text content!</div>
315+
</template>
316+
```
317+
318+
</eslint-code-block>
319+
320+
## :books: Further reading
321+
322+
- [max-len]
323+
324+
[max-len]: https://eslint.org/docs/rules/max-len
325+
326+
## :mag: Implementation
327+
328+
- [Rule source](https://github.com/vuejs/eslint-plugin-vue/blob/master/lib/rules/max-len.js)
329+
- [Test source](https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/max-len.js)

‎lib/configs/no-layout-rules.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module.exports = {
1919
'vue/key-spacing': 'off',
2020
'vue/keyword-spacing': 'off',
2121
'vue/max-attributes-per-line': 'off',
22+
'vue/max-len': 'off',
2223
'vue/multiline-html-element-content-newline': 'off',
2324
'vue/mustache-interpolation-spacing': 'off',
2425
'vue/no-multi-spaces': 'off',

‎lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ module.exports = {
3232
'keyword-spacing': require('./rules/keyword-spacing'),
3333
'match-component-file-name': require('./rules/match-component-file-name'),
3434
'max-attributes-per-line': require('./rules/max-attributes-per-line'),
35+
'max-len': require('./rules/max-len'),
3536
'multiline-html-element-content-newline': require('./rules/multiline-html-element-content-newline'),
3637
'mustache-interpolation-spacing': require('./rules/mustache-interpolation-spacing'),
3738
'name-property-casing': require('./rules/name-property-casing'),

‎lib/rules/max-len.js

Lines changed: 494 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,494 @@
1+
/**
2+
* @author Yosuke Ota
3+
* @fileoverview Rule to check for max length on a line of Vue file.
4+
*/
5+
'use strict'
6+
7+
// ------------------------------------------------------------------------------
8+
// Requirements
9+
// ------------------------------------------------------------------------------
10+
11+
const utils = require('../utils')
12+
13+
// ------------------------------------------------------------------------------
14+
// Constants
15+
// ------------------------------------------------------------------------------
16+
17+
const OPTIONS_SCHEMA = {
18+
type: 'object',
19+
properties: {
20+
code: {
21+
type: 'integer',
22+
minimum: 0
23+
},
24+
template: {
25+
type: 'integer',
26+
minimum: 0
27+
},
28+
comments: {
29+
type: 'integer',
30+
minimum: 0
31+
},
32+
tabWidth: {
33+
type: 'integer',
34+
minimum: 0
35+
},
36+
ignorePattern: {
37+
type: 'string'
38+
},
39+
ignoreComments: {
40+
type: 'boolean'
41+
},
42+
ignoreTrailingComments: {
43+
type: 'boolean'
44+
},
45+
ignoreUrls: {
46+
type: 'boolean'
47+
},
48+
ignoreStrings: {
49+
type: 'boolean'
50+
},
51+
ignoreTemplateLiterals: {
52+
type: 'boolean'
53+
},
54+
ignoreRegExpLiterals: {
55+
type: 'boolean'
56+
},
57+
ignoreHTMLAttributeValues: {
58+
type: 'boolean'
59+
},
60+
ignoreHTMLTextContents: {
61+
type: 'boolean'
62+
}
63+
},
64+
additionalProperties: false
65+
}
66+
67+
const OPTIONS_OR_INTEGER_SCHEMA = {
68+
anyOf: [
69+
OPTIONS_SCHEMA,
70+
{
71+
type: 'integer',
72+
minimum: 0
73+
}
74+
]
75+
}
76+
77+
// --------------------------------------------------------------------------
78+
// Helpers
79+
// --------------------------------------------------------------------------
80+
81+
/**
82+
* Computes the length of a line that may contain tabs. The width of each
83+
* tab will be the number of spaces to the next tab stop.
84+
* @param {string} line The line.
85+
* @param {int} tabWidth The width of each tab stop in spaces.
86+
* @returns {int} The computed line length.
87+
* @private
88+
*/
89+
function computeLineLength (line, tabWidth) {
90+
let extraCharacterCount = 0
91+
92+
line.replace(/\t/gu, (match, offset) => {
93+
const totalOffset = offset + extraCharacterCount
94+
const previousTabStopOffset = tabWidth ? totalOffset % tabWidth : 0
95+
const spaceCount = tabWidth - previousTabStopOffset
96+
97+
extraCharacterCount += spaceCount - 1 // -1 for the replaced tab
98+
})
99+
return Array.from(line).length + extraCharacterCount
100+
}
101+
102+
/**
103+
* Tells if a given comment is trailing: it starts on the current line and
104+
* extends to or past the end of the current line.
105+
* @param {string} line The source line we want to check for a trailing comment on
106+
* @param {number} lineNumber The one-indexed line number for line
107+
* @param {ASTNode} comment The comment to inspect
108+
* @returns {boolean} If the comment is trailing on the given line
109+
*/
110+
function isTrailingComment (line, lineNumber, comment) {
111+
return comment &&
112+
(comment.loc.start.line === lineNumber && lineNumber <= comment.loc.end.line) &&
113+
(comment.loc.end.line > lineNumber || comment.loc.end.column === line.length)
114+
}
115+
116+
/**
117+
* Tells if a comment encompasses the entire line.
118+
* @param {string} line The source line with a trailing comment
119+
* @param {number} lineNumber The one-indexed line number this is on
120+
* @param {ASTNode} comment The comment to remove
121+
* @returns {boolean} If the comment covers the entire line
122+
*/
123+
function isFullLineComment (line, lineNumber, comment) {
124+
const start = comment.loc.start
125+
const end = comment.loc.end
126+
const isFirstTokenOnLine = !line.slice(0, comment.loc.start.column).trim()
127+
128+
return comment &&
129+
(start.line < lineNumber || (start.line === lineNumber && isFirstTokenOnLine)) &&
130+
(end.line > lineNumber || (end.line === lineNumber && end.column === line.length))
131+
}
132+
133+
/**
134+
* Gets the line after the comment and any remaining trailing whitespace is
135+
* stripped.
136+
* @param {string} line The source line with a trailing comment
137+
* @param {ASTNode} comment The comment to remove
138+
* @returns {string} Line without comment and trailing whitepace
139+
*/
140+
function stripTrailingComment (line, comment) {
141+
// loc.column is zero-indexed
142+
return line.slice(0, comment.loc.start.column).replace(/\s+$/u, '')
143+
}
144+
145+
/**
146+
* Ensure that an array exists at [key] on `object`, and add `value` to it.
147+
*
148+
* @param {Object} object the object to mutate
149+
* @param {string} key the object's key
150+
* @param {*} value the value to add
151+
* @returns {void}
152+
* @private
153+
*/
154+
function ensureArrayAndPush (object, key, value) {
155+
if (!Array.isArray(object[key])) {
156+
object[key] = []
157+
}
158+
object[key].push(value)
159+
}
160+
161+
/**
162+
* A reducer to group an AST node by line number, both start and end.
163+
*
164+
* @param {Object} acc the accumulator
165+
* @param {ASTNode} node the AST node in question
166+
* @returns {Object} the modified accumulator
167+
* @private
168+
*/
169+
function groupByLineNumber (acc, node) {
170+
for (let i = node.loc.start.line; i <= node.loc.end.line; ++i) {
171+
ensureArrayAndPush(acc, i, node)
172+
}
173+
return acc
174+
}
175+
176+
// ------------------------------------------------------------------------------
177+
// Rule Definition
178+
// ------------------------------------------------------------------------------
179+
180+
module.exports = {
181+
meta: {
182+
type: 'layout',
183+
184+
docs: {
185+
description: 'enforce a maximum line length',
186+
category: undefined,
187+
url: 'https://eslint.vuejs.org/rules/max-len.html'
188+
},
189+
190+
schema: [
191+
OPTIONS_OR_INTEGER_SCHEMA,
192+
OPTIONS_OR_INTEGER_SCHEMA,
193+
OPTIONS_SCHEMA
194+
],
195+
messages: {
196+
max: 'This line has a length of {{lineLength}}. Maximum allowed is {{maxLength}}.',
197+
maxComment: 'This line has a comment length of {{lineLength}}. Maximum allowed is {{maxCommentLength}}.'
198+
}
199+
},
200+
201+
create (context) {
202+
/*
203+
* Inspired by http://tools.ietf.org/html/rfc3986#appendix-B, however:
204+
* - They're matching an entire string that we know is a URI
205+
* - We're matching part of a string where we think there *might* be a URL
206+
* - We're only concerned about URLs, as picking out any URI would cause
207+
* too many false positives
208+
* - We don't care about matching the entire URL, any small segment is fine
209+
*/
210+
const URL_REGEXP = /[^:/?#]:\/\/[^?#]/u
211+
212+
const sourceCode = context.getSourceCode()
213+
const tokens = []
214+
const comments = []
215+
const htmlAttributeValues = []
216+
217+
// The options object must be the last option specified…
218+
const options = Object.assign({}, context.options[context.options.length - 1])
219+
220+
// …but max code length…
221+
if (typeof context.options[0] === 'number') {
222+
options.code = context.options[0]
223+
}
224+
225+
// …and tabWidth can be optionally specified directly as integers.
226+
if (typeof context.options[1] === 'number') {
227+
options.tabWidth = context.options[1]
228+
}
229+
230+
const scriptMaxLength = typeof options.code === 'number' ? options.code : 80
231+
const tabWidth = typeof options.tabWidth === 'number' ? options.tabWidth : 2// default value of `vue/html-indent`
232+
const templateMaxLength = typeof options.template === 'number' ? options.template : scriptMaxLength
233+
const ignoreComments = !!options.ignoreComments
234+
const ignoreStrings = !!options.ignoreStrings
235+
const ignoreTemplateLiterals = !!options.ignoreTemplateLiterals
236+
const ignoreRegExpLiterals = !!options.ignoreRegExpLiterals
237+
const ignoreTrailingComments = !!options.ignoreTrailingComments || !!options.ignoreComments
238+
const ignoreUrls = !!options.ignoreUrls
239+
const ignoreHTMLAttributeValues = !!options.ignoreHTMLAttributeValues
240+
const ignoreHTMLTextContents = !!options.ignoreHTMLTextContents
241+
const maxCommentLength = options.comments
242+
let ignorePattern = options.ignorePattern || null
243+
244+
if (ignorePattern) {
245+
ignorePattern = new RegExp(ignorePattern, 'u')
246+
}
247+
248+
// --------------------------------------------------------------------------
249+
// Helpers
250+
// --------------------------------------------------------------------------
251+
252+
/**
253+
* Retrieves an array containing all strings (" or ') in the source code.
254+
*
255+
* @returns {ASTNode[]} An array of string nodes.
256+
*/
257+
function getAllStrings () {
258+
return tokens.filter(token => (token.type === 'String' ||
259+
(token.type === 'JSXText' && sourceCode.getNodeByRangeIndex(token.range[0] - 1).type === 'JSXAttribute')))
260+
}
261+
262+
/**
263+
* Retrieves an array containing all template literals in the source code.
264+
*
265+
* @returns {ASTNode[]} An array of template literal nodes.
266+
*/
267+
function getAllTemplateLiterals () {
268+
return tokens.filter(token => token.type === 'Template')
269+
}
270+
271+
/**
272+
* Retrieves an array containing all RegExp literals in the source code.
273+
*
274+
* @returns {ASTNode[]} An array of RegExp literal nodes.
275+
*/
276+
function getAllRegExpLiterals () {
277+
return tokens.filter(token => token.type === 'RegularExpression')
278+
}
279+
280+
/**
281+
* Retrieves an array containing all HTML texts in the source code.
282+
*
283+
* @returns {ASTNode[]} An array of HTML text nodes.
284+
*/
285+
function getAllHTMLTextContents () {
286+
return tokens.filter(token => token.type === 'HTMLText')
287+
}
288+
289+
/**
290+
* Check the program for max length
291+
* @param {ASTNode} node Node to examine
292+
* @returns {void}
293+
* @private
294+
*/
295+
function checkProgramForMaxLength (node) {
296+
const programNode = node
297+
const templateBody = node.templateBody
298+
299+
// setup tokens
300+
const scriptTokens = sourceCode.ast.tokens
301+
const scriptComments = sourceCode.getAllComments()
302+
303+
if (context.parserServices.getTemplateBodyTokenStore && templateBody) {
304+
const tokenStore = context.parserServices.getTemplateBodyTokenStore()
305+
306+
const templateTokens = tokenStore.getTokens(templateBody, { includeComments: true })
307+
308+
if (templateBody.range[0] < programNode.range[0]) {
309+
tokens.push(...templateTokens, ...scriptTokens)
310+
} else {
311+
tokens.push(...scriptTokens, ...templateTokens)
312+
}
313+
} else {
314+
tokens.push(...scriptTokens)
315+
}
316+
317+
if (ignoreComments || maxCommentLength || ignoreTrailingComments) {
318+
// list of comments to ignore
319+
if (templateBody) {
320+
if (templateBody.range[0] < programNode.range[0]) {
321+
comments.push(...templateBody.comments, ...scriptComments)
322+
} else {
323+
comments.push(...scriptComments, ...templateBody.comments)
324+
}
325+
} else {
326+
comments.push(...scriptComments)
327+
}
328+
}
329+
330+
let scriptLinesRange
331+
if (scriptTokens.length) {
332+
if (scriptComments.length) {
333+
scriptLinesRange = [
334+
Math.min(scriptTokens[0].loc.start.line, scriptComments[0].loc.start.line),
335+
Math.max(scriptTokens[scriptTokens.length - 1].loc.end.line, scriptComments[scriptComments.length - 1].loc.end.line)
336+
]
337+
} else {
338+
scriptLinesRange = [
339+
scriptTokens[0].loc.start.line,
340+
scriptTokens[scriptTokens.length - 1].loc.end.line
341+
]
342+
}
343+
} else if (scriptComments.length) {
344+
scriptLinesRange = [
345+
scriptComments[0].loc.start.line,
346+
scriptComments[scriptComments.length - 1].loc.end.line
347+
]
348+
}
349+
const templateLinesRange = templateBody && [templateBody.loc.start.line, templateBody.loc.end.line]
350+
351+
// split (honors line-ending)
352+
const lines = sourceCode.lines
353+
354+
const strings = getAllStrings()
355+
const stringsByLine = strings.reduce(groupByLineNumber, {})
356+
357+
const templateLiterals = getAllTemplateLiterals()
358+
const templateLiteralsByLine = templateLiterals.reduce(groupByLineNumber, {})
359+
360+
const regExpLiterals = getAllRegExpLiterals()
361+
const regExpLiteralsByLine = regExpLiterals.reduce(groupByLineNumber, {})
362+
363+
const htmlAttributeValuesByLine = htmlAttributeValues.reduce(groupByLineNumber, {})
364+
365+
const htmlTextContents = getAllHTMLTextContents()
366+
const htmlTextContentsByLine = htmlTextContents.reduce(groupByLineNumber, {})
367+
368+
const commentsByLine = comments.reduce(groupByLineNumber, {})
369+
370+
lines.forEach((line, i) => {
371+
// i is zero-indexed, line numbers are one-indexed
372+
const lineNumber = i + 1
373+
374+
const inScript = (scriptLinesRange && scriptLinesRange[0] <= lineNumber && lineNumber <= scriptLinesRange[1])
375+
const inTemplate = (templateLinesRange && templateLinesRange[0] <= lineNumber && lineNumber <= templateLinesRange[1])
376+
// check if line is inside a script or template.
377+
if (!inScript && !inTemplate) {
378+
// out of range.
379+
return
380+
}
381+
const maxLength = inScript && inTemplate
382+
? Math.max(scriptMaxLength, templateMaxLength)
383+
: inScript
384+
? scriptMaxLength
385+
: templateMaxLength
386+
387+
if (
388+
(ignoreStrings && stringsByLine[lineNumber]) ||
389+
(ignoreTemplateLiterals && templateLiteralsByLine[lineNumber]) ||
390+
(ignoreRegExpLiterals && regExpLiteralsByLine[lineNumber]) ||
391+
(ignoreHTMLAttributeValues && htmlAttributeValuesByLine[lineNumber]) ||
392+
(ignoreHTMLTextContents && htmlTextContentsByLine[lineNumber])
393+
) {
394+
// ignore this line
395+
return
396+
}
397+
398+
/*
399+
* if we're checking comment length; we need to know whether this
400+
* line is a comment
401+
*/
402+
let lineIsComment = false
403+
let textToMeasure
404+
405+
/*
406+
* comments to check.
407+
*/
408+
if (commentsByLine[lineNumber]) {
409+
const commentList = [...commentsByLine[lineNumber]]
410+
411+
let comment = commentList.pop()
412+
413+
if (isFullLineComment(line, lineNumber, comment)) {
414+
lineIsComment = true
415+
textToMeasure = line
416+
} else if (ignoreTrailingComments && isTrailingComment(line, lineNumber, comment)) {
417+
textToMeasure = stripTrailingComment(line, comment)
418+
419+
// ignore multiple trailing comments in the same line
420+
comment = commentList.pop()
421+
422+
while (isTrailingComment(textToMeasure, lineNumber, comment)) {
423+
textToMeasure = stripTrailingComment(textToMeasure, comment)
424+
}
425+
} else {
426+
textToMeasure = line
427+
}
428+
} else {
429+
textToMeasure = line
430+
}
431+
432+
if ((ignorePattern && ignorePattern.test(textToMeasure)) ||
433+
(ignoreUrls && URL_REGEXP.test(textToMeasure))) {
434+
// ignore this line
435+
return
436+
}
437+
438+
const lineLength = computeLineLength(textToMeasure, tabWidth)
439+
const commentLengthApplies = lineIsComment && maxCommentLength
440+
441+
if (lineIsComment && ignoreComments) {
442+
return
443+
}
444+
445+
if (commentLengthApplies) {
446+
if (lineLength > maxCommentLength) {
447+
context.report({
448+
node,
449+
loc: { line: lineNumber, column: 0 },
450+
messageId: 'maxComment',
451+
data: {
452+
lineLength,
453+
maxCommentLength
454+
}
455+
})
456+
}
457+
} else if (lineLength > maxLength) {
458+
context.report({
459+
node,
460+
loc: { line: lineNumber, column: 0 },
461+
messageId: 'max',
462+
data: {
463+
lineLength,
464+
maxLength
465+
}
466+
})
467+
}
468+
})
469+
}
470+
471+
// --------------------------------------------------------------------------
472+
// Public API
473+
// --------------------------------------------------------------------------
474+
475+
const bodyVisitor = utils.defineTemplateBodyVisitor(context,
476+
{
477+
'VAttribute[directive=false] > VLiteral' (node) {
478+
htmlAttributeValues.push(node)
479+
}
480+
}
481+
)
482+
483+
return Object.assign({}, bodyVisitor,
484+
{
485+
'Program:exit' (node) {
486+
if (bodyVisitor['Program:exit']) {
487+
bodyVisitor['Program:exit'](node)
488+
}
489+
checkProgramForMaxLength(node)
490+
}
491+
}
492+
)
493+
}
494+
}

‎tests/lib/rules/max-len.js

Lines changed: 1241 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.