Skip to content

Commit

Permalink
fix(compiler-cli): don't emit empty providers array (#46301)
Browse files Browse the repository at this point in the history
Saves us some bytes by not emitting `providers` in `defineInjector`. While the amount of bytes isn't huge, I think that this change is worthwhile, because `ng generate` currently generates `providers: []` with every `NgModule` which users can forget to remove.

PR Close #46301
  • Loading branch information
crisbeto authored and jessicajaniuk committed Jun 10, 2022
1 parent c2ba7c4 commit 8ecfd71
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 10 deletions.
Expand Up @@ -316,11 +316,16 @@ export class NgModuleDecoratorHandler implements
};

const rawProviders = ngModule.has('providers') ? ngModule.get('providers')! : null;
const wrapperProviders = rawProviders !== null ?
new WrappedNodeExpr(
this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(rawProviders) :
rawProviders) :
null;
let wrappedProviders: WrappedNodeExpr<ts.Expression>|null = null;

// In most cases the providers will be an array literal. Check if it has any elements
// and don't include the providers if it doesn't which saves us a few bytes.
if (rawProviders !== null &&
(!ts.isArrayLiteralExpression(rawProviders) || rawProviders.elements.length > 0)) {
wrappedProviders = new WrappedNodeExpr(
this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(rawProviders) :
rawProviders);
}

const topLevelImports: TopLevelImportedExpression[] = [];
if (ngModule.has('imports')) {
Expand Down Expand Up @@ -362,7 +367,7 @@ export class NgModuleDecoratorHandler implements
name,
type,
internalType,
providers: wrapperProviders,
providers: wrappedProviders,
};

const factoryMetadata: R3FactoryMetadata = {
Expand Down
Expand Up @@ -584,3 +584,58 @@ export declare class ForwardModule {
static ɵinj: i0.ɵɵInjectorDeclaration<ForwardModule>;
}

/****************************************************************************************************
* PARTIAL FILE: empty_fields.js
****************************************************************************************************/
import { NgModule } from '@angular/core';
import * as i0 from "@angular/core";
export class FooModule {
}
FooModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: FooModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
FooModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: FooModule });
FooModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: FooModule });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: FooModule, decorators: [{
type: NgModule,
args: [{
providers: [],
declarations: [],
imports: [],
}]
}] });

/****************************************************************************************************
* PARTIAL FILE: empty_fields.d.ts
****************************************************************************************************/
import * as i0 from "@angular/core";
export declare class FooModule {
static ɵfac: i0.ɵɵFactoryDeclaration<FooModule, never>;
static ɵmod: i0.ɵɵNgModuleDeclaration<FooModule, never, never, never>;
static ɵinj: i0.ɵɵInjectorDeclaration<FooModule>;
}

/****************************************************************************************************
* PARTIAL FILE: variable_providers.js
****************************************************************************************************/
import { InjectionToken, NgModule } from '@angular/core';
import * as i0 from "@angular/core";
const PROVIDERS = [{ provide: new InjectionToken('token'), useValue: 1 }];
export class FooModule {
}
FooModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: FooModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
FooModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: FooModule });
FooModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: FooModule, providers: PROVIDERS });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: FooModule, decorators: [{
type: NgModule,
args: [{ providers: PROVIDERS }]
}] });

/****************************************************************************************************
* PARTIAL FILE: variable_providers.d.ts
****************************************************************************************************/
import * as i0 from "@angular/core";
export declare class FooModule {
static ɵfac: i0.ɵɵFactoryDeclaration<FooModule, never>;
static ɵmod: i0.ɵɵNgModuleDeclaration<FooModule, never, never, never>;
static ɵinj: i0.ɵɵInjectorDeclaration<FooModule>;
}

Expand Up @@ -164,6 +164,34 @@
"angularCompilerOptions": {
"linkerJitMode": true
}
},
{
"description": "should not pass along empty array fields to the declaration",
"inputFiles": [
"empty_fields.ts"
],
"expectations": [
{
"failureMessage": "Invalid injector definition",
"files": [
"empty_fields.js"
]
}
]
},
{
"description": "should handle providers passed in as a variable",
"inputFiles": [
"variable_providers.ts"
],
"expectations": [
{
"failureMessage": "Invalid injector definition",
"files": [
"variable_providers.js"
]
}
]
}
]
}
}
@@ -0,0 +1,4 @@
export class FooModule {}
FooModule.ɵfac = ;
FooModule.ɵmod = ;
FooModule.ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({});
@@ -0,0 +1,9 @@
import {NgModule} from '@angular/core';

@NgModule({
providers: [],
declarations: [],
imports: [],
})
export class FooModule {
}
@@ -0,0 +1,5 @@
const PROVIDERS = [{provide: new InjectionToken('token'), useValue: 1}];
export class FooModule {}
FooModule.ɵfac = ;
FooModule.ɵmod = ;
FooModule.ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({providers: PROVIDERS});
@@ -0,0 +1,7 @@
import {InjectionToken, NgModule} from '@angular/core';

const PROVIDERS = [{provide: new InjectionToken('token'), useValue: 1}];

@NgModule({providers: PROVIDERS})
export class FooModule {
}
9 changes: 6 additions & 3 deletions packages/compiler/src/jit_compiler_facade.ts
Expand Up @@ -107,7 +107,9 @@ export class CompilerFacadeImpl implements CompilerFacade {
name: facade.name,
type: wrapReference(facade.type),
internalType: new WrappedNodeExpr(facade.type),
providers: new WrappedNodeExpr(facade.providers),
providers: facade.providers && facade.providers.length > 0 ?
new WrappedNodeExpr(facade.providers) :
null,
imports: facade.imports.map(i => new WrappedNodeExpr(i)),
};
const res = compileInjector(meta);
Expand Down Expand Up @@ -659,8 +661,9 @@ function convertDeclareInjectorFacadeToMetadata(declaration: R3DeclareInjectorFa
name: declaration.type.name,
type: wrapReference(declaration.type),
internalType: new WrappedNodeExpr(declaration.type),
providers: declaration.providers !== undefined ? new WrappedNodeExpr(declaration.providers) :
null,
providers: declaration.providers !== undefined && declaration.providers.length > 0 ?
new WrappedNodeExpr(declaration.providers) :
null,
imports: declaration.imports !== undefined ?
declaration.imports.map(i => new WrappedNodeExpr(i)) :
[],
Expand Down

0 comments on commit 8ecfd71

Please sign in to comment.