@@ -134,10 +134,6 @@ module.exports = {
134134        } , 
135135      ] , 
136136    } , 
137-     messages : { 
138-       missingExtension : 'Missing file extension for "{{importPath}}" (expected {{expected}}).' , 
139-       unexpectedExtension : 'Unexpected use of file extension "{{extension}}" for "{{importPath}}"' , 
140-     } , 
141137  } , 
142138
143139  create ( context )  { 
@@ -156,7 +152,6 @@ module.exports = {
156152      return  getModifier ( extension )  ===  'never' ; 
157153    } 
158154
159-     // If the configured option for the extension is "never", we return true immediately. 
160155    function  isResolvableWithoutExtension ( file ,  ext )  { 
161156      if  ( isUseOfExtensionForbidden ( ext ) )  { 
162157        return  true ; 
@@ -198,7 +193,9 @@ module.exports = {
198193      if  ( ! source  ||  ! source . value )  {  return ;  } 
199194
200195      const  importPathWithQueryString  =  source . value ; 
196+       const  hasQuery  =  importPathWithQueryString . includes ( '?' ) ; 
201197      const  currentDir  =  path . dirname ( context . getFilename ( ) ) ; 
198+       const  isRelative  =  importPathWithQueryString . startsWith ( '.' ) ; 
202199
203200      // If not undefined, the user decided if rules are enforced on this import 
204201      const  overrideAction  =  computeOverrideAction ( 
@@ -210,59 +207,85 @@ module.exports = {
210207        return ; 
211208      } 
212209
213-       // don't enforce anything on builtins 
214210      if  ( ! overrideAction  &&  isBuiltIn ( importPathWithQueryString ,  context . settings ) )  {  return ;  } 
215211
216212      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 ;  } 
213+       if  ( ! overrideAction  &&  isExternalRootModule ( importPath )  &&  ! ( props . checkTypeImports  &&  ( node . importKind  ===  'type'  ||  node . exportKind  ===  'type' ) ) )  {  return ;  } 
221214
222215      const  resolvedPath  =  resolve ( importPath ,  context ) ; 
223-       const  extensionWithDot  =  path . extname ( resolvedPath  ||  importPath ) ; 
216+       const  isPackage  =  isExternalModule ( importPath ,  resolvedPath ,  context )  ||  isScoped ( importPath ) ; 
217+       const  extension  =  path . extname ( resolvedPath  ||  importPath ) . slice ( 1 ) ; 
224218
225-       // determine if this is a module 
226-       const  isPackage  =  isExternalModule ( 
227-         importPath , 
228-         resolve ( importPath ,  context ) , 
229-         context , 
230-       )  ||  isScoped ( importPath ) ; 
219+       const  sourceCode  =  context . getSourceCode ( ) ; 
220+       const  fileHasExports  =  sourceCode . ast . body . some ( ( n )  =>  n . type . indexOf ( 'Export' )  ===  0 ) ; 
221+       const  isExport  =  node  &&  node . type  &&  node . type . indexOf ( 'Export' )  ===  0 ; 
222+       const  isImportDeclaration  =  node  &&  node . type  ===  'ImportDeclaration' ; 
231223
232-       // Case 1: Missing extension. 
233-       if  ( ! extensionWithDot  ||  ! importPath . endsWith ( extensionWithDot ) )  { 
224+       if  ( ! extension  ||  ! importPath . endsWith ( `.${ extension }  ` ) )  { 
234225        // ignore type-only imports and exports 
235226        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-           } ) ; 
227+         let  candidate  =  getCandidateExtension ( importPath ,  currentDir ) ; 
228+         if  ( ! candidate  &&  isUseOfExtensionRequired ( 'js' ,  isPackage ) )  {  candidate  =  '.js' ;  } 
229+         if  ( candidate  &&  isUseOfExtensionRequired ( candidate . slice ( 1 ) ,  isPackage ) )  { 
230+           if  ( isExport  ||  hasQuery  ||  ! isImportDeclaration  &&  fileHasExports  ||  ! Object . prototype . hasOwnProperty . call ( 
231+             props . pattern , 
232+             candidate . slice ( 1 ) , 
233+           )  ||  ! isRelative  ||  isPackage )  { 
234+             context . report ( { 
235+               node : source , 
236+               message : `Missing file extension ${ extension  ? `"${ extension }  " `  : '' }  for "${ importPathWithQueryString }  "` , 
237+               data : { 
238+                 importPath : importPathWithQueryString , 
239+                 expected : candidate , 
240+               } , 
241+             } ) ; 
242+           }  else  { 
243+             context . report ( { 
244+               node : source , 
245+               message : `Missing file extension ${ extension  ? `"${ extension }  " `  : '' }  for "${ importPathWithQueryString }  "` , 
246+               data : { 
247+                 importPath : importPathWithQueryString , 
248+                 expected : candidate , 
249+               } , 
250+               fix ( fixer )  { 
251+                 return  fixer . replaceText ( 
252+                   source , 
253+                   JSON . stringify ( importPathWithQueryString  +  candidate ) , 
254+                 ) ; 
255+               } , 
256+             } ) ; 
257+           } 
250258        } 
251259      }  else  { 
252260        // Case 2: Unexpected extension provided. 
253-         const  extension  =  extensionWithDot . slice ( 1 ) ; 
254261        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-           } ) ; 
262+           if  ( isExport  ||  hasQuery  ||  ! isImportDeclaration  &&  fileHasExports  ||  ! Object . prototype . hasOwnProperty . call ( props . pattern ,  extension )  ||  ! isRelative  ||  isPackage )  { 
263+             context . report ( { 
264+               node : source , 
265+               message : `Unexpected use of file extension "${ extension }  " for "${ importPathWithQueryString }  "` , 
266+               data : { 
267+                 extension, 
268+                 importPath : importPathWithQueryString , 
269+               } , 
270+             } ) ; 
271+           }  else  { 
272+             context . report ( { 
273+               node : source , 
274+               message : `Unexpected use of file extension "${ extension }  " for "${ importPathWithQueryString }  "` , 
275+               data : { 
276+                 extension, 
277+                 importPath : importPathWithQueryString , 
278+               } , 
279+               fix ( fixer )  { 
280+                 return  fixer . replaceText ( 
281+                   source , 
282+                   JSON . stringify ( 
283+                     importPath . slice ( 0 ,  - ( extension . length  +  1 ) ) , 
284+                   ) , 
285+                 ) ; 
286+               } , 
287+             } ) ; 
288+           } 
266289        } 
267290      } 
268291    } 
0 commit comments