With #28594 we refactored the `@angular/compiler` slightly to allow opting out from external symbol re-exports which are enabled by default. Since symbol re-exports only benefit projects which have a very strict dependency enforcement, external symbols should not be re-exported by default as this could grow the size of factory files and cause unexpected behavior with Angular's AOT symbol resolving (e.g. see: #25644). Note that the common strict dependency enforcement for source files does still work with external symbol re-exports disabled, but there are also strict dependency checks that enforce strict module dependencies also for _generated files_ (such as the ngfactory files). This is how Google3 manages it's dependencies and therefore external symbol re-exports need to be enabled within Google3. Also "ngtsc" also does not provide any way of using external symbol re-exports, so this means that with this change, NGC can partially match the behavior of "ngtsc" then (unless explicitly opted-out). As mentioned before, internally at Google symbol re-exports need to be still enabled, so the `ng_module` Bazel rule will enable the symbol re-exports by default when running within Blaze. Fixes #25644. PR Close #28633
		
			
				
	
	
		
			138 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			138 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /**
 | |
|  * @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
 | |
|  */
 | |
| 
 | |
| import {AotSummaryResolver, AotSummaryResolverHost, CompileSummaryKind, CompileTypeSummary, ResolvedStaticSymbol, StaticSymbol, StaticSymbolCache, StaticSymbolResolver} from '@angular/compiler';
 | |
| import {deserializeSummaries, serializeSummaries} from '@angular/compiler/src/aot/summary_serializer';
 | |
| import {ConstantPool} from '@angular/compiler/src/constant_pool';
 | |
| import * as o from '@angular/compiler/src/output/output_ast';
 | |
| import {OutputContext} from '@angular/compiler/src/util';
 | |
| import * as path from 'path';
 | |
| 
 | |
| import {MockStaticSymbolResolverHost, MockSummaryResolver} from './static_symbol_resolver_spec';
 | |
| 
 | |
| const EXT = /(\.d)?\.ts$/;
 | |
| 
 | |
| {
 | |
|   describe('AotSummaryResolver', () => {
 | |
|     let summaryResolver: AotSummaryResolver;
 | |
|     let symbolCache: StaticSymbolCache;
 | |
|     let host: MockAotSummaryResolverHost;
 | |
| 
 | |
|     beforeEach(() => { symbolCache = new StaticSymbolCache(); });
 | |
| 
 | |
|     function init(summaries: {[filePath: string]: string} = {}) {
 | |
|       host = new MockAotSummaryResolverHost(summaries);
 | |
|       summaryResolver = new AotSummaryResolver(host, symbolCache);
 | |
|     }
 | |
| 
 | |
|     function serialize(
 | |
|         symbols: ResolvedStaticSymbol[], enableExternalSymbolReexports = false): string {
 | |
|       // Note: Don't use the top level host / summaryResolver as they might not be created yet
 | |
|       const mockSummaryResolver = new MockSummaryResolver([]);
 | |
|       const symbolResolver = new StaticSymbolResolver(
 | |
|           new MockStaticSymbolResolverHost({}), symbolCache, mockSummaryResolver);
 | |
|       return serializeSummaries(
 | |
|                  'someFile.ts', createMockOutputContext(), mockSummaryResolver, symbolResolver,
 | |
|                  symbols, [], enableExternalSymbolReexports)
 | |
|           .json;
 | |
|     }
 | |
| 
 | |
|     it('should load serialized summary files', () => {
 | |
|       const asymbol = symbolCache.get('/a.d.ts', 'a');
 | |
|       init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}])});
 | |
|       expect(summaryResolver.resolveSummary(asymbol)).toEqual({symbol: asymbol, metadata: 1});
 | |
|     });
 | |
| 
 | |
|     it('should not load summaries for source files', () => {
 | |
|       init({});
 | |
|       spyOn(host, 'loadSummary').and.callThrough();
 | |
| 
 | |
|       expect(summaryResolver.resolveSummary(symbolCache.get('/a.ts', 'a'))).toBeFalsy();
 | |
|       expect(host.loadSummary).not.toHaveBeenCalled();
 | |
|     });
 | |
| 
 | |
