Skip to content

Commit

Permalink
Update: shebang came to check BOM and \r\n (fixes #16)
Browse files Browse the repository at this point in the history
  • Loading branch information
mysticatea committed Jan 21, 2016
1 parent b323378 commit fddf2f0
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 26 deletions.
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
55 changes: 55 additions & 0 deletions tests/lib/rules/shebang.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,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)."
// ]
}
]
});

0 comments on commit fddf2f0

Please sign in to comment.