diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts index 6754c93cc2..446b9f39b6 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/context.ts @@ -385,7 +385,8 @@ export class TypeCheckContextImpl implements TypeCheckContext { path: pendingShimData.file.fileName, templates: pendingShimData.templates, }); - updates.set(pendingShimData.file.fileName, pendingShimData.file.render()); + updates.set( + pendingShimData.file.fileName, pendingShimData.file.render(false /* removeComments */)); } } diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_file.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_file.ts index 88efdeb6b0..56c2f85e71 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_file.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_file.ts @@ -49,12 +49,12 @@ export class TypeCheckFile extends Environment { this.tcbStatements.push(fn); } - render(): string { + render(removeComments: boolean): string { let source: string = this.importManager.getAllImports(this.contextFile.fileName) .map(i => `import * as ${i.qualifier.text} from '${i.specifier}';`) .join('\n') + '\n\n'; - const printer = ts.createPrinter(); + const printer = ts.createPrinter({removeComments}); source += '\n'; for (const stmt of this.pipeInstStatements) { source += printer.printNode(ts.EmitHint.Unspecified, stmt, this.contextFile) + '\n'; diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts index dae00dc0ef..231a1e1712 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts @@ -6,9 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ +import {initMockFileSystem} from '../../file_system/testing'; import {tcb, TestDeclaration} from './test_utils'; describe('type check blocks diagnostics', () => { + beforeEach(() => initMockFileSystem('Native')); + describe('parse spans', () => { it('should annotate unary ops', () => { expect(tcbWithSpans('{{ -a }}')).toContain('(-((ctx).a /*4,5*/) /*4,5*/) /*3,5*/'); @@ -146,8 +149,9 @@ describe('type check blocks diagnostics', () => { pipeName: 'test', }]; const block = tcbWithSpans(TEMPLATE, PIPES); + expect(block).toContain('var _pipe1: i0.TestPipe = null!'); expect(block).toContain( - '((null as TestPipe).transform /*7,11*/(((ctx).a /*3,4*/) /*3,4*/, ((ctx).b /*12,13*/) /*12,13*/) /*3,13*/);'); + '(_pipe1.transform /*7,11*/(((ctx).a /*3,4*/) /*3,4*/, ((ctx).b /*12,13*/) /*12,13*/) /*3,13*/);'); }); describe('attaching multiple comments for multiple references', () => { diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts index 23b82d9fcd..0f1caacc25 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/test_utils.ts @@ -11,7 +11,7 @@ import * as ts from 'typescript'; import {absoluteFrom, AbsoluteFsPath, getSourceFileOrError, LogicalFileSystem} from '../../file_system'; import {TestFile} from '../../file_system/testing'; -import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, Reexport, Reference, ReferenceEmitter} from '../../imports'; +import {AbsoluteModuleStrategy, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, Reexport, Reference, ReferenceEmitter, RelativePathStrategy} from '../../imports'; import {NOOP_INCREMENTAL_BUILD} from '../../incremental'; import {ClassPropertyMapping, CompoundMetadataReader} from '../../metadata'; import {ClassDeclaration, isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection'; @@ -28,6 +28,7 @@ import {Environment} from '../src/environment'; import {OutOfBandDiagnosticRecorder} from '../src/oob'; import {TypeCheckShimGenerator} from '../src/shim'; import {generateTypeCheckBlock} from '../src/type_check_block'; +import {TypeCheckFile} from '../src/type_check_file'; export function typescriptLibDts(): TestFile { return { @@ -201,6 +202,7 @@ export type TestDirective = Partial decl.name)]; - const code = classes.map(name => `class ${name} {}`).join('\n'); + const code = classes.map(name => `export class ${name} {}`).join('\n'); - const sf = ts.createSourceFile('synthetic.ts', code, ts.ScriptTarget.Latest, true); + const rootFilePath = absoluteFrom('/synthetic.ts'); + const {program, host} = makeProgram([ + {name: rootFilePath, contents: code, isRoot: true}, + ]); + + const sf = getSourceFileOrError(program, rootFilePath); const clazz = getClass(sf, 'Test'); const templateUrl = 'synthetic.html'; const {nodes} = parseTemplate(template, templateUrl); @@ -251,13 +258,25 @@ export function tcb( emitSpans: false, }; - const tcb = generateTypeCheckBlock( - FakeEnvironment.newFake(config), new Reference(clazz), ts.createIdentifier('Test_TCB'), meta, - new NoopSchemaChecker(), new NoopOobRecorder()); + const fileName = absoluteFrom('/type-check-file.ts'); - const removeComments = !options.emitSpans; - const res = ts.createPrinter({removeComments}).printNode(ts.EmitHint.Unspecified, tcb, sf); - return res.replace(/\s+/g, ' '); + const reflectionHost = new TypeScriptReflectionHost(program.getTypeChecker()); + + const refEmmiter: ReferenceEmitter = new ReferenceEmitter( + [new LocalIdentifierStrategy(), new RelativePathStrategy(reflectionHost)]); + + const env = new TypeCheckFile(fileName, config, refEmmiter, reflectionHost, host); + + const ref = new Reference(clazz); + + const tcb = generateTypeCheckBlock( + env, ref, ts.createIdentifier('Test_TCB'), meta, new NoopSchemaChecker(), + new NoopOobRecorder()); + + env.addTypeCheckBlock(ref, meta, new NoopSchemaChecker(), new NoopOobRecorder()); + + const rendered = env.render(!options.emitSpans /* removeComments */); + return rendered.replace(/\s+/g, ' '); } /** diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts index 6dc48d7c34..6a36e7547c 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts @@ -6,12 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ +import {initMockFileSystem} from '../../file_system/testing'; import {TypeCheckingConfig} from '../api'; import {ALL_ENABLED_CONFIG, tcb, TestDeclaration, TestDirective} from './test_utils'; describe('type check blocks', () => { + beforeEach(() => initMockFileSystem('Native')); + it('should generate a basic block for a binding', () => { expect(tcb('{{hello}} {{world}}')).toContain('"" + (((ctx).hello)) + (((ctx).world));'); }); @@ -60,7 +63,7 @@ describe('type check blocks', () => { selector: '[dir]', inputs: {inputA: 'inputA'}, }]; - expect(tcb(TEMPLATE, DIRECTIVES)).toContain('_t1: DirA = null!; _t1.inputA = ("value");'); + expect(tcb(TEMPLATE, DIRECTIVES)).toContain('_t1: i0.DirA = null!; _t1.inputA = ("value");'); }); it('should handle multiple bindings to the same property', () => { @@ -133,9 +136,11 @@ describe('type check blocks', () => { }, isGeneric: true, }]; - expect(tcb(TEMPLATE, DIRECTIVES)) - .toContain( - 'var _t1 = Dir.ngTypeCtor({ "fieldA": (((ctx).foo)), "fieldB": null as any });'); + const actual = tcb(TEMPLATE, DIRECTIVES); + expect(actual).toContain( + 'const _ctor1: (init: Pick, "fieldA" | "fieldB">) => i0.Dir = null!;'); + expect(actual).toContain( + 'var _t1 = _ctor1({ "fieldA": (((ctx).foo)), "fieldB": null as any });'); }); it('should handle multiple bindings to the same property', () => { @@ -183,11 +188,14 @@ describe('type check blocks', () => { inputs: {input: 'input'}, isGeneric: true, }]; - expect(tcb(TEMPLATE, DIRECTIVES)) - .toContain( - 'var _t2 = Dir.ngTypeCtor({ "input": (null!) }); ' + - 'var _t1 = _t2; ' + - '_t2.input = (_t1);'); + + const actual = tcb(TEMPLATE, DIRECTIVES); + expect(actual).toContain( + 'const _ctor1: (init: Pick, "input">) => i0.Dir = null!;'); + expect(actual).toContain( + 'var _t2 = _ctor1({ "input": (null!) }); ' + + 'var _t1 = _t2; ' + + '_t2.input = (_t1);'); }); it('should generate circular references between two directives correctly', () => { @@ -213,14 +221,16 @@ describe('type check blocks', () => { isGeneric: true, } ]; - expect(tcb(TEMPLATE, DIRECTIVES)) - .toContain( - 'var _t4 = DirA.ngTypeCtor({ "inputA": (null!) }); ' + - 'var _t3 = _t4; ' + - 'var _t2 = DirB.ngTypeCtor({ "inputB": (_t3) }); ' + - 'var _t1 = _t2; ' + - '_t4.inputA = (_t1); ' + - '_t2.inputB = (_t3);'); + const actual = tcb(TEMPLATE, DIRECTIVES); + expect(actual).toContain( + 'const _ctor1: (init: Pick, "inputA">) => i0.DirA = null!; const _ctor2: (init: Pick, "inputB">) => i0.DirB = null!;'); + expect(actual).toContain( + 'var _t4 = _ctor1({ "inputA": (null!) }); ' + + 'var _t3 = _t4; ' + + 'var _t2 = _ctor2({ "inputB": (_t3) }); ' + + 'var _t1 = _t2; ' + + '_t4.inputA = (_t1); ' + + '_t2.inputB = (_t3);'); }); it('should handle empty bindings', () => { @@ -261,7 +271,7 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t1: typeof Dir.ngAcceptInputType_fieldA = null!; ' + + 'var _t1: typeof i0.Dir.ngAcceptInputType_fieldA = null!; ' + '_t1 = (((ctx).foo));'); }); }); @@ -321,11 +331,11 @@ describe('type check blocks', () => { }, ]; const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('var _t1: HasInput = null!'); + expect(block).toContain('var _t1: i0.HasInput = null!'); expect(block).toContain('_t1.input = (((ctx).value));'); - expect(block).toContain('var _t2: HasOutput = null!'); + expect(block).toContain('var _t2: i0.HasOutput = null!'); expect(block).toContain('_t2["output"]'); - expect(block).toContain('var _t4: HasReference = null!'); + expect(block).toContain('var _t4: i0.HasReference = null!'); expect(block).toContain('var _t3 = _t4;'); expect(block).toContain('(_t3).a'); expect(block).not.toContain('NoBindings'); @@ -355,7 +365,7 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t2: Dir = null!; ' + + 'var _t2: i0.Dir = null!; ' + 'var _t1 = _t2; ' + '"" + (((_t1).value));'); }); @@ -403,7 +413,7 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t2: Dir = null!; ' + + 'var _t2: i0.Dir = null!; ' + 'var _t1 = _t2; ' + '_t2.input = (_t1);'); }); @@ -431,9 +441,9 @@ describe('type check blocks', () => { ]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t2: DirB = null!; ' + + 'var _t2: i0.DirB = null!; ' + 'var _t1 = _t2; ' + - 'var _t3: DirA = null!; ' + + 'var _t3: i0.DirA = null!; ' + '_t3.inputA = (_t1); ' + 'var _t4 = _t3; ' + '_t2.inputA = (_t4);'); @@ -468,7 +478,7 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t1: Dir = null!; ' + + 'var _t1: i0.Dir = null!; ' + 'var _t2: typeof _t1["fieldA"] = null!; ' + '_t2 = (((ctx).foo)); '); }); @@ -487,7 +497,7 @@ describe('type check blocks', () => { }]; const block = tcb(TEMPLATE, DIRECTIVES); expect(block).toContain( - 'var _t1: Dir = null!; ' + + 'var _t1: i0.Dir = null!; ' + '_t1["some-input.xs"] = (((ctx).foo)); '); }); @@ -504,7 +514,7 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t1: Dir = null!; ' + + 'var _t1: i0.Dir = null!; ' + '_t1.field2 = _t1.field1 = (((ctx).foo));'); }); @@ -523,8 +533,8 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t1: typeof Dir.ngAcceptInputType_field1 = null!; ' + - 'var _t2: Dir = null!; ' + + 'var _t1: typeof i0.Dir.ngAcceptInputType_field1 = null!; ' + + 'var _t2: i0.Dir = null!; ' + '_t2.field2 = _t1 = (((ctx).foo));'); }); @@ -543,7 +553,7 @@ describe('type check blocks', () => { }]; expect(tcb(TEMPLATE, DIRECTIVES)) .toContain( - 'var _t1: Dir = null!; ' + + 'var _t1: i0.Dir = null!; ' + '_t1.field2 = (((ctx).foo));'); }); @@ -561,7 +571,7 @@ describe('type check blocks', () => { const block = tcb(TEMPLATE, DIRECTIVES); expect(block).not.toContain('var _t1: Dir = null!;'); expect(block).toContain( - 'var _t1: typeof Dir.ngAcceptInputType_fieldA = null!; ' + + 'var _t1: typeof i0.Dir.ngAcceptInputType_fieldA = null!; ' + '_t1 = (((ctx).foo));'); }); @@ -580,7 +590,7 @@ describe('type check blocks', () => { const block = tcb(TEMPLATE, DIRECTIVES); expect(block).not.toContain('var _t1: Dir = null!;'); expect(block).toContain( - 'var _t1: typeof Dir.ngAcceptInputType_fieldA = null!; ' + + 'var _t1: typeof i0.Dir.ngAcceptInputType_fieldA = null!; ' + '_t1 = (((ctx).foo));'); }); @@ -619,7 +629,7 @@ describe('type check blocks', () => { }]; const TEMPLATE = `
{{person.name}}
`; const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('if (NgIf.ngTemplateGuard_ngIf(_t1, ((ctx).person)))'); + expect(block).toContain('if (i0.NgIf.ngTemplateGuard_ngIf(_t1, ((ctx).person)))'); }); it('should emit binding guards', () => { @@ -672,8 +682,7 @@ describe('type check blocks', () => { it('should emit a listener function with AnimationEvent for animation events', () => { const TEMPLATE = `
`; const block = tcb(TEMPLATE); - expect(block).toContain( - 'function ($event: animations.AnimationEvent): any { (ctx).foo($event); }'); + expect(block).toContain('function ($event: i1.AnimationEvent): any { (ctx).foo($event); }'); }); it('should emit addEventListener calls for unclaimed outputs', () => { @@ -740,7 +749,7 @@ describe('type check blocks', () => { describe('config.applyTemplateContextGuards', () => { const TEMPLATE = `
{{ value }}
`; - const GUARD_APPLIED = 'if (Dir.ngTemplateContextGuard('; + const GUARD_APPLIED = 'if (i0.Dir.ngTemplateContextGuard('; it('should apply template context guards when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); @@ -769,13 +778,13 @@ describe('type check blocks', () => { it('generates a references var when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('var _t1 = (_t2 as any as core.TemplateRef);'); + expect(block).toContain('var _t1 = (_t2 as any as i1.TemplateRef);'); }); it('generates a reference var when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTemplateBodies: false}; const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); - expect(block).toContain('var _t1 = (_t2 as any as core.TemplateRef);'); + expect(block).toContain('var _t1 = (_t2 as any as i1.TemplateRef);'); }); }); @@ -848,8 +857,7 @@ describe('type check blocks', () => { it('should check types of animation events when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain( - 'function ($event: animations.AnimationEvent): any { (ctx).foo($event); }'); + expect(block).toContain('function ($event: i1.AnimationEvent): any { (ctx).foo($event); }'); }); it('should not check types of animation events when disabled', () => { const DISABLED_CONFIG: @@ -919,7 +927,7 @@ describe('type check blocks', () => { it('should trace references to an when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); expect(block).toContain( - 'var _t3 = (_t4 as any as core.TemplateRef); ' + + 'var _t3 = (_t4 as any as i1.TemplateRef); ' + '"" + (((_t3).value2));'); }); @@ -968,13 +976,14 @@ describe('type check blocks', () => { it('should check types of pipes when enabled', () => { const block = tcb(TEMPLATE, PIPES); - expect(block).toContain('(null as TestPipe).transform(((ctx).a), ((ctx).b), ((ctx).c))'); + expect(block).toContain('var _pipe1: i0.TestPipe = null!;'); + expect(block).toContain('(_pipe1.transform(((ctx).a), ((ctx).b), ((ctx).c)));'); }); it('should not check types of pipes when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfPipes: false}; const block = tcb(TEMPLATE, PIPES, DISABLED_CONFIG); - expect(block).toContain( - '((null as TestPipe) as any).transform(((ctx).a), ((ctx).b), ((ctx).c))'); + expect(block).toContain('var _pipe1: i0.TestPipe = null!;'); + expect(block).toContain('((_pipe1 as any).transform(((ctx).a), ((ctx).b), ((ctx).c)));'); }); }); @@ -1016,13 +1025,13 @@ describe('type check blocks', () => { it('should use the generic type of the context when enabled', () => { const block = tcb(TEMPLATE); - expect(block).toContain('function Test_TCB(ctx: Test)'); + expect(block).toContain('function _tcb1(ctx: i0.Test)'); }); it('should use any for the context generic type when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, useContextGenericType: false}; const block = tcb(TEMPLATE, undefined, DISABLED_CONFIG); - expect(block).toContain('function Test_TCB(ctx: Test)'); + expect(block).toContain('function _tcb1(ctx: i0.Test)'); }); }); @@ -1045,7 +1054,7 @@ describe('type check blocks', () => { TypeCheckingConfig = {...BASE_CONFIG, honorAccessModifiersForInputBindings: true}; const block = tcb(TEMPLATE, DIRECTIVES, enableChecks); expect(block).toContain( - 'var _t1: Dir = null!; ' + + 'var _t1: i0.Dir = null!; ' + '_t1["some-input.xs"] = (((ctx).foo)); '); }); @@ -1063,7 +1072,7 @@ describe('type check blocks', () => { TypeCheckingConfig = {...BASE_CONFIG, honorAccessModifiersForInputBindings: true}; const block = tcb(TEMPLATE, DIRECTIVES, enableChecks); expect(block).toContain( - 'var _t1: Dir = null!; ' + + 'var _t1: i0.Dir = null!; ' + '_t1.fieldA = (((ctx).foo)); '); }); }); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_constructor_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_constructor_spec.ts index f490cda7f5..4ac6638c0e 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_constructor_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_constructor_spec.ts @@ -43,7 +43,7 @@ runInEachFileSystem(() => { const file = new TypeCheckFile( _('/_typecheck_.ts'), ALL_ENABLED_CONFIG, new ReferenceEmitter([]), /* reflector */ null!, host); - const sf = file.render(); + const sf = file.render(false /* removeComments */); expect(sf).toContain('export const IS_A_MODULE = true;'); });