Skip to content
← Back to rules

import/extensions Restriction

What it does

Some file resolve algorithms allow you to omit the file extension within the import source path. For example the node resolver (which does not yet support ESM/import) can resolve ./foo/bar to the absolute path /User/someone/foo/bar.js because the .js extension is resolved automatically by default in CJS. Depending on the resolver you can configure more extensions to get resolved automatically. In order to provide a consistent use of file extensions across your code base, this rule can enforce or disallow the use of certain file extensions.

Why is this bad?

ESM-based file resolve algorithms (e.g., the one that Vite provides) recommend specifying the file extension to improve performance. Without extensions, the bundler must check for various possible file extensions, which can slow down the build process on large projects. In addition, common ESM environments (such as browsers and Node.js) typically require fully specified relative imports, which means extensionless imports are not supported there.

For personal preference and compatibility reasons, the rule also allows configuration to disallow extensions in imports. This is generally not recommended, but it can be done if preferred.

Examples

Examples of incorrect code for this rule:

The following patterns are considered problems when configuration set to "always":

js
import foo from "./foo";
import bar from "./bar";
import Component from "./Component";
import foo from "@/foo";

The following patterns are considered problems when configuration set to "never":

js
import foo from "./foo.js";
import bar from "./bar.json";
import Component from "./Component.jsx";
import express from "express/index.js";

Examples of correct code for this rule:

The following patterns are not considered problems when configuration set to "always":

js
import foo from "./foo.js";
import bar from "./bar.json";
import Component from "./Component.jsx";
import * as path from "path";
import foo from "@/foo.js";

The following patterns are not considered problems when configuration set to "never":

js
import foo from "./foo";
import bar from "./bar";
import Component from "./Component";
import express from "express/index";
import * as path from "path";

Per-extension configuration examples:

js
// Configuration: { "vue": "always", "ts": "never" }
import Component from "./Component.vue"; // ✓ OK - .vue configured as "always"
import utils from "./utils"; // ✓ OK - .ts configured as "never"
import styles from "./styles.css"; // ✓ OK - .css not configured, ignored

// Configuration: ["ignorePackages", { "js": "never", "ts": "never" }]
import foo from "./foo"; // ✓ OK - no extension
import bar from "lodash/fp"; // ✓ OK - package import, ignored (ignorePackages sets this to true)

Differences compared to eslint-plugin-import

If you see differences between this rule implementation and the original eslint-plugin-import rule, please note that there are some intentional, inherent differences in the behavior of this rule and the original.

Oxlint understands and can resolve TypeScript paths out-of-the-box. If your ESLint configuration did not include eslint-import-resolver-typescript or similar, then the original rule will not have been able to resolve TypeScript paths correctly, and so the behavior between ESLint and Oxlint may differ. Generally, this means that Oxlint will catch more valid violations than the original rule, though this depends on the specific configuration.

Oxlint is also able to resolve multiple tsconfig.json files in a single repo, and so in a monorepo setup will be more capable with regards to resolving file paths.

Configuration

[0]

type: "always" | "never" | "ignorePackages"

Extension rule configuration; Copy to avoid extra indirection.

[1]

type: object

[1].checkTypeImports

type: boolean

default: false

Whether to check type imports when enforcing extension rules.

[1].ignorePackages

type: boolean

default: false

Whether to ignore package imports when enforcing extension rules.

[1].pathGroupOverrides

type: array

Path group overrides for bespoke import specifiers.

[1].pathGroupOverrides[n]

type: object

[1].pathGroupOverrides[n].action

type: "enforce" | "ignore"

Action to take when pattern matches.

Action to take for path group overrides.

Determines how import extensions are validated for matching bespoke import specifiers.

####### "enforce"

Enforce extension validation for matching imports (require extensions based on config).

####### "ignore"

Ignore matching imports entirely (skip all extension validation).

[1].pathGroupOverrides[n].pattern

type: string

Glob pattern to match import specifiers.

[1].pattern

type: Record<string, string>

Per-extension rules.

How to use

To enable this rule using the config file or in the CLI, you can use:

json
{
  "plugins": ["import"],
  "rules": {
    "import/extensions": "error"
  }
}
ts
import { defineConfig } from "oxlint";

export default defineConfig({
  plugins: ["import"],
  rules: {
    "import/extensions": "error",
  },
});
bash
oxlint --deny import/extensions --import-plugin

Version

This rule was added in v1.2.0.

References