Converter to Polymer2 classes. See readme.txt for more information about usages. Change-Id: I60843b32bc56faa04ad2b70898ab1f5535a2ad71
diff --git a/tools/polygerrit-updater/src/funcToClassConversion/funcToClassBasedElementConverter.ts b/tools/polygerrit-updater/src/funcToClassConversion/funcToClassBasedElementConverter.ts new file mode 100644 index 0000000..b92a6e9 --- /dev/null +++ b/tools/polygerrit-updater/src/funcToClassConversion/funcToClassBasedElementConverter.ts
@@ -0,0 +1,131 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {LegacyLifecycleMethodsArray, LegacyPolymerComponent} from './polymerComponentParser'; +import {LifecycleMethodsBuilder} from './lifecycleMethodsBuilder'; +import {ClassBasedPolymerElement, PolymerElementBuilder} from './polymerElementBuilder'; +import * as codeUtils from '../utils/codeUtils'; +import * as ts from 'typescript'; + +export class PolymerFuncToClassBasedConverter { + public static convert(component: LegacyPolymerComponent): ClassBasedPolymerElement { + const legacySettings = component.componentSettings; + const reservedDeclarations = legacySettings.reservedDeclarations; + + if(!reservedDeclarations.is) { + throw new Error("Legacy component doesn't have 'is' property"); + } + const className = this.generateClassNameFromTagName(reservedDeclarations.is.data); + const updater = new PolymerElementBuilder(component, className); + updater.addIsAccessor(reservedDeclarations.is.data); + + if(reservedDeclarations.properties) { + updater.addPolymerPropertiesAccessor(reservedDeclarations.properties); + } + + updater.addMixin("Polymer.Element"); + updater.addMixin("Polymer.LegacyElementMixin"); + updater.addMixin("Polymer.GestureEventListeners"); + + if(reservedDeclarations._legacyUndefinedCheck) { + updater.addMixin("Polymer.LegacyDataMixin"); + } + + if(reservedDeclarations.behaviors) { + updater.addMixin("Polymer.mixinBehaviors", [reservedDeclarations.behaviors.data]); + const mixinNames = this.getMixinNamesFromBehaviors(reservedDeclarations.behaviors.data); + const jsDocLines = mixinNames.map(mixinName => { + return `@appliesMixin ${mixinName}`; + }); + updater.addClassJSDocComments(jsDocLines); + } + + if(reservedDeclarations.observers) { + updater.addPolymerPropertiesObservers(reservedDeclarations.observers.data); + } + + if(reservedDeclarations.keyBindings) { + updater.addKeyBindings(reservedDeclarations.keyBindings.data); + } + + + const lifecycleBuilder = new LifecycleMethodsBuilder(); + if (reservedDeclarations.listeners) { + lifecycleBuilder.addListeners(reservedDeclarations.listeners.data, legacySettings.ordinaryMethods); + } + + if (reservedDeclarations.hostAttributes) { + lifecycleBuilder.addHostAttributes(reservedDeclarations.hostAttributes.data); + } + + for(const name of LegacyLifecycleMethodsArray) { + const existingMethod = legacySettings.lifecycleMethods.get(name); + if(existingMethod) { + lifecycleBuilder.addLegacyLifecycleMethod(name, existingMethod) + } + } + + const newLifecycleMethods = lifecycleBuilder.buildNewMethods(); + updater.addLifecycleMethods(newLifecycleMethods); + + + updater.addOrdinaryMethods(legacySettings.ordinaryMethods); + updater.addOrdinaryGetAccessors(legacySettings.ordinaryGetAccessors); + updater.addOrdinaryShorthandProperties(legacySettings.ordinaryShorthandProperties); + updater.addOrdinaryPropertyAssignments(legacySettings.ordinaryPropertyAssignments); + + return updater.build(); + } + + private static generateClassNameFromTagName(tagName: string) { + let result = ""; + let nextUppercase = true; + for(const ch of tagName) { + if (ch === '-') { + nextUppercase = true; + continue; + } + result += nextUppercase ? ch.toUpperCase() : ch; + nextUppercase = false; + } + return result; + } + + private static getMixinNamesFromBehaviors(behaviors: ts.ArrayLiteralExpression): string[] { + return behaviors.elements.map((expression) => { + const propertyAccessExpression = codeUtils.assertNodeKind(expression, ts.SyntaxKind.PropertyAccessExpression) as ts.PropertyAccessExpression; + const namespaceName = codeUtils.assertNodeKind(propertyAccessExpression.expression, ts.SyntaxKind.Identifier) as ts.Identifier; + const behaviorName = propertyAccessExpression.name; + if(namespaceName.text === 'Gerrit') { + let behaviorNameText = behaviorName.text; + const suffix = 'Behavior'; + if(behaviorNameText.endsWith(suffix)) { + behaviorNameText = + behaviorNameText.substr(0, behaviorNameText.length - suffix.length); + } + const mixinName = behaviorNameText + 'Mixin'; + return `${namespaceName.text}.${mixinName}` + } else if(namespaceName.text === 'Polymer') { + let behaviorNameText = behaviorName.text; + if(behaviorNameText === "IronFitBehavior") { + return "Polymer.IronFitMixin"; + } else if(behaviorNameText === "IronOverlayBehavior") { + return ""; + } + throw new Error(`Unsupported behavior: ${propertyAccessExpression.getText()}`); + } + throw new Error(`Unsupported behavior name ${expression.getFullText()}`) + }).filter(name => name.length > 0); + } +}