From e82339dc148ffe1b28afc19b4141f47d7254e600 Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Tue, 19 May 2026 15:29:00 -0400 Subject: [PATCH 1/6] chore: test custom eslint plugin to check local import extensions --- .oxlintrc.json | 14 +- package.json | 4 +- packages/eslint-plugin-import-ext/README.md | 245 ++++++++++++++++ packages/eslint-plugin-import-ext/index.mjs | 20 ++ .../eslint-plugin-import-ext/package.json | 34 +++ .../rules/require-local-extension.mjs | 262 +++++++++++++++++ .../test/require-local-extension.spec.mjs | 148 ++++++++++ pnpm-lock.yaml | 274 ++++++++++++++++++ scripts/lint-local-js-imports.mjs | 111 ------- 9 files changed, 996 insertions(+), 116 deletions(-) create mode 100644 packages/eslint-plugin-import-ext/README.md create mode 100644 packages/eslint-plugin-import-ext/index.mjs create mode 100644 packages/eslint-plugin-import-ext/package.json create mode 100644 packages/eslint-plugin-import-ext/rules/require-local-extension.mjs create mode 100644 packages/eslint-plugin-import-ext/test/require-local-extension.spec.mjs delete mode 100644 scripts/lint-local-js-imports.mjs diff --git a/.oxlintrc.json b/.oxlintrc.json index e3d3374d31..0c5cef91e4 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -1,7 +1,7 @@ { "$schema": "./node_modules/oxlint/configuration_schema.json", "plugins": ["import", "oxc", "typescript"], - "jsPlugins": ["eslint-plugin-cypress"], + "jsPlugins": ["eslint-plugin-cypress", "eslint-plugin-import-ext"], "env": { "es2025": true, "browser": true, @@ -62,9 +62,17 @@ "typescript/no-non-null-assertion": "off", "typescript/no-unused-expressions": "off", "typescript/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "destructuredArrayIgnorePattern": "^_", "caughtErrors": "none" }], - // blocked by https://github.com/oxc-project/oxc/issues/19431 - // use "lint:local-js-imports" npm script in the meantime to catch any missing local JS imports + // "import/extensions" rule is blocked by https://github.com/oxc-project/oxc/issues/19431 + // use custom "eslint-plugin-import-ext" custom eslint plugin in the meantime to catch any missing local import file extensions "import/extensions": "off", + "import-ext/require-local-extension": [ + "error", + { + "js": "always", + "ts": "never", + "excludedFolders": ["demos/aurelia/test", "demos/react/test", "demos/vue/test", "frameworks/angular-slickgrid", "test"] + } + ], "import/no-self-import": "error", "no-async-promise-executor": "off", "oxc/erasing-op": "off", diff --git a/package.json b/package.json index 7e618d6999..2ffc6842df 100644 --- a/package.json +++ b/package.json @@ -48,9 +48,8 @@ "roll-new-release": "pnpm build && pnpm new-version && pnpm new-publish", "build:dev": "pnpm -r --filter=vanilla-demo build:dev", "serve:vite": "pnpm -r --filter=vanilla-demo dev", - "lint": "oxlint . && pnpm lint:local-js-imports", + "lint": "oxlint .", "lint:fix": "oxlint . --fix", - "lint:local-js-imports": "node ./scripts/lint-local-js-imports.mjs", "prettier:check": "prettier --check **/*.{html,js,ts,tsx,vue}", "prettier:write": "prettier --write **/*.{html,js,ts,tsx,vue}", "test": "vitest --config ./test/vitest.config.mts", @@ -134,6 +133,7 @@ "jsdom-global": "catalog:", "npm-run-all2": "catalog:", "oxlint": "catalog:", + "eslint-plugin-import-ext": "workspace:*", "prettier": "^3.8.3", "remove-glob": "catalog:", "rxjs": "catalog:", diff --git a/packages/eslint-plugin-import-ext/README.md b/packages/eslint-plugin-import-ext/README.md new file mode 100644 index 0000000000..c7987d0fb4 --- /dev/null +++ b/packages/eslint-plugin-import-ext/README.md @@ -0,0 +1,245 @@ +# eslint-plugin-import-ext — require-local-extension + +📝 Enforce the style of file extensions in import declarations. + +🔧 This rule is automatically fixable by the `--fix` CLI option. + +Targets local imports (paths starting with `./` or `../`) and performs safe autofixes when run with `--fix`. + +Supported targets +- `import` / `export` sources +- Common `require()` call sites + +It 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`). + +> [!NOTE] +> 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. +> +> Recommended migration (disable `import/extensions` and enable this rule): +> +> ```json +> { +> "rules": { +> "import/extensions": "off", +> "import-ext/require-local-extension": ["error", { "js": "always", "ts": "never" }] +> } +> } +> ``` + +Quick usage + +Below 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). + +Basic OXLint config + +```json +{ + "plugins": ["import-ext"], + "jsPlugins": ["eslint-plugin-import-ext"], + "rules": { + "import-ext/require-local-extension": [ + "error", + { + "js": "always", + "ts": "never" + } + ] +} +``` + +Full OXLint config (recommended) + +```json +{ + "plugins": ["import-ext"], + "jsPlugins": ["eslint-plugin-import-ext"], + "settings": { + "localImportExt": { + "preferredFixExt": ".js", + "extRules": { "js": "always", "ts": "never" }, + "allowedExts": [".js",".mjs",".cjs",".ts",".tsx",".jsx",".vue",".json"], + "excludedFolders": ["demos/**","test/**"], + "ignorePackages": true + } + }, + "rules": { + "import-ext/require-local-extension": [ + "error", + { + "preferredFixExt": ".js", + "extRules": { "js": "always", "ts": "never" }, + "excludedFolders": ["demos/**","test/**"], + "ignorePackages": true + } + ] + } +} +``` + +Configuration options + +Options can be passed either as rule options or via `settings.localImportExt`. + +Key options: +- `excludedFolders`: array of repo-relative folder globs to ignore (e.g. tests, demos). +- `allowedExts`: array of allowed extensions for the rule. +- `preferredFixExt`: single extension (string) the fixer should prefer when offering fixes (defaults to `.js`). +- `disallowedExts`: array of extensions to explicitly avoid when fixing. + +- `extRules`: object mapping extensions to `"always"` or `"never"` (also supports the n-style mapping described below). +- `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. + +Two common config styles are supported: + +- object form (recommended): + +```json +{ + "rules": { + "import-ext/require-local-extension": [ + "error", + { + "preferredFixExt": ".js", + "extRules": { "js": "always", "ts": "never" }, + "excludedFolders": ["demos/aurelia/test"] + } + ] + } +} +``` + +- n-style shorthand (like eslint-plugin-n): + +```json +{ + "rules": { + "import-ext/require-local-extension": [ + "error", + { + "js": "always", + "ts": "never", + "excludedFolders": ["demos/aurelia/test"] + } + ] + } +} +``` + +How the fixer chooses an extension + +- The fixer prefers `preferredFixExt` (default `.js`). +- 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. +- 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. + +Behavior notes + +- Query/hash suffixes are preserved (e.g. `./file?raw` -> `./file.js?raw`). +- Original quote style (`'` or `"`) is preserved. +- Directory imports that resolve to an `index` file are supported (the fixer will append `/index.js` when that file exists). +- Asset and style imports are ignored if their extensions are included in `allowedExts`. + +Examples (before -> after) + +- Simple import + +Before: +```js +Quick usage + +Below 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). + +Basic OXLint config + +```json +{ + "plugins": ["import-ext"], + "jsPlugins": ["eslint-plugin-import-ext"], + "rules": { + "import-ext/require-local-extension": [ + "error", + { + "js": "always", + "ts": "never" + } + ] + } +} +``` + +Full OXLint config (recommended) + +```json +{ + "plugins": ["import-ext"], + "jsPlugins": ["eslint-plugin-import-ext"], + "settings": { + "localImportExt": { + "preferredFixExt": ".js", + "extRules": { "js": "always", "ts": "never" }, + "allowedExts": [".js",".mjs",".cjs",".ts",".tsx",".jsx",".vue",".json"], + "excludedFolders": ["demos/**","test/**"], + "ignorePackages": true + } + }, + "rules": { + "import-ext/require-local-extension": [ + "error", + { + "preferredFixExt": ".js", + "extRules": { "js": "always", "ts": "never" }, + "excludedFolders": ["demos/**","test/**"], + "ignorePackages": true + } + ] + } +} +``` + +Configuration options + +Options can be passed either as rule options or via `settings.localImportExt`. + +Key options: +- `excludedFolders`: array of repo-relative folder globs to ignore (e.g. tests, demos). +- `allowedExts`: array of allowed extensions for the rule. +- `preferredFixExt`: single extension (string) the fixer should prefer when offering fixes (defaults to `.js`). +- `disallowedExts`: array of extensions to explicitly avoid when fixing. + +- `extRules`: object mapping extensions to `"always"` or `"never"` (also supports the n-style mapping described below). +- `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. + +Two common config styles are supported: + +- object form (recommended): + +```json +{ + "rules": { + "import-ext/require-local-extension": [ + "error", + { + "preferredFixExt": ".js", + "extRules": { "js": "always", "ts": "never" }, + "excludedFolders": ["demos/aurelia/test"] + } + ] + } +} +``` + +- n-style shorthand (like eslint-plugin-n): + +```json +{ + "rules": { + "import-ext/require-local-extension": [ + "error", + { + "js": "always", + "ts": "never", + "excludedFolders": ["demos/aurelia/test"] + } + ] + } +} +``` diff --git a/packages/eslint-plugin-import-ext/index.mjs b/packages/eslint-plugin-import-ext/index.mjs new file mode 100644 index 0000000000..465f2433eb --- /dev/null +++ b/packages/eslint-plugin-import-ext/index.mjs @@ -0,0 +1,20 @@ +import requireLocalExtension from './rules/require-local-extension.mjs'; + +export default { + rules: { + 'require-local-extension': requireLocalExtension, + }, + configs: { + recommended: { + plugins: ['import-ext'], + rules: { + 'import-ext/require-local-extension': [ + 'error', + { + excludedFolders: [], + }, + ], + }, + }, + }, +}; diff --git a/packages/eslint-plugin-import-ext/package.json b/packages/eslint-plugin-import-ext/package.json new file mode 100644 index 0000000000..28516050f8 --- /dev/null +++ b/packages/eslint-plugin-import-ext/package.json @@ -0,0 +1,34 @@ +{ + "name": "eslint-plugin-import-ext", + "version": "0.1.0", + "description": "Enforce file extension style in import declarations (fixable with --fix)", + "main": "index.mjs", + "type": "module", + "keywords": [ + "eslint", + "eslint-plugin", + "imports", + "extensions" + ], + "files": [ + "index.mjs", + "rules/", + "README.md" + ], + "exports": { + ".": { + "default": "./index.mjs" + } + }, + "engines": { + "node": "^20.0.0 || >=22.0.0" + }, + "publishConfig": { + "access": "public" + }, + "license": "MIT", + "devDependencies": { + "eslint": "^8.49.0", + "vitest": "^5.0.0-beta.2" + } +} diff --git a/packages/eslint-plugin-import-ext/rules/require-local-extension.mjs b/packages/eslint-plugin-import-ext/rules/require-local-extension.mjs new file mode 100644 index 0000000000..0029d81ced --- /dev/null +++ b/packages/eslint-plugin-import-ext/rules/require-local-extension.mjs @@ -0,0 +1,262 @@ +import fs from 'node:fs'; +import path from 'path'; + +const defaultAllowedExts = [ + '.js', + '.mjs', + '.cjs', + '.ts', + '.tsx', + '.jsx', + '.vue', + '.json', + // images + '.png', + '.jpg', + '.jpeg', + '.gif', + '.svg', + '.webp', + '.avif', + '.ico', + // styles + '.css', + '.scss', + '.sass', + '.less', + '.styl', + // markup / templates + '.html', + '.htm', + // media + '.mp3', + '.mp4', + '.wav', + '.webm', +]; + +function normalizeSlashes(value) { + return value.replace(/\\/g, '/'); +} + +function isFileExcludedForPath(filePath, excludedFolders) { + if (!filePath || filePath === '') return false; + const rel = normalizeSlashes(path.relative(process.cwd(), filePath)); + if (rel.includes('node_modules') || rel.includes('generated-parser') || rel.includes('/dist/')) return true; + return excludedFolders.some((p) => rel === p || rel.startsWith(p + '/')); +} + +export default { + meta: { + type: 'problem', + docs: { + description: 'Require file extension on local imports (./ or ../)', + category: 'Possible Errors', + recommended: false, + }, + fixable: 'code', + schema: [ + { + type: 'object', + properties: { + excludedFolders: { type: 'array', items: { type: 'string' } }, + allowedExts: { type: 'array', items: { type: 'string' } }, + preferredFixExt: { type: 'string' }, + disallowedExts: { type: 'array', items: { type: 'string' } }, + extRules: { + type: 'object', + additionalProperties: { enum: ['always', 'never'] }, + }, + ignorePackages: { type: 'boolean' }, + }, + // allow top-level per-extension keys like "js": "always" or "ts": "never" + patternProperties: { + '^\\.?[a-z0-9]{1,5}$': { enum: ['always', 'never'] }, + }, + additionalProperties: false, + }, + ], + }, + + create(context) { + const rawOptions = context.options || []; + const settings = (context.settings && context.settings.localImportExt) || {}; + + // support two config styles: + // 1) ['error', {'.ts': 'never'}] (like eslint-plugin-n) where options[1] is the mapping + // 2) [{ allowedExts: [...], preferredFixExt: '.js', extRules: {...} }] + let userOptions = {}; + if (rawOptions.length > 0) { + if (typeof rawOptions[0] === 'object' && !Array.isArray(rawOptions[0])) { + userOptions = rawOptions[0]; + } else if (rawOptions.length > 1 && typeof rawOptions[1] === 'object' && !Array.isArray(rawOptions[1])) { + // support ['error', {'.ts': 'never'}] or ['always', { extRules: {...} }] + userOptions = rawOptions[1]; + } + } + + const allowedExts = new Set((userOptions.allowedExts || settings.allowedExts || defaultAllowedExts).map((e) => e.toLowerCase())); + + const preferredFixExtRaw = userOptions.preferredFixExt || settings.preferredFixExt || '.js'; + const preferredFixExt = preferredFixExtRaw.startsWith('.') ? preferredFixExtRaw.toLowerCase() : '.' + preferredFixExtRaw.toLowerCase(); + const defaultExcluded = []; + const excludedFolders = userOptions.excludedFolders || settings.excludedFolders || defaultExcluded; + + // extRules may be provided directly as mapping (['error', {'.ts':'never'}]) or under extRules + const extRules = userOptions.extRules || (typeof userOptions === 'object' ? userOptions : {}); + const disallowedExts = new Set( + (userOptions.disallowedExts || settings.disallowedExts || Object.keys(extRules).filter((k) => extRules[k] === 'never')).map((e) => + e.startsWith('.') ? e.toLowerCase() : '.' + e.toLowerCase() + ) + ); + + // default: ignore package imports (only enforce local imports) + const ignorePackages = Object.prototype.hasOwnProperty.call(userOptions, 'ignorePackages') + ? Boolean(userOptions.ignorePackages) + : Object.prototype.hasOwnProperty.call(settings, 'ignorePackages') + ? Boolean(settings.ignorePackages) + : true; + + function checkSource(node, sourceValue) { + const filename = context.getFilename && context.getFilename(); + if (isFileExcludedForPath(filename, excludedFolders)) return; + + if (typeof sourceValue !== 'string') return; + const isLocal = /^\.\.?\//.test(sourceValue); + // If it's not a local import and the config asks to ignore packages, skip it. + if (!isLocal && ignorePackages) return; + if (sourceValue.endsWith('/')) return; // directory import + const ext = (path.extname(sourceValue.split('?')[0].split('#')[0]) || '').toLowerCase(); + if (!ext || !allowedExts.has(ext)) { + const sourceCode = context.getSourceCode(); + // attempt to find a candidate file to auto-fix (only for local imports) + const raw = sourceValue; + const base = raw.split('?')[0].split('#')[0]; + const suffix = raw.slice(base.length); + const fileDir = filename && filename !== '' ? path.dirname(filename) : process.cwd(); + const absBase = path.resolve(fileDir, base); + + // discovery order for existing files + const fixExtOrder = ['.js', '.ts', '.tsx', '.jsx', '.mjs', '.cjs', '.vue', '.json']; + + let found = null; + let foundIsIndex = false; + + if (isLocal) { + for (const e of fixExtOrder) { + const candidate = absBase + e; + if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) { + found = e; + foundIsIndex = false; + break; + } + } + + // try index files if base refers to a directory + if (!found) { + for (const e of fixExtOrder) { + const candidate = path.join(absBase, 'index' + e); + if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) { + found = e; + foundIsIndex = true; + break; + } + } + } + } + + const report = { + node, + message: "Local import '{{value}}' must include a file extension (e.g. .js, .vue)", + data: { value: sourceValue }, + }; + + // Only register a fixer when ESLint was invoked with `--fix` (CLI or npm script) + const isCliFix = Array.isArray(process.argv) && process.argv.some((a) => a === '--fix' || a.startsWith('--fix')); + // Register fixer only when running the CLI with --fix and for local imports + if (isLocal && isCliFix) { + // Determine a safe extension to use for the fix. Preference order: + // 1) If we discovered an existing file (`found`), prefer it unless it's disallowed. + // 2) Otherwise, prefer `preferredFixExt` (unless disallowed) but only if that file actually exists. + let chosenExt = null; + + if (found) { + if (!disallowedExts.has(found)) { + chosenExt = found; + } else { + // if the discovered ext is disallowed, prefer the user-preferred fix ext when allowed + if (!disallowedExts.has(preferredFixExt)) { + chosenExt = preferredFixExt; + } else { + // otherwise try to find another existing ext that is allowed + const alt = fixExtOrder.find((e) => { + const candidate = foundIsIndex ? path.join(absBase, 'index' + e) : absBase + e; + return !disallowedExts.has(e) && fs.existsSync(candidate) && fs.statSync(candidate).isFile(); + }); + if (alt) chosenExt = alt; + } + } + } else { + // No discovered ext; prefer preferredFixExt when allowed (even if target file doesn't exist) + let candidateExt = preferredFixExt; + if (disallowedExts.has(candidateExt)) { + candidateExt = fixExtOrder.find((e) => !disallowedExts.has(e)) || preferredFixExt; + } + // If preferred/fallback ext exists, use it. If not, still allow preferredFixExt to be used + // to restore autofix behavior (user preference) — but only when it's not disallowed. + const candidatePath = foundIsIndex ? path.join(absBase, 'index' + candidateExt) : absBase + candidateExt; + if (!disallowedExts.has(preferredFixExt)) { + chosenExt = preferredFixExt; + } else if (fs.existsSync(candidatePath) && fs.statSync(candidatePath).isFile()) { + chosenExt = candidateExt; + } + } + + if (chosenExt) { + report.fix = (fixer) => { + let newPath; + if (foundIsIndex) { + newPath = base.replace(/\/$/, '') + '/index' + chosenExt + suffix; + } else { + newPath = base + chosenExt + suffix; + } + const sourceCode = context.getSourceCode(); + const sourceNode = node; // node is the literal node when called + const origText = sourceCode.getText(sourceNode); + const origQuote = origText[0] === "'" || origText[0] === '"' ? origText[0] : '"'; + const replaced = origQuote + newPath + origQuote; + return fixer.replaceText(sourceNode, replaced); + }; + } + } + + context.report(report); + } + } + + return { + ImportDeclaration(node) { + checkSource(node.source, node.source && node.source.value); + }, + ExportAllDeclaration(node) { + if (node.source) checkSource(node.source, node.source.value); + }, + ExportNamedDeclaration(node) { + if (node.source) checkSource(node.source, node.source.value); + }, + CallExpression(node) { + // require('...') style + if ( + node.callee && + node.callee.type === 'Identifier' && + node.callee.name === 'require' && + node.arguments && + node.arguments[0] && + node.arguments[0].type === 'Literal' + ) { + checkSource(node.arguments[0], node.arguments[0].value); + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin-import-ext/test/require-local-extension.spec.mjs b/packages/eslint-plugin-import-ext/test/require-local-extension.spec.mjs new file mode 100644 index 0000000000..f91812e947 --- /dev/null +++ b/packages/eslint-plugin-import-ext/test/require-local-extension.spec.mjs @@ -0,0 +1,148 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { afterAll, test } from 'vitest'; + +// Ensure `--fix` is present before loading the rule so the fixer is registered. +const originalArgv = process.argv.slice(); +if (!process.argv.includes('--fix')) process.argv.push('--fix'); + +// dynamic import so argv is already modified before modules evaluate +const { RuleTester } = await import('eslint'); +const ruleModule = await import('../rules/require-local-extension.mjs'); +const rule = ruleModule.default; + +afterAll(() => { + // restore argv to avoid polluting other tests + process.argv.length = 0; + process.argv.push(...originalArgv); +}); + +// helper to create fixture files +function writeFixture(relPath, content = '') { + const root = path.resolve(process.cwd(), 'packages', 'eslint-plugin-import-ext', 'test', 'fixtures'); + const abs = path.join(root, relPath); + fs.mkdirSync(path.dirname(abs), { recursive: true }); + fs.writeFileSync(abs, content, 'utf8'); + return abs; +} + +function removeFixture(relPath) { + const root = path.resolve(process.cwd(), 'packages', 'eslint-plugin-import-ext', 'test', 'fixtures'); + const abs = path.join(root, relPath); + try { + fs.rmSync(abs, { force: true, recursive: true }); + } catch (e) {} +} + +test('discovered extension is preferred when existing', () => { + writeFixture('prefer/prefer-me.ts', 'export const x = 1;'); + + ruleTester.run('require-local-extension', rule, { + valid: [], + invalid: [ + { + filename: path.join('packages', 'eslint-plugin-import-ext', 'test', 'fixtures', 'prefer', 'tstarget.js'), + code: "import '../fixtures/prefer/prefer-me';", + output: "import '../fixtures/prefer/prefer-me.js';", + errors: 1, + }, + ], + }); + + removeFixture('prefer'); +}); + +test('preferredFixExt applied when no discovered file exists', () => { + ruleTester.run('require-local-extension', rule, { + valid: [], + invalid: [ + { + code: "import './does-not-exist';", + output: "import './does-not-exist.js';", + errors: 1, + }, + ], + }); +}); + +test('disallowedExts prevents fixer when preferred is disallowed', () => { + ruleTester.run('require-local-extension', rule, { + valid: [], + invalid: [ + { + code: "import './no-fix-when-disallowed';", + options: [{ disallowedExts: ['.js'] }], + errors: 1, + }, + ], + }); +}); + +test('directory index resolution', () => { + writeFixture('widget/index.js', 'export default "ok";'); + + ruleTester.run('require-local-extension', rule, { + valid: [], + invalid: [ + { + filename: path.join('packages', 'eslint-plugin-import-ext', 'test', 'fixtures', 'widget', 'caller.js'), + code: "import widget from '../fixtures/widget';", + output: "import widget from '../fixtures/widget.js';", + errors: 1, + }, + ], + }); + + removeFixture('widget'); +}); + +test('ignorePackages false will flag bare specifiers', () => { + ruleTester.run('require-local-extension', rule, { + valid: [], + invalid: [ + { + code: "import pkg from 'lodash';", + options: [{ ignorePackages: false }], + errors: 1, + }, + ], + }); +}); + +test('excludedFolders are skipped', () => { + ruleTester.run('require-local-extension', rule, { + valid: [ + { + filename: path.join('demos', 'a', 'file.js'), + code: "import './something';", + options: [{ excludedFolders: ['demos'] }], + }, + ], + invalid: [], + }); +}); + +const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2020, sourceType: 'module' } }); + +test('require-local-extension RuleTester', () => { + ruleTester.run('require-local-extension', rule, { + valid: ["import './file.js';", "import './file.js?raw';", "const x = require('./file.js');", "import pkg from 'lodash';"], + + invalid: [ + { + code: "import './file';", + output: "import './file.js';", + errors: [ + { + message: "Local import './file' must include a file extension (e.g. .js, .vue)", + }, + ], + }, + { + code: "import tpl from './template?raw';", + output: "import tpl from './template.js?raw';", + errors: 1, + }, + ], + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7121592384..05ff66a4fb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -200,6 +200,9 @@ importers: eslint-plugin-cypress: specifier: ^6.4.1 version: 6.4.1(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import-ext: + specifier: workspace:* + version: link:packages/eslint-plugin-import-ext globals: specifier: 'catalog:' version: 17.6.0 @@ -1432,6 +1435,15 @@ importers: specifier: workspace:* version: link:../common + packages/eslint-plugin-import-ext: + devDependencies: + eslint: + specifier: ^8.49.0 + version: 8.57.1 + vitest: + specifier: ^5.0.0-beta.2 + version: 5.0.0-beta.2(@types/node@24.12.4)(@vitest/coverage-v8@5.0.0-beta.2)(@vitest/ui@5.0.0-beta.2)(jsdom@29.1.1)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(jiti@2.6.1)(less@4.5.1)(sass-embedded@1.97.3)(sass@1.99.0)(terser@5.46.0)(yaml@2.9.0)) + packages/event-pub-sub: dependencies: '@slickgrid-universal/utils': @@ -2357,10 +2369,18 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/eslintrc@3.3.5': resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/js@9.39.2': resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2964,10 +2984,19 @@ packages: resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} engines: {node: '>=18.18.0'} + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + '@humanwhocodes/retry@0.4.3': resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} @@ -3490,6 +3519,18 @@ packages: '@angular/common': '>=16' '@angular/core': '>=16' + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + '@npmcli/agent@4.0.0': resolution: {integrity: sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==} engines: {node: ^20.17.0 || >=22.9.0} @@ -4530,6 +4571,9 @@ packages: resolution: {integrity: sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.1': + resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} + '@vitejs/plugin-basic-ssl@2.1.4': resolution: {integrity: sha512-HXciTXN/sDBYWgeAD4V4s0DN0g72x5mlxQhHxtYu3Tt8BLa6MzcJZUyDVFCdtjNs3bfENVHVzOsmooTVuNgAAw==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -5307,6 +5351,10 @@ packages: resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} engines: {node: '>=0.3.1'} + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -5452,6 +5500,10 @@ packages: '@typescript-eslint/parser': optional: true + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@8.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -5464,6 +5516,12 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + eslint@9.39.2: resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -5478,6 +5536,10 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + esquery@1.7.0: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} @@ -5588,6 +5650,9 @@ packages: fast-wrap-ansi@0.2.0: resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + fd-package-json@2.0.0: resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} @@ -5606,6 +5671,10 @@ packages: fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -5634,6 +5703,10 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -5675,6 +5748,9 @@ packages: resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -5731,10 +5807,18 @@ packages: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + global-dirs@3.0.1: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} engines: {node: '>=10'} + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -5753,6 +5837,9 @@ packages: grammex@3.1.12: resolution: {integrity: sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==} + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphmatch@1.1.1: resolution: {integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==} @@ -5894,6 +5981,10 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -6735,6 +6826,10 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -7087,6 +7182,9 @@ packages: quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + raf@3.4.1: resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} @@ -7203,6 +7301,10 @@ packages: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} @@ -7210,6 +7312,11 @@ packages: resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==} engines: {node: '>= 0.8.15'} + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + rolldown@1.0.0-rc.4: resolution: {integrity: sha512-V2tPDUrY3WSevrvU2E41ijZlpF+5PbZu4giH+VpNraaadsJGHa4fR6IFwsocVwEXDoAdIv5qgPPxgrvKAOIPtA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -7239,6 +7346,9 @@ packages: rtl-css-js@1.16.1: resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} @@ -7643,6 +7753,9 @@ packages: text-segmentation@1.0.3: resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==} + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + thenby@1.3.4: resolution: {integrity: sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==} @@ -7744,6 +7857,10 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + type-fest@0.8.1: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} @@ -9050,6 +9167,11 @@ snapshots: '@esbuild/win32-x64@0.27.3': optional: true + '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.6.1))': dependencies: eslint: 9.39.2(jiti@2.6.1) @@ -9073,6 +9195,20 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.15.0 + debug: 4.4.3(supports-color@8.1.1) + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 10.2.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + '@eslint/eslintrc@3.3.5': dependencies: ajv: 6.15.0 @@ -9087,6 +9223,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@eslint/js@8.57.1': {} + '@eslint/js@9.39.2': {} '@eslint/object-schema@2.1.7': {} @@ -10317,8 +10455,18 @@ snapshots: '@humanfs/types@0.15.0': {} + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 10.2.5 + transitivePeerDependencies: + - supports-color + '@humanwhocodes/module-importer@1.0.1': {} + '@humanwhocodes/object-schema@2.0.3': {} + '@humanwhocodes/retry@0.4.3': {} '@ianvs/prettier-plugin-sort-imports@4.7.1(@prettier/plugin-oxc@0.1.4)(@vue/compiler-sfc@3.5.34)(prettier@3.8.3)': @@ -10883,6 +11031,18 @@ snapshots: '@angular/core': 21.2.13(@angular/compiler@21.2.13)(rxjs@7.8.2)(zone.js@0.16.0) tslib: 2.8.1 + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + '@npmcli/agent@4.0.0': dependencies: agent-base: 7.1.4 @@ -11729,6 +11889,8 @@ snapshots: '@typescript-eslint/types': 8.54.0 eslint-visitor-keys: 4.2.1 + '@ungap/structured-clone@1.3.1': {} + '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.2(@types/node@24.12.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.32.0)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.46.0)(yaml@2.9.0))': dependencies: vite: 7.3.2(@types/node@24.12.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.32.0)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.46.0)(yaml@2.9.0) @@ -12632,6 +12794,10 @@ snapshots: diff@8.0.3: {} + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -12778,6 +12944,11 @@ snapshots: eslint: 9.39.2(jiti@2.6.1) globals: 17.6.0 + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 @@ -12787,6 +12958,49 @@ snapshots: eslint-visitor-keys@4.2.1: {} + eslint@8.57.1: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@eslint-community/regexpp': 4.12.2 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.3.1 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.1 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 10.2.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + eslint@9.39.2(jiti@2.6.1): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) @@ -12834,6 +13048,12 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.16.0) eslint-visitor-keys: 4.2.1 + espree@9.6.1: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 3.4.3 + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -12968,6 +13188,10 @@ snapshots: dependencies: fast-string-width: 3.0.2 + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + fd-package-json@2.0.0: dependencies: walk-up-path: 4.0.0 @@ -12982,6 +13206,10 @@ snapshots: fflate@0.8.2: {} + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -13018,6 +13246,12 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 + flat-cache@3.2.0: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + rimraf: 3.0.2 + flat-cache@4.0.1: dependencies: flatted: 3.4.2 @@ -13064,6 +13298,8 @@ snapshots: dependencies: minipass: 7.1.3 + fs.realpath@1.0.0: {} + fsevents@2.3.3: optional: true @@ -13126,10 +13362,23 @@ snapshots: minipass: 7.1.3 path-scurry: 2.0.2 + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 10.2.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + global-dirs@3.0.1: dependencies: ini: 2.0.0 + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + globals@14.0.0: {} globals@17.6.0: {} @@ -13140,6 +13389,8 @@ snapshots: grammex@3.1.12: {} + graphemer@1.4.0: {} + graphmatch@1.1.1: {} handlebars@4.7.9: @@ -13284,6 +13535,11 @@ snapshots: imurmurhash@0.1.4: {} + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + inherits@2.0.4: {} ini@2.0.0: {} @@ -14225,6 +14481,8 @@ snapshots: path-exists@4.0.0: {} + path-is-absolute@1.0.1: {} + path-key@3.1.1: {} path-parse@1.0.7: {} @@ -14540,6 +14798,8 @@ snapshots: quansync@0.2.11: {} + queue-microtask@1.2.3: {} + raf@3.4.1: dependencies: performance-now: 2.1.0 @@ -14638,11 +14898,17 @@ snapshots: retry@0.12.0: {} + reusify@1.1.0: {} + rfdc@1.4.1: {} rgbcolor@1.0.1: optional: true + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + rolldown@1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): dependencies: '@oxc-project/types': 0.113.0 @@ -14742,6 +15008,10 @@ snapshots: dependencies: '@babel/runtime': 7.29.2 + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + rxjs@7.8.2: dependencies: tslib: 2.8.1 @@ -15148,6 +15418,8 @@ snapshots: utrie: 1.0.2 optional: true + text-table@0.2.0: {} + thenby@1.3.4: {} throttleit@1.0.1: {} @@ -15236,6 +15508,8 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-fest@0.20.2: {} + type-fest@0.8.1: {} type-fest@2.19.0: {} diff --git a/scripts/lint-local-js-imports.mjs b/scripts/lint-local-js-imports.mjs deleted file mode 100644 index e9b6762483..0000000000 --- a/scripts/lint-local-js-imports.mjs +++ /dev/null @@ -1,111 +0,0 @@ -// temp script to check for missing ".js" extensions in relative local imports -// related oxlint issue: https://github.com/oxc-project/oxc/issues/19431 - -import fs from 'node:fs'; -import path from 'node:path'; -import { styleText } from 'node:util'; - -const roots = ['demos', 'frameworks', 'frameworks-plugins', 'packages']; -const excludedFolders = ['demos/aurelia/test', 'demos/react/test', 'demos/vue/test', 'frameworks/angular-slickgrid']; -const allowedExtPattern = /\.(html|js|json|mjs|png|vue)(\?.*)?$/; -const fromPattern = /from\s+['\"](\.\.?\/[^'\"]+)['\"]/g; - -function color(text, format) { - return process.stdout.isTTY ? styleText(format, text) : text; -} - -function normalizeSlashes(value) { - return value.replaceAll('\\', '/'); -} - -function isExcluded(fullPath) { - const normalized = normalizeSlashes(fullPath); - return excludedFolders.some((excludedPath) => normalized === excludedPath || normalized.startsWith(`${excludedPath}/`)); -} - -/** @param {string} dir */ -function walk(dir, acc) { - if (!fs.existsSync(dir)) { - return; - } - - for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { - const fullPath = path.join(dir, entry.name); - - if (entry.isDirectory()) { - if (isExcluded(fullPath)) { - continue; - } - if (entry.name === 'node_modules' || entry.name === 'generated-parser' || entry.name === 'dist') { - continue; - } - walk(fullPath, acc); - continue; - } - - if (entry.isFile() && entry.name.endsWith('.ts')) { - acc.push(fullPath); - } - } -} - -const startTime = performance.now(); -const files = []; -for (const root of roots) { - walk(root, files); -} - -const scopedFiles = files.filter((filePath) => { - if (filePath.startsWith(`packages${path.sep}`)) { - return filePath.includes(`${path.sep}src${path.sep}`); - } - return true; -}); - -const errors = []; -for (const filePath of scopedFiles) { - const content = fs.readFileSync(filePath, 'utf8'); - const lines = content.split(/\r?\n/); - - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - fromPattern.lastIndex = 0; - - for (let match = fromPattern.exec(line); match !== null; match = fromPattern.exec(line)) { - const importPath = match[1]; - if (!allowedExtPattern.test(importPath)) { - errors.push({ - filePath, - lineNumber: i + 1, - line: line.trim(), - }); - } - } - } -} - -const endTime = performance.now(); -const elapsedMs = endTime - startTime; -const elapsedTime = elapsedMs < 1000 ? `${elapsedMs.toFixed(0)}ms` : `${(elapsedMs / 1000).toFixed(1)}s`; - -if (errors.length > 0) { - console.error(color(`Found ${errors.length} relative imports missing a file extension (.js, .json, .mjs, or .vue).`, ['red', 'bold'])); - console.error(color('Add the appropriate extension to the import path in the following lines:', 'yellow')); - console.error(''); - - for (const error of errors) { - console.error(`${color('', 'red')}${color(`${error.filePath}:${error.lineNumber}`, 'cyan')}`); - console.error(` - ${color(`${error.line}`, 'red')}`); - console.error(''); - } - - console.error(''); - console.error(color(`Total violations: ${errors.length}`, ['red', 'bold'])); - console.error(`Finished in ${elapsedTime} on ${scopedFiles.length} files.`); - process.exit(1); -} else { - console.log('Found 0 violations.'); - console.log(`Finished in ${elapsedTime} on ${scopedFiles.length} files.`); -} - -console.log('All relative imports have file extensions.'); From a0459006856007c8b2da39bb05f8caa20c9d856a Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Thu, 21 May 2026 18:37:36 -0400 Subject: [PATCH 2/6] chore: migrate to my external custom npm package --- .oxlintrc.json | 4 +- package.json | 2 +- packages/eslint-plugin-import-ext/README.md | 245 --------------- packages/eslint-plugin-import-ext/index.mjs | 20 -- .../eslint-plugin-import-ext/package.json | 34 --- .../rules/require-local-extension.mjs | 262 ---------------- .../test/require-local-extension.spec.mjs | 148 ---------- pnpm-lock.yaml | 279 +----------------- pnpm-workspace.yaml | 1 + 9 files changed, 11 insertions(+), 984 deletions(-) delete mode 100644 packages/eslint-plugin-import-ext/README.md delete mode 100644 packages/eslint-plugin-import-ext/index.mjs delete mode 100644 packages/eslint-plugin-import-ext/package.json delete mode 100644 packages/eslint-plugin-import-ext/rules/require-local-extension.mjs delete mode 100644 packages/eslint-plugin-import-ext/test/require-local-extension.spec.mjs diff --git a/.oxlintrc.json b/.oxlintrc.json index 0c5cef91e4..798a617ba9 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -1,7 +1,7 @@ { "$schema": "./node_modules/oxlint/configuration_schema.json", "plugins": ["import", "oxc", "typescript"], - "jsPlugins": ["eslint-plugin-cypress", "eslint-plugin-import-ext"], + "jsPlugins": ["eslint-plugin-cypress", "eslint-plugin-local-import-ext"], "env": { "es2025": true, "browser": true, @@ -65,7 +65,7 @@ // "import/extensions" rule is blocked by https://github.com/oxc-project/oxc/issues/19431 // use custom "eslint-plugin-import-ext" custom eslint plugin in the meantime to catch any missing local import file extensions "import/extensions": "off", - "import-ext/require-local-extension": [ + "local-import-ext/require-local-extension": [ "error", { "js": "always", diff --git a/package.json b/package.json index 2ffc6842df..dc00179353 100644 --- a/package.json +++ b/package.json @@ -133,7 +133,7 @@ "jsdom-global": "catalog:", "npm-run-all2": "catalog:", "oxlint": "catalog:", - "eslint-plugin-import-ext": "workspace:*", + "eslint-plugin-local-import-ext": "0.1.1", "prettier": "^3.8.3", "remove-glob": "catalog:", "rxjs": "catalog:", diff --git a/packages/eslint-plugin-import-ext/README.md b/packages/eslint-plugin-import-ext/README.md deleted file mode 100644 index c7987d0fb4..0000000000 --- a/packages/eslint-plugin-import-ext/README.md +++ /dev/null @@ -1,245 +0,0 @@ -# eslint-plugin-import-ext — require-local-extension - -📝 Enforce the style of file extensions in import declarations. - -🔧 This rule is automatically fixable by the `--fix` CLI option. - -Targets local imports (paths starting with `./` or `../`) and performs safe autofixes when run with `--fix`. - -Supported targets -- `import` / `export` sources -- Common `require()` call sites - -It 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`). - -> [!NOTE] -> 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. -> -> Recommended migration (disable `import/extensions` and enable this rule): -> -> ```json -> { -> "rules": { -> "import/extensions": "off", -> "import-ext/require-local-extension": ["error", { "js": "always", "ts": "never" }] -> } -> } -> ``` - -Quick usage - -Below 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). - -Basic OXLint config - -```json -{ - "plugins": ["import-ext"], - "jsPlugins": ["eslint-plugin-import-ext"], - "rules": { - "import-ext/require-local-extension": [ - "error", - { - "js": "always", - "ts": "never" - } - ] -} -``` - -Full OXLint config (recommended) - -```json -{ - "plugins": ["import-ext"], - "jsPlugins": ["eslint-plugin-import-ext"], - "settings": { - "localImportExt": { - "preferredFixExt": ".js", - "extRules": { "js": "always", "ts": "never" }, - "allowedExts": [".js",".mjs",".cjs",".ts",".tsx",".jsx",".vue",".json"], - "excludedFolders": ["demos/**","test/**"], - "ignorePackages": true - } - }, - "rules": { - "import-ext/require-local-extension": [ - "error", - { - "preferredFixExt": ".js", - "extRules": { "js": "always", "ts": "never" }, - "excludedFolders": ["demos/**","test/**"], - "ignorePackages": true - } - ] - } -} -``` - -Configuration options - -Options can be passed either as rule options or via `settings.localImportExt`. - -Key options: -- `excludedFolders`: array of repo-relative folder globs to ignore (e.g. tests, demos). -- `allowedExts`: array of allowed extensions for the rule. -- `preferredFixExt`: single extension (string) the fixer should prefer when offering fixes (defaults to `.js`). -- `disallowedExts`: array of extensions to explicitly avoid when fixing. - -- `extRules`: object mapping extensions to `"always"` or `"never"` (also supports the n-style mapping described below). -- `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. - -Two common config styles are supported: - -- object form (recommended): - -```json -{ - "rules": { - "import-ext/require-local-extension": [ - "error", - { - "preferredFixExt": ".js", - "extRules": { "js": "always", "ts": "never" }, - "excludedFolders": ["demos/aurelia/test"] - } - ] - } -} -``` - -- n-style shorthand (like eslint-plugin-n): - -```json -{ - "rules": { - "import-ext/require-local-extension": [ - "error", - { - "js": "always", - "ts": "never", - "excludedFolders": ["demos/aurelia/test"] - } - ] - } -} -``` - -How the fixer chooses an extension - -- The fixer prefers `preferredFixExt` (default `.js`). -- 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. -- 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. - -Behavior notes - -- Query/hash suffixes are preserved (e.g. `./file?raw` -> `./file.js?raw`). -- Original quote style (`'` or `"`) is preserved. -- Directory imports that resolve to an `index` file are supported (the fixer will append `/index.js` when that file exists). -- Asset and style imports are ignored if their extensions are included in `allowedExts`. - -Examples (before -> after) - -- Simple import - -Before: -```js -Quick usage - -Below 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). - -Basic OXLint config - -```json -{ - "plugins": ["import-ext"], - "jsPlugins": ["eslint-plugin-import-ext"], - "rules": { - "import-ext/require-local-extension": [ - "error", - { - "js": "always", - "ts": "never" - } - ] - } -} -``` - -Full OXLint config (recommended) - -```json -{ - "plugins": ["import-ext"], - "jsPlugins": ["eslint-plugin-import-ext"], - "settings": { - "localImportExt": { - "preferredFixExt": ".js", - "extRules": { "js": "always", "ts": "never" }, - "allowedExts": [".js",".mjs",".cjs",".ts",".tsx",".jsx",".vue",".json"], - "excludedFolders": ["demos/**","test/**"], - "ignorePackages": true - } - }, - "rules": { - "import-ext/require-local-extension": [ - "error", - { - "preferredFixExt": ".js", - "extRules": { "js": "always", "ts": "never" }, - "excludedFolders": ["demos/**","test/**"], - "ignorePackages": true - } - ] - } -} -``` - -Configuration options - -Options can be passed either as rule options or via `settings.localImportExt`. - -Key options: -- `excludedFolders`: array of repo-relative folder globs to ignore (e.g. tests, demos). -- `allowedExts`: array of allowed extensions for the rule. -- `preferredFixExt`: single extension (string) the fixer should prefer when offering fixes (defaults to `.js`). -- `disallowedExts`: array of extensions to explicitly avoid when fixing. - -- `extRules`: object mapping extensions to `"always"` or `"never"` (also supports the n-style mapping described below). -- `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. - -Two common config styles are supported: - -- object form (recommended): - -```json -{ - "rules": { - "import-ext/require-local-extension": [ - "error", - { - "preferredFixExt": ".js", - "extRules": { "js": "always", "ts": "never" }, - "excludedFolders": ["demos/aurelia/test"] - } - ] - } -} -``` - -- n-style shorthand (like eslint-plugin-n): - -```json -{ - "rules": { - "import-ext/require-local-extension": [ - "error", - { - "js": "always", - "ts": "never", - "excludedFolders": ["demos/aurelia/test"] - } - ] - } -} -``` diff --git a/packages/eslint-plugin-import-ext/index.mjs b/packages/eslint-plugin-import-ext/index.mjs deleted file mode 100644 index 465f2433eb..0000000000 --- a/packages/eslint-plugin-import-ext/index.mjs +++ /dev/null @@ -1,20 +0,0 @@ -import requireLocalExtension from './rules/require-local-extension.mjs'; - -export default { - rules: { - 'require-local-extension': requireLocalExtension, - }, - configs: { - recommended: { - plugins: ['import-ext'], - rules: { - 'import-ext/require-local-extension': [ - 'error', - { - excludedFolders: [], - }, - ], - }, - }, - }, -}; diff --git a/packages/eslint-plugin-import-ext/package.json b/packages/eslint-plugin-import-ext/package.json deleted file mode 100644 index 28516050f8..0000000000 --- a/packages/eslint-plugin-import-ext/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "eslint-plugin-import-ext", - "version": "0.1.0", - "description": "Enforce file extension style in import declarations (fixable with --fix)", - "main": "index.mjs", - "type": "module", - "keywords": [ - "eslint", - "eslint-plugin", - "imports", - "extensions" - ], - "files": [ - "index.mjs", - "rules/", - "README.md" - ], - "exports": { - ".": { - "default": "./index.mjs" - } - }, - "engines": { - "node": "^20.0.0 || >=22.0.0" - }, - "publishConfig": { - "access": "public" - }, - "license": "MIT", - "devDependencies": { - "eslint": "^8.49.0", - "vitest": "^5.0.0-beta.2" - } -} diff --git a/packages/eslint-plugin-import-ext/rules/require-local-extension.mjs b/packages/eslint-plugin-import-ext/rules/require-local-extension.mjs deleted file mode 100644 index 0029d81ced..0000000000 --- a/packages/eslint-plugin-import-ext/rules/require-local-extension.mjs +++ /dev/null @@ -1,262 +0,0 @@ -import fs from 'node:fs'; -import path from 'path'; - -const defaultAllowedExts = [ - '.js', - '.mjs', - '.cjs', - '.ts', - '.tsx', - '.jsx', - '.vue', - '.json', - // images - '.png', - '.jpg', - '.jpeg', - '.gif', - '.svg', - '.webp', - '.avif', - '.ico', - // styles - '.css', - '.scss', - '.sass', - '.less', - '.styl', - // markup / templates - '.html', - '.htm', - // media - '.mp3', - '.mp4', - '.wav', - '.webm', -]; - -function normalizeSlashes(value) { - return value.replace(/\\/g, '/'); -} - -function isFileExcludedForPath(filePath, excludedFolders) { - if (!filePath || filePath === '') return false; - const rel = normalizeSlashes(path.relative(process.cwd(), filePath)); - if (rel.includes('node_modules') || rel.includes('generated-parser') || rel.includes('/dist/')) return true; - return excludedFolders.some((p) => rel === p || rel.startsWith(p + '/')); -} - -export default { - meta: { - type: 'problem', - docs: { - description: 'Require file extension on local imports (./ or ../)', - category: 'Possible Errors', - recommended: false, - }, - fixable: 'code', - schema: [ - { - type: 'object', - properties: { - excludedFolders: { type: 'array', items: { type: 'string' } }, - allowedExts: { type: 'array', items: { type: 'string' } }, - preferredFixExt: { type: 'string' }, - disallowedExts: { type: 'array', items: { type: 'string' } }, - extRules: { - type: 'object', - additionalProperties: { enum: ['always', 'never'] }, - }, - ignorePackages: { type: 'boolean' }, - }, - // allow top-level per-extension keys like "js": "always" or "ts": "never" - patternProperties: { - '^\\.?[a-z0-9]{1,5}$': { enum: ['always', 'never'] }, - }, - additionalProperties: false, - }, - ], - }, - - create(context) { - const rawOptions = context.options || []; - const settings = (context.settings && context.settings.localImportExt) || {}; - - // support two config styles: - // 1) ['error', {'.ts': 'never'}] (like eslint-plugin-n) where options[1] is the mapping - // 2) [{ allowedExts: [...], preferredFixExt: '.js', extRules: {...} }] - let userOptions = {}; - if (rawOptions.length > 0) { - if (typeof rawOptions[0] === 'object' && !Array.isArray(rawOptions[0])) { - userOptions = rawOptions[0]; - } else if (rawOptions.length > 1 && typeof rawOptions[1] === 'object' && !Array.isArray(rawOptions[1])) { - // support ['error', {'.ts': 'never'}] or ['always', { extRules: {...} }] - userOptions = rawOptions[1]; - } - } - - const allowedExts = new Set((userOptions.allowedExts || settings.allowedExts || defaultAllowedExts).map((e) => e.toLowerCase())); - - const preferredFixExtRaw = userOptions.preferredFixExt || settings.preferredFixExt || '.js'; - const preferredFixExt = preferredFixExtRaw.startsWith('.') ? preferredFixExtRaw.toLowerCase() : '.' + preferredFixExtRaw.toLowerCase(); - const defaultExcluded = []; - const excludedFolders = userOptions.excludedFolders || settings.excludedFolders || defaultExcluded; - - // extRules may be provided directly as mapping (['error', {'.ts':'never'}]) or under extRules - const extRules = userOptions.extRules || (typeof userOptions === 'object' ? userOptions : {}); - const disallowedExts = new Set( - (userOptions.disallowedExts || settings.disallowedExts || Object.keys(extRules).filter((k) => extRules[k] === 'never')).map((e) => - e.startsWith('.') ? e.toLowerCase() : '.' + e.toLowerCase() - ) - ); - - // default: ignore package imports (only enforce local imports) - const ignorePackages = Object.prototype.hasOwnProperty.call(userOptions, 'ignorePackages') - ? Boolean(userOptions.ignorePackages) - : Object.prototype.hasOwnProperty.call(settings, 'ignorePackages') - ? Boolean(settings.ignorePackages) - : true; - - function checkSource(node, sourceValue) { - const filename = context.getFilename && context.getFilename(); - if (isFileExcludedForPath(filename, excludedFolders)) return; - - if (typeof sourceValue !== 'string') return; - const isLocal = /^\.\.?\//.test(sourceValue); - // If it's not a local import and the config asks to ignore packages, skip it. - if (!isLocal && ignorePackages) return; - if (sourceValue.endsWith('/')) return; // directory import - const ext = (path.extname(sourceValue.split('?')[0].split('#')[0]) || '').toLowerCase(); - if (!ext || !allowedExts.has(ext)) { - const sourceCode = context.getSourceCode(); - // attempt to find a candidate file to auto-fix (only for local imports) - const raw = sourceValue; - const base = raw.split('?')[0].split('#')[0]; - const suffix = raw.slice(base.length); - const fileDir = filename && filename !== '' ? path.dirname(filename) : process.cwd(); - const absBase = path.resolve(fileDir, base); - - // discovery order for existing files - const fixExtOrder = ['.js', '.ts', '.tsx', '.jsx', '.mjs', '.cjs', '.vue', '.json']; - - let found = null; - let foundIsIndex = false; - - if (isLocal) { - for (const e of fixExtOrder) { - const candidate = absBase + e; - if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) { - found = e; - foundIsIndex = false; - break; - } - } - - // try index files if base refers to a directory - if (!found) { - for (const e of fixExtOrder) { - const candidate = path.join(absBase, 'index' + e); - if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) { - found = e; - foundIsIndex = true; - break; - } - } - } - } - - const report = { - node, - message: "Local import '{{value}}' must include a file extension (e.g. .js, .vue)", - data: { value: sourceValue }, - }; - - // Only register a fixer when ESLint was invoked with `--fix` (CLI or npm script) - const isCliFix = Array.isArray(process.argv) && process.argv.some((a) => a === '--fix' || a.startsWith('--fix')); - // Register fixer only when running the CLI with --fix and for local imports - if (isLocal && isCliFix) { - // Determine a safe extension to use for the fix. Preference order: - // 1) If we discovered an existing file (`found`), prefer it unless it's disallowed. - // 2) Otherwise, prefer `preferredFixExt` (unless disallowed) but only if that file actually exists. - let chosenExt = null; - - if (found) { - if (!disallowedExts.has(found)) { - chosenExt = found; - } else { - // if the discovered ext is disallowed, prefer the user-preferred fix ext when allowed - if (!disallowedExts.has(preferredFixExt)) { - chosenExt = preferredFixExt; - } else { - // otherwise try to find another existing ext that is allowed - const alt = fixExtOrder.find((e) => { - const candidate = foundIsIndex ? path.join(absBase, 'index' + e) : absBase + e; - return !disallowedExts.has(e) && fs.existsSync(candidate) && fs.statSync(candidate).isFile(); - }); - if (alt) chosenExt = alt; - } - } - } else { - // No discovered ext; prefer preferredFixExt when allowed (even if target file doesn't exist) - let candidateExt = preferredFixExt; - if (disallowedExts.has(candidateExt)) { - candidateExt = fixExtOrder.find((e) => !disallowedExts.has(e)) || preferredFixExt; - } - // If preferred/fallback ext exists, use it. If not, still allow preferredFixExt to be used - // to restore autofix behavior (user preference) — but only when it's not disallowed. - const candidatePath = foundIsIndex ? path.join(absBase, 'index' + candidateExt) : absBase + candidateExt; - if (!disallowedExts.has(preferredFixExt)) { - chosenExt = preferredFixExt; - } else if (fs.existsSync(candidatePath) && fs.statSync(candidatePath).isFile()) { - chosenExt = candidateExt; - } - } - - if (chosenExt) { - report.fix = (fixer) => { - let newPath; - if (foundIsIndex) { - newPath = base.replace(/\/$/, '') + '/index' + chosenExt + suffix; - } else { - newPath = base + chosenExt + suffix; - } - const sourceCode = context.getSourceCode(); - const sourceNode = node; // node is the literal node when called - const origText = sourceCode.getText(sourceNode); - const origQuote = origText[0] === "'" || origText[0] === '"' ? origText[0] : '"'; - const replaced = origQuote + newPath + origQuote; - return fixer.replaceText(sourceNode, replaced); - }; - } - } - - context.report(report); - } - } - - return { - ImportDeclaration(node) { - checkSource(node.source, node.source && node.source.value); - }, - ExportAllDeclaration(node) { - if (node.source) checkSource(node.source, node.source.value); - }, - ExportNamedDeclaration(node) { - if (node.source) checkSource(node.source, node.source.value); - }, - CallExpression(node) { - // require('...') style - if ( - node.callee && - node.callee.type === 'Identifier' && - node.callee.name === 'require' && - node.arguments && - node.arguments[0] && - node.arguments[0].type === 'Literal' - ) { - checkSource(node.arguments[0], node.arguments[0].value); - } - }, - }; - }, -}; diff --git a/packages/eslint-plugin-import-ext/test/require-local-extension.spec.mjs b/packages/eslint-plugin-import-ext/test/require-local-extension.spec.mjs deleted file mode 100644 index f91812e947..0000000000 --- a/packages/eslint-plugin-import-ext/test/require-local-extension.spec.mjs +++ /dev/null @@ -1,148 +0,0 @@ -import fs from 'node:fs'; -import path from 'node:path'; -import { afterAll, test } from 'vitest'; - -// Ensure `--fix` is present before loading the rule so the fixer is registered. -const originalArgv = process.argv.slice(); -if (!process.argv.includes('--fix')) process.argv.push('--fix'); - -// dynamic import so argv is already modified before modules evaluate -const { RuleTester } = await import('eslint'); -const ruleModule = await import('../rules/require-local-extension.mjs'); -const rule = ruleModule.default; - -afterAll(() => { - // restore argv to avoid polluting other tests - process.argv.length = 0; - process.argv.push(...originalArgv); -}); - -// helper to create fixture files -function writeFixture(relPath, content = '') { - const root = path.resolve(process.cwd(), 'packages', 'eslint-plugin-import-ext', 'test', 'fixtures'); - const abs = path.join(root, relPath); - fs.mkdirSync(path.dirname(abs), { recursive: true }); - fs.writeFileSync(abs, content, 'utf8'); - return abs; -} - -function removeFixture(relPath) { - const root = path.resolve(process.cwd(), 'packages', 'eslint-plugin-import-ext', 'test', 'fixtures'); - const abs = path.join(root, relPath); - try { - fs.rmSync(abs, { force: true, recursive: true }); - } catch (e) {} -} - -test('discovered extension is preferred when existing', () => { - writeFixture('prefer/prefer-me.ts', 'export const x = 1;'); - - ruleTester.run('require-local-extension', rule, { - valid: [], - invalid: [ - { - filename: path.join('packages', 'eslint-plugin-import-ext', 'test', 'fixtures', 'prefer', 'tstarget.js'), - code: "import '../fixtures/prefer/prefer-me';", - output: "import '../fixtures/prefer/prefer-me.js';", - errors: 1, - }, - ], - }); - - removeFixture('prefer'); -}); - -test('preferredFixExt applied when no discovered file exists', () => { - ruleTester.run('require-local-extension', rule, { - valid: [], - invalid: [ - { - code: "import './does-not-exist';", - output: "import './does-not-exist.js';", - errors: 1, - }, - ], - }); -}); - -test('disallowedExts prevents fixer when preferred is disallowed', () => { - ruleTester.run('require-local-extension', rule, { - valid: [], - invalid: [ - { - code: "import './no-fix-when-disallowed';", - options: [{ disallowedExts: ['.js'] }], - errors: 1, - }, - ], - }); -}); - -test('directory index resolution', () => { - writeFixture('widget/index.js', 'export default "ok";'); - - ruleTester.run('require-local-extension', rule, { - valid: [], - invalid: [ - { - filename: path.join('packages', 'eslint-plugin-import-ext', 'test', 'fixtures', 'widget', 'caller.js'), - code: "import widget from '../fixtures/widget';", - output: "import widget from '../fixtures/widget.js';", - errors: 1, - }, - ], - }); - - removeFixture('widget'); -}); - -test('ignorePackages false will flag bare specifiers', () => { - ruleTester.run('require-local-extension', rule, { - valid: [], - invalid: [ - { - code: "import pkg from 'lodash';", - options: [{ ignorePackages: false }], - errors: 1, - }, - ], - }); -}); - -test('excludedFolders are skipped', () => { - ruleTester.run('require-local-extension', rule, { - valid: [ - { - filename: path.join('demos', 'a', 'file.js'), - code: "import './something';", - options: [{ excludedFolders: ['demos'] }], - }, - ], - invalid: [], - }); -}); - -const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2020, sourceType: 'module' } }); - -test('require-local-extension RuleTester', () => { - ruleTester.run('require-local-extension', rule, { - valid: ["import './file.js';", "import './file.js?raw';", "const x = require('./file.js');", "import pkg from 'lodash';"], - - invalid: [ - { - code: "import './file';", - output: "import './file.js';", - errors: [ - { - message: "Local import './file' must include a file extension (e.g. .js, .vue)", - }, - ], - }, - { - code: "import tpl from './template?raw';", - output: "import tpl from './template.js?raw';", - errors: 1, - }, - ], - }); -}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05ff66a4fb..e2f188cfd6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -200,9 +200,9 @@ importers: eslint-plugin-cypress: specifier: ^6.4.1 version: 6.4.1(eslint@9.39.2(jiti@2.6.1)) - eslint-plugin-import-ext: - specifier: workspace:* - version: link:packages/eslint-plugin-import-ext + eslint-plugin-local-import-ext: + specifier: 0.1.1 + version: 0.1.1 globals: specifier: 'catalog:' version: 17.6.0 @@ -1435,15 +1435,6 @@ importers: specifier: workspace:* version: link:../common - packages/eslint-plugin-import-ext: - devDependencies: - eslint: - specifier: ^8.49.0 - version: 8.57.1 - vitest: - specifier: ^5.0.0-beta.2 - version: 5.0.0-beta.2(@types/node@24.12.4)(@vitest/coverage-v8@5.0.0-beta.2)(@vitest/ui@5.0.0-beta.2)(jsdom@29.1.1)(vite@8.0.13(@types/node@24.12.4)(esbuild@0.27.3)(jiti@2.6.1)(less@4.5.1)(sass-embedded@1.97.3)(sass@1.99.0)(terser@5.46.0)(yaml@2.9.0)) - packages/event-pub-sub: dependencies: '@slickgrid-universal/utils': @@ -2369,18 +2360,10 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/eslintrc@3.3.5': resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@8.57.1': - resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/js@9.39.2': resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2984,19 +2967,10 @@ packages: resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} engines: {node: '>=18.18.0'} - '@humanwhocodes/config-array@0.13.0': - resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead - '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/object-schema@2.0.3': - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead - '@humanwhocodes/retry@0.4.3': resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} @@ -3519,18 +3493,6 @@ packages: '@angular/common': '>=16' '@angular/core': '>=16' - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - '@npmcli/agent@4.0.0': resolution: {integrity: sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==} engines: {node: ^20.17.0 || >=22.9.0} @@ -4571,9 +4533,6 @@ packages: resolution: {integrity: sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@ungap/structured-clone@1.3.1': - resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} - '@vitejs/plugin-basic-ssl@2.1.4': resolution: {integrity: sha512-HXciTXN/sDBYWgeAD4V4s0DN0g72x5mlxQhHxtYu3Tt8BLa6MzcJZUyDVFCdtjNs3bfENVHVzOsmooTVuNgAAw==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -5351,10 +5310,6 @@ packages: resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} engines: {node: '>=0.3.1'} - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -5500,9 +5455,9 @@ packages: '@typescript-eslint/parser': optional: true - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-plugin-local-import-ext@0.1.1: + resolution: {integrity: sha512-BwdF84UADLirpNmviVEuo3yEllyifcnMWovxnwVPJhK39vHOraovipg0P/BRLvk0ZGSIyR7BuCr7x4FqwvGeVw==} + engines: {node: ^20.0.0 || >=22.0.0} eslint-scope@8.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} @@ -5516,12 +5471,6 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@8.57.1: - resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. - hasBin: true - eslint@9.39.2: resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -5536,10 +5485,6 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - esquery@1.7.0: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} @@ -5650,9 +5595,6 @@ packages: fast-wrap-ansi@0.2.0: resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - fd-package-json@2.0.0: resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} @@ -5671,10 +5613,6 @@ packages: fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -5703,10 +5641,6 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} - flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -5748,9 +5682,6 @@ packages: resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -5807,18 +5738,10 @@ packages: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - global-dirs@3.0.1: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} engines: {node: '>=10'} - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -5837,9 +5760,6 @@ packages: grammex@3.1.12: resolution: {integrity: sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - graphmatch@1.1.1: resolution: {integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==} @@ -5981,10 +5901,6 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -6826,10 +6742,6 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -7182,9 +7094,6 @@ packages: quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - raf@3.4.1: resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} @@ -7301,10 +7210,6 @@ packages: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} @@ -7312,11 +7217,6 @@ packages: resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==} engines: {node: '>= 0.8.15'} - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - rolldown@1.0.0-rc.4: resolution: {integrity: sha512-V2tPDUrY3WSevrvU2E41ijZlpF+5PbZu4giH+VpNraaadsJGHa4fR6IFwsocVwEXDoAdIv5qgPPxgrvKAOIPtA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -7346,9 +7246,6 @@ packages: rtl-css-js@1.16.1: resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} @@ -7753,9 +7650,6 @@ packages: text-segmentation@1.0.3: resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==} - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - thenby@1.3.4: resolution: {integrity: sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==} @@ -7857,10 +7751,6 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - type-fest@0.8.1: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} @@ -9167,11 +9057,6 @@ snapshots: '@esbuild/win32-x64@0.27.3': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)': - dependencies: - eslint: 8.57.1 - eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(jiti@2.6.1))': dependencies: eslint: 9.39.2(jiti@2.6.1) @@ -9195,20 +9080,6 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@2.1.4': - dependencies: - ajv: 6.15.0 - debug: 4.4.3(supports-color@8.1.1) - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - minimatch: 10.2.5 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - '@eslint/eslintrc@3.3.5': dependencies: ajv: 6.15.0 @@ -9223,8 +9094,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@8.57.1': {} - '@eslint/js@9.39.2': {} '@eslint/object-schema@2.1.7': {} @@ -10455,18 +10324,8 @@ snapshots: '@humanfs/types@0.15.0': {} - '@humanwhocodes/config-array@0.13.0': - dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.3(supports-color@8.1.1) - minimatch: 10.2.5 - transitivePeerDependencies: - - supports-color - '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/object-schema@2.0.3': {} - '@humanwhocodes/retry@0.4.3': {} '@ianvs/prettier-plugin-sort-imports@4.7.1(@prettier/plugin-oxc@0.1.4)(@vue/compiler-sfc@3.5.34)(prettier@3.8.3)': @@ -11031,18 +10890,6 @@ snapshots: '@angular/core': 21.2.13(@angular/compiler@21.2.13)(rxjs@7.8.2)(zone.js@0.16.0) tslib: 2.8.1 - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 - '@npmcli/agent@4.0.0': dependencies: agent-base: 7.1.4 @@ -11889,8 +11736,6 @@ snapshots: '@typescript-eslint/types': 8.54.0 eslint-visitor-keys: 4.2.1 - '@ungap/structured-clone@1.3.1': {} - '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.2(@types/node@24.12.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.32.0)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.46.0)(yaml@2.9.0))': dependencies: vite: 7.3.2(@types/node@24.12.4)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.32.0)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.46.0)(yaml@2.9.0) @@ -12794,10 +12639,6 @@ snapshots: diff@8.0.3: {} - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -12944,10 +12785,7 @@ snapshots: eslint: 9.39.2(jiti@2.6.1) globals: 17.6.0 - eslint-scope@7.2.2: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 + eslint-plugin-local-import-ext@0.1.1: {} eslint-scope@8.4.0: dependencies: @@ -12958,49 +12796,6 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@8.57.1: - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) - '@eslint-community/regexpp': 4.12.2 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.1 - '@humanwhocodes/config-array': 0.13.0 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.3.1 - ajv: 6.15.0 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.3(supports-color@8.1.1) - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.7.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.1 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 10.2.5 - natural-compare: 1.4.0 - optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - eslint@9.39.2(jiti@2.6.1): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) @@ -13048,12 +12843,6 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.16.0) eslint-visitor-keys: 4.2.1 - espree@9.6.1: - dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) - eslint-visitor-keys: 3.4.3 - esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -13188,10 +12977,6 @@ snapshots: dependencies: fast-string-width: 3.0.2 - fastq@1.20.1: - dependencies: - reusify: 1.1.0 - fd-package-json@2.0.0: dependencies: walk-up-path: 4.0.0 @@ -13206,10 +12991,6 @@ snapshots: fflate@0.8.2: {} - file-entry-cache@6.0.1: - dependencies: - flat-cache: 3.2.0 - file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -13246,12 +13027,6 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - flat-cache@3.2.0: - dependencies: - flatted: 3.4.2 - keyv: 4.5.4 - rimraf: 3.0.2 - flat-cache@4.0.1: dependencies: flatted: 3.4.2 @@ -13298,8 +13073,6 @@ snapshots: dependencies: minipass: 7.1.3 - fs.realpath@1.0.0: {} - fsevents@2.3.3: optional: true @@ -13362,23 +13135,10 @@ snapshots: minipass: 7.1.3 path-scurry: 2.0.2 - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 10.2.5 - once: 1.4.0 - path-is-absolute: 1.0.1 - global-dirs@3.0.1: dependencies: ini: 2.0.0 - globals@13.24.0: - dependencies: - type-fest: 0.20.2 - globals@14.0.0: {} globals@17.6.0: {} @@ -13389,8 +13149,6 @@ snapshots: grammex@3.1.12: {} - graphemer@1.4.0: {} - graphmatch@1.1.1: {} handlebars@4.7.9: @@ -13535,11 +13293,6 @@ snapshots: imurmurhash@0.1.4: {} - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - inherits@2.0.4: {} ini@2.0.0: {} @@ -14481,8 +14234,6 @@ snapshots: path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} path-parse@1.0.7: {} @@ -14798,8 +14549,6 @@ snapshots: quansync@0.2.11: {} - queue-microtask@1.2.3: {} - raf@3.4.1: dependencies: performance-now: 2.1.0 @@ -14898,17 +14647,11 @@ snapshots: retry@0.12.0: {} - reusify@1.1.0: {} - rfdc@1.4.1: {} rgbcolor@1.0.1: optional: true - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - rolldown@1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): dependencies: '@oxc-project/types': 0.113.0 @@ -15008,10 +14751,6 @@ snapshots: dependencies: '@babel/runtime': 7.29.2 - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - rxjs@7.8.2: dependencies: tslib: 2.8.1 @@ -15418,8 +15157,6 @@ snapshots: utrie: 1.0.2 optional: true - text-table@0.2.0: {} - thenby@1.3.4: {} throttleit@1.0.1: {} @@ -15508,8 +15245,6 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-fest@0.20.2: {} - type-fest@0.8.1: {} type-fest@2.19.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 91582f3a5b..97a600c2ba 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -9,6 +9,7 @@ minimumReleaseAge: 2880 # 2 days in minutes minimumReleaseAgeExclude: # exclude packages that I personally maintain and are safe to use on the same day - '@lerna-lite/*' + - 'eslint-plugin-local-import-ext' # Renovate security update: hono@4.12.18 - hono@4.12.18 From c8a7896ee98649cfff9a3037cfce765dea2ff5f8 Mon Sep 17 00:00:00 2001 From: "Ghislain B." Date: Thu, 21 May 2026 23:30:29 -0400 Subject: [PATCH 3/6] chore: sort dependencies --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index dc00179353..ef9207b4aa 100644 --- a/package.json +++ b/package.json @@ -128,12 +128,12 @@ "cypress": "catalog:", "cypress-real-events": "catalog:", "eslint-plugin-cypress": "^6.4.1", + "eslint-plugin-local-import-ext": "0.1.1", "globals": "catalog:", "jsdom": "catalog:", "jsdom-global": "catalog:", "npm-run-all2": "catalog:", - "oxlint": "catalog:", - "eslint-plugin-local-import-ext": "0.1.1", + "oxlint": "catalog:", "prettier": "^3.8.3", "remove-glob": "catalog:", "rxjs": "catalog:", From b1bc3c9515b0b5e5e547c4ff693fd4efd2c20d69 Mon Sep 17 00:00:00 2001 From: "Ghislain B." Date: Thu, 21 May 2026 23:31:03 -0400 Subject: [PATCH 4/6] chore: remove whitespace --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ef9207b4aa..9fcf853218 100644 --- a/package.json +++ b/package.json @@ -133,7 +133,7 @@ "jsdom": "catalog:", "jsdom-global": "catalog:", "npm-run-all2": "catalog:", - "oxlint": "catalog:", + "oxlint": "catalog:", "prettier": "^3.8.3", "remove-glob": "catalog:", "rxjs": "catalog:", From 9157883b244cb88cb5115a72ff61e69a27fd18f9 Mon Sep 17 00:00:00 2001 From: "Ghislain B." Date: Thu, 21 May 2026 23:41:34 -0400 Subject: [PATCH 5/6] chore: remove unused min release age skip Removed hono@4.12.18 from minimumReleaseAgeExclude. --- pnpm-workspace.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 97a600c2ba..57f5f802cd 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -10,8 +10,6 @@ minimumReleaseAgeExclude: # exclude packages that I personally maintain and are safe to use on the same day - '@lerna-lite/*' - 'eslint-plugin-local-import-ext' - # Renovate security update: hono@4.12.18 - - hono@4.12.18 catalog: '@4tw/cypress-drag-drop': ^2.3.1 From 24563693ebd300aad8a6c665099a4ceb8e3f881d Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Fri, 22 May 2026 00:00:07 -0400 Subject: [PATCH 6/6] chore: update to latest release with oidc --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 9fcf853218..93bfa18521 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "cypress": "catalog:", "cypress-real-events": "catalog:", "eslint-plugin-cypress": "^6.4.1", - "eslint-plugin-local-import-ext": "0.1.1", + "eslint-plugin-local-import-ext": "0.2.0", "globals": "catalog:", "jsdom": "catalog:", "jsdom-global": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e2f188cfd6..9acbaa4982 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -201,8 +201,8 @@ importers: specifier: ^6.4.1 version: 6.4.1(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-local-import-ext: - specifier: 0.1.1 - version: 0.1.1 + specifier: 0.2.0 + version: 0.2.0 globals: specifier: 'catalog:' version: 17.6.0 @@ -5455,8 +5455,8 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-local-import-ext@0.1.1: - resolution: {integrity: sha512-BwdF84UADLirpNmviVEuo3yEllyifcnMWovxnwVPJhK39vHOraovipg0P/BRLvk0ZGSIyR7BuCr7x4FqwvGeVw==} + eslint-plugin-local-import-ext@0.2.0: + resolution: {integrity: sha512-2DelnKhTzSuZ0Xd9a5o740w+CL01O7LgGiOJq+0+fo+nTxkCTVl13/3L6LlJfCYhKCb79s1UkR9pa2bFe3yP1w==} engines: {node: ^20.0.0 || >=22.0.0} eslint-scope@8.4.0: @@ -12785,7 +12785,7 @@ snapshots: eslint: 9.39.2(jiti@2.6.1) globals: 17.6.0 - eslint-plugin-local-import-ext@0.1.1: {} + eslint-plugin-local-import-ext@0.2.0: {} eslint-scope@8.4.0: dependencies: