Skip to content

[DO NOT MERGE]feat(compiler): prefer client.tsp over main.tsp when no entrypoint configured#10960

Closed
m-nash wants to merge 1 commit into
microsoft:mainfrom
m-nash:feature/entrypoint-prefer-client-tsp
Closed

[DO NOT MERGE]feat(compiler): prefer client.tsp over main.tsp when no entrypoint configured#10960
m-nash wants to merge 1 commit into
microsoft:mainfrom
m-nash:feature/entrypoint-prefer-client-tsp

Conversation

@m-nash

@m-nash m-nash commented Jun 10, 2026

Copy link
Copy Markdown
Member

Summary

When resolving the default entrypoint and no entrypoint is explicitly set (via tspconfig.yaml entrypoint or package.json tspMain), prefer client.tsp over main.tsp when a sibling client.tsp exists.

This matches the convention already used by tsp-client and allows augment decorators in client.tsp (e.g. @@clientName) to participate in compilation and linting without requiring imports: - ./client.tsp in tspconfig.yaml.

Motivation

Linter rules that emit codefixes targeting client.tsp (e.g. Azure/typespec-azure#4541) currently have two options to ensure the augmented decorators participate in the compile/lint pass:

  1. Mutate tspconfig.yaml to add imports: - ./client.tsp.
  2. Require the user to add that import line manually.

Both are awkward. tsp-client already solves this by treating client.tsp as the entrypoint when present. This change brings the same convention to the core compiler/language server so that:

  • Linter rules that write into client.tsp Just Work without YAML editing.
  • The dependency direction stays natural: client.tsp imports main.tsp (client depends on service, not the other way around).

Behavior

Resolution order is unchanged except for the implicit default:

  • If tspconfig.yaml explicitly sets entrypoint, that wins.
  • If package.json sets tspMain, that wins.
  • Otherwise: prefer client.tsp if it exists in the project root, else main.tsp.

Both the CLI entrypoint resolver (core/entrypoint-resolution.ts) and the language-server entrypoint resolver (server/entrypoint-resolver.ts) are updated.

Caveat

For augments in client.tsp to reference types defined in main.tsp, client.tsp must import './main.tsp'. This matches the existing tsp-client convention and is already the standard pattern.

Tests

  • 2 new tests added in test/server/entrypoint-resolver.test.ts.
  • Verified end-to-end with a scratch project: both resolvers pick client.tsp over main.tsp when both exist, and compilation succeeds when client.tsp imports main.tsp.
  • All 765 existing compiler server tests pass.

…nfigured

When resolving the default entrypoint and no entrypoint is explicitly
set (via tspconfig.yaml entrypoint or package.json tspMain), prefer
client.tsp over main.tsp when a sibling client.tsp exists.

This matches the convention already used by tsp-client and allows
augment decorators in client.tsp (e.g. @@clientName) to participate in
compilation and linting without requiring imports: - ./client.tsp in
tspconfig.yaml. client.tsp is expected to import './main.tsp' per
existing convention, which keeps the dependency direction
client -> service intact.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@microsoft-github-policy-service microsoft-github-policy-service Bot added the compiler:core Issues for @typespec/compiler label Jun 10, 2026
@m-nash m-nash requested a review from Copilot June 10, 2026 22:23
@pkg-pr-new

pkg-pr-new Bot commented Jun 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/compiler@10960

commit: 693cbef

@m-nash m-nash changed the title feat(compiler): prefer client.tsp over main.tsp when no entrypoint configured [DO NOT MERGE]feat(compiler): prefer client.tsp over main.tsp when no entrypoint configured Jun 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

All changed packages have been documented.

  • @typespec/compiler
Show changes

@typespec/compiler - feature ✏️

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates TypeSpec compiler entrypoint resolution so that, when no entrypoint is explicitly configured, client.tsp is preferred over main.tsp if both exist—aligning core compiler + language server behavior with the existing tsp-client convention.

Changes:

  • Updated the language server entrypoint resolver to default to ["client.tsp", "main.tsp"], and to default to client.tsp within kind: project boundaries when present.
  • Updated the CLI/compiler entrypoint resolver to use a shared default resolver that prefers client.tsp when present.
  • Added server-side unit tests covering the new default behavior, and added a Chronus entry documenting the change.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
packages/compiler/test/server/entrypoint-resolver.test.ts Adds tests ensuring client.tsp is preferred over main.tsp in default resolution cases.
packages/compiler/src/server/entrypoint-resolver.ts Changes default fallback entrypoints to check client.tsp before main.tsp, including for kind: project configs.
packages/compiler/src/core/entrypoint-resolution.ts Adjusts compiler/CLI directory entrypoint resolution to prefer client.tsp when present.
.chronus/changes/prefer-client-tsp-entrypoint-2026-6-10-15-15-0.md Adds a changelog entry for the new default entrypoint behavior.

Comment on lines +55 to +58
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 +34 to 36
const entrypoint =
config.entrypoint ?? ((await existingFile(dir, "client.tsp")) ? "client.tsp" : "main.tsp");
const candidate = await existingFile(dir, entrypoint);
- "@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`.
@azure-sdk-automation

Copy link
Copy Markdown

You can try these changes here

🛝 Playground 🌐 Website 🛝 VSCode Extension

@m-nash m-nash closed this Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

compiler:core Issues for @typespec/compiler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants