From 125bdfe90af58d9606172a92fc96e1db2006524d Mon Sep 17 00:00:00 2001 From: scagood <2230835+scagood@users.noreply.github.com> Date: Fri, 26 Jan 2024 19:13:36 +0000 Subject: [PATCH] feat: Add shebangs to all ignored executable files --- lib/rules/shebang.js | 45 ++++++++++++++++---- tests/fixtures/shebang/ignored/executable.js | 2 + tests/fixtures/shebang/ignored/normal.js | 1 + tests/fixtures/shebang/ignored/package.json | 7 +++ tests/lib/rules/shebang.js | 24 +++++++++++ 5 files changed, 70 insertions(+), 9 deletions(-) create mode 100755 tests/fixtures/shebang/ignored/executable.js create mode 100644 tests/fixtures/shebang/ignored/normal.js create mode 100644 tests/fixtures/shebang/ignored/package.json diff --git a/lib/rules/shebang.js b/lib/rules/shebang.js index 179f347c..d99fbee8 100644 --- a/lib/rules/shebang.js +++ b/lib/rules/shebang.js @@ -5,8 +5,11 @@ "use strict" const path = require("path") +const { accessSync, constants } = require("node:fs") + 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 @@ -21,6 +24,15 @@ function simulateNodeResolutionAlgorithm(filePath, binField) { return possibilities.includes(binField) } +function isExecutable(path) { + try { + accessSync(path, constants.X_OK) + return true + } catch (error) { + return false + } +} + /** * Checks whether or not a given path is a `bin` file. * @@ -95,26 +107,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 === "") { 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 npmignore = getNpmignore(convertedAbsolutePath) + + const isFileBin = isBinFile( + convertedAbsolutePath, + p.bin, + packageDirectory ) + const isFileIgnored = npmignore.match(convertedRelativePath) + const isFileExecutable = isExecutable(originalAbsolutePath) - const needsShebang = isBinFile(filePath, p.bin, basedir) + const needsShebang = isFileBin || (isFileIgnored && isFileExecutable) const info = getShebangInfo(sourceCode) return { diff --git a/tests/fixtures/shebang/ignored/executable.js b/tests/fixtures/shebang/ignored/executable.js new file mode 100755 index 00000000..06bcbf4e --- /dev/null +++ b/tests/fixtures/shebang/ignored/executable.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +module.exports = {} diff --git a/tests/fixtures/shebang/ignored/normal.js b/tests/fixtures/shebang/ignored/normal.js new file mode 100644 index 00000000..4ba52ba2 --- /dev/null +++ b/tests/fixtures/shebang/ignored/normal.js @@ -0,0 +1 @@ +module.exports = {} diff --git a/tests/fixtures/shebang/ignored/package.json b/tests/fixtures/shebang/ignored/package.json new file mode 100644 index 00000000..f564f113 --- /dev/null +++ b/tests/fixtures/shebang/ignored/package.json @@ -0,0 +1,7 @@ +{ + "name": "test", + "version": "0.0.0", + "files": [ + "./bin/test.js" + ] +} diff --git a/tests/lib/rules/shebang.js b/tests/lib/rules/shebang.js index 47d9460c..9ad76e49 100644 --- a/tests/lib/rules/shebang.js +++ b/tests/lib/rules/shebang.js @@ -124,6 +124,16 @@ ruleTester.run("shebang", rule, { filename: fixture("object-bin/bin/index.js"), code: "#!/usr/bin/env node\nhello();", }, + + // ignored files are executable + { + filename: fixture("ignored/executable.js"), + code: "#!/usr/bin/env node\nhello();", + }, + { + filename: fixture("ignored/normal.js"), + code: "hello();", + }, ], invalid: [ { @@ -319,5 +329,19 @@ ruleTester.run("shebang", rule, { output: "#!/usr/bin/env node\nhello();", errors: ['This file needs shebang "#!/usr/bin/env node".'], }, + + // ignored files are executable + { + filename: fixture("ignored/executable.js"), + code: "hello();", + output: "#!/usr/bin/env node\nhello();", + errors: ['This file needs shebang "#!/usr/bin/env node".'], + }, + { + filename: fixture("ignored/normal.js"), + code: "#!/usr/bin/env node\nhello();", + output: "hello();", + errors: ["This file needs no shebang."], + }, ], })