diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index eb9f1e3cf34a6..9c30cb87a4daa 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -186,6 +186,12 @@ namespace ts { category: Diagnostics.Basic_Options, description: Diagnostics.Generates_corresponding_d_ts_file, }, + { + name: "emitDeclarationsOnly", + type: "boolean", + category: Diagnostics.Advanced_Options, + description: Diagnostics.Only_emit_d_ts_declaration_files, + }, { name: "sourceMap", type: "boolean", diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 7a62d6d5aa4a1..6fffd576e7caf 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2807,6 +2807,10 @@ "category": "Message", "code": 6013 }, + "Only emit '.d.ts' declaration files.": { + "category": "Message", + "code": 6014 + }, "Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'.": { "category": "Message", "code": 6015 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 6f3041666bab1..03ff63a421123 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -135,7 +135,7 @@ namespace ts { function emitSourceFileOrBundle({ jsFilePath, sourceMapFilePath, declarationFilePath }: EmitFileNames, sourceFileOrBundle: SourceFile | Bundle) { // Make sure not to write js file and source map file if any of them cannot be written - if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit) { + if (!host.isEmitBlocked(jsFilePath) && !compilerOptions.noEmit && !compilerOptions.emitDeclarationsOnly) { if (!emitOnlyDtsFiles) { printSourceFileOrBundle(jsFilePath, sourceMapFilePath, sourceFileOrBundle); } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index bd45857fc51dc..23f9b08aa798c 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2201,6 +2201,16 @@ namespace ts { programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "checkJs", "allowJs")); } + if (options.emitDeclarationsOnly) { + if (!options.declaration) { + createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDeclarationsOnly", "declarations"); + } + + if (options.noEmit) { + createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_with_option_1, "emitDeclarationsOnly", "noEmit"); + } + } + if (options.emitDecoratorMetadata && !options.experimentalDecorators) { createDiagnosticForOptionName(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "emitDecoratorMetadata", "experimentalDecorators"); @@ -2223,7 +2233,9 @@ namespace ts { const emitHost = getEmitHost(); const emitFilesSeen = createMap(); forEachEmittedFile(emitHost, (emitFileNames) => { - verifyEmitFilePath(emitFileNames.jsFilePath, emitFilesSeen); + if (!options.emitDeclarationsOnly) { + verifyEmitFilePath(emitFileNames.jsFilePath, emitFilesSeen); + } verifyEmitFilePath(emitFileNames.declarationFilePath, emitFilesSeen); }); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f6ca2f45d3401..481b91c9276e1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3967,6 +3967,7 @@ namespace ts { /** configFile is set as non enumerable property so as to avoid checking of json source files */ /* @internal */ readonly configFile?: JsonSourceFile; declaration?: boolean; + emitDeclarationsOnly?: boolean; declarationDir?: string; /* @internal */ diagnostics?: boolean; /* @internal */ extendedDiagnostics?: boolean; diff --git a/src/harness/harness.ts b/src/harness/harness.ts index f2f8587d83412..3e6f4cfa16390 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1252,8 +1252,18 @@ namespace Harness { options: ts.CompilerOptions, // Current directory is needed for rwcRunner to be able to use currentDirectory defined in json file currentDirectory: string): DeclarationCompilationContext | undefined { - if (options.declaration && result.errors.length === 0 && result.declFilesCode.length !== result.files.length) { - throw new Error("There were no errors and declFiles generated did not match number of js files generated"); + + if (result.errors.length === 0) { + if (options.declaration) { + if (options.emitDeclarationsOnly) { + if (result.files.length > 0 || result.declFilesCode.length === 0) { + throw new Error("Only declaration files should be generated when emitDeclarationsOnly:true"); + } + } + else if (result.declFilesCode.length !== result.files.length) { + throw new Error("There were no errors and declFiles generated did not match number of js files generated"); + } + } } const declInputFiles: TestFile[] = []; @@ -1654,7 +1664,7 @@ namespace Harness { } export function doJsEmitBaseline(baselinePath: string, header: string, options: ts.CompilerOptions, result: CompilerResult, tsConfigFiles: Harness.Compiler.TestFile[], toBeCompiled: Harness.Compiler.TestFile[], otherFiles: Harness.Compiler.TestFile[], harnessSettings: Harness.TestCaseParser.CompilerSettings) { - if (!options.noEmit && result.files.length === 0 && result.errors.length === 0) { + if (!options.noEmit && !options.emitDeclarationsOnly && result.files.length === 0 && result.errors.length === 0) { throw new Error("Expected at least one js file to be emitted or at least one error to be created."); } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index cd1b577b71173..3db774c736f01 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2266,6 +2266,7 @@ declare namespace ts { charset?: string; checkJs?: boolean; declaration?: boolean; + emitDeclarationsOnly?: boolean; declarationDir?: string; disableSizeLimit?: boolean; downlevelIteration?: boolean; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 3cbca96f8fa97..41dde69a87f83 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2266,6 +2266,7 @@ declare namespace ts { charset?: string; checkJs?: boolean; declaration?: boolean; + emitDeclarationsOnly?: boolean; declarationDir?: string; disableSizeLimit?: boolean; downlevelIteration?: boolean; diff --git a/tests/baselines/reference/declFileEmitDeclarationsOnly.js b/tests/baselines/reference/declFileEmitDeclarationsOnly.js new file mode 100644 index 0000000000000..78d9842fe88b6 --- /dev/null +++ b/tests/baselines/reference/declFileEmitDeclarationsOnly.js @@ -0,0 +1,26 @@ +//// [helloworld.ts] +const Log = { + info(msg: string) {} +} + +class HelloWorld { + constructor(private name: string) { + } + + public hello() { + Log.info(`Hello ${this.name}`); + } +} + + + + +//// [helloworld.d.ts] +declare const Log: { + info(msg: string): void; +}; +declare class HelloWorld { + private name; + constructor(name: string); + hello(): void; +} diff --git a/tests/baselines/reference/declFileEmitDeclarationsOnly.symbols b/tests/baselines/reference/declFileEmitDeclarationsOnly.symbols new file mode 100644 index 0000000000000..e2a277fb60347 --- /dev/null +++ b/tests/baselines/reference/declFileEmitDeclarationsOnly.symbols @@ -0,0 +1,29 @@ +=== tests/cases/compiler/helloworld.ts === +const Log = { +>Log : Symbol(Log, Decl(helloworld.ts, 0, 5)) + + info(msg: string) {} +>info : Symbol(info, Decl(helloworld.ts, 0, 13)) +>msg : Symbol(msg, Decl(helloworld.ts, 1, 7)) +} + +class HelloWorld { +>HelloWorld : Symbol(HelloWorld, Decl(helloworld.ts, 2, 1)) + + constructor(private name: string) { +>name : Symbol(HelloWorld.name, Decl(helloworld.ts, 5, 14)) + } + + public hello() { +>hello : Symbol(HelloWorld.hello, Decl(helloworld.ts, 6, 3)) + + Log.info(`Hello ${this.name}`); +>Log.info : Symbol(info, Decl(helloworld.ts, 0, 13)) +>Log : Symbol(Log, Decl(helloworld.ts, 0, 5)) +>info : Symbol(info, Decl(helloworld.ts, 0, 13)) +>this.name : Symbol(HelloWorld.name, Decl(helloworld.ts, 5, 14)) +>this : Symbol(HelloWorld, Decl(helloworld.ts, 2, 1)) +>name : Symbol(HelloWorld.name, Decl(helloworld.ts, 5, 14)) + } +} + diff --git a/tests/baselines/reference/declFileEmitDeclarationsOnly.types b/tests/baselines/reference/declFileEmitDeclarationsOnly.types new file mode 100644 index 0000000000000..9a3cf8cfc70b0 --- /dev/null +++ b/tests/baselines/reference/declFileEmitDeclarationsOnly.types @@ -0,0 +1,32 @@ +=== tests/cases/compiler/helloworld.ts === +const Log = { +>Log : { info(msg: string): void; } +>{ info(msg: string) {}} : { info(msg: string): void; } + + info(msg: string) {} +>info : (msg: string) => void +>msg : string +} + +class HelloWorld { +>HelloWorld : HelloWorld + + constructor(private name: string) { +>name : string + } + + public hello() { +>hello : () => void + + Log.info(`Hello ${this.name}`); +>Log.info(`Hello ${this.name}`) : void +>Log.info : (msg: string) => void +>Log : { info(msg: string): void; } +>info : (msg: string) => void +>`Hello ${this.name}` : string +>this.name : string +>this : this +>name : string + } +} + diff --git a/tests/baselines/reference/declFileEmitDeclarationsOnlyError1.errors.txt b/tests/baselines/reference/declFileEmitDeclarationsOnlyError1.errors.txt new file mode 100644 index 0000000000000..88a154cc508fb --- /dev/null +++ b/tests/baselines/reference/declFileEmitDeclarationsOnlyError1.errors.txt @@ -0,0 +1,7 @@ +error TS5052: Option 'emitDeclarationsOnly' cannot be specified without specifying option 'declarations'. + + +!!! error TS5052: Option 'emitDeclarationsOnly' cannot be specified without specifying option 'declarations'. +==== tests/cases/compiler/hello.ts (0 errors) ==== + var hello = "yo!"; + \ No newline at end of file diff --git a/tests/baselines/reference/declFileEmitDeclarationsOnlyError1.symbols b/tests/baselines/reference/declFileEmitDeclarationsOnlyError1.symbols new file mode 100644 index 0000000000000..52b2f9bbe64a4 --- /dev/null +++ b/tests/baselines/reference/declFileEmitDeclarationsOnlyError1.symbols @@ -0,0 +1,4 @@ +=== tests/cases/compiler/hello.ts === +var hello = "yo!"; +>hello : Symbol(hello, Decl(hello.ts, 0, 3)) + diff --git a/tests/baselines/reference/declFileEmitDeclarationsOnlyError1.types b/tests/baselines/reference/declFileEmitDeclarationsOnlyError1.types new file mode 100644 index 0000000000000..e1603394d7c97 --- /dev/null +++ b/tests/baselines/reference/declFileEmitDeclarationsOnlyError1.types @@ -0,0 +1,5 @@ +=== tests/cases/compiler/hello.ts === +var hello = "yo!"; +>hello : string +>"yo!" : "yo!" + diff --git a/tests/baselines/reference/declFileEmitDeclarationsOnlyError2.errors.txt b/tests/baselines/reference/declFileEmitDeclarationsOnlyError2.errors.txt new file mode 100644 index 0000000000000..7c067d83ba221 --- /dev/null +++ b/tests/baselines/reference/declFileEmitDeclarationsOnlyError2.errors.txt @@ -0,0 +1,9 @@ +error TS5052: Option 'emitDeclarationsOnly' cannot be specified without specifying option 'declarations'. +error TS5053: Option 'emitDeclarationsOnly' cannot be specified with option 'noEmit'. + + +!!! error TS5052: Option 'emitDeclarationsOnly' cannot be specified without specifying option 'declarations'. +!!! error TS5053: Option 'emitDeclarationsOnly' cannot be specified with option 'noEmit'. +==== tests/cases/compiler/hello.ts (0 errors) ==== + var hello = "yo!"; + \ No newline at end of file diff --git a/tests/baselines/reference/declFileEmitDeclarationsOnlyError2.symbols b/tests/baselines/reference/declFileEmitDeclarationsOnlyError2.symbols new file mode 100644 index 0000000000000..52b2f9bbe64a4 --- /dev/null +++ b/tests/baselines/reference/declFileEmitDeclarationsOnlyError2.symbols @@ -0,0 +1,4 @@ +=== tests/cases/compiler/hello.ts === +var hello = "yo!"; +>hello : Symbol(hello, Decl(hello.ts, 0, 3)) + diff --git a/tests/baselines/reference/declFileEmitDeclarationsOnlyError2.types b/tests/baselines/reference/declFileEmitDeclarationsOnlyError2.types new file mode 100644 index 0000000000000..e1603394d7c97 --- /dev/null +++ b/tests/baselines/reference/declFileEmitDeclarationsOnlyError2.types @@ -0,0 +1,5 @@ +=== tests/cases/compiler/hello.ts === +var hello = "yo!"; +>hello : string +>"yo!" : "yo!" + diff --git a/tests/cases/compiler/declFileEmitDeclarationsOnly.ts b/tests/cases/compiler/declFileEmitDeclarationsOnly.ts new file mode 100644 index 0000000000000..e55496e4ca368 --- /dev/null +++ b/tests/cases/compiler/declFileEmitDeclarationsOnly.ts @@ -0,0 +1,16 @@ +// @declaration: true +// @emitDeclarationsOnly: true + +// @filename: helloworld.ts +const Log = { + info(msg: string) {} +} + +class HelloWorld { + constructor(private name: string) { + } + + public hello() { + Log.info(`Hello ${this.name}`); + } +} diff --git a/tests/cases/compiler/declFileEmitDeclarationsOnlyError1.ts b/tests/cases/compiler/declFileEmitDeclarationsOnlyError1.ts new file mode 100644 index 0000000000000..477934a7b2a61 --- /dev/null +++ b/tests/cases/compiler/declFileEmitDeclarationsOnlyError1.ts @@ -0,0 +1,4 @@ +// @emitDeclarationsOnly: true + +// @filename: hello.ts +var hello = "yo!"; diff --git a/tests/cases/compiler/declFileEmitDeclarationsOnlyError2.ts b/tests/cases/compiler/declFileEmitDeclarationsOnlyError2.ts new file mode 100644 index 0000000000000..994a6a5dbd867 --- /dev/null +++ b/tests/cases/compiler/declFileEmitDeclarationsOnlyError2.ts @@ -0,0 +1,5 @@ +// @noEmit: true +// @emitDeclarationsOnly: true + +// @filename: hello.ts +var hello = "yo!";