bring back type sniffing

This commit is contained in:
Chris Wanstrath 2025-10-07 19:34:00 -07:00
parent ebfddbb278
commit 34379e40d4
5 changed files with 30 additions and 8 deletions

View File

@ -4,6 +4,7 @@
import { commandPath } from "@/commands" import { commandPath } from "@/commands"
import type { CommandOutput } from "@/shared/types" import type { CommandOutput } from "@/shared/types"
import { moduleExports, type ExportInfo } from "@/sniffer"
import commands from "./commands" import commands from "./commands"
export default async function (cmd: string): Promise<CommandOutput> { export default async function (cmd: string): Promise<CommandOutput> {
@ -12,9 +13,14 @@ export default async function (cmd: string): Promise<CommandOutput> {
const path = commandPath(cmd) const path = commandPath(cmd)
if (!path) return await commands(cmd) if (!path) return await commands(cmd)
const signatures = await moduleExports(path)
const signature = signatures.default
const code = (await Bun.file(path).text()).split("\n") const code = (await Bun.file(path).text()).split("\n")
let docs = [] let docs = []
docs.push(usage(cmd, signature), "")
for (const line of code) { for (const line of code) {
if (line.startsWith("///")) { if (line.startsWith("///")) {
docs.push("Runs in the browser.\n") docs.push("Runs in the browser.\n")
@ -28,3 +34,18 @@ export default async function (cmd: string): Promise<CommandOutput> {
return docs.join("\n") return docs.join("\n")
} }
function usage(cmd: string, signature?: ExportInfo) {
let out: string[] = [`usage: ${cmd}`]
if (signature?.kind === "function" && signature.signatures.length) {
const params = signature.signatures[0]!.params
for (const param of params) {
let desc = `${param.name}=`
desc += param.default ? `${param.default}` : `<${param.type}>`
out.push(desc)
}
}
return out.join(" ")
}

View File

@ -82,7 +82,7 @@ export async function commandSource(name: string): Promise<string> {
export async function loadCommandModule(cmd: string) { export async function loadCommandModule(cmd: string) {
const path = commandPath(cmd) const path = commandPath(cmd)
if (!path) return if (!path) return
return await importUrl(path + "?t+" + Date.now()) return await importUrl(path + "?t=" + Date.now())
} }
let noseDirWatcher let noseDirWatcher

View File

@ -8,7 +8,7 @@ import { send } from "./websocket"
import { setState } from "./state" import { setState } from "./state"
export async function dispatchMessage(ws: any, msg: Message) { export async function dispatchMessage(ws: any, msg: Message) {
console.log("<- receive", msg) // console.log("<- receive", msg)
switch (msg.type) { switch (msg.type) {
case "input": case "input":
await inputMessage(ws, msg); break await inputMessage(ws, msg); break

View File

@ -30,8 +30,7 @@ export type ExportInfo =
let prevProgram: ts.Program | undefined let prevProgram: ts.Program | undefined
export async function moduleExports(file: string): Promise<Record<string, ExportInfo>> {
export async function allExports(file: string): Promise<ExportInfo[]> {
const program = ts.createProgram([file], { const program = ts.createProgram([file], {
target: ts.ScriptTarget.ESNext, target: ts.ScriptTarget.ESNext,
module: ts.ModuleKind.ESNext, module: ts.ModuleKind.ESNext,
@ -48,7 +47,7 @@ export async function allExports(file: string): Promise<ExportInfo[]> {
if (!sf) throw new SniffError(`File not found: ${file}`) if (!sf) throw new SniffError(`File not found: ${file}`)
const moduleSym = (sf as any).symbol as ts.Symbol | undefined const moduleSym = (sf as any).symbol as ts.Symbol | undefined
if (!moduleSym) return [] if (!moduleSym) return {}
const exportSymbols = checker.getExportsOfModule(moduleSym) const exportSymbols = checker.getExportsOfModule(moduleSym)
const result: ExportInfo[] = [] const result: ExportInfo[] = []
@ -92,7 +91,9 @@ export async function allExports(file: string): Promise<ExportInfo[]> {
} }
} }
return result return Object.fromEntries(
result.map(item => [item.name, item])
)
} }
if (import.meta.main) { if (import.meta.main) {
@ -101,5 +102,5 @@ if (import.meta.main) {
console.error("usage: sniff <path>") console.error("usage: sniff <path>")
process.exit(1) process.exit(1)
} }
console.log(await allExports(path)) console.log(await moduleExports(path))
} }

View File

@ -6,7 +6,7 @@ import type { Message } from "./shared/types"
const wsConnections: any[] = [] const wsConnections: any[] = []
export function send(ws: any, msg: Message) { export function send(ws: any, msg: Message) {
console.log("-> send", msg) // console.log("-> send", msg)
ws.send(JSON.stringify(msg)) ws.send(JSON.stringify(msg))
} }