diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d87157aa75362..cbff1fb2512f7 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -393,7 +393,9 @@ namespace ts { declarationFilePath: string | undefined, declarationMapPath: string | undefined, relativeToBuildInfo: (path: string) => string) { - if (!sourceFileOrBundle || !declarationFilePath) { + if (!sourceFileOrBundle) return; + if (!declarationFilePath) { + if (emitOnlyDtsFiles || compilerOptions.emitDeclarationOnly) emitSkipped = true; return; } const sourceFiles = isSourceFile(sourceFileOrBundle) ? [sourceFileOrBundle] : sourceFileOrBundle.sourceFiles; diff --git a/src/services/services.ts b/src/services/services.ts index acd06a8ac6dec..6d2a3d085314e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1638,12 +1638,12 @@ namespace ts { return NavigateTo.getNavigateToItems(sourceFiles, program.getTypeChecker(), cancellationToken, searchValue, maxResultCount, excludeDtsFiles); } - function getEmitOutput(fileName: string, emitOnlyDtsFiles = false) { + function getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, forceDtsEmit?: boolean) { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); const customTransformers = host.getCustomTransformers && host.getCustomTransformers(); - return getFileEmitOutput(program, sourceFile, emitOnlyDtsFiles, cancellationToken, customTransformers); + return getFileEmitOutput(program, sourceFile, !!emitOnlyDtsFiles, cancellationToken, customTransformers, forceDtsEmit); } // Signature help diff --git a/src/services/types.ts b/src/services/types.ts index 8bf2cf2504527..3c08ddf5fd689 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -391,7 +391,7 @@ namespace ts { organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; - getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; + getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, forceDtsEmit?: boolean): EmitOutput; getProgram(): Program | undefined; diff --git a/src/testRunner/unittests/services/languageService.ts b/src/testRunner/unittests/services/languageService.ts index a5f38eb323bd5..d9a69dcd2e66b 100644 --- a/src/testRunner/unittests/services/languageService.ts +++ b/src/testRunner/unittests/services/languageService.ts @@ -15,10 +15,9 @@ class Carousel extends Vue { "vue-class-component.d.ts": `import Vue from "./vue"; export function Component(x: Config): any;` }; - // Regression test for GH #18245 - bug in single line comment writer caused a debug assertion when attempting - // to write an alias to a module's default export was referrenced across files and had no default export - it("should be able to create a language service which can respond to deinition requests without throwing", () => { - const languageService = createLanguageService({ + + function createLanguageService() { + return ts.createLanguageService({ getCompilationSettings() { return {}; }, @@ -39,8 +38,45 @@ export function Component(x: Config): any;` return getDefaultLibFilePath(options); }, }); + } + // Regression test for GH #18245 - bug in single line comment writer caused a debug assertion when attempting + // to write an alias to a module's default export was referrenced across files and had no default export + it("should be able to create a language service which can respond to deinition requests without throwing", () => { + const languageService = createLanguageService(); const definitions = languageService.getDefinitionAtPosition("foo.ts", 160); // 160 is the latter `vueTemplateHtml` position expect(definitions).to.exist; // eslint-disable-line no-unused-expressions }); + + it("getEmitOutput on language service has way to force dts emit", () => { + const languageService = createLanguageService(); + assert.deepEqual( + languageService.getEmitOutput( + "foo.ts", + /*emitOnlyDtsFiles*/ true + ), + { + emitSkipped: true, + outputFiles: emptyArray, + exportedModulesFromDeclarationEmit: undefined + } + ); + + assert.deepEqual( + languageService.getEmitOutput( + "foo.ts", + /*emitOnlyDtsFiles*/ true, + /*forceDtsEmit*/ true + ), + { + emitSkipped: false, + outputFiles: [{ + name: "foo.d.ts", + text: "export {};\r\n", + writeByteOrderMark: false + }], + exportedModulesFromDeclarationEmit: undefined + } + ); + }); }); } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index a9808fc440c2a..aa1945e74b9d1 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5073,7 +5073,7 @@ declare namespace ts { getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined; organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; - getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; + getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, forceDtsEmit?: boolean): EmitOutput; getProgram(): Program | undefined; dispose(): void; } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 9d1ffdf5e57e1..7c756d8a0a3a3 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5073,7 +5073,7 @@ declare namespace ts { getEditsForRefactor(fileName: string, formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string, preferences: UserPreferences | undefined): RefactorEditInfo | undefined; organizeImports(scope: OrganizeImportsScope, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; getEditsForFileRename(oldFilePath: string, newFilePath: string, formatOptions: FormatCodeSettings, preferences: UserPreferences | undefined): readonly FileTextChanges[]; - getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean): EmitOutput; + getEmitOutput(fileName: string, emitOnlyDtsFiles?: boolean, forceDtsEmit?: boolean): EmitOutput; getProgram(): Program | undefined; dispose(): void; }