Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
5f53d28
fix(migrate): remove ESLint plugins/configs and detect type-aware usage
fengmk2 May 26, 2026
a8fa3be
fix(migrate): drop unused hasTypeAwareEslintConfig sniffer
fengmk2 May 26, 2026
6743b68
fix(migrate): broaden ESLint ecosystem cleanup and address review fin…
fengmk2 May 26, 2026
c396bfd
test(migrate): show generated vite.config.ts in plugins-cleanup snap
fengmk2 May 26, 2026
85fd6d9
feat(migrate): skip ESLint migration when @nuxt/eslint is present
fengmk2 May 26, 2026
39c5f3f
test(migrate): drop @nuxt/eslint from plugins-cleanup fixture
fengmk2 May 26, 2026
19a6005
refactor(migrate): apply full ESLint cleanup to workspace packages too
fengmk2 May 26, 2026
c381443
fix(migrate): apply ESLint cleanup uniformly to root and workspace pa…
fengmk2 May 26, 2026
35acc22
fix(migrate): strip unresolvable plugin references from generated lin…
fengmk2 May 26, 2026
d2accd8
fix(migrate): don't reorder lint config keys when rules is absent
fengmk2 May 26, 2026
1d32eff
fix(migrate): address sanitizer review findings
fengmk2 May 26, 2026
0469abc
fix(migrate): recognize `oxlint-plugin-*` namespace convention
fengmk2 May 26, 2026
271c04e
refactor(migrate): drop @nuxt/eslint from ESLINT_ECOSYSTEM_NAMES
fengmk2 May 26, 2026
0ed5e4e
test(e2e): add zustand to ecosystem-ci
fengmk2 May 26, 2026
2834124
test(e2e): replace zustand with vite-plugin-vue (zustand is CJS)
fengmk2 May 26, 2026
21d581a
revert: drop the e2e ecosystem-ci entries added in this PR
fengmk2 May 26, 2026
20b4ca0
feat(migrate): preserve packages referenced via lint.jsPlugins
fengmk2 May 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Exercises the sanitizer:
// 1. base-level `fictional/*` rule via an inline plugin namespace that
// doesn't resolve to a native Oxlint plugin nor an installed
// package — translates into `jsPlugins: ['eslint-plugin-fictional']`
// + rule under `fictional/*` (the WeakAuras-style failure shape).
// 2. an OVERRIDE that introduces a second unresolvable plugin
// (`./*.test.js` files only) — verifies the per-override sanitize
// path strips both the override's `jsPlugins` entry and its rules.
export default [
{
plugins: {
fictional: {
rules: {
'no-fiction': {
meta: { type: 'problem' },
create() {
return {};
},
},
},
},
},
rules: {
'fictional/no-fiction': 'warn',
},
},
{
files: ['**/*.test.js'],
plugins: {
'override-only': {
rules: {
'no-skip': {
meta: { type: 'problem' },
create() {
return {};
},
},
},
},
},
rules: {
'override-only/no-skip': 'error',
},
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "migration-eslint-jsplugins-orphan-strip",
"scripts": {
"lint": "eslint ."
},
"devDependencies": {
"eslint": "^9.0.0",
"vite": "^7.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
> vp migrate --no-interactive # orphan jsPlugin / unknown plugin / dangling rule should be stripped, with warnings
◇ Migrated . to Vite+
• Node <semver> pnpm <semver>
• 4 config updates applied
• ESLint rules migrated to Oxlint
! Warnings:
- Stripped JS plugin reference(s) from the generated lint config: eslint-plugin-fictional, eslint-plugin-override-only. No matching package is present in this workspace, so loading them at lint time would fail. If you want their Oxlint coverage back, install each package (e.g. `vp install <name>`) and add its name back to `lint.jsPlugins` in vite.config.ts.

> cat vite.config.ts # lint block should NOT contain `fictional` in plugins / jsPlugins / rules
import { defineConfig } from 'vite-plus';

export default defineConfig({
staged: {
"*": "vp check --fix"
},
fmt: {},
lint: {
"plugins": [
"oxc",
"typescript",
"unicorn",
"react"
],
"jsPlugins": [
{
"name": "vite-plus",
"specifier": "vite-plus/oxlint-plugin"
}
],
"categories": {
"correctness": "warn"
},
"env": {
"builtin": true
},
"rules": {
"vite-plus/prefer-vite-plus-imports": "error"
},
"overrides": [
{
"files": [
"**/*.test.js"
],
"rules": {},
"jsPlugins": []
}
],
"options": {
"typeAware": true,
"typeCheck": true
}
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"commands": [
"vp migrate --no-interactive # orphan jsPlugin / unknown plugin / dangling rule should be stripped, with warnings",
"cat vite.config.ts # lint block should NOT contain `fictional` in plugins / jsPlugins / rules"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Inline-defined `survives` plugin — @oxlint/migrate translates it into
// `lint.jsPlugins: ["eslint-plugin-survives"]`. The package is listed
// in this fixture's package.json devDependencies, so:
// 1. The cleanup step should NOT delete `eslint-plugin-survives`
// from package.json (it's referenced by the generated jsPlugins
// array — removing it would invalidate the lint config we just
// generated).
// 2. The sanitizer should NOT strip the jsPlugins entry (the
// package is present in the workspace).
// 3. The `survives/no-fiction` rule should survive in the merged
// `lint.rules` (the `survives` namespace is backed by the kept
// jsPlugin).
export default [
{
plugins: {
survives: {
rules: {
'no-fiction': {
meta: { type: 'problem' },
create() {
return {};
},
},
},
},
},
rules: {
'survives/no-fiction': 'warn',
},
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "migration-eslint-jsplugins-preserve",
"scripts": {
"lint": "eslint ."
},
"devDependencies": {
"eslint": "^9.0.0",
"eslint-plugin-survives": "^1.0.0",
"vite": "^7.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
> vp migrate --no-interactive # plugin referenced via lint.jsPlugins must be preserved through cleanup AND sanitization
◇ Migrated . to Vite+
• Node <semver> pnpm <semver>
• 4 config updates applied
• ESLint rules migrated to Oxlint

> cat package.json # eslint-plugin-survives stays in devDependencies (eslint itself is removed)
{
"name": "migration-eslint-jsplugins-preserve",
"scripts": {
"lint": "vp lint .",
"prepare": "vp config"
},
"devDependencies": {
"eslint-plugin-survives": "^1.0.0",
"vite": "catalog:",
"vite-plus": "catalog:"
},
"packageManager": "pnpm@<semver>"
}

> cat vite.config.ts # lint.jsPlugins keeps `eslint-plugin-survives`; lint.rules keeps `survives/no-fiction`
import { defineConfig } from 'vite-plus';

export default defineConfig({
staged: {
"*": "vp check --fix"
},
fmt: {},
lint: {
"plugins": [
"oxc",
"typescript",
"unicorn",
"react"
],
"jsPlugins": [
"eslint-plugin-survives",
{
"name": "vite-plus",
"specifier": "vite-plus/oxlint-plugin"
}
],
"categories": {
"correctness": "warn"
},
"env": {
"builtin": true
},
"rules": {
"survives/no-fiction": "warn",
"vite-plus/prefer-vite-plus-imports": "error"
},
"options": {
"typeAware": true,
"typeCheck": true
}
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"commands": [
"vp migrate --no-interactive # plugin referenced via lint.jsPlugins must be preserved through cleanup AND sanitization",
"cat package.json # eslint-plugin-survives stays in devDependencies (eslint itself is removed)",
"cat vite.config.ts # lint.jsPlugins keeps `eslint-plugin-survives`; lint.rules keeps `survives/no-fiction`"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default [
{
rules: {
'no-unused-vars': 'error',
},
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "migration-eslint-monorepo-plugins-in-packages",
"scripts": {
"lint": "eslint ."
},
"devDependencies": {
"eslint": "^9.0.0",
"eslint-config-airbnb": "^19.0.0",
"vite": "^7.0.0"
},
"packageManager": "pnpm@10.18.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"*.ts": "eslint --fix"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Per-workspace eslint config — should be deleted by the migration
// alongside the root config (covers the workspace-config-deletion path).
export default [
{
rules: {
'no-console': 'warn',
},
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "@test/app",
"scripts": {
"dev": "vite",
"lint": "eslint ."
},
"devDependencies": {
"@typescript-eslint/parser": "^8.0.0",
"@typescript-eslint/utils": "^8.0.0",
"eslint": "^9.0.0",
"eslint-plugin-vue": "^10.0.0",
"vite": "^7.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "@test/lint-config",
"scripts": {
"lint": "eslint ."
},
"dependencies": {
"@stylistic/eslint-plugin": "^2.0.0",
"eslint-plugin-import": "^2.0.0",
"eslint-plugin-vue": "^10.0.0"
},
"peerDependencies": {
"eslint": ">=9"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
packages:
- packages/*
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
> vp migrate --no-interactive # workspace packages get the SAME aggressive cleanup as the root (deps, configs, lint-staged)

✔ Created vite.config.ts in vite.config.ts

✔ Merged .oxlintrc.json into vite.config.ts
◇ Migrated . to Vite+
• Node <semver> pnpm <semver>
• 3 config updates applied
• ESLint rules migrated to Oxlint

> cat package.json # root: eslint + eslint-config-airbnb removed
{
"name": "migration-eslint-monorepo-plugins-in-packages",
"scripts": {
"lint": "vp lint .",
"prepare": "vp config"
},
"devDependencies": {
"vite": "catalog:",
"vite-plus": "catalog:"
},
"packageManager": "pnpm@<semver>"
}

> cat packages/app/package.json # workspace: eslint, eslint-plugin-vue, @typescript-eslint/parser removed; @typescript-eslint/utils preserved (reusable AST lib)
{
"name": "@test/app",
"scripts": {
"dev": "vp dev",
"lint": "vp lint ."
},
"devDependencies": {
"@typescript-eslint/utils": "^8.0.0",
"vite": "catalog:",
"vite-plus": "catalog:"
}
}

> cat packages/lint-config/package.json # workspace: all eslint-plugin-* removed; peerDeps.eslint scrubbed (field deleted when empty)
{
"name": "@test/lint-config",
"scripts": {
"lint": "vp lint ."
}
}

> test ! -f packages/app/eslint.config.mjs # workspace eslint config is deleted
> cat packages/app/.lintstagedrc.json # workspace lint-staged rewritten (eslint --fix → vp lint --fix)
{
"*.ts": "vp lint --fix"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"commands": [
"vp migrate --no-interactive # workspace packages get the SAME aggressive cleanup as the root (deps, configs, lint-staged)",
"cat package.json # root: eslint + eslint-config-airbnb removed",
"cat packages/app/package.json # workspace: eslint, eslint-plugin-vue, @typescript-eslint/parser removed; @typescript-eslint/utils preserved (reusable AST lib)",
"cat packages/lint-config/package.json # workspace: all eslint-plugin-* removed; peerDeps.eslint scrubbed (field deleted when empty)",
"test ! -f packages/app/eslint.config.mjs # workspace eslint config is deleted",
"cat packages/app/.lintstagedrc.json # workspace lint-staged rewritten (eslint --fix → vp lint --fix)"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@
"name": "@test/utils",
"scripts": {
"lint": "vp lint ."
},
"devDependencies": {}
}
}

> test ! -f eslint.config.mjs # check root eslint config is removed
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Stand-in for the auto-generated `.nuxt/eslint.config.mjs` flow.
// We don't actually re-export from `.nuxt/` here so the snap-test
// sandbox can load this file without running `nuxt prepare` first.
export default [
{
rules: {
'no-unused-vars': 'error',
},
},
];
Loading
Loading