diff --git a/README.md b/README.md index 230aef34..2873db27 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ For [Shareable Configs](https://eslint.org/docs/latest/developer-guide/shareable | [file-extension-in-import](docs/rules/file-extension-in-import.md) | enforce the style of file extensions in `import` declarations | | 🔧 | | | [global-require](docs/rules/global-require.md) | require `require()` calls to be placed at top-level module scope | | | | | [handle-callback-err](docs/rules/handle-callback-err.md) | require error handling in callbacks | | | | +| [hashbang](docs/rules/hashbang.md) | require correct usage of hashbang | ☑️ 🟢 ✅ ☑️ 🟢 ✅ | 🔧 | | | [no-callback-literal](docs/rules/no-callback-literal.md) | enforce Node.js-style error-first callback pattern is followed | | | | | [no-deprecated-api](docs/rules/no-deprecated-api.md) | disallow deprecated APIs | ☑️ 🟢 ✅ ☑️ 🟢 ✅ | | | | [no-exports-assign](docs/rules/no-exports-assign.md) | disallow the assignment to `exports` | ☑️ 🟢 ✅ ☑️ 🟢 ✅ | | | @@ -147,7 +148,7 @@ For [Shareable Configs](https://eslint.org/docs/latest/developer-guide/shareable | [prefer-promises/dns](docs/rules/prefer-promises/dns.md) | enforce `require("dns").promises` | | | | | [prefer-promises/fs](docs/rules/prefer-promises/fs.md) | enforce `require("fs").promises` | | | | | [process-exit-as-throw](docs/rules/process-exit-as-throw.md) | require that `process.exit()` expressions use the same code path as `throw` | ☑️ 🟢 ✅ ☑️ 🟢 ✅ | | | -| [shebang](docs/rules/shebang.md) | require correct usage of shebang | ☑️ 🟢 ✅ ☑️ 🟢 ✅ | 🔧 | | +| [shebang](docs/rules/shebang.md) | require correct usage of hashbang | | 🔧 | ❌ | diff --git a/docs/rules/hashbang.md b/docs/rules/hashbang.md new file mode 100644 index 00000000..7e35aa7b --- /dev/null +++ b/docs/rules/hashbang.md @@ -0,0 +1,88 @@ +# Require correct usage of hashbang (`n/hashbang`) + +💼 This rule is enabled in the following [configs](https://github.com/eslint-community/eslint-plugin-n#-configs): ☑️ `flat/recommended`, 🟢 `flat/recommended-module`, ✅ `flat/recommended-script`, ☑️ `recommended`, 🟢 `recommended-module`, ✅ `recommended-script`. + +🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). + + + +When we make a CLI tool with Node.js, we add `bin` field to `package.json`, then we add a hashbang the entry file. +This rule suggests correct usage of hashbang. + +## 📖 Rule Details + +This rule looks up `package.json` file from each linting target file. +Starting from the directory of the target file, it goes up ancestor directories until found. + +If `package.json` was not found, this rule does nothing. + +This rule checks `bin` field of `package.json`, then if a target file matches one of `bin` files, it checks whether or not there is a correct hashbang. +Otherwise it checks whether or not there is not a hashbang. + +The following patterns are considered problems for files in `bin` field of `package.json`: + +```js +console.log("hello"); /*error This file needs hashbang "#!/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 +#!/usr/bin/env node /*error This file needs no hashbang.*/ +console.log("hello"); +``` + +The following patterns are not considered problems for files in `bin` field of `package.json`: + +```js +#!/usr/bin/env node +console.log("hello"); +``` + +The following patterns are not considered problems for other files: + +```js +console.log("hello"); +``` + +### Options + +```json +{ + "n/hashbang": ["error", { + "convertPath": null, + "ignoreUnpublished": false, + "additionalExecutables": [], + }] +} +``` + +#### convertPath + +This can be configured in the rule options or as a shared setting [`settings.convertPath`](../shared-settings.md#convertpath). +Please see the shared settings documentation for more information. + +#### ignoreUnpublished + +Allow for files that are not published to npm to be ignored by this rule. + +#### additionalExecutables + +Mark files as executable that are not referenced by the package.json#bin property + +## 🔎 Implementation + +- [Rule source](../../lib/rules/hashbang.js) +- [Test source](../../tests/lib/rules/hashbang.js) diff --git a/docs/rules/shebang.md b/docs/rules/shebang.md index 126daea5..67739614 100644 --- a/docs/rules/shebang.md +++ b/docs/rules/shebang.md @@ -1,6 +1,6 @@ -# Require correct usage of shebang (`n/shebang`) +# Require correct usage of hashbang (`n/shebang`) -💼 This rule is enabled in the following [configs](https://github.com/eslint-community/eslint-plugin-n#-configs): ☑️ `flat/recommended`, 🟢 `flat/recommended-module`, ✅ `flat/recommended-script`, ☑️ `recommended`, 🟢 `recommended-module`, ✅ `recommended-script`. +❌ This rule is deprecated. It was replaced by [`n/hashbang`](hashbang.md). 🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). diff --git a/lib/configs/_commons.js b/lib/configs/_commons.js index 86ae6d0c..1e2b070d 100644 --- a/lib/configs/_commons.js +++ b/lib/configs/_commons.js @@ -16,6 +16,6 @@ module.exports = { "n/no-unsupported-features/es-syntax": "error", "n/no-unsupported-features/node-builtins": "error", "n/process-exit-as-throw": "error", - "n/shebang": "error", + "n/hashbang": "error", }, } diff --git a/lib/index.js b/lib/index.js index 6497d227..39331a60 100644 --- a/lib/index.js +++ b/lib/index.js @@ -43,10 +43,11 @@ const rules = { "prefer-promises/dns": require("./rules/prefer-promises/dns"), "prefer-promises/fs": require("./rules/prefer-promises/fs"), "process-exit-as-throw": require("./rules/process-exit-as-throw"), - shebang: require("./rules/shebang"), + hashbang: require("./rules/hashbang"), // Deprecated rules. "no-hide-core-modules": require("./rules/no-hide-core-modules"), + shebang: require("./rules/shebang"), } const mod = { diff --git a/lib/rules/hashbang.js b/lib/rules/hashbang.js new file mode 100644 index 00000000..8baee0a0 --- /dev/null +++ b/lib/rules/hashbang.js @@ -0,0 +1,212 @@ +/** + * @author Toru Nagashima + * See LICENSE file in root directory for full license. + */ +"use strict" + +const path = require("path") +const matcher = require("ignore") + +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 +const NODE_SHEBANG_PATTERN = + /^#!\/usr\/bin\/env(?: -\S+)*(?: [^\s=-]+=\S+)* node(?: [^\r\n]+?)?\n/u + +function simulateNodeResolutionAlgorithm(filePath, binField) { + const possibilities = [filePath] + let newFilePath = filePath.replace(/\.js$/u, "") + possibilities.push(newFilePath) + newFilePath = newFilePath.replace(/[/\\]index$/u, "") + possibilities.push(newFilePath) + return possibilities.includes(binField) +} + +/** + * Checks whether or not a given path is a `bin` file. + * + * @param {string} filePath - A file path to check. + * @param {string|object|undefined} binField - A value of the `bin` field of `package.json`. + * @param {string} basedir - A directory path that `package.json` exists. + * @returns {boolean} `true` if the file is a `bin` file. + */ +function isBinFile(filePath, binField, basedir) { + if (!binField) { + return false + } + if (typeof binField === "string") { + return simulateNodeResolutionAlgorithm( + filePath, + path.resolve(basedir, binField) + ) + } + return Object.keys(binField).some(key => + simulateNodeResolutionAlgorithm( + filePath, + path.resolve(basedir, binField[key]) + ) + ) +} + +/** + * Gets the shebang line (includes a line ending) from a given code. + * + * @param {SourceCode} sourceCode - A source code object to check. + * @returns {{length: number, bom: boolean, shebang: string, cr: boolean}} + * shebang's information. + * `retv.shebang` is an empty string if shebang doesn't exist. + */ +function getShebangInfo(sourceCode) { + const m = SHEBANG_PATTERN.exec(sourceCode.text) + + return { + bom: sourceCode.hasBOM, + cr: Boolean(m && m[2]), + length: (m && m[0].length) || 0, + shebang: (m && m[1] && `${m[1]}\n`) || "", + } +} + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: "require correct usage of hashbang", + recommended: true, + url: "https://github.com/eslint-community/eslint-plugin-n/blob/HEAD/docs/rules/hashbang.md", + }, + type: "problem", + fixable: "code", + schema: [ + { + type: "object", + properties: { + convertPath: getConvertPath.schema, + ignoreUnpublished: { type: "boolean" }, + additionalExecutables: { + type: "array", + items: { type: "string" }, + }, + }, + additionalProperties: false, + }, + ], + messages: { + unexpectedBOM: "This file must not have Unicode BOM.", + expectedLF: "This file must have Unix linebreaks (LF).", + expectedHashbangNode: + 'This file needs shebang "#!/usr/bin/env node".', + expectedHashbang: "This file needs no shebang.", + }, + }, + create(context) { + const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 + const filePath = context.filename ?? context.getFilename() + if (filePath === "") { + return {} + } + + const p = getPackageJson(filePath) + if (!p) { + return {} + } + + 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 { additionalExecutables = [] } = context.options?.[0] ?? {} + + const executable = matcher() + executable.add(additionalExecutables) + const isExecutable = executable.test(convertedRelativePath) + + if ( + (additionalExecutables.length === 0 || + isExecutable.ignored === false) && + context.options?.[0]?.ignoreUnpublished === true + ) { + const npmignore = getNpmignore(convertedAbsolutePath) + + if (npmignore.match(convertedRelativePath)) { + return {} + } + } + + const needsShebang = + isExecutable.ignored === true || + isBinFile(convertedAbsolutePath, p.bin, packageDirectory) + const info = getShebangInfo(sourceCode) + + return { + Program() { + const loc = { + start: { line: 1, column: 0 }, + end: { line: 1, column: sourceCode.lines.at(0).length }, + } + + if ( + needsShebang + ? NODE_SHEBANG_PATTERN.test(info.shebang) + : !info.shebang + ) { + // Good the shebang target. + // Checks BOM and \r. + if (needsShebang && info.bom) { + context.report({ + loc, + messageId: "unexpectedBOM", + fix(fixer) { + return fixer.removeRange([-1, 0]) + }, + }) + } + if (needsShebang && info.cr) { + context.report({ + loc, + messageId: "expectedLF", + fix(fixer) { + const index = sourceCode.text.indexOf("\r") + return fixer.removeRange([index, index + 1]) + }, + }) + } + } else if (needsShebang) { + // Shebang is lacking. + context.report({ + loc, + messageId: "expectedHashbangNode", + fix(fixer) { + return fixer.replaceTextRange( + [-1, info.length], + NODE_SHEBANG + ) + }, + }) + } else { + // Shebang is extra. + context.report({ + loc, + messageId: "expectedHashbang", + fix(fixer) { + return fixer.removeRange([0, info.length]) + }, + }) + } + }, + } + }, +} diff --git a/lib/rules/shebang.js b/lib/rules/shebang.js index 0989d50d..86ac0236 100644 --- a/lib/rules/shebang.js +++ b/lib/rules/shebang.js @@ -1,212 +1,18 @@ /** - * @author Toru Nagashima - * See LICENSE file in root directory for full license. + * @fileoverview the rule has been renamed to `hashbang`. Please use `hashbang` instead. + * @deprecated + * @author 唯然 */ "use strict" -const path = require("path") -const matcher = require("ignore") +const hashbang = require("./hashbang.js") -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 -const NODE_SHEBANG_PATTERN = - /^#!\/usr\/bin\/env(?: -\S+)*(?: [^\s=-]+=\S+)* node(?: [^\r\n]+?)?\n/u - -function simulateNodeResolutionAlgorithm(filePath, binField) { - const possibilities = [filePath] - let newFilePath = filePath.replace(/\.js$/u, "") - possibilities.push(newFilePath) - newFilePath = newFilePath.replace(/[/\\]index$/u, "") - possibilities.push(newFilePath) - return possibilities.includes(binField) -} - -/** - * Checks whether or not a given path is a `bin` file. - * - * @param {string} filePath - A file path to check. - * @param {string|object|undefined} binField - A value of the `bin` field of `package.json`. - * @param {string} basedir - A directory path that `package.json` exists. - * @returns {boolean} `true` if the file is a `bin` file. - */ -function isBinFile(filePath, binField, basedir) { - if (!binField) { - return false - } - if (typeof binField === "string") { - return simulateNodeResolutionAlgorithm( - filePath, - path.resolve(basedir, binField) - ) - } - return Object.keys(binField).some(key => - simulateNodeResolutionAlgorithm( - filePath, - path.resolve(basedir, binField[key]) - ) - ) -} - -/** - * Gets the shebang line (includes a line ending) from a given code. - * - * @param {SourceCode} sourceCode - A source code object to check. - * @returns {{length: number, bom: boolean, shebang: string, cr: boolean}} - * shebang's information. - * `retv.shebang` is an empty string if shebang doesn't exist. - */ -function getShebangInfo(sourceCode) { - const m = SHEBANG_PATTERN.exec(sourceCode.text) - - return { - bom: sourceCode.hasBOM, - cr: Boolean(m && m[2]), - length: (m && m[0].length) || 0, - shebang: (m && m[1] && `${m[1]}\n`) || "", - } -} - -/** @type {import('eslint').Rule.RuleModule} */ module.exports = { meta: { - docs: { - description: "require correct usage of shebang", - recommended: true, - url: "https://github.com/eslint-community/eslint-plugin-n/blob/HEAD/docs/rules/shebang.md", - }, - type: "problem", - fixable: "code", - schema: [ - { - type: "object", - properties: { - convertPath: getConvertPath.schema, - ignoreUnpublished: { type: "boolean" }, - additionalExecutables: { - type: "array", - items: { type: "string" }, - }, - }, - additionalProperties: false, - }, - ], - messages: { - unexpectedBOM: "This file must not have Unicode BOM.", - expectedLF: "This file must have Unix linebreaks (LF).", - expectedHashbangNode: - 'This file needs shebang "#!/usr/bin/env node".', - expectedHashbang: "This file needs no shebang.", - }, - }, - create(context) { - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 - const filePath = context.filename ?? context.getFilename() - if (filePath === "") { - return {} - } - - const p = getPackageJson(filePath) - if (!p) { - return {} - } - - 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 { additionalExecutables = [] } = context.options?.[0] ?? {} - - const executable = matcher() - executable.add(additionalExecutables) - const isExecutable = executable.test(convertedRelativePath) - - if ( - (additionalExecutables.length === 0 || - isExecutable.ignored === false) && - context.options?.[0]?.ignoreUnpublished === true - ) { - const npmignore = getNpmignore(convertedAbsolutePath) - - if (npmignore.match(convertedRelativePath)) { - return {} - } - } - - const needsShebang = - isExecutable.ignored === true || - isBinFile(convertedAbsolutePath, p.bin, packageDirectory) - const info = getShebangInfo(sourceCode) - - return { - Program() { - const loc = { - start: { line: 1, column: 0 }, - end: { line: 1, column: sourceCode.lines.at(0).length }, - } - - if ( - needsShebang - ? NODE_SHEBANG_PATTERN.test(info.shebang) - : !info.shebang - ) { - // Good the shebang target. - // Checks BOM and \r. - if (needsShebang && info.bom) { - context.report({ - loc, - messageId: "unexpectedBOM", - fix(fixer) { - return fixer.removeRange([-1, 0]) - }, - }) - } - if (needsShebang && info.cr) { - context.report({ - loc, - messageId: "expectedLF", - fix(fixer) { - const index = sourceCode.text.indexOf("\r") - return fixer.removeRange([index, index + 1]) - }, - }) - } - } else if (needsShebang) { - // Shebang is lacking. - context.report({ - loc, - messageId: "expectedHashbangNode", - fix(fixer) { - return fixer.replaceTextRange( - [-1, info.length], - NODE_SHEBANG - ) - }, - }) - } else { - // Shebang is extra. - context.report({ - loc, - messageId: "expectedHashbang", - fix(fixer) { - return fixer.removeRange([0, info.length]) - }, - }) - } - }, - } + ...hashbang.meta, + deprecated: true, + replacedBy: ["n/hashbang"], + docs: { ...hashbang.meta.docs, recommended: false }, }, + create: hashbang.create, } diff --git a/tests/lib/rules/hashbang.js b/tests/lib/rules/hashbang.js new file mode 100644 index 00000000..f2640679 --- /dev/null +++ b/tests/lib/rules/hashbang.js @@ -0,0 +1,465 @@ +/** + * @author Toru Nagashima + * See LICENSE file in root directory for full license. + */ +"use strict" + +const path = require("path") +const RuleTester = require("#eslint-rule-tester").RuleTester +const rule = require("../../../lib/rules/shebang") + +/** + * Makes a file path to a fixture. + * @param {string} name - A name. + * @returns {string} A file path to a fixture. + */ +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: "string-bin/bin/test.js", + filename: fixture("string-bin/bin/test.js"), + code: "#!/usr/bin/env -S node\nhello();", + }, + { + name: "string-bin/bin/test.js", + filename: fixture("string-bin/bin/test.js"), + code: "#!/usr/bin/env -S node --loader tsm\nhello();", + }, + { + name: "string-bin/bin/test.js", + filename: fixture("string-bin/bin/test.js"), + code: "#!/usr/bin/env --ignore-environment node\nhello();", + }, + { + name: "string-bin/bin/test.js", + filename: fixture("string-bin/bin/test.js"), + code: "#!/usr/bin/env -i -S node --loader tsm\nhello();", + }, + { + name: "string-bin/bin/test.js", + filename: fixture("string-bin/bin/test.js"), + code: "#!/usr/bin/env --block-signal=SIGINT -S FOO=bar node --loader tsm\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();", + }, + { + name: " with shebang", + code: "#!/usr/bin/env node\nhello();", + }, + { + name: " 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 unpublished files are ignored + { + name: "published file cant have shebang", + filename: fixture("unpublished/published.js"), + code: "hello();", + options: [{ ignoreUnpublished: true }], + }, + { + name: "unpublished file can have shebang", + filename: fixture("unpublished/unpublished.js"), + code: "#!/usr/bin/env node\nhello();", + options: [{ ignoreUnpublished: true }], + }, + { + name: "unpublished file can have noshebang", + filename: fixture("unpublished/unpublished.js"), + code: "hello();", + options: [{ ignoreUnpublished: true }], + }, + + { + name: "file matching additionalExecutables", + filename: fixture("unpublished/something.test.js"), + code: "#!/usr/bin/env node\nhello();", + options: [{ additionalExecutables: ["*.test.js"] }], + }, + ], + 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();", + errors: ["This file needs no shebang."], + }, + + // 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();", + errors: ['This file needs shebang "#!/usr/bin/env node".'], + settings: { + node: { convertPath: { "src/**": ["^src/(.+)$", "$1"] } }, + }, + }, + { + 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();", + options: [{ convertPath: { "src/**": ["^src/(.+)$", "$1"] } }], + errors: ["This file needs no shebang."], + }, + + // 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();", + errors: ["This file needs no shebang."], + }, + + // header comments + { + name: "header comments", + filename: fixture("string-bin/bin/test.js"), + 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".'], + }, + { + 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)."], + }, + { + 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: "\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).", + ], + }, + + // 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();", + errors: ["This file needs no shebang."], + }, + + // use node resolution + { + name: "use node resolution", + filename: fixture("object-bin/bin/index.js"), + code: "hello();", + output: "#!/usr/bin/env node\nhello();", + errors: ['This file needs shebang "#!/usr/bin/env node".'], + }, + + // npm unpublished files are ignored + { + name: "unpublished file should not have shebang", + filename: fixture("unpublished/unpublished.js"), + code: "#!/usr/bin/env node\nhello();", + output: "hello();", + errors: ["This file needs no shebang."], + }, + { + name: "published file should have shebang", + filename: fixture("unpublished/published.js"), + code: "#!/usr/bin/env node\nhello();", + output: "hello();", + errors: ["This file needs no shebang."], + }, + + { + name: "unpublished file shebang ignored", + filename: fixture("unpublished/published.js"), + code: "#!/usr/bin/env node\nhello();", + options: [{ ignoreUnpublished: true }], + output: "hello();", + errors: ["This file needs no shebang."], + }, + + { + name: "executable in additionalExecutables without shebang", + filename: fixture("unpublished/something.test.js"), + code: "hello();", + options: [{ additionalExecutables: ["*.test.js"] }], + output: "#!/usr/bin/env node\nhello();", + errors: ['This file needs shebang "#!/usr/bin/env node".'], + }, + { + name: "file not in additionalExecutables with shebang", + filename: fixture("unpublished/not-a-test.js"), + code: "#!/usr/bin/env node\nhello();", + options: [{ additionalExecutables: ["*.test.js"] }], + output: "hello();", + errors: ["This file needs no shebang."], + }, + ], +}) diff --git a/tests/lib/rules/shebang.js b/tests/lib/rules/shebang.js index f2640679..7e096606 100644 --- a/tests/lib/rules/shebang.js +++ b/tests/lib/rules/shebang.js @@ -1,465 +1,11 @@ -/** - * @author Toru Nagashima - * See LICENSE file in root directory for full license. - */ "use strict" +const assert = require("assert/strict") -const path = require("path") -const RuleTester = require("#eslint-rule-tester").RuleTester -const rule = require("../../../lib/rules/shebang") +it("should export shebang as alias ", () => { + const shebang = require("../../../lib/rules/shebang.js") + const hashbang = require("../../../lib/rules/hashbang.js") -/** - * Makes a file path to a fixture. - * @param {string} name - A name. - * @returns {string} A file path to a fixture. - */ -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: "string-bin/bin/test.js", - filename: fixture("string-bin/bin/test.js"), - code: "#!/usr/bin/env -S node\nhello();", - }, - { - name: "string-bin/bin/test.js", - filename: fixture("string-bin/bin/test.js"), - code: "#!/usr/bin/env -S node --loader tsm\nhello();", - }, - { - name: "string-bin/bin/test.js", - filename: fixture("string-bin/bin/test.js"), - code: "#!/usr/bin/env --ignore-environment node\nhello();", - }, - { - name: "string-bin/bin/test.js", - filename: fixture("string-bin/bin/test.js"), - code: "#!/usr/bin/env -i -S node --loader tsm\nhello();", - }, - { - name: "string-bin/bin/test.js", - filename: fixture("string-bin/bin/test.js"), - code: "#!/usr/bin/env --block-signal=SIGINT -S FOO=bar node --loader tsm\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();", - }, - { - name: " with shebang", - code: "#!/usr/bin/env node\nhello();", - }, - { - name: " 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 unpublished files are ignored - { - name: "published file cant have shebang", - filename: fixture("unpublished/published.js"), - code: "hello();", - options: [{ ignoreUnpublished: true }], - }, - { - name: "unpublished file can have shebang", - filename: fixture("unpublished/unpublished.js"), - code: "#!/usr/bin/env node\nhello();", - options: [{ ignoreUnpublished: true }], - }, - { - name: "unpublished file can have noshebang", - filename: fixture("unpublished/unpublished.js"), - code: "hello();", - options: [{ ignoreUnpublished: true }], - }, - - { - name: "file matching additionalExecutables", - filename: fixture("unpublished/something.test.js"), - code: "#!/usr/bin/env node\nhello();", - options: [{ additionalExecutables: ["*.test.js"] }], - }, - ], - 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();", - errors: ["This file needs no shebang."], - }, - - // 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();", - errors: ['This file needs shebang "#!/usr/bin/env node".'], - settings: { - node: { convertPath: { "src/**": ["^src/(.+)$", "$1"] } }, - }, - }, - { - 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();", - options: [{ convertPath: { "src/**": ["^src/(.+)$", "$1"] } }], - errors: ["This file needs no shebang."], - }, - - // 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();", - errors: ["This file needs no shebang."], - }, - - // header comments - { - name: "header comments", - filename: fixture("string-bin/bin/test.js"), - 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".'], - }, - { - 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)."], - }, - { - 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: "\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).", - ], - }, - - // 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();", - errors: ["This file needs no shebang."], - }, - - // use node resolution - { - name: "use node resolution", - filename: fixture("object-bin/bin/index.js"), - code: "hello();", - output: "#!/usr/bin/env node\nhello();", - errors: ['This file needs shebang "#!/usr/bin/env node".'], - }, - - // npm unpublished files are ignored - { - name: "unpublished file should not have shebang", - filename: fixture("unpublished/unpublished.js"), - code: "#!/usr/bin/env node\nhello();", - output: "hello();", - errors: ["This file needs no shebang."], - }, - { - name: "published file should have shebang", - filename: fixture("unpublished/published.js"), - code: "#!/usr/bin/env node\nhello();", - output: "hello();", - errors: ["This file needs no shebang."], - }, - - { - name: "unpublished file shebang ignored", - filename: fixture("unpublished/published.js"), - code: "#!/usr/bin/env node\nhello();", - options: [{ ignoreUnpublished: true }], - output: "hello();", - errors: ["This file needs no shebang."], - }, - - { - name: "executable in additionalExecutables without shebang", - filename: fixture("unpublished/something.test.js"), - code: "hello();", - options: [{ additionalExecutables: ["*.test.js"] }], - output: "#!/usr/bin/env node\nhello();", - errors: ['This file needs shebang "#!/usr/bin/env node".'], - }, - { - name: "file not in additionalExecutables with shebang", - filename: fixture("unpublished/not-a-test.js"), - code: "#!/usr/bin/env node\nhello();", - options: [{ additionalExecutables: ["*.test.js"] }], - output: "hello();", - errors: ["This file needs no shebang."], - }, - ], + assert.strictEqual(shebang.meta.deprecated, true) + assert.deepStrictEqual(shebang.meta.replacedBy, ["n/hashbang"]) + assert.strictEqual(shebang.create, hashbang.create) })