Skip to content

Commit dcfc88d

Browse files
committed
fix(emitter-typescript): fix namespace exporting
1 parent a519e68 commit dcfc88d

File tree

6 files changed

+79
-65
lines changed

6 files changed

+79
-65
lines changed

.changeset/big-ants-beam.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@typespec-tools/emitter-typescript": patch
3+
"@typespec-tools/integration-tests": patch
4+
"@typespec-tools/emitter-express": patch
5+
---
6+
7+
Fix namespace exporting

packages/emitter-express/src/emitter.ts

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {
2020
Declaration,
2121
EmittedSourceFile,
2222
EmitterOutput,
23-
Scope,
2423
SourceFile,
2524
StringBuilder,
2625
} from "@typespec/compiler/emitter-framework";
@@ -35,24 +34,6 @@ type NamespaceDeclarations = {
3534
namespaceChain: string[];
3635
};
3736

38-
function emitNamespaces(scope: Scope<string>) {
39-
let res = "";
40-
for (const childScope of scope.childScopes) {
41-
res += emitNamespace(childScope);
42-
}
43-
return res;
44-
}
45-
function emitNamespace(scope: Scope<string>) {
46-
let ns = `export namespace ${scope.name} {\n`;
47-
ns += emitNamespaces(scope);
48-
for (const decl of scope.declarations) {
49-
ns += decl.value + "\n";
50-
}
51-
ns += `}\n`;
52-
53-
return ns;
54-
}
55-
5637
export class ExpressEmitter extends TypescriptEmitter<EmitterOptions> {
5738
operationDeclaration(
5839
operation: Operation,
@@ -220,7 +201,7 @@ export class ExpressEmitter extends TypescriptEmitter<EmitterOptions> {
220201
);
221202
}
222203

223-
emittedSourceFile.contents += emitNamespaces(sourceFile.globalScope);
204+
emittedSourceFile.contents += this.emitNamespaces(sourceFile.globalScope);
224205

225206
const namespaces = Array.from(declarationsByNamespace.values());
226207

packages/emitter-typescript/src/emitter.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -55,29 +55,29 @@ export const intrinsicNameToTSType = new Map<string, string>([
5555
["void", "void"],
5656
]);
5757

58-
function emitNamespaces(scope: Scope<string>) {
59-
let res = "";
60-
for (const childScope of scope.childScopes) {
61-
res += emitNamespace(childScope);
62-
}
63-
return res;
64-
}
65-
function emitNamespace(scope: Scope<string>) {
66-
let ns = `namespace ${scope.name} {\n`;
67-
ns += emitNamespaces(scope);
68-
for (const decl of scope.declarations) {
69-
ns += decl.value + "\n";
70-
}
71-
ns += `}\n`;
72-
73-
return ns;
74-
}
75-
7658
export class TypescriptEmitter<
7759
TEmitterOptions extends object = EmitterOptions,
7860
> extends CodeTypeEmitter<TEmitterOptions> {
7961
protected nsByName: Map<string, Scope<string>> = new Map();
8062

63+
emitNamespaces(scope: Scope<string>) {
64+
let res = "";
65+
for (const childScope of scope.childScopes) {
66+
res += this.emitNamespace(childScope);
67+
}
68+
return res;
69+
}
70+
emitNamespace(scope: Scope<string>) {
71+
let ns = `export namespace ${scope.name} {\n`;
72+
ns += this.emitNamespaces(scope);
73+
for (const decl of scope.declarations) {
74+
ns += decl.value + "\n";
75+
}
76+
ns += `}\n`;
77+
78+
return ns;
79+
}
80+
8181
declarationContext(
8282
decl: TypeSpecDeclaration & { namespace?: Namespace }
8383
): Context {
@@ -409,7 +409,7 @@ export class TypescriptEmitter<
409409
emittedSourceFile.contents += decl.value + "\n";
410410
}
411411

412-
emittedSourceFile.contents += emitNamespaces(sourceFile.globalScope);
412+
emittedSourceFile.contents += this.emitNamespaces(sourceFile.globalScope);
413413

414414
emittedSourceFile.contents = await prettier.format(
415415
emittedSourceFile.contents,

packages/integration-tests/main.tsp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ namespace Pets {
6464
@body error: NotFoundError;
6565
};
6666

67-
@route("/{petType}")
67+
@route("/type/{petType}")
6868
namespace ByType {
6969
@get
7070
op listPets(@path petType: petType): {

packages/integration-tests/tests/emitter-express.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,4 +243,34 @@ describe("emitter-express", () => {
243243
expect(res.status).toBe(404);
244244
});
245245
});
246+
247+
describe("ByType", () => {
248+
describe('GET "/pets/:type"', () => {
249+
beforeAll(() => {
250+
typedRouter.PetStorePets.ByType.listPets((req, res) => {
251+
const filteredPets = pets.filter((pet) => {
252+
return pet.kind === req.params.petType;
253+
});
254+
res.json({ pets: filteredPets });
255+
});
256+
});
257+
258+
it("should return 200", async () => {
259+
const res = await fetch("http://localhost:3456/pets/type/dog");
260+
expect(res.status).toBe(200);
261+
});
262+
263+
it("should return the list of pets", async () => {
264+
const res = await fetch("http://localhost:3456/pets/type/dog");
265+
const data = await res.json();
266+
expect(data).toMatchObject({ pets: [pets[0], pets[4]] });
267+
});
268+
269+
it("should return an empty list", async () => {
270+
const res = await fetch("http://localhost:3456/pets/type/zebra");
271+
const data = await res.json();
272+
expect(data).toMatchObject({ pets: [] });
273+
});
274+
});
275+
});
246276
});

packages/integration-tests/tests/emitter-typescript.test.ts

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { describe, expectTypeOf, it } from "vitest";
2-
import {
3-
petType,
4-
Pet,
5-
listPets,
6-
getPet,
7-
} from "../tsp-output/@typespec-tools/emitter-typescript/output";
2+
import { PetStore } from "../tsp-output/@typespec-tools/emitter-typescript/output";
3+
4+
const petType = PetStore.petType;
5+
type Pet = PetStore.Pet;
6+
type listPetsParams = PetStore.Pets.listPetsParams;
7+
type listPetsReturnType = PetStore.Pets.listPetsReturnType;
8+
type getPetParams = PetStore.Pets.getPetParams;
9+
type getPetReturnType = PetStore.Pets.getPetReturnType;
810

911
describe("emitter-zod", () => {
1012
describe("PetSchema", () => {
@@ -28,21 +30,19 @@ describe("emitter-zod", () => {
2830

2931
describe("listPetsSchema", () => {
3032
it("validates a valid function", () => {
31-
const validFn = () => ({
33+
const validFn = (type?: PetStore.petType) => ({
3234
pets: [{ id: 123, name: "Fluffy", age: 3, kind: petType.dog }],
3335
});
34-
expectTypeOf(validFn).parameters.toEqualTypeOf<Parameters<listPets>>();
35-
expectTypeOf(validFn).returns.toEqualTypeOf<ReturnType<listPets>>();
36+
expectTypeOf(validFn).parameters.toEqualTypeOf<listPetsParams>();
37+
expectTypeOf(validFn).returns.toEqualTypeOf<listPetsReturnType>();
3638
});
3739

3840
it("validates an invalid function", () => {
3941
const invalidFn = (x: number) => ({
4042
pets: [{ id: "invalid", name: 123, kind: "invalid" }],
4143
});
42-
expectTypeOf(invalidFn).parameters.not.toEqualTypeOf<
43-
Parameters<listPets>
44-
>();
45-
expectTypeOf(invalidFn).returns.not.toEqualTypeOf<ReturnType<listPets>>();
44+
expectTypeOf(invalidFn).parameters.not.toEqualTypeOf<listPetsParams>();
45+
expectTypeOf(invalidFn).returns.not.toEqualTypeOf<listPetsReturnType>();
4646
});
4747
});
4848

@@ -52,20 +52,18 @@ describe("emitter-zod", () => {
5252
const validFn = (x: number) => ({
5353
pet: { id: 123, name: "Fluffy", age: 3, kind: petType.dog },
5454
});
55-
expectTypeOf(validFn).parameters.toEqualTypeOf<Parameters<getPet>>();
55+
expectTypeOf(validFn).parameters.toEqualTypeOf<getPetParams>();
5656
expectTypeOf(validFn).returns.toEqualTypeOf<
57-
Extract<ReturnType<getPet>, { pet: any }>
57+
Extract<getPetReturnType, { pet: any }>
5858
>();
5959
});
6060

6161
it("validates an invalid function", () => {
6262
const invalidFn = () => ({
6363
pet: { id: "invalid", name: 123, kind: "invalid" },
6464
});
65-
expectTypeOf(invalidFn).parameters.not.toEqualTypeOf<
66-
Parameters<getPet>
67-
>();
68-
expectTypeOf(invalidFn).returns.not.toEqualTypeOf<ReturnType<getPet>>();
65+
expectTypeOf(invalidFn).parameters.not.toEqualTypeOf<getPetParams>();
66+
expectTypeOf(invalidFn).returns.not.toEqualTypeOf<getPetReturnType>();
6967
});
7068
});
7169

@@ -74,20 +72,18 @@ describe("emitter-zod", () => {
7472
const validFn = (x: number) => ({
7573
error: { code: "NOT_FOUND" as const, message: "Testing" },
7674
});
77-
expectTypeOf(validFn).parameters.toEqualTypeOf<Parameters<getPet>>();
75+
expectTypeOf(validFn).parameters.toEqualTypeOf<getPetParams>();
7876
expectTypeOf(validFn).returns.toEqualTypeOf<
79-
Extract<ReturnType<getPet>, { error: any }>
77+
Extract<getPetReturnType, { error: any }>
8078
>();
8179
});
8280

8381
it("validates an invalid function", () => {
8482
const invalidFn = () => ({
8583
error: { code: "NOT_FOUND", message: 123 },
8684
});
87-
expectTypeOf(invalidFn).parameters.not.toEqualTypeOf<
88-
Parameters<getPet>
89-
>();
90-
expectTypeOf(invalidFn).returns.not.toEqualTypeOf<ReturnType<getPet>>();
85+
expectTypeOf(invalidFn).parameters.not.toEqualTypeOf<getPetParams>();
86+
expectTypeOf(invalidFn).returns.not.toEqualTypeOf<getPetReturnType>();
9187
});
9288
});
9389
});

0 commit comments

Comments
 (0)