mirror of
https://github.com/chenasraf/dotfiles.git
synced 2026-05-18 01:29:06 +00:00
feat: recreate tblf in ts
This commit is contained in:
@@ -5,10 +5,12 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
"tx": "tmux.js"
|
||||
"tx": "tmux.js",
|
||||
"tblf": "tblf.js"
|
||||
},
|
||||
"scripts": {
|
||||
"tmux": "ts-node src/tmux.ts",
|
||||
"tx": "ts-node src/tmux.ts",
|
||||
"tblf": "ts-node src/tblf.ts",
|
||||
"build": "tsc && cp package.json build/",
|
||||
"ginst": "pnpm build && pnpm i -g utils@file:$(pwd)/build",
|
||||
"dev": "tsc -w"
|
||||
|
||||
51
utils/src/common.ts
Normal file
51
utils/src/common.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { spawn } from 'node:child_process'
|
||||
|
||||
export function log({ verbose, dry }: Opts, ...content: any[]) {
|
||||
if (!verbose && !dry) return
|
||||
console.log(...content)
|
||||
}
|
||||
|
||||
export type Opts = {
|
||||
key: string
|
||||
verbose: boolean
|
||||
dry: boolean
|
||||
}
|
||||
|
||||
export async function runCommand(opts: Opts, command: string) {
|
||||
const [cmd, ...args] = command.split(' ')
|
||||
log(opts, '$ ' + command)
|
||||
if (opts.dry) return 0
|
||||
const proc = spawn(cmd, args, { stdio: 'inherit' })
|
||||
return new Promise((resolve, reject) => {
|
||||
proc.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
resolve(code)
|
||||
} else {
|
||||
reject(code)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export async function getCommandOutput(
|
||||
opts: Opts,
|
||||
command: string,
|
||||
): Promise<{ code: number; output: string }> {
|
||||
const [cmd, ...args] = command.split(' ')
|
||||
log(opts, '$ ' + command)
|
||||
if (opts.dry) return { code: 0, output: '' }
|
||||
const proc = spawn(cmd, args, { stdio: 'pipe' })
|
||||
return new Promise<{ code: number; output: string }>((resolve, reject) => {
|
||||
let output = ''
|
||||
proc.stdout.on('data', (data) => {
|
||||
output += data.toString()
|
||||
})
|
||||
proc.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
resolve({ code, output })
|
||||
} else {
|
||||
reject(code)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
97
utils/src/tblf.ts
Normal file
97
utils/src/tblf.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { massarg } from 'massarg'
|
||||
import { Opts as _Opts, log } from './common'
|
||||
|
||||
type Opts = _Opts & {
|
||||
input?: string
|
||||
header?: string
|
||||
separator?: string
|
||||
outputSeparator?: string
|
||||
}
|
||||
|
||||
async function main(opts: Opts) {
|
||||
let input = opts.input || ''
|
||||
if (!opts.input) {
|
||||
// read from stdin
|
||||
log(opts, 'Reading from stdin...')
|
||||
input = await new Promise((resolve) => {
|
||||
let data = ''
|
||||
process.stdin.on('data', (chunk) => {
|
||||
data += chunk
|
||||
})
|
||||
process.stdin.on('end', () => {
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
input = input.replaceAll('\\n', '\n')
|
||||
}
|
||||
|
||||
if (!input) {
|
||||
throw new Error('No input provided.')
|
||||
}
|
||||
|
||||
log(opts, 'Input:', input + '\n')
|
||||
|
||||
const lines = input.split('\n')
|
||||
if (opts.header) {
|
||||
lines.unshift(opts.header)
|
||||
}
|
||||
const separator = opts.separator ? new RegExp(opts.separator) : /[\s\t]+/
|
||||
const outputSeparator = opts.outputSeparator || ' '
|
||||
const rows = lines.map((line) => line.split(separator))
|
||||
const columnMaxes = new Array(rows[0].length).fill(0)
|
||||
|
||||
for (const row of rows) {
|
||||
for (let i = 0; i < row.length; i++) {
|
||||
columnMaxes[i] = Math.max(columnMaxes[i], row[i].length)
|
||||
}
|
||||
}
|
||||
|
||||
let output = ''
|
||||
for (const row of rows) {
|
||||
for (let i = 0; i < row.length; i++) {
|
||||
const column = row[i]
|
||||
output += column.padEnd(columnMaxes[i]) + outputSeparator
|
||||
}
|
||||
output += '\n'
|
||||
}
|
||||
|
||||
console.log(output.trimEnd())
|
||||
}
|
||||
|
||||
massarg<Opts>({
|
||||
name: 'tblf',
|
||||
description: 'Generate a table from a file or stdin.',
|
||||
})
|
||||
.main(main)
|
||||
.option({
|
||||
name: 'input',
|
||||
description: 'The input file to read from.',
|
||||
aliases: ['i'],
|
||||
isDefault: true,
|
||||
})
|
||||
.option({
|
||||
name: 'header',
|
||||
description: 'The header to prepend to the input, which will be aligned.',
|
||||
aliases: ['th'],
|
||||
})
|
||||
.option({
|
||||
name: 'separator',
|
||||
description: 'The separator to use when reading from stdin.',
|
||||
aliases: ['s'],
|
||||
})
|
||||
.option({
|
||||
name: 'output-separator',
|
||||
description: 'The separator to use when writing to stdout.',
|
||||
aliases: ['o'],
|
||||
})
|
||||
.flag({
|
||||
name: 'verbose',
|
||||
aliases: ['v'],
|
||||
description: 'Print verbose output.',
|
||||
})
|
||||
.help({
|
||||
bindOption: true,
|
||||
bindCommand: true,
|
||||
})
|
||||
.parse()
|
||||
Reference in New Issue
Block a user