import-js / eslint-import-resolver-typescript

This plugin adds `TypeScript` support to `eslint-plugin-import`
https://www.npmjs.com/package/eslint-import-resolver-typescript
ISC License
698 stars 61 forks source link

`import/extensions`, `import/no-unresolved` don't highlight import of nonexistent .js #291

Open jwbth opened 2 weeks ago

jwbth commented 2 weeks ago

(Original issue is posted at https://github.com/import-js/eslint-plugin-import/issues/3015.)

Take this simple setup:

eslint.config.js

import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import * as pluginImport from 'eslint-plugin-import';

export default [
  eslint.configs.recommended,
  ...tseslint.configs.recommended,
  {
    files: ['**/*.ts'],
    plugins: {
      'import': pluginImport,
    },
    settings: {
      'import/resolver': {
        node: true,
        typescript: true,
      },
    },
    rules: {
      'import/extensions': 'error',
      'import/no-unresolved': 'error',
    },
  },
];

index.ts

import { num } from './exported.js';

exported.ts

export const num = 3;

In index.ts, ./exported.js points to a nonexistent file. Neither import/extensions, nor import/no-unresolved highlight it though.

I narrowed down the problem to typescript: true – as soon as I comment that line, I see both errors:

Unexpected use of file extension "js" for "./exported.js" eslint(import/extensions) Unable to resolve path to module './exported.js'. eslint(import/no-unresolved)

I assume this is the problem with eslint-import-resolver-typescript. I tried messing with it in some ways, but to no avail.

I should also note that the highlight will appear also if:

So, this seems to be a pretty unique case with a nonexistent .js silently mapping to an existent .ts with the same basename.

turbocrime commented 1 week ago

I'm experiencing this same issue. Unfortunately, I think this behavior may be technically correct, by present state of the longstanding conflict between typescript and node about module resolution.

https://github.com/microsoft/TypeScript/issues/13422 https://github.com/typescript-eslint/typescript-eslint/issues/3288

jwbth commented 1 week ago

@turbocrime Well, it brings real harm, because Webpack can't resolve it and can't build. (On the other hand, there is strange behavior in VSCode that adds this .js to auto-added imports when the first import has .js or something, see stackoverflow.)

I suppose the point of ESLint is to prevent this type of situations and be literal in terms of what it is asked to consider an error. And please see the list "the highlight will appear also if" at the end of my post – it demonstrates that, even if this kind of resolving is correct for some reason, it is not consistent.

turbocrime commented 1 week ago

part of the situation is you're using three independent resolvers

  1. typescript's moduleResolution as configured in your tsconfig
  2. this resolver which attempts to respect tsconfig, but is used by the linter with extension lint rules that contradict tsconfig
  3. webpack's independent module resolution which also needs configuration

you could configure your lint rules to permit .js extensions to refer to .ts imports. i understand this is awkward, but typescript devs seem to encourage this.

if you are just bundling this code with webpack, you may be interested in tsconfig's "bundler" moduleResolution which is more permissive with file extensions. then configure this resolver's search paths to match your webpack config, so they both import a .ts extension for a bare name. you'll need to pass a config object instead of true for settings.import/resolver.typescript

if you are using or publishing bare tsc output anywhere, you probably don't want to use bundler resolution.

jwbth commented 1 week ago

Thanks for the detailed response. At this stage I just configured VS Code to prevent it from inserting .js when not asked; that solved it for me.