1namespace ts.projectSystem { 2 describe("unittests:: tsserver:: symLinks", () => { 3 it("rename in common file renames all project", () => { 4 const projects = "/users/username/projects"; 5 const folderA = `${projects}/a`; 6 const aFile: File = { 7 path: `${folderA}/a.ts`, 8 content: `import {C} from "./c/fc"; console.log(C)` 9 }; 10 const aTsconfig: File = { 11 path: `${folderA}/tsconfig.json`, 12 content: JSON.stringify({ compilerOptions: { module: "commonjs" } }) 13 }; 14 const aC: SymLink = { 15 path: `${folderA}/c`, 16 symLink: "../c" 17 }; 18 const aFc = `${folderA}/c/fc.ts`; 19 20 const folderB = `${projects}/b`; 21 const bFile: File = { 22 path: `${folderB}/b.ts`, 23 content: `import {C} from "./c/fc"; console.log(C)` 24 }; 25 const bTsconfig: File = { 26 path: `${folderB}/tsconfig.json`, 27 content: JSON.stringify({ compilerOptions: { module: "commonjs" } }) 28 }; 29 const bC: SymLink = { 30 path: `${folderB}/c`, 31 symLink: "../c" 32 }; 33 const bFc = `${folderB}/c/fc.ts`; 34 35 const folderC = `${projects}/c`; 36 const cFile: File = { 37 path: `${folderC}/fc.ts`, 38 content: `export const C = 8` 39 }; 40 41 const files = [cFile, libFile, aFile, aTsconfig, aC, bFile, bTsconfig, bC]; 42 const host = createServerHost(files); 43 const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) }); 44 openFilesForSession( 45 [ 46 { file: aFile, projectRootPath: folderA }, 47 { file: bFile, projectRootPath: folderB }, 48 { file: aFc, projectRootPath: folderA }, 49 { file: bFc, projectRootPath: folderB }, 50 ], 51 session); 52 executeSessionRequest<protocol.RenameRequest, protocol.RenameResponse>(session, protocol.CommandTypes.Rename, { file: aFc, ...protocolLocationFromSubstring(cFile.content, "C") }); 53 baselineTsserverLogs("symLinks", "rename in common file renames all project", session); 54 }); 55 56 describe("module resolution when symlinked folder contents change and resolve modules", () => { 57 const projectRootPath = "/users/username/projects/myproject"; 58 const packages = `${projectRootPath}/javascript/packages`; 59 const recognizersDateTime = `${packages}/recognizers-date-time`; 60 const recognizersText = `${packages}/recognizers-text`; 61 const recognizersTextDist = `${recognizersText}/dist`; 62 const moduleName = "@microsoft/recognizers-text"; 63 const moduleNameInFile = `"${moduleName}"`; 64 const recognizersDateTimeSrcFile: File = { 65 path: `${recognizersDateTime}/src/datetime/baseDate.ts`, 66 content: `import {C} from ${moduleNameInFile}; 67new C();` 68 }; 69 const recognizerDateTimeTsconfigPath = `${recognizersDateTime}/tsconfig.json`; 70 const recognizerDateTimeTsconfigWithoutPathMapping: File = { 71 path: recognizerDateTimeTsconfigPath, 72 content: JSON.stringify({ 73 include: ["src"] 74 }) 75 }; 76 const recognizerDateTimeTsconfigWithPathMapping: File = { 77 path: recognizerDateTimeTsconfigPath, 78 content: JSON.stringify({ 79 compilerOptions: { 80 rootDir: "src", 81 baseUrl: "./", 82 paths: { 83 "@microsoft/*": ["../*"] 84 } 85 }, 86 include: ["src"] 87 }) 88 }; 89 const nodeModulesRecorgnizersText: SymLink = { 90 path: `${recognizersDateTime}/node_modules/@microsoft/recognizers-text`, 91 symLink: recognizersText 92 }; 93 const recognizerTextSrcFile: File = { 94 path: `${recognizersText}/src/recognizers-text.ts`, 95 content: `export class C { method () { return 10; } }` 96 }; 97 const recongnizerTextDistTypingFile: File = { 98 path: `${recognizersTextDist}/types/recognizers-text.d.ts`, 99 content: `export class C { method(): number; }` 100 }; 101 const recongnizerTextPackageJson: File = { 102 path: `${recognizersText}/package.json`, 103 content: JSON.stringify({ 104 typings: "dist/types/recognizers-text.d.ts" 105 }) 106 }; 107 108 function createSessionAndOpenFile(host: TestServerHost) { 109 const session = createSession(host, { canUseEvents: true, logger: createLoggerWithInMemoryLogs(host) }); 110 session.executeCommandSeq<protocol.OpenRequest>({ 111 command: protocol.CommandTypes.Open, 112 arguments: { 113 file: recognizersDateTimeSrcFile.path, 114 projectRootPath 115 } 116 }); 117 return session; 118 } 119 120 function verifyModuleResolution(withPathMapping: boolean) { 121 describe(withPathMapping ? "when tsconfig file contains path mapping" : "when tsconfig does not contain path mapping", () => { 122 const filesWithSources = [libFile, recognizersDateTimeSrcFile, withPathMapping ? recognizerDateTimeTsconfigWithPathMapping : recognizerDateTimeTsconfigWithoutPathMapping, recognizerTextSrcFile, recongnizerTextPackageJson]; 123 it("when project compiles from sources", () => { 124 const host = createServerHost(filesWithSources); 125 const session = createSessionAndOpenFile(host); 126 verifyGetErrRequest({ session, host, files: [recognizersDateTimeSrcFile] }); 127 128 host.ensureFileOrFolder(nodeModulesRecorgnizersText); 129 host.writeFile(recongnizerTextDistTypingFile.path, recongnizerTextDistTypingFile.content); 130 host.runQueuedTimeoutCallbacks(); // Scheduled invalidation of resolutions 131 host.runQueuedTimeoutCallbacks(); // Actual update 132 133 verifyGetErrRequest({ session, host, files: [recognizersDateTimeSrcFile] }); 134 135 // Change config file's module resolution affecting option 136 const config = JSON.parse(host.readFile(recognizerDateTimeTsconfigPath)!); 137 host.writeFile(recognizerDateTimeTsconfigPath, JSON.stringify({ 138 ...config, 139 compilerOptions: { ...config.compilerOptions, resolveJsonModule: true } 140 })); 141 host.runQueuedTimeoutCallbacks(); // Scheduled invalidation of resolutions 142 host.runQueuedTimeoutCallbacks(); // Actual update 143 144 baselineTsserverLogs("symLinks", `module resolution${withPathMapping ? " with path mapping" : ""} when project compiles from sources`, session); 145 }); 146 147 it("when project has node_modules setup but doesnt have modules in typings folder and then recompiles", () => { 148 const host = createServerHost([...filesWithSources, nodeModulesRecorgnizersText]); 149 const session = createSessionAndOpenFile(host); 150 verifyGetErrRequest({ session, host, files: [recognizersDateTimeSrcFile] }); 151 152 host.writeFile(recongnizerTextDistTypingFile.path, recongnizerTextDistTypingFile.content); 153 host.runQueuedTimeoutCallbacks(); // Scheduled invalidation of resolutions 154 host.runQueuedTimeoutCallbacks(); // Actual update 155 156 verifyGetErrRequest({ session, host, files: [recognizersDateTimeSrcFile] }); 157 baselineTsserverLogs("symLinks", `module resolution${withPathMapping ? " with path mapping" : ""} when project has node_modules setup but doesnt have modules in typings folder and then recompiles`, session); 158 }); 159 160 it("when project recompiles after deleting generated folders", () => { 161 const host = createServerHost([...filesWithSources, nodeModulesRecorgnizersText, recongnizerTextDistTypingFile]); 162 const session = createSessionAndOpenFile(host); 163 164 verifyGetErrRequest({ session, host, files: [recognizersDateTimeSrcFile] }); 165 166 host.deleteFolder(recognizersTextDist, /*recursive*/ true); 167 host.runQueuedTimeoutCallbacks(); // Scheduled invalidation of resolutions 168 host.runQueuedTimeoutCallbacks(); // Actual update 169 170 verifyGetErrRequest({ session, host, files: [recognizersDateTimeSrcFile] }); 171 172 host.ensureFileOrFolder(recongnizerTextDistTypingFile); 173 host.runQueuedTimeoutCallbacks(); // Scheduled invalidation of resolutions 174 host.runQueuedTimeoutCallbacks(); // Actual update 175 176 verifyGetErrRequest({ session, host, files: [recognizersDateTimeSrcFile] }); 177 baselineTsserverLogs("symLinks", `module resolution${withPathMapping ? " with path mapping" : ""} when project recompiles after deleting generated folders`, session); 178 }); 179 }); 180 } 181 182 verifyModuleResolution(/*withPathMapping*/ false); 183 verifyModuleResolution(/*withPathMapping*/ true); 184 }); 185 }); 186} 187