From 9cb43fb507b2db7d05fbcdaaf85f5fef36eded5d Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Thu, 11 Feb 2021 14:42:10 +0000 Subject: [PATCH] =?UTF-8?q?refactor(compiler-cli):=20implement=20`=C9=B5?= =?UTF-8?q?=C9=B5ngDeclarePipe()`=20(#40803)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit implements creating of `ɵɵngDeclarePipe()` calls in partial compilation, and processing of those calls in the linker and JIT compiler. See #40677 PR Close #40803 --- .../partial_linker_selector.ts | 9 ++- .../partial_linkers/partial_pipe_linker_1.ts | 58 +++++++++++++++++++ .../partial_linker_selector_spec.ts | 4 ++ .../src/ngtsc/annotations/src/pipe.ts | 20 +++++-- .../GOLDEN_PARTIAL.js | 2 +- .../pipes/GOLDEN_PARTIAL.js | 10 ++-- .../elements/GOLDEN_PARTIAL.js | 2 +- .../property_bindings/GOLDEN_PARTIAL.js | 2 +- .../r3_view_compiler_di/di/GOLDEN_PARTIAL.js | 4 +- .../element_attributes/GOLDEN_PARTIAL.js | 8 +-- .../nested_nodes/GOLDEN_PARTIAL.js | 8 +-- .../GOLDEN_PARTIAL.js | 4 +- .../mixed_style_and_class/GOLDEN_PARTIAL.js | 6 +- .../inline_templates/GOLDEN_PARTIAL.js | 8 +-- packages/compiler/src/compiler.ts | 1 + .../compiler/src/compiler_facade_interface.ts | 8 +++ packages/compiler/src/jit_compiler_facade.ts | 22 ++++++- packages/compiler/src/render3/partial/api.ts | 41 +++++++++++-- packages/compiler/src/render3/partial/pipe.ts | 50 ++++++++++++++++ .../compiler/src/render3/r3_identifiers.ts | 1 + .../compiler/src/render3/r3_pipe_compiler.ts | 13 +++-- packages/compiler/src/render3/view/api.ts | 8 +++ .../src/compiler/compiler_facade_interface.ts | 8 +++ .../core/src/core_render3_private_export.ts | 1 + packages/core/src/render3/jit/partial.ts | 12 +++- .../core/test/render3/jit_environment_spec.ts | 1 + 26 files changed, 265 insertions(+), 46 deletions(-) create mode 100644 packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_pipe_linker_1.ts create mode 100644 packages/compiler/src/render3/partial/pipe.ts diff --git a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker_selector.ts b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker_selector.ts index 461e2522f9..b4d33f4d57 100644 --- a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker_selector.ts +++ b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker_selector.ts @@ -14,10 +14,12 @@ import {LinkerEnvironment} from '../linker_environment'; import {PartialComponentLinkerVersion1} from './partial_component_linker_1'; import {PartialDirectiveLinkerVersion1} from './partial_directive_linker_1'; import {PartialLinker} from './partial_linker'; +import {PartialPipeLinkerVersion1} from './partial_pipe_linker_1'; export const ɵɵngDeclareDirective = 'ɵɵngDeclareDirective'; export const ɵɵngDeclareComponent = 'ɵɵngDeclareComponent'; -export const declarationFunctions = [ɵɵngDeclareDirective, ɵɵngDeclareComponent]; +export const ɵɵngDeclarePipe = 'ɵɵngDeclarePipe'; +export const declarationFunctions = [ɵɵngDeclareDirective, ɵɵngDeclareComponent, ɵɵngDeclarePipe]; interface LinkerRange { range: string; @@ -81,6 +83,7 @@ export class PartialLinkerSelector { const partialComponentLinkerVersion1 = new PartialComponentLinkerVersion1( environment, createGetSourceFile(sourceUrl, code, environment.sourceFileLoader), sourceUrl, code); + const partialPipeLinkerVersion1 = new PartialPipeLinkerVersion1(); const linkers = new Map[]>(); linkers.set(ɵɵngDeclareDirective, [ @@ -91,6 +94,10 @@ export class PartialLinkerSelector { {range: '0.0.0-PLACEHOLDER', linker: partialComponentLinkerVersion1}, {range: '>=11.1.0-next.1', linker: partialComponentLinkerVersion1}, ]); + linkers.set(ɵɵngDeclarePipe, [ + {range: '0.0.0-PLACEHOLDER', linker: partialPipeLinkerVersion1}, + {range: '>=11.1.0-next.1', linker: partialPipeLinkerVersion1}, + ]); return linkers; } } diff --git a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_pipe_linker_1.ts b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_pipe_linker_1.ts new file mode 100644 index 0000000000..11b4239a72 --- /dev/null +++ b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_pipe_linker_1.ts @@ -0,0 +1,58 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {compilePipeFromMetadata, ConstantPool, R3DeclarePipeMetadata, R3PartialDeclaration, R3PipeMetadata, R3Reference} from '@angular/compiler'; +import * as o from '@angular/compiler/src/output/output_ast'; + +import {AstObject} from '../../ast/ast_value'; +import {FatalLinkerError} from '../../fatal_linker_error'; + +import {PartialLinker} from './partial_linker'; + +/** + * A `PartialLinker` that is designed to process `ɵɵngDeclarePipe()` call expressions. + */ +export class PartialPipeLinkerVersion1 implements PartialLinker { + constructor() {} + + linkPartialDeclaration( + constantPool: ConstantPool, + metaObj: AstObject): o.Expression { + const meta = toR3PipeMeta(metaObj); + const def = compilePipeFromMetadata(meta); + return def.expression; + } +} + +/** + * Derives the `R3PipeMetadata` structure from the AST object. + */ +export function toR3PipeMeta(metaObj: AstObject): + R3PipeMetadata { + const typeExpr = metaObj.getValue('type'); + const typeName = typeExpr.getSymbolName(); + if (typeName === null) { + throw new FatalLinkerError( + typeExpr.expression, 'Unsupported type, its name could not be determined'); + } + + const pure = metaObj.has('pure') ? metaObj.getBoolean('pure') : true; + + return { + name: typeName, + type: wrapReference(typeExpr.getOpaque()), + internalType: metaObj.getOpaque('type'), + typeArgumentCount: 0, + deps: null, + pipeName: metaObj.getString('name'), + pure, + }; +} + +function wrapReference(wrapped: o.WrappedNodeExpr): R3Reference { + return {value: wrapped, type: wrapped}; +} diff --git a/packages/compiler-cli/linker/test/file_linker/partial_linkers/partial_linker_selector_spec.ts b/packages/compiler-cli/linker/test/file_linker/partial_linkers/partial_linker_selector_spec.ts index 773f4867fa..7d8cc687e8 100644 --- a/packages/compiler-cli/linker/test/file_linker/partial_linkers/partial_linker_selector_spec.ts +++ b/packages/compiler-cli/linker/test/file_linker/partial_linkers/partial_linker_selector_spec.ts @@ -17,6 +17,7 @@ import {LinkerEnvironment} from '../../../src/file_linker/linker_environment'; import {PartialComponentLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_component_linker_1'; import {PartialDirectiveLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_directive_linker_1'; import {PartialLinkerSelector} from '../../../src/file_linker/partial_linkers/partial_linker_selector'; +import {PartialPipeLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_pipe_linker_1'; describe('PartialLinkerSelector', () => { const options: LinkerOptions = { @@ -42,6 +43,7 @@ describe('PartialLinkerSelector', () => { environment, fs.resolve('/some/path/to/file.js'), 'some file contents'); expect(selector.supportsDeclaration('ɵɵngDeclareDirective')).toBe(true); expect(selector.supportsDeclaration('ɵɵngDeclareComponent')).toBe(true); + expect(selector.supportsDeclaration('ɵɵngDeclarePipe')).toBe(true); expect(selector.supportsDeclaration('$foo')).toBe(false); }); @@ -60,6 +62,8 @@ describe('PartialLinkerSelector', () => { .toBeInstanceOf(PartialDirectiveLinkerVersion1); expect(selector.getLinker('ɵɵngDeclareComponent', '0.0.0-PLACEHOLDER')) .toBeInstanceOf(PartialComponentLinkerVersion1); + expect(selector.getLinker('ɵɵngDeclarePipe', '0.0.0-PLACEHOLDER')) + .toBeInstanceOf(PartialPipeLinkerVersion1); }); it('should return the linker that matches the name and valid full version', () => { diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts b/packages/compiler-cli/src/ngtsc/annotations/src/pipe.ts index 2bbc29719a..052720eb34 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 {compilePipeFromMetadata, Identifiers, R3FactoryTarget, R3PipeMetadata, Statement, WrappedNodeExpr} from '@angular/compiler'; +import {compileDeclarePipeFromMetadata, compilePipeFromMetadata, Identifiers, R3FactoryTarget, R3PipeDef, R3PipeMetadata, Statement, WrappedNodeExpr} from '@angular/compiler'; import * as ts from 'typescript'; import {ErrorCode, FatalDiagnosticError} from '../../diagnostics'; @@ -134,10 +134,18 @@ export class PipeDecoratorHandler implements DecoratorHandler): CompileResult[] { - const meta = analysis.meta; - const res = compilePipeFromMetadata(meta); + const res = compilePipeFromMetadata(analysis.meta); + return this.compilePipe(analysis, res); + } + + compilePartial(node: ClassDeclaration, analysis: Readonly): CompileResult[] { + const res = compileDeclarePipeFromMetadata(analysis.meta); + return this.compilePipe(analysis, res); + } + + private compilePipe(analysis: Readonly, def: R3PipeDef) { const factoryRes = compileNgFactoryDefField({ - ...meta, + ...analysis.meta, injectFn: Identifiers.directiveInject, target: R3FactoryTarget.Pipe, }); @@ -147,9 +155,9 @@ export class PipeDecoratorHandler implements DecoratorHandler { + const definitionMap = new DefinitionMap(); + + definitionMap.set('version', o.literal('0.0.0-PLACEHOLDER')); + definitionMap.set('ngImport', o.importExpr(R3.core)); + + // e.g. `type: MyPipe` + definitionMap.set('type', meta.internalType); + // e.g. `name: "myPipe"` + definitionMap.set('name', o.literal(meta.pipeName)); + + if (meta.pure === false) { + // e.g. `pure: false` + definitionMap.set('pure', o.literal(meta.pure)); + } + + return definitionMap; +} diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts index d3163693a8..30b5005bad 100644 --- a/packages/compiler/src/render3/r3_identifiers.ts +++ b/packages/compiler/src/render3/r3_identifiers.ts @@ -294,6 +294,7 @@ export class Identifiers { static PipeDefWithMeta: o.ExternalReference = {name: 'ɵɵPipeDefWithMeta', moduleName: CORE}; static definePipe: o.ExternalReference = {name: 'ɵɵdefinePipe', moduleName: CORE}; + static declarePipe: o.ExternalReference = {name: 'ɵɵngDeclarePipe', moduleName: CORE}; static queryRefresh: o.ExternalReference = {name: 'ɵɵqueryRefresh', moduleName: CORE}; static viewQuery: o.ExternalReference = {name: 'ɵɵviewQuery', moduleName: CORE}; diff --git a/packages/compiler/src/render3/r3_pipe_compiler.ts b/packages/compiler/src/render3/r3_pipe_compiler.ts index 1f22d629d3..a677d1fec5 100644 --- a/packages/compiler/src/render3/r3_pipe_compiler.ts +++ b/packages/compiler/src/render3/r3_pipe_compiler.ts @@ -15,6 +15,7 @@ import {error, OutputContext} from '../util'; import {compileFactoryFunction, dependenciesFromGlobalMetadata, R3DependencyMetadata, R3FactoryTarget} from './r3_factory'; import {Identifiers as R3} from './r3_identifiers'; import {R3Reference, typeWithParameters, wrapReference} from './util'; +import {R3PipeDef} from './view/api'; export interface R3PipeMetadata { /** @@ -57,7 +58,7 @@ export interface R3PipeMetadata { pure: boolean; } -export function compilePipeFromMetadata(metadata: R3PipeMetadata) { +export function compilePipeFromMetadata(metadata: R3PipeMetadata): R3PipeDef { const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = []; // e.g. `name: 'myPipe'` @@ -70,12 +71,16 @@ export function compilePipeFromMetadata(metadata: R3PipeMetadata) { definitionMapValues.push({key: 'pure', value: o.literal(metadata.pure), quoted: false}); const expression = o.importExpr(R3.definePipe).callFn([o.literalMap(definitionMapValues)]); - const type = new o.ExpressionType(o.importExpr(R3.PipeDefWithMeta, [ + const type = createPipeType(metadata); + + return {expression, type}; +} + +export function createPipeType(metadata: R3PipeMetadata): o.Type { + return new o.ExpressionType(o.importExpr(R3.PipeDefWithMeta, [ typeWithParameters(metadata.type.type, metadata.typeArgumentCount), new o.ExpressionType(new o.LiteralExpr(metadata.pipeName)), ])); - - return {expression, type}; } /** diff --git a/packages/compiler/src/render3/view/api.ts b/packages/compiler/src/render3/view/api.ts index 050efaba0a..89946ac5f9 100644 --- a/packages/compiler/src/render3/view/api.ts +++ b/packages/compiler/src/render3/view/api.ts @@ -349,6 +349,14 @@ export interface R3ComponentDef { type: o.Type; } +/** + * Output of render3 pipe compilation. + */ +export interface R3PipeDef { + expression: o.Expression; + type: o.Type; +} + /** * Mappings indicating how the class interacts with its * host element (host bindings, listeners, etc). diff --git a/packages/core/src/compiler/compiler_facade_interface.ts b/packages/core/src/compiler/compiler_facade_interface.ts index 336a5fa76c..5a40d65b18 100644 --- a/packages/core/src/compiler/compiler_facade_interface.ts +++ b/packages/core/src/compiler/compiler_facade_interface.ts @@ -29,6 +29,8 @@ export interface ExportedCompilerFacade { export interface CompilerFacade { compilePipe(angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3PipeMetadataFacade): any; + compilePipeDeclaration( + angularCoreEnv: CoreEnvironment, sourceMapUrl: string, declaration: R3DeclarePipeFacade): any; compileInjectable( angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3InjectableMetadataFacade): any; compileInjector( @@ -255,6 +257,12 @@ export interface R3DeclareQueryMetadataFacade { emitDistinctChangesOnly?: boolean; } +export interface R3DeclarePipeFacade { + type: Function; + name: string; + pure?: boolean; +} + export interface ParseSourceSpan { start: any; end: any; diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index 70975ee65c..268dbe0f33 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -273,6 +273,7 @@ export { export { ɵɵngDeclareComponent, ɵɵngDeclareDirective, + ɵɵngDeclarePipe, } from './render3/jit/partial'; export { compilePipe as ɵcompilePipe, diff --git a/packages/core/src/render3/jit/partial.ts b/packages/core/src/render3/jit/partial.ts index 7a2b359d04..70e3dc2ff0 100644 --- a/packages/core/src/render3/jit/partial.ts +++ b/packages/core/src/render3/jit/partial.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {getCompilerFacade, R3DeclareComponentFacade, R3DeclareDirectiveFacade} from '../../compiler/compiler_facade'; +import {getCompilerFacade, R3DeclareComponentFacade, R3DeclareDirectiveFacade, R3DeclarePipeFacade} from '../../compiler/compiler_facade'; import {angularCoreEnv} from './environment'; /** @@ -30,3 +30,13 @@ export function ɵɵngDeclareComponent(decl: R3DeclareComponentFacade): unknown return compiler.compileComponentDeclaration( angularCoreEnv, `ng:///${decl.type.name}/ɵcmp.js`, decl); } + +/** + * Compiles a partial pipe declaration object into a full pipe definition object. + * + * @codeGenApi + */ +export function ɵɵngDeclarePipe(decl: R3DeclarePipeFacade): unknown { + const compiler = getCompilerFacade(); + return compiler.compilePipeDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵpipe.js`, decl); +} diff --git a/packages/core/test/render3/jit_environment_spec.ts b/packages/core/test/render3/jit_environment_spec.ts index 4f09a0910b..b19eef9c93 100644 --- a/packages/core/test/render3/jit_environment_spec.ts +++ b/packages/core/test/render3/jit_environment_spec.ts @@ -28,6 +28,7 @@ const INTERFACE_EXCEPTIONS = new Set([ const PARTIAL_ONLY = new Set([ 'ɵɵngDeclareDirective', 'ɵɵngDeclareComponent', + 'ɵɵngDeclarePipe', 'ChangeDetectionStrategy', 'ViewEncapsulation', ]);