← back to Dw Slideshow Gen
lib/util.js
41 lines
// lib/util.js — tiny shared helpers (zero deps)
import fs from 'node:fs'
import path from 'node:path'
export const slugify = (s = '') =>
String(s).toLowerCase().trim()
.replace(/['’"]/g, '')
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '')
.slice(0, 60) || 'untitled'
export const titleCase = (s = '') =>
String(s).replace(/\w\S*/g, w => w[0].toUpperCase() + w.slice(1))
// ISO-ish date string used in spec ids / output folders. We can't call Date.now()
// in some sandboxes, but the CLI runs in a normal shell, so a plain Date is fine here.
export const today = () => {
const d = new Date()
const p = n => String(n).padStart(2, '0')
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`
}
export const stamp = () => new Date().toISOString()
export const ensureDir = d => { fs.mkdirSync(d, { recursive: true }); return d }
export const readJSON = p => JSON.parse(fs.readFileSync(p, 'utf8'))
export const writeJSON = (p, obj) => {
ensureDir(path.dirname(p))
fs.writeFileSync(p, JSON.stringify(obj, null, 2))
return p
}
export const exists = p => { try { fs.accessSync(p); return true } catch { return false } }
// cap a string to n chars on a word boundary, with an ellipsis
export const cap = (s = '', n = 80) => {
s = String(s).trim()
if (s.length <= n) return s
const cut = s.slice(0, n)
const sp = cut.lastIndexOf(' ')
return (sp > n * 0.6 ? cut.slice(0, sp) : cut).replace(/[,;:.\s]+$/, '') + '…'
}