• 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 { getAnnotationName, PresetDecorators } from '../utils';
18import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule';
19
20// Decorators that cannot be repeated
21const validDecorators = [
22  PresetDecorators.ENTRY,
23  PresetDecorators.COMPONENT_V1,
24  PresetDecorators.COMPONENT_V2,
25  PresetDecorators.REUSABLE_V1,
26  PresetDecorators.PREVIEW,
27  PresetDecorators.REUSABLE_V2,
28  PresetDecorators.CUSTOM_DIALOG,
29];
30
31function checkForDuplicateDecorators(context: UISyntaxRuleContext, node: arkts.StructDeclaration): void {
32  // Initialize a map to record decorators and their occurrences
33  const decoratorCounts: Map<string, { count: number, annotations: arkts.AnnotationUsage[] }> = new Map();
34  if (!node.definition || !node.definition.annotations) {
35    return;
36  }
37  // Record all decorators and their counts
38  node.definition.annotations.forEach((annotation) => {
39    const decoratorName = getAnnotationName(annotation);
40    if (!validDecorators.includes(decoratorName)) {
41      return;
42    }
43
44    if (decoratorCounts.has(decoratorName)) {
45      const decoratorInfo = decoratorCounts.get(decoratorName)!;
46      decoratorInfo.count += 1;
47      decoratorInfo.annotations.push(annotation);
48    } else {
49      decoratorCounts.set(decoratorName, { count: 1, annotations: [annotation] });
50    }
51  });
52
53  // Process decorators with more than one occurrence
54  decoratorCounts.forEach(({ count, annotations }, decoratorName) => {
55    if (count <= 1) {
56      return;
57    }
58    // Report errors for all occurrences except the last one
59    for (let i = 0; i < annotations.length - 1; i++) {
60      const prevAnnotation = annotations[i];
61      reportDuplicateDecorator(context, prevAnnotation);
62    }
63    // For the last occurrence, report an error but do not provide a fix
64    const lastAnnotation = annotations[annotations.length - 1];
65    context.report({
66      node: lastAnnotation,
67      message: rule.messages.duplicateDecorator,
68    });
69  });
70}
71
72function reportDuplicateDecorator(context: UISyntaxRuleContext, annotation: arkts.AnnotationUsage): void {
73  context.report({
74    node: annotation,
75    message: rule.messages.duplicateDecorator,
76    fix: () => {
77      const startPosition = arkts.getStartPosition(annotation);
78      const endPosition = arkts.getEndPosition(annotation);
79      return {
80        range: [startPosition, endPosition],
81        code: '',
82      };
83    },
84  });
85}
86
87const rule: UISyntaxRule = {
88  name: 'no-duplicate-decorators',
89  messages: {
90    duplicateDecorator: `Duplicate decorators for struct are not allowed.`,
91  },
92  setup(context) {
93    return {
94      parsed: (node): void => {
95        if (!arkts.isStructDeclaration(node)) {
96          return;
97        }
98        checkForDuplicateDecorators(context, node);
99      },
100    };
101  },
102};
103
104export default rule;