Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@typespec/compiler"
---

Default entrypoint resolution now prefers `client.tsp` over `main.tsp` when a project's `tspconfig.yaml` does not explicitly set `entrypoint` and a sibling `client.tsp` exists. This matches the convention used by `tsp-client` and allows augment decorators in `client.tsp` (e.g. `@@clientName`) to participate in compilation and linting without needing to add `imports: - ./client.tsp` to `tspconfig.yaml`.
10 changes: 8 additions & 2 deletions packages/compiler/src/core/entrypoint-resolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export async function resolveTypeSpecEntrypointForDir(
// Check for project tspconfig first
const config = await loadTypeSpecConfigForPath(host, dir, false, false);
if (config.kind === "project") {
const entrypoint = config.entrypoint ?? "main.tsp";
const entrypoint = config.entrypoint ?? (await resolveDefaultEntrypoint(host, dir));
return resolvePath(dir, entrypoint);
}

Expand All @@ -49,5 +49,11 @@ export async function resolveTypeSpecEntrypointForDir(
return resolvePath(dir, tspMain);
}

return resolvePath(dir, "main.tsp");
return resolvePath(dir, await resolveDefaultEntrypoint(host, dir));
}

async function resolveDefaultEntrypoint(host: CompilerHost, dir: string): Promise<string> {
const clientTspPath = resolvePath(dir, "client.tsp");
const stat = await doIO(host.stat, clientTspPath, () => {}, { allowFileNotFound: true });
return stat?.isFile() ? "client.tsp" : "main.tsp";
Comment on lines +55 to +58
}
5 changes: 3 additions & 2 deletions packages/compiler/src/server/entrypoint-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ export async function resolveEntrypointFile(
let dir = isFilePath ? getDirectoryPath(path) : path;

if (!entrypoints) {
entrypoints = ["main.tsp"];
entrypoints = ["client.tsp", "main.tsp"];
}

while (true) {
// Check for project tspconfig first (highest priority)
const config = await loadTypeSpecConfigForPath(host, dir, false, false);
if (config.kind === "project") {
const entrypoint = config.entrypoint ?? "main.tsp";
const entrypoint =
config.entrypoint ?? ((await existingFile(dir, "client.tsp")) ? "client.tsp" : "main.tsp");
const candidate = await existingFile(dir, entrypoint);
Comment on lines +34 to 36
logDebug({
level: "debug",
Expand Down
19 changes: 19 additions & 0 deletions packages/compiler/test/server/entrypoint-resolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ describe("entrypoint resolution", () => {
const resultForUndefined = await resolveEntrypoint(files, "project/src/file.tsp", undefined);
expect(resultForUndefined).toBe(resolveVirtualPath("project/main.tsp"));
});

it("prefers client.tsp over main.tsp in default fallback when both exist", async () => {
const files = { "project/main.tsp": "", "project/client.tsp": "" };

const result = await resolveEntrypoint(files, "project/src/file.tsp");
expect(result).toBe(resolveVirtualPath("project/client.tsp"));
});
});

describe("project tspconfig entrypoint resolution", () => {
Expand Down Expand Up @@ -106,6 +113,18 @@ describe("project tspconfig entrypoint resolution", () => {
expect(result).toBe(resolveVirtualPath("project/main.tsp"));
});

it("defaults to client.tsp when kind is project, no entrypoint specified, and client.tsp exists", async () => {
const result = await resolveEntrypoint(
{
"project/tspconfig.yaml": "kind: project\n",
"project/main.tsp": "",
"project/client.tsp": "",
},
"project/src/doc.tsp",
);
expect(result).toBe(resolveVirtualPath("project/client.tsp"));
});

it("project tspconfig stops the walk even if entrypoint file doesn't exist", async () => {
const result = await resolveEntrypoint(
{
Expand Down
Loading