@@ -134,10 +134,6 @@ module.exports = {
134
134
} ,
135
135
] ,
136
136
} ,
137
- messages : {
138
- missingExtension : 'Missing file extension for "{{importPath}}" (expected {{expected}}).' ,
139
- unexpectedExtension : 'Unexpected use of file extension "{{extension}}" for "{{importPath}}"' ,
140
- } ,
141
137
} ,
142
138
143
139
create ( context ) {
@@ -198,7 +194,9 @@ module.exports = {
198
194
if ( ! source || ! source . value ) { return ; }
199
195
200
196
const importPathWithQueryString = source . value ;
197
+ const hasQuery = importPathWithQueryString . includes ( '?' ) ;
201
198
const currentDir = path . dirname ( context . getFilename ( ) ) ;
199
+ const isRelative = importPathWithQueryString . startsWith ( '.' ) ;
202
200
203
201
// If not undefined, the user decided if rules are enforced on this import
204
202
const overrideAction = computeOverrideAction (
@@ -210,59 +208,86 @@ module.exports = {
210
208
return ;
211
209
}
212
210
213
- // don't enforce anything on builtins
214
211
if ( ! overrideAction && isBuiltIn ( importPathWithQueryString , context . settings ) ) { return ; }
215
212
216
213
const importPath = importPathWithQueryString . replace ( / \? ( .* ) $ / , '' ) ;
217
-
218
- // don't enforce in root external packages as they may have names with `.js`.
219
- // Like `import Decimal from decimal.js`)
220
- if ( ! overrideAction && isExternalRootModule ( importPath ) ) { return ; }
214
+ if ( ! overrideAction && isExternalRootModule ( importPath ) && ! ( props . checkTypeImports && ( node . importKind === 'type' || node . exportKind === 'type' ) ) ) { return ; }
221
215
222
216
const resolvedPath = resolve ( importPath , context ) ;
217
+ const isPackage = isExternalModule ( importPath , resolvedPath , context ) || isScoped ( importPath ) ;
223
218
const extensionWithDot = path . extname ( resolvedPath || importPath ) ;
219
+ const extension = extensionWithDot . slice ( 1 ) ;
224
220
225
- // determine if this is a module
226
- const isPackage = isExternalModule (
227
- importPath ,
228
- resolve ( importPath , context ) ,
229
- context ,
230
- ) || isScoped ( importPath ) ;
221
+ const sourceCode = context . getSourceCode ( ) ;
222
+ const fileHasExports = sourceCode . ast . body . some ( ( n ) => n . type . indexOf ( 'Export' ) === 0 ) ;
223
+ const isExport = node && node . type && node . type . indexOf ( 'Export' ) === 0 ;
224
+ const isImportDeclaration = node && node . type === 'ImportDeclaration' ;
231
225
232
- // Case 1: Missing extension.
233
226
if ( ! extensionWithDot || ! importPath . endsWith ( extensionWithDot ) ) {
234
227
// ignore type-only imports and exports
235
228
if ( ! props . checkTypeImports && ( node . importKind === 'type' || node . exportKind === 'type' ) ) { return ; }
236
- const candidate = getCandidateExtension ( importPath , currentDir ) ;
237
- if ( candidate && isUseOfExtensionRequired ( candidate . replace ( / ^ \. / , '' ) , isPackage ) ) {
238
- context . report ( {
239
- node,
240
- message :
241
- `Missing file extension for "${ importPathWithQueryString } "` ,
242
- data : {
243
- importPath : importPathWithQueryString ,
244
- expected : candidate ,
245
- } ,
246
- fix ( fixer ) {
247
- return fixer . replaceText ( source , JSON . stringify ( importPathWithQueryString + candidate ) ) ;
248
- } ,
249
- } ) ;
229
+ let candidate = getCandidateExtension ( importPath , currentDir ) ;
230
+ if ( ! candidate && isUseOfExtensionRequired ( 'js' , isPackage ) ) { candidate = '.js' ; }
231
+ if ( candidate && isUseOfExtensionRequired ( candidate . slice ( 1 ) , isPackage ) ) {
232
+ if ( isExport || hasQuery || ! isImportDeclaration && fileHasExports || ! Object . prototype . hasOwnProperty . call (
233
+ props . pattern ,
234
+ candidate . slice ( 1 ) ,
235
+ ) || ! isRelative || isPackage ) {
236
+ context . report ( {
237
+ node : source ,
238
+ message : `Missing file extension ${ extension ? `"${ extension } " ` : '' } for "${ importPathWithQueryString } "` ,
239
+ data : {
240
+ importPath : importPathWithQueryString ,
241
+ expected : candidate ,
242
+ } ,
243
+ } ) ;
244
+ } else {
245
+ context . report ( {
246
+ node : source ,
247
+ message : `Missing file extension ${ extension ? `"${ extension } " ` : '' } for "${ importPathWithQueryString } "` ,
248
+ data : {
249
+ importPath : importPathWithQueryString ,
250
+ expected : candidate ,
251
+ } ,
252
+ fix ( fixer ) {
253
+ return fixer . replaceText (
254
+ source ,
255
+ JSON . stringify ( importPathWithQueryString + candidate ) ,
256
+ ) ;
257
+ } ,
258
+ } ) ;
259
+ }
250
260
}
251
261
} else {
252
262
// Case 2: Unexpected extension provided.
253
- const extension = extensionWithDot . slice ( 1 ) ;
254
263
if ( isUseOfExtensionForbidden ( extension ) && isResolvableWithoutExtension ( importPath , extension ) ) {
255
- context . report ( {
256
- node : source ,
257
- message : `Unexpected use of file extension "${ extension } " for "${ importPathWithQueryString } "` ,
258
- data : {
259
- extension,
260
- importPath : importPathWithQueryString ,
261
- } ,
262
- fix ( fixer ) {
263
- return fixer . replaceText ( source , JSON . stringify ( importPath . slice ( 0 , - extensionWithDot . length ) ) ) ;
264
- } ,
265
- } ) ;
264
+ if ( isExport || hasQuery || ! isImportDeclaration && fileHasExports || ! Object . prototype . hasOwnProperty . call ( props . pattern , extension ) || ! isRelative || isPackage ) {
265
+ context . report ( {
266
+ node : source ,
267
+ message : `Unexpected use of file extension "${ extension } " for "${ importPathWithQueryString } "` ,
268
+ data : {
269
+ extension,
270
+ importPath : importPathWithQueryString ,
271
+ } ,
272
+ } ) ;
273
+ } else {
274
+ context . report ( {
275
+ node : source ,
276
+ message : `Unexpected use of file extension "${ extension } " for "${ importPathWithQueryString } "` ,
277
+ data : {
278
+ extension,
279
+ importPath : importPathWithQueryString ,
280
+ } ,
281
+ fix ( fixer ) {
282
+ return fixer . replaceText (
283
+ source ,
284
+ JSON . stringify (
285
+ importPath . slice ( 0 , - extensionWithDot . length ) ,
286
+ ) ,
287
+ ) ;
288
+ } ,
289
+ } ) ;
290
+ }
266
291
}
267
292
}
268
293
}
0 commit comments