Skip to content

Commit

Permalink
fix(compiler-cli): use existing imports for standalone dependencies (#…
Browse files Browse the repository at this point in the history
…46029)

This commit fixes a small issue in the logic around the calculation of
template scopes for standalone components. These scopes include a
`Reference` for each dependency of a standalone component, which is used to
generate references to that dependency in various contexts.

Previously, the `Reference` used for a dependency was the one generated from
its own metadata. For example, a referenced directive used the `Reference`
that was created when analyzing the directive declaration itself. This still
works, as the compiler is always able to emit a reference to any valid
`Reference`. However, it's not optimal.

The `Reference` which should be used instead is the one generated from
analyzing the standalone component's `imports` array, which has knowledge of
how the dependency is referenced from within the standalone component's file
itself. This allows the compiler to skip creating a new import for the
dependency when emitting the standalone component, and use the existing,
user-authored import instead. This saves on code size and avoids taxing the
bundler with unnecessary imports.

PR Close #46029
  • Loading branch information
alxhub authored and thePunderWoman committed May 18, 2022
1 parent a524a50 commit 35f20af
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 4 deletions.
6 changes: 3 additions & 3 deletions packages/compiler-cli/src/ngtsc/scope/src/standalone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,21 @@ export class StandaloneComponentScopeReader implements ComponentScopeReader {

const dirMeta = this.metaReader.getDirectiveMetadata(ref);
if (dirMeta !== null) {
dependencies.add(dirMeta);
dependencies.add({...dirMeta, ref});
isPoisoned = isPoisoned || dirMeta.isPoisoned || !dirMeta.isStandalone;
continue;
}

const pipeMeta = this.metaReader.getPipeMetadata(ref);
if (pipeMeta !== null) {
dependencies.add(pipeMeta);
dependencies.add({...pipeMeta, ref});
isPoisoned = isPoisoned || !pipeMeta.isStandalone;
continue;
}

const ngModuleMeta = this.metaReader.getNgModuleMetadata(ref);
if (ngModuleMeta !== null) {
dependencies.add(ngModuleMeta);
dependencies.add({...ngModuleMeta, ref});

let ngModuleScope: ExportScope|null;
if (ref.node.getSourceFile().isDeclarationFile) {
Expand Down
31 changes: 30 additions & 1 deletion packages/compiler-cli/test/ngtsc/standalone_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,35 @@ runInEachFileSystem(() => {
expect(dtsCode).toContain('i0.ɵɵPipeDeclaration<TestPipe, "test", true>');
});

it('should use existing imports for dependencies', () => {
env.write('dep.ts', `
import {Directive} from '@angular/core';
@Directive({
standalone: true,
selector: '[dir]',
})
export class TestDir {}
`);
env.write('test.ts', `
import {Component} from '@angular/core';
import {TestDir} from './dep';
@Component({
standalone: true,
imports: [TestDir],
selector: 'test-cmp',
template: '<div dir></div>',
})
export class TestCmp {}
`);

env.driveMain();

const jsContents = env.getContents('test.js');
expect(jsContents).toContain('dependencies: [TestDir]');
});

it('should compile a standalone component even in the presence of cycles', () => {
env.write('dep.ts', `
import {Directive, Input} from '@angular/core';
Expand Down Expand Up @@ -128,7 +157,7 @@ runInEachFileSystem(() => {
env.driveMain();

const jsContents = env.getContents('test.js');
expect(jsContents).toContain('dependencies: [i1.TestDir]');
expect(jsContents).toContain('dependencies: [TestDir]');
});

it('should error when a non-standalone component tries to use imports', () => {
Expand Down

0 comments on commit 35f20af

Please sign in to comment.