| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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
 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-03-20 13:47:58 +00:00
										 |  |  | import {AbsoluteFsPath} from '../../../src/ngtsc/path'; | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:57 +01:00
										 |  |  | import {DependencyResolver, SortedEntryPointsInfo} from '../../src/dependencies/dependency_resolver'; | 
					
						
							|  |  |  | import {EsmDependencyHost} from '../../src/dependencies/esm_dependency_host'; | 
					
						
							|  |  |  | import {ModuleResolver} from '../../src/dependencies/module_resolver'; | 
					
						
							| 
									
										
										
										
											2019-04-28 20:48:35 +01:00
										 |  |  | import {FileSystem} from '../../src/file_system/file_system'; | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  | import {EntryPoint} from '../../src/packages/entry_point'; | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:57 +01:00
										 |  |  | import {MockFileSystem} from '../helpers/mock_file_system'; | 
					
						
							| 
									
										
										
										
											2019-03-29 10:13:14 +00:00
										 |  |  | import {MockLogger} from '../helpers/mock_logger'; | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 13:47:58 +00:00
										 |  |  | const _ = AbsoluteFsPath.from; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-15 17:08:44 +01:00
										 |  |  | describe('DependencyResolver', () => { | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:57 +01:00
										 |  |  |   let host: EsmDependencyHost; | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |   let resolver: DependencyResolver; | 
					
						
							| 
									
										
										
										
											2019-04-28 20:48:35 +01:00
										 |  |  |   let fs: FileSystem; | 
					
						
							|  |  |  |   let moduleResolver: ModuleResolver; | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |   beforeEach(() => { | 
					
						
							| 
									
										
										
										
											2019-04-28 20:48:35 +01:00
										 |  |  |     fs = new MockFileSystem(); | 
					
						
							|  |  |  |     moduleResolver = new ModuleResolver(fs); | 
					
						
							|  |  |  |     host = new EsmDependencyHost(fs, moduleResolver); | 
					
						
							| 
									
										
										
										
											2019-04-29 18:51:52 +01:00
										 |  |  |     resolver = new DependencyResolver(fs, new MockLogger(), {esm5: host, esm2015: host}); | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |   }); | 
					
						
							|  |  |  |   describe('sortEntryPointsByDependency()', () => { | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:56 +01:00
										 |  |  |     const first = { | 
					
						
							|  |  |  |       path: _('/first'), | 
					
						
							|  |  |  |       packageJson: {esm5: './index.js'}, | 
					
						
							|  |  |  |       compiledByAngular: true | 
					
						
							|  |  |  |     } as EntryPoint; | 
					
						
							|  |  |  |     const second = { | 
					
						
							|  |  |  |       path: _('/second'), | 
					
						
							|  |  |  |       packageJson: {esm2015: './sub/index.js'}, | 
					
						
							|  |  |  |       compiledByAngular: true | 
					
						
							|  |  |  |     } as EntryPoint; | 
					
						
							|  |  |  |     const third = { | 
					
						
							|  |  |  |       path: _('/third'), | 
					
						
							|  |  |  |       packageJson: {fesm5: './index.js'}, | 
					
						
							|  |  |  |       compiledByAngular: true | 
					
						
							|  |  |  |     } as EntryPoint; | 
					
						
							|  |  |  |     const fourth = { | 
					
						
							|  |  |  |       path: _('/fourth'), | 
					
						
							|  |  |  |       packageJson: {fesm2015: './sub2/index.js'}, | 
					
						
							|  |  |  |       compiledByAngular: true | 
					
						
							|  |  |  |     } as EntryPoint; | 
					
						
							|  |  |  |     const fifth = { | 
					
						
							|  |  |  |       path: _('/fifth'), | 
					
						
							|  |  |  |       packageJson: {module: './index.js'}, | 
					
						
							|  |  |  |       compiledByAngular: true | 
					
						
							|  |  |  |     } as EntryPoint; | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const dependencies = { | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:56 +01:00
										 |  |  |       [_('/first/index.js')]: {resolved: [second.path, third.path, '/ignored-1'], missing: []}, | 
					
						
							|  |  |  |       [_('/second/sub/index.js')]: {resolved: [third.path, fifth.path], missing: []}, | 
					
						
							|  |  |  |       [_('/third/index.js')]: {resolved: [fourth.path, '/ignored-2'], missing: []}, | 
					
						
							|  |  |  |       [_('/fourth/sub2/index.js')]: {resolved: [fifth.path], missing: []}, | 
					
						
							|  |  |  |       [_('/fifth/index.js')]: {resolved: [], missing: []}, | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should order the entry points by their dependency on each other', () => { | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:57 +01:00
										 |  |  |       spyOn(host, 'findDependencies').and.callFake(createFakeComputeDependencies(dependencies)); | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |       const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); | 
					
						
							|  |  |  |       expect(result.entryPoints).toEqual([fifth, fourth, third, second, first]); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should remove entry-points that have missing direct dependencies', () => { | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:57 +01:00
										 |  |  |       spyOn(host, 'findDependencies').and.callFake(createFakeComputeDependencies({ | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:56 +01:00
										 |  |  |         [_('/first/index.js')]: {resolved: [], missing: ['/missing']}, | 
					
						
							|  |  |  |         [_('/second/sub/index.js')]: {resolved: [], missing: []}, | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |       })); | 
					
						
							|  |  |  |       const result = resolver.sortEntryPointsByDependency([first, second]); | 
					
						
							|  |  |  |       expect(result.entryPoints).toEqual([second]); | 
					
						
							|  |  |  |       expect(result.invalidEntryPoints).toEqual([ | 
					
						
							| 
									
										
										
										
											2019-03-20 13:47:58 +00:00
										 |  |  |         {entryPoint: first, missingDependencies: ['/missing']}, | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |       ]); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should remove entry points that depended upon an invalid entry-point', () => { | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:57 +01:00
										 |  |  |       spyOn(host, 'findDependencies').and.callFake(createFakeComputeDependencies({ | 
					
						
							| 
									
										
										
										
											2019-05-05 14:39:50 +01:00
										 |  |  |         [_('/first/index.js')]: {resolved: [second.path, third.path], missing: []}, | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:56 +01:00
										 |  |  |         [_('/second/sub/index.js')]: {resolved: [], missing: ['/missing']}, | 
					
						
							|  |  |  |         [_('/third/index.js')]: {resolved: [], missing: []}, | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |       })); | 
					
						
							|  |  |  |       // Note that we will process `first` before `second`, which has the missing dependency.
 | 
					
						
							|  |  |  |       const result = resolver.sortEntryPointsByDependency([first, second, third]); | 
					
						
							|  |  |  |       expect(result.entryPoints).toEqual([third]); | 
					
						
							|  |  |  |       expect(result.invalidEntryPoints).toEqual([ | 
					
						
							| 
									
										
										
										
											2019-03-20 13:47:58 +00:00
										 |  |  |         {entryPoint: second, missingDependencies: ['/missing']}, | 
					
						
							|  |  |  |         {entryPoint: first, missingDependencies: ['/missing']}, | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |       ]); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should remove entry points that will depend upon an invalid entry-point', () => { | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:57 +01:00
										 |  |  |       spyOn(host, 'findDependencies').and.callFake(createFakeComputeDependencies({ | 
					
						
							| 
									
										
										
										
											2019-05-05 14:39:50 +01:00
										 |  |  |         [_('/first/index.js')]: {resolved: [second.path, third.path], missing: []}, | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:56 +01:00
										 |  |  |         [_('/second/sub/index.js')]: {resolved: [], missing: ['/missing']}, | 
					
						
							|  |  |  |         [_('/third/index.js')]: {resolved: [], missing: []}, | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |       })); | 
					
						
							|  |  |  |       // Note that we will process `first` after `second`, which has the missing dependency.
 | 
					
						
							|  |  |  |       const result = resolver.sortEntryPointsByDependency([second, first, third]); | 
					
						
							|  |  |  |       expect(result.entryPoints).toEqual([third]); | 
					
						
							|  |  |  |       expect(result.invalidEntryPoints).toEqual([ | 
					
						
							| 
									
										
										
										
											2019-03-20 13:47:58 +00:00
										 |  |  |         {entryPoint: second, missingDependencies: ['/missing']}, | 
					
						
							|  |  |  |         {entryPoint: first, missingDependencies: [second.path]}, | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |       ]); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-28 20:48:35 +01:00
										 |  |  |     it('should error if the entry point does not have a suitable format', () => { | 
					
						
							| 
									
										
										
										
											2019-03-20 13:47:58 +00:00
										 |  |  |       expect(() => resolver.sortEntryPointsByDependency([ | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:56 +01:00
										 |  |  |         { path: '/first', packageJson: {}, compiledByAngular: true } as EntryPoint | 
					
						
							| 
									
										
										
										
											2019-04-28 20:48:35 +01:00
										 |  |  |       ])).toThrowError(`There is no appropriate source code format in '/first' entry-point.`); | 
					
						
							| 
									
										
										
										
											2019-03-20 13:47:58 +00:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-28 20:48:35 +01:00
										 |  |  |     it('should error if there is no appropriate DependencyHost for the given formats', () => { | 
					
						
							| 
									
										
										
										
											2019-04-29 18:51:52 +01:00
										 |  |  |       resolver = new DependencyResolver(fs, new MockLogger(), {esm2015: host}); | 
					
						
							| 
									
										
										
										
											2019-04-28 20:48:35 +01:00
										 |  |  |       expect(() => resolver.sortEntryPointsByDependency([first])) | 
					
						
							|  |  |  |           .toThrowError( | 
					
						
							| 
									
										
										
										
											2019-05-16 20:30:03 +01:00
										 |  |  |               `Could not find a suitable format for computing dependencies of entry-point: '${first.path}'.`); | 
					
						
							| 
									
										
										
										
											2019-04-28 20:48:35 +01:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |     it('should capture any dependencies that were ignored', () => { | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:57 +01:00
										 |  |  |       spyOn(host, 'findDependencies').and.callFake(createFakeComputeDependencies(dependencies)); | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |       const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); | 
					
						
							|  |  |  |       expect(result.ignoredDependencies).toEqual([ | 
					
						
							| 
									
										
										
										
											2019-03-20 13:47:58 +00:00
										 |  |  |         {entryPoint: first, dependencyPath: '/ignored-1'}, | 
					
						
							|  |  |  |         {entryPoint: third, dependencyPath: '/ignored-2'}, | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |       ]); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 13:47:58 +00:00
										 |  |  |     it('should only return dependencies of the target, if provided', () => { | 
					
						
							| 
									
										
										
										
											2019-04-28 20:47:57 +01:00
										 |  |  |       spyOn(host, 'findDependencies').and.callFake(createFakeComputeDependencies(dependencies)); | 
					
						
							| 
									
										
										
										
											2019-03-20 13:47:58 +00:00
										 |  |  |       const entryPoints = [fifth, first, fourth, second, third]; | 
					
						
							|  |  |  |       let sorted: SortedEntryPointsInfo; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       sorted = resolver.sortEntryPointsByDependency(entryPoints, first); | 
					
						
							|  |  |  |       expect(sorted.entryPoints).toEqual([fifth, fourth, third, second, first]); | 
					
						
							|  |  |  |       sorted = resolver.sortEntryPointsByDependency(entryPoints, second); | 
					
						
							|  |  |  |       expect(sorted.entryPoints).toEqual([fifth, fourth, third, second]); | 
					
						
							|  |  |  |       sorted = resolver.sortEntryPointsByDependency(entryPoints, third); | 
					
						
							|  |  |  |       expect(sorted.entryPoints).toEqual([fifth, fourth, third]); | 
					
						
							|  |  |  |       sorted = resolver.sortEntryPointsByDependency(entryPoints, fourth); | 
					
						
							|  |  |  |       expect(sorted.entryPoints).toEqual([fifth, fourth]); | 
					
						
							|  |  |  |       sorted = resolver.sortEntryPointsByDependency(entryPoints, fifth); | 
					
						
							|  |  |  |       expect(sorted.entryPoints).toEqual([fifth]); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-28 20:48:35 +01:00
										 |  |  |     it('should use the appropriate DependencyHost for each entry-point', () => { | 
					
						
							|  |  |  |       const esm5Host = new EsmDependencyHost(fs, moduleResolver); | 
					
						
							|  |  |  |       const esm2015Host = new EsmDependencyHost(fs, moduleResolver); | 
					
						
							| 
									
										
										
										
											2019-04-29 18:51:52 +01:00
										 |  |  |       resolver = | 
					
						
							|  |  |  |           new DependencyResolver(fs, new MockLogger(), {esm5: esm5Host, esm2015: esm2015Host}); | 
					
						
							| 
									
										
										
										
											2019-04-28 20:48:35 +01:00
										 |  |  |       spyOn(esm5Host, 'findDependencies').and.callFake(createFakeComputeDependencies(dependencies)); | 
					
						
							|  |  |  |       spyOn(esm2015Host, 'findDependencies') | 
					
						
							|  |  |  |           .and.callFake(createFakeComputeDependencies(dependencies)); | 
					
						
							|  |  |  |       const result = resolver.sortEntryPointsByDependency([fifth, first, fourth, second, third]); | 
					
						
							|  |  |  |       expect(result.entryPoints).toEqual([fifth, fourth, third, second, first]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 20:30:03 +01:00
										 |  |  |       expect(esm5Host.findDependencies).toHaveBeenCalledWith(`${first.path}/index.js`); | 
					
						
							|  |  |  |       expect(esm5Host.findDependencies).not.toHaveBeenCalledWith(`${second.path}/sub/index.js`); | 
					
						
							|  |  |  |       expect(esm5Host.findDependencies).toHaveBeenCalledWith(`${third.path}/index.js`); | 
					
						
							|  |  |  |       expect(esm5Host.findDependencies).not.toHaveBeenCalledWith(`${fourth.path}/sub2/index.js`); | 
					
						
							|  |  |  |       expect(esm5Host.findDependencies).toHaveBeenCalledWith(`${fifth.path}/index.js`); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       expect(esm2015Host.findDependencies).not.toHaveBeenCalledWith(`${first.path}/index.js`); | 
					
						
							|  |  |  |       expect(esm2015Host.findDependencies).toHaveBeenCalledWith(`${second.path}/sub/index.js`); | 
					
						
							|  |  |  |       expect(esm2015Host.findDependencies).not.toHaveBeenCalledWith(`${third.path}/index.js`); | 
					
						
							|  |  |  |       expect(esm2015Host.findDependencies).toHaveBeenCalledWith(`${fourth.path}/sub2/index.js`); | 
					
						
							|  |  |  |       expect(esm2015Host.findDependencies).not.toHaveBeenCalledWith(`${fifth.path}/index.js`); | 
					
						
							| 
									
										
										
										
											2019-04-28 20:48:35 +01:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |     interface DepMap { | 
					
						
							|  |  |  |       [path: string]: {resolved: string[], missing: string[]}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 13:47:58 +00:00
										 |  |  |     function createFakeComputeDependencies(deps: DepMap) { | 
					
						
							|  |  |  |       return (entryPoint: string) => { | 
					
						
							|  |  |  |         const dependencies = new Set(); | 
					
						
							|  |  |  |         const missing = new Set(); | 
					
						
							|  |  |  |         const deepImports = new Set(); | 
					
						
							|  |  |  |         deps[entryPoint].resolved.forEach(dep => dependencies.add(dep)); | 
					
						
							|  |  |  |         deps[entryPoint].missing.forEach(dep => missing.add(dep)); | 
					
						
							|  |  |  |         return {dependencies, missing, deepImports}; | 
					
						
							| 
									
										
										
										
											2018-08-09 15:59:10 +01:00
										 |  |  |       }; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); |