55"use strict"
66
77const path = require ( "path" )
8+ const matcher = require ( "ignore" )
9+
810const getConvertPath = require ( "../util/get-convert-path" )
911const getPackageJson = require ( "../util/get-package-json" )
12+ const getNpmignore = require ( "../util/get-npmignore" )
1013
1114const NODE_SHEBANG = "#!/usr/bin/env node\n"
1215const SHEBANG_PATTERN = / ^ ( # ! .+ ?) ? ( \r ) ? \n / u
@@ -66,6 +69,7 @@ function getShebangInfo(sourceCode) {
6669 }
6770}
6871
72+ /** @type {import('eslint').Rule.RuleModule } */
6973module . exports = {
7074 meta : {
7175 docs : {
@@ -79,8 +83,12 @@ module.exports = {
7983 {
8084 type : "object" ,
8185 properties : {
82- //
8386 convertPath : getConvertPath . schema ,
87+ ignoreUnpublished : { type : "boolean" } ,
88+ additionalExecutables : {
89+ type : "array" ,
90+ items : { type : "string" } ,
91+ } ,
8492 } ,
8593 additionalProperties : false ,
8694 } ,
@@ -95,30 +103,60 @@ module.exports = {
95103 } ,
96104 create ( context ) {
97105 const sourceCode = context . sourceCode ?? context . getSourceCode ( ) // TODO: just use context.sourceCode when dropping eslint < v9
98- let filePath = context . filename ?? context . getFilename ( )
106+ const filePath = context . filename ?? context . getFilename ( )
99107 if ( filePath === "<input>" ) {
100108 return { }
101109 }
102- filePath = path . resolve ( filePath )
103110
104111 const p = getPackageJson ( filePath )
105112 if ( ! p ) {
106113 return { }
107114 }
108115
109- const basedir = path . dirname ( p . filePath )
110- filePath = path . join (
111- basedir ,
112- getConvertPath ( context ) (
113- path . relative ( basedir , filePath ) . replace ( / \\ / gu, "/" )
114- )
116+ const packageDirectory = path . dirname ( p . filePath )
117+
118+ const originalAbsolutePath = path . resolve ( filePath )
119+ const originalRelativePath = path
120+ . relative ( packageDirectory , originalAbsolutePath )
121+ . replace ( / \\ / gu, "/" )
122+
123+ const convertedRelativePath =
124+ getConvertPath ( context ) ( originalRelativePath )
125+ const convertedAbsolutePath = path . resolve (
126+ packageDirectory ,
127+ convertedRelativePath
115128 )
116129
117- const needsShebang = isBinFile ( filePath , p . bin , basedir )
130+ const { additionalExecutables = [ ] } = context . options ?. [ 0 ] ?? { }
131+
132+ const executable = matcher ( )
133+ executable . add ( additionalExecutables )
134+ const isExecutable = executable . test ( convertedRelativePath )
135+
136+ if (
137+ ( additionalExecutables . length === 0 ||
138+ isExecutable . ignored === false ) &&
139+ context . options ?. [ 0 ] ?. ignoreUnpublished === true
140+ ) {
141+ const npmignore = getNpmignore ( convertedAbsolutePath )
142+
143+ if ( npmignore . match ( convertedRelativePath ) ) {
144+ return { }
145+ }
146+ }
147+
148+ const needsShebang =
149+ isExecutable . ignored === true ||
150+ isBinFile ( convertedAbsolutePath , p . bin , packageDirectory )
118151 const info = getShebangInfo ( sourceCode )
119152
120153 return {
121- Program ( node ) {
154+ Program ( ) {
155+ const loc = {
156+ start : { line : 1 , column : 0 } ,
157+ end : { line : 1 , column : sourceCode . lines . at ( 0 ) . length } ,
158+ }
159+
122160 if (
123161 needsShebang
124162 ? NODE_SHEBANG_PATTERN . test ( info . shebang )
@@ -128,7 +166,7 @@ module.exports = {
128166 // Checks BOM and \r.
129167 if ( needsShebang && info . bom ) {
130168 context . report ( {
131- node ,
169+ loc ,
132170 messageId : "unexpectedBOM" ,
133171 fix ( fixer ) {
134172 return fixer . removeRange ( [ - 1 , 0 ] )
@@ -137,7 +175,7 @@ module.exports = {
137175 }
138176 if ( needsShebang && info . cr ) {
139177 context . report ( {
140- node ,
178+ loc ,
141179 messageId : "expectedLF" ,
142180 fix ( fixer ) {
143181 const index = sourceCode . text . indexOf ( "\r" )
@@ -148,7 +186,7 @@ module.exports = {
148186 } else if ( needsShebang ) {
149187 // Shebang is lacking.
150188 context . report ( {
151- node ,
189+ loc ,
152190 messageId : "expectedHashbangNode" ,
153191 fix ( fixer ) {
154192 return fixer . replaceTextRange (
@@ -160,7 +198,7 @@ module.exports = {
160198 } else {
161199 // Shebang is extra.
162200 context . report ( {
163- node ,
201+ loc ,
164202 messageId : "expectedHashbang" ,
165203 fix ( fixer ) {
166204 return fixer . removeRange ( [ 0 , info . length ] )
0 commit comments