groupImports
Imports as one statement per module (type-only names in one of their own): import { a, b as c } from "x";,
wrapped when long. Default and namespace imports stay as they are. In order of module.
export function groupImports(lines: string[]): string[] { const named = new Map<string, { type: boolean; from: string; names: string[] }>(); const other: { from: string; line: string }[] = []; for (const l of lines) { const m = /^import (type )?\{ (.+) \} from ("[^"]*");$/.exec(l); if (!m) { other.push({ from: /from ("[^"]*");$/.exec(l)?.[1] ?? "", line: l }); continue; } const key = `${m[1] ?? ""}${m[3]}`; const g = named.get(key) ?? { type: !!m[1], from: m[3]!, names: [] }; named.set(key, g); g.names.push(m[2]!); } const statement = (g: { type: boolean; from: string; names: string[] }) => { const head = `import ${g.type ? "type " : ""}`; const one = `${head}{ ${g.names.join(", ")} } from ${g.from};`; if (one.length <= 120) return one; // wrapped: as many names to a line as fit const rows: string[] = []; for (const n of g.names) { const last = rows.at(-1); if (last !== undefined && last.length + n.length + 2 <= 116) rows[rows.length - 1] = `${last} ${n},`; else rows.push(`${n},`); } return `${head}{\n${rows.map((r) => ` ${r}`).join("\n")}\n} from ${g.from};`; }; const all = [...[...named.values()].map((g) => ({ from: g.from, type: g.type, line: statement(g) })), ...other.map((o) => ({ ...o, type: false }))]; return all.sort((a, b) => (a.from < b.from ? -1 : a.from > b.from ? 1 : Number(a.type) - Number(b.type))).map((x) => x.line); }