Skip to content

Commit b774e22

Browse files
pmvaldpkozlowski-opensource
authored andcommitted
feat(compiler-cli): make it configurable to generate alias reexports (#53937)
At the moment when unified host is selected (through option `_useHostForImportGeneration`) the compiler always generates alias reexports. Such reexports are mainly generated to satisfy strict dependency condition for generated files. Such condition is no longer the case for G3. At the same time, these alias reexports make it impossible to mix locally compiled targets with globally compiled targets. More precisely, a globally compiled target may not be able to consume a locally compiled target as its dependency since the former may import from the alias reexports which do not exist in the latter due to local compilation mode. So, to make global-local compilation interop possible, it is required to be able to turn off alias reexport generation. PR Close #53937
1 parent ec03e46 commit b774e22

File tree

3 files changed

+154
-31
lines changed

3 files changed

+154
-31
lines changed

packages/compiler-cli/src/ngtsc/core/src/compiler.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,9 @@ export class NgCompiler {
985985
// Construct the ReferenceEmitter.
986986
let refEmitter: ReferenceEmitter;
987987
let aliasingHost: AliasingHost|null = null;
988-
if (this.adapter.unifiedModulesHost === null || !this.options['_useHostForImportGeneration']) {
988+
if (this.adapter.unifiedModulesHost === null ||
989+
(!this.options['_useHostForImportGeneration'] &&
990+
!this.options['_useHostForImportAndAliasGeneration'])) {
989991
let localImportStrategy: ReferenceEmitStrategy;
990992

991993
// The strategy used for local, in-project imports depends on whether TS has been configured
@@ -1031,11 +1033,14 @@ export class NgCompiler {
10311033
// First, try to use local identifiers if available.
10321034
new LocalIdentifierStrategy(),
10331035
// Then use aliased references (this is a workaround to StrictDeps checks).
1034-
new AliasStrategy(),
1036+
...(this.options['_useHostForImportAndAliasGeneration'] ? [new AliasStrategy()] : []),
10351037
// Then use fileNameToModuleName to emit imports.
10361038
new UnifiedModulesStrategy(reflector, this.adapter.unifiedModulesHost),
10371039
]);
1038-
aliasingHost = new UnifiedModulesAliasingHost(this.adapter.unifiedModulesHost);
1040+
1041+
if (this.options['_useHostForImportAndAliasGeneration']) {
1042+
aliasingHost = new UnifiedModulesAliasingHost(this.adapter.unifiedModulesHost);
1043+
}
10391044
}
10401045

10411046
const evaluator =

packages/compiler-cli/test/ngtsc/env.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,8 @@ export class NgtscTestEnvironment {
204204
}
205205
this.write('tsconfig.json', JSON.stringify(tsconfig, null, 2));
206206

207-
if (extraOpts['_useHostForImportGeneration'] === true) {
207+
if (extraOpts['_useHostForImportGeneration'] ||
208+
extraOpts['_useHostForImportAndAliasGeneration']) {
208209
setWrapHostForTest(makeWrapHost(new FileNameToModuleNameHost(this.fs)));
209210
}
210211
}

packages/compiler-cli/test/ngtsc/ngtsc_spec.ts

Lines changed: 144 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6823,11 +6823,12 @@ function allTests(os: string) {
68236823
});
68246824

68256825
describe('NgModule export aliasing', () => {
6826-
it('should use an alias to import a directive from a deep dependency', () => {
6827-
env.tsconfig({'_useHostForImportGeneration': true});
6826+
it('should use an alias to import a directive from a deep dependency when _useHostForImportAndAliasGeneration is set',
6827+
() => {
6828+
env.tsconfig({'_useHostForImportAndAliasGeneration': true});
68286829

6829-
// 'alpha' declares the directive which will ultimately be imported.
6830-
env.write('alpha.d.ts', `
6830+
// 'alpha' declares the directive which will ultimately be imported.
6831+
env.write('alpha.d.ts', `
68316832
import {ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration} from '@angular/core';
68326833
68336834
export declare class ExternalDir {
@@ -6839,8 +6840,8 @@ function allTests(os: string) {
68396840
}
68406841
`);
68416842

6842-
// 'beta' re-exports AlphaModule from alpha.
6843-
env.write('beta.d.ts', `
6843+
// 'beta' re-exports AlphaModule from alpha.
6844+
env.write('beta.d.ts', `
68446845
import {ɵɵNgModuleDeclaration} from '@angular/core';
68456846
import {AlphaModule} from './alpha';
68466847
@@ -6849,9 +6850,9 @@ function allTests(os: string) {
68496850
}
68506851
`);
68516852

6852-
// The application imports BetaModule from beta, gaining visibility of
6853-
// ExternalDir from alpha.
6854-
env.write('test.ts', `
6853+
// The application imports BetaModule from beta, gaining visibility of
6854+
// ExternalDir from alpha.
6855+
env.write('test.ts', `
68556856
import {Component, NgModule} from '@angular/core';
68566857
import {BetaModule} from './beta';
68576858
@@ -6867,17 +6868,71 @@ function allTests(os: string) {
68676868
})
68686869
export class Module {}
68696870
`);
6870-
env.driveMain();
6871-
const jsContents = env.getContents('test.js');
6871+
env.driveMain();
6872+
const jsContents = env.getContents('test.js');
68726873

6873-
// Expect that ExternalDir from alpha is imported via the re-export from beta.
6874-
expect(jsContents).toContain('import * as i1 from "root/beta";');
6875-
expect(jsContents).toContain('dependencies: [i1.\u0275ng$root$alpha$$ExternalDir]');
6876-
});
6874+
// Expect that ExternalDir from alpha is imported via the re-export from beta.
6875+
expect(jsContents).toContain('import * as i1 from "root/beta";');
6876+
expect(jsContents).toContain('dependencies: [i1.\u0275ng$root$alpha$$ExternalDir]');
6877+
});
6878+
6879+
it('should directly import a directive from a deep dependency when _useHostForImportGeneration is set',
6880+
() => {
6881+
env.tsconfig({'_useHostForImportGeneration': true});
68776882

6878-
it('should write alias ES2015 exports for NgModule exported directives', () => {
6879-
env.tsconfig({'_useHostForImportGeneration': true});
6880-
env.write('external.d.ts', `
6883+
// 'alpha' declares the directive which will ultimately be imported.
6884+
env.write('alpha.d.ts', `
6885+
import {ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration} from '@angular/core';
6886+
6887+
export declare class ExternalDir {
6888+
static ɵdir: ɵɵDirectiveDeclaration<ExternalDir, '[test]', never, never, never, never>;
6889+
}
6890+
6891+
export declare class AlphaModule {
6892+
static ɵmod: ɵɵNgModuleDeclaration<AlphaModule, [typeof ExternalDir], never, [typeof ExternalDir]>;
6893+
}
6894+
`);
6895+
6896+
// 'beta' re-exports AlphaModule from alpha.
6897+
env.write('beta.d.ts', `
6898+
import {ɵɵNgModuleDeclaration} from '@angular/core';
6899+
import {AlphaModule} from './alpha';
6900+
6901+
export declare class BetaModule {
6902+
static ɵmod: ɵɵNgModuleDeclaration<AlphaModule, never, never, [typeof AlphaModule]>;
6903+
}
6904+
`);
6905+
6906+
// The application imports BetaModule from beta, gaining visibility of
6907+
// ExternalDir from alpha.
6908+
env.write('test.ts', `
6909+
import {Component, NgModule} from '@angular/core';
6910+
import {BetaModule} from './beta';
6911+
6912+
@Component({
6913+
selector: 'cmp',
6914+
template: '<div test></div>',
6915+
})
6916+
export class Cmp {}
6917+
6918+
@NgModule({
6919+
declarations: [Cmp],
6920+
imports: [BetaModule],
6921+
})
6922+
export class Module {}
6923+
`);
6924+
env.driveMain();
6925+
const jsContents = env.getContents('test.js');
6926+
6927+
// Expect that ExternalDir from alpha is imported via the re-export from beta.
6928+
expect(jsContents).toContain('import * as i1 from "root/alpha";');
6929+
expect(jsContents).toContain('dependencies: [i1.ExternalDir]');
6930+
});
6931+
6932+
it('should write alias ES2015 exports for NgModule exported directives when _useHostForImportAndAliasGeneration is set',
6933+
() => {
6934+
env.tsconfig({'_useHostForImportAndAliasGeneration': true});
6935+
env.write('external.d.ts', `
68816936
import {ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration} from '@angular/core';
68826937
import {LibModule} from './lib';
68836938
@@ -6889,7 +6944,7 @@ function allTests(os: string) {
68896944
static ɵmod: ɵɵNgModuleDeclaration<ExternalModule, [typeof ExternalDir], never, [typeof ExternalDir, typeof LibModule]>;
68906945
}
68916946
`);
6892-
env.write('lib.d.ts', `
6947+
env.write('lib.d.ts', `
68936948
import {ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration} from '@angular/core';
68946949
68956950
export declare class LibDir {
@@ -6900,7 +6955,7 @@ function allTests(os: string) {
69006955
static ɵmod: ɵɵNgModuleDeclaration<LibModule, [typeof LibDir], never, [typeof LibDir]>;
69016956
}
69026957
`);
6903-
env.write('foo.ts', `
6958+
env.write('foo.ts', `
69046959
import {Directive, NgModule} from '@angular/core';
69056960
import {ExternalModule} from './external';
69066961
@@ -6913,7 +6968,7 @@ function allTests(os: string) {
69136968
})
69146969
export class FooModule {}
69156970
`);
6916-
env.write('index.ts', `
6971+
env.write('index.ts', `
69176972
import {Component, NgModule} from '@angular/core';
69186973
import {FooModule} from './foo';
69196974
@@ -6929,14 +6984,76 @@ function allTests(os: string) {
69296984
})
69306985
export class IndexModule {}
69316986
`);
6932-
env.driveMain();
6933-
const jsContents = env.getContents('index.js');
6934-
expect(jsContents)
6935-
.toContain('export { FooDir as \u0275ng$root$foo$$FooDir } from "root/foo";');
6936-
});
6987+
env.driveMain();
6988+
const jsContents = env.getContents('index.js');
6989+
expect(jsContents)
6990+
.toContain('export { FooDir as \u0275ng$root$foo$$FooDir } from "root/foo";');
6991+
});
6992+
6993+
it('should not write alias ES2015 exports for NgModule exported directives when _useHostForImportGeneration is set',
6994+
() => {
6995+
env.tsconfig({'_useHostForImportGeneration': true});
6996+
env.write('external.d.ts', `
6997+
import {ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration} from '@angular/core';
6998+
import {LibModule} from './lib';
6999+
7000+
export declare class ExternalDir {
7001+
static ɵdir: ɵɵDirectiveDeclaration<ExternalDir, '[test]', never, never, never, never>;
7002+
}
7003+
7004+
export declare class ExternalModule {
7005+
static ɵmod: ɵɵNgModuleDeclaration<ExternalModule, [typeof ExternalDir], never, [typeof ExternalDir, typeof LibModule]>;
7006+
}
7007+
`);
7008+
env.write('lib.d.ts', `
7009+
import {ɵɵDirectiveDeclaration, ɵɵNgModuleDeclaration} from '@angular/core';
7010+
7011+
export declare class LibDir {
7012+
static ɵdir: ɵɵDirectiveDeclaration<LibDir, '[lib]', never, never, never, never>;
7013+
}
7014+
7015+
export declare class LibModule {
7016+
static ɵmod: ɵɵNgModuleDeclaration<LibModule, [typeof LibDir], never, [typeof LibDir]>;
7017+
}
7018+
`);
7019+
env.write('foo.ts', `
7020+
import {Directive, NgModule} from '@angular/core';
7021+
import {ExternalModule} from './external';
7022+
7023+
@Directive({selector: '[foo]'})
7024+
export class FooDir {}
7025+
7026+
@NgModule({
7027+
declarations: [FooDir],
7028+
exports: [FooDir, ExternalModule]
7029+
})
7030+
export class FooModule {}
7031+
`);
7032+
env.write('index.ts', `
7033+
import {Component, NgModule} from '@angular/core';
7034+
import {FooModule} from './foo';
7035+
7036+
@Component({
7037+
selector: 'index',
7038+
template: '<div foo test lib></div>',
7039+
})
7040+
export class IndexCmp {}
7041+
7042+
@NgModule({
7043+
declarations: [IndexCmp],
7044+
exports: [FooModule],
7045+
})
7046+
export class IndexModule {}
7047+
`);
7048+
env.driveMain();
7049+
const jsContents = env.getContents('index.js');
7050+
expect(jsContents)
7051+
.not.toMatch(
7052+
/export\s+\{\s*FooDir\s+as\s+ \u0275ng$root$foo$$FooDir\s*\}\s+from\s+"root\/foo";/);
7053+
});
69377054

69387055
it('should escape unusual characters in aliased filenames', () => {
6939-
env.tsconfig({'_useHostForImportGeneration': true});
7056+
env.tsconfig({'_useHostForImportAndAliasGeneration': true});
69407057
env.write('other._$test.ts', `
69417058
import {Directive, NgModule} from '@angular/core';
69427059

0 commit comments

Comments
 (0)