| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  |  * Copyright Google LLC All Rights Reserved. | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  | import * as chai from 'chai'; | 
					
						
							|  |  |  | import * as ts from 'typescript'; | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  | import {publicApiInternal, SerializationOptions} from '../lib/serializer'; | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | const classesAndInterfaces = `
 | 
					
						
							|  |  |  |   export declare class A { | 
					
						
							|  |  |  |       field: string; | 
					
						
							|  |  |  |       method(a: string): number; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   export interface B { | 
					
						
							|  |  |  |       field: A; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   export declare class C { | 
					
						
							|  |  |  |       someProp: string; | 
					
						
							|  |  |  |       propWithDefault: number; | 
					
						
							|  |  |  |       private privateProp; | 
					
						
							|  |  |  |       protected protectedProp: number; | 
					
						
							|  |  |  |       constructor(someProp: string, propWithDefault: number, privateProp: any, protectedProp: number); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | `;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | describe('unit test', () => { | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |   let _warn: any = null; | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   let warnings: string[] = []; | 
					
						
							|  |  |  |   beforeEach(() => { | 
					
						
							|  |  |  |     _warn = console.warn; | 
					
						
							|  |  |  |     console.warn = (...args: string[]) => warnings.push(args.join(' ')); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   afterEach(() => { | 
					
						
							|  |  |  |     console.warn = _warn; | 
					
						
							|  |  |  |     warnings = []; | 
					
						
							|  |  |  |     _warn = null; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should ignore private methods', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |           fa(): void; | 
					
						
							|  |  |  |           protected fb(): void; | 
					
						
							|  |  |  |           private fc(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |           fa(): void; | 
					
						
							|  |  |  |           protected fb(): void; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'file.d.ts': input}, expected); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-09 15:45:11 +01:00
										 |  |  |   it('should support overloads functions', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       export declare function group(steps: AnimationMetadata[], options?: AnimationOptions | null): AnimationGroupMetadata; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export declare function registerLocaleData(data: any, extraData?: any): void; | 
					
						
							|  |  |  |       export declare function registerLocaleData(data: any, localeId?: string, extraData?: any): void; | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export declare function group(steps: AnimationMetadata[], options?: AnimationOptions | null): AnimationGroupMetadata; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export declare function registerLocaleData(data: any, extraData?: any): void; | 
					
						
							|  |  |  |       export declare function registerLocaleData(data: any, localeId?: string, extraData?: any): void; | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     check({'file.d.ts': input}, expected); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   it('should ignore private props', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |           fa: any; | 
					
						
							|  |  |  |           protected fb: any; | 
					
						
							|  |  |  |           private fc; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |           fa: any; | 
					
						
							|  |  |  |           protected fb: any; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'file.d.ts': input}, expected); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should support imports without capturing imports', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       import {A} from './classes_and_interfaces'; | 
					
						
							|  |  |  |       export declare class C { | 
					
						
							|  |  |  |           field: A; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export declare class C { | 
					
						
							|  |  |  |           field: A; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'classes_and_interfaces.d.ts': classesAndInterfaces, 'file.d.ts': input}, expected); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should throw on aliased reexports', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       export { A as Apple } from './classes_and_interfaces'; | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     checkThrows( | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |         {'classes_and_interfaces.d.ts': classesAndInterfaces, 'file.d.ts': input}, | 
					
						
							|  |  |  |         'Symbol "A" was aliased as "Apple". Aliases are not supported.'); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should remove reexported external symbols', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       export { Foo } from 'some-external-module-that-cannot-be-resolved'; | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'classes_and_interfaces.d.ts': classesAndInterfaces, 'file.d.ts': input}, expected); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |     chai.assert.deepEqual( | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |         warnings, ['file.d.ts(1,1): error: No export declaration found for symbol "Foo"']); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should sort exports', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       export declare type E = string; | 
					
						
							|  |  |  |       export interface D { | 
					
						
							|  |  |  |           e: number; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       export declare var e: C; | 
					
						
							|  |  |  |       export declare class C { | 
					
						
							|  |  |  |           e: number; | 
					
						
							|  |  |  |           d: string; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       export declare function b(): boolean; | 
					
						
							|  |  |  |       export declare const a: string; | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export declare const a: string; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export declare function b(): boolean; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export declare class C { | 
					
						
							|  |  |  |           d: string; | 
					
						
							|  |  |  |           e: number; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export interface D { | 
					
						
							|  |  |  |           e: number; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export declare var e: C; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export declare type E = string; | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'file.d.ts': input}, expected); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should sort class members', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       export class A { | 
					
						
							|  |  |  |         f: number; | 
					
						
							|  |  |  |         static foo(): void; | 
					
						
							|  |  |  |         c: string; | 
					
						
							|  |  |  |         static a: boolean; | 
					
						
							|  |  |  |         constructor(); | 
					
						
							|  |  |  |         static bar(): void; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export class A { | 
					
						
							|  |  |  |         c: string; | 
					
						
							|  |  |  |         f: number; | 
					
						
							|  |  |  |         constructor(); | 
					
						
							|  |  |  |         static a: boolean; | 
					
						
							|  |  |  |         static bar(): void; | 
					
						
							|  |  |  |         static foo(): void; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'file.d.ts': input}, expected); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should sort interface members', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       export interface A { | 
					
						
							|  |  |  |         (): void; | 
					
						
							|  |  |  |         [key: string]: any; | 
					
						
							|  |  |  |         c(): void; | 
					
						
							|  |  |  |         a: number; | 
					
						
							|  |  |  |         new (): Object; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export interface A { | 
					
						
							|  |  |  |         a: number; | 
					
						
							|  |  |  |         (): void; | 
					
						
							|  |  |  |         new (): Object; | 
					
						
							|  |  |  |         [key: string]: any; | 
					
						
							|  |  |  |         c(): void; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'file.d.ts': input}, expected); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should sort class members including readonly', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |         export declare class DebugNode { | 
					
						
							|  |  |  |           private _debugContext; | 
					
						
							|  |  |  |           nativeNode: any; | 
					
						
							|  |  |  |           listeners: any[]; | 
					
						
							|  |  |  |           parent: any | null; | 
					
						
							|  |  |  |           constructor(nativeNode: any, parent: DebugNode | null, _debugContext: any); | 
					
						
							|  |  |  |           readonly injector: any; | 
					
						
							|  |  |  |           readonly componentInstance: any; | 
					
						
							|  |  |  |           readonly context: any; | 
					
						
							|  |  |  |           readonly references: { | 
					
						
							|  |  |  |               [key: string]: any; | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |           readonly providerTokens: any[]; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |         export declare class DebugNode { | 
					
						
							|  |  |  |           readonly componentInstance: any; | 
					
						
							|  |  |  |           readonly context: any; | 
					
						
							|  |  |  |           readonly injector: any; | 
					
						
							|  |  |  |           listeners: any[]; | 
					
						
							|  |  |  |           nativeNode: any; | 
					
						
							|  |  |  |           parent: any | null; | 
					
						
							|  |  |  |           readonly providerTokens: any[]; | 
					
						
							|  |  |  |           readonly references: { | 
					
						
							|  |  |  |               [key: string]: any; | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |           constructor(nativeNode: any, parent: DebugNode | null, _debugContext: any); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'file.d.ts': input}, expected); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should sort two call signatures', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       export interface A { | 
					
						
							|  |  |  |         (b: number): void; | 
					
						
							|  |  |  |         (a: number): void; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export interface A { | 
					
						
							|  |  |  |         (a: number): void; | 
					
						
							|  |  |  |         (b: number): void; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'file.d.ts': input}, expected); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should sort exports including re-exports', () => { | 
					
						
							|  |  |  |     const submodule = `
 | 
					
						
							|  |  |  |       export declare var e: C; | 
					
						
							|  |  |  |       export declare class C { | 
					
						
							|  |  |  |           e: number; | 
					
						
							|  |  |  |           d: string; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       export * from './submodule'; | 
					
						
							|  |  |  |       export declare type E = string; | 
					
						
							|  |  |  |       export interface D { | 
					
						
							|  |  |  |           e: number; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       export declare function b(): boolean; | 
					
						
							|  |  |  |       export declare const a: string; | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export declare const a: string; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export declare function b(): boolean; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export declare class C { | 
					
						
							|  |  |  |           d: string; | 
					
						
							|  |  |  |           e: number; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export interface D { | 
					
						
							|  |  |  |           e: number; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export declare var e: C; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export declare type E = string; | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'submodule.d.ts': submodule, 'file.d.ts': input}, expected); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should remove module comments', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       /** | 
					
						
							|  |  |  |        * An amazing module. | 
					
						
							|  |  |  |        * @module | 
					
						
							|  |  |  |        */ | 
					
						
							|  |  |  |       /** | 
					
						
							|  |  |  |        * Foo function. | 
					
						
							|  |  |  |        */ | 
					
						
							|  |  |  |       export declare function foo(): boolean; | 
					
						
							|  |  |  |       export declare const bar: number; | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export declare const bar: number; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export declare function foo(): boolean; | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'file.d.ts': input}, expected); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should remove class and field comments', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       /** | 
					
						
							|  |  |  |        * Does something really cool. | 
					
						
							|  |  |  |        */ | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |           /** | 
					
						
							|  |  |  |            * A very interesting getter. | 
					
						
							|  |  |  |            */ | 
					
						
							|  |  |  |           b: string; | 
					
						
							|  |  |  |           /** | 
					
						
							|  |  |  |            * A very useful field. | 
					
						
							|  |  |  |            */ | 
					
						
							|  |  |  |           name: string; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |           b: string; | 
					
						
							|  |  |  |           name: string; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'file.d.ts': input}, expected); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should skip symbols matching specified pattern', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       export const __a__: string; | 
					
						
							|  |  |  |       export class B { | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export class B { | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'file.d.ts': input}, expected, {stripExportPattern: /^__.*/}); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-29 11:20:17 -07:00
										 |  |  |   it('should throw on using module imports in expression position that were not explicitly allowed', | 
					
						
							|  |  |  |      () => { | 
					
						
							|  |  |  |        const input = `
 | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |       import * as foo from './foo'; | 
					
						
							|  |  |  |       export declare class A extends foo.A { | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2019-03-29 11:20:17 -07:00
										 |  |  |        checkThrows( | 
					
						
							|  |  |  |            {'file.d.ts': input}, | 
					
						
							|  |  |  |            'file.d.ts(2,32): error: Module identifier "foo" is not allowed. ' + | 
					
						
							|  |  |  |                'Remove it from source or allow it via --allowModuleIdentifiers.'); | 
					
						
							|  |  |  |      }); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-29 11:20:17 -07:00
										 |  |  |   it('should throw on using module imports in type position that were not explicitly allowed', | 
					
						
							|  |  |  |      () => { | 
					
						
							|  |  |  |        const input = `
 | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |       import * as foo from './foo'; | 
					
						
							|  |  |  |       export type A = foo.A; | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2019-03-29 11:20:17 -07:00
										 |  |  |        checkThrows( | 
					
						
							|  |  |  |            {'file.d.ts': input}, | 
					
						
							|  |  |  |            'file.d.ts(2,17): error: Module identifier "foo" is not allowed. ' + | 
					
						
							|  |  |  |                'Remove it from source or allow it via --allowModuleIdentifiers.'); | 
					
						
							|  |  |  |      }); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-29 11:20:17 -07:00
										 |  |  |   it('should not throw on using explicitly allowed module imports', () => { | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |     const input = `
 | 
					
						
							|  |  |  |       import * as foo from './foo'; | 
					
						
							|  |  |  |       export declare class A extends foo.A { | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export declare class A extends foo.A { | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'file.d.ts': input}, expected, {allowModuleIdentifiers: ['foo']}); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-29 11:20:17 -07:00
										 |  |  |   it('should not throw if module imports, that were not explicitly allowed, are not used', () => { | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |     const input = `
 | 
					
						
							|  |  |  |       import * as foo from './foo'; | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     check({'file.d.ts': input}, expected); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-19 10:44:33 +01:00
										 |  |  |   it('should copy specified jsdoc tags of exports in docstrings', () => { | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |     const input = `
 | 
					
						
							|  |  |  |       /** | 
					
						
							|  |  |  |        * @deprecated This is useless now | 
					
						
							|  |  |  |        */ | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       /** | 
					
						
							|  |  |  |        * @experimental | 
					
						
							|  |  |  |        */ | 
					
						
							|  |  |  |       export declare const b: string; | 
					
						
							|  |  |  |       /** | 
					
						
							|  |  |  |        * @stable | 
					
						
							|  |  |  |        */ | 
					
						
							|  |  |  |       export declare var c: number; | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       /** @deprecated */ | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       /** @experimental */ | 
					
						
							|  |  |  |       export declare const b: string; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       export declare var c: number; | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-10-19 10:44:33 +01:00
										 |  |  |     check({'file.d.ts': input}, expected, {exportTags: {toCopy: ['deprecated', 'experimental']}}); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-19 10:44:33 +01:00
										 |  |  |   it('should copy specified jsdoc tags of fields in docstrings', () => { | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |     const input = `
 | 
					
						
							| 
									
										
										
										
											2018-10-19 10:44:33 +01:00
										 |  |  |       /** @otherTag */ | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |       export declare class A { | 
					
						
							|  |  |  |         /** | 
					
						
							|  |  |  |          * @stable | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         value: number; | 
					
						
							|  |  |  |         /** | 
					
						
							|  |  |  |          * @experimental | 
					
						
							| 
									
										
										
										
											2018-10-19 10:44:33 +01:00
										 |  |  |          * @otherTag | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |          */ | 
					
						
							|  |  |  |         constructor(); | 
					
						
							|  |  |  |         /** | 
					
						
							|  |  |  |          * @deprecated | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         foo(): void; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							| 
									
										
										
										
											2018-10-19 10:44:33 +01:00
										 |  |  |         value: number; | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |         /** @experimental */ constructor(); | 
					
						
							|  |  |  |         /** @deprecated */ foo(): void; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							| 
									
										
										
										
											2018-10-19 10:44:33 +01:00
										 |  |  |     check({'file.d.ts': input}, expected, {memberTags: {toCopy: ['deprecated', 'experimental']}}); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should copy specified jsdoc tags of parameters in docstrings', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |         foo(str: string, /** @deprecated */ value: number): void; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |         foo(str: string, /** @deprecated */ value: number): void; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     check({'file.d.ts': input}, expected, {paramTags: {toCopy: ['deprecated', 'experimental']}}); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should throw on using banned jsdoc tags on exports', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       /** | 
					
						
							|  |  |  |        * @stable | 
					
						
							|  |  |  |        */ | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |         value: number; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     checkThrows( | 
					
						
							|  |  |  |         {'file.d.ts': input}, | 
					
						
							|  |  |  |         'file.d.ts(4,1): error: Banned jsdoc tags - "@stable" - were found on `A`.', | 
					
						
							|  |  |  |         {exportTags: {banned: ['stable']}}); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should throw on using banned jsdoc tags on fields', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |         /** | 
					
						
							|  |  |  |          * @stable | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  |         value: number; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     checkThrows( | 
					
						
							|  |  |  |         {'file.d.ts': input}, | 
					
						
							|  |  |  |         'file.d.ts(5,3): error: Banned jsdoc tags - "@stable" - were found on `value`.', | 
					
						
							|  |  |  |         {memberTags: {banned: ['stable']}}); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should throw on using banned jsdoc tags on parameters', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |         foo(/** @stable */ param: number): void; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     checkThrows( | 
					
						
							|  |  |  |         {'file.d.ts': input}, | 
					
						
							|  |  |  |         'file.d.ts(2,22): error: Banned jsdoc tags - "@stable" - were found on `param`.', | 
					
						
							|  |  |  |         {paramTags: {banned: ['stable']}}); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should throw on missing required jsdoc tags on exports', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       /** @experimental */ | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |         value: number; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     checkThrows( | 
					
						
							|  |  |  |         {'file.d.ts': input}, | 
					
						
							| 
									
										
										
										
											2019-04-10 13:45:26 -07:00
										 |  |  |         'file.d.ts(2,1): error: Required jsdoc tags - One of the tags: "@stable" - must exist on `A`.', | 
					
						
							|  |  |  |         {exportTags: {requireAtLeastOne: ['stable']}}); | 
					
						
							| 
									
										
										
										
											2018-10-19 10:44:33 +01:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should throw on missing required jsdoc tags on fields', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       /** @experimental */ | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |         value: number; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     checkThrows( | 
					
						
							|  |  |  |         {'file.d.ts': input}, | 
					
						
							| 
									
										
										
										
											2019-04-10 13:45:26 -07:00
										 |  |  |         'file.d.ts(3,3): error: Required jsdoc tags - One of the tags: "@stable" - must exist on `value`.', | 
					
						
							|  |  |  |         {memberTags: {requireAtLeastOne: ['stable']}}); | 
					
						
							| 
									
										
										
										
											2018-10-19 10:44:33 +01:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should throw on missing required jsdoc tags on parameters', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       /** @experimental */ | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |         foo(param: number): void; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     checkThrows( | 
					
						
							|  |  |  |         {'file.d.ts': input}, | 
					
						
							| 
									
										
										
										
											2019-04-10 13:45:26 -07:00
										 |  |  |         'file.d.ts(3,7): error: Required jsdoc tags - One of the tags: "@stable" - must exist on `param`.', | 
					
						
							|  |  |  |         {paramTags: {requireAtLeastOne: ['stable']}}); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-11 11:01:01 -07:00
										 |  |  |   it('should require at least one of the requireAtLeastOne tags', () => { | 
					
						
							| 
									
										
										
										
											2019-04-10 13:45:26 -07:00
										 |  |  |     const input = `
 | 
					
						
							|  |  |  |       /** @experimental */ | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |         foo(param: number): void; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     checkThrows( | 
					
						
							|  |  |  |         {'file.d.ts': input}, | 
					
						
							|  |  |  |         'file.d.ts(3,7): error: Required jsdoc tags - One of the tags: "@stable", "@foo", "@bar" - must exist on `param`.', | 
					
						
							|  |  |  |         {paramTags: {requireAtLeastOne: ['stable', 'foo', 'bar']}}); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2019-04-11 11:01:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   it('should allow with one of the requireAtLeastOne tags found', () => { | 
					
						
							|  |  |  |     const input = `
 | 
					
						
							|  |  |  |       /** | 
					
						
							|  |  |  |        * @foo | 
					
						
							|  |  |  |        * @bar | 
					
						
							|  |  |  |        * @stable | 
					
						
							|  |  |  |        */ | 
					
						
							|  |  |  |       export declare class A { | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       /** | 
					
						
							|  |  |  |        * @foo | 
					
						
							|  |  |  |        */ | 
					
						
							|  |  |  |       export declare const b: string; | 
					
						
							|  |  |  |       /** | 
					
						
							|  |  |  |        * @bar | 
					
						
							|  |  |  |        */ | 
					
						
							|  |  |  |       export declare var c: number; | 
					
						
							|  |  |  |       /** | 
					
						
							|  |  |  |        * @stable | 
					
						
							|  |  |  |        */ | 
					
						
							|  |  |  |       export declare function d(): void; | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     const expected = `
 | 
					
						
							|  |  |  |     export declare class A { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     export declare const b: string; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     export declare var c: number; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     export declare function d(): void; | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |     check( | 
					
						
							|  |  |  |         {'file.d.ts': input}, expected, | 
					
						
							|  |  |  |         {exportTags: {requireAtLeastOne: ['stable', 'foo', 'bar']}}); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  | function getMockHost(files: {[name: string]: string}): ts.CompilerHost { | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   return { | 
					
						
							|  |  |  |     getSourceFile: (sourceName, languageVersion) => { | 
					
						
							|  |  |  |       if (!files[sourceName]) return undefined; | 
					
						
							|  |  |  |       return ts.createSourceFile( | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |           sourceName, stripExtraIndentation(files[sourceName]), languageVersion, true); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |     }, | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     writeFile: (name, text, writeByteOrderMark) => {}, | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |     fileExists: (filename) => !!files[filename], | 
					
						
							|  |  |  |     readFile: (filename) => stripExtraIndentation(files[filename]), | 
					
						
							|  |  |  |     getDefaultLibFileName: () => 'lib.ts', | 
					
						
							|  |  |  |     useCaseSensitiveFileNames: () => true, | 
					
						
							|  |  |  |     getCanonicalFileName: (filename) => filename, | 
					
						
							|  |  |  |     getCurrentDirectory: () => './', | 
					
						
							|  |  |  |     getNewLine: () => '\n', | 
					
						
							|  |  |  |     getDirectories: () => [] | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function check( | 
					
						
							| 
									
										
										
										
											2018-03-01 10:41:35 -08:00
										 |  |  |     files: {[name: string]: string}, expected: string, options: SerializationOptions = {}) { | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |   const actual = publicApiInternal(getMockHost(files), 'file.d.ts', {}, options); | 
					
						
							|  |  |  |   chai.assert.equal(actual.trim(), stripExtraIndentation(expected).trim()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-19 10:44:33 +01:00
										 |  |  | function checkThrows( | 
					
						
							|  |  |  |     files: {[name: string]: string}, error: string, options: SerializationOptions = {}) { | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |   chai.assert.throws(() => { | 
					
						
							|  |  |  |     publicApiInternal(getMockHost(files), 'file.d.ts', {}, options); | 
					
						
							|  |  |  |   }, error); | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function stripExtraIndentation(text: string) { | 
					
						
							|  |  |  |   let lines = text.split('\n'); | 
					
						
							|  |  |  |   // Ignore first and last new line
 | 
					
						
							|  |  |  |   lines = lines.slice(1, lines.length - 1); | 
					
						
							|  |  |  |   const commonIndent = lines.reduce((min, line) => { | 
					
						
							| 
									
										
										
										
											2020-04-13 16:40:21 -07:00
										 |  |  |     const indent = /^( *)/.exec(line)![1].length; | 
					
						
							| 
									
										
										
										
											2018-03-02 14:19:01 -08:00
										 |  |  |     // Ignore empty line
 | 
					
						
							|  |  |  |     return line.length ? Math.min(min, indent) : min; | 
					
						
							|  |  |  |   }, text.length); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return lines.map(line => line.substr(commonIndent)).join('\n') + '\n'; | 
					
						
							|  |  |  | } |