microsoft/typespec

Public

mirrored from https://github.com/microsoft/typespecAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/fix-missing-extension-methods

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

packages/asset-emitter/src/type-emitter.ts

896lines · modecode

1import {
2 compilerAssert,
3 emitFile,
4 isTemplateDeclaration,
5 type BooleanLiteral,
6 type Enum,
7 type EnumMember,
8 type Interface,
9 type IntrinsicType,
10 type Model,
11 type ModelProperty,
12 type Namespace,
13 type NumericLiteral,
14 type Operation,
15 type Program,
16 type Scalar,
17 type StringLiteral,
18 type StringTemplate,
19 type Tuple,
20 type Type,
21 type Union,
22 type UnionVariant,
23} from "@typespec/compiler";
24import type { Context } from "vm";
25import { StringBuilder, code } from "./builders/string-builder.js";
26import type { Placeholder } from "./placeholder.js";
27import { resolveDeclarationReferenceScope } from "./ref-scope.js";
28import { ReferenceCycle } from "./reference-cycle.js";
29import type {
30 AssetEmitter,
31 Declaration,
32 EmitEntity,
33 EmittedSourceFile,
34 Scope,
35 SourceFile,
36 TypeSpecDeclaration,
37} from "./types.js";
38
39export type EmitterOutput<T> = EmitEntity<T> | Placeholder<T> | T;
40
41/**
42 * Implement emitter logic by extending this class and passing it to
43 * `emitContext.createAssetEmitter`. This class should not be constructed
44 * directly.
45 *
46 * TypeEmitters serve two primary purposes:
47 *
48 * 1. Handle emitting TypeSpec types into other languages
49 * 2. Set emitter context
50 *
51 * The generic type parameter `T` is the type you expect to produce for each TypeSpec type.
52 * In the case of generating source code for a programming language, this is probably `string`
53 * (in which case, consider using the `CodeTypeEmitter`) but might also be an AST node. If you
54 * are emitting JSON or similar, `T` would likely be `object`.
55 *
56 * ## Emitting types
57 *
58 * Emitting TypeSpec types into other languages is accomplished by implementing
59 * the AssetEmitter method that corresponds with the TypeSpec type you are
60 * emitting. For example, to emit a TypeSpec model declaration, implement the
61 * `modelDeclaration` method.
62 *
63 * TypeSpec types that have both declaration and literal forms like models or
64 * unions will have separate methods. For example, models have both
65 * `modelDeclaration` and `modelLiteral` methods that can be implemented
66 * separately.
67 *
68 * Also, types which can be instantiated like models or operations have a
69 * separate method for the instantiated type. For example, models have a
70 * `modelInstantiation` method that gets called with such types. Generally these
71 * will be treated either as if they were declarations or literals depending on
72 * preference, but may also be treated specially.
73 *
74 * ## Emitter results
75 * There are three kinds of results your methods might return - declarations,
76 * raw code, or nothing.
77 *
78 * ### Declarations
79 *
80 * Create declarations by calling `this.emitter.result.declaration` passing it a
81 * name and the emit output for the declaration. Note that you must have scope
82 * in your context or you will get an error. If you want all declarations to be
83 * emitted to the same source file, you can create a single scope in
84 * `programContext` via something like:
85 *
86 * ```typescript
87 * programContext(program: Program): Context {
88 * const sourceFile = this.emitter.createSourceFile("test.txt");
89 * return {
90 * scope: sourceFile.globalScope,
91 * };
92 * }
93 * ```
94 *
95 * ### Raw Code
96 *
97 * Create raw code, or emitter output that doesn't contribute to a declaration,
98 * by calling `this.emitter.result.rawCode` passing it a value. Returning just a
99 * value is considered raw code and so you often don't need to call this
100 * directly.
101 *
102 * ### No Emit
103 *
104 * When a type doesn't contribute anything to the emitted output, return
105 * `this.emitter.result.none()`.
106 *
107 * ## Context
108 *
109 * The TypeEmitter will often want to keep track of what context a type is found
110 * in. There are two kinds of context - lexical context, and reference context.
111 *
112 * * Lexical context is context that applies to the type and every type
113 * contained inside of it. For example, lexical context for a model will apply
114 * to the model, its properties, and any nested model literals.
115 * * Reference context is context that applies to types contained inside of the
116 * type and referenced anywhere inside of it. For example, reference context
117 * set on a model will apply to the model, its properties, any nested model
118 * literals, and any type referenced inside anywhere inside the model and any
119 * of the referenced types' references.
120 *
121 * In both cases, context is an object. It's strongly recommended that the context
122 * object either contain only primitive types, or else only reference immutable
123 * objects.
124 *
125 * Set lexical by implementing the `*Context` methods of the TypeEmitter and
126 * returning the context, for example `modelDeclarationContext` sets the context
127 * for model declarations and the types contained inside of it.
128 *
129 * Set reference context by implementing the `*ReferenceContext` methods of the
130 * TypeEmitter and returning the context. Note that not all types have reference
131 * context methods, because not all types can actually reference anything.
132 *
133 * When a context method returns some context, it is merged with the current
134 * context. It is not possible to remove previous context, but it can be
135 * overridden with `undefined`.
136 *
137 * When emitting types with context, the same type might be emitted multiple
138 * times if we come across that type with different contexts. For example, if we
139 * have a TypeSpec program like
140 *
141 * ```typespec
142 * model Pet { }
143 * model Person {
144 * pet: Pet;
145 * }
146 * ```
147 *
148 * And we set reference context for the Person model, Pet will be emitted twice,
149 * once without context and once with the reference context.
150 */
151export class TypeEmitter<T, TOptions extends object = Record<string, never>> {
152 /**
153 * @private
154 *
155 * Constructs a TypeEmitter. Do not use this constructor directly, instead
156 * call `createAssetEmitter` on the emitter context object.
157 * @param emitter The asset emitter
158 */
159 constructor(protected emitter: AssetEmitter<T, TOptions>) {}
160
161 /**
162 * Context shared by the entire program. In cases where you are emitting to a
163 * single file, use this method to establish your main source file and set the
164 * `scope` property to that source file's `globalScope`.
165 * @param program
166 * @returns Context
167 */
168 programContext(program: Program): Context {
169 return {};
170 }
171
172 /**
173 * Emit a namespace
174 *
175 * @param namespace
176 * @returns Emitter output
177 */
178 namespace(namespace: Namespace): EmitterOutput<T> {
179 for (const ns of namespace.namespaces.values()) {
180 this.emitter.emitType(ns);
181 }
182
183 for (const model of namespace.models.values()) {
184 if (!isTemplateDeclaration(model)) {
185 this.emitter.emitType(model);
186 }
187 }
188
189 for (const operation of namespace.operations.values()) {
190 if (!isTemplateDeclaration(operation)) {
191 this.emitter.emitType(operation);
192 }
193 }
194
195 for (const enumeration of namespace.enums.values()) {
196 this.emitter.emitType(enumeration);
197 }
198
199 for (const union of namespace.unions.values()) {
200 if (!isTemplateDeclaration(union)) {
201 this.emitter.emitType(union);
202 }
203 }
204
205 for (const iface of namespace.interfaces.values()) {
206 if (!isTemplateDeclaration(iface)) {
207 this.emitter.emitType(iface);
208 }
209 }
210
211 for (const scalar of namespace.scalars.values()) {
212 this.emitter.emitType(scalar);
213 }
214
215 return this.emitter.result.none();
216 }
217
218 /**
219 * Set lexical context for a namespace
220 *
221 * @param namespace
222 */
223 namespaceContext(namespace: Namespace): Context {
224 return {};
225 }
226
227 /**
228 * Set reference context for a namespace.
229 *
230 * @param namespace
231 */
232 namespaceReferenceContext(namespace: Namespace): Context {
233 return {};
234 }
235
236 /**
237 * Emit a model literal (e.g. as created by `{}` syntax in TypeSpec).
238 *
239 * @param model
240 */
241 modelLiteral(model: Model): EmitterOutput<T> {
242 if (model.baseModel) {
243 this.emitter.emitType(model.baseModel);
244 }
245
246 this.emitter.emitModelProperties(model);
247 return this.emitter.result.none();
248 }
249
250 /**
251 * Set lexical context for a model literal.
252 * @param model
253 */
254 modelLiteralContext(model: Model): Context {
255 return {};
256 }
257
258 /**
259 * Set reference context for a model literal.
260 * @param model
261 */
262 modelLiteralReferenceContext(model: Model): Context {
263 return {};
264 }
265
266 /**
267 * Emit a model declaration (e.g. as created by `model Foo { }` syntax in
268 * TypeSpec).
269 *
270 * @param model
271 */
272 modelDeclaration(model: Model, name: string): EmitterOutput<T> {
273 if (model.baseModel) {
274 this.emitter.emitType(model.baseModel);
275 }
276 this.emitter.emitModelProperties(model);
277 return this.emitter.result.none();
278 }
279
280 /**
281 * Set lexical context for a model declaration.
282 *
283 * @param model
284 * @param name the model's declaration name as retrieved from the
285 * `declarationName` method.
286 */
287 modelDeclarationContext(model: Model, name: string): Context {
288 return {};
289 }
290
291 /**
292 * Set reference context for a model declaration.
293 * @param model
294 */
295 modelDeclarationReferenceContext(model: Model, name: string): Context {
296 return {};
297 }
298
299 /**
300 * Emit a model instantiation (e.g. as created by `Box<string>` syntax in
301 * TypeSpec). In some cases, `name` is undefined because a good name could
302 * not be found for the instantiation. This often occurs with for instantiations
303 * involving type expressions like `Box<string | int32>`.
304 *
305 * @param model
306 * @param name The name of the instantiation as retrieved from the
307 * `declarationName` method.
308 */
309 modelInstantiation(model: Model, name: string | undefined): EmitterOutput<T> {
310 if (model.baseModel) {
311 this.emitter.emitType(model.baseModel);
312 }
313 this.emitter.emitModelProperties(model);
314 return this.emitter.result.none();
315 }
316
317 /**
318 * Set lexical context for a model instantiation.
319 * @param model
320 */
321 modelInstantiationContext(model: Model, name: string | undefined): Context {
322 return {};
323 }
324
325 /**
326 * Set reference context for a model declaration.
327 * @param model
328 */
329 modelInstantiationReferenceContext(model: Model, name: string | undefined): Context {
330 return {};
331 }
332
333 /**
334 * Emit a model's properties. Unless overridden, this method will emit each of
335 * the model's properties and return a no emit result.
336 *
337 * @param model
338 */
339 modelProperties(model: Model): EmitterOutput<T> {
340 for (const prop of model.properties.values()) {
341 this.emitter.emitModelProperty(prop);
342 }
343 return this.emitter.result.none();
344 }
345
346 modelPropertiesContext(model: Model): Context {
347 return {};
348 }
349
350 modelPropertiesReferenceContext(model: Model): Context {
351 return {};
352 }
353
354 /**
355 * Emit a property of a model.
356 *
357 * @param property
358 */
359 modelPropertyLiteral(property: ModelProperty): EmitterOutput<T> {
360 this.emitter.emitTypeReference(property.type);
361 return this.emitter.result.none();
362 }
363
364 /**
365 * Set lexical context for a property of a model.
366 *
367 * @param property
368 */
369 modelPropertyLiteralContext(property: ModelProperty): Context {
370 return {};
371 }
372
373 /**
374 * Set reference context for a property of a model.
375 *
376 * @param property
377 */
378 modelPropertyLiteralReferenceContext(property: ModelProperty): Context {
379 return {};
380 }
381
382 /**
383 * Emit a model property reference (e.g. as created by the `SomeModel.prop`
384 * syntax in TypeSpec). By default, this will emit the type of the referenced
385 * property and return that result. In other words, the emit will look as if
386 * `SomeModel.prop` were replaced with the type of `prop`.
387 *
388 * @param property
389 */
390 modelPropertyReference(property: ModelProperty): EmitterOutput<T> {
391 return this.emitter.emitTypeReference(property.type);
392 }
393
394 /**
395 * Emit an enum member reference (e.g. as created by the `SomeEnum.member` syntax
396 * in TypeSpec). By default, this will emit nothing.
397 *
398 * @param property the enum member
399 */
400 enumMemberReference(member: EnumMember): EmitterOutput<T> {
401 return this.emitter.result.none();
402 }
403
404 arrayDeclaration(array: Model, name: string, elementType: Type): EmitterOutput<T> {
405 this.emitter.emitType(array.indexer!.value);
406 return this.emitter.result.none();
407 }
408
409 arrayDeclarationContext(array: Model, name: string, elementType: Type): Context {
410 return {};
411 }
412
413 arrayDeclarationReferenceContext(array: Model, name: string, elementType: Type): Context {
414 return {};
415 }
416
417 arrayLiteral(array: Model, elementType: Type): EmitterOutput<T> {
418 return this.emitter.result.none();
419 }
420
421 arrayLiteralContext(array: Model, elementType: Type): Context {
422 return {};
423 }
424
425 arrayLiteralReferenceContext(array: Model, elementType: Type): Context {
426 return {};
427 }
428
429 scalarDeclaration(scalar: Scalar, name: string): EmitterOutput<T> {
430 if (scalar.baseScalar) {
431 this.emitter.emitType(scalar.baseScalar);
432 }
433 return this.emitter.result.none();
434 }
435
436 scalarDeclarationContext(scalar: Scalar, name: string): Context {
437 return {};
438 }
439
440 scalarDeclarationReferenceContext(scalar: Scalar, name: string): Context {
441 return {};
442 }
443
444 scalarInstantiation(scalar: Scalar, name: string | undefined): EmitterOutput<T> {
445 return this.emitter.result.none();
446 }
447
448 scalarInstantiationContext(scalar: Scalar, name: string | undefined): Context {
449 return {};
450 }
451
452 intrinsic(intrinsic: IntrinsicType, name: string): EmitterOutput<T> {
453 return this.emitter.result.none();
454 }
455
456 intrinsicContext(intrinsic: IntrinsicType, name: string): Context {
457 return {};
458 }
459
460 booleanLiteralContext(boolean: BooleanLiteral): Context {
461 return {};
462 }
463
464 booleanLiteral(boolean: BooleanLiteral): EmitterOutput<T> {
465 return this.emitter.result.none();
466 }
467
468 stringTemplateContext(string: StringTemplate): Context {
469 return {};
470 }
471
472 stringTemplate(stringTemplate: StringTemplate): EmitterOutput<T> {
473 return this.emitter.result.none();
474 }
475
476 stringLiteralContext(string: StringLiteral): Context {
477 return {};
478 }
479
480 stringLiteral(string: StringLiteral): EmitterOutput<T> {
481 return this.emitter.result.none();
482 }
483
484 numericLiteralContext(number: NumericLiteral): Context {
485 return {};
486 }
487
488 numericLiteral(number: NumericLiteral): EmitterOutput<T> {
489 return this.emitter.result.none();
490 }
491
492 operationDeclaration(operation: Operation, name: string): EmitterOutput<T> {
493 this.emitter.emitOperationParameters(operation);
494 this.emitter.emitOperationReturnType(operation);
495
496 return this.emitter.result.none();
497 }
498
499 operationDeclarationContext(operation: Operation, name: string): Context {
500 return {};
501 }
502
503 operationDeclarationReferenceContext(operation: Operation, name: string): Context {
504 return {};
505 }
506
507 interfaceDeclarationOperationsContext(iface: Interface): Context {
508 return {};
509 }
510
511 interfaceDeclarationOperationsReferenceContext(iface: Interface): Context {
512 return {};
513 }
514
515 interfaceOperationDeclarationContext(operation: Operation, name: string): Context {
516 return {};
517 }
518
519 interfaceOperationDeclarationReferenceContext(operation: Operation, name: string): Context {
520 return {};
521 }
522
523 operationParameters(operation: Operation, parameters: Model): EmitterOutput<T> {
524 return this.emitter.result.none();
525 }
526
527 operationParametersContext(operation: Operation, parameters: Model): Context {
528 return {};
529 }
530
531 operationParametersReferenceContext(operation: Operation, parameters: Model): Context {
532 return {};
533 }
534
535 operationReturnType(operation: Operation, returnType: Type): EmitterOutput<T> {
536 return this.emitter.result.none();
537 }
538
539 operationReturnTypeContext(operation: Operation, returnType: Type): Context {
540 return {};
541 }
542
543 operationReturnTypeReferenceContext(operation: Operation, returnType: Type): Context {
544 return {};
545 }
546
547 interfaceDeclaration(iface: Interface, name: string): EmitterOutput<T> {
548 this.emitter.emitInterfaceOperations(iface);
549 return this.emitter.result.none();
550 }
551
552 interfaceDeclarationContext(iface: Interface, name: string): Context {
553 return {};
554 }
555
556 interfaceDeclarationReferenceContext(iface: Interface, name: string): Context {
557 return {};
558 }
559
560 interfaceDeclarationOperations(iface: Interface): EmitterOutput<T> {
561 for (const op of iface.operations.values()) {
562 this.emitter.emitInterfaceOperation(op);
563 }
564 return this.emitter.result.none();
565 }
566
567 interfaceOperationDeclaration(operation: Operation, name: string): EmitterOutput<T> {
568 this.emitter.emitOperationParameters(operation);
569 this.emitter.emitOperationReturnType(operation);
570
571 return this.emitter.result.none();
572 }
573
574 enumDeclaration(en: Enum, name: string): EmitterOutput<T> {
575 this.emitter.emitEnumMembers(en);
576 return this.emitter.result.none();
577 }
578
579 enumDeclarationContext(en: Enum, name: string): Context {
580 return {};
581 }
582
583 enumDeclarationReferenceContext(en: Enum, name: string): Context {
584 return {};
585 }
586
587 enumMembers(en: Enum): EmitterOutput<T> {
588 for (const member of en.members.values()) {
589 this.emitter.emitType(member);
590 }
591 return this.emitter.result.none();
592 }
593
594 enumMembersContext(en: Enum): Context {
595 return {};
596 }
597
598 enumMember(member: EnumMember): EmitterOutput<T> {
599 return this.emitter.result.none();
600 }
601
602 enumMemberContext(member: EnumMember) {
603 return {};
604 }
605
606 unionDeclaration(union: Union, name: string): EmitterOutput<T> {
607 this.emitter.emitUnionVariants(union);
608 return this.emitter.result.none();
609 }
610
611 unionDeclarationContext(union: Union): Context {
612 return {};
613 }
614
615 unionDeclarationReferenceContext(union: Union): Context {
616 return {};
617 }
618
619 unionInstantiation(union: Union, name: string): EmitterOutput<T> {
620 this.emitter.emitUnionVariants(union);
621 return this.emitter.result.none();
622 }
623
624 unionInstantiationContext(union: Union, name: string): Context {
625 return {};
626 }
627
628 unionInstantiationReferenceContext(union: Union, name: string): Context {
629 return {};
630 }
631
632 unionLiteral(union: Union): EmitterOutput<T> {
633 this.emitter.emitUnionVariants(union);
634 return this.emitter.result.none();
635 }
636
637 unionLiteralContext(union: Union): Context {
638 return {};
639 }
640
641 unionLiteralReferenceContext(union: Union): Context {
642 return {};
643 }
644
645 unionVariants(union: Union): EmitterOutput<T> {
646 for (const variant of union.variants.values()) {
647 this.emitter.emitType(variant);
648 }
649 return this.emitter.result.none();
650 }
651
652 unionVariantsContext(): Context {
653 return {};
654 }
655
656 unionVariantsReferenceContext(): Context {
657 return {};
658 }
659
660 unionVariant(variant: UnionVariant): EmitterOutput<T> {
661 this.emitter.emitTypeReference(variant.type);
662 return this.emitter.result.none();
663 }
664 unionVariantContext(union: Union): Context {
665 return {};
666 }
667
668 unionVariantReferenceContext(union: Union): Context {
669 return {};
670 }
671
672 tupleLiteral(tuple: Tuple): EmitterOutput<T> {
673 this.emitter.emitTupleLiteralValues(tuple);
674 return this.emitter.result.none();
675 }
676
677 tupleLiteralContext(tuple: Tuple): Context {
678 return {};
679 }
680
681 tupleLiteralValues(tuple: Tuple): EmitterOutput<T> {
682 for (const value of tuple.values.values()) {
683 this.emitter.emitType(value);
684 }
685 return this.emitter.result.none();
686 }
687
688 tupleLiteralValuesContext(tuple: Tuple): Context {
689 return {};
690 }
691
692 tupleLiteralValuesReferenceContext(tuple: Tuple): Context {
693 return {};
694 }
695
696 tupleLiteralReferenceContext(tuple: Tuple): Context {
697 return {};
698 }
699
700 sourceFile(sourceFile: SourceFile<T>): Promise<EmittedSourceFile> | EmittedSourceFile {
701 const emittedSourceFile: EmittedSourceFile = {
702 path: sourceFile.path,
703 contents: "",
704 };
705
706 for (const decl of sourceFile.globalScope.declarations) {
707 emittedSourceFile.contents += decl.value + "\n";
708 }
709
710 return emittedSourceFile;
711 }
712
713 async writeOutput(sourceFiles: SourceFile<T>[]) {
714 for (const file of sourceFiles) {
715 const outputFile = await this.emitter.emitSourceFile(file);
716 await emitFile(this.emitter.getProgram(), {
717 path: outputFile.path,
718 content: outputFile.contents,
719 });
720 }
721 }
722
723 reference(
724 targetDeclaration: Declaration<T>,
725 pathUp: Scope<T>[],
726 pathDown: Scope<T>[],
727 commonScope: Scope<T> | null,
728 ): EmitEntity<T> | T {
729 return this.emitter.result.none();
730 }
731
732 /**
733 * Handle circular references. When this method is called it means we are resolving a circular reference.
734 * By default if the target is a declaration it will call to {@link reference} otherwise it means we have an inline reference
735 * @param target Reference target.
736 * @param scope Current scope.
737 * @returns Resolved reference entity.
738 */
739 circularReference(
740 target: EmitEntity<T>,
741 scope: Scope<T> | undefined,
742 cycle: ReferenceCycle,
743 ): EmitEntity<T> | T {
744 if (!cycle.containsDeclaration) {
745 throw new Error(
746 `Circular references to non-declarations are not supported by this emitter. Cycle:\n${cycle}`,
747 );
748 }
749 if (target.kind !== "declaration") {
750 return target;
751 }
752 compilerAssert(
753 scope,
754 "Emit context must have a scope set in order to create references to declarations.",
755 );
756 const { pathUp, pathDown, commonScope } = resolveDeclarationReferenceScope(target, scope);
757 return this.reference(target, pathUp, pathDown, commonScope);
758 }
759
760 declarationName(declarationType: TypeSpecDeclaration): string | undefined {
761 compilerAssert(
762 declarationType.name !== undefined,
763 "Can't emit a declaration that doesn't have a name.",
764 );
765
766 if (declarationType.kind === "Enum" || declarationType.kind === "Intrinsic") {
767 return declarationType.name;
768 }
769
770 // for operations inside interfaces, we don't want to do the fancy thing because it will make
771 // operations inside instantiated interfaces get weird names
772 if (declarationType.kind === "Operation" && declarationType.interface) {
773 return declarationType.name;
774 }
775
776 if (!declarationType.templateMapper) {
777 return declarationType.name;
778 }
779
780 let unspeakable = false;
781
782 const parameterNames = declarationType.templateMapper.args.map((t) => {
783 if (t.entityKind === "Indeterminate") {
784 t = t.type;
785 }
786 if (!("kind" in t)) {
787 return undefined;
788 }
789 switch (t.kind) {
790 case "Model":
791 case "Scalar":
792 case "Interface":
793 case "Operation":
794 case "Enum":
795 case "Union":
796 case "Intrinsic":
797 if (!t.name) {
798 unspeakable = true;
799 return undefined;
800 }
801 const declName = this.emitter.emitDeclarationName(t);
802 if (declName === undefined) {
803 unspeakable = true;
804 return undefined;
805 }
806 return declName[0].toUpperCase() + declName.slice(1);
807 default:
808 unspeakable = true;
809 return undefined;
810 }
811 });
812
813 if (unspeakable) {
814 return undefined;
815 }
816
817 return declarationType.name + parameterNames.join("");
818 }
819}
820
821/**
822 * A subclass of `TypeEmitter<string>` that makes working with strings a bit easier.
823 * In particular, when emitting members of a type (`modelProperties`, `enumMembers`, etc.),
824 * instead of returning no result, it returns the value of each of the members concatenated
825 * by commas. It will also construct references by concatenating namespace elements together
826 * with `.` which should work nicely in many object oriented languages.
827 */
828export class CodeTypeEmitter<TOptions extends object = Record<string, never>> extends TypeEmitter<
829 string,
830 TOptions
831> {
832 modelProperties(model: Model): EmitterOutput<string> {
833 const builder = new StringBuilder();
834 let i = 0;
835 for (const prop of model.properties.values()) {
836 i++;
837 const propVal = this.emitter.emitModelProperty(prop);
838 builder.push(code`${propVal}${i < model.properties.size ? "," : ""}`);
839 }
840 return this.emitter.result.rawCode(builder.reduce());
841 }
842
843 interfaceDeclarationOperations(iface: Interface): EmitterOutput<string> {
844 const builder = new StringBuilder();
845 let i = 0;
846 for (const op of iface.operations.values()) {
847 i++;
848 builder.push(
849 code`${this.emitter.emitInterfaceOperation(op)}${i < iface.operations.size ? "," : ""}`,
850 );
851 }
852 return builder.reduce();
853 }
854
855 enumMembers(en: Enum): EmitterOutput<string> {
856 const builder = new StringBuilder();
857 let i = 0;
858 for (const enumMember of en.members.values()) {
859 i++;
860 builder.push(code`${this.emitter.emitType(enumMember)}${i < en.members.size ? "," : ""}`);
861 }
862 return builder.reduce();
863 }
864
865 unionVariants(union: Union): EmitterOutput<string> {
866 const builder = new StringBuilder();
867 let i = 0;
868 for (const v of union.variants.values()) {
869 i++;
870 builder.push(code`${this.emitter.emitType(v)}${i < union.variants.size ? "," : ""}`);
871 }
872 return builder.reduce();
873 }
874
875 tupleLiteralValues(tuple: Tuple): EmitterOutput<string> {
876 const builder = new StringBuilder();
877 let i = 0;
878 for (const v of tuple.values) {
879 i++;
880 builder.push(code`${this.emitter.emitTypeReference(v)}${i < tuple.values.length ? "," : ""}`);
881 }
882 return builder.reduce();
883 }
884
885 reference(
886 targetDeclaration: Declaration<string>,
887 pathUp: Scope<string>[],
888 pathDown: Scope<string>[],
889 commonScope: Scope<string> | null,
890 ): string | EmitEntity<string> {
891 const basePath = pathDown.map((s) => s.name).join(".");
892 return basePath
893 ? this.emitter.result.rawCode(basePath + "." + targetDeclaration.name)
894 : this.emitter.result.rawCode(targetDeclaration.name);
895 }
896}
897