An experimental TypeSpec syntax for Lexicon
56
fork

Configure Feed

Select the types of activity you want to include in your feed.

refs

+137 -6
+22 -6
typelex-emitter/src/emitter.ts
··· 28 28 29 29 export class TypeLexEmitter { 30 30 private lexicons = new Map<string, LexiconDocument>(); 31 + private currentLexiconId: string | null = null; 31 32 32 33 constructor( 33 34 private program: Program, ··· 62 63 const isLikelyDefsFile = hasModels && !hasOperations && !hasChildNamespaces; 63 64 64 65 if (isLikelyDefsFile) { 65 - // Create a single lexicon for all models in this namespace 66 + // Create a single lexicon for all models in this namespace 66 67 const lexiconId = fullName + ".defs"; 68 + this.currentLexiconId = lexiconId; 69 + 67 70 const lexicon: LexiconDocument = { 68 71 lexicon: 1, 69 72 id: lexiconId, 70 73 defs: {}, 71 74 }; 72 - 75 + 73 76 // Add all models as definitions 74 77 for (const [_, model] of ns.models) { 75 78 const defName = model.name.charAt(0).toLowerCase() + model.name.slice(1); 76 79 const modelDef = this.modelToLexiconObject(model); 77 80 lexicon.defs[defName] = modelDef; 78 81 } 79 - 82 + 80 83 this.lexicons.set(lexiconId, lexicon); 84 + this.currentLexiconId = null; 81 85 } else if (hasModels && !hasOperations) { 82 86 // Process models individually for non-defs files 83 87 for (const [_, model] of ns.models) { ··· 270 274 // Check if this is a reference to another model 271 275 const modelRef = this.getModelReference(type as Model); 272 276 if (modelRef) { 273 - return { 277 + const refDef: LexiconRef = { 274 278 type: "ref", 275 279 ref: modelRef, 276 280 }; 281 + if (prop) { 282 + const propDesc = getDoc(this.program, prop); 283 + if (propDesc) { 284 + refDef.description = propDesc; 285 + } 286 + } 287 + return refDef; 277 288 } 278 289 const obj = this.modelToLexiconObject(type as Model); 279 290 if (prop) { ··· 369 380 } 370 381 371 382 const defName = model.name.charAt(0).toLowerCase() + model.name.slice(1); 383 + const modelLexiconId = `${namespaceName}.defs`; 372 384 373 385 // Check if it's in the current namespace being processed 374 - // For now, always return fully qualified refs 375 - // TODO: optimize for same-namespace refs 386 + if (this.currentLexiconId && modelLexiconId === this.currentLexiconId) { 387 + // Same namespace - use local ref 388 + return `#${defName}`; 389 + } 390 + 391 + // Different namespace - use fully qualified ref 376 392 return `${namespaceName}.defs#${defName}`; 377 393 } 378 394
+19
typelex-emitter/test/scenarios/same-namespace-ref/input/main.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + // Multiple models in same namespace - refs should use # format 4 + namespace app.bsky.feed { 5 + model ReplyRef { 6 + @lexFormat("at-uri") 7 + root: string; 8 + 9 + @lexFormat("at-uri") 10 + parent: string; 11 + } 12 + 13 + model Post { 14 + text: string; 15 + 16 + @doc("Reply information if this post is a reply") 17 + reply?: ReplyRef; // Same-namespace reference 18 + } 19 + }
+34
typelex-emitter/test/scenarios/same-namespace-ref/output/app/bsky/feed/defs.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.bsky.feed.defs", 4 + "defs": { 5 + "replyRef": { 6 + "type": "object", 7 + "required": ["root", "parent"], 8 + "properties": { 9 + "root": { 10 + "type": "string", 11 + "format": "at-uri" 12 + }, 13 + "parent": { 14 + "type": "string", 15 + "format": "at-uri" 16 + } 17 + } 18 + }, 19 + "post": { 20 + "type": "object", 21 + "required": ["text"], 22 + "properties": { 23 + "text": { 24 + "type": "string" 25 + }, 26 + "reply": { 27 + "type": "ref", 28 + "ref": "#replyRef", 29 + "description": "Reply information if this post is a reply" 30 + } 31 + } 32 + } 33 + } 34 + }
+22
typelex-emitter/test/scenarios/simple-ref/input/main.tsp
··· 1 + import "@typelex/emitter"; 2 + 3 + // Define a simple model that will be referenced 4 + namespace com.atproto.repo { 5 + model StrongRef { 6 + @lexFormat("at-uri") 7 + uri: string; 8 + 9 + @lexFormat("cid") 10 + cid: string; 11 + } 12 + } 13 + 14 + // Define another model that references StrongRef 15 + namespace app.bsky.feed { 16 + model Post { 17 + text: string; 18 + 19 + @doc("Reference to parent post if this is a reply") 20 + reply?: com.atproto.repo.StrongRef; // Fully qualified reference! 21 + } 22 + }
+20
typelex-emitter/test/scenarios/simple-ref/output/app/bsky/feed/defs.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "app.bsky.feed.defs", 4 + "defs": { 5 + "post": { 6 + "type": "object", 7 + "required": ["text"], 8 + "properties": { 9 + "text": { 10 + "type": "string" 11 + }, 12 + "reply": { 13 + "type": "ref", 14 + "ref": "com.atproto.repo.defs#strongRef", 15 + "description": "Reference to parent post if this is a reply" 16 + } 17 + } 18 + } 19 + } 20 + }
+20
typelex-emitter/test/scenarios/simple-ref/output/com/atproto/repo/defs.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "com.atproto.repo.defs", 4 + "defs": { 5 + "strongRef": { 6 + "type": "object", 7 + "required": ["uri", "cid"], 8 + "properties": { 9 + "uri": { 10 + "type": "string", 11 + "format": "at-uri" 12 + }, 13 + "cid": { 14 + "type": "string", 15 + "format": "cid" 16 + } 17 + } 18 + } 19 + } 20 + }