Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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,21 @@
// Authored against the API vite-plus re-exports, with no `@oxlint/plugins`
// dependency of its own: the point of the test is that this resolves and loads.
import { definePlugin, defineRule } from 'vite-plus/lint/plugins';

const noFoo = defineRule({
meta: { messages: { noFoo: 'Do not name things "foo".' } },
create(context) {
return {
Identifier(node) {
if (node.name === 'foo') {
context.report({ node, messageId: 'noFoo' });
}
},
};
},
});

export default definePlugin({
meta: { name: 'local' },
rules: { 'no-foo': noFoo },
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "lint-oxlint-plugin-api",
"version": "0.0.0",
"private": true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[[case]]
name = "lint_oxlint_plugin_api"
vp = "local"
skip-platforms = [{ os = "linux", libc = "musl" }]
steps = [
{ argv = [
"vp",
"lint",
"src/uses-foo.ts",
], comment = "the local JS plugin imports its API from vite-plus/lint/plugins and declares no @oxlint/plugins dependency; a reported diagnostic proves the export resolved and loaded", continue-on-failure = true },
{ argv = [
"vp",
"lint",
"src/legacy-imports.ts",
], comment = "prefer-vite-plus-imports flags the legacy authoring specifiers", continue-on-failure = true },
{ argv = [
"vp",
"lint",
"src/config-surface.ts",
], comment = "oxlint still owns defineConfig/OxlintOverride, so these are clean", continue-on-failure = true },
{ argv = [
"vp",
"lint",
"--fix",
"src/legacy-imports.ts",
], comment = "autofix mirrors what vp migrate rewrites", continue-on-failure = true },
{ argv = [
"vpt",
"print-file",
"src/legacy-imports.ts",
], continue-on-failure = true },
{ argv = [
"vp",
"lint",
"src/legacy-imports.ts",
], comment = "confirm the rewritten file is clean", continue-on-failure = true },
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# lint_oxlint_plugin_api

## `vp lint src/uses-foo.ts`

the local JS plugin imports its API from vite-plus/lint/plugins and declares no @oxlint/plugins dependency; a reported diagnostic proves the export resolved and loaded

**Exit code:** 1

```

× local(no-foo): Do not name things "foo".
╭─[src/uses-foo.ts:1:14]
1 │ export const foo = 1;
· ───
2 │ export const bar = 2;
╰────

Found 0 warnings and 1 error.
Finished in <duration> on 1 file with <n> rules using <n> threads.
```

## `vp lint src/legacy-imports.ts`

prefer-vite-plus-imports flags the legacy authoring specifiers

**Exit code:** 1

```

× vite-plus(prefer-vite-plus-imports): Use 'vite-plus/lint/plugins' instead of 'oxlint' in Vite+ projects.
╭─[src/legacy-imports.ts:1:28]
1 │ import { defineRule } from 'oxlint';
· ────────
2 │ import { definePlugin } from '@oxlint/plugins';
╰────

× vite-plus(prefer-vite-plus-imports): Use 'vite-plus/lint/plugins' instead of '@oxlint/plugins' in Vite+ projects.
╭─[src/legacy-imports.ts:2:30]
1 │ import { defineRule } from 'oxlint';
2 │ import { definePlugin } from '@oxlint/plugins';
· ─────────────────
3 │ import { RuleTester } from 'oxlint/plugins-dev';
╰────

× vite-plus(prefer-vite-plus-imports): Use 'vite-plus/lint/rule-tester' instead of 'oxlint/plugins-dev' in Vite+ projects.
╭─[src/legacy-imports.ts:3:28]
2 │ import { definePlugin } from '@oxlint/plugins';
3 │ import { RuleTester } from 'oxlint/plugins-dev';
· ────────────────────
4 │
╰────

Found 0 warnings and 3 errors.
Finished in <duration> on 1 file with <n> rules using <n> threads.
```

## `vp lint src/config-surface.ts`

oxlint still owns defineConfig/OxlintOverride, so these are clean

```
Found 0 warnings and 0 errors.
Finished in <duration> on 1 file with <n> rules using <n> threads.
```

## `vp lint --fix src/legacy-imports.ts`

autofix mirrors what vp migrate rewrites

```
Found 0 warnings and 0 errors.
Finished in <duration> on 1 file with <n> rules using <n> threads.
```

## `vpt print-file src/legacy-imports.ts`

```
import { defineRule } from 'vite-plus/lint/plugins';
import { definePlugin } from 'vite-plus/lint/plugins';
import { RuleTester } from 'vite-plus/lint/rule-tester';

export { defineRule, definePlugin, RuleTester };
```

## `vp lint src/legacy-imports.ts`

confirm the rewritten file is clean

```
Found 0 warnings and 0 errors.
Finished in <duration> on 1 file with <n> rules using <n> threads.
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from 'oxlint';
import type { OxlintOverride } from 'oxlint';

export const override: OxlintOverride = { files: ['**/*.ts'] };

export default defineConfig({ overrides: [override] });
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { defineRule } from 'oxlint';
import { definePlugin } from '@oxlint/plugins';
import { RuleTester } from 'oxlint/plugins-dev';

export { defineRule, definePlugin, RuleTester };
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const foo = 1;
export const bar = 2;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { defineConfig } from 'vite-plus';

export default defineConfig({
lint: {
jsPlugins: [
'./lint/plugin.js',
{ name: 'vite-plus', specifier: 'vite-plus/oxlint-plugin' },
],
rules: {
'local/no-foo': 'error',
'vite-plus/prefer-vite-plus-imports': 'error',
},
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"jsPlugins": ["./lint/plugin.js"],
"rules": {
"local/no-foo": "error"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { defineRule } from 'oxlint';

export const noFoo = defineRule({
meta: { messages: { noFoo: 'Do not name things "foo".' } },
create(context) {
return {
Identifier(node) {
if (node.name === 'foo') {
context.report({ node, messageId: 'noFoo' });
}
},
};
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { Context } from 'oxlint';
import { RuleTester } from 'oxlint/plugins-dev';

import { noFoo } from './no-foo.js';

export type RuleContext = Context;

new RuleTester().run('no-foo', noFoo, {
valid: ['const bar = 1;'],
invalid: [{ code: 'const foo = 1;', errors: 1 }],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { definePlugin } from '@oxlint/plugins';

import { noFoo } from './no-foo.js';

export default definePlugin({
meta: { name: 'local' },
rules: { 'no-foo': noFoo },
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'oxlint';
import type { OxlintOverride } from 'oxlint';

export const testOverride: OxlintOverride = {
files: ['**/*.test.ts'],
rules: { 'local/no-foo': 'off' },
};

export default defineConfig({ overrides: [testOverride] });
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "migration-oxlint-js-plugin-imports",
"scripts": {
"lint": "oxlint ."
},
"devDependencies": {
"@oxlint/plugins": "^1.0.0",
"oxlint": "^1.0.0",
"vite": "^7.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[[case]]
name = "migration_oxlint_js_plugin_imports"
vp = "global"
steps = [
{ argv = [
"vp",
"migrate",
"--no-interactive",
], comment = "the standalone oxlint dependency goes away, so the JS plugin's authoring imports have to move to vite-plus", continue-on-failure = true },
{ argv = [
"vpt",
"print-file",
"package.json",
], comment = "oxlint is removed and nothing replaces it: the API now comes from vite-plus. @oxlint/plugins is deliberately left in place (it is inert once the imports are rewritten, and stripping it would also strip the peer dep of a published Oxlint plugin)", continue-on-failure = true },
{ argv = [
"vpt",
"print-file",
"lint/no-foo.js",
], comment = "legacy `defineRule` from 'oxlint' -> 'vite-plus/lint/plugins'", continue-on-failure = true },
{ argv = [
"vpt",
"print-file",
"lint/plugin.js",
], comment = "'@oxlint/plugins' -> 'vite-plus/lint/plugins'", continue-on-failure = true },
{ argv = [
"vpt",
"print-file",
"lint/no-foo.test.ts",
], comment = "RuleTester moved to 'oxlint/plugins-dev' upstream and breaks the same way -> 'vite-plus/lint/rule-tester'; the plugin type import follows the runtime API", continue-on-failure = true },
{ argv = [
"vpt",
"print-file",
"lint/shared-config.ts",
], comment = "config surface is NOT redirected: vite-plus/lint/plugins has no defineConfig/OxlintOverride", continue-on-failure = true },
{ argv = [
"vpt",
"print-file",
"vite.config.ts",
], comment = "the jsPlugins entry survives the .oxlintrc.json merge and still points at the (now rewritten) plugin file. KNOWN PRE-EXISTING GAP, unrelated to the import rewrite: `local/no-foo` is dropped because sanitizeMigratedOxlintConfig derives a plugin's rule namespace from its package name, and a relative-path plugin has none (its namespace comes from `meta.name` at load time). Recorded so a fix shows up as a snapshot diff", continue-on-failure = true },
]
Loading
Loading