diff --git a/packages/typescript/src/api/async/api.ts b/packages/typescript/src/api/async/api.ts index bf3fe53bbce5d..b6b9798550f16 100644 --- a/packages/typescript/src/api/async/api.ts +++ b/packages/typescript/src/api/async/api.ts @@ -486,13 +486,12 @@ export class API implements FormatDiagnosticsHo /** @internal */ async createBuildOrchestrator(rootNames: readonly string[], buildOrchestratorOptions: BuildOrchestratorOptions): Promise { await this.ensureInitialized(); + const { cwd, dry, force, verbose, stopBuildOnErrors, overrideCompilerOptions } = buildOrchestratorOptions; const orchestratorResponse = await this.client.apiRequest("createBuildOrchestrator", { - ...buildOrchestratorOptions, - ...buildOrchestratorOptions.overrideCompilerOptions, - cwd: buildOrchestratorOptions.cwd === undefined - ? undefined - : toRootedDirectoryPath(buildOrchestratorOptions.cwd, this.getCurrentDirectory()), rootNames, + cwd: cwd === undefined ? undefined : toRootedDirectoryPath(cwd, this.getCurrentDirectory()), + buildOptions: { dry, force, verbose, stopBuildOnErrors }, + compilerOptions: overrideCompilerOptions, }); const orchestrator = new BuildOrchestrator(this.client, orchestratorResponse, () => { @@ -2201,6 +2200,7 @@ export interface OverrideCompilerOptions { sourceMap?: boolean; inlineSourceMap?: boolean; traceResolution?: boolean; + noEmit?: boolean; } export class BuildOrchestrator { diff --git a/packages/typescript/src/api/proto.generated.ts b/packages/typescript/src/api/proto.generated.ts index 35502aade2ac5..90dea2f6933f6 100644 --- a/packages/typescript/src/api/proto.generated.ts +++ b/packages/typescript/src/api/proto.generated.ts @@ -351,9 +351,12 @@ export interface GetCurrentLanguageServerSnapshotParams { changes?: LanguageServerSnapshotChanges | undefined; } -export interface CreateBuildOrchestratorParams extends BuildOptions, CompilerOptions { +export interface CreateBuildOrchestratorParams { rootNames: readonly string[] | null; cwd?: RootedDirectoryPath | undefined; + /** Only a subset of these options are exposed in the API */ + buildOptions?: BuildOptions | undefined; + compilerOptions?: CompilerOptions | undefined; } export interface CreateBuildOrchestratorResponse { diff --git a/packages/typescript/src/api/sync/api.ts b/packages/typescript/src/api/sync/api.ts index c8db7d186170c..7b1783a4d92ce 100644 --- a/packages/typescript/src/api/sync/api.ts +++ b/packages/typescript/src/api/sync/api.ts @@ -592,13 +592,12 @@ export class API implements FormatDiagnosticsHo "createBuildOrchestrator", function (rootNames: readonly string[], buildOrchestratorOptions: BuildOrchestratorOptions): BuildOrchestrator { owner.ensureInitialized(); + const { cwd, dry, force, verbose, stopBuildOnErrors, overrideCompilerOptions } = buildOrchestratorOptions; const orchestratorResponse = owner.client.apiRequest("createBuildOrchestrator", { - ...buildOrchestratorOptions, - ...buildOrchestratorOptions.overrideCompilerOptions, - cwd: buildOrchestratorOptions.cwd === undefined - ? undefined - : toRootedDirectoryPath(buildOrchestratorOptions.cwd, owner.getCurrentDirectory()), rootNames, + cwd: cwd === undefined ? undefined : toRootedDirectoryPath(cwd, owner.getCurrentDirectory()), + buildOptions: { dry, force, verbose, stopBuildOnErrors }, + compilerOptions: overrideCompilerOptions, }); const orchestrator = new BuildOrchestrator(owner.client, orchestratorResponse, () => { @@ -609,13 +608,12 @@ export class API implements FormatDiagnosticsHo }, function* (rootNames: readonly string[], buildOrchestratorOptions: BuildOrchestratorOptions): Generator { yield* owner.ensureInitialized.gen(); + const { cwd, dry, force, verbose, stopBuildOnErrors, overrideCompilerOptions } = buildOrchestratorOptions; const orchestratorResponse = yield* apiRequest("createBuildOrchestrator", { - ...buildOrchestratorOptions, - ...buildOrchestratorOptions.overrideCompilerOptions, - cwd: buildOrchestratorOptions.cwd === undefined - ? undefined - : toRootedDirectoryPath(buildOrchestratorOptions.cwd, owner.getCurrentDirectory()), rootNames, + cwd: cwd === undefined ? undefined : toRootedDirectoryPath(cwd, owner.getCurrentDirectory()), + buildOptions: { dry, force, verbose, stopBuildOnErrors }, + compilerOptions: overrideCompilerOptions, }); const orchestrator = new BuildOrchestrator(owner.client, orchestratorResponse, () => { @@ -3946,6 +3944,7 @@ export interface OverrideCompilerOptions { sourceMap?: boolean; inlineSourceMap?: boolean; traceResolution?: boolean; + noEmit?: boolean; } export class BuildOrchestrator { diff --git a/packages/typescript/test/async/api.test.ts b/packages/typescript/test/async/api.test.ts index 3fc2126b968bd..eca0e838982b2 100644 --- a/packages/typescript/test/async/api.test.ts +++ b/packages/typescript/test/async/api.test.ts @@ -1712,7 +1712,7 @@ describe("BuildOrchestrator", () => { overrideCompilerOptions: { incremental: true, assumeChangesOnlyAffectDirectDependencies: true, - declaration: false, + declaration: true, declarationMap: true, emitDeclarationOnly: false, sourceMap: false, @@ -1731,6 +1731,76 @@ describe("BuildOrchestrator", () => { assert.match(fs.readFile!(toRootedFilePath("/b/dist/index.js", undefined))!, /export const b = 2/); }); + test("applies overrideCompilerOptions to the build", async () => { + const { api: disposableApi, fs } = spawnAPIWithFS({ ...files }); + await using api = disposableApi; + // @ts-ignore -- Internal API exercised by source tests. + const orchestrator = await api.createBuildOrchestrator( + ["/a/tsconfig.json"], + { cwd: "/", overrideCompilerOptions: { declarationMap: true } }, + ); + + assert.equal((await orchestrator.build()).status, 0); + assert.match(fs.readFile!(toRootedFilePath("/a/dist/index.d.ts.map", undefined))!, /"sources":\["\.\.\/src\/index\.ts"\]/); + }); + + test("applies build options to the build", async () => { + const { api: disposableApi, fs } = spawnAPIWithFS({ ...files }); + await using api = disposableApi; + // @ts-ignore -- Internal API exercised by source tests. + const orchestrator = await api.createBuildOrchestrator( + ["/a/tsconfig.json"], + { cwd: "/", dry: true }, + ); + + assert.equal((await orchestrator.build()).status, 0); + assert.equal(fs.readFile!(toRootedFilePath("/a/dist/index.js", undefined)), serverFS.useOS); + }); + + test("does not emit error-free projects when noEmit is overridden", async () => { + const { api: disposableApi, fs } = spawnAPIWithFS({ + "/a/tsconfig.json": JSON.stringify({ + compilerOptions: { outDir: "dist", rootDir: "src" }, + files: ["src/index.ts"], + }), + "/a/src/index.ts": `export const a = 1;`, + }); + await using api = disposableApi; + // @ts-ignore -- Internal API exercised by source tests. + const orchestrator = await api.createBuildOrchestrator( + ["/a/tsconfig.json"], + { cwd: "/", overrideCompilerOptions: { noEmit: true } }, + ); + + const response = await orchestrator.build(); + assert.equal(response.status, 0); + assert.equal(response.diagnostics, undefined); + assert.equal(fs.readFile!(toRootedFilePath("/a/dist/index.js", undefined)), serverFS.useOS); + }); + + test("does not emit when noEmit is overridden", async () => { + const source = `export const value: string = 1;`; + const { api: disposableApi, fs } = spawnAPIWithFS({ + "/a/tsconfig.json": JSON.stringify({ + compilerOptions: { outDir: "dist", rootDir: "src" }, + files: ["src/index.ts"], + }), + "/a/src/index.ts": source, + }); + await using api = disposableApi; + // @ts-ignore -- Internal API exercised by source tests. + const orchestrator = await api.createBuildOrchestrator( + ["/a/tsconfig.json"], + { cwd: "/", overrideCompilerOptions: { noEmit: true } }, + ); + + const response = await orchestrator.build(); + assert.equal(response.status, 2); + assert.equal(response.diagnostics?.length, 1); + assert.equal(response.diagnostics[0].code, 2322); + assert.equal(fs.readFile!(toRootedFilePath("/a/dist/index.js", undefined)), serverFS.useOS); + }); + test("returns diagnostics in build response information", async () => { const source = `export const value: string = 1;`; const { api: disposableApi } = spawnAPIWithFS({ diff --git a/packages/typescript/test/sync/api.test.ts b/packages/typescript/test/sync/api.test.ts index bb4caee65856e..256c021b5a535 100644 --- a/packages/typescript/test/sync/api.test.ts +++ b/packages/typescript/test/sync/api.test.ts @@ -1578,7 +1578,7 @@ describe("BuildOrchestrator", () => { overrideCompilerOptions: { incremental: true, assumeChangesOnlyAffectDirectDependencies: true, - declaration: false, + declaration: true, declarationMap: true, emitDeclarationOnly: false, sourceMap: false, @@ -1597,6 +1597,76 @@ describe("BuildOrchestrator", () => { assert.match(fs.readFile!(toRootedFilePath("/b/dist/index.js", undefined))!, /export const b = 2/); }); + test("applies overrideCompilerOptions to the build", () => { + const { api: disposableApi, fs } = spawnAPIWithFS({ ...files }); + using api = disposableApi; + // @ts-ignore -- Internal API exercised by source tests. + const orchestrator = api.createBuildOrchestrator( + ["/a/tsconfig.json"], + { cwd: "/", overrideCompilerOptions: { declarationMap: true } }, + ); + + assert.equal((orchestrator.build()).status, 0); + assert.match(fs.readFile!(toRootedFilePath("/a/dist/index.d.ts.map", undefined))!, /"sources":\["\.\.\/src\/index\.ts"\]/); + }); + + test("applies build options to the build", () => { + const { api: disposableApi, fs } = spawnAPIWithFS({ ...files }); + using api = disposableApi; + // @ts-ignore -- Internal API exercised by source tests. + const orchestrator = api.createBuildOrchestrator( + ["/a/tsconfig.json"], + { cwd: "/", dry: true }, + ); + + assert.equal((orchestrator.build()).status, 0); + assert.equal(fs.readFile!(toRootedFilePath("/a/dist/index.js", undefined)), serverFS.useOS); + }); + + test("does not emit error-free projects when noEmit is overridden", () => { + const { api: disposableApi, fs } = spawnAPIWithFS({ + "/a/tsconfig.json": JSON.stringify({ + compilerOptions: { outDir: "dist", rootDir: "src" }, + files: ["src/index.ts"], + }), + "/a/src/index.ts": `export const a = 1;`, + }); + using api = disposableApi; + // @ts-ignore -- Internal API exercised by source tests. + const orchestrator = api.createBuildOrchestrator( + ["/a/tsconfig.json"], + { cwd: "/", overrideCompilerOptions: { noEmit: true } }, + ); + + const response = orchestrator.build(); + assert.equal(response.status, 0); + assert.equal(response.diagnostics, undefined); + assert.equal(fs.readFile!(toRootedFilePath("/a/dist/index.js", undefined)), serverFS.useOS); + }); + + test("does not emit when noEmit is overridden", () => { + const source = `export const value: string = 1;`; + const { api: disposableApi, fs } = spawnAPIWithFS({ + "/a/tsconfig.json": JSON.stringify({ + compilerOptions: { outDir: "dist", rootDir: "src" }, + files: ["src/index.ts"], + }), + "/a/src/index.ts": source, + }); + using api = disposableApi; + // @ts-ignore -- Internal API exercised by source tests. + const orchestrator = api.createBuildOrchestrator( + ["/a/tsconfig.json"], + { cwd: "/", overrideCompilerOptions: { noEmit: true } }, + ); + + const response = orchestrator.build(); + assert.equal(response.status, 2); + assert.equal(response.diagnostics?.length, 1); + assert.equal(response.diagnostics[0].code, 2322); + assert.equal(fs.readFile!(toRootedFilePath("/a/dist/index.js", undefined)), serverFS.useOS); + }); + test("returns diagnostics in build response information", () => { const source = `export const value: string = 1;`; const { api: disposableApi } = spawnAPIWithFS({ diff --git a/tools/gen-proto/main_test.go b/tools/gen-proto/main_test.go index c7cb555c4cd36..446b2f3d41486 100644 --- a/tools/gen-proto/main_test.go +++ b/tools/gen-proto/main_test.go @@ -49,7 +49,8 @@ func TestGenerate(t *testing.T) { `import type { CompilerOptions, PluginImport } from "./compilerOptions.generated.ts";`, `export type { CompilerOptions, PluginImport } from "./compilerOptions.generated.ts";`, `export * from "./compilerOptions.generated.ts";`, - `export interface CreateBuildOrchestratorParams extends BuildOptions, CompilerOptions`, + `buildOptions?: BuildOptions | undefined;`, + `compilerOptions?: CompilerOptions | undefined;`, `jsx?: JsxEmit | undefined;`, `module?: ModuleKind | undefined;`, `moduleResolution?: ModuleResolutionKind | undefined;`, diff --git a/tsc/internal/api/proto.go b/tsc/internal/api/proto.go index c3e55e188956d..fc617acfed133 100644 --- a/tsc/internal/api/proto.go +++ b/tsc/internal/api/proto.go @@ -992,9 +992,9 @@ type ProfileResult struct { type CreateBuildOrchestratorParams struct { RootNames []string `json:"rootNames"` Cwd tspath.RootedDirectoryPath `json:"cwd,omitempty"` - // Only a subset of these options are exposed the API - *core.BuildOptions `json:"buildOptions,omitempty"` - *core.CompilerOptions `json:"compilerOptions,omitempty"` + // Only a subset of these options are exposed in the API + BuildOptions *core.BuildOptions `json:"buildOptions,omitempty"` + CompilerOptions *core.CompilerOptions `json:"compilerOptions,omitempty"` } type CreateBuildOrchestratorResponse struct {