AT Protocol lexicon schemas for Sifa professional profiles (id.sifa.*) sifa.id/
at main 114 lines 4.0 kB view raw
1/** 2 * Post-generation fixup for lex-cli output. 3 * 4 * 1. Replaces the generated index.ts with clean type re-exports (only id.sifa.* types) 5 * 2. Fixes missing .js import extensions for NodeNext compatibility 6 * 3. Injects the authProfileAccess lexicon into schemaDict/ids 7 */ 8import { readdir, readFile, writeFile } from 'node:fs/promises'; 9import { join, relative } from 'node:path'; 10 11const GENERATED_DIR = new URL('../src/generated', import.meta.url).pathname; 12const TYPES_DIR = join(GENERATED_DIR, 'types'); 13const LEXICONS_DIR = new URL('../lexicons', import.meta.url).pathname; 14 15const EXCLUDED_LEXICONS = [ 16 { file: 'id/sifa/authProfileAccess.json', dictKey: 'IdSifaAuthProfileAccess' }, 17]; 18 19async function getTypeFiles(dir) { 20 const entries = await readdir(dir, { withFileTypes: true, recursive: true }); 21 return entries 22 .filter((e) => e.isFile() && e.name.endsWith('.ts')) 23 .map((e) => join(e.parentPath ?? e.path, e.name)); 24} 25 26function toExportName(filePath) { 27 const rel = relative(TYPES_DIR, filePath).replace(/\.ts$/, ''); 28 return rel 29 .split('/') 30 .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)) 31 .join(''); 32} 33 34async function buildReplacementIndex(typeFiles) { 35 // Only export id.sifa.* types (not com.atproto.* or community.lexicon.*) 36 const sifaFiles = typeFiles.filter((f) => { 37 const rel = relative(TYPES_DIR, f); 38 return rel.startsWith('id/sifa/'); 39 }); 40 41 const exports = sifaFiles 42 .map((file) => { 43 const name = toExportName(file); 44 const relPath = './' + relative(GENERATED_DIR, file).replace(/\.ts$/, '.js'); 45 return `export * as ${name} from "${relPath}";`; 46 }) 47 .sort(); 48 49 return `/** 50 * GENERATED CODE - Re-exports only. 51 * Only id.sifa.* types are exported. External types (com.atproto.*, community.lexicon.*) 52 * are included in generation for reference resolution but not re-exported. 53 */ 54${exports.join('\n')} 55export { schemas, validate } from "./lexicons.js"; 56`; 57} 58 59async function fixImportExtensions(filePath) { 60 let content = await readFile(filePath, 'utf-8'); 61 const original = content; 62 content = content.replace(/from '(\.\.?\/[^']+?)(?<!\.js)'/g, "from '$1.js'"); 63 if (content !== original) { 64 await writeFile(filePath, content); 65 } 66} 67 68async function injectExcludedLexicons(lexiconsFile) { 69 let content = await readFile(lexiconsFile, 'utf-8'); 70 71 for (const { file, dictKey } of EXCLUDED_LEXICONS) { 72 if (content.includes(` ${dictKey}:`)) continue; 73 74 const lexiconJson = JSON.parse(await readFile(join(LEXICONS_DIR, file), 'utf-8')); 75 76 const schemaDictEntry = ` ${dictKey}: ${JSON.stringify(lexiconJson, null, 4).replace(/\n/g, '\n ')},\n`; 77 content = content.replace( 78 '} as const satisfies Record<string, LexiconDoc>', 79 `${schemaDictEntry}} as const satisfies Record<string, LexiconDoc>`, 80 ); 81 82 const idsEntry = ` ${dictKey}: '${lexiconJson.id}',\n`; 83 const idsMatch = content.match(/export const ids = \{[\s\S]*?\} as const/); 84 if (idsMatch && !idsMatch[0].includes(dictKey)) { 85 content = content.replace(/(\} as const)$/m, `${idsEntry}$1`); 86 } 87 88 console.log(`Injected excluded lexicon: ${dictKey} (${lexiconJson.id})`); 89 } 90 91 await writeFile(lexiconsFile, content); 92} 93 94async function main() { 95 const typeFiles = await getTypeFiles(TYPES_DIR); 96 const indexContent = await buildReplacementIndex(typeFiles); 97 await writeFile(join(GENERATED_DIR, 'index.ts'), indexContent); 98 99 for (const file of typeFiles) { 100 await fixImportExtensions(file); 101 } 102 103 await fixImportExtensions(join(GENERATED_DIR, 'lexicons.ts')); 104 await fixImportExtensions(join(GENERATED_DIR, 'util.ts')); 105 106 await injectExcludedLexicons(join(GENERATED_DIR, 'lexicons.ts')); 107 108 const sifaFiles = typeFiles.filter((f) => relative(TYPES_DIR, f).startsWith('id/sifa/')); 109 console.log( 110 `Fixed ${typeFiles.length + 2} generated files (${sifaFiles.length} sifa types exported, ${typeFiles.length - sifaFiles.length} external types included for resolution)`, 111 ); 112} 113 114main();