eslint-plugin-header
Advanced tools
Comparing version 3.0.0 to 3.1.0
@@ -0,1 +1,6 @@ | ||
# 3.1.0 | ||
* Update to eslint 7.7.0 | ||
* Add a third option to specify number of linebreaks after the header. (#29) | ||
# 3.0.0 | ||
@@ -2,0 +7,0 @@ |
@@ -8,3 +8,3 @@ "use strict"; | ||
function isPattern(object) { | ||
return typeof object === "object" && object.hasOwnProperty("pattern"); | ||
return typeof object === "object" && Object.prototype.hasOwnProperty.call(object, "pattern"); | ||
} | ||
@@ -26,13 +26,26 @@ | ||
// Returns either the first block comment or the first set of line comments that | ||
// are ONLY separated by a single newline. Note that this does not actually | ||
// check if they are at the start of the file since that is already checked by | ||
// hasHeader(). | ||
function getLeadingComments(context, node) { | ||
return node.body.length ? | ||
context.getComments(node.body[0]).leading : | ||
context.getComments(node).leading; | ||
var all = excludeShebangs(context.getSourceCode().getAllComments(node.body.length ? node.body[0] : node)); | ||
if (all[0].type.toLowerCase() === "block") { | ||
return [all[0]]; | ||
} | ||
for (var i = 1; i < all.length; ++i) { | ||
var txt = context.getSourceCode().getText().slice(all[i - 1].range[1], all[i].range[0]); | ||
if (!txt.match(/^(\r\n|\r|\n)$/)) { | ||
break; | ||
} | ||
} | ||
return all.slice(0, i); | ||
} | ||
function genCommentBody(commentType, textArray, eol) { | ||
function genCommentBody(commentType, textArray, eol, numNewlines) { | ||
var eols = eol.repeat(numNewlines); | ||
if (commentType === "block") { | ||
return "/*" + textArray.join(eol) + "*/" + eol; | ||
return "/*" + textArray.join(eol) + "*/" + eols; | ||
} else { | ||
return "//" + textArray.join(eol + "//") + eol; | ||
return "//" + textArray.join(eol + "//") + eols; | ||
} | ||
@@ -50,7 +63,7 @@ } | ||
function genPrependFixer(commentType, node, headerLines, eol) { | ||
function genPrependFixer(commentType, node, headerLines, eol, numNewlines) { | ||
return function(fixer) { | ||
return fixer.insertTextBefore( | ||
node, | ||
genCommentBody(commentType, headerLines, eol) | ||
genCommentBody(commentType, headerLines, eol, numNewlines) | ||
); | ||
@@ -60,7 +73,7 @@ }; | ||
function genReplaceFixer(commentType, context, leadingComments, headerLines, eol) { | ||
function genReplaceFixer(commentType, context, leadingComments, headerLines, eol, numNewlines) { | ||
return function(fixer) { | ||
return fixer.replaceTextRange( | ||
genCommentsRange(context, leadingComments, eol), | ||
genCommentBody(commentType, headerLines, eol) | ||
genCommentBody(commentType, headerLines, eol, numNewlines) | ||
); | ||
@@ -72,3 +85,4 @@ }; | ||
var lastOption = options.length > 0 ? options[options.length - 1] : null; | ||
if (typeof lastOption === "object" && !Array.isArray(lastOption) && lastOption !== null && !lastOption.hasOwnProperty("pattern")) { | ||
if (typeof lastOption === "object" && !Array.isArray(lastOption) && lastOption !== null | ||
&& !Object.prototype.hasOwnProperty.call(lastOption, "pattern")) { | ||
return lastOption; | ||
@@ -90,112 +104,167 @@ } | ||
module.exports = function(context) { | ||
var options = context.options; | ||
function hasHeader(src) { | ||
if (src.substr(0, 2) === "#!") { | ||
var m = src.match(/(\r\n|\r|\n)/); | ||
if (m) { | ||
src = src.slice(m.index + 1); | ||
} | ||
} | ||
return src.substr(0, 2) === "/*" || src.substr(0, 2) === "//"; | ||
} | ||
var eol = getEOL(options); | ||
// If just one option then read comment from file | ||
if (options.length === 1 || (options.length === 2 && findSettings(options))) { | ||
var text = fs.readFileSync(context.options[0], "utf8"); | ||
options = commentParser(text); | ||
function matchesLineEndings(src, num) { | ||
for (var j = 0; j < num; ++j) { | ||
var m = src.match(/^(\r\n|\r|\n)/); | ||
if (m) { | ||
src = src.slice(m.index + m[0].length); | ||
} else { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
var commentType = options[0]; | ||
var headerLines, fixLines = []; | ||
// If any of the lines are regular expressions, then we can't | ||
// automatically fix them. We set this to true below once we | ||
// ensure none of the lines are of type RegExp | ||
var canFix = false; | ||
if (Array.isArray(options[1])) { | ||
canFix = true; | ||
headerLines = options[1].map(function(line) { | ||
var isRegex = isPattern(line); | ||
// Can only fix regex option if a template is also provided | ||
if (isRegex && !line.template) { | ||
canFix = false; | ||
} | ||
module.exports = { | ||
meta: { | ||
type: "layout", | ||
fixable: "whitespace" | ||
}, | ||
create: function(context) { | ||
var options = context.options; | ||
var numNewlines = options.length > 2 ? options[2] : 1; | ||
var eol = getEOL(options); | ||
// If just one option then read comment from file | ||
if (options.length === 1 || (options.length === 2 && findSettings(options))) { | ||
var text = fs.readFileSync(context.options[0], "utf8"); | ||
options = commentParser(text); | ||
} | ||
var commentType = options[0].toLowerCase(); | ||
var headerLines, fixLines = []; | ||
// If any of the lines are regular expressions, then we can't | ||
// automatically fix them. We set this to true below once we | ||
// ensure none of the lines are of type RegExp | ||
var canFix = false; | ||
if (Array.isArray(options[1])) { | ||
canFix = true; | ||
headerLines = options[1].map(function(line) { | ||
var isRegex = isPattern(line); | ||
// Can only fix regex option if a template is also provided | ||
if (isRegex && !line.template) { | ||
canFix = false; | ||
} | ||
fixLines.push(line.template || line); | ||
return isRegex ? new RegExp(line.pattern) : line; | ||
}); | ||
} else if (isPattern(options[1])) { | ||
var line = options[1]; | ||
headerLines = [new RegExp(line.pattern)]; | ||
fixLines.push(line.template || line); | ||
return isRegex ? new RegExp(line.pattern) : line; | ||
}); | ||
} else if (isPattern(options[1])) { | ||
var line = options[1]; | ||
headerLines = [new RegExp(line.pattern)]; | ||
fixLines.push(line.template || line); | ||
// Same as above for regex and template | ||
canFix = !!line.template; | ||
} else { | ||
canFix = true; | ||
headerLines = options[1].split(/\r?\n/); | ||
fixLines = headerLines; | ||
} | ||
// Same as above for regex and template | ||
canFix = !!line.template; | ||
} else { | ||
canFix = true; | ||
headerLines = options[1].split(/\r?\n/); | ||
fixLines = headerLines; | ||
} | ||
return { | ||
Program: function(node) { | ||
var leadingComments = excludeShebangs(getLeadingComments(context, node)); | ||
return { | ||
Program: function(node) { | ||
if (!hasHeader(context.getSourceCode().getText())) { | ||
context.report({ | ||
loc: node.loc, | ||
message: "missing header", | ||
fix: genPrependFixer(commentType, node, fixLines, eol, numNewlines) | ||
}); | ||
} else { | ||
var leadingComments = getLeadingComments(context, node); | ||
if (!leadingComments.length) { | ||
context.report({ | ||
loc: node.loc, | ||
message: "missing header", | ||
fix: canFix ? genPrependFixer(commentType, node, fixLines, eol) : null | ||
}); | ||
} else if (leadingComments[0].type.toLowerCase() !== commentType) { | ||
context.report({ | ||
loc: node.loc, | ||
message: "header should be a {{commentType}} comment", | ||
data: { | ||
commentType: commentType | ||
}, | ||
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol) : null | ||
}); | ||
} else { | ||
if (commentType === "line") { | ||
if (leadingComments.length < headerLines.length) { | ||
if (!leadingComments.length) { | ||
context.report({ | ||
loc: node.loc, | ||
message: "incorrect header", | ||
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol) : null | ||
message: "missing header", | ||
fix: canFix ? genPrependFixer(commentType, node, fixLines, eol, numNewlines) : null | ||
}); | ||
return; | ||
} | ||
for (var i = 0; i < headerLines.length; i++) { | ||
if (!match(leadingComments[i].value, headerLines[i])) { | ||
context.report({ | ||
loc: node.loc, | ||
message: "incorrect header", | ||
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol) : null | ||
}); | ||
return; | ||
} | ||
} | ||
} else { | ||
// if block comment pattern has more than 1 line, we also split the comment | ||
var leadingLines = [leadingComments[0].value]; | ||
if (headerLines.length > 1) { | ||
leadingLines = leadingComments[0].value.split(/\r?\n/); | ||
} | ||
} else if (leadingComments[0].type.toLowerCase() !== commentType) { | ||
context.report({ | ||
loc: node.loc, | ||
message: "header should be a {{commentType}} comment", | ||
data: { | ||
commentType: commentType | ||
}, | ||
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines) : null | ||
}); | ||
} else { | ||
if (commentType === "line") { | ||
if (leadingComments.length < headerLines.length) { | ||
context.report({ | ||
loc: node.loc, | ||
message: "incorrect header", | ||
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines) : null | ||
}); | ||
return; | ||
} | ||
for (var i = 0; i < headerLines.length; i++) { | ||
if (!match(leadingComments[i].value, headerLines[i])) { | ||
context.report({ | ||
loc: node.loc, | ||
message: "incorrect header", | ||
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines) : null | ||
}); | ||
return; | ||
} | ||
} | ||
var hasError = false; | ||
if (leadingLines.length > headerLines.length) { | ||
hasError = true; | ||
} | ||
for (i = 0; !hasError && i < headerLines.length; i++) { | ||
if (!match(leadingLines[i], headerLines[i])) { | ||
hasError = true; | ||
} | ||
} | ||
var postLineHeader = context.getSourceCode().text.substr(leadingComments[headerLines.length - 1].range[1], numNewlines * 2); | ||
if (!matchesLineEndings(postLineHeader, numNewlines)) { | ||
context.report({ | ||
loc: node.loc, | ||
message: "no newline after header", | ||
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines) : null | ||
}); | ||
} | ||
if (hasError) { | ||
if (canFix && headerLines.length > 1) { | ||
fixLines = [fixLines.join(eol)]; | ||
} else { | ||
// if block comment pattern has more than 1 line, we also split the comment | ||
var leadingLines = [leadingComments[0].value]; | ||
if (headerLines.length > 1) { | ||
leadingLines = leadingComments[0].value.split(/\r?\n/); | ||
} | ||
var hasError = false; | ||
if (leadingLines.length > headerLines.length) { | ||
hasError = true; | ||
} | ||
for (i = 0; !hasError && i < headerLines.length; i++) { | ||
if (!match(leadingLines[i], headerLines[i])) { | ||
hasError = true; | ||
} | ||
} | ||
if (hasError) { | ||
if (canFix && headerLines.length > 1) { | ||
fixLines = [fixLines.join(eol)]; | ||
} | ||
context.report({ | ||
loc: node.loc, | ||
message: "incorrect header", | ||
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines) : null | ||
}); | ||
} else { | ||
var postBlockHeader = context.getSourceCode().text.substr(leadingComments[0].range[1], numNewlines * 2); | ||
if (!matchesLineEndings(postBlockHeader, numNewlines)) { | ||
context.report({ | ||
loc: node.loc, | ||
message: "no newline after header", | ||
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol, numNewlines) : null | ||
}); | ||
} | ||
} | ||
} | ||
context.report({ | ||
loc: node.loc, | ||
message: "incorrect header", | ||
fix: canFix ? genReplaceFixer(commentType, context, leadingComments, fixLines, eol) : null | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
} | ||
}; |
{ | ||
"name": "eslint-plugin-header", | ||
"version": "3.0.0", | ||
"version": "3.1.0", | ||
"description": "ESLint plugin to ensure that files begin with given comment", | ||
@@ -12,7 +12,7 @@ "main": "index.js", | ||
"devDependencies": { | ||
"eslint": "^5.12.0", | ||
"mocha": "^5.2.0" | ||
"eslint": "^7.7.0", | ||
"mocha": "^8.1.1" | ||
}, | ||
"peerDependencies": { | ||
"eslint": ">=0.18.0" | ||
"eslint": ">=7.7.0" | ||
}, | ||
@@ -19,0 +19,0 @@ "keywords": [ |
@@ -10,3 +10,3 @@ eslint-plugin-header | ||
This rule takes 1 or 2 arguments with an optional settings object. | ||
This rule takes 1, 2 or 3 arguments with an optional settings object. | ||
@@ -52,2 +52,57 @@ ### 1 argument | ||
### 3 arguments | ||
The optional third argument which defaults to 1 specifies the number of newlines that are enforced after the header. | ||
Zero newlines: | ||
```json | ||
{ | ||
"plugins": [ | ||
"header" | ||
], | ||
"rules": { | ||
"header/header": [2, "block", [" Copyright now","My Company "], 0] | ||
} | ||
} | ||
``` | ||
```js | ||
/* Copyright now | ||
My Company */ console.log(1) | ||
``` | ||
One newline (default) | ||
```json | ||
{ | ||
"plugins": [ | ||
"header" | ||
], | ||
"rules": { | ||
"header/header": [2, "block", [" Copyright now","My Company "], 1] | ||
} | ||
} | ||
``` | ||
```js | ||
/* Copyright now | ||
My Company */ | ||
console.log(1) | ||
``` | ||
two newlines | ||
```json | ||
{ | ||
"plugins": [ | ||
"header" | ||
], | ||
"rules": { | ||
"header/header": [2, "block", [" Copyright now","My Company "], 2] | ||
} | ||
} | ||
``` | ||
```js | ||
/* Copyright now | ||
My Company */ | ||
console.log(1) | ||
``` | ||
#### Regular expressions | ||
@@ -54,0 +109,0 @@ |
@@ -19,3 +19,3 @@ "use strict"; | ||
code: "/*Copyright 2015, My Company*/", | ||
options: ["block", "Copyright 2015, My Company"] | ||
options: ["block", "Copyright 2015, My Company", 0] | ||
}, | ||
@@ -57,11 +57,11 @@ { | ||
code: "// Copyright 2017", | ||
options: ["line", {pattern: "^ Copyright \\d+$"}] | ||
options: ["line", {pattern: "^ Copyright \\d+$"}, 0] | ||
}, | ||
{ | ||
code: "// Copyright 2017\n// Author: abc@example.com", | ||
options: ["line", [{pattern: "^ Copyright \\d+$"}, {pattern: "^ Author: \\w+@\\w+\\.\\w+$"}]] | ||
options: ["line", [{pattern: "^ Copyright \\d+$"}, {pattern: "^ Author: \\w+@\\w+\\.\\w+$"}], 0] | ||
}, | ||
{ | ||
code: "/* Copyright 2017\n Author: abc@example.com */", | ||
options: ["block", {pattern: "^ Copyright \\d{4}\\n Author: \\w+@\\w+\\.\\w+ $"}] | ||
options: ["block", {pattern: "^ Copyright \\d{4}\\n Author: \\w+@\\w+\\.\\w+ $"}, 0] | ||
}, | ||
@@ -74,3 +74,3 @@ { | ||
" " | ||
]] | ||
], 0] | ||
}, | ||
@@ -101,2 +101,22 @@ { | ||
]] | ||
}, | ||
{ | ||
code: "/*Copyright 2020, My Company*/\nconsole.log(1);", | ||
options: ["block", "Copyright 2020, My Company", 1], | ||
}, | ||
{ | ||
code: "/*Copyright 2020, My Company*/\n\nconsole.log(1);", | ||
options: ["block", "Copyright 2020, My Company", 2], | ||
}, | ||
{ | ||
code: "/*Copyright 2020, My Company*/\n\n// Log number one\nconsole.log(1);", | ||
options: ["block", "Copyright 2020, My Company", 2], | ||
}, | ||
{ | ||
code: "/*Copyright 2020, My Company*/\n\n/*Log number one*/\nconsole.log(1);", | ||
options: ["block", "Copyright 2020, My Company", 2], | ||
}, | ||
{ | ||
code: "/**\n * Copyright 2020\n * My Company\n **/\n\n/*Log number one*/\nconsole.log(1);", | ||
options: ["block", "*\n * Copyright 2020\n * My Company\n *", 2], | ||
} | ||
@@ -219,4 +239,44 @@ ], | ||
output: "/*************************\n * Copyright 2019\n * My Company\n *************************/\nconsole.log(1)" | ||
}, | ||
{ | ||
code: "/*Copyright 2020, My Company*/console.log(1);", | ||
options: ["block", "Copyright 2020, My Company", 2], | ||
errors: [ | ||
{message: "no newline after header"} | ||
], | ||
output: "/*Copyright 2020, My Company*/\n\nconsole.log(1);" | ||
}, | ||
{ | ||
code: "/*Copyright 2020, My Company*/console.log(1);", | ||
options: ["block", "Copyright 2020, My Company", 1], | ||
errors: [ | ||
{message: "no newline after header"} | ||
], | ||
output: "/*Copyright 2020, My Company*/\nconsole.log(1);" | ||
}, | ||
{ | ||
code: "//Copyright 2020\n//My Company\nconsole.log(1);", | ||
options: ["line", ["Copyright 2020", "My Company"], 2], | ||
errors: [ | ||
{message: "no newline after header"} | ||
], | ||
output: "//Copyright 2020\n//My Company\n\nconsole.log(1);" | ||
}, | ||
{ | ||
code: "/*Copyright 2020, My Company*/\nconsole.log(1);\n//Comment\nconsole.log(2);\n//Comment", | ||
options: ["block", "Copyright 2020, My Company", 2], | ||
errors: [ | ||
{message: "no newline after header"} | ||
], | ||
output: "/*Copyright 2020, My Company*/\n\nconsole.log(1);\n//Comment\nconsole.log(2);\n//Comment" | ||
}, | ||
{ | ||
code: "//Copyright 2020\n//My Company\nconsole.log(1);\n//Comment\nconsole.log(2);\n//Comment", | ||
options: ["line", ["Copyright 2020", "My Company"], 2], | ||
errors: [ | ||
{message: "no newline after header"} | ||
], | ||
output: "//Copyright 2020\n//My Company\n\nconsole.log(1);\n//Comment\nconsole.log(2);\n//Comment" | ||
} | ||
] | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
30858
572
206
12