5
5
"use strict"
6
6
7
7
const path = require ( "path" )
8
+ const matcher = require ( "ignore" )
9
+
8
10
const getConvertPath = require ( "../util/get-convert-path" )
9
11
const getPackageJson = require ( "../util/get-package-json" )
12
+ const getNpmignore = require ( "../util/get-npmignore" )
10
13
11
14
const NODE_SHEBANG = "#!/usr/bin/env node\n"
12
15
const SHEBANG_PATTERN = / ^ ( # ! .+ ?) ? ( \r ) ? \n / u
@@ -66,6 +69,7 @@ function getShebangInfo(sourceCode) {
66
69
}
67
70
}
68
71
72
+ /** @type {import('eslint').Rule.RuleModule } */
69
73
module . exports = {
70
74
meta : {
71
75
docs : {
@@ -79,8 +83,12 @@ module.exports = {
79
83
{
80
84
type : "object" ,
81
85
properties : {
82
- //
83
86
convertPath : getConvertPath . schema ,
87
+ ignoreUnpublished : { type : "boolean" } ,
88
+ additionalExecutables : {
89
+ type : "array" ,
90
+ items : { type : "string" } ,
91
+ } ,
84
92
} ,
85
93
additionalProperties : false ,
86
94
} ,
@@ -95,30 +103,60 @@ module.exports = {
95
103
} ,
96
104
create ( context ) {
97
105
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 ( )
99
107
if ( filePath === "<input>" ) {
100
108
return { }
101
109
}
102
- filePath = path . resolve ( filePath )
103
110
104
111
const p = getPackageJson ( filePath )
105
112
if ( ! p ) {
106
113
return { }
107
114
}
108
115
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
115
128
)
116
129
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 )
118
151
const info = getShebangInfo ( sourceCode )
119
152
120
153
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
+
122
160
if (
123
161
needsShebang
124
162
? NODE_SHEBANG_PATTERN . test ( info . shebang )
@@ -128,7 +166,7 @@ module.exports = {
128
166
// Checks BOM and \r.
129
167
if ( needsShebang && info . bom ) {
130
168
context . report ( {
131
- node ,
169
+ loc ,
132
170
messageId : "unexpectedBOM" ,
133
171
fix ( fixer ) {
134
172
return fixer . removeRange ( [ - 1 , 0 ] )
@@ -137,7 +175,7 @@ module.exports = {
137
175
}
138
176
if ( needsShebang && info . cr ) {
139
177
context . report ( {
140
- node ,
178
+ loc ,
141
179
messageId : "expectedLF" ,
142
180
fix ( fixer ) {
143
181
const index = sourceCode . text . indexOf ( "\r" )
@@ -148,7 +186,7 @@ module.exports = {
148
186
} else if ( needsShebang ) {
149
187
// Shebang is lacking.
150
188
context . report ( {
151
- node ,
189
+ loc ,
152
190
messageId : "expectedHashbangNode" ,
153
191
fix ( fixer ) {
154
192
return fixer . replaceTextRange (
@@ -160,7 +198,7 @@ module.exports = {
160
198
} else {
161
199
// Shebang is extra.
162
200
context . report ( {
163
- node ,
201
+ loc ,
164
202
messageId : "expectedHashbang" ,
165
203
fix ( fixer ) {
166
204
return fixer . removeRange ( [ 0 , info . length ] )
0 commit comments