Skip to content

Commit

Permalink
Emitter framework handle circular reference
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheeguerin committed Oct 9, 2023
1 parent e713281 commit c9c6f0e
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/compiler/src/emitter-framework/asset-emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ export function createAssetEmitter<T, TOptions extends object>(
entity: EmitEntity<T>
): EmitEntity<T> {
if (entity.kind !== "declaration") {
typeEmitter.circularReference(entity);
return entity;
}

Expand Down
7 changes: 7 additions & 0 deletions packages/compiler/src/emitter-framework/type-emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,13 @@ export class TypeEmitter<T, TOptions extends object = Record<string, never>> {
return this.emitter.result.none();
}

circularReference(target: EmitEntity<T>): EmitEntity<T> | T {
// if (target.kind === "declaration") {
// return this.reference(target, pathUp, pathDown, commonScope);
// }
return this.emitter.result.none();
}

declarationName(declarationType: TypeSpecDeclaration): string | undefined {
compilerAssert(
declarationType.name !== undefined,
Expand Down
1 change: 1 addition & 0 deletions packages/compiler/src/emitter-framework/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ export type TypeEmitterMethod = keyof Omit<
| "sourceFile"
| "declarationName"
| "reference"
| "circularReference"
| "emitValue"
| "writeOutput"
| EndingWith<keyof TypeEmitter<any, any>, "Context">
Expand Down
89 changes: 88 additions & 1 deletion packages/compiler/test/emitter-framework/emitter.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import assert from "assert";
import assert, { strictEqual } from "assert";
import * as prettier from "prettier";
import {
Enum,
Expand Down Expand Up @@ -735,4 +735,91 @@ describe("emitter-framework: object emitter", () => {
const contents = JSON.parse((await host.compilerHost.readFile("tsp-output/test.json")!).text);
assert.strictEqual(contents.declarations.length, 2);
});

describe.only("circular references", () => {
interface FindOptions {
noDeclaration: boolean;
circleReference: boolean;
}
async function findCircularReferences(options: FindOptions) {
const invalidReferences: any[] = [];

const cls = class extends TypeEmitter<any, any> {
modelDeclaration(model: Model, _: string): EmitterOutput<object> {
const obj = new ObjectBuilder();
obj.set("props", this.emitter.emitModelProperties(model));
if (options.noDeclaration) {
return obj; // Never make a declaration
} else {
return this.emitter.result.declaration(model.name, obj);
}
}

modelProperties(model: Model) {
const arr = new ArrayBuilder();
for (const prop of model.properties.values()) {
arr.push(this.emitter.emitModelProperty(prop));
}
return arr;
}

modelPropertyLiteral(property: ModelProperty) {
if (options.circleReference) {
return this.emitter.emitTypeReference(property.type);
} else {
const obj = new ObjectBuilder();
obj.set("name", property.name);
return obj;
}
}

programContext(program: Program): Context {
const sourceFile = this.emitter.createSourceFile("main");
return { scope: sourceFile.globalScope };
}

circularReference(target: EmitEntity<any>) {
invalidReferences.push({ entity: target });
}
};

const host = await getHostForTypeSpecFile(
`
model Foo {
foo: Foo
}
`
);
const assetEmitter = createAssetEmitter(host.program, cls, {
emitterOutputDir: host.program.compilerOptions.outputDir!,
options: {},
} as any);
assetEmitter.emitProgram();

return invalidReferences;
}
it("self referencing with declaration works fine", async () => {
const invalidReferences = await findCircularReferences({
noDeclaration: false,
circleReference: true,
});
strictEqual(invalidReferences.length, 0);
});

it("self referencing without declaration report circular reference", async () => {
const invalidReferences = await findCircularReferences({
noDeclaration: true,
circleReference: true,
});
strictEqual(invalidReferences.length, 1);
});

it("without circular reference inline types cause no issue", async () => {
const invalidReferences = await findCircularReferences({
noDeclaration: true,
circleReference: true,
});
strictEqual(invalidReferences.length, 0);
});
});
});

0 comments on commit c9c6f0e

Please sign in to comment.