• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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