{"id":50489193,"url":"https://github.com/ghiscoding/eslint-plugin-local-import-ext","last_synced_at":"2026-06-02T01:02:47.466Z","repository":{"id":359028156,"uuid":"1244179324","full_name":"ghiscoding/eslint-plugin-local-import-ext","owner":"ghiscoding","description":"Enforce file extension style in import declarations (fixable with --fix)","archived":false,"fork":false,"pushed_at":"2026-05-20T04:04:07.000Z","size":63,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-20T06:58:22.911Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ghiscoding.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-20T03:14:55.000Z","updated_at":"2026-05-20T04:04:11.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ghiscoding/eslint-plugin-local-import-ext","commit_stats":null,"previous_names":["ghiscoding/eslint-plugin-local-import-ext"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/ghiscoding/eslint-plugin-local-import-ext","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghiscoding%2Feslint-plugin-local-import-ext","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghiscoding%2Feslint-plugin-local-import-ext/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghiscoding%2Feslint-plugin-local-import-ext/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghiscoding%2Feslint-plugin-local-import-ext/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ghiscoding","download_url":"https://codeload.github.com/ghiscoding/eslint-plugin-local-import-ext/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ghiscoding%2Feslint-plugin-local-import-ext/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33800676,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-01T02:00:06.963Z","response_time":115,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-02T01:02:42.096Z","updated_at":"2026-06-02T01:02:47.461Z","avatar_url":"https://github.com/ghiscoding.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)\n[![Vitest](https://img.shields.io/badge/tested%20with-vitest-fcc72b.svg?logo=vitest)](https://vitest.dev/)\n[![codecov](https://codecov.io/gh/ghiscoding/eslint-plugin-local-import-ext/branch/main/graph/badge.svg)](https://codecov.io/gh/ghiscoding/eslint-plugin-local-import-ext)\n\u003ca href=\"https://nodejs.org/en/about/previous-releases\"\u003e\u003cimg src=\"https://img.shields.io/node/v/eslint-plugin-local-import-ext.svg\" alt=\"Node\" /\u003e\u003c/a\u003e\n\n[![npm](https://img.shields.io/npm/dy/eslint-plugin-local-import-ext)](https://www.npmjs.com/package/eslint-plugin-local-import-ext)\n[![npm](https://img.shields.io/npm/v/eslint-plugin-local-import-ext.svg)](https://www.npmjs.com/package/eslint-plugin-local-import-ext)\n\n# eslint-plugin-local-import-ext\n\n📝 Enforce the style of file extensions in import declarations.\n\n🔧 This rule is automatically fixable by the `--fix` CLI option.\n\nTargets local imports (paths starting with `./` or `../`) and performs safe autofixes when run with `--fix`.\n\nIt recognizes many common extensions by default (`.js`, `.mjs`, `.cjs`, `.ts`, `.tsx`, `.jsx`, `.vue`, `.json`, images, styles, etc.) and supports Vite-style import query suffixes (e.g. `?raw`).\n\n\u003e [!NOTE]\n\u003e This ESLint plugin was created as a temporary workaround until the OXC/OXLint issue #19431 (https://github.com/oxc-project/oxc/issues/19431) is resolved. It helps catch missing local import file extensions during linting.\n\u003e\n\u003e Recommended migration (disable `import/extensions` and enable this rule):\n\u003e\n\u003e ```json\n\u003e {\n\u003e   \"rules\": {\n\u003e     \"import/extensions\": \"off\",\n\u003e     \"local-import-ext/require-local-extension\": [\"error\", { \"js\": \"always\", \"ts\": \"never\" }]\n\u003e   }\n\u003e }\n\u003e ```\n\n### Install\n\n```sh\nnpm install eslint-plugin-local-import-ext\n```\n\n### Quick usage\n\nBelow are two example ways to enable the rule in an OXLint/ESLint config: a minimal \"Basic\" example and a more explicit \"Full\" example (recommended for repo-wide consistency).\n\nBasic OXLint config\n\n```json\n{\n  \"plugins\": [\"import-ext\"],\n  \"jsPlugins\": [\"eslint-plugin-local-import-ext\"],\n  \"rules\": {\n    \"local-import-ext/require-local-extension\": [\n      \"error\",\n      {\n        \"js\": \"always\",\n        \"ts\": \"never\"\n      }\n    ]\n  }\n}\n```\n\nFull OXLint config (recommended)\n\n```json\n{\n  \"plugins\": [\"import-ext\"],\n  \"jsPlugins\": [\"eslint-plugin-local-import-ext\"],\n  \"settings\": {\n    \"localImportExt\": {\n      \"preferredFixExt\": \".js\",\n      \"extRules\": { \"js\": \"always\", \"ts\": \"never\" },\n      \"allowedExts\": [\".js\",\".mjs\",\".cjs\",\".ts\",\".tsx\",\".jsx\",\".vue\",\".json\"],\n      \"excludedFolders\": [\"demos/**\",\"test/**\"],\n      \"ignorePackages\": true\n    }\n  },\n  \"rules\": {\n    \"local-import-ext/require-local-extension\": [\n      \"error\",\n      {\n        \"preferredFixExt\": \".js\",\n        \"extRules\": { \"js\": \"always\", \"ts\": \"never\" },\n        \"excludedFolders\": [\"demos/**\",\"test/**\"],\n        \"ignorePackages\": true\n      }\n    ]\n  }\n}\n```\n\n### Configuration options\n\nOptions can be passed either as rule options or via `settings.localImportExt`.\n\nKey options:\n- `excludedFolders`: array of repo-relative folder globs to ignore (e.g. tests, demos).\n- `allowedExts`: array of allowed extensions for the rule.\n- `preferredFixExt`: single extension (string) the fixer should prefer when offering fixes (defaults to `.js`).\n- `disallowedExts`: array of extensions to explicitly avoid when fixing.\n\n- `extRules`: object mapping extensions to `\"always\"` or `\"never\"` (also supports the n-style mapping described below).\n- `ignorePackages`: when true, package imports (bare specifiers) are ignored and the rule only enforces local imports. This matches `import/extensions` compatibility where `\"ignorePackages\"` omits package imports from enforcement.\n\nTwo common config styles are supported:\n\n- object form (recommended):\n\n```json\n{\n  \"rules\": {\n    \"local-import-ext/require-local-extension\": [\n      \"error\",\n      {\n        \"preferredFixExt\": \".js\",\n        \"extRules\": { \"js\": \"always\", \"ts\": \"never\" },\n        \"excludedFolders\": [\"demos/aurelia/test\"]\n      }\n    ]\n  }\n}\n```\n\n- n-style shorthand (like eslint-plugin-n):\n\n```json\n{\n  \"rules\": {\n    \"local-import-ext/require-local-extension\": [\n      \"error\",\n      {\n        \"js\": \"always\",\n        \"ts\": \"never\",\n        \"excludedFolders\": [\"demos/aurelia/test\"]\n      }\n    ]\n  }\n}\n```\n\nHow the fixer chooses an extension\n\n- The fixer prefers `preferredFixExt` (default `.js`).\n- If `preferredFixExt` is explicitly disallowed via `extRules`/`disallowedExts` (e.g. `\"ts\": \"never\"`), the fixer picks the first non-disallowed extension from its internal discovery order.\n- Crucially, the autofixer now only offers a fix when the chosen target file actually exists on disk (either `base + ext` or `base/index + ext`). This prevents converting to extensions that don't exist in your repo.\n\nBehavior notes\n\n- Query/hash suffixes are preserved (e.g. `./file?raw` -\u003e `./file.js?raw`).\n- Original quote style (`'` or `\"`) is preserved.\n- Directory imports that resolve to an `index` file are supported (the fixer will append `/index.js` when that file exists).\n- Asset and style imports are ignored if their extensions are included in `allowedExts`.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fghiscoding%2Feslint-plugin-local-import-ext","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fghiscoding%2Feslint-plugin-local-import-ext","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fghiscoding%2Feslint-plugin-local-import-ext/lists"}