Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
jwbth opened this issue Jun 16, 2024 · 6 comments

Comments

@jwbth
Copy link

jwbth commented Jun 16, 2024

(Original issue is posted at import-js/eslint-plugin-import#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:

  • I change the base name to one that has no corresponding .ts filename:

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

  • I change the extension to any other:

    Unable to resolve path to module './exported.jsx'. eslint(import/no-unresolved)

  • I rename exported.ts to exported.js so that the import exists

    Unexpected use of file extension "js" for "./exported.js". eslint(import/extensions)

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

@turbocrime
Copy link

turbocrime commented Jun 22, 2024

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.

microsoft/TypeScript#13422
typescript-eslint/typescript-eslint#3288

@jwbth
Copy link
Author

jwbth commented Jun 22, 2024

@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
Copy link

turbocrime commented Jun 22, 2024

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
Copy link
Author

jwbth commented Jun 22, 2024

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.

@AndreaPontrandolfo
Copy link

Sorry if this is a tangent, but what's the benefit of using import/no-unresolved in Typescript projects? Typescript already handle unresolved imports with it's own warnings.

@jwbth
Copy link
Author

jwbth commented Aug 21, 2024

@AndreaPontrandolfo TypeScript warnings for .js (and only .js) are absent for me just as ESLint warnings. For explicitness, this is my tsconfig.json for this particular test:

{
  "compilerOptions": {
    "outDir": "./built",
    "target": "es2022",
    "moduleResolution": "Bundler"
  }
}

And as I said,

it brings real harm, because Webpack can't resolve it and can't build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants