From 6be90dc3728ee50969c6d94728b02d29ae90c590 Mon Sep 17 00:00:00 2001 From: Wagner Maciel Date: Wed, 2 Nov 2022 12:02:32 -0400 Subject: [PATCH] fix(material/schematics): add binding parsing logic --- .../components/slider/slider-template.ts | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/material/schematics/ng-generate/mdc-migration/rules/components/slider/slider-template.ts b/src/material/schematics/ng-generate/mdc-migration/rules/components/slider/slider-template.ts index 73f9013f0c56..969478a4373f 100644 --- a/src/material/schematics/ng-generate/mdc-migration/rules/components/slider/slider-template.ts +++ b/src/material/schematics/ng-generate/mdc-migration/rules/components/slider/slider-template.ts @@ -11,12 +11,34 @@ import {TemplateMigrator} from '../../template-migrator'; import {visitElements} from '../../tree-traversal'; import {Update} from '../../../../../migration-utilities'; +/** Contains a parsed binding node's standardized data. */ +interface Binding { + /** The actual compiler ast binding node. */ + node: compiler.TmplAstNode; + /** The type of binding this node is. */ + type: BindingType; + /** The name of the property, attribute, or event. */ + name: string; + /** The event handler or property/attribute value. */ + value: string; +} + +/** Describes the different types of bindings we care about. */ +const enum BindingType { + INPUT, + OUTPUT, + ATTRIBUTE, + TWO_WAY_BINDING, +} + export class SliderTemplateMigrator extends TemplateMigrator { getUpdates(ast: compiler.ParsedTemplate): Update[] { const updates: Update[] = []; visitElements(ast.nodes, (node: compiler.TmplAstElement) => { if (node.name === 'mat-slider') { + this._getBindings(node); + updates.push({ offset: node.sourceSpan.start.offset, updateFn: (html: string) => html, @@ -25,4 +47,72 @@ export class SliderTemplateMigrator extends TemplateMigrator { }); return updates; } + + /** Returns all of the property, attribute, event, or two-way bindings on the given node. */ + private _getBindings(node: compiler.TmplAstElement): Binding[] { + const allInputs = this._getInputs(node); + const allOutputs = this._getOutputs(node); + const attributes = this._getAttributes(node); + const twoWayBindings = this._getTwoWayBindings(allInputs, allOutputs); + + // Remove the inputs & outputs that are two-way bindings. + + const inputs = allInputs.filter( + input => !twoWayBindings.some(binding => binding.name === input.name), + ); + const outputs = allOutputs.filter( + output => !twoWayBindings.some(binding => binding.name === output.name), + ); + + return inputs.concat(outputs).concat(attributes).concat(twoWayBindings); + } + + /** Returns the two-way bindings based on the given input & output bindings. */ + private _getTwoWayBindings(inputs: Binding[], outputs: Binding[]): Binding[] { + return inputs + .filter(input => outputs.some(output => output.name === input.name)) + .map(input => ({...input, type: BindingType.TWO_WAY_BINDING})); + } + + /** Returns the output bindings on the given node. */ + private _getOutputs(node: compiler.TmplAstElement): Binding[] { + return node.outputs.map(output => ({ + node: output, + type: BindingType.OUTPUT, + name: node.sourceSpan.start.file.content.slice( + output.keySpan.start.offset, + output.keySpan.end.offset, + ), + value: node.sourceSpan.start.file.content.slice( + output.handlerSpan.start.offset, + output.handlerSpan.end.offset, + ), + })); + } + + /** Returns the input bindings on the given node. */ + private _getInputs(node: compiler.TmplAstElement): Binding[] { + return node.inputs.map(input => ({ + node: input, + type: BindingType.INPUT, + name: node.sourceSpan.start.file.content.slice( + input.keySpan.start.offset, + input.keySpan.end.offset, + ), + value: node.sourceSpan.start.file.content.slice( + input.value.sourceSpan.start, + input.value.sourceSpan.end, + ), + })); + } + + /** Returns the attributes on the given node. */ + private _getAttributes(node: compiler.TmplAstElement): Binding[] { + return node.attributes.map(attribute => ({ + node: attribute, + type: BindingType.ATTRIBUTE, + name: attribute.name, + value: attribute.value, + })); + } }