add auto formatting and experimental hooks feature

This commit is contained in:
Dax Raad
2025-06-26 22:12:23 -04:00
parent a4f32d602b
commit f8b78f08b4
8 changed files with 269 additions and 3 deletions

View File

@@ -0,0 +1,143 @@
import { App } from '../app/app'
import { BunProc } from '../bun'
import { Config } from '../config/config'
import { Log } from '../util/log'
import path from 'path'
export namespace Format {
const log = Log.create({ service: 'format' })
const state = App.state('format', async () => {
const hooks: Record<string, Hook[]> = {}
for (const item of FORMATTERS) {
if (await item.enabled()) {
for (const ext of item.extensions) {
const list = hooks[ext] ?? []
list.push({
command: item.command,
environment: item.environment,
})
hooks[ext] = list
}
}
}
const cfg = await Config.get()
for (const [file, items] of Object.entries(
cfg.experimental?.hook?.file_edited ?? {},
)) {
for (const item of items) {
const list = hooks[file] ?? []
list.push({
command: item.command,
environment: item.environment,
})
hooks[file] = list
}
}
return {
hooks,
}
})
export async function run(file: string) {
log.info('formatting', { file })
const { hooks } = await state()
const ext = path.extname(file)
const match = hooks[ext]
if (!match) return
for (const item of match) {
log.info('running', { command: item.command })
const proc = Bun.spawn({
cmd: item.command.map((x) => x.replace('$FILE', file)),
cwd: App.info().path.cwd,
env: item.environment,
})
const exit = await proc.exited
if (exit !== 0)
log.error('failed', {
command: item.command,
...item.environment,
})
}
}
interface Hook {
command: string[]
environment?: Record<string, string>
}
interface Native {
name: string
command: string[]
environment?: Record<string, string>
extensions: string[]
enabled(): Promise<boolean>
}
const FORMATTERS: Native[] = [
{
name: 'prettier',
extensions: [
'.js',
'.jsx',
'.mjs',
'.cjs',
'.ts',
'.tsx',
'.mts',
'.cts',
'.html',
'.htm',
'.css',
'.scss',
'.sass',
'.less',
'.vue',
'.svelte',
'.json',
'.jsonc',
'.yaml',
'.yml',
'.toml',
'.xml',
'.md',
'.mdx',
'.php',
'.rb',
'.java',
'.go',
'.rs',
'.swift',
'.kt',
'.kts',
'.sol',
'.graphql',
'.gql',
],
command: [BunProc.which(), 'run', 'prettier', '--write', '$FILE'],
environment: {
BUN_BE_BUN: '1',
},
async enabled() {
try {
const proc = Bun.spawn({
cmd: [BunProc.which(), 'run', 'prettier', '--version'],
cwd: App.info().path.cwd,
env: {
BUN_BE_BUN: '1',
},
stdout: 'ignore',
stderr: 'ignore',
})
const exit = await proc.exited
return exit === 0
} catch {
return false
}
},
},
]
}