Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update: shebang came to check BOM and \r\n #20

Merged
merged 1 commit into from
Jan 24, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/rules/shebang.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ The following patterns are considered problems for files in `bin` field of `pack
console.log("hello"); /*error This file needs shebang "#!/usr/bin/env node".*/
```

```js
#!/usr/bin/env node /*error This file must not have Unicode BOM.*/
console.log("hello");
// If this file has Unicode BOM.
```

```js
#!/usr/bin/env node /*error This file must have Unix linebreaks (LF).*/
console.log("hello");
// If this file has Windows' linebreaks (CRLF).
```

The following patterns are considered problems for other files:

```js
Expand Down
81 changes: 55 additions & 26 deletions lib/rules/shebang.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var getPackageJson = require("../util/get-package-json");
//------------------------------------------------------------------------------

var NODE_SHEBANG = "#!/usr/bin/env node\n";
var SHEBANG_PATTERN = /^#\!.+?\n/;
var SHEBANG_PATTERN = /^(\uFEFF)?(#\!.+?)?(\r)?\n/;

/**
* Checks whether or not a given path is a `bin` file.
Expand All @@ -45,18 +45,30 @@ function isBinFile(filePath, binField, basedir) {
* Gets the shebang line (includes a line ending) from a given code.
*
* @param {SourceCode} sourceCode - A source code object to check.
* @returns {string} shebang if exists. Otherwise an empty string.
* @returns {{length: number, bom: boolean, shebang: string, cr: boolean}}
* shebang's information.
* `retv.shebang` is an empty string if shebang doesn't exist.
*/
function getShebang(sourceCode) {
function getShebangInfo(sourceCode) {
var m = SHEBANG_PATTERN.exec(sourceCode.text);
return (m && m[0]) || "";

// ESLint v1 has BOM in text.
// ESLint v2 strips BOM from text, and has the "hasBOM" property.
return {
bomIndex: (typeof sourceCode.hasBOM === "boolean" ? -1 : 0),
bom: (typeof sourceCode.hasBOM === "boolean" ? sourceCode.hasBOM : Boolean(m && m[1])),
cr: Boolean(m && m[3]),
length: (m && m[0].length || (sourceCode.text.charCodeAt(0) === 0xFEFF ? 1 : 0)),
shebang: (m && (m[2] + "\n") || "")
};
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {
var sourceCode = context.getSourceCode();
var filePath = context.getFilename();
if (filePath === "<input>") {
return {};
Expand All @@ -75,36 +87,53 @@ module.exports = function(context) {
);

var needsShebang = isBinFile(filePath, p.bin, basedir);
var shebang = getShebang(context.getSourceCode());
if (needsShebang ? shebang === NODE_SHEBANG : !shebang) {
return {};
}
var info = getShebangInfo(sourceCode);

if (needsShebang) {
return {
Program: function(node) {
return {
Program: function(node) {
if (needsShebang ? info.shebang === NODE_SHEBANG : !info.shebang) {
// Good the shebang target.
// Checks BOM and \r.
if (needsShebang && info.bom) {
context.report({
node: node,
message: "This file must not have Unicode BOM.",
fix: function(fixer) {
return fixer.removeRange([info.bomIndex, info.bomIndex + 1]);
}
});
}
if (needsShebang && info.cr) {
context.report({
node: node,
message: "This file must have Unix linebreaks (LF).",
fix: function(fixer) {
var index = sourceCode.text.indexOf("\r");
return fixer.removeRange([index, index + 1]);
}
});
}
}
else if (needsShebang) {
// Shebang is lacking.
context.report({
node: node,
message: "This file needs shebang \"#!/usr/bin/env node\".",
fix: function(fixer) {
if (shebang) {
return fixer.replaceTextRange([0, shebang.length], NODE_SHEBANG);
}
return fixer.insertTextBeforeRange([0, 0], NODE_SHEBANG);
return fixer.replaceTextRange([info.bomIndex, info.length], NODE_SHEBANG);
}
});
}
else {
// Shebang is extra.
context.report({
node: node,
message: "This file needs no shebang.",
fix: function(fixer) {
return fixer.removeRange([0, info.length]);
}
});
}
};
}
return {
Program: function(node) {
context.report({
node: node,
message: "This file needs no shebang.",
fix: function(fixer) {
return fixer.removeRange([0, shebang.length]);
}
});
}
};
};
Expand Down
73 changes: 73 additions & 0 deletions tests/lib/rules/shebang.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,24 @@ ruleTester.run("shebang", rule, {
{
filename: "tests/fixtures/shebang/string-bin/lib/test.js",
code: "hello();"
},

// BOM and \r\n
{
filename: fixture("string-bin/lib/test.js"),
code: "\uFEFFhello();"
},
{
filename: fixture("string-bin/lib/test.js"),
code: "\uFEFFhello();\n"
},
{
filename: fixture("string-bin/lib/test.js"),
code: "hello();\r\n"
},
{
filename: fixture("string-bin/lib/test.js"),
code: "\uFEFFhello();\r\n"
}
],
invalid: [
Expand Down Expand Up @@ -225,6 +243,61 @@ ruleTester.run("shebang", rule, {
code: "/* header */\nhello();",
output: "#!/usr/bin/env node\n/* header */\nhello();",
errors: ["This file needs shebang \"#!/usr/bin/env node\"."]
},

// BOM and \r\n
{
filename: fixture("string-bin/bin/test.js"),
code: "\uFEFFhello();",
output: "#!/usr/bin/env node\nhello();",
errors: ["This file needs shebang \"#!/usr/bin/env node\"."]
},
{
filename: fixture("string-bin/bin/test.js"),
code: "hello();\n",
output: "#!/usr/bin/env node\nhello();\n",
errors: ["This file needs shebang \"#!/usr/bin/env node\"."]
},
{
filename: fixture("string-bin/bin/test.js"),
code: "hello();\r\n",
output: "#!/usr/bin/env node\nhello();\r\n",
errors: ["This file needs shebang \"#!/usr/bin/env node\"."]
},
{
filename: fixture("string-bin/bin/test.js"),
code: "\uFEFFhello();\n",
output: "#!/usr/bin/env node\nhello();\n",
errors: ["This file needs shebang \"#!/usr/bin/env node\"."]
},
{
filename: fixture("string-bin/bin/test.js"),
code: "\uFEFFhello();\r\n",
output: "#!/usr/bin/env node\nhello();\r\n",
errors: ["This file needs shebang \"#!/usr/bin/env node\"."]
// ESLint v1 does not support
// },
// {
// filename: fixture("string-bin/bin/test.js"),
// code: "\uFEFF#!/usr/bin/env node\nhello();",
// output: "#!/usr/bin/env node\nhello();",
// errors: ["This file must not have Unicode BOM."]
},
{
filename: fixture("string-bin/bin/test.js"),
code: "#!/usr/bin/env node\r\nhello();",
output: "#!/usr/bin/env node\nhello();",
errors: ["This file must have Unix linebreaks (LF)."]
// ESLint v1 does not support
// },
// {
// filename: fixture("string-bin/bin/test.js"),
// code: "\uFEFF#!/usr/bin/env node\r\nhello();",
// output: "#!/usr/bin/env node\nhello();",
// errors: [
// "This file must not have Unicode BOM.",
// "This file must have Unix linebreaks (LF)."
// ]
}
]
});