1namespace ts { 2 const _chai: typeof import("chai") = require("chai"); 3 const expect: typeof _chai.expect = _chai.expect; 4 describe("unittests:: services:: languageService", () => { 5 const files: {[index: string]: string} = { 6 "foo.ts": `import Vue from "./vue"; 7import Component from "./vue-class-component"; 8import { vueTemplateHtml } from "./variables"; 9 10@Component({ 11 template: vueTemplateHtml, 12}) 13class Carousel<T> extends Vue { 14}`, 15 "variables.ts": `export const vueTemplateHtml = \`<div></div>\`;`, 16 "vue.d.ts": `export namespace Vue { export type Config = { template: string }; }`, 17 "vue-class-component.d.ts": `import Vue from "./vue"; 18export function Component(x: Config): any;` 19 }; 20 21 function createLanguageService() { 22 return ts.createLanguageService({ 23 getCompilationSettings() { 24 return {}; 25 }, 26 getScriptFileNames() { 27 return ["foo.ts", "variables.ts", "vue.d.ts", "vue-class-component.d.ts"]; 28 }, 29 getScriptVersion(_fileName) { 30 return ""; 31 }, 32 getScriptSnapshot(fileName) { 33 if (fileName === ".ts") { 34 return ScriptSnapshot.fromString(""); 35 } 36 return ScriptSnapshot.fromString(files[fileName] || ""); 37 }, 38 getCurrentDirectory: () => ".", 39 getDefaultLibFileName(options) { 40 return getDefaultLibFilePath(options); 41 }, 42 }); 43 } 44 // Regression test for GH #18245 - bug in single line comment writer caused a debug assertion when attempting 45 // to write an alias to a module's default export was referrenced across files and had no default export 46 it("should be able to create a language service which can respond to deinition requests without throwing", () => { 47 const languageService = createLanguageService(); 48 const definitions = languageService.getDefinitionAtPosition("foo.ts", 160); // 160 is the latter `vueTemplateHtml` position 49 expect(definitions).to.exist; // eslint-disable-line @typescript-eslint/no-unused-expressions 50 }); 51 52 it("getEmitOutput on language service has way to force dts emit", () => { 53 const languageService = createLanguageService(); 54 assert.deepEqual( 55 languageService.getEmitOutput( 56 "foo.ts", 57 /*emitOnlyDtsFiles*/ true 58 ), 59 { 60 emitSkipped: true, 61 diagnostics: emptyArray, 62 outputFiles: emptyArray, 63 exportedModulesFromDeclarationEmit: undefined 64 } 65 ); 66 67 assert.deepEqual( 68 languageService.getEmitOutput( 69 "foo.ts", 70 /*emitOnlyDtsFiles*/ true, 71 /*forceDtsEmit*/ true 72 ), 73 { 74 emitSkipped: false, 75 diagnostics: emptyArray, 76 outputFiles: [{ 77 name: "foo.d.ts", 78 text: "export {};\r\n", 79 writeByteOrderMark: false 80 }], 81 exportedModulesFromDeclarationEmit: undefined 82 } 83 ); 84 }); 85 86 describe("detects program upto date correctly", () => { 87 function verifyProgramUptoDate(useProjectVersion: boolean) { 88 let projectVersion = "1"; 89 const files = new Map<string, { version: string, text: string; }>(); 90 files.set("/project/root.ts", { version: "1", text: `import { foo } from "./other"` }); 91 files.set("/project/other.ts", { version: "1", text: `export function foo() { }` }); 92 files.set("/lib/lib.d.ts", { version: "1", text: projectSystem.libFile.content }); 93 const host: LanguageServiceHost = { 94 useCaseSensitiveFileNames: returnTrue, 95 getCompilationSettings: getDefaultCompilerOptions, 96 fileExists: path => files.has(path), 97 getProjectVersion: !useProjectVersion ? undefined : () => projectVersion, 98 getScriptFileNames: () => ["/project/root.ts"], 99 getScriptVersion: path => files.get(path)?.version || "", 100 getScriptSnapshot: path => { 101 const text = files.get(path)?.text; 102 return text ? ScriptSnapshot.fromString(text) : undefined; 103 }, 104 getCurrentDirectory: () => "/project", 105 getDefaultLibFileName: () => "/lib/lib.d.ts" 106 }; 107 const ls = ts.createLanguageService(host); 108 const program1 = ls.getProgram()!; 109 const program2 = ls.getProgram()!; 110 assert.strictEqual(program1, program2); 111 verifyProgramFiles(program1); 112 113 // Change other 114 projectVersion = "2"; 115 files.set("/project/other.ts", { version: "2", text: `export function foo() { } export function bar() { }` }); 116 const program3 = ls.getProgram()!; 117 assert.notStrictEqual(program2, program3); 118 verifyProgramFiles(program3); 119 120 // change root 121 projectVersion = "3"; 122 files.set("/project/root.ts", { version: "2", text: `import { foo, bar } from "./other"` }); 123 const program4 = ls.getProgram()!; 124 assert.notStrictEqual(program3, program4); 125 verifyProgramFiles(program4); 126 127 function verifyProgramFiles(program: Program) { 128 assert.deepEqual( 129 program.getSourceFiles().map(f => f.fileName), 130 ["/lib/lib.d.ts", "/project/other.ts", "/project/root.ts"] 131 ); 132 } 133 } 134 it("when host implements getProjectVersion", () => { 135 verifyProgramUptoDate(/*useProjectVersion*/ true); 136 }); 137 it("when host does not implement getProjectVersion", () => { 138 verifyProgramUptoDate(/*useProjectVersion*/ false); 139 }); 140 }); 141 }); 142} 143