You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

143 lines
4.2 KiB

/// <reference types="vitest/config" />
import path from 'node:path'
import { exec } from 'node:child_process'
import os from 'node:os'
import { defineConfig, loadEnv, type Plugin } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import babel from '@babel/core'
import addDataLocator from './plugins/add-data-locator.cjs'
function openInIdePlugin(): Plugin {
return {
name: 'open-in-ide-plugin',
apply: 'serve',
configureServer(server) {
server.middlewares.use((req, res, next) => {
const url = req.url?.split('?')[0]
if (url === '/api/open-in-ide' && req.method === 'POST') {
let body = ''
req.on('data', (chunk) => {
body += chunk
})
req.on('end', () => {
try {
const { locator } = JSON.parse(body)
if (!locator) {
res.statusCode = 400
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ message: 'Locator is required' }))
return
}
const isWindows = os.platform() === 'win32'
const bin = isWindows ? 'antigravity-ide.cmd' : 'antigravity-ide'
const cmd = `${bin} -r -g "${locator}"`
exec(cmd, (error) => {
if (error) {
console.error(`[open-in-ide] Error: ${error.message}`)
res.statusCode = 500
res.setHeader('Content-Type', 'application/json')
res.end(
JSON.stringify({
message: 'Failed to open file in IDE',
error: error.message,
})
)
return
}
res.statusCode = 200
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ success: true }))
})
} catch (err) {
console.error('[open-in-ide] Error parsing body:', err, 'raw body:', body)
res.statusCode = 500
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ message: 'Internal server error', error: String(err), rawBody: body }))
}
})
return
}
if (
(url === '/api/remote-logs' ||
url === '/api/remote-network' ||
url === '/api/dev-storage') &&
req.method === 'POST'
) {
res.statusCode = 200
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ ok: true }))
return
}
next()
})
},
}
}
function addDataLocatorPlugin(): Plugin {
return {
name: 'add-data-locator-plugin',
enforce: 'pre',
apply: 'serve',
transform(code, id) {
const cleanId = id.split('?')[0]
if (cleanId.includes('node_modules') || !/\.[jt]sx$/.test(cleanId)) {
return null
}
try {
const result = babel.transformSync(code, {
filename: cleanId,
configFile: false,
babelrc: false,
parserOpts: { plugins: ['jsx', 'typescript'] },
plugins: [addDataLocator],
})
if (result?.code) {
return { code: result.code, map: result.map }
}
} catch (err) {
console.error('[add-data-locator-plugin] Transform error:', err)
}
return null
},
}
}
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '')
/* مقصد بک‌اند برای پراکسیِ dev — همهٔ '/api/*' بدون CORS به این‌جا می‌رود. */
const apiTarget = env.VITE_API_TARGET ?? 'http://localhost:8000'
return {
plugins: [
openInIdePlugin(),
addDataLocatorPlugin(),
react(),
tailwindcss(),
],
resolve: {
alias: { '@': path.resolve(__dirname, './src') },
},
server: {
proxy: {
'/api': {
target: apiTarget,
changeOrigin: true,
},
'/static': {
target: apiTarget,
changeOrigin: true,
},
},
},
test: {
environment: 'node',
include: ['src/**/*.test.{ts,tsx}'],
},
}
})