• 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 { PresetDecorators } from '../utils';
18import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule';
19
20// Report an Observed version violation error
21function reportObservedConflict(
22  node: arkts.ClassProperty,
23  context: UISyntaxRuleContext,
24  message: string
25): void {
26  node.annotations.forEach((anno) => {
27    if (anno.expr?.dumpSrc()) {
28      context.report({
29        node: anno,
30        message: message,
31        data: {
32          annotation: anno.expr?.dumpSrc(),
33        }
34      });
35    }
36  });
37}
38
39function processNode(
40  node: arkts.ClassProperty,
41  annotationName: string,
42  observedV1Name: Set<string>,
43  observedV2Name: Set<string>,
44  context: UISyntaxRuleContext
45): void {
46  const queue: Array<arkts.AstNode> = [node];
47  while (queue.length > 0) {
48    const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode;
49    if (arkts.isIdentifier(currentNode)) {
50      if (observedV1Name.has(currentNode.dumpSrc()) && annotationName === PresetDecorators.COMPONENT_V2) {
51        reportObservedConflict(node, context, rule.messages.observedv1_v2);
52        break;
53      }
54      if (observedV2Name.has(currentNode.dumpSrc()) && annotationName === PresetDecorators.COMPONENT_V1) {
55        reportObservedConflict(node, context, rule.messages.observedv2_v1);
56        break;
57      }
58    }
59    const children = currentNode.getChildren();
60    for (const child of children) {
61      queue.push(child);
62    }
63  }
64}
65
66function traverseTree(
67  node: arkts.AstNode,
68  annotationName: string,
69  observedV1Name: Set<string>,
70  observedV2Name: Set<string>,
71  context: UISyntaxRuleContext
72): void {
73  if (arkts.isClassProperty(node)) {
74    processNode(node, annotationName, observedV1Name, observedV2Name, context);
75  }
76  const children = node.getChildren();
77  for (const child of children) {
78    traverseTree(child, annotationName, observedV1Name, observedV2Name, context);
79  }
80}
81
82function findAllObserved(node: arkts.AstNode, observedV1Name: Set<string>, observedV2Name: Set<string>): void {
83  if (arkts.isClassDeclaration(node)) {
84    node.definition?.annotations.forEach((anno) => {
85      if (anno.expr?.dumpSrc() === PresetDecorators.OBSERVED_V1) {
86        const componentV1Name = node?.definition?.ident?.name;
87        componentV1Name ? observedV1Name.add(componentV1Name) : null;
88      }
89      if (anno.expr?.dumpSrc() === PresetDecorators.OBSERVED_V2) {
90        const componentV2Name = node?.definition?.ident?.name;
91        componentV2Name ? observedV2Name.add(componentV2Name) : null;
92      }
93    });
94  }
95  const children = node.getChildren();
96  for (const child of children) {
97    findAllObserved(child, observedV1Name, observedV2Name);
98  }
99}
100
101function findAllTSTypeAliasDeclaration(
102  node: arkts.AstNode,
103  observedV1Name: Set<string>,
104  observedV2Name: Set<string>
105): void {
106  if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION) {
107    node.getChildren().forEach((child) => {
108      if (arkts.isIdentifier(child)) {
109        const typeName = child.dumpSrc();
110        findAllObservedType(node, typeName, observedV1Name, observedV2Name);
111      }
112    });
113  }
114  const children = node.getChildren();
115  for (const child of children) {
116    findAllTSTypeAliasDeclaration(child, observedV1Name, observedV2Name);
117  }
118}
119
120function findAllObservedType(
121  node: arkts.AstNode,
122  typeName: string,
123  observedV1Name: Set<string>,
124  observedV2Name: Set<string>
125): void {
126  if (arkts.isIdentifier(node) && observedV1Name.has(node.dumpSrc())) {
127    observedV1Name.add(typeName);
128  }
129  if (arkts.isIdentifier(node) && observedV2Name.has(node.dumpSrc())) {
130    observedV2Name.add(typeName);
131  }
132  const children = node.getChildren();
133  for (const child of children) {
134    findAllObservedType(child, typeName, observedV1Name, observedV2Name);
135  }
136}
137
138function processComponentAnnotations(
139  node: arkts.StructDeclaration,
140  observedV1Name: Set<string>,
141  observedV2Name: Set<string>,
142  context: UISyntaxRuleContext
143): void {
144  node?.definition?.annotations.forEach((anno) => {
145    if (anno.expr?.dumpSrc() === PresetDecorators.COMPONENT_V2) {
146      traverseTree(node, PresetDecorators.COMPONENT_V2, observedV1Name, observedV2Name, context);
147    }
148    if (anno.expr?.dumpSrc() === PresetDecorators.COMPONENT_V1) {
149      traverseTree(node, PresetDecorators.COMPONENT_V1, observedV1Name, observedV2Name, context);
150    }
151  });
152}
153
154const rule: UISyntaxRule = {
155  name: 'component-componentV2-mix-use-check',
156  messages: {
157    observedv1_v2: `The type of the @{{annotation}} Decorator property can not be a class decorated with @Observed.`,
158    observedv2_v1: `The type of the @{{annotation}} Decorator property can not be a class decorated with @ObservedV2.`
159  },
160  setup(context) {
161    let observedV1Name: Set<string> = new Set();
162    let observedV2Name: Set<string> = new Set();
163    return {
164      parsed: (node): void => {
165        if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) {
166          findAllObserved(node, observedV1Name, observedV2Name);
167          findAllTSTypeAliasDeclaration(node, observedV1Name, observedV2Name);
168        }
169        if (arkts.isStructDeclaration(node)) {
170          processComponentAnnotations(node, observedV1Name, observedV2Name, context);
171        }
172      },
173    };
174  },
175};
176
177export default rule;