Skip to content
Open
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
16 changes: 16 additions & 0 deletions docs/errors/OXDT0005.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
outline: deep
---
# OXDT0005: Oxlint Setup Failed

## Message
> Failed to set up Oxlint: `{reason}`

## Cause
Vite DevTools could not install or initialize Oxlint in the project root.

## Fix
Check the project package manager and configuration, then try again.

## Source
- [`packages/oxc/src/node/rpc/functions/oxlint-migrate.ts`](https://github.com/vitejs/devtools/blob/main/packages/oxc/src/node/rpc/functions/oxlint-migrate.ts) — runs the installation, initialization, and migration commands.
1 change: 1 addition & 0 deletions docs/errors/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,4 @@ Emitted by `@vitejs/devtools-oxc`.
| [OXDT0002](./OXDT0002) | error | Invalid Lint Result ID |
| [OXDT0003](./OXDT0003) | error | Failed to Delete Lint Result |
| [OXDT0004](./OXDT0004) | error | Oxlint Config Inspection Failed |
| [OXDT0005](./OXDT0005) | error | Oxlint Setup Failed |
1 change: 1 addition & 0 deletions packages/oxc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"cac": "catalog:deps",
"devframe": "catalog:deps",
"local-pkg": "catalog:deps",
"nypm": "catalog:deps",
"nostics": "catalog:deps",
"pathe": "catalog:deps",
"tinyexec": "catalog:deps"
Expand Down
186 changes: 186 additions & 0 deletions packages/oxc/src/app/components/SetupOxlintDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
<script setup lang="ts">
import { DEVTOOLS_TERMINALS_DOCK_ID } from '@vitejs/devtools-kit/constants'
import ActionButton from '@vitejs/devtools-ui/components/Action/ActionButton.vue'
import FormCheckbox from '@vitejs/devtools-ui/components/Form/FormCheckbox.vue'
import OverlayModal from '@vitejs/devtools-ui/components/Overlay/OverlayModal.vue'
import VisualLoading from '@vitejs/devtools-ui/components/Visual/VisualLoading.vue'
import { ref, watch } from 'vue'
import { useRpc } from '#imports'

const emit = defineEmits<{ refresh: [] }>()
const open = defineModel<boolean>('open', { default: false })
const rpc = useRpc()

type Stage = 'confirm' | 'running' | 'error'

const stage = ref<Stage>('confirm')
const canMigrate = ref(false)
const migrate = ref(true)
const gitDirty = ref(false)
const commandLine = ref('')
const sessionId = ref<string>()
const errorMessage = ref<string>()
let previewRequest = 0

async function loadPreview() {
const request = ++previewRequest
try {
const preview = await rpc.value.call('devtools-oxc:setup-preview', {
migrate: migrate.value,
})
if (request !== previewRequest) return
canMigrate.value = preview.canMigrate
if (!preview.canMigrate) migrate.value = false
commandLine.value = preview.command
gitDirty.value = preview.gitDirty
} catch (error) {
if (request !== previewRequest) return
errorMessage.value = error instanceof Error ? error.message : String(error)
}
}

watch(open, async isOpen => {
if (!isOpen) return
stage.value = 'confirm'
canMigrate.value = false
migrate.value = true
gitDirty.value = false
commandLine.value = ''
sessionId.value = undefined
errorMessage.value = undefined
await loadPreview()
})

watch(migrate, () => {
if (open.value && stage.value === 'confirm') loadPreview()
})

async function confirmSetup() {
stage.value = 'running'
errorMessage.value = undefined
try {
const action =
migrate.value && canMigrate.value
? 'devtools-oxc:migrate-eslint'
: 'devtools-oxc:install-oxlint'
const result = await rpc.value.call(action)
sessionId.value = result.sessionId
await rpc.value.call('devtools-oxc:wait-for-setup')
if (!open.value) return
emit('refresh')
open.value = false
} catch (error) {
if (!open.value) return
stage.value = 'error'
errorMessage.value = error instanceof Error ? error.message : String(error)
}
}

async function viewInTerminal() {
if (sessionId.value) {
await rpc.value.call('hub:docks:activate', {
dockId: DEVTOOLS_TERMINALS_DOCK_ID,
params: { sessionId: sessionId.value },
})
}
open.value = false
}
</script>

<template>
<OverlayModal v-model:open="open">
<template #title> Setup Oxlint with devtools</template>

<div class="flex flex-col gap-4 w-140 max-w-full min-h-64">
<template v-if="stage === 'confirm'">
<p class="m0 op70 text-sm">
<template v-if="canMigrate && migrate">
Oxlint will be installed as a development dependency, and
<a
href="https://github.com/oxc-project/oxlint-migrate"
target="_blank"
rel="noopener noreferrer"
class="color-active hover:underline"
>
oxlint-migrate
</a>
will convert your ESLint flat config into an Oxlint configuration.
</template>
<template v-else>
Oxlint will be installed as a development dependency and create a starter configuration
with <code>oxlint --init</code>.
</template>
</p>

<div>
<pre
class="m0 p3 rounded-lg border border-base bg-code font-mono text-sm of-auto text-left"
><code>{{ commandLine || 'Loading…' }}</code></pre>
</div>

<p v-if="gitDirty" class="m0 text-amber text-sm flex gap-2 items-start">
<span class="i-ph-warning-duotone mt-0.5 shrink-0" />
<span>The Git working tree is not clean. Setup may modify project files.</span>
</p>

<p v-if="errorMessage" class="m0 text-red text-sm">
{{ errorMessage }}
</p>

<div class="flex-auto" />
<div class="flex items-center justify-between gap-2">
<FormCheckbox v-if="canMigrate" v-model="migrate">
<span class="text-sm">Migrate from ESLint</span>
</FormCheckbox>
<div class="flex gap-2">
<ActionButton @click="open = false"> Cancel </ActionButton>
<ActionButton
variant="primary"
icon="i-ph-rocket-launch-duotone"
@click="confirmSetup()"
>
Setup Oxlint
</ActionButton>
</div>
</div>
</template>

<template v-else-if="stage === 'running'">
<VisualLoading class="flex-auto" text="Setting up Oxlint…" />
<div class="flex justify-end gap-2">
<ActionButton @click="open = false"> Dismiss </ActionButton>
<ActionButton
v-if="sessionId"
variant="primary"
icon="i-ph-terminal-window-duotone"
@click="viewInTerminal()"
>
View in terminals
</ActionButton>
</div>
</template>

<template v-else>
<div class="flex gap-2 items-center text-red">
<span class="i-ph-x-circle-duotone" />
Oxlint setup failed.
</div>
<p v-if="errorMessage" class="m0 op70 text-sm">
{{ errorMessage }}
</p>
<div class="flex-auto" />
<div class="flex justify-end gap-2">
<ActionButton @click="open = false"> Close </ActionButton>
<ActionButton
v-if="sessionId"
variant="primary"
icon="i-ph-terminal-window-duotone"
@click="viewInTerminal()"
>
View in terminals
</ActionButton>
</div>
</template>
</div>
</OverlayModal>
</template>
Loading
Loading