Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/danielroe/roe.dev. This is the code and content for my personal website, built in Nuxt. roe.dev
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132#!/usr/bin/env node/** * Regenerate TS types from the lexicon JSON files under `lexicons/`. * * The generated `shared/lex/index.ts` (full XRPC client surface) is removed * after generation because we drive the PDS via `AtpAgent` from * `@atproto/api` and only need the per-record types and the validator * registry. A hand-written barrel is restored from this script. */import { spawn } from 'node:child_process'import { readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'import { join, resolve } from 'node:path'import { fileURLToPath } from 'node:url'
const here = fileURLToPath(new URL('.', import.meta.url))const root = resolve(here, '..')const lexRoot = join(root, 'lexicons')const outDir = join(root, 'shared/lex')
const lexiconFiles = []function walk (dir) { for (const entry of readdirSync(dir, { withFileTypes: true })) { const full = join(dir, entry.name) if (entry.isDirectory()) walk(full) else if (entry.isFile() && entry.name.endsWith('.json')) lexiconFiles.push(full) }}walk(lexRoot)
if (!lexiconFiles.length) { console.error('No lexicon JSON files found under', lexRoot) process.exit(1)}
console.log(`Generating types for ${lexiconFiles.length} lexicons → ${outDir}`)
/** * `@atproto/lex-cli gen-api` prompts to overwrite each generated file. We * pipe a stream of `y\n` into its stdin instead of shelling out to * `yes y | …`, so lexicon paths containing spaces or shell metacharacters * can't break (or be abused to alter) the command line. */await new Promise((resolvePromise, reject) => { const child = spawn( 'pnpm', ['dlx', '@atproto/lex-cli', 'gen-api', outDir, ...lexiconFiles], { stdio: ['pipe', 'inherit', 'inherit'], cwd: root }, ) const writeY = () => { while (child.stdin.writable && child.stdin.write('y\n')) { // keep filling the buffer until the kernel says back off } } child.stdin.on('drain', writeY) child.stdin.on('error', () => {}) writeY() child.on('error', reject) child.on('exit', code => { child.stdin.end() if (code === 0) resolvePromise() else reject(new Error(`lex-cli exited with code ${code}`)) })})
/** * Rewrite the generated relative-imports from `.js` to `.ts` so Node’s native * type-stripping (`node script.ts`) can resolve them at runtime without a * loader. TypeScript is configured with `allowImportingTsExtensions` to * accept the same shape at type-check time. */function rewriteJsToTs (dir) { for (const entry of readdirSync(dir, { withFileTypes: true })) { const full = join(dir, entry.name) if (entry.isDirectory()) { rewriteJsToTs(full) continue } if (!entry.isFile() || !entry.name.endsWith('.ts')) continue const src = readFileSync(full, 'utf8') const next = src.replace(/((?:from|import)\s+['"])((?:\.{1,2}\/)[^'"]+?)\.js(['"])/g, '$1$2.ts$3') if (next !== src) writeFileSync(full, next) }}rewriteJsToTs(outDir)
const generatedClient = join(outDir, 'index.ts')rmSync(generatedClient, { force: true })
/** * Build the namespace-export lines by scanning the generated `types/` * tree, so adding a new lexicon JSON doesn't need a matching edit to this * script. */function collectNamespaceExports (dir, prefix = '') { const lines = [] for (const entry of readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) { const full = join(dir, entry.name) if (entry.isDirectory()) { lines.push(...collectNamespaceExports(full, `${prefix}${capitalise(entry.name)}`)) continue } if (!entry.isFile() || !entry.name.endsWith('.ts')) continue const base = entry.name.replace(/\.ts$/, '') const namespace = `${prefix}${capitalise(base)}` const relPath = `./types/${full.slice(typesRoot.length + 1).replace(/\\/g, '/')}` lines.push(`export * as ${namespace} from '${relPath}'`) } return lines}function capitalise (s) { return s.charAt(0).toUpperCase() + s.slice(1)}const typesRoot = join(outDir, 'types')const namespaceLines = collectNamespaceExports(typesRoot).join('\n')
const barrel = `/** * Auto-generated barrel for the dev.roe lexicons. Re-run \`pnpm lex:gen\` to * refresh after adding or removing lexicon JSON files. * * The generated \`gen-api\` client surface was deleted because we drive the PDS * via the existing \`AtpAgent\` from \`@atproto/api\`; we only need the typed * record interfaces and the validator registry. */export { schemas, lexicons, validate, schemaDict } from './lexicons.ts'export type { $Typed, Un$Typed, OmitKey } from './util.ts'
${namespaceLines}`writeFileSync(generatedClient, barrel)
console.log('Done. Barrel written to', generatedClient)