oxc icon indicating copy to clipboard operation
oxc copied to clipboard

linter: eslint-plugin-import(extensions) false positives with missing extension

Open drvn-mr opened this issue 6 months ago • 11 comments

What version of Oxlint are you using?

1.6.0

What command did you run?

oxlint -c .oxlintrc.json test.ts

What does your .oxlintrc.json config file look like?

{
  "env": {
    "browser": true,
    "node": true
  },
  "plugins": [
    "import"
  ],
  "rules": {
    "import/extensions": [
      "error",
      "ignorePackages",
      {
        "js": "never",
        "ts": "never"
      }
    ]
  }
}

What happened?

Import extensions are always required for non-package paths (relative, aliased) since #11872. With ESLint and eslint-plugin-import, there are no errors when extension is missing.

Continuing from #11818.

Code

import { Package } from 'vue'; // OK, ignorePackages
import { A } from './something.hooks'; // OK
import { B } from './something.ts'; // OK, File extension "ts" should not be included in the import declaration.

import { C } from './something'; // Error, Missing file extension in import declaration
import { D } from '~/common/something'; // Error, Missing file extension in import declaration

oxlint output

❯ oxlint -c .oxlintrc.json test.ts

  × eslint-plugin-import(extensions): Missing file extension in import declaration
   ╭─[test.ts:4:1]
 3 │ 
 4 │ import { C } from './something'; // Error, Missing file extension in import declaration
   · ────────────────────────────────
 5 │ import { D } from '~/common/something'; // Error, Missing file extension in import declaration
   ╰────
  help: Add a file extension to this import.

  × eslint-plugin-import(extensions): File extension "ts" should not be included in the import declaration.
   ╭─[test.ts:2:1]
 1 │ import { A } from './something.hooks'; // OK
 2 │ import { B } from './something.ts'; // OK, File extension "ts" should not be included in the import declaration.
   · ───────────────────────────────────
 3 │ 
   ╰────
  help: Remove the file extension from this import.

  × eslint-plugin-import(extensions): Missing file extension in import declaration
   ╭─[test.ts:5:1]
 4 │ import { C } from './something'; // Error, Missing file extension in import declaration
 5 │ import { D } from '~/common/something'; // Error, Missing file extension in import declaration
   · ───────────────────────────────────────
   ╰────
  help: Add a file extension to this import.

drvn-mr avatar Jul 11 '25 13:07 drvn-mr

I don't really understand why the ESLint plugin allows these when the rule is described as such:

If it is the string "ignorePackages", then the rule enforces the use of extensions for all import statements except package imports.

So maybe oxlint is doing the right thing but it's a different behaviour in our setup.

It might also be caused by aliases but it also ignores missing extensions for relative imports.

Unfortunately, in more advanced linting setups, such as when employing custom specifier aliases (e.g. you're using eslint-import-resolver-alias, paths in tsconfig.json, etc), this rule can be too coarse-grained when determining which imports to ignore and on which to enforce the config

drvn-mr avatar Jul 11 '25 13:07 drvn-mr

I can't really seem to reproduce the issue in our project anymore.

drvn-mr avatar Sep 12 '25 14:09 drvn-mr

I am experiencing this issue, was this bug reintroduced somehow?

sgarfinkel avatar Sep 23 '25 19:09 sgarfinkel

I'll reopen because it still happens occasionally. The basics seem to be that oxlint always expects an extension while ESLint is more lax about it since it has resolving capabilities.

drvn-mr avatar Sep 23 '25 20:09 drvn-mr

For now I’ve just setup my oxlint/eslint config to handle this rule with eslint but obviously that’s not ideal.

sgarfinkel avatar Sep 24 '25 14:09 sgarfinkel

The option ignorePackages seems like to not be used.

For example, I've got the issue with the following import:

import { fn } from "storybook/test"

theoludwig avatar Sep 25 '25 06:09 theoludwig

We want to enforce usage of extensions to follow module resolution standard, and have false positives:

"import/extensions": [
  "error",
  "always",
  {
    "ignorePackages": true,
    "checkTypeImports": true
  }
],
import "foo"; // ok
import "foo/bar"; // fails - unexpected

import "./foo"; // fails - as expected
import "./foo.ts" // ok

avocadowastaken avatar Oct 23 '25 07:10 avocadowastaken

import { createContext } from  'preact/compat' // error - unexpected
import { signal } from "@preact/signals"; // ok - as expected

Akiyamka avatar Oct 27 '25 22:10 Akiyamka

Same here, though I am not even sure I understand what the correct syntax is. I tried many variants, including this monstrosity below, but there is a bug going on, right?:

"import/extensions": [
  "error",
  "ignorePackages",
  { "js": "ignorePackages", "ts": "ignorePackages", "ignorePackages": true }
 ],

Were fix(linter/extensions): false positives with non configured extensions by camc314 · Pull Request #11872 · oxc-project/oxc and fix(linter): fix false positives reported when analyzing package imports by taearls · Pull Request #14602 · oxc-project/oxc supposed to fix it? One of them looks merged, but it's still bugging on "import type { LibSQLDatabase } from 'drizzle-orm/libsql';" for example in today's 1.25.0.

sebastienbarre avatar Oct 30 '25 19:10 sebastienbarre

Marked this as higher priority since it seems to be a problem effecting a few folks and I seem to be seeing this issue when testing repos like mastodon as well when experimenting with oxlint on it

If anyone can provide simple reproduction repo(s) of problematic behavior that'd be incredibly helpful for testing purposes. Preferably, with both ESLint and oxlint to show the discrepancies.

connorshea avatar Nov 09 '25 17:11 connorshea

Potentially related to problems with this rule, including a repro case of the issue with ignorePackages not working correctly: https://github.com/oxc-project/oxc/issues/11881

connorshea avatar Nov 09 '25 17:11 connorshea