support validate track and observed.

Signed-off-by: lihong <lihong67@huawei.com>
Change-Id: Ia679b6467d7866fd03ad8bf7d9c7673b02a74691
This commit is contained in:
lihong 2024-02-06 10:45:07 +08:00
parent 4a94ab0590
commit f46bf2946e
4 changed files with 114 additions and 17 deletions

View File

@ -50,6 +50,7 @@ export const COMPONENT_CUSTOM_DECORATOR: string = 'COMPONENT_CUSTOM_DECORATOR';
export const COMPONENT_REQUIRE_DECORATOR: string = '@Require';
export const CLASS_TRACK_DECORATOR: string = 'Track';
export const CLASS_MIN_TRACK_DECORATOR: string = 'track';
export const COMPONENT_DECORATORS_PARAMS: Set<string> = new Set([COMPONENT_CONSUME_DECORATOR,
COMPONENT_STORAGE_PROP_DECORATOR, COMPONENT_STORAGE_LINK_DECORATOR, COMPONENT_PROVIDE_DECORATOR,
@ -68,6 +69,7 @@ export const STRUCT_DECORATORS: Set<string> = new Set([...INNER_COMPONENT_DECORA
export const COMPONENT_OBSERVED_DECORATOR: string = '@Observed';
export const OBSERVED: string = 'Observed';
export const MIN_OBSERVED: string = 'observed';
export const COMPONENT_BUILDER_DECORATOR: string = '@Builder';
export const COMPONENT_EXTEND_DECORATOR: string = '@Extend';
export const COMPONENT_STYLES_DECORATOR: string = '@Styles';

View File

@ -58,7 +58,9 @@ import {
CHECK_COMPONENT_ANIMATABLE_EXTEND_DECORATOR,
CLASS_TRACK_DECORATOR,
COMPONENT_REQUIRE_DECORATOR,
COMPONENT_SENDABLE_DECORATOR
COMPONENT_SENDABLE_DECORATOR,
CLASS_MIN_TRACK_DECORATOR,
MIN_OBSERVED
} from './pre_define';
import {
INNER_COMPONENT_NAMES,
@ -429,21 +431,34 @@ function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponent
extendResult.componentName, node.name.getText());
}
} else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) {
if (ts.isBlock(node.body) && node.body.statements) {
if (ts.isFunctionDeclaration(node)) {
GLOBAL_STYLE_FUNCTION.set(node.name.getText(), node.body);
} else {
INNER_STYLE_FUNCTION.set(node.name.getText(), node.body);
}
STYLES_ATTRIBUTE.add(node.name.getText());
BUILDIN_STYLE_NAMES.add(node.name.getText());
}
collectStyles(node);
}
if (hasDecorator(node, COMPONENT_CONCURRENT_DECORATOR)) {
// ark compiler's feature
checkConcurrentDecorator(node, log, sourceFileNode);
}
}
checkDecorator(sourceFileNode, node, log, structContext, classContext);
node.getChildren().forEach((item: ts.Node) => visitAllNode(item, sourceFileNode, allComponentNames,
log, structContext, classContext, fileQuery));
structContext = false;
classContext = false;
}
function collectStyles(node: ts.FunctionLikeDeclarationBase): void {
if (ts.isBlock(node.body) && node.body.statements) {
if (ts.isFunctionDeclaration(node)) {
GLOBAL_STYLE_FUNCTION.set(node.name.getText(), node.body);
} else {
INNER_STYLE_FUNCTION.set(node.name.getText(), node.body);
}
STYLES_ATTRIBUTE.add(node.name.getText());
BUILDIN_STYLE_NAMES.add(node.name.getText());
}
}
function checkDecorator(sourceFileNode: ts.SourceFile, node: ts.Node,
log: LogInfo[], structContext: boolean, classContext: boolean): void {
if (ts.isIdentifier(node) && (ts.isDecorator(node.parent) ||
(ts.isCallExpression(node.parent) && ts.isDecorator(node.parent.parent)))) {
const decoratorName: string = node.escapedText.toString();
@ -451,24 +466,41 @@ function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponent
validateMethodDecorator(sourceFileNode, node, log, structContext, decoratorName);
validateClassDecorator(sourceFileNode, node, log, classContext, decoratorName);
}
node.getChildren().forEach((item: ts.Node) => visitAllNode(item, sourceFileNode, allComponentNames,
log, structContext, classContext, fileQuery));
structContext = false;
classContext = false;
}
const classDecorators: string[] = [CLASS_TRACK_DECORATOR, CLASS_MIN_TRACK_DECORATOR, MIN_OBSERVED];
function validateClassDecorator(sourceFileNode: ts.SourceFile, node: ts.Identifier, log: LogInfo[],
classContext: boolean, decoratorName: string): void {
if (!classContext && decoratorName === CLASS_TRACK_DECORATOR) {
const message: string = `The '@Track' decorator can only be used in 'class'.`;
if (!classContext && classDecorators.includes(decoratorName)) {
const message: string = `The '@${decoratorName}' decorator can only be used in 'class'.`;
addLog(LogType.ERROR, message, node.pos, log, sourceFileNode);
} else if ('@' + decoratorName === COMPONENT_SENDABLE_DECORATOR &&
(!node.parent || !node.parent.parent || !ts.isClassDeclaration(node.parent.parent))) {
const message: string = 'The \'@Sendable\' decorator can only be added to \'class\'.';
addLog(LogType.ERROR, message, node.pos, log, sourceFileNode);
} else if (decoratorName === CLASS_MIN_TRACK_DECORATOR && !hasObservedClass(classContext, node)) {
const message: string = `The '@track' decorator can only be used within a 'class' decorated with observed.`;
addLog(LogType.ERROR, message, node.pos, log, sourceFileNode);
}
}
function parseClassDecorator(node: ts.ClassDeclaration): boolean {
const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
return decorators.some((item: ts.Decorator) => {
return ts.isIdentifier(item.expression) && item.expression.escapedText.toString() === MIN_OBSERVED;
});
}
function hasObservedClass(classContext: boolean, node: ts.Identifier): boolean {
let isObservedClass: boolean = false;
if (classContext && ts.isDecorator(node.parent) && ts.isPropertyDeclaration(node.parent.parent) &&
ts.isClassDeclaration(node.parent.parent.parent)) {
isObservedClass = parseClassDecorator(node.parent.parent.parent);
}
return isObservedClass;
}
function validateStructDecorator(sourceFileNode: ts.SourceFile, node: ts.Identifier, log: LogInfo[],
structContext: boolean, decoratorName: string): void {
if (!structContext && STRUCT_DECORATORS.has(`@${decoratorName}`)) {

View File

@ -280,5 +280,23 @@
"@Track": {
"message": "The '@Track' decorator can only be used in 'class'.",
"type": "ERROR"
}
},
"validate_track_observed": [
{
"message": "The struct 'child' use invalid decorator.",
"type": "WARN"
},
{
"message": "The '@track' decorator can only be used in 'class'.",
"type": "ERROR"
},
{
"message": "The '@observed' decorator can only be used in 'class'.",
"type": "ERROR"
},
{
"message": "The '@track' decorator can only be used within a 'class' decorated with observed.",
"type": "ERROR"
}
]
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* 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.
*/
exports.source = `
@Entry
@Component
struct HomeComponent {
@track value: string
build() {
Column() {
Text("hello")
}
.height(500)
}
}
@observed
@Component
struct child {
build() {
Text("")
}
}
class A {
@track value: string
}
@observed
class B {
@track value: string
}
`;