Skip to content

Commit 125bdfe

Browse files
committed
feat: Add shebangs to all ignored executable files
1 parent 5ad657c commit 125bdfe

File tree

5 files changed

+70
-9
lines changed

5 files changed

+70
-9
lines changed

lib/rules/shebang.js

+36-9
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
"use strict"
66

77
const path = require("path")
8+
const { accessSync, constants } = require("node:fs")
9+
810
const getConvertPath = require("../util/get-convert-path")
911
const getPackageJson = require("../util/get-package-json")
12+
const getNpmignore = require("../util/get-npmignore")
1013

1114
const NODE_SHEBANG = "#!/usr/bin/env node\n"
1215
const SHEBANG_PATTERN = /^(#!.+?)?(\r)?\n/u
@@ -21,6 +24,15 @@ function simulateNodeResolutionAlgorithm(filePath, binField) {
2124
return possibilities.includes(binField)
2225
}
2326

27+
function isExecutable(path) {
28+
try {
29+
accessSync(path, constants.X_OK)
30+
return true
31+
} catch (error) {
32+
return false
33+
}
34+
}
35+
2436
/**
2537
* Checks whether or not a given path is a `bin` file.
2638
*
@@ -95,26 +107,41 @@ module.exports = {
95107
},
96108
create(context) {
97109
const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9
98-
let filePath = context.filename ?? context.getFilename()
110+
const filePath = context.filename ?? context.getFilename()
99111
if (filePath === "<input>") {
100112
return {}
101113
}
102-
filePath = path.resolve(filePath)
103114

104115
const p = getPackageJson(filePath)
105116
if (!p) {
106117
return {}
107118
}
108119

109-
const basedir = path.dirname(p.filePath)
110-
filePath = path.join(
111-
basedir,
112-
getConvertPath(context)(
113-
path.relative(basedir, filePath).replace(/\\/gu, "/")
114-
)
120+
const packageDirectory = path.dirname(p.filePath)
121+
122+
const originalAbsolutePath = path.resolve(filePath)
123+
const originalRelativePath = path
124+
.relative(packageDirectory, originalAbsolutePath)
125+
.replace(/\\/gu, "/")
126+
127+
const convertedRelativePath =
128+
getConvertPath(context)(originalRelativePath)
129+
const convertedAbsolutePath = path.resolve(
130+
packageDirectory,
131+
convertedRelativePath
132+
)
133+
134+
const npmignore = getNpmignore(convertedAbsolutePath)
135+
136+
const isFileBin = isBinFile(
137+
convertedAbsolutePath,
138+
p.bin,
139+
packageDirectory
115140
)
141+
const isFileIgnored = npmignore.match(convertedRelativePath)
142+
const isFileExecutable = isExecutable(originalAbsolutePath)
116143

117-
const needsShebang = isBinFile(filePath, p.bin, basedir)
144+
const needsShebang = isFileBin || (isFileIgnored && isFileExecutable)
118145
const info = getShebangInfo(sourceCode)
119146

120147
return {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env node
2+
module.exports = {}
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "test",
3+
"version": "0.0.0",
4+
"files": [
5+
"./bin/test.js"
6+
]
7+
}

tests/lib/rules/shebang.js

+24
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,16 @@ ruleTester.run("shebang", rule, {
124124
filename: fixture("object-bin/bin/index.js"),
125125
code: "#!/usr/bin/env node\nhello();",
126126
},
127+
128+
// ignored files are executable
129+
{
130+
filename: fixture("ignored/executable.js"),
131+
code: "#!/usr/bin/env node\nhello();",
132+
},
133+
{
134+
filename: fixture("ignored/normal.js"),
135+
code: "hello();",
136+
},
127137
],
128138
invalid: [
129139
{
@@ -319,5 +329,19 @@ ruleTester.run("shebang", rule, {
319329
output: "#!/usr/bin/env node\nhello();",
320330
errors: ['This file needs shebang "#!/usr/bin/env node".'],
321331
},
332+
333+
// ignored files are executable
334+
{
335+
filename: fixture("ignored/executable.js"),
336+
code: "hello();",
337+
output: "#!/usr/bin/env node\nhello();",
338+
errors: ['This file needs shebang "#!/usr/bin/env node".'],
339+
},
340+
{
341+
filename: fixture("ignored/normal.js"),
342+
code: "#!/usr/bin/env node\nhello();",
343+
output: "hello();",
344+
errors: ["This file needs no shebang."],
345+
},
322346
],
323347
})

0 commit comments

Comments
 (0)