1namespace ts.tscWatch { 2 import getFileFromProject = TestFSWithWatch.getTsBuildProjectFile; 3 describe("unittests:: tsc-watch:: watchAPI:: with sourceOfProjectReferenceRedirect", () => { 4 interface VerifyWatchInput { 5 files: readonly TestFSWithWatch.FileOrFolderOrSymLink[]; 6 config: string; 7 subScenario: string; 8 } 9 10 function verifyWatch({ files, config, subScenario }: VerifyWatchInput, alreadyBuilt: boolean) { 11 const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline( 12 createWatchedSystem(files), 13 alreadyBuilt ? (sys, originalRead) => { 14 solutionBuildWithBaseline(sys, [config], originalRead); 15 sys.clearOutput(); 16 } : undefined 17 ); 18 const host = createWatchCompilerHostOfConfigFileForBaseline({ 19 configFileName: config, 20 system: sys, 21 cb, 22 }); 23 host.useSourceOfProjectReferenceRedirect = returnTrue; 24 const watch = createWatchProgram(host); 25 runWatchBaseline({ 26 scenario: "sourceOfProjectReferenceRedirect", 27 subScenario: `${subScenario}${alreadyBuilt ? " when solution is already built" : ""}`, 28 commandLineArgs: ["--w", "--p", config], 29 sys, 30 baseline, 31 oldSnap, 32 getPrograms, 33 changes: emptyArray, 34 watchOrSolution: watch 35 }); 36 } 37 38 function verifyScenario(input: () => VerifyWatchInput) { 39 it("when solution is not built", () => { 40 verifyWatch(input(), /*alreadyBuilt*/ false); 41 }); 42 43 it("when solution is already built", () => { 44 verifyWatch(input(), /*alreadyBuilt*/ true); 45 }); 46 } 47 48 describe("with simple project", () => { 49 verifyScenario(() => { 50 const baseConfig = getFileFromProject("demo", "tsconfig-base.json"); 51 const coreTs = getFileFromProject("demo", "core/utilities.ts"); 52 const coreConfig = getFileFromProject("demo", "core/tsconfig.json"); 53 const animalTs = getFileFromProject("demo", "animals/animal.ts"); 54 const dogTs = getFileFromProject("demo", "animals/dog.ts"); 55 const indexTs = getFileFromProject("demo", "animals/index.ts"); 56 const animalsConfig = getFileFromProject("demo", "animals/tsconfig.json"); 57 return { 58 files: [{ path: libFile.path, content: libContent }, baseConfig, coreTs, coreConfig, animalTs, dogTs, indexTs, animalsConfig], 59 config: animalsConfig.path, 60 subScenario: "with simple project" 61 }; 62 }); 63 }); 64 65 describe("when references are monorepo like with symlinks", () => { 66 interface Packages { 67 bPackageJson: File; 68 aTest: File; 69 bFoo: File; 70 bBar: File; 71 bSymlink: SymLink; 72 subScenario: string; 73 } 74 function verifySymlinkScenario(packages: () => Packages) { 75 describe("when preserveSymlinks is turned off", () => { 76 verifySymlinkScenarioWorker(packages, {}); 77 }); 78 describe("when preserveSymlinks is turned on", () => { 79 verifySymlinkScenarioWorker(packages, { preserveSymlinks: true }); 80 }); 81 } 82 83 function verifySymlinkScenarioWorker(packages: () => Packages, extraOptions: CompilerOptions) { 84 verifyScenario(() => { 85 const { bPackageJson, aTest, bFoo, bBar, bSymlink, subScenario } = packages(); 86 const aConfig = config("A", extraOptions, ["../B"]); 87 const bConfig = config("B", extraOptions); 88 return { 89 files: [libFile, bPackageJson, aConfig, bConfig, aTest, bFoo, bBar, bSymlink], 90 config: aConfig.path, 91 subScenario: `${subScenario}${extraOptions.preserveSymlinks ? " with preserveSymlinks" : ""}` 92 }; 93 }); 94 } 95 96 function config(packageName: string, extraOptions: CompilerOptions, references?: string[]): File { 97 return { 98 path: `${projectRoot}/packages/${packageName}/tsconfig.json`, 99 content: JSON.stringify({ 100 compilerOptions: { 101 outDir: "lib", 102 rootDir: "src", 103 composite: true, 104 ...extraOptions 105 }, 106 include: ["src"], 107 ...(references ? { references: references.map(path => ({ path })) } : {}) 108 }) 109 }; 110 } 111 112 function file(packageName: string, fileName: string, content: string): File { 113 return { 114 path: `${projectRoot}/packages/${packageName}/src/${fileName}`, 115 content 116 }; 117 } 118 119 function verifyMonoRepoLike(scope = "") { 120 describe("when packageJson has types field", () => { 121 verifySymlinkScenario(() => ({ 122 bPackageJson: { 123 path: `${projectRoot}/packages/B/package.json`, 124 content: JSON.stringify({ 125 main: "lib/index.js", 126 types: "lib/index.d.ts" 127 }) 128 }, 129 aTest: file("A", "index.ts", `import { foo } from '${scope}b'; 130import { bar } from '${scope}b/lib/bar'; 131foo(); 132bar(); 133`), 134 bFoo: file("B", "index.ts", `export function foo() { }`), 135 bBar: file("B", "bar.ts", `export function bar() { }`), 136 bSymlink: { 137 path: `${projectRoot}/node_modules/${scope}b`, 138 symLink: `${projectRoot}/packages/B` 139 }, 140 subScenario: `when packageJson has types field${scope ? " with scoped package" : ""}` 141 })); 142 }); 143 144 describe("when referencing file from subFolder", () => { 145 verifySymlinkScenario(() => ({ 146 bPackageJson: { 147 path: `${projectRoot}/packages/B/package.json`, 148 content: "{}" 149 }, 150 aTest: file("A", "test.ts", `import { foo } from '${scope}b/lib/foo'; 151import { bar } from '${scope}b/lib/bar/foo'; 152foo(); 153bar(); 154`), 155 bFoo: file("B", "foo.ts", `export function foo() { }`), 156 bBar: file("B", "bar/foo.ts", `export function bar() { }`), 157 bSymlink: { 158 path: `${projectRoot}/node_modules/${scope}b`, 159 symLink: `${projectRoot}/packages/B` 160 }, 161 subScenario: `when referencing file from subFolder${scope ? " with scoped package" : ""}` 162 })); 163 }); 164 } 165 describe("when package is not scoped", () => { 166 verifyMonoRepoLike(); 167 }); 168 describe("when package is scoped", () => { 169 verifyMonoRepoLike("@issue/"); 170 }); 171 }); 172 }); 173} 174