refactor(ivy): generate pipe names instead of defs (#23104)
PR Close #23104
This commit is contained in:
parent
43d62029f0
commit
85d3b591b6
@ -24,7 +24,10 @@ export function compilePipe(
|
|||||||
mode: OutputMode) {
|
mode: OutputMode) {
|
||||||
const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = [];
|
const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = [];
|
||||||
|
|
||||||
// e.g. 'type: MyPipe`
|
// e.g. `name: 'myPipe'`
|
||||||
|
definitionMapValues.push({key: 'name', value: o.literal(pipe.name), quoted: false});
|
||||||
|
|
||||||
|
// e.g. `type: MyPipe`
|
||||||
definitionMapValues.push(
|
definitionMapValues.push(
|
||||||
{key: 'type', value: outputCtx.importExpr(pipe.type.reference), quoted: false});
|
{key: 'type', value: outputCtx.importExpr(pipe.type.reference), quoted: false});
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata, flatten, identifierName, rendererTypeName, sanitizeIdentifier, tokenReference, viewClassName} from '../compile_metadata';
|
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata, CompileTypeSummary, flatten, identifierName, rendererTypeName, sanitizeIdentifier, tokenReference, viewClassName} from '../compile_metadata';
|
||||||
import {CompileReflector} from '../compile_reflector';
|
import {CompileReflector} from '../compile_reflector';
|
||||||
import {BindingForm, BuiltinConverter, BuiltinFunctionCall, ConvertPropertyBindingResult, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
|
import {BindingForm, BuiltinConverter, BuiltinFunctionCall, ConvertPropertyBindingResult, EventHandlerVars, LocalResolver, convertActionBinding, convertPropertyBinding, convertPropertyBindingBuiltins} from '../compiler_util/expression_converter';
|
||||||
import {ConstantPool, DefinitionKind} from '../constant_pool';
|
import {ConstantPool, DefinitionKind} from '../constant_pool';
|
||||||
@ -111,6 +111,14 @@ export function compileComponent(
|
|||||||
template: TemplateAst[], reflector: CompileReflector, bindingParser: BindingParser,
|
template: TemplateAst[], reflector: CompileReflector, bindingParser: BindingParser,
|
||||||
mode: OutputMode) {
|
mode: OutputMode) {
|
||||||
const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = [];
|
const definitionMapValues: {key: string, quoted: boolean, value: o.Expression}[] = [];
|
||||||
|
// Set of pipe names for pipe exps that have already been stored in pipes[] (to avoid dupes)
|
||||||
|
const pipeSet = new Set<string>();
|
||||||
|
// Pipe expressions for pipes[] field in component def
|
||||||
|
const pipeExps: o.Expression[] = [];
|
||||||
|
|
||||||
|
function addPipeDependency(summary: CompilePipeSummary): void {
|
||||||
|
addDependencyToComponent(outputCtx, summary, pipeSet, pipeExps);
|
||||||
|
}
|
||||||
|
|
||||||
const field = (key: string, value: o.Expression | null) => {
|
const field = (key: string, value: o.Expression | null) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
@ -154,11 +162,16 @@ export function compileComponent(
|
|||||||
new TemplateDefinitionBuilder(
|
new TemplateDefinitionBuilder(
|
||||||
outputCtx, outputCtx.constantPool, reflector, CONTEXT_NAME, ROOT_SCOPE.nestedScope(), 0,
|
outputCtx, outputCtx.constantPool, reflector, CONTEXT_NAME, ROOT_SCOPE.nestedScope(), 0,
|
||||||
component.template !.ngContentSelectors, templateTypeName, templateName, pipeMap,
|
component.template !.ngContentSelectors, templateTypeName, templateName, pipeMap,
|
||||||
component.viewQueries)
|
component.viewQueries, addPipeDependency)
|
||||||
.buildTemplateFunction(template, []);
|
.buildTemplateFunction(template, []);
|
||||||
|
|
||||||
field('template', templateFunctionExpression);
|
field('template', templateFunctionExpression);
|
||||||
|
|
||||||
|
// e.g. `pipes: [MyPipe]`
|
||||||
|
if (pipeExps.length) {
|
||||||
|
field('pipes', o.literalArr(pipeExps));
|
||||||
|
}
|
||||||
|
|
||||||
// e.g `inputs: {a: 'a'}`
|
// e.g `inputs: {a: 'a'}`
|
||||||
field('inputs', createInputsObject(component, outputCtx));
|
field('inputs', createInputsObject(component, outputCtx));
|
||||||
|
|
||||||
@ -201,6 +214,19 @@ export function compileComponent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: this should be used for addDirectiveDependency as well when Misko's PR goes in
|
||||||
|
function addDependencyToComponent(
|
||||||
|
outputCtx: OutputContext, summary: CompileTypeSummary, set: Set<string>,
|
||||||
|
exps: o.Expression[]): void {
|
||||||
|
const importExpr = outputCtx.importExpr(summary.type.reference) as o.ExternalExpr;
|
||||||
|
const uniqueKey = importExpr.value.moduleName + ':' + importExpr.value.name;
|
||||||
|
|
||||||
|
if (!set.has(uniqueKey)) {
|
||||||
|
set.add(uniqueKey);
|
||||||
|
exps.push(importExpr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Remove these when the things are fully supported
|
// TODO: Remove these when the things are fully supported
|
||||||
function unknown<T>(arg: o.Expression | o.Statement | TemplateAst): never {
|
function unknown<T>(arg: o.Expression | o.Statement | TemplateAst): never {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -347,20 +373,16 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
|||||||
private reflector: CompileReflector, private contextParameter: string,
|
private reflector: CompileReflector, private contextParameter: string,
|
||||||
private bindingScope: BindingScope, private level = 0, private ngContentSelectors: string[],
|
private bindingScope: BindingScope, private level = 0, private ngContentSelectors: string[],
|
||||||
private contextName: string|null, private templateName: string|null,
|
private contextName: string|null, private templateName: string|null,
|
||||||
private pipes: Map<string, CompilePipeSummary>, private viewQueries: CompileQueryMetadata[]) {
|
private pipes: Map<string, CompilePipeSummary>, private viewQueries: CompileQueryMetadata[],
|
||||||
|
private addPipeDependency: (summary: CompilePipeSummary) => void) {
|
||||||
this._valueConverter = new ValueConverter(
|
this._valueConverter = new ValueConverter(
|
||||||
outputCtx, () => this.allocateDataSlot(), (name, localName, slot, value) => {
|
outputCtx, () => this.allocateDataSlot(), (name, localName, slot, value) => {
|
||||||
bindingScope.set(localName, value);
|
bindingScope.set(localName, value);
|
||||||
const pipe = pipes.get(name) !;
|
const pipe = pipes.get(name) !;
|
||||||
pipe || error(`Could not find pipe ${name}`);
|
pipe || error(`Could not find pipe ${name}`);
|
||||||
const pipeDefinition = constantPool.getDefinition(
|
this.addPipeDependency(pipe);
|
||||||
pipe.type.reference, DefinitionKind.Pipe, outputCtx, /* forceShared */ true);
|
|
||||||
this._creationMode.push(
|
this._creationMode.push(
|
||||||
o.importExpr(R3.pipe)
|
o.importExpr(R3.pipe).callFn([o.literal(slot), o.literal(name)]).toStmt());
|
||||||
.callFn([
|
|
||||||
o.literal(slot), pipeDefinition, pipeDefinition.callMethod(R3.NEW_METHOD, [])
|
|
||||||
])
|
|
||||||
.toStmt());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -736,7 +758,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
|||||||
const templateVisitor = new TemplateDefinitionBuilder(
|
const templateVisitor = new TemplateDefinitionBuilder(
|
||||||
this.outputCtx, this.constantPool, this.reflector, templateContext,
|
this.outputCtx, this.constantPool, this.reflector, templateContext,
|
||||||
this.bindingScope.nestedScope(), this.level + 1, this.ngContentSelectors, contextName,
|
this.bindingScope.nestedScope(), this.level + 1, this.ngContentSelectors, contextName,
|
||||||
templateName, this.pipes, []);
|
templateName, this.pipes, [], this.addPipeDependency);
|
||||||
const templateFunctionExpr = templateVisitor.buildTemplateFunction(ast.children, ast.variables);
|
const templateFunctionExpr = templateVisitor.buildTemplateFunction(ast.children, ast.variables);
|
||||||
this._postfix.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
this._postfix.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
||||||
}
|
}
|
||||||
@ -1044,11 +1066,7 @@ class ValueConverter extends AstMemoryEfficientTransformer {
|
|||||||
// AstMemoryEfficientTransformer
|
// AstMemoryEfficientTransformer
|
||||||
visitPipe(ast: BindingPipe, context: any): AST {
|
visitPipe(ast: BindingPipe, context: any): AST {
|
||||||
// Allocate a slot to create the pipe
|
// Allocate a slot to create the pipe
|
||||||
let slot = this.pipeSlots.get(ast.name);
|
const slot = this.allocateSlot();
|
||||||
if (slot == null) {
|
|
||||||
slot = this.allocateSlot();
|
|
||||||
this.pipeSlots.set(ast.name, slot);
|
|
||||||
}
|
|
||||||
const slotPseudoLocal = `PIPE:${slot}`;
|
const slotPseudoLocal = `PIPE:${slot}`;
|
||||||
const target = new PropertyRead(ast.span, new ImplicitReceiver(ast.span), slotPseudoLocal);
|
const target = new PropertyRead(ast.span, new ImplicitReceiver(ast.span), slotPseudoLocal);
|
||||||
const bindingId = pipeBinding(ast.args);
|
const bindingId = pipeBinding(ast.args);
|
||||||
|
@ -97,7 +97,6 @@ describe('compiler compliance', () => {
|
|||||||
template: function ChildComponent_Template(ctx: IDENT, cm: IDENT) {
|
template: function ChildComponent_Template(ctx: IDENT, cm: IDENT) {
|
||||||
if (cm) {
|
if (cm) {
|
||||||
$r3$.ɵT(0, 'child-view');
|
$r3$.ɵT(0, 'child-view');
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});`;
|
});`;
|
||||||
@ -784,7 +783,10 @@ describe('compiler compliance', () => {
|
|||||||
transform(value: any, ...args: any[]) { return value; }
|
transform(value: any, ...args: any[]) { return value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({selector: 'my-app', template: '{{name | myPipe:size | myPurePipe:size }}'})
|
@Component({
|
||||||
|
selector: 'my-app',
|
||||||
|
template: '{{name | myPipe:size | myPurePipe:size }}<p>{{ name | myPurePipe:size }}</p>'
|
||||||
|
})
|
||||||
export class MyApp {
|
export class MyApp {
|
||||||
name = 'World';
|
name = 'World';
|
||||||
size = 0;
|
size = 0;
|
||||||
@ -799,19 +801,18 @@ describe('compiler compliance', () => {
|
|||||||
it('should render pipes', () => {
|
it('should render pipes', () => {
|
||||||
const MyPipeDefinition = `
|
const MyPipeDefinition = `
|
||||||
static ngPipeDef = $r3$.ɵdefinePipe(
|
static ngPipeDef = $r3$.ɵdefinePipe(
|
||||||
{type: MyPipe, factory: function MyPipe_Factory() { return new MyPipe(); }});
|
{name: 'myPipe', type: MyPipe, factory: function MyPipe_Factory() { return new MyPipe(); }});
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const MyPurePipeDefinition = `
|
const MyPurePipeDefinition = `
|
||||||
static ngPipeDef = $r3$.ɵdefinePipe({
|
static ngPipeDef = $r3$.ɵdefinePipe({
|
||||||
|
name: 'myPurePipe',
|
||||||
type: MyPurePipe,
|
type: MyPurePipe,
|
||||||
factory: function MyPurePipe_Factory() { return new MyPurePipe(); },
|
factory: function MyPurePipe_Factory() { return new MyPurePipe(); },
|
||||||
pure: true
|
pure: true
|
||||||
});`;
|
});`;
|
||||||
|
|
||||||
const MyAppDefinition = `
|
const MyAppDefinition = `
|
||||||
const $MyPurePipe_ngPipeDef$ = MyPurePipe.ngPipeDef;
|
|
||||||
const $MyPipe_ngPipeDef$ = MyPipe.ngPipeDef;
|
|
||||||
…
|
…
|
||||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
type: MyApp,
|
type: MyApp,
|
||||||
@ -820,11 +821,17 @@ describe('compiler compliance', () => {
|
|||||||
template: function MyApp_Template(ctx: IDENT, cm: IDENT) {
|
template: function MyApp_Template(ctx: IDENT, cm: IDENT) {
|
||||||
if (cm) {
|
if (cm) {
|
||||||
$r3$.ɵT(0);
|
$r3$.ɵT(0);
|
||||||
$r3$.ɵPp(1, $MyPurePipe_ngPipeDef$, $MyPurePipe_ngPipeDef$.n());
|
$r3$.ɵPp(1, 'myPurePipe');
|
||||||
$r3$.ɵPp(2, $MyPipe_ngPipeDef$, $MyPipe_ngPipeDef$.n());
|
$r3$.ɵPp(2, 'myPipe');
|
||||||
|
$r3$.ɵE(3, 'p');
|
||||||
|
$r3$.ɵT(4);
|
||||||
|
$r3$.ɵPp(5, 'myPurePipe');
|
||||||
|
$r3$.ɵe();
|
||||||
}
|
}
|
||||||
$r3$.ɵt(0, $r3$.ɵi1('', $r3$.ɵpb2(1, $r3$.ɵpb2(2,ctx.name, ctx.size), ctx.size), ''));
|
$r3$.ɵt(0, $r3$.ɵi1('', $r3$.ɵpb2(1, $r3$.ɵpb2(2,ctx.name, ctx.size), ctx.size), ''));
|
||||||
}
|
$r3$.ɵt(4, $r3$.ɵi1('', $r3$.ɵpb2(5, ctx.name, ctx.size), ''));
|
||||||
|
},
|
||||||
|
pipes: [MyPurePipe, MyPipe]
|
||||||
});`;
|
});`;
|
||||||
|
|
||||||
const result = compile(files, angularFiles);
|
const result = compile(files, angularFiles);
|
||||||
|
@ -16,11 +16,10 @@ import {pureFunction1, pureFunction2, pureFunction3, pureFunction4, pureFunction
|
|||||||
* Create a pipe.
|
* Create a pipe.
|
||||||
*
|
*
|
||||||
* @param index Pipe index where the pipe will be stored.
|
* @param index Pipe index where the pipe will be stored.
|
||||||
* @param pipeDef Pipe definition object for registering life cycle hooks.
|
* @param pipeName The name of the pipe
|
||||||
* @param firstInstance (optional) The first instance of the pipe that can be reused for pure pipes.
|
|
||||||
* @returns T the instance of the pipe.
|
* @returns T the instance of the pipe.
|
||||||
*/
|
*/
|
||||||
export function pipe(index: number, pipeName: string, firstInstance?: any): any {
|
export function pipe(index: number, pipeName: string): any {
|
||||||
const tView = getTView();
|
const tView = getTView();
|
||||||
let pipeDef: PipeDef<any>;
|
let pipeDef: PipeDef<any>;
|
||||||
|
|
||||||
@ -34,7 +33,7 @@ export function pipe(index: number, pipeName: string, firstInstance?: any): any
|
|||||||
pipeDef = tView.data[index] as PipeDef<any>;
|
pipeDef = tView.data[index] as PipeDef<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pipeInstance = pipeDef.pure && firstInstance ? firstInstance : pipeDef.n();
|
const pipeInstance = pipeDef.n();
|
||||||
store(index, pipeInstance);
|
store(index, pipeInstance);
|
||||||
return pipeInstance;
|
return pipeInstance;
|
||||||
}
|
}
|
||||||
|
@ -155,12 +155,11 @@ describe('pipes', () => {
|
|||||||
selectors: [['my-app']],
|
selectors: [['my-app']],
|
||||||
factory: function MyApp_Factory() { return new MyApp(); },
|
factory: function MyApp_Factory() { return new MyApp(); },
|
||||||
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
|
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
|
||||||
let $pi$: $any$;
|
|
||||||
if (cm) {
|
if (cm) {
|
||||||
$r3$.ɵT(0);
|
$r3$.ɵT(0);
|
||||||
$pi$ = $r3$.ɵPp(1, 'myPurePipe');
|
$r3$.ɵPp(1, 'myPurePipe');
|
||||||
$r3$.ɵT(2);
|
$r3$.ɵT(2);
|
||||||
$r3$.ɵPp(3, 'myPurePipe', $pi$);
|
$r3$.ɵPp(3, 'myPurePipe');
|
||||||
$r3$.ɵC(4, C4, '', ['oneTimeIf', '']);
|
$r3$.ɵC(4, C4, '', ['oneTimeIf', '']);
|
||||||
}
|
}
|
||||||
$r3$.ɵt(0, $r3$.ɵi1('', $r3$.ɵpb2(1, ctx.name, ctx.size), ''));
|
$r3$.ɵt(0, $r3$.ɵi1('', $r3$.ɵpb2(1, ctx.name, ctx.size), ''));
|
||||||
@ -173,7 +172,7 @@ describe('pipes', () => {
|
|||||||
if (cm) {
|
if (cm) {
|
||||||
$r3$.ɵE(0, 'div');
|
$r3$.ɵE(0, 'div');
|
||||||
$r3$.ɵT(1);
|
$r3$.ɵT(1);
|
||||||
$r3$.ɵPp(2, 'myPurePipe', $pi$);
|
$r3$.ɵPp(2, 'myPurePipe');
|
||||||
$r3$.ɵe();
|
$r3$.ɵe();
|
||||||
}
|
}
|
||||||
$r3$.ɵt(1, $r3$.ɵi1('', $r3$.ɵpb2(2, ctx.name, ctx.size), ''));
|
$r3$.ɵt(1, $r3$.ɵi1('', $r3$.ɵpb2(2, ctx.name, ctx.size), ''));
|
||||||
|
@ -183,49 +183,6 @@ describe('pipe', () => {
|
|||||||
expect(renderToHtml(Template, person, null, defs)).toEqual('bart state:2');
|
expect(renderToHtml(Template, person, null, defs)).toEqual('bart state:2');
|
||||||
expect(renderToHtml(Template, person, null, defs)).toEqual('bart state:2');
|
expect(renderToHtml(Template, person, null, defs)).toEqual('bart state:2');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should cache pure pipes', () => {
|
|
||||||
function Template(ctx: any, cm: boolean) {
|
|
||||||
let pipeInstance: any;
|
|
||||||
if (cm) {
|
|
||||||
elementStart(0, 'div');
|
|
||||||
pipeInstance = pipe(1, 'countingPipe');
|
|
||||||
elementEnd();
|
|
||||||
elementStart(2, 'div');
|
|
||||||
pipe(3, 'countingPipe', pipeInstance);
|
|
||||||
elementEnd();
|
|
||||||
container(4);
|
|
||||||
}
|
|
||||||
elementProperty(0, 'someProp', bind(pipeBind1(1, true)));
|
|
||||||
elementProperty(2, 'someProp', bind(pipeBind1(3, true)));
|
|
||||||
pipeInstances.push(load<CountingPipe>(1), load(3));
|
|
||||||
containerRefreshStart(4);
|
|
||||||
{
|
|
||||||
for (let i of [1, 2]) {
|
|
||||||
let cm1 = embeddedViewStart(1);
|
|
||||||
{
|
|
||||||
if (cm1) {
|
|
||||||
elementStart(0, 'div');
|
|
||||||
pipe(1, 'countingPipe', pipeInstance);
|
|
||||||
elementEnd();
|
|
||||||
}
|
|
||||||
elementProperty(0, 'someProp', bind(pipeBind1(1, true)));
|
|
||||||
pipeInstances.push(load<CountingPipe>(1));
|
|
||||||
}
|
|
||||||
embeddedViewEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
containerRefreshEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
const pipeInstances: CountingPipe[] = [];
|
|
||||||
renderToHtml(Template, {}, null, defs, rendererFactory2);
|
|
||||||
expect(pipeInstances.length).toEqual(4);
|
|
||||||
expect(pipeInstances[0]).toBeAnInstanceOf(CountingPipe);
|
|
||||||
expect(pipeInstances[1]).toBe(pipeInstances[0]);
|
|
||||||
expect(pipeInstances[2]).toBe(pipeInstances[0]);
|
|
||||||
expect(pipeInstances[3]).toBe(pipeInstances[0]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('impure', () => {
|
describe('impure', () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user