• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import * as arkts from '@koalaui/libarkts';
17import { getIdentifierName, PresetDecorators } from '../utils';
18import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule';
19
20// Gets the names of all methods in the struct
21function getMethodNames(node: arkts.StructDeclaration): string[] {
22  const methodNames: string[] = [];
23  node.definition.body.forEach((member) => {
24    if (arkts.isMethodDefinition(member)) {
25      const methodName = getIdentifierName(member.name);
26      if (methodName) {
27        methodNames.push(methodName);
28      }
29    }
30  });
31  return methodNames;
32}
33
34// Invalid @Watch decorator bugs are reported
35function reportInvalidWatch(
36  member: arkts.ClassProperty,
37  methodName: string,
38  hasWatchDecorator: arkts.AnnotationUsage,
39  context: UISyntaxRuleContext
40): void {
41  context.report({
42    node: hasWatchDecorator,
43    message: rule.messages.invalidWatch,
44    data: { methodName },
45    fix: () => {
46      const startPosition = arkts.getEndPosition(member);
47      const endPosition = arkts.getEndPosition(member);
48      return {
49        range: [startPosition, endPosition],
50        code: `\n${methodName}(){\n}`,
51      };
52    },
53  });
54}
55
56function validateWatchDecorator(
57  member: arkts.ClassProperty,
58  methodNames: string[],
59  hasWatchDecorator: arkts.AnnotationUsage | undefined,
60  context: UISyntaxRuleContext
61): void {
62  member.annotations.forEach((annotation) => {
63    validateWatchProperty(annotation, member, methodNames, hasWatchDecorator, context);
64  });
65}
66
67function validateWatchProperty(
68  annotation: arkts.AnnotationUsage,
69  member: arkts.ClassProperty,
70  methodNames: string[],
71  hasWatchDecorator: arkts.AnnotationUsage | undefined,
72  context: UISyntaxRuleContext
73): void {
74  if (
75    annotation.expr &&
76    annotation.expr.dumpSrc() === PresetDecorators.WATCH
77  ) {
78    annotation.properties.forEach((element) => {
79      if (!arkts.isClassProperty(element)) {
80        return;
81      }
82      const methodName = element.value?.dumpSrc().slice(1, -1);
83      if (hasWatchDecorator && methodName && !methodNames.includes(methodName)) {
84        reportInvalidWatch(member, methodName, hasWatchDecorator, context);
85      }
86    });
87  }
88
89}
90
91function validateWatch(
92  node: arkts.StructDeclaration,
93  methodNames: string[],
94  context: UISyntaxRuleContext
95): void {
96  node.definition.body.forEach(member => {
97    if (!arkts.isClassProperty(member)) {
98      return;
99    }
100    const hasWatchDecorator = member.annotations?.find(annotation =>
101      annotation.expr &&
102      annotation.expr.dumpSrc() === PresetDecorators.WATCH
103    );
104    // Determine whether it contains @watch decorators
105    validateWatchDecorator(member, methodNames, hasWatchDecorator, context);
106  });
107}
108
109const rule: UISyntaxRule = {
110  name: 'watch-decorator-function',
111  messages: {
112    invalidWatch: `The '@Watch' decorated parameter must be a callback '{{methodName}}' of a function in a custom component.`,
113  },
114  setup(context) {
115    return {
116      parsed: (node): void => {
117        if (!arkts.isStructDeclaration(node)) {
118          return;
119        }
120        // Get all method names
121        const methodNames = getMethodNames(node);
122        validateWatch(node, methodNames, context);
123      },
124    };
125  },
126};
127
128export default rule;