blob: d6e113ce010c68adec6057e45ae3567358c6ecb0 [file] [log] [blame]
// 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 {DataWithComments, LegacyPolymerComponent, LegacyReservedDeclarations, OrdinaryGetAccessors, OrdinaryMethods, OrdinaryPropertyAssignments, OrdinaryShorthandProperties} from './polymerComponentParser';
import * as ts from 'typescript';
import * as codeUtils from '../utils/codeUtils';
import {LifecycleMethod} from './lifecycleMethodsBuilder';
import {PolymerClassBuilder} from '../utils/polymerClassBuilder';
import {SyntaxKind} from 'typescript';
export interface ClassBasedPolymerElement {
classDeclaration: ts.ClassDeclaration;
componentRegistration: ts.ExpressionStatement;
eventsComments: string[];
generatedComments: string[];
}
export class PolymerElementBuilder {
private readonly reservedDeclarations: LegacyReservedDeclarations;
private readonly classBuilder: PolymerClassBuilder;
private mixins: ts.ExpressionWithTypeArguments | null;
public constructor(private readonly legacyComponent: LegacyPolymerComponent, className: string) {
this.reservedDeclarations = legacyComponent.componentSettings.reservedDeclarations;
this.classBuilder = new PolymerClassBuilder(className);
this.mixins = null;
}
public addIsAccessor(tagName: string) {
this.classBuilder.addIsAccessor(this.createIsAccessor(tagName));
}
public addPolymerPropertiesAccessor(legacyProperties: DataWithComments<ts.ObjectLiteralExpression>) {
const returnStatement = ts.createReturn(legacyProperties.data);
const block = ts.createBlock([returnStatement]);
let propertiesAccessor = ts.createGetAccessor(undefined, [ts.createModifier(ts.SyntaxKind.StaticKeyword)], "properties", [], undefined, block);
if(legacyProperties.leadingComments.length > 0) {
propertiesAccessor = codeUtils.restoreLeadingComments(propertiesAccessor, legacyProperties.leadingComments);
}
this.classBuilder.addPolymerPropertiesAccessor(legacyProperties.data.pos, propertiesAccessor);
}
public addPolymerPropertiesObservers(legacyObservers: ts.ArrayLiteralExpression) {
const returnStatement = ts.createReturn(legacyObservers);
const block = ts.createBlock([returnStatement]);
const propertiesAccessor = ts.createGetAccessor(undefined, [ts.createModifier(ts.SyntaxKind.StaticKeyword)], "observers", [], undefined, block);
this.classBuilder.addPolymerObserversAccessor(legacyObservers.pos, propertiesAccessor);
}
public addKeyBindings(keyBindings: ts.ObjectLiteralExpression) {
//In Polymer 2 keyBindings must be a property with get accessor
const returnStatement = ts.createReturn(keyBindings);
const block = ts.createBlock([returnStatement]);
const keyBindingsAccessor = ts.createGetAccessor(undefined, [], "keyBindings", [], undefined, block);
this.classBuilder.addGetAccessor(keyBindings.pos, keyBindingsAccessor);
}
public addOrdinaryMethods(ordinaryMethods: OrdinaryMethods) {
for(const [name, method] of ordinaryMethods) {
this.classBuilder.addMethod(method.pos, method);
}
}
public addOrdinaryGetAccessors(ordinaryGetAccessors: OrdinaryGetAccessors) {
for(const [name, accessor] of ordinaryGetAccessors) {
this.classBuilder.addGetAccessor(accessor.pos, accessor);
}
}
public addOrdinaryShorthandProperties(ordinaryShorthandProperties: OrdinaryShorthandProperties) {
for (const [name, property] of ordinaryShorthandProperties) {
this.classBuilder.addClassFieldInitializer(property.name, property.name);
}
}
public addOrdinaryPropertyAssignments(ordinaryPropertyAssignments: OrdinaryPropertyAssignments) {
for (const [name, property] of ordinaryPropertyAssignments) {
const propertyName = codeUtils.assertNodeKind(property.name, ts.SyntaxKind.Identifier) as ts.Identifier;
this.classBuilder.addClassFieldInitializer(propertyName, property.initializer);
}
}
public addMixin(name: string, mixinArguments?: ts.Expression[]) {
let fullMixinArguments: ts.Expression[] = [];
if(mixinArguments) {
fullMixinArguments.push(...mixinArguments);
}
if(this.mixins) {
fullMixinArguments.push(this.mixins.expression);
}
if(fullMixinArguments.length > 0) {
this.mixins = ts.createExpressionWithTypeArguments([], ts.createCall(codeUtils.createNameExpression(name), [], fullMixinArguments.length > 0 ? fullMixinArguments : undefined));
}
else {
this.mixins = ts.createExpressionWithTypeArguments([], codeUtils.createNameExpression(name));
}
}
public addClassJSDocComments(lines: string[]) {
this.classBuilder.addClassJSDocComments(lines);
}
public build(): ClassBasedPolymerElement {
if(this.mixins) {
this.classBuilder.setBaseType(this.mixins);
}
const className = this.classBuilder.className;
const callExpression = ts.createCall(ts.createPropertyAccess(ts.createIdentifier("customElements"), "define"), undefined, [ts.createPropertyAccess(ts.createIdentifier(className), "is"), ts.createIdentifier(className)]);
const classBuilderResult = this.classBuilder.build();
return {
classDeclaration: classBuilderResult.classDeclaration,
generatedComments: classBuilderResult.generatedComments,
componentRegistration: ts.createExpressionStatement(callExpression),
eventsComments: this.legacyComponent.componentSettings.eventsComments,
};
}
private createIsAccessor(tagName: string): ts.GetAccessorDeclaration {
const returnStatement = ts.createReturn(ts.createStringLiteral(tagName));
const block = ts.createBlock([returnStatement]);
const accessor = ts.createGetAccessor([], [ts.createModifier(ts.SyntaxKind.StaticKeyword)], "is", [], undefined, block);
return codeUtils.addReplacableCommentAfterNode(accessor, "eventsComments");
}
public addLifecycleMethods(newLifecycleMethods: LifecycleMethod[]) {
for(const lifecycleMethod of newLifecycleMethods) {
this.classBuilder.addLifecycleMethod(lifecycleMethod.name, lifecycleMethod.originalPos, lifecycleMethod.method);
}
}
}