|     it('should cache summaries', () => {
 | |
|       const asymbol = symbolCache.get('/a.d.ts', 'a');
 | |
|       init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}])});
 | |
|       expect(summaryResolver.resolveSummary(asymbol)).toBe(summaryResolver.resolveSummary(asymbol));
 | |
|     });
 | |
| 
 | |
|     it('should return all symbols in a summary', () => {
 | |
|       const asymbol = symbolCache.get('/a.d.ts', 'a');
 | |
|       init({'/a.ngsummary.json': serialize([{symbol: asymbol, metadata: 1}])});
 | |
|       expect(summaryResolver.getSymbolsOf('/a.d.ts')).toEqual([asymbol]);
 | |
|     });
 | |
| 
 | |
|     it('should fill importAs for deep symbols if external symbol re-exports are enabled', () => {
 | |
|       const libSymbol = symbolCache.get('/lib.d.ts', 'Lib');
 | |
|       const srcSymbol = symbolCache.get('/src.ts', 'Src');
 | |
|       init({
 | |
|         '/src.ngsummary.json':
 | |
|             serialize([{symbol: srcSymbol, metadata: 1}, {symbol: libSymbol, metadata: 2}], true)
 | |
|       });
 | |
|       summaryResolver.getSymbolsOf('/src.d.ts');
 | |
| 
 | |
|       expect(summaryResolver.getImportAs(symbolCache.get('/src.d.ts', 'Src'))).toBeFalsy();
 | |
|       expect(summaryResolver.getImportAs(libSymbol))
 | |
|           .toBe(symbolCache.get('/src.ngfactory.d.ts', 'Lib_1'));
 | |
|     });
 | |
| 
 | |
|     describe('isLibraryFile', () => {
 | |
|       it('should use host.isSourceFile to calculate the result', () => {
 | |
|         init();
 | |
|         expect(summaryResolver.isLibraryFile('someFile.ts')).toBe(false);
 | |
|         expect(summaryResolver.isLibraryFile('someFile.d.ts')).toBe(true);
 | |
|       });
 | |
| 
 | |
|       it('should calculate the result for generated files based on the result for non generated files',
 | |
|          () => {
 | |
|            init();
 | |
|            spyOn(host, 'isSourceFile').and.callThrough();
 | |
|            expect(summaryResolver.isLibraryFile('someFile.ngfactory.ts')).toBe(false);
 | |
|            expect(host.isSourceFile).toHaveBeenCalledWith('someFile.ts');
 | |
|          });
 | |
|     });
 | |
| 
 | |
|     describe('regression', () => {
 | |
|       // #18170
 | |
|       it('should support resolving symbol with members ', () => {
 | |
|         init();
 | |
|         expect(summaryResolver.resolveSummary(symbolCache.get('/src.d.ts', 'Src', ['One', 'Two'])))
 | |
|             .toBeNull();
 | |
|       });
 | |
|     });
 | |
|   });
 | |
| }
 | |
| 
 | |
| 
 | |
| export class MockAotSummaryResolverHost implements AotSummaryResolverHost {
 | |
|   constructor(private summaries: {[fileName: string]: string}) {}
 | |
| 
 | |
|   fileNameToModuleName(fileName: string): string {
 | |
|     return './' + path.basename(fileName).replace(EXT, '');
 | |
|   }
 | |
| 
 | |
|   toSummaryFileName(sourceFileName: string): string {
 | |
|     return sourceFileName.replace(EXT, '') + '.d.ts';
 | |
|   }
 | |
| 
 | |
|   fromSummaryFileName(filePath: string): string { return filePath; }
 | |
| 
 | |
|   isSourceFile(filePath: string) { return !filePath.endsWith('.d.ts'); }
 | |
| 
 | |
|   loadSummary(filePath: string): string { return this.summaries[filePath]; }
 | |
| }
 | |
| 
 | |
| export function createMockOutputContext(): OutputContext {
 | |
|   return {
 | |
|     statements: [],
 | |
|     genFilePath: 'someGenFilePath',
 | |
|     importExpr: () => o.NULL_EXPR,
 | |
|     constantPool: new ConstantPool()
 | |
|   };
 | |
| } |