1namespace ts { 2 describe("unittests:: tsc:: declarationEmit::", () => { 3 interface VerifyDeclarationEmitInput { 4 subScenario: string; 5 files: TestFSWithWatch.FileOrFolderOrSymLink[]; 6 rootProject: string; 7 changeCaseFileTestPath: (path: string) => boolean; 8 } 9 10 function changeCaseFile(file: TestFSWithWatch.FileOrFolderOrSymLink, testPath: (path: string) => boolean, replacePath: (path: string) => string): TestFSWithWatch.FileOrFolderOrSymLink { 11 return !TestFSWithWatch.isSymLink(file) || !testPath(file.symLink) ? 12 testPath(file.path) ? { ...file, path: replacePath(file.path) } : file : 13 { path: testPath(file.path) ? replacePath(file.path) : file.path, symLink: replacePath(file.symLink) }; 14 } 15 16 function verifyDeclarationEmit({ subScenario, files, rootProject, changeCaseFileTestPath }: VerifyDeclarationEmitInput) { 17 describe(subScenario, () => { 18 tscWatch.verifyTscWatch({ 19 scenario: "declarationEmit", 20 subScenario, 21 sys: () => tscWatch.createWatchedSystem(files, { currentDirectory: tscWatch.projectRoot }), 22 commandLineArgs: ["-p", rootProject, "--explainFiles"], 23 changes: emptyArray 24 }); 25 }); 26 27 const caseChangeScenario = `${subScenario} moduleCaseChange`; 28 describe(caseChangeScenario, () => { 29 tscWatch.verifyTscWatch({ 30 scenario: "declarationEmit", 31 subScenario: caseChangeScenario, 32 sys: () => tscWatch.createWatchedSystem( 33 files.map(f => changeCaseFile(f, changeCaseFileTestPath, str => str.replace("myproject", "myProject"))), 34 { currentDirectory: tscWatch.projectRoot } 35 ), 36 commandLineArgs: ["-p", rootProject, "--explainFiles"], 37 changes: emptyArray 38 }); 39 }); 40 } 41 42 describe("with symlinks in sibling folders and common package referenced from both folders", () => { 43 function pluginOneConfig() { 44 return JSON.stringify({ 45 compilerOptions: { 46 target: "es5", 47 declaration: true, 48 traceResolution: true 49 }, 50 }); 51 } 52 function pluginOneIndex() { 53 return `import pluginTwo from "plugin-two"; // include this to add reference to symlink`; 54 } 55 function pluginOneAction() { 56 return Utils.dedent` 57 import { actionCreatorFactory } from "typescript-fsa"; // Include version of shared lib 58 const action = actionCreatorFactory("somekey"); 59 const featureOne = action<{ route: string }>("feature-one"); 60 export const actions = { featureOne };`; 61 } 62 function pluginTwoDts() { 63 return Utils.dedent` 64 declare const _default: { 65 features: { 66 featureOne: { 67 actions: { 68 featureOne: { 69 (payload: { 70 name: string; 71 order: number; 72 }, meta?: { 73 [key: string]: any; 74 }): import("typescript-fsa").Action<{ 75 name: string; 76 order: number; 77 }>; 78 }; 79 }; 80 path: string; 81 }; 82 }; 83 }; 84 export default _default;`; 85 } 86 function fsaPackageJson() { 87 return JSON.stringify({ 88 name: "typescript-fsa", 89 version: "3.0.0-beta-2" 90 }); 91 } 92 function fsaIndex() { 93 return Utils.dedent` 94 export interface Action<Payload> { 95 type: string; 96 payload: Payload; 97 } 98 export declare type ActionCreator<Payload> = { 99 type: string; 100 (payload: Payload): Action<Payload>; 101 } 102 export interface ActionCreatorFactory { 103 <Payload = void>(type: string): ActionCreator<Payload>; 104 } 105 export declare function actionCreatorFactory(prefix?: string | null): ActionCreatorFactory; 106 export default actionCreatorFactory;`; 107 } 108 109 verifyDeclarationEmit({ 110 subScenario: "when same version is referenced through source and another symlinked package", 111 rootProject: "plugin-one", 112 files: [ 113 { path: `${tscWatch.projectRoot}/plugin-two/index.d.ts`, content: pluginTwoDts() }, 114 { path: `${tscWatch.projectRoot}/plugin-two/node_modules/typescript-fsa/package.json`, content: fsaPackageJson() }, 115 { path: `${tscWatch.projectRoot}/plugin-two/node_modules/typescript-fsa/index.d.ts`, content: fsaIndex() }, 116 { path: `${tscWatch.projectRoot}/plugin-one/tsconfig.json`, content: pluginOneConfig() }, 117 { path: `${tscWatch.projectRoot}/plugin-one/index.ts`, content: pluginOneIndex() }, 118 { path: `${tscWatch.projectRoot}/plugin-one/action.ts`, content: pluginOneAction() }, 119 { path: `${tscWatch.projectRoot}/plugin-one/node_modules/typescript-fsa/package.json`, content: fsaPackageJson() }, 120 { path: `${tscWatch.projectRoot}/plugin-one/node_modules/typescript-fsa/index.d.ts`, content: fsaIndex() }, 121 { path: `${tscWatch.projectRoot}/plugin-one/node_modules/plugin-two`, symLink: `${tscWatch.projectRoot}/plugin-two` }, 122 tscWatch.libFile 123 ], 124 changeCaseFileTestPath: str => stringContains(str, "/plugin-two"), 125 }); 126 127 verifyDeclarationEmit({ 128 subScenario: "when same version is referenced through source and another symlinked package with indirect link", 129 rootProject: "plugin-one", 130 files: [ 131 { 132 path: `${tscWatch.projectRoot}/plugin-two/package.json`, 133 content: JSON.stringify({ 134 name: "plugin-two", 135 version: "0.1.3", 136 main: "dist/commonjs/index.js" 137 }) 138 }, 139 { path: `${tscWatch.projectRoot}/plugin-two/dist/commonjs/index.d.ts`, content: pluginTwoDts() }, 140 { path: `${tscWatch.projectRoot}/plugin-two/node_modules/typescript-fsa/package.json`, content: fsaPackageJson() }, 141 { path: `${tscWatch.projectRoot}/plugin-two/node_modules/typescript-fsa/index.d.ts`, content: fsaIndex() }, 142 { path: `${tscWatch.projectRoot}/plugin-one/tsconfig.json`, content: pluginOneConfig() }, 143 { 144 path: `${tscWatch.projectRoot}/plugin-one/index.ts`, 145 content: `${pluginOneIndex()} 146${pluginOneAction()}` 147 }, 148 { path: `${tscWatch.projectRoot}/plugin-one/node_modules/typescript-fsa/package.json`, content: fsaPackageJson() }, 149 { path: `${tscWatch.projectRoot}/plugin-one/node_modules/typescript-fsa/index.d.ts`, content: fsaIndex() }, 150 { path: `/temp/yarn/data/link/plugin-two`, symLink: `${tscWatch.projectRoot}/plugin-two` }, 151 { path: `${tscWatch.projectRoot}/plugin-one/node_modules/plugin-two`, symLink: `/temp/yarn/data/link/plugin-two` }, 152 tscWatch.libFile 153 ], 154 changeCaseFileTestPath: str => stringContains(str, "/plugin-two"), 155 }); 156 }); 157 158 verifyDeclarationEmit({ 159 subScenario: "when pkg references sibling package through indirect symlink", 160 rootProject: "pkg3", 161 files: [ 162 { 163 path: `${tscWatch.projectRoot}/pkg1/dist/index.d.ts`, 164 content: Utils.dedent` 165 export * from './types';` 166 }, 167 { 168 path: `${tscWatch.projectRoot}/pkg1/dist/types.d.ts`, 169 content: Utils.dedent` 170 export declare type A = { 171 id: string; 172 }; 173 export declare type B = { 174 id: number; 175 }; 176 export declare type IdType = A | B; 177 export declare class MetadataAccessor<T, D extends IdType = IdType> { 178 readonly key: string; 179 private constructor(); 180 toString(): string; 181 static create<T, D extends IdType = IdType>(key: string): MetadataAccessor<T, D>; 182 }` 183 }, 184 { 185 path: `${tscWatch.projectRoot}/pkg1/package.json`, 186 content: JSON.stringify({ 187 name: "@raymondfeng/pkg1", 188 version: "1.0.0", 189 main: "dist/index.js", 190 typings: "dist/index.d.ts" 191 }) 192 }, 193 { 194 path: `${tscWatch.projectRoot}/pkg2/dist/index.d.ts`, 195 content: Utils.dedent` 196 export * from './types';` 197 }, 198 { 199 path: `${tscWatch.projectRoot}/pkg2/dist/types.d.ts`, 200 content: Utils.dedent` 201 export {MetadataAccessor} from '@raymondfeng/pkg1';` 202 }, 203 { 204 path: `${tscWatch.projectRoot}/pkg2/package.json`, 205 content: JSON.stringify({ 206 name: "@raymondfeng/pkg2", 207 version: "1.0.0", 208 main: "dist/index.js", 209 typings: "dist/index.d.ts" 210 }) 211 }, 212 { 213 path: `${tscWatch.projectRoot}/pkg3/src/index.ts`, 214 content: Utils.dedent` 215 export * from './keys';` 216 }, 217 { 218 path: `${tscWatch.projectRoot}/pkg3/src/keys.ts`, 219 content: Utils.dedent` 220 import {MetadataAccessor} from "@raymondfeng/pkg2"; 221 export const ADMIN = MetadataAccessor.create<boolean>('1');` 222 }, 223 { 224 path: `${tscWatch.projectRoot}/pkg3/tsconfig.json`, 225 content: JSON.stringify({ 226 compilerOptions: { 227 outDir: "dist", 228 rootDir: "src", 229 target: "es5", 230 module: "commonjs", 231 strict: true, 232 esModuleInterop: true, 233 declaration: true 234 } 235 }) 236 }, 237 { 238 path: `${tscWatch.projectRoot}/pkg2/node_modules/@raymondfeng/pkg1`, 239 symLink: `${tscWatch.projectRoot}/pkg1` 240 }, 241 { 242 path: `${tscWatch.projectRoot}/pkg3/node_modules/@raymondfeng/pkg2`, 243 symLink: `${tscWatch.projectRoot}/pkg2` 244 }, 245 tscWatch.libFile 246 ], 247 changeCaseFileTestPath: str => stringContains(str, "/pkg1"), 248 }); 249 }); 250} 251