diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index 28f8d03f8bc4b..560f5dde95c9d 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, Identifiers, InterpolationConfig, LexerRange, ParseError, ParseSourceFile, ParseTemplateOptions, R3ComponentMetadata, R3TargetBinder, SchemaMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler'; +import {ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, Identifiers, InterpolationConfig, LexerRange, ParseError, ParseSourceFile, ParseTemplateOptions, R3ComponentMetadata, R3FactoryTarget, R3TargetBinder, SchemaMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler'; import * as ts from 'typescript'; import {CycleAnalyzer} from '../../cycles'; @@ -490,7 +490,8 @@ export class ComponentDecoratorHandler implements CompileResult[] { const meta = analysis.meta; const res = compileComponentFromMetadata(meta, pool, makeBindingParser()); - const factoryRes = compileNgFactoryDefField({...meta, injectFn: Identifiers.directiveInject}); + const factoryRes = compileNgFactoryDefField( + {...meta, injectFn: Identifiers.directiveInject, target: R3FactoryTarget.Component}); if (analysis.metadataStmt !== null) { factoryRes.statements.push(analysis.metadataStmt); } diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts index adbb5069408d5..b1e58f00b3a0f 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/directive.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ConstantPool, EMPTY_SOURCE_SPAN, Expression, Identifiers, ParseError, ParsedHostBindings, R3DirectiveMetadata, R3QueryMetadata, Statement, WrappedNodeExpr, compileDirectiveFromMetadata, makeBindingParser, parseHostBindings, verifyHostBindings} from '@angular/compiler'; +import {ConstantPool, EMPTY_SOURCE_SPAN, Expression, Identifiers, ParseError, ParsedHostBindings, R3DependencyMetadata, R3DirectiveMetadata, R3FactoryTarget, R3QueryMetadata, Statement, WrappedNodeExpr, compileDirectiveFromMetadata, makeBindingParser, parseHostBindings, verifyHostBindings} from '@angular/compiler'; import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; @@ -19,7 +19,7 @@ import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFl import {compileNgFactoryDefField} from './factory'; import {generateSetClassMetadataCall} from './metadata'; -import {findAngularDecorator, getValidConstructorDependencies, readBaseClass, unwrapExpression, unwrapForwardRef} from './util'; +import {findAngularDecorator, getConstructorDependencies, readBaseClass, unwrapConstructorDependencies, unwrapExpression, unwrapForwardRef, validateConstructorDependencies} from './util'; const EMPTY_OBJECT: {[key: string]: string} = {}; @@ -89,7 +89,8 @@ export class DirectiveDecoratorHandler implements CompileResult[] { const meta = analysis.meta; const res = compileDirectiveFromMetadata(meta, pool, makeBindingParser()); - const factoryRes = compileNgFactoryDefField({...meta, injectFn: Identifiers.directiveInject}); + const factoryRes = compileNgFactoryDefField( + {...meta, injectFn: Identifiers.directiveInject, target: R3FactoryTarget.Directive}); if (analysis.metadataStmt !== null) { factoryRes.statements.push(analysis.metadataStmt); } @@ -228,11 +229,23 @@ export function extractDirectiveMetadata( exportAs = resolved.split(',').map(part => part.trim()); } + const rawCtorDeps = getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore); + let ctorDeps: R3DependencyMetadata[]|'invalid'|null; + + // Non-abstract directives (those with a selector) require valid constructor dependencies, whereas + // abstract directives are allowed to have invalid dependencies, given that a subclass may call + // the constructor explicitly. + if (selector !== null) { + ctorDeps = validateConstructorDependencies(clazz, rawCtorDeps); + } else { + ctorDeps = unwrapConstructorDependencies(rawCtorDeps); + } + // Detect if the component inherits from another class const usesInheritance = reflector.hasBaseClass(clazz); const metadata: R3DirectiveMetadata = { name: clazz.name.text, - deps: getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore), host, + deps: ctorDeps, host, lifecycle: { usesOnChanges, }, diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/factory.ts b/packages/compiler-cli/src/ngtsc/annotations/src/factory.ts index 28027d7f511bb..0aa284ca7697b 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/factory.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/factory.ts @@ -6,11 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ -import {R3FactoryDefMetadata, compileFactoryFromMetadata} from '@angular/compiler'; +import {R3FactoryMetadata, compileFactoryFunction} from '@angular/compiler'; import {CompileResult} from '../../transform'; -export function compileNgFactoryDefField(metadata: R3FactoryDefMetadata): CompileResult { - const res = compileFactoryFromMetadata(metadata); +export function compileNgFactoryDefField(metadata: R3FactoryMetadata): CompileResult { + const res = compileFactoryFunction(metadata); return {name: 'ɵfac', initializer: res.factory, statements: res.statements, type: res.type}; } diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts b/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts index c1ca25603b266..36dd84b8360e2 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/injectable.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Expression, Identifiers, LiteralExpr, R3DependencyMetadata, R3InjectableMetadata, R3ResolvedDependencyType, Statement, WrappedNodeExpr, compileInjectable as compileIvyInjectable} from '@angular/compiler'; +import {Expression, Identifiers, LiteralExpr, R3DependencyMetadata, R3FactoryTarget, R3InjectableMetadata, R3ResolvedDependencyType, Statement, WrappedNodeExpr, compileInjectable as compileIvyInjectable} from '@angular/compiler'; import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; @@ -16,7 +16,7 @@ import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPr import {compileNgFactoryDefField} from './factory'; import {generateSetClassMetadataCall} from './metadata'; -import {findAngularDecorator, getConstructorDependencies, getValidConstructorDependencies, isAngularCore, unwrapForwardRef, validateConstructorDependencies} from './util'; +import {findAngularDecorator, getConstructorDependencies, getValidConstructorDependencies, isAngularCore, unwrapConstructorDependencies, unwrapForwardRef, validateConstructorDependencies} from './util'; export interface InjectableHandlerData { meta: R3InjectableMetadata; @@ -83,7 +83,8 @@ export class InjectableDecoratorHandler implements type: meta.type, typeArgumentCount: meta.typeArgumentCount, deps: analysis.ctorDeps, - injectFn: Identifiers.inject + injectFn: Identifiers.inject, + target: R3FactoryTarget.Injectable, }); if (analysis.metadataStmt !== null) { factoryRes.statements.push(analysis.metadataStmt); @@ -216,45 +217,25 @@ function extractInjectableCtorDeps( // Angular's DI. // // To deal with this, @Injectable() without an argument is more lenient, and if the - // constructor signature does not work for DI then a provider def (ɵprov) that throws. + // constructor signature does not work for DI then a factory definition (ɵfac) that throws is + // generated. if (strictCtorDeps) { ctorDeps = getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore); } else { - const possibleCtorDeps = - getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore); - if (possibleCtorDeps !== null) { - if (possibleCtorDeps.deps !== null) { - // This use of @Injectable has valid constructor dependencies. - ctorDeps = possibleCtorDeps.deps; - } else { - // This use of @Injectable is technically invalid. Generate a factory function which - // throws - // an error. - // TODO(alxhub): log warnings for the bad use of @Injectable. - ctorDeps = 'invalid'; - } - } + ctorDeps = unwrapConstructorDependencies( + getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore)); } return ctorDeps; } else if (decorator.args.length === 1) { const rawCtorDeps = getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore); - // rawCtorDeps will be null if the class has no constructor. - if (rawCtorDeps !== null) { - if (rawCtorDeps.deps !== null) { - // A constructor existed and had valid dependencies. - ctorDeps = rawCtorDeps.deps; - } else { - // A constructor existed but had invalid dependencies. - ctorDeps = 'invalid'; - } - } - - if (strictCtorDeps && !meta.useValue && !meta.useExisting && !meta.useClass && - !meta.useFactory) { + if (strictCtorDeps && meta.useValue === undefined && meta.useExisting === undefined && + meta.useClass === undefined && meta.useFactory === undefined) { // Since use* was not provided, validate the deps according to strictCtorDeps. - validateConstructorDependencies(clazz, rawCtorDeps); + ctorDeps = validateConstructorDependencies(clazz, rawCtorDeps); + } else { + ctorDeps = unwrapConstructorDependencies(rawCtorDeps); } } diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts b/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts index 0857548a99850..5ae8d93ac7ce7 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Identifiers, R3PipeMetadata, Statement, WrappedNodeExpr, compilePipeFromMetadata} from '@angular/compiler'; +import {Identifiers, R3FactoryTarget, R3PipeMetadata, Statement, WrappedNodeExpr, compilePipeFromMetadata} from '@angular/compiler'; import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; @@ -114,7 +114,7 @@ export class PipeDecoratorHandler implements DecoratorHandler { env.write('test.ts', ` import {Injectable} from '@angular/core'; - @Injectable() + @Injectable({providedIn: 'root'}) export class Test { constructor(private notInjectable: string) {} } @@ -1364,7 +1364,75 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); - expect(jsContents).toMatch(/function Test_Factory\(t\) { throw new Error\(/ms); + expect(jsContents) + .toMatch(/function Test_Factory\(t\) { i0\.ɵɵinvalidFactory\(\)/ms); + }); + + it('should not give a compile-time error if an invalid @Injectable is used with useFactory', + () => { + env.tsconfig({strictInjectionParameters: true}); + env.write('test.ts', ` + import {Injectable} from '@angular/core'; + + @Injectable({ + providedIn: 'root', + useFactory: () => '42', + }) + export class Test { + constructor(private notInjectable: string) {} + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents) + .toMatch(/function Test_Factory\(t\) { i0\.ɵɵinvalidFactory\(\)/ms); + }); + + it('should not give a compile-time error if an invalid @Injectable is used with useExisting', + () => { + env.tsconfig({strictInjectionParameters: true}); + env.write('test.ts', ` + import {Injectable} from '@angular/core'; + + export class MyService {} + + @Injectable({ + providedIn: 'root', + useExisting: MyService, + }) + export class Test { + constructor(private notInjectable: string) {} + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents) + .toMatch(/function Test_Factory\(t\) { i0\.ɵɵinvalidFactory\(\)/ms); + }); + + it('should not give a compile-time error if an invalid @Injectable is used with useClass', + () => { + env.tsconfig({strictInjectionParameters: true}); + env.write('test.ts', ` + import {Injectable} from '@angular/core'; + + export class MyService {} + + @Injectable({ + providedIn: 'root', + useClass: MyService, + }) + export class Test { + constructor(private notInjectable: string) {} + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents) + .toMatch(/function Test_Factory\(t\) { i0\.ɵɵinvalidFactory\(\)/ms); }); }); @@ -1382,7 +1450,8 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('Test.ɵfac = function Test_Factory(t) { throw new Error('); + expect(jsContents) + .toContain('Test.ɵfac = function Test_Factory(t) { i0.ɵɵinvalidFactory()'); }); it('should compile an @Injectable provided in the root on a class with a non-injectable constructor', @@ -1399,40 +1468,98 @@ runInEachFileSystem(os => { env.driveMain(); const jsContents = env.getContents('test.js'); expect(jsContents) - .toContain('Test.ɵfac = function Test_Factory(t) { throw new Error('); + .toContain('Test.ɵfac = function Test_Factory(t) { i0.ɵɵinvalidFactory()'); }); }); }); - describe('templateUrl and styleUrls processing', () => { - const testsForResource = (resource: string) => [ - // [component location, resource location, resource reference] - - // component and resource are in the same folder - [`a/app.ts`, `a/${resource}`, `./${resource}`], // - [`a/app.ts`, `a/${resource}`, resource], // - [`a/app.ts`, `a/${resource}`, `/a/${resource}`], - - // resource is one level up - [`a/app.ts`, resource, `../${resource}`], // - [`a/app.ts`, resource, `/${resource}`], - - // component and resource are in different folders - [`a/app.ts`, `b/${resource}`, `../b/${resource}`], // - [`a/app.ts`, `b/${resource}`, `/b/${resource}`], - - // resource is in subfolder of component directory - [`a/app.ts`, `a/b/c/${resource}`, `./b/c/${resource}`], // - [`a/app.ts`, `a/b/c/${resource}`, `b/c/${resource}`], // - [`a/app.ts`, `a/b/c/${resource}`, `/a/b/c/${resource}`], - ]; - - testsForResource('style.css').forEach((test) => { - const [compLoc, styleLoc, styleRef] = test; - it(`should handle ${styleRef}`, () => { - env.write(styleLoc, ':host { background-color: blue; }'); - env.write(compLoc, ` + describe('compiling invalid @Directives', () => { + describe('directives with a selector', () => { + it('should give a compile-time error if an invalid constructor is used', () => { + env.tsconfig({strictInjectionParameters: true}); + env.write('test.ts', ` + import {Directive} from '@angular/core'; + + @Directive({selector: 'app-test'}) + export class Test { + constructor(private notInjectable: string) {} + } + `); + + const errors = env.driveDiagnostics(); + expect(errors.length).toBe(1); + expect(errors[0].messageText).toContain('No suitable injection token for parameter'); + }); + }); + + describe('abstract directives', () => { + it('should generate a factory function that throws', () => { + env.tsconfig({strictInjectionParameters: false}); + env.write('test.ts', ` + import {Directive} from '@angular/core'; + + @Directive() + export class Test { + constructor(private notInjectable: string) {} + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents) + .toContain('Test.ɵfac = function Test_Factory(t) { i0.ɵɵinvalidFactory()'); + }); + }); + + it('should generate a factory function that throws, even under strictInjectionParameters', + () => { + env.tsconfig({strictInjectionParameters: true}); + env.write('test.ts', ` + import {Directive} from '@angular/core'; + + @Directive() + export class Test { + constructor(private notInjectable: string) {} + } + `); + + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents) + .toContain('Test.ɵfac = function Test_Factory(t) { i0.ɵɵinvalidFactory()'); + }); + }); + }); + + describe('templateUrl and styleUrls processing', () => { + const testsForResource = (resource: string) => [ + // [component location, resource location, resource reference] + + // component and resource are in the same folder + [`a/app.ts`, `a/${resource}`, `./${resource}`], // + [`a/app.ts`, `a/${resource}`, resource], // + [`a/app.ts`, `a/${resource}`, `/a/${resource}`], + + // resource is one level up + [`a/app.ts`, resource, `../${resource}`], // + [`a/app.ts`, resource, `/${resource}`], + + // component and resource are in different folders + [`a/app.ts`, `b/${resource}`, `../b/${resource}`], // + [`a/app.ts`, `b/${resource}`, `/b/${resource}`], + + // resource is in subfolder of component directory + [`a/app.ts`, `a/b/c/${resource}`, `./b/c/${resource}`], // + [`a/app.ts`, `a/b/c/${resource}`, `b/c/${resource}`], // + [`a/app.ts`, `a/b/c/${resource}`, `/a/b/c/${resource}`], + ]; + + testsForResource('style.css').forEach((test) => { + const [compLoc, styleLoc, styleRef] = test; + it(`should handle ${styleRef}`, () => { + env.write(styleLoc, ':host { background-color: blue; }'); + env.write(compLoc, ` import {Component} from '@angular/core'; @Component({ @@ -1443,18 +1570,18 @@ runInEachFileSystem(os => { export class TestCmp {} `); - env.driveMain(); + env.driveMain(); - const jsContents = env.getContents(compLoc.replace('.ts', '.js')); - expect(jsContents).toContain('background-color: blue'); - }); + const jsContents = env.getContents(compLoc.replace('.ts', '.js')); + expect(jsContents).toContain('background-color: blue'); }); + }); - testsForResource('template.html').forEach((test) => { - const [compLoc, templateLoc, templateRef] = test; - it(`should handle ${templateRef}`, () => { - env.write(templateLoc, 'Template Content'); - env.write(compLoc, ` + testsForResource('template.html').forEach((test) => { + const [compLoc, templateLoc, templateRef] = test; + it(`should handle ${templateRef}`, () => { + env.write(templateLoc, 'Template Content'); + env.write(compLoc, ` import {Component} from '@angular/core'; @Component({ @@ -1464,17 +1591,17 @@ runInEachFileSystem(os => { export class TestCmp {} `); - env.driveMain(); + env.driveMain(); - const jsContents = env.getContents(compLoc.replace('.ts', '.js')); - expect(jsContents).toContain('Template Content'); - }); + const jsContents = env.getContents(compLoc.replace('.ts', '.js')); + expect(jsContents).toContain('Template Content'); }); }); + }); - describe('former View Engine AST transform bugs', () => { - it('should compile array literals behind conditionals', () => { - env.write('test.ts', ` + describe('former View Engine AST transform bugs', () => { + it('should compile array literals behind conditionals', () => { + env.write('test.ts', ` import {Component} from '@angular/core'; @Component({ @@ -1487,12 +1614,12 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - expect(env.getContents('test.js')).toContain('i0.ɵɵpureFunction1'); - }); + env.driveMain(); + expect(env.getContents('test.js')).toContain('i0.ɵɵpureFunction1'); + }); - it('should compile array literals inside function arguments', () => { - env.write('test.ts', ` + it('should compile array literals inside function arguments', () => { + env.write('test.ts', ` import {Component} from '@angular/core'; @Component({ @@ -1508,14 +1635,14 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - expect(env.getContents('test.js')).toContain('i0.ɵɵpureFunction1'); - }); + env.driveMain(); + expect(env.getContents('test.js')).toContain('i0.ɵɵpureFunction1'); }); + }); - describe('unwrapping ModuleWithProviders functions', () => { - it('should extract the generic type and include it in the module\'s declaration', () => { - env.write(`test.ts`, ` + describe('unwrapping ModuleWithProviders functions', () => { + it('should extract the generic type and include it in the module\'s declaration', () => { + env.write(`test.ts`, ` import {NgModule} from '@angular/core'; import {RouterModule} from 'router'; @@ -1523,7 +1650,7 @@ runInEachFileSystem(os => { export class TestModule {} `); - env.write('node_modules/router/index.d.ts', ` + env.write('node_modules/router/index.d.ts', ` import {ModuleWithProviders, ɵɵNgModuleDefWithMeta} from '@angular/core'; declare class RouterModule { @@ -1532,20 +1659,20 @@ runInEachFileSystem(os => { } `); - env.driveMain(); + env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]'); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]'); - const dtsContents = env.getContents('test.d.ts'); - expect(dtsContents).toContain(`import * as i1 from "router";`); - expect(dtsContents) - .toContain( - 'i0.ɵɵNgModuleDefWithMeta'); - }); + const dtsContents = env.getContents('test.d.ts'); + expect(dtsContents).toContain(`import * as i1 from "router";`); + expect(dtsContents) + .toContain( + 'i0.ɵɵNgModuleDefWithMeta'); + }); - it('should throw if ModuleWithProviders is missing its generic type argument', () => { - env.write(`test.ts`, ` + it('should throw if ModuleWithProviders is missing its generic type argument', () => { + env.write(`test.ts`, ` import {NgModule} from '@angular/core'; import {RouterModule} from 'router'; @@ -1553,7 +1680,7 @@ runInEachFileSystem(os => { export class TestModule {} `); - env.write('node_modules/router/index.d.ts', ` + env.write('node_modules/router/index.d.ts', ` import {ModuleWithProviders, ɵɵNgModuleDefWithMeta} from '@angular/core'; declare class RouterModule { @@ -1561,16 +1688,16 @@ runInEachFileSystem(os => { static ɵmod: ɵɵNgModuleDefWithMeta; } `); - const errors = env.driveDiagnostics(); - expect(trim(errors[0].messageText as string)) - .toContain( - `RouterModule.forRoot returns a ModuleWithProviders type without a generic type argument. ` + - `Please add a generic type argument to the ModuleWithProviders type. If this ` + - `occurrence is in library code you don't control, please contact the library authors.`); - }); + const errors = env.driveDiagnostics(); + expect(trim(errors[0].messageText as string)) + .toContain( + `RouterModule.forRoot returns a ModuleWithProviders type without a generic type argument. ` + + `Please add a generic type argument to the ModuleWithProviders type. If this ` + + `occurrence is in library code you don't control, please contact the library authors.`); + }); - it('should extract the generic type if it is provided as qualified type name', () => { - env.write(`test.ts`, ` + it('should extract the generic type if it is provided as qualified type name', () => { + env.write(`test.ts`, ` import {NgModule} from '@angular/core'; import {RouterModule} from 'router'; @@ -1578,7 +1705,7 @@ runInEachFileSystem(os => { export class TestModule {} `); - env.write('node_modules/router/index.d.ts', ` + env.write('node_modules/router/index.d.ts', ` import {ModuleWithProviders} from '@angular/core'; import * as internal from './internal'; export {InternalRouterModule} from './internal'; @@ -1589,28 +1716,28 @@ runInEachFileSystem(os => { `); - env.write('node_modules/router/internal.d.ts', ` + env.write('node_modules/router/internal.d.ts', ` import {ɵɵNgModuleDefWithMeta} from '@angular/core'; export declare class InternalRouterModule { static ɵmod: ɵɵNgModuleDefWithMeta; } `); - env.driveMain(); + env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]'); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]'); - const dtsContents = env.getContents('test.d.ts'); - expect(dtsContents).toContain(`import * as i1 from "router";`); - expect(dtsContents) - .toContain( - 'i0.ɵɵNgModuleDefWithMeta'); - }); + const dtsContents = env.getContents('test.d.ts'); + expect(dtsContents).toContain(`import * as i1 from "router";`); + expect(dtsContents) + .toContain( + 'i0.ɵɵNgModuleDefWithMeta'); + }); - it('should not reference a constant with a ModuleWithProviders value in module def imports', - () => { - env.write('dep.d.ts', ` + it('should not reference a constant with a ModuleWithProviders value in module def imports', + () => { + env.write('dep.d.ts', ` import {ModuleWithProviders, ɵɵNgModuleDefWithMeta as ɵɵNgModuleDefWithMeta} from '@angular/core'; export declare class DepModule { @@ -1618,7 +1745,7 @@ runInEachFileSystem(os => { static ɵmod: ɵɵNgModuleDefWithMeta; } `); - env.write('test.ts', ` + env.write('test.ts', ` import {NgModule, ModuleWithProviders} from '@angular/core'; import {DepModule} from './dep'; @@ -1632,15 +1759,15 @@ runInEachFileSystem(os => { }) export class Module {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('imports: [i1.DepModule]'); - }); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('imports: [i1.DepModule]'); + }); + }); - it('should unwrap a ModuleWithProviders-like function if a matching literal type is provided for it', - () => { - env.write(`test.ts`, ` + it('should unwrap a ModuleWithProviders-like function if a matching literal type is provided for it', + () => { + env.write(`test.ts`, ` import {NgModule} from '@angular/core'; import {RouterModule} from 'router'; @@ -1648,7 +1775,7 @@ runInEachFileSystem(os => { export class TestModule {} `); - env.write('node_modules/router/index.d.ts', ` + env.write('node_modules/router/index.d.ts', ` import {ModuleWithProviders, ɵɵNgModuleDefWithMeta} from '@angular/core'; export interface MyType extends ModuleWithProviders {} @@ -1659,21 +1786,21 @@ runInEachFileSystem(os => { } `); - env.driveMain(); + env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]'); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]'); - const dtsContents = env.getContents('test.d.ts'); - expect(dtsContents).toContain(`import * as i1 from "router";`); - expect(dtsContents) - .toContain( - 'i0.ɵɵNgModuleDefWithMeta'); - }); + const dtsContents = env.getContents('test.d.ts'); + expect(dtsContents).toContain(`import * as i1 from "router";`); + expect(dtsContents) + .toContain( + 'i0.ɵɵNgModuleDefWithMeta'); + }); - it('should unwrap a namespace imported ModuleWithProviders function if a generic type is provided for it', - () => { - env.write(`test.ts`, ` + it('should unwrap a namespace imported ModuleWithProviders function if a generic type is provided for it', + () => { + env.write(`test.ts`, ` import {NgModule} from '@angular/core'; import {RouterModule} from 'router'; @@ -1681,7 +1808,7 @@ runInEachFileSystem(os => { export class TestModule {} `); - env.write('node_modules/router/index.d.ts', ` + env.write('node_modules/router/index.d.ts', ` import * as core from '@angular/core'; import {RouterModule} from 'router'; @@ -1691,20 +1818,20 @@ runInEachFileSystem(os => { } `); - env.driveMain(); + env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]'); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('imports: [[RouterModule.forRoot()]]'); - const dtsContents = env.getContents('test.d.ts'); - expect(dtsContents).toContain(`import * as i1 from "router";`); - expect(dtsContents) - .toContain( - 'i0.ɵɵNgModuleDefWithMeta'); - }); + const dtsContents = env.getContents('test.d.ts'); + expect(dtsContents).toContain(`import * as i1 from "router";`); + expect(dtsContents) + .toContain( + 'i0.ɵɵNgModuleDefWithMeta'); + }); - it('should inject special types according to the metadata', () => { - env.write(`test.ts`, ` + it('should inject special types according to the metadata', () => { + env.write(`test.ts`, ` import { Attribute, ChangeDetectorRef, @@ -1733,15 +1860,15 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents) - .toContain( - `FooCmp.ɵfac = function FooCmp_Factory(t) { return new (t || FooCmp)(i0.ɵɵinjectAttribute("test"), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i0.Injector), i0.ɵɵdirectiveInject(i0.Renderer2), i0.ɵɵdirectiveInject(i0.TemplateRef), i0.ɵɵdirectiveInject(i0.ViewContainerRef)); }`); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents) + .toContain( + `FooCmp.ɵfac = function FooCmp_Factory(t) { return new (t || FooCmp)(i0.ɵɵinjectAttribute("test"), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i0.Injector), i0.ɵɵdirectiveInject(i0.Renderer2), i0.ɵɵdirectiveInject(i0.TemplateRef), i0.ɵɵdirectiveInject(i0.ViewContainerRef)); }`); + }); - it('should generate queries for components', () => { - env.write(`test.ts`, ` + it('should generate queries for components', () => { + env.write(`test.ts`, ` import {Component, ContentChild, ContentChildren, TemplateRef, ViewChild} from '@angular/core'; @Component({ @@ -1760,20 +1887,20 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toMatch(varRegExp('bar')); - expect(jsContents).toMatch(varRegExp('test1')); - expect(jsContents).toMatch(varRegExp('test2')); - expect(jsContents).toMatch(varRegExp('accessor')); - // match `i0.ɵɵcontentQuery(dirIndex, _c1, true, TemplateRef)` - expect(jsContents).toMatch(contentQueryRegExp('\\w+', true, 'TemplateRef')); - // match `i0.ɵɵviewQuery(_c2, true, null)` - expect(jsContents).toMatch(viewQueryRegExp(true)); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toMatch(varRegExp('bar')); + expect(jsContents).toMatch(varRegExp('test1')); + expect(jsContents).toMatch(varRegExp('test2')); + expect(jsContents).toMatch(varRegExp('accessor')); + // match `i0.ɵɵcontentQuery(dirIndex, _c1, true, TemplateRef)` + expect(jsContents).toMatch(contentQueryRegExp('\\w+', true, 'TemplateRef')); + // match `i0.ɵɵviewQuery(_c2, true, null)` + expect(jsContents).toMatch(viewQueryRegExp(true)); + }); - it('should generate queries for directives', () => { - env.write(`test.ts`, ` + it('should generate queries for directives', () => { + env.write(`test.ts`, ` import {Directive, ContentChild, ContentChildren, TemplateRef, ViewChild} from '@angular/core'; @Directive({ @@ -1791,23 +1918,23 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toMatch(varRegExp('bar')); - expect(jsContents).toMatch(varRegExp('test1')); - expect(jsContents).toMatch(varRegExp('test2')); - expect(jsContents).toMatch(varRegExp('accessor')); - // match `i0.ɵɵcontentQuery(dirIndex, _c1, true, TemplateRef)` - expect(jsContents).toMatch(contentQueryRegExp('\\w+', true, 'TemplateRef')); - - // match `i0.ɵɵviewQuery(_c2, true)` - // Note that while ViewQuery doesn't necessarily make sense on a directive, because it doesn't - // have a view, we still need to handle it because a component could extend the directive. - expect(jsContents).toMatch(viewQueryRegExp(true)); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toMatch(varRegExp('bar')); + expect(jsContents).toMatch(varRegExp('test1')); + expect(jsContents).toMatch(varRegExp('test2')); + expect(jsContents).toMatch(varRegExp('accessor')); + // match `i0.ɵɵcontentQuery(dirIndex, _c1, true, TemplateRef)` + expect(jsContents).toMatch(contentQueryRegExp('\\w+', true, 'TemplateRef')); + + // match `i0.ɵɵviewQuery(_c2, true)` + // Note that while ViewQuery doesn't necessarily make sense on a directive, because it doesn't + // have a view, we still need to handle it because a component could extend the directive. + expect(jsContents).toMatch(viewQueryRegExp(true)); + }); - it('should handle queries that use forwardRef', () => { - env.write(`test.ts`, ` + it('should handle queries that use forwardRef', () => { + env.write(`test.ts`, ` import {Component, ContentChild, TemplateRef, ViewContainerRef, forwardRef} from '@angular/core'; @Component({ @@ -1823,19 +1950,19 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - // match `i0.ɵɵcontentQuery(dirIndex, TemplateRef, true, null)` - expect(jsContents).toMatch(contentQueryRegExp('TemplateRef', true)); - // match `i0.ɵɵcontentQuery(dirIndex, ViewContainerRef, true, null)` - expect(jsContents).toMatch(contentQueryRegExp('ViewContainerRef', true)); - // match `i0.ɵɵcontentQuery(dirIndex, _c0, true, null)` - expect(jsContents).toContain('_c0 = ["parens"];'); - expect(jsContents).toMatch(contentQueryRegExp('_c0', true)); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + // match `i0.ɵɵcontentQuery(dirIndex, TemplateRef, true, null)` + expect(jsContents).toMatch(contentQueryRegExp('TemplateRef', true)); + // match `i0.ɵɵcontentQuery(dirIndex, ViewContainerRef, true, null)` + expect(jsContents).toMatch(contentQueryRegExp('ViewContainerRef', true)); + // match `i0.ɵɵcontentQuery(dirIndex, _c0, true, null)` + expect(jsContents).toContain('_c0 = ["parens"];'); + expect(jsContents).toMatch(contentQueryRegExp('_c0', true)); + }); - it('should compile expressions that write keys', () => { - env.write(`test.ts`, ` + it('should compile expressions that write keys', () => { + env.write(`test.ts`, ` import {Component, ContentChild, TemplateRef, ViewContainerRef, forwardRef} from '@angular/core'; @Component({ @@ -1848,12 +1975,12 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - expect(env.getContents('test.js')).toContain('test[key] = $event'); - }); + env.driveMain(); + expect(env.getContents('test.js')).toContain('test[key] = $event'); + }); - it('should generate host listeners for components', () => { - env.write(`test.ts`, ` + it('should generate host listeners for components', () => { + env.write(`test.ts`, ` import {Component, HostListener} from '@angular/core'; @Component({ @@ -1872,9 +1999,9 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - const hostBindingsFn = ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + const hostBindingsFn = ` hostBindings: function FooCmp_HostBindings(rf, ctx, elIndex) { if (rf & 1) { i0.ɵɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onClick(); }); @@ -1883,11 +2010,11 @@ runInEachFileSystem(os => { } } `; - expect(trim(jsContents)).toContain(trim(hostBindingsFn)); - }); + expect(trim(jsContents)).toContain(trim(hostBindingsFn)); + }); - it('should throw in case unknown global target is provided', () => { - env.write(`test.ts`, ` + it('should throw in case unknown global target is provided', () => { + env.write(`test.ts`, ` import {Component, HostListener} from '@angular/core'; @Component({ @@ -1899,14 +2026,14 @@ runInEachFileSystem(os => { onClick(event: any): void {} } `); - const errors = env.driveDiagnostics(); - expect(trim(errors[0].messageText as string)) - .toContain( - `Unexpected global target 'UnknownTarget' defined for 'click' event. Supported list of global targets: window,document,body.`); - }); + const errors = env.driveDiagnostics(); + expect(trim(errors[0].messageText as string)) + .toContain( + `Unexpected global target 'UnknownTarget' defined for 'click' event. Supported list of global targets: window,document,body.`); + }); - it('should throw in case pipes are used in host listeners', () => { - env.write(`test.ts`, ` + it('should throw in case pipes are used in host listeners', () => { + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ @@ -1918,13 +2045,13 @@ runInEachFileSystem(os => { }) class FooCmp {} `); - const errors = env.driveDiagnostics(); - expect(trim(errors[0].messageText as string)) - .toContain('Cannot have a pipe in an action expression'); - }); + const errors = env.driveDiagnostics(); + expect(trim(errors[0].messageText as string)) + .toContain('Cannot have a pipe in an action expression'); + }); - it('should throw in case pipes are used in host bindings', () => { - env.write(`test.ts`, ` + it('should throw in case pipes are used in host bindings', () => { + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ @@ -1936,13 +2063,13 @@ runInEachFileSystem(os => { }) class FooCmp {} `); - const errors = env.driveDiagnostics(); - expect(trim(errors[0].messageText as string)) - .toContain('Host binding expression cannot contain pipes'); - }); + const errors = env.driveDiagnostics(); + expect(trim(errors[0].messageText as string)) + .toContain('Host binding expression cannot contain pipes'); + }); - it('should generate host bindings for directives', () => { - env.write(`test.ts`, ` + it('should generate host bindings for directives', () => { + env.write(`test.ts`, ` import {Component, HostBinding, HostListener, TemplateRef} from '@angular/core'; @Component({ @@ -1966,9 +2093,9 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - const hostBindingsFn = ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + const hostBindingsFn = ` hostBindings: function FooCmp_HostBindings(rf, ctx, elIndex) { if (rf & 1) { i0.ɵɵallocHostVars(3); @@ -1983,14 +2110,14 @@ runInEachFileSystem(os => { } } `; - expect(trim(jsContents)).toContain(trim(hostBindingsFn)); - }); + expect(trim(jsContents)).toContain(trim(hostBindingsFn)); + }); - it('should accept dynamic host attribute bindings', () => { - env.write('other.d.ts', ` + it('should accept dynamic host attribute bindings', () => { + env.write('other.d.ts', ` export declare const foo: any; `); - env.write('test.ts', ` + env.write('test.ts', ` import {Component} from '@angular/core'; import {foo} from './other'; @@ -2005,13 +2132,13 @@ runInEachFileSystem(os => { }) export class TestCmp {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('i0.ɵɵelementHostAttrs(["test", test])'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('i0.ɵɵelementHostAttrs(["test", test])'); + }); - it('should accept enum values as host bindings', () => { - env.write(`test.ts`, ` + it('should accept enum values as host bindings', () => { + env.write(`test.ts`, ` import {Component, HostBinding, HostListener, TemplateRef} from '@angular/core'; enum HostBindings { @@ -2030,12 +2157,12 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - expect(env.getContents('test.js')).toContain('i0.ɵɵattribute("hello", ctx.foo)'); - }); + env.driveMain(); + expect(env.getContents('test.js')).toContain('i0.ɵɵattribute("hello", ctx.foo)'); + }); - it('should generate host listeners for directives within hostBindings section', () => { - env.write(`test.ts`, ` + it('should generate host listeners for directives within hostBindings section', () => { + env.write(`test.ts`, ` import {Directive, HostListener} from '@angular/core'; @Directive({ @@ -2047,21 +2174,21 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - const hostBindingsFn = ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + const hostBindingsFn = ` hostBindings: function Dir_HostBindings(rf, ctx, elIndex) { if (rf & 1) { i0.ɵɵlistener("change", function Dir_change_HostBindingHandler($event) { return ctx.onChange(ctx.arg); }); } } `; - expect(trim(jsContents)).toContain(trim(hostBindingsFn)); - }); + expect(trim(jsContents)).toContain(trim(hostBindingsFn)); + }); - it('should use proper default value for preserveWhitespaces config param', () => { - env.tsconfig(); // default is `false` - env.write(`test.ts`, ` + it('should use proper default value for preserveWhitespaces config param', () => { + env.tsconfig(); // default is `false` + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'test', @@ -2074,14 +2201,14 @@ runInEachFileSystem(os => { }) class FooCmp {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('text(1, " Template with whitespaces ");'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('text(1, " Template with whitespaces ");'); + }); - it('should take preserveWhitespaces config option into account', () => { - env.tsconfig({preserveWhitespaces: true}); - env.write(`test.ts`, ` + it('should take preserveWhitespaces config option into account', () => { + env.tsconfig({preserveWhitespaces: true}); + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'test', @@ -2093,15 +2220,15 @@ runInEachFileSystem(os => { }) class FooCmp {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents) - .toContain('text(2, "\\n Template with whitespaces\\n ");'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents) + .toContain('text(2, "\\n Template with whitespaces\\n ");'); + }); - it('@Component\'s preserveWhitespaces should override the one defined in config', () => { - env.tsconfig({preserveWhitespaces: true}); - env.write(`test.ts`, ` + it('@Component\'s preserveWhitespaces should override the one defined in config', () => { + env.tsconfig({preserveWhitespaces: true}); + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'test', @@ -2114,14 +2241,14 @@ runInEachFileSystem(os => { }) class FooCmp {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('text(1, " Template with whitespaces ");'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('text(1, " Template with whitespaces ");'); + }); - it('should use proper default value for i18nUseExternalIds config param', () => { - env.tsconfig(); // default is `true` - env.write(`test.ts`, ` + it('should use proper default value for i18nUseExternalIds config param', () => { + env.tsconfig(); // default is `true` + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'test', @@ -2129,14 +2256,14 @@ runInEachFileSystem(os => { }) class FooCmp {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('MSG_EXTERNAL_8321000940098097247$$TEST_TS_1'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('MSG_EXTERNAL_8321000940098097247$$TEST_TS_1'); + }); - it('should take i18nUseExternalIds config option into account', () => { - env.tsconfig({i18nUseExternalIds: false}); - env.write(`test.ts`, ` + it('should take i18nUseExternalIds config option into account', () => { + env.tsconfig({i18nUseExternalIds: false}); + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'test', @@ -2144,135 +2271,135 @@ runInEachFileSystem(os => { }) class FooCmp {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).not.toContain('MSG_EXTERNAL_'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).not.toContain('MSG_EXTERNAL_'); + }); - it('should render legacy id when `enableI18nLegacyMessageIdFormat` is not false and `i18nInFormat` is set to "xlf"', - () => { - env.tsconfig({i18nInFormat: 'xlf'}); - env.write(`test.ts`, ` + it('should render legacy id when `enableI18nLegacyMessageIdFormat` is not false and `i18nInFormat` is set to "xlf"', + () => { + env.tsconfig({i18nInFormat: 'xlf'}); + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'test', template: '
Some text
' }) class FooCmp {}`); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain(':@@5dbba0a3da8dff890e20cf76eb075d58900fbcd3:Some text'); - }); - - it('should render legacy id when `enableI18nLegacyMessageIdFormat` is not false and `i18nInFormat` is set to "xliff"', - () => { - env.tsconfig({i18nInFormat: 'xliff'}); - env.write(`test.ts`, ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain(':@@5dbba0a3da8dff890e20cf76eb075d58900fbcd3:Some text'); + }); + + it('should render legacy id when `enableI18nLegacyMessageIdFormat` is not false and `i18nInFormat` is set to "xliff"', + () => { + env.tsconfig({i18nInFormat: 'xliff'}); + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'test', template: '
Some text
' }) class FooCmp {}`); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain(':@@5dbba0a3da8dff890e20cf76eb075d58900fbcd3:Some text'); - }); - - it('should render legacy id when `enableI18nLegacyMessageIdFormat` is not false and `i18nInFormat` is set to "xlf2"', - () => { - env.tsconfig({i18nInFormat: 'xlf2'}); - env.write(`test.ts`, ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain(':@@5dbba0a3da8dff890e20cf76eb075d58900fbcd3:Some text'); + }); + + it('should render legacy id when `enableI18nLegacyMessageIdFormat` is not false and `i18nInFormat` is set to "xlf2"', + () => { + env.tsconfig({i18nInFormat: 'xlf2'}); + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'test', template: '
Some text
' }) class FooCmp {}`); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain(':@@8321000940098097247:Some text'); - }); - - it('should render legacy id when `enableI18nLegacyMessageIdFormat` is not false and `i18nInFormat` is set to "xliff2"', - () => { - env.tsconfig({i18nInFormat: 'xliff2'}); - env.write(`test.ts`, ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain(':@@8321000940098097247:Some text'); + }); + + it('should render legacy id when `enableI18nLegacyMessageIdFormat` is not false and `i18nInFormat` is set to "xliff2"', + () => { + env.tsconfig({i18nInFormat: 'xliff2'}); + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'test', template: '
Some text
' }) class FooCmp {}`); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain(':@@8321000940098097247:Some text'); - }); - - it('should render legacy id when `enableI18nLegacyMessageIdFormat` is not false and `i18nInFormat` is set to "xmb"', - () => { - env.tsconfig({i18nInFormat: 'xmb'}); - env.write(`test.ts`, ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain(':@@8321000940098097247:Some text'); + }); + + it('should render legacy id when `enableI18nLegacyMessageIdFormat` is not false and `i18nInFormat` is set to "xmb"', + () => { + env.tsconfig({i18nInFormat: 'xmb'}); + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'test', template: '
Some text
' }) class FooCmp {}`); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain(':@@8321000940098097247:Some text'); - }); - - it('should render custom id even if `enableI18nLegacyMessageIdFormat` is not false and `i18nInFormat` is set', - () => { - env.tsconfig({i18nFormatIn: 'xlf'}); - env.write(`test.ts`, ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain(':@@8321000940098097247:Some text'); + }); + + it('should render custom id even if `enableI18nLegacyMessageIdFormat` is not false and `i18nInFormat` is set', + () => { + env.tsconfig({i18nFormatIn: 'xlf'}); + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'test', template: '
Some text
' }) class FooCmp {}`); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain(':@@custom:Some text'); - }); - - it('should not render legacy id when `enableI18nLegacyMessageIdFormat` is set to false', () => { - env.tsconfig({enableI18nLegacyMessageIdFormat: false, i18nInFormat: 'xmb'}); - env.write(`test.ts`, ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain(':@@custom:Some text'); + }); + + it('should not render legacy id when `enableI18nLegacyMessageIdFormat` is set to false', () => { + env.tsconfig({enableI18nLegacyMessageIdFormat: false, i18nInFormat: 'xmb'}); + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'test', template: '
Some text
' }) class FooCmp {}`); - env.driveMain(); - const jsContents = env.getContents('test.js'); - // Note that the colon would only be there if there is an id attached to the string. - expect(jsContents).not.toContain(':Some text'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + // Note that the colon would only be there if there is an id attached to the string. + expect(jsContents).not.toContain(':Some text'); + }); - it('should also render legacy id for ICUs when normal messages are using legacy ids', () => { - env.tsconfig({i18nInFormat: 'xliff'}); - env.write(`test.ts`, ` + it('should also render legacy id for ICUs when normal messages are using legacy ids', () => { + env.tsconfig({i18nInFormat: 'xliff'}); + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'test', template: '
Some text {age, plural, 10 {ten} other {other}}
' }) class FooCmp {}`); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents) - .toContain( - ':@@720ba589d043a0497ac721ff972f41db0c919efb:{VAR_PLURAL, plural, 10 {ten} other {other}}'); - expect(jsContents).toContain(':@@custom:Some text'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents) + .toContain( + ':@@720ba589d043a0497ac721ff972f41db0c919efb:{VAR_PLURAL, plural, 10 {ten} other {other}}'); + expect(jsContents).toContain(':@@custom:Some text'); + }); - it('@Component\'s `interpolation` should override default interpolation config', () => { - env.write(`test.ts`, ` + it('@Component\'s `interpolation` should override default interpolation config', () => { + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'cmp-with-custom-interpolation-a', @@ -2284,13 +2411,13 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('ɵɵtextInterpolate(ctx.text)'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('ɵɵtextInterpolate(ctx.text)'); + }); - it('should handle `encapsulation` field', () => { - env.write(`test.ts`, ` + it('should handle `encapsulation` field', () => { + env.write(`test.ts`, ` import {Component, ViewEncapsulation} from '@angular/core'; @Component({ selector: 'comp-a', @@ -2300,13 +2427,13 @@ runInEachFileSystem(os => { class CompA {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('encapsulation: 2'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('encapsulation: 2'); + }); - it('should throw if `encapsulation` contains invalid value', () => { - env.write('test.ts', ` + it('should throw if `encapsulation` contains invalid value', () => { + env.write('test.ts', ` import {Component} from '@angular/core'; @Component({ selector: 'comp-a', @@ -2315,13 +2442,13 @@ runInEachFileSystem(os => { }) class CompA {} `); - const errors = env.driveDiagnostics(); - expect(errors[0].messageText) - .toContain('encapsulation must be a member of ViewEncapsulation enum from @angular/core'); - }); + const errors = env.driveDiagnostics(); + expect(errors[0].messageText) + .toContain('encapsulation must be a member of ViewEncapsulation enum from @angular/core'); + }); - it('should handle `changeDetection` field', () => { - env.write(`test.ts`, ` + it('should handle `changeDetection` field', () => { + env.write(`test.ts`, ` import {Component, ChangeDetectionStrategy} from '@angular/core'; @Component({ selector: 'comp-a', @@ -2331,13 +2458,13 @@ runInEachFileSystem(os => { class CompA {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('changeDetection: 0'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('changeDetection: 0'); + }); - it('should throw if `changeDetection` contains invalid value', () => { - env.write('test.ts', ` + it('should throw if `changeDetection` contains invalid value', () => { + env.write('test.ts', ` import {Component} from '@angular/core'; @Component({ selector: 'comp-a', @@ -2346,14 +2473,14 @@ runInEachFileSystem(os => { }) class CompA {} `); - const errors = env.driveDiagnostics(); - expect(errors[0].messageText) - .toContain( - 'changeDetection must be a member of ChangeDetectionStrategy enum from @angular/core'); - }); + const errors = env.driveDiagnostics(); + expect(errors[0].messageText) + .toContain( + 'changeDetection must be a member of ChangeDetectionStrategy enum from @angular/core'); + }); - it('should ignore empty bindings', () => { - env.write(`test.ts`, ` + it('should ignore empty bindings', () => { + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ selector: 'test', @@ -2361,13 +2488,13 @@ runInEachFileSystem(os => { }) class FooCmp {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).not.toContain('i0.ɵɵproperty'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).not.toContain('i0.ɵɵproperty'); + }); - it('should correctly recognize local symbols', () => { - env.write('module.ts', ` + it('should correctly recognize local symbols', () => { + env.write('module.ts', ` import {NgModule} from '@angular/core'; import {Dir, Comp} from './test'; @@ -2377,7 +2504,7 @@ runInEachFileSystem(os => { }) class Module {} `); - env.write(`test.ts`, ` + env.write(`test.ts`, ` import {Component, Directive} from '@angular/core'; @Directive({ @@ -2392,13 +2519,13 @@ runInEachFileSystem(os => { export class Comp {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).not.toMatch(/import \* as i[0-9] from ['"].\/test['"]/); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).not.toMatch(/import \* as i[0-9] from ['"].\/test['"]/); + }); - it('should generate exportAs declarations', () => { - env.write('test.ts', ` + it('should generate exportAs declarations', () => { + env.write('test.ts', ` import {Component, Directive} from '@angular/core'; @Directive({ @@ -2408,14 +2535,14 @@ runInEachFileSystem(os => { class Dir {} `); - env.driveMain(); + env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain(`exportAs: ["foo"]`); - }); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain(`exportAs: ["foo"]`); + }); - it('should generate multiple exportAs declarations', () => { - env.write('test.ts', ` + it('should generate multiple exportAs declarations', () => { + env.write('test.ts', ` import {Component, Directive} from '@angular/core'; @Directive({ @@ -2425,16 +2552,16 @@ runInEachFileSystem(os => { class Dir {} `); - env.driveMain(); + env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain(`exportAs: ["foo", "bar"]`); - }); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain(`exportAs: ["foo", "bar"]`); + }); - it('should generate correct factory stubs for a test module', () => { - env.tsconfig({'generateNgFactoryShims': true}); + it('should generate correct factory stubs for a test module', () => { + env.tsconfig({'generateNgFactoryShims': true}); - env.write('test.ts', ` + env.write('test.ts', ` import {Injectable, NgModule} from '@angular/core'; @Injectable() @@ -2444,33 +2571,33 @@ runInEachFileSystem(os => { export class TestModule {} `); - env.write('empty.ts', ` + env.write('empty.ts', ` import {Injectable} from '@angular/core'; @Injectable() export class NotAModule {} `); - env.driveMain(); + env.driveMain(); - const factoryContents = env.getContents('test.ngfactory.js'); - expect(factoryContents).toContain(`import * as i0 from '@angular/core';`); - expect(factoryContents).toContain(`import { NotAModule, TestModule } from './test';`); - expect(factoryContents) - .toContain(`export var TestModuleNgFactory = new i0.\u0275NgModuleFactory(TestModule);`); - expect(factoryContents).not.toContain(`NotAModuleNgFactory`); - expect(factoryContents).not.toContain('\u0275NonEmptyModule'); - - const emptyFactory = env.getContents('empty.ngfactory.js'); - expect(emptyFactory).toContain(`import * as i0 from '@angular/core';`); - expect(emptyFactory).toContain(`export var \u0275NonEmptyModule = true;`); - }); + const factoryContents = env.getContents('test.ngfactory.js'); + expect(factoryContents).toContain(`import * as i0 from '@angular/core';`); + expect(factoryContents).toContain(`import { NotAModule, TestModule } from './test';`); + expect(factoryContents) + .toContain(`export var TestModuleNgFactory = new i0.\u0275NgModuleFactory(TestModule);`); + expect(factoryContents).not.toContain(`NotAModuleNgFactory`); + expect(factoryContents).not.toContain('\u0275NonEmptyModule'); - describe('ngfactory shims', () => { - beforeEach(() => { env.tsconfig({'generateNgFactoryShims': true}); }); + const emptyFactory = env.getContents('empty.ngfactory.js'); + expect(emptyFactory).toContain(`import * as i0 from '@angular/core';`); + expect(emptyFactory).toContain(`export var \u0275NonEmptyModule = true;`); + }); - it('should generate correct type annotation for NgModuleFactory calls in ngfactories', () => { - env.write('test.ts', ` + describe('ngfactory shims', () => { + beforeEach(() => { env.tsconfig({'generateNgFactoryShims': true}); }); + + it('should generate correct type annotation for NgModuleFactory calls in ngfactories', () => { + env.write('test.ts', ` import {Component} from '@angular/core'; @Component({ selector: 'test', @@ -2478,74 +2605,74 @@ runInEachFileSystem(os => { }) export class TestCmp {} `); - env.driveMain(); + env.driveMain(); - const ngfactoryContents = env.getContents('test.ngfactory.d.ts'); - expect(ngfactoryContents).toContain(`i0.ɵNgModuleFactory`); - }); + const ngfactoryContents = env.getContents('test.ngfactory.d.ts'); + expect(ngfactoryContents).toContain(`i0.ɵNgModuleFactory`); + }); - it('should copy a top-level comment into a factory stub', () => { - env.tsconfig({'allowEmptyCodegenFiles': true}); + it('should copy a top-level comment into a factory stub', () => { + env.tsconfig({'allowEmptyCodegenFiles': true}); - env.write('test.ts', `/** I am a top-level comment. */ + env.write('test.ts', `/** I am a top-level comment. */ import {NgModule} from '@angular/core'; @NgModule({}) export class TestModule {} `); - env.driveMain(); + env.driveMain(); - const factoryContents = env.getContents('test.ngfactory.js'); - expect(factoryContents).toMatch(/^\/\*\* I am a top-level comment\. \*\//); - }); + const factoryContents = env.getContents('test.ngfactory.js'); + expect(factoryContents).toMatch(/^\/\*\* I am a top-level comment\. \*\//); + }); - it('should be able to compile an app using the factory shim', () => { - env.tsconfig({'allowEmptyCodegenFiles': true}); + it('should be able to compile an app using the factory shim', () => { + env.tsconfig({'allowEmptyCodegenFiles': true}); - env.write('test.ts', ` + env.write('test.ts', ` export {MyModuleNgFactory} from './my-module.ngfactory'; `); - env.write('my-module.ts', ` + env.write('my-module.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class MyModule {} `); - env.driveMain(); - }); + env.driveMain(); + }); - it('should generate correct imports in factory stubs when compiling @angular/core', () => { - env.tsconfig({'allowEmptyCodegenFiles': true}); + it('should generate correct imports in factory stubs when compiling @angular/core', () => { + env.tsconfig({'allowEmptyCodegenFiles': true}); - env.write('test.ts', ` + env.write('test.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class TestModule {} `); - // Trick the compiler into thinking it's compiling @angular/core. - env.write('r3_symbols.ts', 'export const ITS_JUST_ANGULAR = true;'); + // Trick the compiler into thinking it's compiling @angular/core. + env.write('r3_symbols.ts', 'export const ITS_JUST_ANGULAR = true;'); - env.driveMain(); + env.driveMain(); - const factoryContents = env.getContents('test.ngfactory.js'); - expect(normalize(factoryContents)).toBe(normalize(` + const factoryContents = env.getContents('test.ngfactory.js'); + expect(normalize(factoryContents)).toBe(normalize(` import * as i0 from "./r3_symbols"; import { TestModule } from './test'; export var TestModuleNgFactory = new i0.NgModuleFactory(TestModule); `)); - }); }); + }); - describe('ngsummary shim generation', () => { - beforeEach(() => { env.tsconfig({'generateNgSummaryShims': true}); }); + describe('ngsummary shim generation', () => { + beforeEach(() => { env.tsconfig({'generateNgSummaryShims': true}); }); - it('should generate a summary stub for decorated classes in the input file only', () => { - env.write('test.ts', ` + it('should generate a summary stub for decorated classes in the input file only', () => { + env.write('test.ts', ` import {Injectable, NgModule} from '@angular/core'; export class NotAModule {} @@ -2554,14 +2681,14 @@ runInEachFileSystem(os => { export class TestModule {} `); - env.driveMain(); + env.driveMain(); - const summaryContents = env.getContents('test.ngsummary.js'); - expect(summaryContents).toEqual(`export var TestModuleNgSummary = null;\n`); - }); + const summaryContents = env.getContents('test.ngsummary.js'); + expect(summaryContents).toEqual(`export var TestModuleNgSummary = null;\n`); + }); - it('should generate a summary stub for classes exported via exports', () => { - env.write('test.ts', ` + it('should generate a summary stub for classes exported via exports', () => { + env.write('test.ts', ` import {Injectable, NgModule} from '@angular/core'; @NgModule({}) @@ -2570,29 +2697,29 @@ runInEachFileSystem(os => { export {NotDirectlyExported}; `); - env.driveMain(); + env.driveMain(); - const summaryContents = env.getContents('test.ngsummary.js'); - expect(summaryContents).toEqual(`export var NotDirectlyExportedNgSummary = null;\n`); - }); + const summaryContents = env.getContents('test.ngsummary.js'); + expect(summaryContents).toEqual(`export var NotDirectlyExportedNgSummary = null;\n`); + }); - it('it should generate empty export when there are no other summary symbols, to ensure the output is a valid ES module', - () => { - env.write('empty.ts', ` + it('it should generate empty export when there are no other summary symbols, to ensure the output is a valid ES module', + () => { + env.write('empty.ts', ` export class NotAModule {} `); - env.driveMain(); + env.driveMain(); - const emptySummary = env.getContents('empty.ngsummary.js'); - // The empty export ensures this js file is still an ES module. - expect(emptySummary).toEqual(`export var \u0275empty = null;\n`); - }); - }); + const emptySummary = env.getContents('empty.ngsummary.js'); + // The empty export ensures this js file is still an ES module. + expect(emptySummary).toEqual(`export var \u0275empty = null;\n`); + }); + }); - it('should compile a banana-in-a-box inside of a template', () => { - env.write('test.ts', ` + it('should compile a banana-in-a-box inside of a template', () => { + env.write('test.ts', ` import {Component} from '@angular/core'; @Component({ @@ -2602,11 +2729,11 @@ runInEachFileSystem(os => { class TestCmp {} `); - env.driveMain(); - }); + env.driveMain(); + }); - it('generates inherited factory definitions', () => { - env.write(`test.ts`, ` + it('generates inherited factory definitions', () => { + env.write(`test.ts`, ` import {Injectable} from '@angular/core'; class Dep {} @@ -2628,20 +2755,20 @@ runInEachFileSystem(os => { `); - env.driveMain(); - const jsContents = env.getContents('test.js'); + env.driveMain(); + const jsContents = env.getContents('test.js'); - expect(jsContents) - .toContain('function Base_Factory(t) { return new (t || Base)(i0.ɵɵinject(Dep)); }'); - expect(jsContents).toContain('var \u0275Child_BaseFactory = i0.ɵɵgetInheritedFactory(Child)'); - expect(jsContents) - .toContain('function Child_Factory(t) { return \u0275Child_BaseFactory(t || Child); }'); - expect(jsContents) - .toContain('function GrandChild_Factory(t) { return new (t || GrandChild)(); }'); - }); + expect(jsContents) + .toContain('function Base_Factory(t) { return new (t || Base)(i0.ɵɵinject(Dep)); }'); + expect(jsContents).toContain('var \u0275Child_BaseFactory = i0.ɵɵgetInheritedFactory(Child)'); + expect(jsContents) + .toContain('function Child_Factory(t) { return \u0275Child_BaseFactory(t || Child); }'); + expect(jsContents) + .toContain('function GrandChild_Factory(t) { return new (t || GrandChild)(); }'); + }); - it('generates base factories for directives', () => { - env.write(`test.ts`, ` + it('generates base factories for directives', () => { + env.write(`test.ts`, ` import {Directive} from '@angular/core'; class Base {} @@ -2654,15 +2781,15 @@ runInEachFileSystem(os => { `); - env.driveMain(); - const jsContents = env.getContents('test.js'); + env.driveMain(); + const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('var \u0275Dir_BaseFactory = i0.ɵɵgetInheritedFactory(Dir)'); - }); + expect(jsContents).toContain('var \u0275Dir_BaseFactory = i0.ɵɵgetInheritedFactory(Dir)'); + }); - it('should wrap "directives" in component metadata in a closure when forward references are present', - () => { - env.write('test.ts', ` + it('should wrap "directives" in component metadata in a closure when forward references are present', + () => { + env.write('test.ts', ` import {Component, NgModule} from '@angular/core'; @Component({ @@ -2683,14 +2810,14 @@ runInEachFileSystem(os => { class Module {} `); - env.driveMain(); + env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('directives: function () { return [CmpB]; }'); - }); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('directives: function () { return [CmpB]; }'); + }); - it('should emit setClassMetadata calls for all types', () => { - env.write('test.ts', ` + it('should emit setClassMetadata calls for all types', () => { + env.write('test.ts', ` import {Component, Directive, Injectable, NgModule, Pipe} from '@angular/core'; @Component({selector: 'cmp', template: 'I am a component!'}) class TestComponent {} @@ -2700,22 +2827,22 @@ runInEachFileSystem(os => { @Pipe({name: 'pipe'}) class TestPipe {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('\u0275setClassMetadata(TestComponent, '); - expect(jsContents).toContain('\u0275setClassMetadata(TestDirective, '); - expect(jsContents).toContain('\u0275setClassMetadata(TestInjectable, '); - expect(jsContents).toContain('\u0275setClassMetadata(TestNgModule, '); - expect(jsContents).toContain('\u0275setClassMetadata(TestPipe, '); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('\u0275setClassMetadata(TestComponent, '); + expect(jsContents).toContain('\u0275setClassMetadata(TestDirective, '); + expect(jsContents).toContain('\u0275setClassMetadata(TestInjectable, '); + expect(jsContents).toContain('\u0275setClassMetadata(TestNgModule, '); + expect(jsContents).toContain('\u0275setClassMetadata(TestPipe, '); + }); - it('should use imported types in setClassMetadata if they can be represented as values', () => { + it('should use imported types in setClassMetadata if they can be represented as values', () => { - env.write(`types.ts`, ` + env.write(`types.ts`, ` export class MyTypeA {} export class MyTypeB {} `); - env.write(`test.ts`, ` + env.write(`test.ts`, ` import {Component, Inject, Injectable} from '@angular/core'; import {MyTypeA, MyTypeB} from './types'; @@ -2733,21 +2860,21 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - const jsContents = trim(env.getContents('test.js')); - expect(jsContents).toContain(`import * as i1 from "./types";`); - expect(jsContents).toMatch(setClassMetadataRegExp('type: i1\\.MyTypeA')); - expect(jsContents).toMatch(setClassMetadataRegExp('type: i1\\.MyTypeB')); - }); + env.driveMain(); + const jsContents = trim(env.getContents('test.js')); + expect(jsContents).toContain(`import * as i1 from "./types";`); + expect(jsContents).toMatch(setClassMetadataRegExp('type: i1\\.MyTypeA')); + expect(jsContents).toMatch(setClassMetadataRegExp('type: i1\\.MyTypeB')); + }); - it('should use imported types in setClassMetadata if they can be represented as values and imported as `* as foo`', - () => { + it('should use imported types in setClassMetadata if they can be represented as values and imported as `* as foo`', + () => { - env.write(`types.ts`, ` + env.write(`types.ts`, ` export class MyTypeA {} export class MyTypeB {} `); - env.write(`test.ts`, ` + env.write(`test.ts`, ` import {Component, Inject, Injectable} from '@angular/core'; import * as types from './types'; @@ -2765,20 +2892,20 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - const jsContents = trim(env.getContents('test.js')); - expect(jsContents).toContain(`import * as i1 from "./types";`); - expect(jsContents).toMatch(setClassMetadataRegExp('type: i1.MyTypeA')); - expect(jsContents).toMatch(setClassMetadataRegExp('type: i1.MyTypeB')); - }); + env.driveMain(); + const jsContents = trim(env.getContents('test.js')); + expect(jsContents).toContain(`import * as i1 from "./types";`); + expect(jsContents).toMatch(setClassMetadataRegExp('type: i1.MyTypeA')); + expect(jsContents).toMatch(setClassMetadataRegExp('type: i1.MyTypeB')); + }); - it('should use default-imported types if they can be represented as values', () => { + it('should use default-imported types if they can be represented as values', () => { - env.write(`types.ts`, ` + env.write(`types.ts`, ` export default class Default {} export class Other {} `); - env.write(`test.ts`, ` + env.write(`test.ts`, ` import {Component} from '@angular/core'; import {Other} from './types'; import Default from './types'; @@ -2789,23 +2916,22 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - const jsContents = trim(env.getContents('test.js')); - expect(jsContents).toContain(`import Default from './types';`); - expect(jsContents).toContain(`import * as i1 from "./types";`); - expect(jsContents).toContain('i0.ɵɵdirectiveInject(Default)'); - expect(jsContents).toContain('i0.ɵɵdirectiveInject(i1.Other)'); - expect(jsContents).toMatch(setClassMetadataRegExp('type: Default')); - expect(jsContents).toMatch(setClassMetadataRegExp('type: i1.Other')); - }); + env.driveMain(); + const jsContents = trim(env.getContents('test.js')); + expect(jsContents).toContain(`import Default from './types';`); + expect(jsContents).toContain(`import * as i1 from "./types";`); + expect(jsContents).toContain('i0.ɵɵdirectiveInject(Default)'); + expect(jsContents).toContain('i0.ɵɵdirectiveInject(i1.Other)'); + expect(jsContents).toMatch(setClassMetadataRegExp('type: Default')); + expect(jsContents).toMatch(setClassMetadataRegExp('type: i1.Other')); + }); - it('should use `undefined` in setClassMetadata if types can\'t be represented as values', - () => { + it('should use `undefined` in setClassMetadata if types can\'t be represented as values', () => { - env.write(`types.ts`, ` + env.write(`types.ts`, ` export type MyType = Map; `); - env.write(`test.ts`, ` + env.write(`test.ts`, ` import {Component, Inject, Injectable} from '@angular/core'; import {MyType} from './types'; @@ -2818,16 +2944,16 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - const jsContents = trim(env.getContents('test.js')); - expect(jsContents).not.toContain(`import { MyType } from './types';`); - // Note: `type: undefined` below, since MyType can't be represented as a value - expect(jsContents).toMatch(setClassMetadataRegExp('type: undefined')); - }); + env.driveMain(); + const jsContents = trim(env.getContents('test.js')); + expect(jsContents).not.toContain(`import { MyType } from './types';`); + // Note: `type: undefined` below, since MyType can't be represented as a value + expect(jsContents).toMatch(setClassMetadataRegExp('type: undefined')); + }); - it('should not throw in case whitespaces and HTML comments are present inside ', - () => { - env.write('test.ts', ` + it('should not throw in case whitespaces and HTML comments are present inside ', + () => { + env.write('test.ts', ` import {Component} from '@angular/core'; @Component({ @@ -2840,12 +2966,12 @@ runInEachFileSystem(os => { }) class CmpA {} `); - const errors = env.driveDiagnostics(); - expect(errors.length).toBe(0); - }); + const errors = env.driveDiagnostics(); + expect(errors.length).toBe(0); + }); - it('should compile a template using multiple directives with the same selector', () => { - env.write('test.ts', ` + it('should compile a template using multiple directives with the same selector', () => { + env.write('test.ts', ` import {Component, Directive, NgModule} from '@angular/core'; @Directive({selector: '[test]'}) @@ -2865,14 +2991,14 @@ runInEachFileSystem(os => { class Module {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toMatch(/directives: \[DirA,\s+DirB\]/); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toMatch(/directives: \[DirA,\s+DirB\]/); + }); - describe('cycle detection', () => { - it('should detect a simple cycle and use remote component scoping', () => { - env.write('test.ts', ` + describe('cycle detection', () => { + it('should detect a simple cycle and use remote component scoping', () => { + env.write('test.ts', ` import {Component, NgModule} from '@angular/core'; import {NormalComponent} from './cyclic'; @@ -2888,7 +3014,7 @@ runInEachFileSystem(os => { export class Module {} `); - env.write('cyclic.ts', ` + env.write('cyclic.ts', ` import {Component} from '@angular/core'; @Component({ @@ -2898,16 +3024,16 @@ runInEachFileSystem(os => { export class NormalComponent {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents) - .toMatch( - /i\d\.ɵɵsetComponentScope\(NormalComponent,\s+\[NormalComponent,\s+CyclicComponent\],\s+\[\]\)/); - expect(jsContents).not.toContain('/*__PURE__*/ i0.ɵɵsetComponentScope'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents) + .toMatch( + /i\d\.ɵɵsetComponentScope\(NormalComponent,\s+\[NormalComponent,\s+CyclicComponent\],\s+\[\]\)/); + expect(jsContents).not.toContain('/*__PURE__*/ i0.ɵɵsetComponentScope'); + }); - it('should detect a cycle added entirely during compilation', () => { - env.write('test.ts', ` + it('should detect a cycle added entirely during compilation', () => { + env.write('test.ts', ` import {NgModule} from '@angular/core'; import {ACmp} from './a'; import {BCmp} from './b'; @@ -2915,7 +3041,7 @@ runInEachFileSystem(os => { @NgModule({declarations: [ACmp, BCmp]}) export class Module {} `); - env.write('a.ts', ` + env.write('a.ts', ` import {Component} from '@angular/core'; @Component({ @@ -2924,7 +3050,7 @@ runInEachFileSystem(os => { }) export class ACmp {} `); - env.write('b.ts', ` + env.write('b.ts', ` import {Component} from '@angular/core'; @Component({ @@ -2933,15 +3059,15 @@ runInEachFileSystem(os => { }) export class BCmp {} `); - env.driveMain(); - const aJsContents = env.getContents('a.js'); - const bJsContents = env.getContents('b.js'); - expect(aJsContents).toMatch(/import \* as i\d? from ".\/b"/); - expect(bJsContents).not.toMatch(/import \* as i\d? from ".\/a"/); - }); + env.driveMain(); + const aJsContents = env.getContents('a.js'); + const bJsContents = env.getContents('b.js'); + expect(aJsContents).toMatch(/import \* as i\d? from ".\/b"/); + expect(bJsContents).not.toMatch(/import \* as i\d? from ".\/a"/); + }); - it('should not detect a potential cycle if it doesn\'t actually happen', () => { - env.write('test.ts', ` + it('should not detect a potential cycle if it doesn\'t actually happen', () => { + env.write('test.ts', ` import {NgModule} from '@angular/core'; import {ACmp} from './a'; import {BCmp} from './b'; @@ -2949,7 +3075,7 @@ runInEachFileSystem(os => { @NgModule({declarations: [ACmp, BCmp]}) export class Module {} `); - env.write('a.ts', ` + env.write('a.ts', ` import {Component} from '@angular/core'; @Component({ @@ -2958,7 +3084,7 @@ runInEachFileSystem(os => { }) export class ACmp {} `); - env.write('b.ts', ` + env.write('b.ts', ` import {Component} from '@angular/core'; @Component({ @@ -2967,14 +3093,14 @@ runInEachFileSystem(os => { }) export class BCmp {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).not.toContain('setComponentScope'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).not.toContain('setComponentScope'); }); + }); - describe('multiple local refs', () => { - const getComponentScript = (template: string): string => ` + describe('multiple local refs', () => { + const getComponentScript = (template: string): string => ` import {Component, Directive, NgModule} from '@angular/core'; @Component({selector: 'my-cmp', template: \`${template}\`}) @@ -2984,44 +3110,44 @@ runInEachFileSystem(os => { class Module {} `; - const cases = [ - ` + const cases = [ + `
`, - ` + `
`, - ` + `
`, - ` + `
`, - ` + `
` - ]; + ]; - cases.forEach(template => { - it('should not throw', () => { - env.write('test.ts', getComponentScript(template)); - const errors = env.driveDiagnostics(); - expect(errors.length).toBe(0); - }); + cases.forEach(template => { + it('should not throw', () => { + env.write('test.ts', getComponentScript(template)); + const errors = env.driveDiagnostics(); + expect(errors.length).toBe(0); }); }); + }); - it('should wrap "inputs" and "outputs" keys if they contain unsafe characters', () => { - env.write(`test.ts`, ` + it('should wrap "inputs" and "outputs" keys if they contain unsafe characters', () => { + env.write(`test.ts`, ` import {Directive, Input} from '@angular/core'; @Directive({ @@ -3035,9 +3161,9 @@ runInEachFileSystem(os => { } `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - const inputsAndOutputs = ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + const inputsAndOutputs = ` inputs: { "track-type": "track-type", "track-name": "track-name", @@ -3053,15 +3179,15 @@ runInEachFileSystem(os => { "output.event": "output.event" } `; - expect(trim(jsContents)).toContain(trim(inputsAndOutputs)); - }); + expect(trim(jsContents)).toContain(trim(inputsAndOutputs)); + }); - it('should compile programs with typeRoots', () => { - // Write out a custom tsconfig.json that includes 'typeRoots' and 'files'. 'files' is - // necessary because otherwise TS picks up the testTypeRoot/test/index.d.ts file into the - // program automatically. Shims are also turned on because the shim ts.CompilerHost wrapper - // can break typeRoot functionality (which this test is meant to detect). - env.write('tsconfig.json', `{ + it('should compile programs with typeRoots', () => { + // Write out a custom tsconfig.json that includes 'typeRoots' and 'files'. 'files' is + // necessary because otherwise TS picks up the testTypeRoot/test/index.d.ts file into the + // program automatically. Shims are also turned on because the shim ts.CompilerHost wrapper + // can break typeRoot functionality (which this test is meant to detect). + env.write('tsconfig.json', `{ "extends": "./tsconfig-base.json", "angularCompilerOptions": { "generateNgFactoryShims": true, @@ -3072,25 +3198,25 @@ runInEachFileSystem(os => { }, "files": ["./test.ts"] }`); - env.write('test.ts', ` + env.write('test.ts', ` import {Test} from 'ambient'; console.log(Test); `); - env.write('testTypeRoot/.exists', ''); - env.write('testTypeRoot/test/index.d.ts', ` + env.write('testTypeRoot/.exists', ''); + env.write('testTypeRoot/test/index.d.ts', ` declare module 'ambient' { export const Test = 'This is a test'; } `); - env.driveMain(); + env.driveMain(); - // Success is enough to indicate that this passes. - }); + // Success is enough to indicate that this passes. + }); - describe('when processing external directives', () => { - it('should not emit multiple references to the same directive', () => { - env.write('node_modules/external/index.d.ts', ` + describe('when processing external directives', () => { + it('should not emit multiple references to the same directive', () => { + env.write('node_modules/external/index.d.ts', ` import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; export declare class ExternalDir { @@ -3101,7 +3227,7 @@ runInEachFileSystem(os => { static ɵmod: ɵɵNgModuleDefWithMeta; } `); - env.write('test.ts', ` + env.write('test.ts', ` import {Component, Directive, NgModule} from '@angular/core'; import {ExternalModule} from 'external'; @@ -3119,13 +3245,13 @@ runInEachFileSystem(os => { class Module {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toMatch(/directives: \[i1\.ExternalDir\]/); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toMatch(/directives: \[i1\.ExternalDir\]/); + }); - it('should import directives by their external name', () => { - env.write('node_modules/external/index.d.ts', ` + it('should import directives by their external name', () => { + env.write('node_modules/external/index.d.ts', ` import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; import {InternalDir} from './internal'; @@ -3135,13 +3261,13 @@ runInEachFileSystem(os => { static ɵmod: ɵɵNgModuleDefWithMeta; } `); - env.write('node_modules/external/internal.d.ts', ` + env.write('node_modules/external/internal.d.ts', ` export declare class InternalDir { static ɵdir: ɵɵDirectiveDefWithMeta; } `); - env.write('test.ts', ` + env.write('test.ts', ` import {Component, Directive, NgModule} from '@angular/core'; import {ExternalModule} from 'external'; @@ -3157,107 +3283,107 @@ runInEachFileSystem(os => { class Module {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toMatch(/directives: \[i1\.ExternalDir\]/); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toMatch(/directives: \[i1\.ExternalDir\]/); }); + }); - describe('flat module indices', () => { - it('should generate a basic flat module index', () => { - env.tsconfig({ - 'flatModuleOutFile': 'flat.js', - }); - env.write('test.ts', 'export const TEST = "this is a test";'); - - env.driveMain(); - const jsContents = env.getContents('flat.js'); - expect(jsContents).toContain('export * from \'./test\';'); + describe('flat module indices', () => { + it('should generate a basic flat module index', () => { + env.tsconfig({ + 'flatModuleOutFile': 'flat.js', }); + env.write('test.ts', 'export const TEST = "this is a test";'); - it('should determine the flat module entry-point within multiple root files', () => { - env.tsconfig({ - 'flatModuleOutFile': 'flat.js', - }); - env.write('ignored.ts', 'export const TEST = "this is ignored";'); - env.write('index.ts', 'export const ENTRY = "this is the entry";'); + env.driveMain(); + const jsContents = env.getContents('flat.js'); + expect(jsContents).toContain('export * from \'./test\';'); + }); - env.driveMain(); - const jsContents = env.getContents('flat.js'); - expect(jsContents) - .toContain( - 'export * from \'./index\';', - 'Should detect the "index.ts" file as flat module entry-point.'); + it('should determine the flat module entry-point within multiple root files', () => { + env.tsconfig({ + 'flatModuleOutFile': 'flat.js', }); + env.write('ignored.ts', 'export const TEST = "this is ignored";'); + env.write('index.ts', 'export const ENTRY = "this is the entry";'); - it('should generate a flat module with an id', () => { - env.tsconfig({ - 'flatModuleOutFile': 'flat.js', - 'flatModuleId': '@mymodule', - }); - env.write('test.ts', 'export const TEST = "this is a test";'); + env.driveMain(); + const jsContents = env.getContents('flat.js'); + expect(jsContents) + .toContain( + 'export * from \'./index\';', + 'Should detect the "index.ts" file as flat module entry-point.'); + }); - env.driveMain(); - const dtsContents = env.getContents('flat.d.ts'); - expect(dtsContents).toContain('/// '); + it('should generate a flat module with an id', () => { + env.tsconfig({ + 'flatModuleOutFile': 'flat.js', + 'flatModuleId': '@mymodule', }); + env.write('test.ts', 'export const TEST = "this is a test";'); - it('should generate a proper flat module index file when nested', () => { - env.tsconfig({ - 'flatModuleOutFile': './public-api/index.js', - }); - - env.write('test.ts', `export const SOME_EXPORT = 'some-export'`); - env.driveMain(); + env.driveMain(); + const dtsContents = env.getContents('flat.d.ts'); + expect(dtsContents).toContain('/// '); + }); - expect(env.getContents('./public-api/index.js')).toContain(`export * from '../test';`); + it('should generate a proper flat module index file when nested', () => { + env.tsconfig({ + 'flatModuleOutFile': './public-api/index.js', }); - it('should not throw if "flatModuleOutFile" is set to null', () => { - env.tsconfig({ - 'flatModuleOutFile': null, - }); + env.write('test.ts', `export const SOME_EXPORT = 'some-export'`); + env.driveMain(); - env.write('test.ts', `export const SOME_EXPORT = 'some-export'`); - // The "driveMain" method automatically ensures that there is no - // exception and that the build succeeded. - env.driveMain(); + expect(env.getContents('./public-api/index.js')).toContain(`export * from '../test';`); + }); + + it('should not throw if "flatModuleOutFile" is set to null', () => { + env.tsconfig({ + 'flatModuleOutFile': null, }); - it('should not throw or produce flat module index if "flatModuleOutFile" is set to ' + - 'empty string', - () => { - env.tsconfig({ - 'flatModuleOutFile': '', - }); + env.write('test.ts', `export const SOME_EXPORT = 'some-export'`); + // The "driveMain" method automatically ensures that there is no + // exception and that the build succeeded. + env.driveMain(); + }); - env.write('test.ts', `export const SOME_EXPORT = 'some-export'`); - // The "driveMain" method automatically ensures that there is no - // exception and that the build succeeded. - env.driveMain(); - // Previously ngtsc incorrectly tried generating a flat module index - // file if the "flatModuleOutFile" was set to an empty string. ngtsc - // just wrote the bundle file with an empty filename (just extension). - env.assertDoesNotExist('.js'); - env.assertDoesNotExist('.d.ts'); + it('should not throw or produce flat module index if "flatModuleOutFile" is set to ' + + 'empty string', + () => { + env.tsconfig({ + 'flatModuleOutFile': '', }); - it('should report an error when a flat module index is requested but no entrypoint can be determined', - () => { - env.tsconfig({'flatModuleOutFile': 'flat.js'}); - env.write('test.ts', 'export class Foo {}'); - env.write('test2.ts', 'export class Bar {}'); - - const errors = env.driveDiagnostics(); - expect(errors.length).toBe(1); - expect(errors[0].messageText) - .toBe( - 'Angular compiler option "flatModuleOutFile" requires one and only one .ts file in the "files" field.'); - }); + env.write('test.ts', `export const SOME_EXPORT = 'some-export'`); + // The "driveMain" method automatically ensures that there is no + // exception and that the build succeeded. + env.driveMain(); + // Previously ngtsc incorrectly tried generating a flat module index + // file if the "flatModuleOutFile" was set to an empty string. ngtsc + // just wrote the bundle file with an empty filename (just extension). + env.assertDoesNotExist('.js'); + env.assertDoesNotExist('.d.ts'); + }); - it('should report an error when a visible directive is not exported', () => { - env.tsconfig({'flatModuleOutFile': 'flat.js'}); - env.write('test.ts', ` + it('should report an error when a flat module index is requested but no entrypoint can be determined', + () => { + env.tsconfig({'flatModuleOutFile': 'flat.js'}); + env.write('test.ts', 'export class Foo {}'); + env.write('test2.ts', 'export class Bar {}'); + + const errors = env.driveDiagnostics(); + expect(errors.length).toBe(1); + expect(errors[0].messageText) + .toBe( + 'Angular compiler option "flatModuleOutFile" requires one and only one .ts file in the "files" field.'); + }); + + it('should report an error when a visible directive is not exported', () => { + env.tsconfig({'flatModuleOutFile': 'flat.js'}); + env.write('test.ts', ` import {Directive, NgModule} from '@angular/core'; // The directive is not exported. @@ -3269,24 +3395,24 @@ runInEachFileSystem(os => { export class Module {} `); - const errors = env.driveDiagnostics(); - expect(errors.length).toBe(1); - expect(errors[0].messageText) - .toBe( - 'Unsupported private class Dir. This class is visible ' + - 'to consumers via Module -> Dir, but is not exported from the top-level library ' + - 'entrypoint.'); - - // Verify that the error is for the correct class. - const error = errors[0] as ts.Diagnostic; - const id = expectTokenAtPosition(error.file !, error.start !, ts.isIdentifier); - expect(id.text).toBe('Dir'); - expect(ts.isClassDeclaration(id.parent)).toBe(true); - }); + const errors = env.driveDiagnostics(); + expect(errors.length).toBe(1); + expect(errors[0].messageText) + .toBe( + 'Unsupported private class Dir. This class is visible ' + + 'to consumers via Module -> Dir, but is not exported from the top-level library ' + + 'entrypoint.'); + + // Verify that the error is for the correct class. + const error = errors[0] as ts.Diagnostic; + const id = expectTokenAtPosition(error.file !, error.start !, ts.isIdentifier); + expect(id.text).toBe('Dir'); + expect(ts.isClassDeclaration(id.parent)).toBe(true); + }); - it('should report an error when a deeply visible directive is not exported', () => { - env.tsconfig({'flatModuleOutFile': 'flat.js'}); - env.write('test.ts', ` + it('should report an error when a deeply visible directive is not exported', () => { + env.tsconfig({'flatModuleOutFile': 'flat.js'}); + env.write('test.ts', ` import {Directive, NgModule} from '@angular/core'; // The directive is not exported. @@ -3302,23 +3428,23 @@ runInEachFileSystem(os => { export class Module {} `); - const errors = env.driveDiagnostics(); - expect(errors.length).toBe(2); - expect(errors[0].messageText) - .toBe( - 'Unsupported private class DirModule. This class is ' + - 'visible to consumers via Module -> DirModule, but is not exported from the top-level ' + - 'library entrypoint.'); - expect(errors[1].messageText) - .toBe( - 'Unsupported private class Dir. This class is visible ' + - 'to consumers via Module -> DirModule -> Dir, but is not exported from the top-level ' + - 'library entrypoint.'); - }); + const errors = env.driveDiagnostics(); + expect(errors.length).toBe(2); + expect(errors[0].messageText) + .toBe( + 'Unsupported private class DirModule. This class is ' + + 'visible to consumers via Module -> DirModule, but is not exported from the top-level ' + + 'library entrypoint.'); + expect(errors[1].messageText) + .toBe( + 'Unsupported private class Dir. This class is visible ' + + 'to consumers via Module -> DirModule -> Dir, but is not exported from the top-level ' + + 'library entrypoint.'); + }); - it('should report an error when a deeply visible module is not exported', () => { - env.tsconfig({'flatModuleOutFile': 'flat.js'}); - env.write('test.ts', ` + it('should report an error when a deeply visible module is not exported', () => { + env.tsconfig({'flatModuleOutFile': 'flat.js'}); + env.write('test.ts', ` import {Directive, NgModule} from '@angular/core'; // The directive is exported. @@ -3334,19 +3460,18 @@ runInEachFileSystem(os => { export class Module {} `); - const errors = env.driveDiagnostics(); - expect(errors.length).toBe(1); - expect(errors[0].messageText) - .toBe( - 'Unsupported private class DirModule. This class is ' + - 'visible to consumers via Module -> DirModule, but is not exported from the top-level ' + - 'library entrypoint.'); - }); + const errors = env.driveDiagnostics(); + expect(errors.length).toBe(1); + expect(errors[0].messageText) + .toBe( + 'Unsupported private class DirModule. This class is ' + + 'visible to consumers via Module -> DirModule, but is not exported from the top-level ' + + 'library entrypoint.'); + }); - it('should not report an error when a non-exported module is imported by a visible one', - () => { - env.tsconfig({'flatModuleOutFile': 'flat.js'}); - env.write('test.ts', ` + it('should not report an error when a non-exported module is imported by a visible one', () => { + env.tsconfig({'flatModuleOutFile': 'flat.js'}); + env.write('test.ts', ` import {Directive, NgModule} from '@angular/core'; // The directive is not exported. @@ -3363,13 +3488,13 @@ runInEachFileSystem(os => { export class Module {} `); - const errors = env.driveDiagnostics(); - expect(errors.length).toBe(0); - }); + const errors = env.driveDiagnostics(); + expect(errors.length).toBe(0); + }); - it('should not report an error when re-exporting an external symbol', () => { - env.tsconfig({'flatModuleOutFile': 'flat.js'}); - env.write('test.ts', ` + it('should not report an error when re-exporting an external symbol', () => { + env.tsconfig({'flatModuleOutFile': 'flat.js'}); + env.write('test.ts', ` import {Directive, NgModule} from '@angular/core'; import {ExternalModule} from 'external'; @@ -3377,7 +3502,7 @@ runInEachFileSystem(os => { @NgModule({exports: [ExternalModule]}) export class Module {} `); - env.write('node_modules/external/index.d.ts', ` + env.write('node_modules/external/index.d.ts', ` import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; export declare class ExternalDir { @@ -3389,20 +3514,20 @@ runInEachFileSystem(os => { } `); - const errors = env.driveDiagnostics(); - expect(errors.length).toBe(0); - }); + const errors = env.driveDiagnostics(); + expect(errors.length).toBe(0); }); + }); - describe('aliasing re-exports', () => { - beforeEach(() => { - env.tsconfig({ - 'generateDeepReexports': true, - }); + describe('aliasing re-exports', () => { + beforeEach(() => { + env.tsconfig({ + 'generateDeepReexports': true, }); + }); - it('should re-export a directive from a different file under a private symbol name', () => { - env.write('dir.ts', ` + it('should re-export a directive from a different file under a private symbol name', () => { + env.write('dir.ts', ` import {Directive} from '@angular/core'; @Directive({ @@ -3410,7 +3535,7 @@ runInEachFileSystem(os => { }) export class Dir {} `); - env.write('module.ts', ` + env.write('module.ts', ` import {Directive, NgModule} from '@angular/core'; import {Dir} from './dir'; @@ -3424,19 +3549,18 @@ runInEachFileSystem(os => { export class Module {} `); - env.driveMain(); - const jsContents = env.getContents('module.js'); - const dtsContents = env.getContents('module.d.ts'); + env.driveMain(); + const jsContents = env.getContents('module.js'); + const dtsContents = env.getContents('module.d.ts'); - expect(jsContents).toContain('export { Dir as ɵngExportɵModuleɵDir } from "./dir";'); - expect(jsContents).not.toContain('ɵngExportɵModuleɵInlineDir'); - expect(dtsContents).toContain('export { Dir as ɵngExportɵModuleɵDir } from "./dir";'); - expect(dtsContents).not.toContain('ɵngExportɵModuleɵInlineDir'); - }); + expect(jsContents).toContain('export { Dir as ɵngExportɵModuleɵDir } from "./dir";'); + expect(jsContents).not.toContain('ɵngExportɵModuleɵInlineDir'); + expect(dtsContents).toContain('export { Dir as ɵngExportɵModuleɵDir } from "./dir";'); + expect(dtsContents).not.toContain('ɵngExportɵModuleɵInlineDir'); + }); - it('should re-export a directive from an exported NgModule under a private symbol name', - () => { - env.write('dir.ts', ` + it('should re-export a directive from an exported NgModule under a private symbol name', () => { + env.write('dir.ts', ` import {Directive, NgModule} from '@angular/core'; @Directive({ @@ -3450,7 +3574,7 @@ runInEachFileSystem(os => { }) export class DirModule {} `); - env.write('module.ts', ` + env.write('module.ts', ` import {NgModule} from '@angular/core'; import {DirModule} from './dir'; @@ -3460,16 +3584,16 @@ runInEachFileSystem(os => { export class Module {} `); - env.driveMain(); - const jsContents = env.getContents('module.js'); - const dtsContents = env.getContents('module.d.ts'); + env.driveMain(); + const jsContents = env.getContents('module.js'); + const dtsContents = env.getContents('module.d.ts'); - expect(jsContents).toContain('export { Dir as ɵngExportɵModuleɵDir } from "./dir";'); - expect(dtsContents).toContain('export { Dir as ɵngExportɵModuleɵDir } from "./dir";'); - }); + expect(jsContents).toContain('export { Dir as ɵngExportɵModuleɵDir } from "./dir";'); + expect(dtsContents).toContain('export { Dir as ɵngExportɵModuleɵDir } from "./dir";'); + }); - it('should not re-export a directive that\'s not exported from the NgModule', () => { - env.write('dir.ts', ` + it('should not re-export a directive that\'s not exported from the NgModule', () => { + env.write('dir.ts', ` import {Directive} from '@angular/core'; @Directive({ @@ -3477,7 +3601,7 @@ runInEachFileSystem(os => { }) export class Dir {} `); - env.write('module.ts', ` + env.write('module.ts', ` import {NgModule} from '@angular/core'; import {Dir} from './dir'; @@ -3488,16 +3612,16 @@ runInEachFileSystem(os => { export class Module {} `); - env.driveMain(); - const jsContents = env.getContents('module.js'); - const dtsContents = env.getContents('module.d.ts'); + env.driveMain(); + const jsContents = env.getContents('module.js'); + const dtsContents = env.getContents('module.d.ts'); - expect(jsContents).not.toContain('ɵngExportɵModuleɵDir'); - expect(dtsContents).not.toContain('ɵngExportɵModuleɵDir'); - }); + expect(jsContents).not.toContain('ɵngExportɵModuleɵDir'); + expect(dtsContents).not.toContain('ɵngExportɵModuleɵDir'); + }); - it('should not re-export a directive that\'s already exported', () => { - env.write('dir.ts', ` + it('should not re-export a directive that\'s already exported', () => { + env.write('dir.ts', ` import {Directive} from '@angular/core'; @Directive({ @@ -3505,7 +3629,7 @@ runInEachFileSystem(os => { }) export class Dir {} `); - env.write('module.ts', ` + env.write('module.ts', ` import {NgModule} from '@angular/core'; import {Dir} from './dir'; @@ -3518,16 +3642,16 @@ runInEachFileSystem(os => { export {Dir}; `); - env.driveMain(); - const jsContents = env.getContents('module.js'); - const dtsContents = env.getContents('module.d.ts'); + env.driveMain(); + const jsContents = env.getContents('module.js'); + const dtsContents = env.getContents('module.d.ts'); - expect(jsContents).not.toContain('ɵngExportɵModuleɵDir'); - expect(dtsContents).not.toContain('ɵngExportɵModuleɵDir'); - }); + expect(jsContents).not.toContain('ɵngExportɵModuleɵDir'); + expect(dtsContents).not.toContain('ɵngExportɵModuleɵDir'); + }); - it('should not re-export a directive from an exported, external NgModule', () => { - env.write(`node_modules/external/index.d.ts`, ` + it('should not re-export a directive from an exported, external NgModule', () => { + env.write(`node_modules/external/index.d.ts`, ` import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; export declare class ExternalDir { @@ -3538,7 +3662,7 @@ runInEachFileSystem(os => { static ɵmod: ɵɵNgModuleDefWithMeta; } `); - env.write('module.ts', ` + env.write('module.ts', ` import {NgModule} from '@angular/core'; import {ExternalModule} from 'external'; @@ -3548,15 +3672,15 @@ runInEachFileSystem(os => { export class Module {} `); - env.driveMain(); - const jsContents = env.getContents('module.js'); + env.driveMain(); + const jsContents = env.getContents('module.js'); - expect(jsContents).not.toContain('ɵngExportɵExternalModuleɵExternalDir'); - }); + expect(jsContents).not.toContain('ɵngExportɵExternalModuleɵExternalDir'); + }); - it('should error when two directives with the same declared name are exported from the same NgModule', - () => { - env.write('dir.ts', ` + it('should error when two directives with the same declared name are exported from the same NgModule', + () => { + env.write('dir.ts', ` import {Directive} from '@angular/core'; @Directive({ @@ -3564,7 +3688,7 @@ runInEachFileSystem(os => { }) export class Dir {} `); - env.write('dir2.ts', ` + env.write('dir2.ts', ` import {Directive} from '@angular/core'; @Directive({ @@ -3572,7 +3696,7 @@ runInEachFileSystem(os => { }) export class Dir {} `); - env.write('module.ts', ` + env.write('module.ts', ` import {NgModule} from '@angular/core'; import {Dir} from './dir'; import {Dir as Dir2} from './dir2'; @@ -3584,14 +3708,14 @@ runInEachFileSystem(os => { export class Module {} `); - const diag = env.driveDiagnostics(); - expect(diag.length).toBe(1); - expect(diag[0] !.code).toEqual(ngErrorCode(ErrorCode.NGMODULE_REEXPORT_NAME_COLLISION)); - }); + const diag = env.driveDiagnostics(); + expect(diag.length).toBe(1); + expect(diag[0] !.code).toEqual(ngErrorCode(ErrorCode.NGMODULE_REEXPORT_NAME_COLLISION)); + }); - it('should not error when two directives with the same declared name are exported from the same NgModule, but one is exported from the file directly', - () => { - env.write('dir.ts', ` + it('should not error when two directives with the same declared name are exported from the same NgModule, but one is exported from the file directly', + () => { + env.write('dir.ts', ` import {Directive} from '@angular/core'; @Directive({ @@ -3599,7 +3723,7 @@ runInEachFileSystem(os => { }) export class Dir {} `); - env.write('dir2.ts', ` + env.write('dir2.ts', ` import {Directive} from '@angular/core'; @Directive({ @@ -3607,7 +3731,7 @@ runInEachFileSystem(os => { }) export class Dir {} `); - env.write('module.ts', ` + env.write('module.ts', ` import {NgModule} from '@angular/core'; import {Dir} from './dir'; import {Dir as Dir2} from './dir2'; @@ -3621,20 +3745,20 @@ runInEachFileSystem(os => { export {Dir} from './dir2'; `); - env.driveMain(); - const jsContents = env.getContents('module.js'); - expect(jsContents).toContain('export { Dir as ɵngExportɵModuleɵDir } from "./dir";'); - }); + env.driveMain(); + const jsContents = env.getContents('module.js'); + expect(jsContents).toContain('export { Dir as ɵngExportɵModuleɵDir } from "./dir";'); + }); - it('should choose a re-exported symbol if one is present', () => { - env.write(`node_modules/external/dir.d.ts`, ` + it('should choose a re-exported symbol if one is present', () => { + env.write(`node_modules/external/dir.d.ts`, ` import {ɵɵDirectiveDefWithMeta} from '@angular/core'; export declare class ExternalDir { static ɵdir: ɵɵDirectiveDefWithMeta; } `); - env.write('node_modules/external/module.d.ts', ` + env.write('node_modules/external/module.d.ts', ` import {ɵɵNgModuleDefWithMeta} from '@angular/core'; import {ExternalDir} from './dir'; @@ -3644,7 +3768,7 @@ runInEachFileSystem(os => { export {ExternalDir as ɵngExportɵExternalModuleɵExternalDir}; `); - env.write('test.ts', ` + env.write('test.ts', ` import {Component, Directive, NgModule} from '@angular/core'; import {ExternalModule} from 'external/module'; @@ -3661,17 +3785,17 @@ runInEachFileSystem(os => { class Module {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - expect(jsContents).toContain('import * as i1 from "external/module";'); - expect(jsContents).toContain('directives: [i1.ɵngExportɵExternalModuleɵExternalDir]'); - }); + env.driveMain(); + const jsContents = env.getContents('test.js'); + expect(jsContents).toContain('import * as i1 from "external/module";'); + expect(jsContents).toContain('directives: [i1.ɵngExportɵExternalModuleɵExternalDir]'); + }); - it('should not generate re-exports when disabled', () => { - // Return to the default configuration, which has re-exports disabled. - env.tsconfig(); + it('should not generate re-exports when disabled', () => { + // Return to the default configuration, which has re-exports disabled. + env.tsconfig(); - env.write('dir.ts', ` + env.write('dir.ts', ` import {Directive} from '@angular/core'; @Directive({ @@ -3679,7 +3803,7 @@ runInEachFileSystem(os => { }) export class Dir {} `); - env.write('module.ts', ` + env.write('module.ts', ` import {NgModule} from '@angular/core'; import {Dir} from './dir'; @@ -3690,51 +3814,51 @@ runInEachFileSystem(os => { export class Module {} `); - env.driveMain(); - const jsContents = env.getContents('module.js'); - const dtsContents = env.getContents('module.d.ts'); + env.driveMain(); + const jsContents = env.getContents('module.js'); + const dtsContents = env.getContents('module.d.ts'); - expect(jsContents).not.toContain('ɵngExportɵModuleɵDir'); - expect(dtsContents).not.toContain('ɵngExportɵModuleɵDir'); - }); + expect(jsContents).not.toContain('ɵngExportɵModuleɵDir'); + expect(dtsContents).not.toContain('ɵngExportɵModuleɵDir'); }); + }); - it('should execute custom transformers', () => { - let beforeCount = 0; - let afterCount = 0; + it('should execute custom transformers', () => { + let beforeCount = 0; + let afterCount = 0; - env.write('test.ts', ` + env.write('test.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) class Module {} `); - env.driveMain({ - beforeTs: [() => (sourceFile: ts.SourceFile) => { - beforeCount++; - return sourceFile; - }], - afterTs: [() => (sourceFile: ts.SourceFile) => { - afterCount++; - return sourceFile; - }], - }); - - expect(beforeCount).toBe(1); - expect(afterCount).toBe(1); + env.driveMain({ + beforeTs: [() => (sourceFile: ts.SourceFile) => { + beforeCount++; + return sourceFile; + }], + afterTs: [() => (sourceFile: ts.SourceFile) => { + afterCount++; + return sourceFile; + }], }); - // These tests trigger the Tsickle compiler which asserts that the file-paths - // are valid for the real OS. When on non-Windows systems it doesn't like paths - // that start with `C:`. - if (os !== 'Windows' || platform() === 'win32') { - describe('@fileoverview Closure annotations', () => { - it('should be produced if not present in source file', () => { - env.tsconfig({ - 'annotateForClosureCompiler': true, - }); - env.write(`test.ts`, ` + expect(beforeCount).toBe(1); + expect(afterCount).toBe(1); + }); + + // These tests trigger the Tsickle compiler which asserts that the file-paths + // are valid for the real OS. When on non-Windows systems it doesn't like paths + // that start with `C:`. + if (os !== 'Windows' || platform() === 'win32') { + describe('@fileoverview Closure annotations', () => { + it('should be produced if not present in source file', () => { + env.tsconfig({ + 'annotateForClosureCompiler': true, + }); + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ @@ -3743,39 +3867,39 @@ runInEachFileSystem(os => { export class SomeComp {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - const fileoverview = ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + const fileoverview = ` /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ `; - expect(trim(jsContents).startsWith(trim(fileoverview))).toBeTruthy(); - }); + expect(trim(jsContents).startsWith(trim(fileoverview))).toBeTruthy(); + }); - it('should be produced for empty source files', () => { - env.tsconfig({ - 'annotateForClosureCompiler': true, - }); - env.write(`test.ts`, ``); + it('should be produced for empty source files', () => { + env.tsconfig({ + 'annotateForClosureCompiler': true, + }); + env.write(`test.ts`, ``); - env.driveMain(); - const jsContents = env.getContents('test.js'); - const fileoverview = ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + const fileoverview = ` /** * @fileoverview added by tsickle * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ `; - expect(trim(jsContents).startsWith(trim(fileoverview))).toBeTruthy(); - }); + expect(trim(jsContents).startsWith(trim(fileoverview))).toBeTruthy(); + }); - it('should always be at the very beginning of a script (if placed above imports)', () => { - env.tsconfig({ - 'annotateForClosureCompiler': true, - }); - env.write(`test.ts`, ` + it('should always be at the very beginning of a script (if placed above imports)', () => { + env.tsconfig({ + 'annotateForClosureCompiler': true, + }); + env.write(`test.ts`, ` /** * @fileoverview Some Comp overview * @modName {some_comp} @@ -3789,9 +3913,9 @@ runInEachFileSystem(os => { export class SomeComp {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - const fileoverview = ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + const fileoverview = ` /** * * @fileoverview Some Comp overview @@ -3800,15 +3924,14 @@ runInEachFileSystem(os => { * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ `; - expect(trim(jsContents).startsWith(trim(fileoverview))).toBeTruthy(); - }); + expect(trim(jsContents).startsWith(trim(fileoverview))).toBeTruthy(); + }); - it('should always be at the very beginning of a script (if placed above non-imports)', - () => { - env.tsconfig({ - 'annotateForClosureCompiler': true, - }); - env.write(`test.ts`, ` + it('should always be at the very beginning of a script (if placed above non-imports)', () => { + env.tsconfig({ + 'annotateForClosureCompiler': true, + }); + env.write(`test.ts`, ` /** * @fileoverview Some Comp overview * @modName {some_comp} @@ -3818,9 +3941,9 @@ runInEachFileSystem(os => { const testFn = function() { return true; } `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - const fileoverview = ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + const fileoverview = ` /** * * @fileoverview Some Comp overview @@ -3829,15 +3952,14 @@ runInEachFileSystem(os => { * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc */ `; - expect(trim(jsContents).startsWith(trim(fileoverview))).toBeTruthy(); - }); + expect(trim(jsContents).startsWith(trim(fileoverview))).toBeTruthy(); }); - } + }); + } - describe('sanitization', () => { - it('should generate sanitizers for unsafe attributes in hostBindings fn in Directives', - () => { - env.write(`test.ts`, ` + describe('sanitization', () => { + it('should generate sanitizers for unsafe attributes in hostBindings fn in Directives', () => { + env.write(`test.ts`, ` import {Component, Directive, HostBinding} from '@angular/core'; @Directive({ @@ -3870,9 +3992,9 @@ runInEachFileSystem(os => { class FooCmp {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - const hostBindingsFn = ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + const hostBindingsFn = ` hostBindings: function UnsafeAttrsDirective_HostBindings(rf, ctx, elIndex) { if (rf & 1) { i0.ɵɵallocHostVars(6); @@ -3882,12 +4004,11 @@ runInEachFileSystem(os => { } } `; - expect(trim(jsContents)).toContain(trim(hostBindingsFn)); - }); + expect(trim(jsContents)).toContain(trim(hostBindingsFn)); + }); - it('should generate sanitizers for unsafe properties in hostBindings fn in Directives', - () => { - env.write(`test.ts`, ` + it('should generate sanitizers for unsafe properties in hostBindings fn in Directives', () => { + env.write(`test.ts`, ` import {Component, Directive, HostBinding} from '@angular/core'; @Directive({ @@ -3920,9 +4041,9 @@ runInEachFileSystem(os => { class FooCmp {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - const hostBindingsFn = ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + const hostBindingsFn = ` hostBindings: function UnsafePropsDirective_HostBindings(rf, ctx, elIndex) { if (rf & 1) { i0.ɵɵallocHostVars(6); @@ -3932,12 +4053,11 @@ runInEachFileSystem(os => { } } `; - expect(trim(jsContents)).toContain(trim(hostBindingsFn)); - }); + expect(trim(jsContents)).toContain(trim(hostBindingsFn)); + }); - it('should not generate sanitizers for URL properties in hostBindings fn in Component', - () => { - env.write(`test.ts`, ` + it('should not generate sanitizers for URL properties in hostBindings fn in Component', () => { + env.write(`test.ts`, ` import {Component} from '@angular/core'; @Component({ @@ -3955,9 +4075,9 @@ runInEachFileSystem(os => { class FooCmp {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); - const hostBindingsFn = ` + env.driveMain(); + const jsContents = env.getContents('test.js'); + const hostBindingsFn = ` hostBindings: function FooCmp_HostBindings(rf, ctx, elIndex) { if (rf & 1) { i0.ɵɵallocHostVars(6); @@ -3968,12 +4088,12 @@ runInEachFileSystem(os => { } } `; - expect(trim(jsContents)).toContain(trim(hostBindingsFn)); - }); + expect(trim(jsContents)).toContain(trim(hostBindingsFn)); }); + }); - describe('listLazyRoutes()', () => { - // clang-format off + describe('listLazyRoutes()', () => { + // clang-format off const lazyRouteMatching = ( route: string, fromModulePath: RegExp, fromModuleName: string, toModulePath: RegExp, toModuleName: string) => { @@ -3989,10 +4109,10 @@ runInEachFileSystem(os => { }), } as unknown as LazyRoute; }; - // clang-format on + // clang-format on - beforeEach(() => { - env.write('node_modules/@angular/router/index.d.ts', ` + beforeEach(() => { + env.write('node_modules/@angular/router/index.d.ts', ` import {ModuleWithProviders, ɵɵNgModuleDefWithMeta as ɵɵNgModuleDefWithMeta} from '@angular/core'; export declare var ROUTES; @@ -4002,11 +4122,11 @@ runInEachFileSystem(os => { static ɵmod: ɵɵNgModuleDefWithMeta; } `); - }); + }); - describe('when called without arguments', () => { - it('should list all routes', () => { - env.write('test.ts', ` + describe('when called without arguments', () => { + it('should list all routes', () => { + env.write('test.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4020,13 +4140,13 @@ runInEachFileSystem(os => { }) export class TestModule {} `); - env.write('lazy/lazy-1.ts', ` + env.write('lazy/lazy-1.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class Lazy1Module {} `); - env.write('lazy/lazy-2.ts', ` + env.write('lazy/lazy-2.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4039,29 +4159,29 @@ runInEachFileSystem(os => { }) export class Lazy2Module {} `); - env.write('lazy/lazy-3.ts', ` + env.write('lazy/lazy-3.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class Lazy3Module {} `); - const routes = env.driveRoutes(); - expect(routes).toEqual([ - lazyRouteMatching( - './lazy-3#Lazy3Module', /\/lazy\/lazy-2\.ts$/, 'Lazy2Module', /\/lazy\/lazy-3\.ts$/, - 'Lazy3Module'), - lazyRouteMatching( - './lazy/lazy-1#Lazy1Module', /\/test\.ts$/, 'TestModule', /\/lazy\/lazy-1\.ts$/, - 'Lazy1Module'), - lazyRouteMatching( - './lazy/lazy-2#Lazy2Module', /\/test\.ts$/, 'TestModule', /\/lazy\/lazy-2\.ts$/, - 'Lazy2Module'), - ]); - }); + const routes = env.driveRoutes(); + expect(routes).toEqual([ + lazyRouteMatching( + './lazy-3#Lazy3Module', /\/lazy\/lazy-2\.ts$/, 'Lazy2Module', /\/lazy\/lazy-3\.ts$/, + 'Lazy3Module'), + lazyRouteMatching( + './lazy/lazy-1#Lazy1Module', /\/test\.ts$/, 'TestModule', /\/lazy\/lazy-1\.ts$/, + 'Lazy1Module'), + lazyRouteMatching( + './lazy/lazy-2#Lazy2Module', /\/test\.ts$/, 'TestModule', /\/lazy\/lazy-2\.ts$/, + 'Lazy2Module'), + ]); + }); - it('should detect lazy routes in simple children routes', () => { - env.write('test.ts', ` + it('should detect lazy routes in simple children routes', () => { + env.write('test.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4083,7 +4203,7 @@ runInEachFileSystem(os => { }) export class TestModule {} `); - env.write('lazy.ts', ` + env.write('lazy.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4091,16 +4211,16 @@ runInEachFileSystem(os => { export class LazyModule {} `); - const routes = env.driveRoutes(); - expect(routes).toEqual([ - lazyRouteMatching( - './lazy#LazyModule', /\/test\.ts$/, 'TestModule', /\/lazy\.ts$/, 'LazyModule'), - ]); - }); + const routes = env.driveRoutes(); + expect(routes).toEqual([ + lazyRouteMatching( + './lazy#LazyModule', /\/test\.ts$/, 'TestModule', /\/lazy\.ts$/, 'LazyModule'), + ]); + }); - it('should detect lazy routes in all root directories', () => { - env.tsconfig({}, ['./foo/other-root-dir', './bar/other-root-dir']); - env.write('src/test.ts', ` + it('should detect lazy routes in all root directories', () => { + env.tsconfig({}, ['./foo/other-root-dir', './bar/other-root-dir']); + env.write('src/test.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4113,7 +4233,7 @@ runInEachFileSystem(os => { }) export class TestModule {} `); - env.write('foo/other-root-dir/src/lazy-foo.ts', ` + env.write('foo/other-root-dir/src/lazy-foo.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4126,7 +4246,7 @@ runInEachFileSystem(os => { }) export class LazyFooModule {} `); - env.write('bar/other-root-dir/src/lazy-bar.ts', ` + env.write('bar/other-root-dir/src/lazy-bar.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4139,32 +4259,32 @@ runInEachFileSystem(os => { }) export class LazyBarModule {} `); - env.write('bar/other-root-dir/src/lazier-bar.ts', ` + env.write('bar/other-root-dir/src/lazier-bar.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class LazierBarModule {} `); - const routes = env.driveRoutes(); - - expect(routes).toEqual([ - lazyRouteMatching( - './lazy-foo#LazyFooModule', /\/test\.ts$/, 'TestModule', - /\/foo\/other-root-dir\/src\/lazy-foo\.ts$/, 'LazyFooModule'), - lazyRouteMatching( - './lazy-bar#LazyBarModule', /\/foo\/other-root-dir\/src\/lazy-foo\.ts$/, - 'LazyFooModule', /\/bar\/other-root-dir\/src\/lazy-bar\.ts$/, 'LazyBarModule'), - lazyRouteMatching( - './lazier-bar#LazierBarModule', /\/bar\/other-root-dir\/src\/lazy-bar\.ts$/, - 'LazyBarModule', /\/bar\/other-root-dir\/src\/lazier-bar\.ts$/, 'LazierBarModule'), - ]); - }); + const routes = env.driveRoutes(); + + expect(routes).toEqual([ + lazyRouteMatching( + './lazy-foo#LazyFooModule', /\/test\.ts$/, 'TestModule', + /\/foo\/other-root-dir\/src\/lazy-foo\.ts$/, 'LazyFooModule'), + lazyRouteMatching( + './lazy-bar#LazyBarModule', /\/foo\/other-root-dir\/src\/lazy-foo\.ts$/, + 'LazyFooModule', /\/bar\/other-root-dir\/src\/lazy-bar\.ts$/, 'LazyBarModule'), + lazyRouteMatching( + './lazier-bar#LazierBarModule', /\/bar\/other-root-dir\/src\/lazy-bar\.ts$/, + 'LazyBarModule', /\/bar\/other-root-dir\/src\/lazier-bar\.ts$/, 'LazierBarModule'), + ]); }); + }); - describe('when called with entry module', () => { - it('should throw if the entry module hasn\'t been analyzed', () => { - env.write('test.ts', ` + describe('when called with entry module', () => { + it('should throw if the entry module hasn\'t been analyzed', () => { + env.write('test.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4178,19 +4298,19 @@ runInEachFileSystem(os => { export class TestModule {} `); - const entryModule1 = absoluteFrom('/test#TestModule'); - const entryModule2 = absoluteFrom('/not-test#TestModule'); - const entryModule3 = absoluteFrom('/test#NotTestModule'); + const entryModule1 = absoluteFrom('/test#TestModule'); + const entryModule2 = absoluteFrom('/not-test#TestModule'); + const entryModule3 = absoluteFrom('/test#NotTestModule'); - expect(() => env.driveRoutes(entryModule1)).not.toThrow(); - expect(() => env.driveRoutes(entryModule2)) - .toThrowError(`Failed to list lazy routes: Unknown module '${entryModule2}'.`); - expect(() => env.driveRoutes(entryModule3)) - .toThrowError(`Failed to list lazy routes: Unknown module '${entryModule3}'.`); - }); + expect(() => env.driveRoutes(entryModule1)).not.toThrow(); + expect(() => env.driveRoutes(entryModule2)) + .toThrowError(`Failed to list lazy routes: Unknown module '${entryModule2}'.`); + expect(() => env.driveRoutes(entryModule3)) + .toThrowError(`Failed to list lazy routes: Unknown module '${entryModule3}'.`); + }); - it('should list all transitive lazy routes', () => { - env.write('test.ts', ` + it('should list all transitive lazy routes', () => { + env.write('test.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; import {Test1Module as Test1ModuleRenamed} from './test-1'; @@ -4209,7 +4329,7 @@ runInEachFileSystem(os => { }) export class TestModule {} `); - env.write('test-1.ts', ` + env.write('test-1.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4222,7 +4342,7 @@ runInEachFileSystem(os => { }) export class Test1Module {} `); - env.write('test-2.ts', ` + env.write('test-2.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4235,42 +4355,42 @@ runInEachFileSystem(os => { }) export class Test2Module {} `); - env.write('lazy/lazy.ts', ` + env.write('lazy/lazy.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class LazyModule {} `); - env.write('lazy-1/lazy-1.ts', ` + env.write('lazy-1/lazy-1.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class Lazy1Module {} `); - env.write('lazy-2/lazy-2.ts', ` + env.write('lazy-2/lazy-2.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class Lazy2Module {} `); - const routes = env.driveRoutes(absoluteFrom('/test#TestModule')); - - expect(routes).toEqual([ - lazyRouteMatching( - './lazy/lazy#LazyModule', /\/test\.ts$/, 'TestModule', /\/lazy\/lazy\.ts$/, - 'LazyModule'), - lazyRouteMatching( - './lazy-1/lazy-1#Lazy1Module', /\/test-1\.ts$/, 'Test1Module', - /\/lazy-1\/lazy-1\.ts$/, 'Lazy1Module'), - lazyRouteMatching( - './lazy-2/lazy-2#Lazy2Module', /\/test-2\.ts$/, 'Test2Module', - /\/lazy-2\/lazy-2\.ts$/, 'Lazy2Module'), - ]); - }); + const routes = env.driveRoutes(absoluteFrom('/test#TestModule')); + + expect(routes).toEqual([ + lazyRouteMatching( + './lazy/lazy#LazyModule', /\/test\.ts$/, 'TestModule', /\/lazy\/lazy\.ts$/, + 'LazyModule'), + lazyRouteMatching( + './lazy-1/lazy-1#Lazy1Module', /\/test-1\.ts$/, 'Test1Module', + /\/lazy-1\/lazy-1\.ts$/, 'Lazy1Module'), + lazyRouteMatching( + './lazy-2/lazy-2#Lazy2Module', /\/test-2\.ts$/, 'Test2Module', + /\/lazy-2\/lazy-2\.ts$/, 'Lazy2Module'), + ]); + }); - it('should ignore exports that do not refer to an `NgModule`', () => { - env.write('test-1.ts', ` + it('should ignore exports that do not refer to an `NgModule`', () => { + env.write('test-1.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; import {Test2Component, Test2Module} from './test-2'; @@ -4289,7 +4409,7 @@ runInEachFileSystem(os => { }) export class Test1Module {} `); - env.write('test-2.ts', ` + env.write('test-2.ts', ` import {Component, NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4312,33 +4432,33 @@ runInEachFileSystem(os => { }) export class Test2Module {} `); - env.write('lazy-1/lazy-1.ts', ` + env.write('lazy-1/lazy-1.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class Lazy1Module {} `); - env.write('lazy-2/lazy-2.ts', ` + env.write('lazy-2/lazy-2.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class Lazy2Module {} `); - const routes = env.driveRoutes(absoluteFrom('/test-1#Test1Module')); + const routes = env.driveRoutes(absoluteFrom('/test-1#Test1Module')); - expect(routes).toEqual([ - lazyRouteMatching( - './lazy-1/lazy-1#Lazy1Module', /\/test-1\.ts$/, 'Test1Module', - /\/lazy-1\/lazy-1\.ts$/, 'Lazy1Module'), - lazyRouteMatching( - './lazy-2/lazy-2#Lazy2Module', /\/test-2\.ts$/, 'Test2Module', - /\/lazy-2\/lazy-2\.ts$/, 'Lazy2Module'), - ]); - }); + expect(routes).toEqual([ + lazyRouteMatching( + './lazy-1/lazy-1#Lazy1Module', /\/test-1\.ts$/, 'Test1Module', + /\/lazy-1\/lazy-1\.ts$/, 'Lazy1Module'), + lazyRouteMatching( + './lazy-2/lazy-2#Lazy2Module', /\/test-2\.ts$/, 'Test2Module', + /\/lazy-2\/lazy-2\.ts$/, 'Lazy2Module'), + ]); + }); - it('should support `ModuleWithProviders`', () => { - env.write('test.ts', ` + it('should support `ModuleWithProviders`', () => { + env.write('test.ts', ` import {ModuleWithProviders, NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4368,33 +4488,33 @@ runInEachFileSystem(os => { }) export class TestModule {} `); - env.write('lazy-1/lazy-1.ts', ` + env.write('lazy-1/lazy-1.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class Lazy1Module {} `); - env.write('lazy-2/lazy-2.ts', ` + env.write('lazy-2/lazy-2.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class Lazy2Module {} `); - const routes = env.driveRoutes(absoluteFrom('/test#TestModule')); + const routes = env.driveRoutes(absoluteFrom('/test#TestModule')); - expect(routes).toEqual([ - lazyRouteMatching( - './lazy-1/lazy-1#Lazy1Module', /\/test\.ts$/, 'TestModule', /\/lazy-1\/lazy-1\.ts$/, - 'Lazy1Module'), - lazyRouteMatching( - './lazy-2/lazy-2#Lazy2Module', /\/test\.ts$/, 'TestRoutingModule', - /\/lazy-2\/lazy-2\.ts$/, 'Lazy2Module'), - ]); - }); + expect(routes).toEqual([ + lazyRouteMatching( + './lazy-1/lazy-1#Lazy1Module', /\/test\.ts$/, 'TestModule', /\/lazy-1\/lazy-1\.ts$/, + 'Lazy1Module'), + lazyRouteMatching( + './lazy-2/lazy-2#Lazy2Module', /\/test\.ts$/, 'TestRoutingModule', + /\/lazy-2\/lazy-2\.ts$/, 'Lazy2Module'), + ]); + }); - it('should only process each module once', () => { - env.write('test.ts', ` + it('should only process each module once', () => { + env.write('test.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4417,7 +4537,7 @@ runInEachFileSystem(os => { }) export class TestModule {} `); - env.write('lazy/lazy.ts', ` + env.write('lazy/lazy.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4430,33 +4550,33 @@ runInEachFileSystem(os => { }) export class LazyModule {} `); - env.write('lazier/lazier.ts', ` + env.write('lazier/lazier.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class LazierModule {} `); - const routes = env.driveRoutes(absoluteFrom('/test#TestModule')); - - // `LazyModule` is referenced in both `SharedModule` and `TestModule`, - // but it is only processed once (hence one `LazierModule` entry). - expect(routes).toEqual([ - lazyRouteMatching( - './lazy/lazy#LazyModule', /\/test\.ts$/, 'TestModule', /\/lazy\/lazy\.ts$/, - 'LazyModule'), - lazyRouteMatching( - './lazy/lazy#LazyModule', /\/test\.ts$/, 'SharedModule', /\/lazy\/lazy\.ts$/, - 'LazyModule'), - lazyRouteMatching( - '../lazier/lazier#LazierModule', /\/lazy\/lazy\.ts$/, 'LazyModule', - /\/lazier\/lazier\.ts$/, 'LazierModule'), - ]); - }); + const routes = env.driveRoutes(absoluteFrom('/test#TestModule')); + + // `LazyModule` is referenced in both `SharedModule` and `TestModule`, + // but it is only processed once (hence one `LazierModule` entry). + expect(routes).toEqual([ + lazyRouteMatching( + './lazy/lazy#LazyModule', /\/test\.ts$/, 'TestModule', /\/lazy\/lazy\.ts$/, + 'LazyModule'), + lazyRouteMatching( + './lazy/lazy#LazyModule', /\/test\.ts$/, 'SharedModule', /\/lazy\/lazy\.ts$/, + 'LazyModule'), + lazyRouteMatching( + '../lazier/lazier#LazierModule', /\/lazy\/lazy\.ts$/, 'LazyModule', + /\/lazier\/lazier\.ts$/, 'LazierModule'), + ]); + }); - it('should detect lazy routes in all root directories', () => { - env.tsconfig({}, ['./foo/other-root-dir', './bar/other-root-dir']); - env.write('src/test.ts', ` + it('should detect lazy routes in all root directories', () => { + env.tsconfig({}, ['./foo/other-root-dir', './bar/other-root-dir']); + env.write('src/test.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4469,7 +4589,7 @@ runInEachFileSystem(os => { }) export class TestModule {} `); - env.write('foo/other-root-dir/src/lazy-foo.ts', ` + env.write('foo/other-root-dir/src/lazy-foo.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4482,7 +4602,7 @@ runInEachFileSystem(os => { }) export class LazyFooModule {} `); - env.write('bar/other-root-dir/src/lazy-bar.ts', ` + env.write('bar/other-root-dir/src/lazy-bar.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4495,30 +4615,30 @@ runInEachFileSystem(os => { }) export class LazyBarModule {} `); - env.write('bar/other-root-dir/src/lazier-bar.ts', ` + env.write('bar/other-root-dir/src/lazier-bar.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class LazierBarModule {} `); - const routes = env.driveRoutes(absoluteFrom('/src/test#TestModule')); - - expect(routes).toEqual([ - lazyRouteMatching( - './lazy-foo#LazyFooModule', /\/test\.ts$/, 'TestModule', - /\/foo\/other-root-dir\/src\/lazy-foo\.ts$/, 'LazyFooModule'), - lazyRouteMatching( - './lazy-bar#LazyBarModule', /\/foo\/other-root-dir\/src\/lazy-foo\.ts$/, - 'LazyFooModule', /\/bar\/other-root-dir\/src\/lazy-bar\.ts$/, 'LazyBarModule'), - lazyRouteMatching( - './lazier-bar#LazierBarModule', /\/bar\/other-root-dir\/src\/lazy-bar\.ts$/, - 'LazyBarModule', /\/bar\/other-root-dir\/src\/lazier-bar\.ts$/, 'LazierBarModule'), - ]); - }); + const routes = env.driveRoutes(absoluteFrom('/src/test#TestModule')); + + expect(routes).toEqual([ + lazyRouteMatching( + './lazy-foo#LazyFooModule', /\/test\.ts$/, 'TestModule', + /\/foo\/other-root-dir\/src\/lazy-foo\.ts$/, 'LazyFooModule'), + lazyRouteMatching( + './lazy-bar#LazyBarModule', /\/foo\/other-root-dir\/src\/lazy-foo\.ts$/, + 'LazyFooModule', /\/bar\/other-root-dir\/src\/lazy-bar\.ts$/, 'LazyBarModule'), + lazyRouteMatching( + './lazier-bar#LazierBarModule', /\/bar\/other-root-dir\/src\/lazy-bar\.ts$/, + 'LazyBarModule', /\/bar\/other-root-dir\/src\/lazier-bar\.ts$/, 'LazierBarModule'), + ]); + }); - it('should ignore modules not (transitively) referenced by the entry module', () => { - env.write('test.ts', ` + it('should ignore modules not (transitively) referenced by the entry module', () => { + env.write('test.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4540,7 +4660,7 @@ runInEachFileSystem(os => { }) export class Test2Module {} `); - env.write('lazy/lazy.ts', ` + env.write('lazy/lazy.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) @@ -4550,17 +4670,17 @@ runInEachFileSystem(os => { export class Lazy2Module {} `); - const routes = env.driveRoutes(absoluteFrom('/test#Test1Module')); + const routes = env.driveRoutes(absoluteFrom('/test#Test1Module')); - expect(routes).toEqual([ - lazyRouteMatching( - './lazy/lazy#Lazy1Module', /\/test\.ts$/, 'Test1Module', /\/lazy\/lazy\.ts$/, - 'Lazy1Module'), - ]); - }); + expect(routes).toEqual([ + lazyRouteMatching( + './lazy/lazy#Lazy1Module', /\/test\.ts$/, 'Test1Module', /\/lazy\/lazy\.ts$/, + 'Lazy1Module'), + ]); + }); - it('should ignore routes to unknown modules', () => { - env.write('test.ts', ` + it('should ignore routes to unknown modules', () => { + env.write('test.ts', ` import {NgModule} from '@angular/core'; import {RouterModule} from '@angular/router'; @@ -4574,61 +4694,59 @@ runInEachFileSystem(os => { }) export class TestModule {} `); - env.write('lazy/lazy.ts', ` + env.write('lazy/lazy.ts', ` import {NgModule} from '@angular/core'; @NgModule({}) export class LazyModule {} `); - const routes = env.driveRoutes(absoluteFrom('/test#TestModule')); + const routes = env.driveRoutes(absoluteFrom('/test#TestModule')); - expect(routes).toEqual([ - lazyRouteMatching( - './lazy/lazy#LazyModule', /\/test\.ts$/, 'TestModule', /\/lazy\/lazy\.ts$/, - 'LazyModule'), - ]); - }); + expect(routes).toEqual([ + lazyRouteMatching( + './lazy/lazy#LazyModule', /\/test\.ts$/, 'TestModule', /\/lazy\/lazy\.ts$/, + 'LazyModule'), + ]); }); }); + }); - describe('ivy switch mode', () => { - it('should allow for symbols to be renamed when they use a SWITCH_IVY naming mechanism', - () => { - env.write('test.ts', ` + describe('ivy switch mode', () => { + it('should allow for symbols to be renamed when they use a SWITCH_IVY naming mechanism', () => { + env.write('test.ts', ` export const FooCmp__POST_R3__ = 1; export const FooCmp__PRE_R3__ = 2; export const FooCmp = FooCmp__PRE_R3__;`); - env.driveMain(); + env.driveMain(); - const source = env.getContents('test.js'); - expect(source).toContain(`export var FooCmp = FooCmp__POST_R3__`); - expect(source).not.toContain(`export var FooCmp = FooCmp__PRE_R3__`); - }); + const source = env.getContents('test.js'); + expect(source).toContain(`export var FooCmp = FooCmp__POST_R3__`); + expect(source).not.toContain(`export var FooCmp = FooCmp__PRE_R3__`); + }); - it('should allow for SWITCH_IVY naming even even if it occurs outside of core', () => { - const content = ` + it('should allow for SWITCH_IVY naming even even if it occurs outside of core', () => { + const content = ` export const Foo__POST_R3__ = 1; export const Foo__PRE_R3__ = 2; export const Foo = Foo__PRE_R3__; `; - env.write('test_outside_angular_core.ts', content); - env.write( - 'test_inside_angular_core.ts', content + '\nexport const ITS_JUST_ANGULAR = true;'); - env.driveMain(); + env.write('test_outside_angular_core.ts', content); + env.write('test_inside_angular_core.ts', content + '\nexport const ITS_JUST_ANGULAR = true;'); + env.driveMain(); - const sourceTestOutsideAngularCore = env.getContents('test_outside_angular_core.js'); - const sourceTestInsideAngularCore = env.getContents('test_inside_angular_core.js'); - expect(sourceTestInsideAngularCore).toContain(sourceTestOutsideAngularCore); - }); + const sourceTestOutsideAngularCore = env.getContents('test_outside_angular_core.js'); + const sourceTestInsideAngularCore = env.getContents('test_inside_angular_core.js'); + expect(sourceTestInsideAngularCore).toContain(sourceTestOutsideAngularCore); }); + }); - describe('NgModule export aliasing', () => { - it('should use an alias to import a directive from a deep dependency', () => { - env.tsconfig({'_useHostForImportGeneration': true}); + describe('NgModule export aliasing', () => { + it('should use an alias to import a directive from a deep dependency', () => { + env.tsconfig({'_useHostForImportGeneration': true}); - // 'alpha' declares the directive which will ultimately be imported. - env.write('alpha.d.ts', ` + // 'alpha' declares the directive which will ultimately be imported. + env.write('alpha.d.ts', ` import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; export declare class ExternalDir { @@ -4640,8 +4758,8 @@ export const Foo = Foo__PRE_R3__; } `); - // 'beta' re-exports AlphaModule from alpha. - env.write('beta.d.ts', ` + // 'beta' re-exports AlphaModule from alpha. + env.write('beta.d.ts', ` import {ɵɵNgModuleDefWithMeta} from '@angular/core'; import {AlphaModule} from './alpha'; @@ -4650,9 +4768,9 @@ export const Foo = Foo__PRE_R3__; } `); - // The application imports BetaModule from beta, gaining visibility of ExternalDir from - // alpha. - env.write('test.ts', ` + // The application imports BetaModule from beta, gaining visibility of ExternalDir from + // alpha. + env.write('test.ts', ` import {Component, NgModule} from '@angular/core'; import {BetaModule} from './beta'; @@ -4668,17 +4786,17 @@ export const Foo = Foo__PRE_R3__; }) export class Module {} `); - env.driveMain(); - const jsContents = env.getContents('test.js'); + env.driveMain(); + const jsContents = env.getContents('test.js'); - // Expect that ExternalDir from alpha is imported via the re-export from beta. - expect(jsContents).toContain('import * as i1 from "root/beta";'); - expect(jsContents).toContain('directives: [i1.\u0275ng$root$alpha$$ExternalDir]'); - }); + // Expect that ExternalDir from alpha is imported via the re-export from beta. + expect(jsContents).toContain('import * as i1 from "root/beta";'); + expect(jsContents).toContain('directives: [i1.\u0275ng$root$alpha$$ExternalDir]'); + }); - it('should write alias ES2015 exports for NgModule exported directives', () => { - env.tsconfig({'_useHostForImportGeneration': true}); - env.write('external.d.ts', ` + it('should write alias ES2015 exports for NgModule exported directives', () => { + env.tsconfig({'_useHostForImportGeneration': true}); + env.write('external.d.ts', ` import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; import {LibModule} from './lib'; @@ -4690,7 +4808,7 @@ export const Foo = Foo__PRE_R3__; static ɵmod: ɵɵNgModuleDefWithMeta; } `); - env.write('lib.d.ts', ` + env.write('lib.d.ts', ` import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core'; export declare class LibDir { @@ -4701,7 +4819,7 @@ export const Foo = Foo__PRE_R3__; static ɵmod: ɵɵNgModuleDefWithMeta; } `); - env.write('foo.ts', ` + env.write('foo.ts', ` import {Directive, NgModule} from '@angular/core'; import {ExternalModule} from './external'; @@ -4714,7 +4832,7 @@ export const Foo = Foo__PRE_R3__; }) export class FooModule {} `); - env.write('index.ts', ` + env.write('index.ts', ` import {Component, NgModule} from '@angular/core'; import {FooModule} from './foo'; @@ -4730,15 +4848,15 @@ export const Foo = Foo__PRE_R3__; }) export class IndexModule {} `); - env.driveMain(); - const jsContents = env.getContents('index.js'); - expect(jsContents) - .toContain('export { FooDir as \u0275ng$root$foo$$FooDir } from "root/foo";'); - }); + env.driveMain(); + const jsContents = env.getContents('index.js'); + expect(jsContents) + .toContain('export { FooDir as \u0275ng$root$foo$$FooDir } from "root/foo";'); + }); - it('should escape unusual characters in aliased filenames', () => { - env.tsconfig({'_useHostForImportGeneration': true}); - env.write('other._$test.ts', ` + it('should escape unusual characters in aliased filenames', () => { + env.tsconfig({'_useHostForImportGeneration': true}); + env.write('other._$test.ts', ` import {Directive, NgModule} from '@angular/core'; @Directive({selector: 'test'}) @@ -4750,7 +4868,7 @@ export const Foo = Foo__PRE_R3__; }) export class OtherModule {} `); - env.write('index.ts', ` + env.write('index.ts', ` import {NgModule} from '@angular/core'; import {OtherModule} from './other._$test'; @@ -4759,59 +4877,59 @@ export const Foo = Foo__PRE_R3__; }) export class IndexModule {} `); - env.driveMain(); - const jsContents = env.getContents('index.js'); - expect(jsContents) - .toContain( - 'export { TestDir as \u0275ng$root$other___test$$TestDir } from "root/other._$test";'); - }); + env.driveMain(); + const jsContents = env.getContents('index.js'); + expect(jsContents) + .toContain( + 'export { TestDir as \u0275ng$root$other___test$$TestDir } from "root/other._$test";'); }); + }); - describe('disableTypeScriptVersionCheck', () => { - afterEach(() => restoreTypeScriptVersionForTesting()); + describe('disableTypeScriptVersionCheck', () => { + afterEach(() => restoreTypeScriptVersionForTesting()); - it('produces an error when not supported and version check is enabled', () => { - setTypeScriptVersionForTesting('3.4.0'); - env.tsconfig({disableTypeScriptVersionCheck: false}); - env.write('empty.ts', ''); + it('produces an error when not supported and version check is enabled', () => { + setTypeScriptVersionForTesting('3.4.0'); + env.tsconfig({disableTypeScriptVersionCheck: false}); + env.write('empty.ts', ''); - const diags = env.driveDiagnostics(); - expect(diags.length).toBe(1); - expect(diags[0].messageText).toContain('but 3.4.0 was found instead'); - }); + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(1); + expect(diags[0].messageText).toContain('but 3.4.0 was found instead'); + }); - it('does not produce an error when supported and version check is enabled', () => { - env.tsconfig({disableTypeScriptVersionCheck: false}); - env.write('empty.ts', ''); + it('does not produce an error when supported and version check is enabled', () => { + env.tsconfig({disableTypeScriptVersionCheck: false}); + env.write('empty.ts', ''); - // The TypeScript version is not overwritten, so the version - // that is actually used should be supported - const diags = env.driveDiagnostics(); - expect(diags.length).toBe(0); - }); + // The TypeScript version is not overwritten, so the version + // that is actually used should be supported + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(0); + }); - it('does not produce an error when not supported but version check is disabled', () => { - setTypeScriptVersionForTesting('3.4.0'); - env.tsconfig({disableTypeScriptVersionCheck: true}); - env.write('empty.ts', ''); + it('does not produce an error when not supported but version check is disabled', () => { + setTypeScriptVersionForTesting('3.4.0'); + env.tsconfig({disableTypeScriptVersionCheck: true}); + env.write('empty.ts', ''); - const diags = env.driveDiagnostics(); - expect(diags.length).toBe(0); - }); + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(0); + }); - it('produces an error when not supported using default configuration', () => { - setTypeScriptVersionForTesting('3.4.0'); - env.write('empty.ts', ''); + it('produces an error when not supported using default configuration', () => { + setTypeScriptVersionForTesting('3.4.0'); + env.write('empty.ts', ''); - const diags = env.driveDiagnostics(); - expect(diags.length).toBe(1); - expect(diags[0].messageText).toContain('but 3.4.0 was found instead'); - }); + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(1); + expect(diags[0].messageText).toContain('but 3.4.0 was found instead'); }); + }); - describe('inline resources', () => { - it('should process inline