Skip to content
Merged
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,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: fix
packages:
- "@typespec/compiler"
---

Fix issue when the 'entrypoint' setting is default to null
4 changes: 4 additions & 0 deletions packages/compiler/src/utils/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export function deepClone<T>(value: T): T {
return value.map(deepClone) as any;
}

if (value === null) {
return value;
}

if (typeof value === "object") {
const obj: any = {};
for (const prop in value) {
Expand Down
59 changes: 41 additions & 18 deletions packages/compiler/test/server/entrypoint-resolver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,37 +99,60 @@ it("falls back to tspMain from package.json when no client entrypoints exist", a
expect(result).toBe(expected);
});

it("falls back to default main.tsp when no client entrypoints and no tspMain in package.json", async () => {
it("uses the given path as main when nothing else is found", async () => {
const host = createMockHost();
const dir = "/lib";
const filePath = joinPaths(dir, "src", "index.tsp");
const expected = joinPaths(dir, "main.tsp");
const filePath = "/standalone/file.tsp";

vi.mocked(host.stat).mockImplementation(async (path) => {
return path === expected ? ({ isFile: () => true } as any) : ({ isFile: () => false } as any);
});

vi.mocked(host.readFile).mockImplementation(async (path: string) => {
// Return empty package.json
return { text: "{}" } as any;
// The initial path should be treated as a file, but no other files exist
return path === filePath ? ({ isFile: () => true } as any) : ({ isFile: () => false } as any);
});
vi.mocked(host.readFile).mockResolvedValue({ text: "{}" } as any);

const { log } = createLogger();
const result = await resolveEntrypointFile(host, undefined, filePath, undefined, log);
expect(result).toBe(expected);
expect(result).toBe(filePath);
});

it("uses the given path as main when nothing else is found", async () => {
it("uses main.tsp as default entrypoint when entrypoints is null or undefined and no package.json tspMain found", async () => {
const host = createMockHost();
const filePath = "/standalone/file.tsp";
const dir = "/project";
const filePath = joinPaths(dir, "src", "file.tsp");
const expectedMainTsp = joinPaths(dir, "main.tsp");

// Mock host.stat to return isFile: true only for the expected main.tsp path
vi.mocked(host.stat).mockImplementation(async (path) => {
// The initial path should be treated as a file, but no other files exist
return path === filePath ? ({ isFile: () => true } as any) : ({ isFile: () => false } as any);
return path === expectedMainTsp
? ({ isFile: () => true } as any)
: ({ isFile: () => false } as any);
});

// Mock host.readFile to return empty package.json (no tspMain)
vi.mocked(host.readFile).mockImplementation(async (path) => {
if (path.endsWith("package.json")) {
return { text: "{}" } as any; // Empty package.json, no tspMain
}
throw new Error("File not found");
});
vi.mocked(host.readFile).mockResolvedValue({ text: "{}" } as any);

const { log } = createLogger();
const result = await resolveEntrypointFile(host, undefined, filePath, undefined, log);
expect(result).toBe(filePath);
const resultForNull = await resolveEntrypointFile(
host,
null, // Explicitly pass null for entrypoints
filePath,
undefined,
log,
);

expect(resultForNull).toBe(expectedMainTsp);

const resultForUndefined = await resolveEntrypointFile(
host,
undefined, // Explicitly pass undefined for entrypoints
filePath,
undefined,
log,
);

expect(resultForUndefined).toBe(expectedMainTsp);
});
Loading