From 78d0f62ef29ca3474f49d42726609c07f564b534 Mon Sep 17 00:00:00 2001 From: youzhi92 Date: Wed, 11 Jun 2025 15:50:05 +0800 Subject: [PATCH] support to get main_pages Signed-off-by: youzhi92 Change-Id: I412b15915056e6548e34ff3cc902b8bc7ec24cef --- OAT.xml | 1 + arkui-plugins/common/plugin-context.ts | 2 + .../demo/localtest/build_config_template.json | 7 +- .../entry/{ => src/main/ets/pages}/index.ets | 0 .../entry/{ => src/main/ets/pages}/new.ets | 0 .../localtest/entry/src/main/module.json5 | 37 ++++ .../resources/base/profile/main_pages.json | 6 + arkui-plugins/test/localtest_config.js | 92 ++++----- arkui-plugins/ui-syntax-plugins/index.ts | 8 +- .../ui-syntax-plugins/processor/index.ts | 177 ++++++++++++------ .../rules/build-root-node.ts | 4 +- .../check-construct-private-parameter.ts | 8 +- .../rules/check-decorated-property-type.ts | 8 +- .../ui-syntax-plugins/rules/ui-syntax-rule.ts | 18 +- .../ui-syntax-plugins/utils/index.ts | 30 ++- 15 files changed, 274 insertions(+), 124 deletions(-) rename arkui-plugins/test/demo/localtest/entry/{ => src/main/ets/pages}/index.ets (100%) rename arkui-plugins/test/demo/localtest/entry/{ => src/main/ets/pages}/new.ets (100%) create mode 100644 arkui-plugins/test/demo/localtest/entry/src/main/module.json5 create mode 100644 arkui-plugins/test/demo/localtest/entry/src/main/resources/base/profile/main_pages.json diff --git a/OAT.xml b/OAT.xml index 3aaf1c1b7..eb7f62ab1 100644 --- a/OAT.xml +++ b/OAT.xml @@ -26,6 +26,7 @@ + diff --git a/arkui-plugins/common/plugin-context.ts b/arkui-plugins/common/plugin-context.ts index 2a07f0670..6396f7bf8 100644 --- a/arkui-plugins/common/plugin-context.ts +++ b/arkui-plugins/common/plugin-context.ts @@ -101,6 +101,8 @@ export interface ProjectConfig { resetBundleName: boolean; allowEmptyBundleName: boolean; moduleType: string; + moduleRootPath: string; + aceModuleJsonPath: string; } export type PluginHandlerFunction = () => void; diff --git a/arkui-plugins/test/demo/localtest/build_config_template.json b/arkui-plugins/test/demo/localtest/build_config_template.json index 64b8fdafb..798e9e377 100755 --- a/arkui-plugins/test/demo/localtest/build_config_template.json +++ b/arkui-plugins/test/demo/localtest/build_config_template.json @@ -6,11 +6,11 @@ }, "compileFiles": [ - "./demo/localtest/entry/new.ets" + "./demo/localtest/entry/src/main/ets/pages/new.ets" ], "entryFiles": [ - "./demo/localtest/entry/new.ets" + "./demo/localtest/entry/src/main/ets/pages/new.ets" ], "buildMode": "Debug", @@ -43,5 +43,6 @@ "levelStr": "INFO", "colour": "green" }, - "isBuildConfigModified": false + "isBuildConfigModified": false, + "aceModuleJsonPath": "./demo/localtest/entry/src/main/module.json5" } diff --git a/arkui-plugins/test/demo/localtest/entry/index.ets b/arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/index.ets similarity index 100% rename from arkui-plugins/test/demo/localtest/entry/index.ets rename to arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/index.ets diff --git a/arkui-plugins/test/demo/localtest/entry/new.ets b/arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/new.ets similarity index 100% rename from arkui-plugins/test/demo/localtest/entry/new.ets rename to arkui-plugins/test/demo/localtest/entry/src/main/ets/pages/new.ets diff --git a/arkui-plugins/test/demo/localtest/entry/src/main/module.json5 b/arkui-plugins/test/demo/localtest/entry/src/main/module.json5 new file mode 100644 index 000000000..f2ff2c7f1 --- /dev/null +++ b/arkui-plugins/test/demo/localtest/entry/src/main/module.json5 @@ -0,0 +1,37 @@ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/arkui-plugins/test/demo/localtest/entry/src/main/resources/base/profile/main_pages.json b/arkui-plugins/test/demo/localtest/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 000000000..bc9792df1 --- /dev/null +++ b/arkui-plugins/test/demo/localtest/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,6 @@ +{ + "src": [ + "pages/index", + "pages/new" + ] +} \ No newline at end of file diff --git a/arkui-plugins/test/localtest_config.js b/arkui-plugins/test/localtest_config.js index 6372cbc8c..00d2cfb51 100644 --- a/arkui-plugins/test/localtest_config.js +++ b/arkui-plugins/test/localtest_config.js @@ -31,55 +31,63 @@ const jsonFilePath = path.join(__dirname, 'demo/localtest/build_config_template. const outJsonFilePath = path.join(__dirname, 'demo/localtest/build_config.json'); try { - // 读取 JSON 文件内容 - const data = fs.readFileSync(jsonFilePath, 'utf8'); - const jsonData = JSON.parse(data); - console.log(jsonData) - // 处理 baseUrl 字段 - if (jsonData.buildSdkPath) { - jsonData.buildSdkPath = jsonData.buildSdkPath.replace(/workspace/g, workSpace); - } + // 读取 JSON 文件内容 + const data = fs.readFileSync(jsonFilePath, 'utf8'); + const jsonData = JSON.parse(data); + console.log(jsonData) + // 处理 baseUrl 字段 + if (jsonData.buildSdkPath) { + jsonData.buildSdkPath = jsonData.buildSdkPath.replace(/workspace/g, workSpace); + } - // 处理 plugins 字段 - if (jsonData.plugins.ui_plugin) { - jsonData.plugins.ui_plugin = jsonData.plugins.ui_plugin.replace(/workspace/g, workSpace); - } - if (jsonData.plugins.memo_plugin) { - jsonData.plugins.memo_plugin = jsonData.plugins.memo_plugin.replace(/workspace/g, workSpace); - } + // 处理 plugins 字段 + if (jsonData.plugins.ui_syntax_plugin) { + jsonData.plugins.ui_syntax_plugin = jsonData.plugins.ui_syntax_plugin.replace(/workspace/g, workSpace); + } + if (jsonData.plugins.ui_plugin) { + jsonData.plugins.ui_plugin = jsonData.plugins.ui_plugin.replace(/workspace/g, workSpace); + } + if (jsonData.plugins.memo_plugin) { + jsonData.plugins.memo_plugin = jsonData.plugins.memo_plugin.replace(/workspace/g, workSpace); + } - // compileFiles - if (jsonData.compileFiles) { - jsonData.compileFiles = jsonData.compileFiles.map((file) => changePathToAbsPath(file)); - } + // compileFiles + if (jsonData.compileFiles) { + jsonData.compileFiles = jsonData.compileFiles.map((file) => changePathToAbsPath(file)); + } - // entryFiles - if (jsonData.entryFiles) { - jsonData.entryFiles = jsonData.entryFiles.map((file) => changePathToAbsPath(file)); - } + // entryFiles + if (jsonData.entryFiles) { + jsonData.entryFiles = jsonData.entryFiles.map((file) => changePathToAbsPath(file)); + } - // moduleRootPath - if (jsonData.moduleRootPath) { - jsonData.moduleRootPath = changePathToAbsPath(jsonData.moduleRootPath); - } + // moduleRootPath + if (jsonData.moduleRootPath) { + jsonData.moduleRootPath = changePathToAbsPath(jsonData.moduleRootPath); + } - // sourceRoots - if (jsonData.sourceRoots) { - jsonData.sourceRoots = jsonData.sourceRoots.map((file) => changePathToAbsPath(file)); - } + // sourceRoots + if (jsonData.sourceRoots) { + jsonData.sourceRoots = jsonData.sourceRoots.map((file) => changePathToAbsPath(file)); + } - // loaderOutPath - if (jsonData.loaderOutPath) { - jsonData.loaderOutPath = changePathToAbsPath(jsonData.loaderOutPath); - } + // loaderOutPath + if (jsonData.loaderOutPath) { + jsonData.loaderOutPath = changePathToAbsPath(jsonData.loaderOutPath); + } - // loaderOutPath - if (jsonData.cachePath) { - jsonData.cachePath = changePathToAbsPath(jsonData.cachePath); - } + // loaderOutPath + if (jsonData.cachePath) { + jsonData.cachePath = changePathToAbsPath(jsonData.cachePath); + } - // 将修改后的内容写回 JSON 文件 - fs.writeFileSync(outJsonFilePath, JSON.stringify(jsonData, null, 2), 'utf8'); + // appModuleJsonPath + if (jsonData.aceModuleJsonPath) { + jsonData.aceModuleJsonPath = changePathToAbsPath(jsonData.aceModuleJsonPath); + } + + // 将修改后的内容写回 JSON 文件 + fs.writeFileSync(outJsonFilePath, JSON.stringify(jsonData, null, 2), 'utf8'); } catch (error) { - console.error('处理 JSON 文件时出错:', error); + console.error('处理 JSON 文件时出错:', error); } \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/index.ts b/arkui-plugins/ui-syntax-plugins/index.ts index 53232081e..36167ae8c 100644 --- a/arkui-plugins/ui-syntax-plugins/index.ts +++ b/arkui-plugins/ui-syntax-plugins/index.ts @@ -24,13 +24,17 @@ export function uiSyntaxLinterTransform(): Plugins { return { name: 'ui-syntax-plugin', parsed(this: PluginContext): arkts.EtsScript | undefined { - const contextPtr = arkts.arktsGlobal.compilerContext?.peer ?? this.getContextPtr(); + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!contextPtr) { return undefined; } - let program = arkts.getOrUpdateGlobalContext(contextPtr).program; + const program = arkts.getOrUpdateGlobalContext(contextPtr).program; const node = program.astNode; if (node) { + const projectConfig = this.getProjectConfig(); + if (projectConfig) { + processor.setProjectConfig(projectConfig); + } const script = new ParsedUISyntaxLinterTransformer(processor).visitor( node, ) as arkts.EtsScript; diff --git a/arkui-plugins/ui-syntax-plugins/processor/index.ts b/arkui-plugins/ui-syntax-plugins/processor/index.ts index 6fdebeac5..d9bb88a10 100644 --- a/arkui-plugins/ui-syntax-plugins/processor/index.ts +++ b/arkui-plugins/ui-syntax-plugins/processor/index.ts @@ -14,76 +14,133 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRule, UISyntaxRuleContext } from '../rules/ui-syntax-rule'; -import { getUIComponents } from '../utils'; +import * as path from "node:path"; +import { ReportOptions, UISyntaxRule, UISyntaxRuleContext, UISyntaxRuleHandler } from '../rules/ui-syntax-rule'; +import { getUIComponents, readJSON, UISyntaxRuleComponents } from '../utils'; +import { ProjectConfig } from 'common/plugin-context'; export type UISyntaxRuleProcessor = { + setProjectConfig(projectConfig: ProjectConfig): void; parsed(node: arkts.AstNode): void; }; -export interface UISyntaxRuleComponents { - builtInAttributes: string[]; - containerComponents: string[]; - atomicComponents: string[]; - singleChildComponents: string[]; - validParentComponent: Map; - validChildComponent: Map; +type ModuleConfig = { + module: { + pages: string; + } } -export function createUISyntaxRuleProcessor( - rules: UISyntaxRule[], -): UISyntaxRuleProcessor { - const componentsInfo: UISyntaxRuleComponents = getUIComponents('../../components/'); - const context: UISyntaxRuleContext = { - report(options) { - const position = arkts.getStartPosition(options.node); - let message: string; - if (!options.data) { - message = options.message; - } else { - message = Object.entries(options.data).reduce( - (message, [placehoderName, placehoderValue]) => { - return message.replace(`{{${placehoderName}}}`, placehoderValue); - }, - options.message, - ); - } +type MainPages = { + src: string[]; +}; - let args: string[] = []; - const kind: arkts.DiagnosticKind = - arkts.DiagnosticKind.create(message, arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_ERROR); - if (options.fix) { - const diagnosticInfo: arkts.DiagnosticInfo = arkts.DiagnosticInfo.create(kind, ...args); - const suggestionInfo: arkts.SuggestionInfo = - arkts.SuggestionInfo.create(kind, options.fix(options.node).code, ...args); - const startPosition = arkts.getStartPosition(options.node); - const endPosition = arkts.getEndPosition(options.node); - const sourceRange: arkts.SourceRange = arkts.SourceRange.create(startPosition, endPosition); - arkts.Diagnostic.logDiagnosticWithSuggestion(diagnosticInfo, suggestionInfo, sourceRange); - } else { - arkts.Diagnostic.logDiagnostic(kind, arkts.getStartPosition(options.node)); - } +const BASE_RESOURCE_PATH = "src/main/resources/base"; +const ETS_PATH = "src/main/ets"; - // todo - if (options.fix) { - const suggestion = options.fix(options.node); - console.log(`syntax-error: ${message}`); - console.log(`range: (${suggestion.range[0].index()}, ${suggestion.range[0].line()}) - (${suggestion.range[1].index()}, ${suggestion.range[1].line()})`, - `code: ${suggestion.code}`); - } else { - console.log(`syntax-error: ${message} (${position.index()},${position.line()})`); - } - }, - componentsInfo: componentsInfo - }; +class ConcreteUISyntaxRuleContext implements UISyntaxRuleContext { + public componentsInfo: UISyntaxRuleComponents; + public projectConfig?: ProjectConfig; + + constructor() { + this.componentsInfo = getUIComponents('../../components/'); + } + + public report(options: ReportOptions): void { + let message: string; + if (!options.data) { + message = options.message; + } else { + message = this.format(options.message, options.data); + } - const instances = rules.map((rule) => rule.setup(context)); + const kind: arkts.DiagnosticKind = + arkts.DiagnosticKind.create(message, arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_ERROR); + if (options.fix) { + const diagnosticInfo: arkts.DiagnosticInfo = arkts.DiagnosticInfo.create(kind); + const fixSuggestion = options.fix(options.node); + const suggestionInfo: arkts.SuggestionInfo = arkts.SuggestionInfo.create(kind, fixSuggestion.code); + const [startPosition, endPosition] = fixSuggestion.range; + const sourceRange: arkts.SourceRange = arkts.SourceRange.create(startPosition, endPosition); + arkts.Diagnostic.logDiagnosticWithSuggestion(diagnosticInfo, suggestionInfo, sourceRange); + } else { + arkts.Diagnostic.logDiagnostic(kind, arkts.getStartPosition(options.node)); + } - return { - parsed(node): void { - for (const instance of instances) { - instance.parsed?.(node); + // todo + const position = arkts.getStartPosition(options.node); + if (options.fix) { + const suggestion = options.fix(options.node); + console.log(`syntax-error: ${message}`); + console.log(`range: (${suggestion.range[0].index()}, ${suggestion.range[0].line()}) - (${suggestion.range[1].index()}, ${suggestion.range[1].line()})`, + `code: ${suggestion.code}`); + } else { + console.log(`syntax-error: ${message} (${position.index()},${position.line()})`); + } + } + + getMainPages(): string[] { + if (!this.projectConfig) { + return []; + } + const { moduleRootPath, aceModuleJsonPath } = this.projectConfig; + if (!aceModuleJsonPath) { + throw new Error('The aceModuleJsonPath config is empty.'); + } + const moduleConfig = readJSON(aceModuleJsonPath); + if (!moduleConfig.module || !moduleConfig.module.pages) { + throw new Error('Failed to read pages because the content of module.json5 is invalid.'); + } + const pagesPath = moduleConfig.module.pages; + const matcher = /\$(?[_A-Za-z]+):(?[_A-Za-z]+)/.exec(pagesPath); + if (matcher && matcher.groups) { + const { directory, filename } = matcher.groups; + const mainPagesPath = path.resolve(moduleRootPath, BASE_RESOURCE_PATH, directory, `${filename}.json`); + const mainPages = readJSON(mainPagesPath); + if (!mainPages.src || !Array.isArray(mainPages.src)) { + throw new Error(`Failed to read pages because the content of ${filename}.json is invalid.`); } - }, - }; + return mainPages.src.map(page => path.resolve(moduleRootPath, ETS_PATH, `${page}.ets`)); + } else { + throw new Error('Failed to read pages from module.json5'); + } + } + + + private format(content: string, placeholders: object): string { + return Object.entries(placeholders).reduce( + (content, [placehoderName, placehoderValue]) => { + return content.replace(`{{${placehoderName}}}`, placehoderValue); + }, + content, + ); + } +} + +class ConcreteUISyntaxRuleProcessor implements UISyntaxRuleProcessor { + protected context: UISyntaxRuleContext; + protected handlers: UISyntaxRuleHandler[]; + + constructor(rules: UISyntaxRule[]) { + this.context = new ConcreteUISyntaxRuleContext(); + this.handlers = rules.map(rule => { + return rule.setup(this.context); + }); + } + + + parsed(node: arkts.AstNode): void { + for (const handlers of this.handlers) { + handlers.parsed?.(node); + } + } + + setProjectConfig(projectConfig: ProjectConfig): void { + this.context.projectConfig = projectConfig; + } +} + +export function createUISyntaxRuleProcessor( + rules: UISyntaxRule[], +): UISyntaxRuleProcessor { + return new ConcreteUISyntaxRuleProcessor(rules); } diff --git a/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts b/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts index 39d8bb07a..f8bbf624c 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts @@ -49,7 +49,7 @@ function reportvalidBuildRoot( } function checkBuildRootNode(node: arkts.AstNode, context: UISyntaxRuleContext): void { - const loadedContainerComponents = context.containerComponents; + const loadedContainerComponents = context.componentsInfo.containerComponents; if (!arkts.isStructDeclaration(node)) { return; } @@ -92,7 +92,7 @@ function checkBuildRootNode(node: arkts.AstNode, context: UISyntaxRuleContext): return; } isContainer = componentName - ? loadedContainerComponents.has(componentName) + ? loadedContainerComponents.includes(componentName) : false; // rule2: its 'build' function can have only one root node, which must be a container component. reportvalidBuildRoot(entryDecoratorUsage, isContainer, buildNode, context); diff --git a/arkui-plugins/ui-syntax-plugins/rules/check-construct-private-parameter.ts b/arkui-plugins/ui-syntax-plugins/rules/check-construct-private-parameter.ts index 6bbdbc59c..d3e4045cf 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/check-construct-private-parameter.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/check-construct-private-parameter.ts @@ -21,13 +21,17 @@ function addProperty(item: arkts.AstNode, structName: string, privatePropertyMap if (!arkts.isClassProperty(item) || !isPrivateClassProperty(item)) { return; } + const propertyName = getClassPropertyName(item); + if (!propertyName) { + return; + } // Check if structName already exists in privateMap if (privatePropertyMap.has(structName)) { // If it exists, retrieve the current string[] and append the new content - privatePropertyMap.get(structName)?.push(getClassPropertyName(item)); + privatePropertyMap.get(structName)!.push(propertyName); } else { // If it doesn't exist, create a new string[] and add the content - privatePropertyMap.set(structName, [getClassPropertyName(item)]); + privatePropertyMap.set(structName, [propertyName]); } } diff --git a/arkui-plugins/ui-syntax-plugins/rules/check-decorated-property-type.ts b/arkui-plugins/ui-syntax-plugins/rules/check-decorated-property-type.ts index d8f8f0dd9..05563bbcc 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/check-decorated-property-type.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/check-decorated-property-type.ts @@ -44,8 +44,11 @@ function checkDecoratedPropertyType( if (!arkts.isClassProperty(member)) { return; } - const propertyName: string = getClassPropertyName(member); - const propertyType: string = getClassPropertyType(member); + const propertyName = getClassPropertyName(member); + const propertyType = getClassPropertyType(member); + if (!propertyName || !propertyType) { + return; + } const propertyAnnotationNames: string[] = getClassPropertyAnnotationNames(member); const decoratorName: string | undefined = propertyAnnotationNames.find((annotation) => annoList.includes(annotation)); @@ -61,6 +64,7 @@ function checkDecoratedPropertyType( data: { decoratorName, propertyName, propertyType }, }); } + } const rule: UISyntaxRule = { diff --git a/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts b/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts index f0096765c..e62fa514b 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts @@ -14,7 +14,8 @@ */ import * as arkts from '@koalaui/libarkts'; -import { UISyntaxRuleComponents } from 'ui-syntax-plugins/processor'; +import { ProjectConfig } from 'common/plugin-context'; +import { UISyntaxRuleComponents } from 'ui-syntax-plugins/utils'; export type FixSuggestion = { range: [start: arkts.SourcePosition, end: arkts.SourcePosition]; @@ -29,17 +30,20 @@ export type ReportOptions = { }; export type UISyntaxRuleContext = { - report(options: ReportOptions): void; - configOfComponent?: Set | undefined; + projectConfig?: ProjectConfig; componentsInfo: UISyntaxRuleComponents; + report(options: ReportOptions): void; + getMainPages(): string[]; }; -export type UISyntaxRuleHandler = (node: arkts.AstNode) => void; +export type UISyntaxRulePhaseHandler = (node: arkts.AstNode) => void; + +export type UISyntaxRuleHandler = { + parsed?: UISyntaxRulePhaseHandler; +}; export type UISyntaxRule = { name: string; messages: Record; - setup(context: UISyntaxRuleContext): { - parsed?: UISyntaxRuleHandler; - }; + setup(context: UISyntaxRuleContext): UISyntaxRuleHandler; }; diff --git a/arkui-plugins/ui-syntax-plugins/utils/index.ts b/arkui-plugins/ui-syntax-plugins/utils/index.ts index 7bd08c525..91694abaf 100644 --- a/arkui-plugins/ui-syntax-plugins/utils/index.ts +++ b/arkui-plugins/ui-syntax-plugins/utils/index.ts @@ -16,7 +16,6 @@ import * as arkts from '@koalaui/libarkts'; import * as fs from 'fs'; import * as path from 'path'; -import { UISyntaxRuleComponents } from 'ui-syntax-plugins/processor'; import { UISyntaxRuleContext } from 'ui-syntax-plugins/rules/ui-syntax-rule'; export const BUILD_NAME: string = 'build'; @@ -166,12 +165,15 @@ export function getClassAnnotationUsage( -export function getClassPropertyName(property: arkts.ClassProperty): string { +export function getClassPropertyName(property: arkts.ClassProperty): string | undefined { + if (!property.key) { + return undefined; + } return getIdentifierName(property.key); } -export function getClassPropertyType(property: arkts.ClassProperty): string { - return property.typeAnnotation.dumpSrc(); +export function getClassPropertyType(property: arkts.ClassProperty): string | undefined { + return property.typeAnnotation?.dumpSrc(); } export function getClassPropertyAnnotationNames( @@ -252,6 +254,15 @@ interface ComponentJson { children?: string[]; } +export interface UISyntaxRuleComponents { + builtInAttributes: string[]; + containerComponents: string[]; + atomicComponents: string[]; + singleChildComponents: string[]; + validParentComponent: Map; + validChildComponent: Map; +} + export function getUIComponents(dirPath: string): UISyntaxRuleComponents { const absolutePath = path.resolve(__dirname, dirPath); let builtInAttributes: string[] = []; @@ -327,4 +338,15 @@ export function isContainerComponent(context: UISyntaxRuleContext, componentName export function isSingleChildComponent(context: UISyntaxRuleContext, componentName: string): boolean { return context.componentsInfo.singleChildComponents.includes(componentName); +} + +export function readJSON(path: string): T { + if (!fs.existsSync(path)) { + throw new Error(`Failed to read file becasue the ${path} is not exist.`); + } + const content = fs.readFileSync(path).toString(); + if (!content) { + throw new Error(`Failed to read file because the file content is empty.`); + } + return JSON.parse(content) as T; } \ No newline at end of file -- Gitee