/** * @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 * as compiler from '@angular/compiler'; import * as ts from 'typescript'; import {MetadataCollector} from '../../src/metadata/collector'; import {CompilerHost, CompilerOptions, LibrarySummary} from '../../src/transformers/api'; import {TsCompilerAotCompilerTypeCheckHostAdapter, createCompilerHost} from '../../src/transformers/compiler_host'; import {Directory, Entry, MockAotContext, MockCompilerHost} from '../mocks'; const dummyModule = 'export let foo: any[];'; const aGeneratedFile = new compiler.GeneratedFile( '/tmp/src/index.ts', '/tmp/src/index.ngfactory.ts', [new compiler.DeclareVarStmt('x', new compiler.LiteralExpr(1))]); const aGeneratedFileText = `var x:any = 1;\n`; describe('NgCompilerHost', () => { let codeGenerator: {generateFile: jasmine.Spy; findGeneratedFileNames: jasmine.Spy;}; beforeEach(() => { codeGenerator = { generateFile: jasmine.createSpy('generateFile').and.returnValue(null), findGeneratedFileNames: jasmine.createSpy('findGeneratedFileNames').and.returnValue([]), }; }); function createNgHost({files = {}}: {files?: Directory} = {}): CompilerHost { const context = new MockAotContext('/tmp/', files); return new MockCompilerHost(context) as ts.CompilerHost; } function createHost({ files = {}, options = { basePath: '/tmp', moduleResolution: ts.ModuleResolutionKind.NodeJs, }, rootNames = ['/tmp/index.ts'], ngHost = createNgHost({files}), librarySummaries = [], }: { files?: Directory, options?: CompilerOptions, rootNames?: string[], ngHost?: CompilerHost, librarySummaries?: LibrarySummary[] } = {}) { return new TsCompilerAotCompilerTypeCheckHostAdapter( rootNames, options, ngHost, new MetadataCollector(), codeGenerator, new Map(librarySummaries.map(entry => [entry.fileName, entry] as[string, LibrarySummary]))); } describe('fileNameToModuleName', () => { let host: TsCompilerAotCompilerTypeCheckHostAdapter; beforeEach(() => { host = createHost(); }); it('should use a package import when accessing a package from a source file', () => { expect(host.fileNameToModuleName('/tmp/node_modules/@angular/core.d.ts', '/tmp/main.ts')) .toBe('@angular/core'); }); it('should use a package import when accessing a package from another package', () => { expect(host.fileNameToModuleName( '/tmp/node_modules/mod1/index.d.ts', '/tmp/node_modules/mod2/index.d.ts')) .toBe('mod1/index'); expect(host.fileNameToModuleName( '/tmp/node_modules/@angular/core/index.d.ts', '/tmp/node_modules/@angular/common/index.d.ts')) .toBe('@angular/core/index'); }); it('should use a relative import when accessing a file in the same package', () => { expect(host.fileNameToModuleName( '/tmp/node_modules/mod/a/child.d.ts', '/tmp/node_modules/mod/index.d.ts')) .toBe('./a/child'); expect(host.fileNameToModuleName( '/tmp/node_modules/@angular/core/src/core.d.ts', '/tmp/node_modules/@angular/core/index.d.ts')) .toBe('./src/core'); }); it('should use a relative import when accessing a source file from a source file', () => { expect(host.fileNameToModuleName('/tmp/src/a/child.ts', '/tmp/src/index.ts')) .toBe('./a/child'); }); it('should use a relative import when accessing generated files, even if crossing packages', () => { expect(host.fileNameToModuleName( '/tmp/node_modules/mod2/b.ngfactory.d.ts', '/tmp/node_modules/mod1/a.ngfactory.d.ts')) .toBe('../mod2/b.ngfactory'); }); it('should support multiple rootDirs when accessing a source file form a source file', () => { const hostWithMultipleRoots = createHost({ options: { basePath: '/tmp/', rootDirs: [ 'src/a', 'src/b', ] } }); // both files are in the rootDirs expect(hostWithMultipleRoots.fileNameToModuleName('/tmp/src/b/b.ts', '/tmp/src/a/a.ts')) .toBe('./b'); // one file is not in the rootDirs expect(hostWithMultipleRoots.fileNameToModuleName('/tmp/src/c/c.ts', '/tmp/src/a/a.ts')) .toBe('../c/c'); }); it('should error if accessing a source file from a package', () => { expect( () => host.fileNameToModuleName( '/tmp/src/a/child.ts', '/tmp/node_modules/@angular/core.d.ts')) .toThrowError( 'Trying to import a source file from a node_modules package: ' + 'import /tmp/src/a/child.ts from /tmp/node_modules/@angular/core.d.ts'); }); it('should use the provided implementation if any', () => { const ngHost = createNgHost(); ngHost.fileNameToModuleName = () => 'someResult'; const host = createHost({ngHost}); expect(host.fileNameToModuleName('a', 'b')).toBe('someResult'); }); }); describe('moduleNameToFileName', () => { it('should resolve an import using the containing file', () => { const host = createHost({files: {'tmp': {'src': {'a': {'child.d.ts': dummyModule}}}}}); expect(host.moduleNameToFileName('./a/child', '/tmp/src/index.ts')) .toBe('/tmp/src/a/child.d.ts'); }); it('should allow to skip the containg file for package imports', () => { const host = createHost({files: {'tmp': {'node_modules': {'@core': {'index.d.ts': dummyModule}}}}}); expect(host.moduleNameToFileName('@core/index')).toBe('/tmp/node_modules/@core/index.d.ts'); }); it('should use the provided implementation if any', () => { const ngHost = createNgHost(); ngHost.moduleNameToFileName = () => 'someResult'; const host = createHost({ngHost}); expect(host.moduleNameToFileName('a', 'b')).toBe('someResult'); }); it('should work well with windows paths', () => { const host = createHost({ rootNames: ['\\tmp\\index.ts'], options: {basePath: '\\tmp'}, files: {'tmp': {'node_modules': {'@core': {'index.d.ts': dummyModule}}}} }); expect(host.moduleNameToFileName('@core/index')).toBe('/tmp/node_modules/@core/index.d.ts'); }); }); describe('resourceNameToFileName', () => { it('should resolve a relative import', () => { const host = createHost({files: {'tmp': {'src': {'a': {'child.html': '