Skip to main content
interlace
Plugin: import-nextRules

extensions

Ensure consistent use of file extensions in imports

💼 This rule is enabled in the following configs: recommended, typescript. 🔧 This rule is automatically fixable by the --fix CLI option.

Rule Details

Enforces a configured convention for writing file extensions on relative specifiers. Package specifiers are not checked.

The policy for an extension is, most specific first: your pattern[ext], your default, the rule's own pattern (js, jsx, ts, tsx: never; json, css, scss, svg, png, jpg: always), then never.

  • A written extension under never is reported (and fixed) when removing it resolves to the same file. It is not reported when it is load-bearing: ./width.js naming width.ts is the spelling moduleResolution: NodeNext requires, and ./only.mjs cannot be written without its extension. A specifier that does not resolve proves nothing and is not reported.
  • A missing extension is judged by the file the specifier resolves to: ./dep naming dep.ts is reported when the policy for ts is always. A specifier that does not resolve, or that only resolves as a directory (./lib naming lib/index.ts), is not reported.
  • A dot in a file name is not an extension: ./source.config naming source.config.ts is a specifier with no extension written. Only extensions the rule ships defaults for, or that you name in pattern, count.

Every spelling is checked: import, side-effect imports, export { a } from, export * from, import(), require() and TS import x = require().

// { default: 'always', pattern: { ts: 'never' } }

// ❌ Incorrect
import { real } from './real'; // real.js: js is `always`
import { dep } from './dep.ts'; // ts is `never`

// ✅ Correct
import { real } from './real.js';
import { dep } from './dep';
import { width } from './width.js'; // names width.ts: load-bearing

Options

[
  {
    "type": "object",
    "properties": {
      "pattern": {
        "type": "object",
        "additionalProperties": {
          "type": "string",
          "enum": [
            "always",
            "never"
          ]
        }
      },
      "default": {
        "type": "string",
        "enum": [
          "always",
          "never"
        ]
      }
    },
    "additionalProperties": false
  }
]

Implementation

OWASP Foundation

  • Category: A00:2021 - General Security

Use this rule

npm install -D oxlint-plugin-import-next
.oxlintrc.json
{  "jsPlugins": ["oxlint-plugin-import-next"],  "rules": {    "import-next/extensions": "error"  }}
vite.config.ts
import { defineConfig } from 'vite-plus';export default defineConfig({  lint: {    jsPlugins: ['oxlint-plugin-import-next'],    rules: {      'import-next/extensions': 'error',    },  },});

oxlint-plugin-import-next re-exports eslint-plugin-import-next/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.

Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.