Previously, the compiler assumed that all TS files logically within a project existed under one or more "root directories". If the TS compiler option `rootDir` or `rootDirs` was set, they would dictate the root directories in use, otherwise the current directory was used. Unfortunately this assumption was unfounded - it's common for projects without explicit `rootDirs` to import from files outside the current working directory. In such cases the `LogicalProjectStrategy` would attempt to generate imports into those files, and fail. This would lead to no `ReferenceEmitStrategy` being able to generate an import, and end in a compiler assertion failure. This commit introduces a new strategy to use when there are no `rootDirs` explicitly present, the `RelativePathStrategy`. It uses simpler, filesystem- relative paths to generate imports, even to files above the current working directory. Fixes #33659 Fixes #33562 PR Close #33828
		
			
				
	
	
		
			118 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			118 lines
		
	
	
		
			3.4 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 {absoluteFrom} from '../../src/ngtsc/file_system';
 | |
| import {runInEachFileSystem} from '../../src/ngtsc/file_system/testing';
 | |
| import {loadStandardTestFiles} from '../helpers/src/mock_file_loading';
 | |
| 
 | |
| import {NgtscTestEnvironment} from './env';
 | |
| 
 | |
| const testFiles = loadStandardTestFiles();
 | |
| 
 | |
| runInEachFileSystem(() => {
 | |
|   describe('monorepos', () => {
 | |
|     let env !: NgtscTestEnvironment;
 | |
| 
 | |
|     beforeEach(() => {
 | |
|       env = NgtscTestEnvironment.setup(testFiles, absoluteFrom('/app'));
 | |
|       env.tsconfig();
 | |
| 
 | |
|       // env.tsconfig() will write to /app/tsconfig.json, but list it as extending
 | |
|       // ./tsconfig-base.json. So that file should exist, and redirect to ../tsconfig-base.json.
 | |
|       env.write('/app/tsconfig-base.json', JSON.stringify({'extends': '../tsconfig-base.json'}));
 | |
|     });
 | |
| 
 | |
|     it('should compile a project with a reference above the current dir', () => {
 | |
|       env.write('/app/index.ts', `
 | |
|         import {Component, NgModule} from '@angular/core';
 | |
|         import {LibModule} from '../lib/module';
 | |
| 
 | |
|         @Component({
 | |
|           selector: 'app-cmp',
 | |
|           template: '<lib-cmp></lib-cmp>',
 | |
|         })
 | |
|         export class AppCmp {}
 | |
| 
 | |
|         @NgModule({
 | |
|           declarations: [AppCmp],
 | |
|           imports: [LibModule],
 | |
|         })
 | |
|         export class AppModule {}
 | |
|       `);
 | |
|       env.write('/lib/module.ts', `
 | |
|         import {NgModule} from '@angular/core';
 | |
|         import {LibCmp} from './cmp';
 | |
| 
 | |
|         @NgModule({
 | |
|           declarations: [LibCmp],
 | |
|           exports: [LibCmp],
 | |
|         })
 | |
|         export class LibModule {}
 | |
|       `);
 | |
|       env.write('/lib/cmp.ts', `
 | |
|         import {Component} from '@angular/core';
 | |
| 
 | |
|         @Component({
 | |
|           selector: 'lib-cmp',
 | |
|           template: '...',
 | |
|         })
 | |
|         export class LibCmp {}
 | |
|       `);
 | |
| 
 | |
|       env.driveMain();
 | |
| 
 | |
|       const jsContents = env.getContents('app/index.js');
 | |
|       expect(jsContents).toContain(`import * as i1 from "../lib/cmp";`);
 | |
|     });
 | |
| 
 | |
|     it('should compile a project with a reference into the same dir', () => {
 | |
|       env.write('/app/index.ts', `
 | |
|         import {Component, NgModule} from '@angular/core';
 | |
|         import {TargetModule} from './target';
 | |
| 
 | |
|         @Component({
 | |
|           selector: 'app-cmp',
 | |
|           template: '<target-cmp></target-cmp>',
 | |
|         })
 | |
|         export class AppCmp {}
 | |
| 
 | |
|         @NgModule({
 | |
|           declarations: [AppCmp],
 | |
|           imports: [TargetModule],
 | |
|         })
 | |
|         export class AppModule {}
 | |
|       `);
 | |
| 
 | |
|       env.write('/app/target.ts', `
 | |
|         import {Component, NgModule} from '@angular/core';
 | |
| 
 | |
|         @Component({
 | |
|           selector: 'target-cmp',
 | |
|           template: '...',
 | |
|         })
 | |
|         export class TargetCmp {}
 | |
| 
 | |
|         @NgModule({
 | |
|           declarations: [TargetCmp],
 | |
|           exports: [TargetCmp],
 | |
|         })
 | |
|         export class TargetModule {}
 | |
|       `);
 | |
| 
 | |
|       env.driveMain();
 | |
| 
 | |
|       // Look for index.js, not app/index.js, because of TypeScript's behavior of stripping off the
 | |
|       // common prefix of all input files.
 | |
|       const jsContents = env.getContents('index.js');
 | |
| 
 | |
|       // The real goal of this test was to check that the relative import has the leading './'.
 | |
|       expect(jsContents).toContain(`import * as i1 from "./target";`);
 | |
|     });
 | |
|   });
 | |
| });
 |