diff --git a/packages/typescript/src/api/async/api.ts b/packages/typescript/src/api/async/api.ts index a9dd53fe3a3f1..78709a7353043 100644 --- a/packages/typescript/src/api/async/api.ts +++ b/packages/typescript/src/api/async/api.ts @@ -485,13 +485,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, () => { @@ -2178,6 +2177,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 93774869c4407..b8f2cded08cc2 100644 --- a/packages/typescript/src/api/proto.generated.ts +++ b/packages/typescript/src/api/proto.generated.ts @@ -338,9 +338,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 8950863c33460..a052d60673d6d 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, () => { @@ -3933,6 +3931,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 379f07b6b68f7..84b4774239196 100644 --- a/packages/typescript/test/async/api.test.ts +++ b/packages/typescript/test/async/api.test.ts @@ -1706,7 +1706,7 @@ describe("BuildOrchestrator", () => { overrideCompilerOptions: { incremental: true, assumeChangesOnlyAffectDirectDependencies: true, - declaration: false, + declaration: true, declarationMap: true, emitDeclarationOnly: false, sourceMap: false, @@ -1725,6 +1725,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 f048e8ede87e6..8580875a57bb8 100644 --- a/packages/typescript/test/sync/api.test.ts +++ b/packages/typescript/test/sync/api.test.ts @@ -1572,7 +1572,7 @@ describe("BuildOrchestrator", () => { overrideCompilerOptions: { incremental: true, assumeChangesOnlyAffectDirectDependencies: true, - declaration: false, + declaration: true, declarationMap: true, emitDeclarationOnly: false, sourceMap: false, @@ -1591,6 +1591,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 6a7cdb969ce43..2d7d1e9c1064c 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 c03e7ff85ed46..b905155e2f499 100644 --- a/tsc/internal/api/proto.go +++ b/tsc/internal/api/proto.go @@ -968,9 +968,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 {