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

feat(shebang): Add options to ignore unpublished files #172

Merged
merged 9 commits into from
Feb 7, 2024
35 changes: 26 additions & 9 deletions lib/rules/shebang.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
"use strict"

const path = require("path")

const getConvertPath = require("../util/get-convert-path")
const getPackageJson = require("../util/get-package-json")
const getNpmignore = require("../util/get-npmignore")

const NODE_SHEBANG = "#!/usr/bin/env node\n"
const SHEBANG_PATTERN = /^(#!.+?)?(\r)?\n/u
Expand Down Expand Up @@ -95,26 +97,41 @@ module.exports = {
},
create(context) {
const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
let filePath = context.filename ?? context.getFilename()
const filePath = context.filename ?? context.getFilename()
if (filePath === "<input>") {
return {}
}
filePath = path.resolve(filePath)

const p = getPackageJson(filePath)
if (!p) {
return {}
}

const basedir = path.dirname(p.filePath)
filePath = path.join(
basedir,
getConvertPath(context)(
path.relative(basedir, filePath).replace(/\\/gu, "/")
)
const packageDirectory = path.dirname(p.filePath)

const originalAbsolutePath = path.resolve(filePath)
const originalRelativePath = path
.relative(packageDirectory, originalAbsolutePath)
.replace(/\\/gu, "/")

const convertedRelativePath =
getConvertPath(context)(originalRelativePath)
const convertedAbsolutePath = path.resolve(
packageDirectory,
convertedRelativePath
)

const needsShebang = isBinFile(filePath, p.bin, basedir)
const npmignore = getNpmignore(convertedAbsolutePath)

if (npmignore.match(convertedRelativePath)) {
return {}
}

const needsShebang = isBinFile(
convertedAbsolutePath,
p.bin,
packageDirectory
)
const info = getShebangInfo(sourceCode)

return {
Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/shebang/ignored/executable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env node
module.exports = {}
1 change: 1 addition & 0 deletions tests/fixtures/shebang/ignored/normal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {}
7 changes: 7 additions & 0 deletions tests/fixtures/shebang/ignored/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "test",
"version": "0.0.0",
"files": [
"./bin/test.js"
]
}
64 changes: 62 additions & 2 deletions tests/lib/rules/shebang.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,152 +17,199 @@ function fixture(name) {
return path.resolve(__dirname, "../../fixtures/shebang", name)
}

/** @type {import('eslint').RuleTester} */
const ruleTester = new RuleTester()
ruleTester.run("shebang", rule, {
valid: [
{
name: "string-bin/bin/test.js",
filename: fixture("string-bin/bin/test.js"),
code: "#!/usr/bin/env node\nhello();",
},
{
name: "string-bin/lib/test.js",
filename: fixture("string-bin/lib/test.js"),
code: "hello();",
},
{
name: "object-bin/bin/a.js",
filename: fixture("object-bin/bin/a.js"),
code: "#!/usr/bin/env node\nhello();",
},
{
name: "object-bin/bin/b.js",
filename: fixture("object-bin/bin/b.js"),
code: "#!/usr/bin/env node\nhello();",
},
{
name: "object-bin/bin/c.js",
filename: fixture("object-bin/bin/c.js"),
code: "hello();",
},
{
name: "no-bin-field/lib/test.js",
filename: fixture("no-bin-field/lib/test.js"),
code: "hello();",
},
"#!/usr/bin/env node\nhello();",
"hello();",
{
name: "<input> with shebang",
code: "#!/usr/bin/env node\nhello();",
},
{
name: "<input> without shebang",
code: "hello();",
},

// convertPath
{
name: "convertPath - string-bin/src/bin/test.js",
filename: fixture("string-bin/src/bin/test.js"),
code: "#!/usr/bin/env node\nhello();",
options: [{ convertPath: { "src/**": ["^src/(.+)$", "$1"] } }],
},
{
name: "convertPath - string-bin/src/lib/test.js",
filename: fixture("string-bin/src/lib/test.js"),
code: "hello();",
options: [{ convertPath: { "src/**": ["^src/(.+)$", "$1"] } }],
},
{
name: "convertPath - object-bin/src/bin/a.js",
filename: fixture("object-bin/src/bin/a.js"),
code: "#!/usr/bin/env node\nhello();",
options: [{ convertPath: { "src/**": ["^src/(.+)$", "$1"] } }],
},
{
name: "convertPath - object-bin/src/bin/b.js",
filename: fixture("object-bin/src/bin/b.js"),
code: "#!/usr/bin/env node\nhello();",
options: [{ convertPath: { "src/**": ["^src/(.+)$", "$1"] } }],
},
{
name: "convertPath - object-bin/src/bin/c.js",
filename: fixture("object-bin/src/bin/c.js"),
code: "hello();",
options: [{ convertPath: { "src/**": ["^src/(.+)$", "$1"] } }],
},
{
name: "convertPath - no-bin-field/src/lib/test.js",
filename: fixture("no-bin-field/src/lib/test.js"),
code: "hello();",
options: [{ convertPath: { "src/**": ["^src/(.+)$", "$1"] } }],
},

// Should work fine if the filename is relative.
{
name: "relative path - string-bin/bin/test.js",
filename: "tests/fixtures/shebang/string-bin/bin/test.js",
code: "#!/usr/bin/env node\nhello();",
},
{
name: "relative path - string-bin/lib/test.js",
filename: "tests/fixtures/shebang/string-bin/lib/test.js",
code: "hello();",
},

// BOM and \r\n
{
name: "BOM without newline",
filename: fixture("string-bin/lib/test.js"),
code: "\uFEFFhello();",
},
{
name: "BOM with newline",
filename: fixture("string-bin/lib/test.js"),
code: "\uFEFFhello();\n",
},
{
name: "with windows newline",
filename: fixture("string-bin/lib/test.js"),
code: "hello();\r\n",
},
{
name: "BOM with windows newline",
filename: fixture("string-bin/lib/test.js"),
code: "\uFEFFhello();\r\n",
},

// blank lines on the top of files.
{
name: "blank lines on the top of files.",
filename: fixture("string-bin/lib/test.js"),
code: "\n\n\nhello();",
},

// https://github.com/mysticatea/eslint-plugin-node/issues/51
{
name: "Shebang with CLI flags",
filename: fixture("string-bin/bin/test.js"),
code: "#!/usr/bin/env node --harmony\nhello();",
},

// use node resolution
{
name: "use node resolution",
filename: fixture("object-bin/bin/index.js"),
code: "#!/usr/bin/env node\nhello();",
},

// npm ignored files are ignored!
{
name: "ignored file is executable",
filename: fixture("ignored/executable.js"),
code: "#!/usr/bin/env node\nhello();",
},
{
name: "ignored file is not executable",
filename: fixture("ignored/normal.js"),
code: "hello();",
},
],
invalid: [
{
name: "bin: string - match - no shebang",
filename: fixture("string-bin/bin/test.js"),
code: "hello();",
output: "#!/usr/bin/env node\nhello();",
errors: ['This file needs shebang "#!/usr/bin/env node".'],
},
{
name: "bin: string - match - incorrect shebang",
filename: fixture("string-bin/bin/test.js"),
code: "#!/usr/bin/node\nhello();",
output: "#!/usr/bin/env node\nhello();",
errors: ['This file needs shebang "#!/usr/bin/env node".'],
},
{
name: "bin: string - no match - with shebang",
filename: fixture("string-bin/lib/test.js"),
code: "#!/usr/bin/env node\nhello();",
output: "hello();",
errors: ["This file needs no shebang."],
},
{
name: 'bin: {a: "./bin/a.js"} - match - no shebang',
filename: fixture("object-bin/bin/a.js"),
code: "hello();",
output: "#!/usr/bin/env node\nhello();",
errors: ['This file needs shebang "#!/usr/bin/env node".'],
},
{
name: 'bin: {b: "./bin/b.js"} - match - no shebang',
filename: fixture("object-bin/bin/b.js"),
code: "#!/usr/bin/node\nhello();",
output: "#!/usr/bin/env node\nhello();",
errors: ['This file needs shebang "#!/usr/bin/env node".'],
},
{
name: 'bin: {c: "./bin"} - no match - with shebang',
filename: fixture("object-bin/bin/c.js"),
code: "#!/usr/bin/env node\nhello();",
output: "hello();",
errors: ["This file needs no shebang."],
},
{
name: "bin: undefined - no match - with shebang",
filename: fixture("no-bin-field/lib/test.js"),
code: "#!/usr/bin/env node\nhello();",
output: "hello();",
Expand All @@ -171,13 +218,15 @@ ruleTester.run("shebang", rule, {

// convertPath
{
name: "convertPath in options",
filename: fixture("string-bin/src/bin/test.js"),
code: "hello();",
output: "#!/usr/bin/env node\nhello();",
options: [{ convertPath: { "src/**": ["^src/(.+)$", "$1"] } }],
errors: ['This file needs shebang "#!/usr/bin/env node".'],
},
{
name: "convertPath in settings",
filename: fixture("string-bin/src/bin/test.js"),
code: "hello();",
output: "#!/usr/bin/env node\nhello();",
Expand All @@ -187,41 +236,47 @@ ruleTester.run("shebang", rule, {
},
},
{
name: "converted path - string-bin/src/bin/test.js",
filename: fixture("string-bin/src/bin/test.js"),
code: "#!/usr/bin/node\nhello();",
output: "#!/usr/bin/env node\nhello();",
options: [{ convertPath: { "src/**": ["^src/(.+)$", "$1"] } }],
errors: ['This file needs shebang "#!/usr/bin/env node".'],
},
{
name: "converted path - string-bin/src/lib/test.js",
filename: fixture("string-bin/src/lib/test.js"),
code: "#!/usr/bin/env node\nhello();",
output: "hello();",
options: [{ convertPath: { "src/**": ["^src/(.+)$", "$1"] } }],
errors: ["This file needs no shebang."],
},
{
name: "converted path - object-bin/src/bin/a.js",
filename: fixture("object-bin/src/bin/a.js"),
code: "hello();",
output: "#!/usr/bin/env node\nhello();",
options: [{ convertPath: { "src/**": ["^src/(.+)$", "$1"] } }],
errors: ['This file needs shebang "#!/usr/bin/env node".'],
},
{
name: "converted path - object-bin/src/bin/b.js",
filename: fixture("object-bin/src/bin/b.js"),
code: "#!/usr/bin/node\nhello();",
output: "#!/usr/bin/env node\nhello();",
options: [{ convertPath: { "src/**": ["^src/(.+)$", "$1"] } }],
errors: ['This file needs shebang "#!/usr/bin/env node".'],
},
{
name: "converted path - object-bin/src/bin/c.js",
filename: fixture("object-bin/src/bin/c.js"),
code: "#!/usr/bin/env node\nhello();",
output: "hello();",
options: [{ convertPath: { "src/**": ["^src/(.+)$", "$1"] } }],
errors: ["This file needs no shebang."],
},
{
name: "converted path - no-bin-field/src/lib/test.js",
filename: fixture("no-bin-field/src/lib/test.js"),
code: "#!/usr/bin/env node\nhello();",
output: "hello();",
Expand All @@ -231,12 +286,14 @@ ruleTester.run("shebang", rule, {

// Should work fine if the filename is relative.
{
name: "relative path - string-bin/bin/test.js",
filename: "tests/fixtures/shebang/string-bin/bin/test.js",
code: "hello();",
output: "#!/usr/bin/env node\nhello();",
errors: ['This file needs shebang "#!/usr/bin/env node".'],
},
{
name: "relative path - string-bin/lib/test.js",
filename: "tests/fixtures/shebang/string-bin/lib/test.js",
code: "#!/usr/bin/env node\nhello();",
output: "hello();",
Expand All @@ -245,6 +302,7 @@ ruleTester.run("shebang", rule, {

// header comments
{
name: "header comments",
filename: fixture("string-bin/bin/test.js"),
code: "/* header */\nhello();",
output: "#!/usr/bin/env node\n/* header */\nhello();",
Expand Down Expand Up @@ -306,6 +364,7 @@ ruleTester.run("shebang", rule, {

// https://github.com/mysticatea/eslint-plugin-node/issues/51
{
name: "Shebang with CLI flags",
filename: fixture("string-bin/lib/test.js"),
code: "#!/usr/bin/env node --harmony\nhello();",
output: "hello();",
Expand All @@ -314,6 +373,7 @@ ruleTester.run("shebang", rule, {

// use node resolution
{
name: "use node resolution",
filename: fixture("object-bin/bin/index.js"),
code: "hello();",
output: "#!/usr/bin/env node\nhello();",
Expand Down