123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- /**
- * @author Toru Nagashima
- * See LICENSE file in root directory for full license.
- */
- "use strict"
- const path = require("path")
- const getConvertPath = require("../util/get-convert-path")
- const getPackageJson = require("../util/get-package-json")
- const NODE_SHEBANG = "#!/usr/bin/env node\n"
- const SHEBANG_PATTERN = /^(#!.+?)?(\r)?\n/u
- const NODE_SHEBANG_PATTERN = /#!\/usr\/bin\/env 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`) || "",
- }
- }
- module.exports = {
- meta: {
- docs: {
- description: "suggest correct usage of shebang",
- category: "Possible Errors",
- recommended: true,
- url:
- "https://github.com/mysticatea/eslint-plugin-node/blob/v11.1.0/docs/rules/shebang.md",
- },
- type: "problem",
- fixable: "code",
- schema: [
- {
- type: "object",
- properties: {
- //
- convertPath: getConvertPath.schema,
- },
- additionalProperties: false,
- },
- ],
- },
- create(context) {
- const sourceCode = context.getSourceCode()
- let filePath = 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 needsShebang = isBinFile(filePath, p.bin, basedir)
- const info = getShebangInfo(sourceCode)
- return {
- Program(node) {
- if (
- needsShebang
- ? NODE_SHEBANG_PATTERN.test(info.shebang)
- : !info.shebang
- ) {
- // Good the shebang target.
- // Checks BOM and \r.
- if (needsShebang && info.bom) {
- context.report({
- node,
- message: "This file must not have Unicode BOM.",
- fix(fixer) {
- return fixer.removeRange([-1, 0])
- },
- })
- }
- if (needsShebang && info.cr) {
- context.report({
- node,
- message:
- "This file must have Unix linebreaks (LF).",
- fix(fixer) {
- const index = sourceCode.text.indexOf("\r")
- return fixer.removeRange([index, index + 1])
- },
- })
- }
- } else if (needsShebang) {
- // Shebang is lacking.
- context.report({
- node,
- message:
- 'This file needs shebang "#!/usr/bin/env node".',
- fix(fixer) {
- return fixer.replaceTextRange(
- [-1, info.length],
- NODE_SHEBANG
- )
- },
- })
- } else {
- // Shebang is extra.
- context.report({
- node,
- message: "This file needs no shebang.",
- fix(fixer) {
- return fixer.removeRange([0, info.length])
- },
- })
- }
- },
- }
- },
- }
|