From 8ba937dbf7a8169bd3c2f05621afc0067611d4de Mon Sep 17 00:00:00 2001 From: youzhi92 Date: Thu, 26 Jun 2025 11:44:15 +0800 Subject: [PATCH] adapter new es2pandaMode Signed-off-by: youzhi92 Change-Id: I930ec5413c81c24bc5c7ef7f463dce0d2efc4a62 --- arkui-plugins/common/plugin-context.ts | 20 +- .../demo/localtest/build_config_template.json | 92 ++-- arkui-plugins/ui-syntax-plugins/index.ts | 124 +++-- .../ui-syntax-plugins/processor/index.ts | 41 +- .../rules/no-duplicate-entry.ts | 93 ++-- .../ui-syntax-plugins/rules/ui-syntax-rule.ts | 5 +- ...mer.ts => ui-syntax-linter-transformer.ts} | 14 +- .../transformers/ui-syntax-linter-visitor.ts | 22 +- .../ui-syntax-plugins/utils/index.ts | 487 +++++++++--------- 9 files changed, 515 insertions(+), 383 deletions(-) rename arkui-plugins/ui-syntax-plugins/transformers/{parsed-ui-syntax-linter-transformer.ts => ui-syntax-linter-transformer.ts} (76%) diff --git a/arkui-plugins/common/plugin-context.ts b/arkui-plugins/common/plugin-context.ts index 9aa704871..f661350a0 100644 --- a/arkui-plugins/common/plugin-context.ts +++ b/arkui-plugins/common/plugin-context.ts @@ -21,12 +21,14 @@ export class PluginContext { private program: arkts.Program | undefined; private projectConfig: ProjectConfig | undefined; private contextPtr: number | undefined; + private codingFilePath: string | undefined; constructor() { this.ast = undefined; this.program = undefined; this.projectConfig = undefined; this.contextPtr = undefined; + this.codingFilePath = undefined; } /** @@ -72,6 +74,18 @@ export class PluginContext { public getContextPtr(): number | undefined { return this.contextPtr; } + + public setCodingFilePath(codingFilePath: string): void { + this.codingFilePath = codingFilePath; + } + + public getCodingFilePath(): string | undefined { + return this.codingFilePath; + } + + public isCoding(): boolean { + return this.codingFilePath !== undefined; + } } export interface DependentModuleConfig { @@ -81,9 +95,9 @@ export interface DependentModuleConfig { modulePath: string; sourceRoots: string[]; entryFile: string; - language: string, - declFilesPath?: string, - dependencies?: string[] + language: string; + declFilesPath?: string; + dependencies?: string[]; } export interface ProjectConfig { diff --git a/arkui-plugins/test/demo/localtest/build_config_template.json b/arkui-plugins/test/demo/localtest/build_config_template.json index 1229a36b4..0d92ceecc 100755 --- a/arkui-plugins/test/demo/localtest/build_config_template.json +++ b/arkui-plugins/test/demo/localtest/build_config_template.json @@ -1,49 +1,45 @@ { - "plugins": { - "ui_syntax_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/ui-syntax-plugins/index", - "ui_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/ui-plugins/index", - "memo_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/memo-plugins/index" - }, - - "compileFiles": [ - "./demo/localtest/entry/src/main/ets/pages/new.ets" - ], - - "entryFiles": [ - "./demo/localtest/entry/src/main/ets/pages/new.ets" - ], - - "buildMode": "Debug", - "projectRootPath": "./demo/localtest/", - "moduleRootPath": "./demo/localtest/entry/", - "cachePath": "./dist/cache", - "loaderOutPath": "./dist", - "compileSdkVersion": 20, - "compatibleSdkVersion": 20, - "bundleName": "com.example.myapplication", - "useNormalizedOHMUrl": true, - "buildType": "build", - "packageName": "entry", - "buildSdkPath": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/", - "sourceRoots": [ - "./" - ], - "moduleType": "shared", - "moduleName": "entry", - "dependentModuleList": [], - "hasMainModule": true, - "buildLoaderJson": "", - "integratedHsp": false, - "allowEmptyBundleName": false, - "declgenV2OutPath": "", - "externalApiPaths": [ - ], - "level": { - "level": 20000, - "levelStr": "INFO", - "colour": "green" - }, - "isBuildConfigModified": false, - "aceModuleJsonPath": "./demo/localtest/entry/src/main/module.json5", - "es2pandaMode": 2 -} + "plugins": { + "ui_syntax_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/ui-syntax-plugins/index", + "ui_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/ui-plugins/index", + "memo_plugin": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/lib/memo-plugins/index" + }, + "compileFiles": [ + "./demo/localtest/entry/src/main/ets/pages/new.ets" + ], + "entryFiles": [ + "./demo/localtest/entry/src/main/ets/pages/new.ets" + ], + "buildMode": "Debug", + "projectRootPath": "./demo/localtest/", + "moduleRootPath": "./demo/localtest/entry/", + "cachePath": "./dist/cache", + "loaderOutPath": "./dist", + "compileSdkVersion": 20, + "compatibleSdkVersion": 20, + "bundleName": "com.example.myapplication", + "useNormalizedOHMUrl": true, + "buildType": "build", + "packageName": "entry", + "buildSdkPath": "workspace/out/sdk/ohos-sdk/linux/ets/ets1.2/", + "sourceRoots": [ + "./" + ], + "moduleType": "shared", + "moduleName": "entry", + "dependentModuleList": [], + "hasMainModule": true, + "buildLoaderJson": "", + "integratedHsp": false, + "allowEmptyBundleName": false, + "declgenV2OutPath": "", + "externalApiPaths": [], + "level": { + "level": 20000, + "levelStr": "INFO", + "colour": "green" + }, + "isBuildConfigModified": false, + "aceModuleJsonPath": "./demo/localtest/entry/src/main/module.json5", + "es2pandaMode": 2 +} \ 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 5447998e2..0ea22cf59 100644 --- a/arkui-plugins/ui-syntax-plugins/index.ts +++ b/arkui-plugins/ui-syntax-plugins/index.ts @@ -14,40 +14,104 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PluginContext, Plugins } from '../common/plugin-context'; -import { ParsedUISyntaxLinterTransformer } from './transformers/parsed-ui-syntax-linter-transformer'; +import { PluginContext, PluginHandler, Plugins } from '../common/plugin-context'; +import { + CheckedUISyntaxLinterTransformer, + ParsedUISyntaxLinterTransformer, +} from './transformers/ui-syntax-linter-transformer'; import { createUISyntaxRuleProcessor } from './processor'; +import { UISyntaxLinterVisitor } from './transformers/ui-syntax-linter-visitor'; import rules from './rules'; +import { matchPrefix } from '../common/arkts-utils'; +import { EXCLUDE_EXTERNAL_SOURCE_PREFIXES } from './utils'; export function uiSyntaxLinterTransform(): Plugins { - const processor = createUISyntaxRuleProcessor(rules); - return { - name: 'ui-syntax-plugin', - parsed(this: PluginContext): arkts.EtsScript | undefined { - const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; - if (!contextPtr) { - return undefined; - } - const program = arkts.getOrUpdateGlobalContext(contextPtr).program; - const projectConfig = this.getProjectConfig(); - const isFrameworkMode = this.getProjectConfig()?.frameworkMode; - const canSkipPhases = !isFrameworkMode; - if (canSkipPhases) { - return undefined; - } - const node = program.astNode; - if (node) { - if (projectConfig) { - processor.setProjectConfig(projectConfig); + const processor = createUISyntaxRuleProcessor(rules); + const parsedTransformer = new ParsedUISyntaxLinterTransformer(processor); + const checkedTransformer = new CheckedUISyntaxLinterTransformer(processor); + function createTransformer(phase: string, transformer: UISyntaxLinterVisitor): PluginHandler { + const visitedPrograms: Set = new Set(); + const visitedExternalSources: Set = new Set(); + return tracePerformance(`UISyntaxPlugin::${phase}`, function (this: PluginContext): + | arkts.EtsScript + | undefined { + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; + if (!contextPtr) { + return undefined; + } + const projectConfig = this.getProjectConfig(); + if (!projectConfig) { + return undefined; + } + processor.setProjectConfig(projectConfig); + if (projectConfig.frameworkMode) { + return undefined; + } + const program = arkts.getOrUpdateGlobalContext(contextPtr).program; + if (visitedPrograms.has(program.peer)) { + return undefined; + } + const isCoding = this.isCoding?.() ?? false; + if (isCoding) { + const codingFilePath = this.getCodingFilePath(); + if (program.absName === codingFilePath) { + return transformProgram.call(this, transformer, program); + } + } else { + transformExternalSources.call(this, program, visitedExternalSources, visitedPrograms, transformer); + if (program.absName) { + return transformProgram.call(this, transformer, program); + } + } + visitedPrograms.add(program.peer); + return undefined; + }); + } + return { + name: 'ui-syntax-plugin', + parsed: createTransformer('parsed', parsedTransformer), + checked: createTransformer('checked', checkedTransformer), + }; +} + +function transformExternalSources( + this: PluginContext, + program: arkts.Program, + visitedExternalSources: Set, + visitedPrograms: Set, + transformer: UISyntaxLinterVisitor +) { + const externalSources = program.externalSources; + for (const externalSource of externalSources) { + if (matchPrefix(EXCLUDE_EXTERNAL_SOURCE_PREFIXES, externalSource.getName())) { + continue; + } + if (visitedExternalSources.has(externalSource)) { + continue; + } + const programs = externalSource.programs; + for (const program of programs) { + if (visitedPrograms.has(program.peer)) { + continue; + } + const script = transformer.transform(program.astNode) as arkts.EtsScript; + this.setArkTSAst(script); } - const script = new ParsedUISyntaxLinterTransformer(processor).visitor( - node, - ) as arkts.EtsScript; - arkts.setAllParents(script); - this.setArkTSAst(script); - return script; - } - return undefined; + visitedExternalSources.add(externalSource.peer); } - }; +} + +function transformProgram(this: PluginContext, transformer: UISyntaxLinterVisitor, program: arkts.Program) { + const script = transformer.transform(program.astNode) as arkts.EtsScript; + this.setArkTSAst(script); + return script; +} + +function tracePerformance any>(name: string, fn: T): T { + return function (this: ThisParameterType, ...args: Parameters): ReturnType { + arkts.Performance.getInstance().createEvent(name); + const result = fn.apply(this, args); + arkts.Performance.getInstance().stopEvent(name, true); + return result; + } as T; } diff --git a/arkui-plugins/ui-syntax-plugins/processor/index.ts b/arkui-plugins/ui-syntax-plugins/processor/index.ts index ff0d0638f..c74443f90 100644 --- a/arkui-plugins/ui-syntax-plugins/processor/index.ts +++ b/arkui-plugins/ui-syntax-plugins/processor/index.ts @@ -16,6 +16,7 @@ import * as arkts from '@koalaui/libarkts'; import * as path from 'node:path'; import { + AbstractUISyntaxRule, ReportOptions, UISyntaxRule, UISyntaxRuleConfig, @@ -27,7 +28,10 @@ import { ProjectConfig } from 'common/plugin-context'; export type UISyntaxRuleProcessor = { setProjectConfig(projectConfig: ProjectConfig): void; + beforeTransform(): void; + afterTransform(): void; parsed(node: arkts.AstNode): void; + checked(node: arkts.AstNode): void; }; type ModuleConfig = { @@ -68,9 +72,14 @@ class ConcreteUISyntaxRuleContext implements UISyntaxRuleContext { if (options.fix) { const diagnosticInfo: arkts.DiagnosticInfo = arkts.DiagnosticInfo.create(diagnosticKind); const fixSuggestion = options.fix(options.node); - const suggestionKind: arkts.DiagnosticKind = - arkts.DiagnosticKind.create(message, arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_SUGGESTION); - const suggestionInfo: arkts.SuggestionInfo = arkts.SuggestionInfo.create(suggestionKind, fixSuggestion.code); + const suggestionKind: arkts.DiagnosticKind = arkts.DiagnosticKind.create( + message, + arkts.PluginDiagnosticType.ES2PANDA_PLUGIN_SUGGESTION + ); + const suggestionInfo: arkts.SuggestionInfo = arkts.SuggestionInfo.create( + suggestionKind, + fixSuggestion.code + ); const [startPosition, endPosition] = fixSuggestion.range; const sourceRange: arkts.SourceRange = arkts.SourceRange.create(startPosition, endPosition); arkts.Diagnostic.logDiagnosticWithSuggestion(diagnosticInfo, suggestionInfo, sourceRange); @@ -145,9 +154,31 @@ class ConcreteUISyntaxRuleProcessor implements UISyntaxRuleProcessor { }, []); } + beforeTransform(): void { + for (const handler of this.handlers) { + if (handler instanceof AbstractUISyntaxRule) { + handler.beforeTransform(); + } + } + } + + afterTransform(): void { + for (const handler of this.handlers) { + if (handler instanceof AbstractUISyntaxRule) { + handler.afterTransform(); + } + } + } + parsed(node: arkts.AstNode): void { - for (const handlers of this.handlers) { - handlers.parsed?.(node); + for (const handler of this.handlers) { + handler.parsed?.(node); + } + } + + checked(node: arkts.AstNode): void { + for (const handler of this.handlers) { + handler.checked?.(node); } } diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts index 90cecb674..41e0a71e2 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts @@ -18,55 +18,58 @@ import { getAnnotationUsage, MAX_ENTRY_DECORATOR_COUNT, PresetDecorators } from import { AbstractUISyntaxRule } from './ui-syntax-rule'; class NoDuplicateEntryRule extends AbstractUISyntaxRule { - private entryDecoratorUsages: arkts.AnnotationUsage[] = []; - private entryDecoratorUsageIndex = 1; + private entryDecoratorUsages: arkts.AnnotationUsage[] = []; + private entryDecoratorUsageIndex = 1; - public setup(): Record { - return { - duplicateEntry: `A page can't contain more then one '@Entry' decorator.`, - }; - } - public parsed(node: arkts.StructDeclaration): void { - if (!arkts.isStructDeclaration(node)) { - return; - } - let entryDecoratorUsage = getAnnotationUsage( - node, - PresetDecorators.ENTRY, - ); - if (entryDecoratorUsage) { - this.entryDecoratorUsages.push(entryDecoratorUsage); + public setup(): Record { + return { + duplicateEntry: `A page can't contain more then one '@Entry' decorator.`, + }; } - // If more than one entry decorator is recorded, an error is reported - if (this.entryDecoratorUsages.length <= MAX_ENTRY_DECORATOR_COUNT) { - return; + + public beforeTransform(): void { + this.entryDecoratorUsages = []; + this.entryDecoratorUsageIndex = 1; } - if (this.entryDecoratorUsageIndex === MAX_ENTRY_DECORATOR_COUNT) { - const entryDecoratorUsage = this.entryDecoratorUsages.at(0)!; - this.report({ - node: entryDecoratorUsage, - message: this.messages.duplicateEntry, - fix: () => { - return { - range: [entryDecoratorUsage.startPosition, entryDecoratorUsage.endPosition], - code: '', - }; + + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isStructDeclaration(node)) { + return; } - }); + let entryDecoratorUsage = getAnnotationUsage(node, PresetDecorators.ENTRY); + if (entryDecoratorUsage) { + this.entryDecoratorUsages.push(entryDecoratorUsage); + } + // If more than one entry decorator is recorded, an error is reported + if (this.entryDecoratorUsages.length <= MAX_ENTRY_DECORATOR_COUNT) { + return; + } + if (this.entryDecoratorUsageIndex === MAX_ENTRY_DECORATOR_COUNT) { + const entryDecoratorUsage = this.entryDecoratorUsages.at(0)!; + this.report({ + node: entryDecoratorUsage, + message: this.messages.duplicateEntry, + fix: () => { + return { + range: [entryDecoratorUsage.startPosition, entryDecoratorUsage.endPosition], + code: '', + }; + }, + }); + } + entryDecoratorUsage = this.entryDecoratorUsages.at(this.entryDecoratorUsageIndex)!; + this.report({ + node: entryDecoratorUsage, + message: this.messages.duplicateEntry, + fix: () => { + return { + range: [entryDecoratorUsage.startPosition, entryDecoratorUsage.endPosition], + code: '', + }; + }, + }); + this.entryDecoratorUsageIndex++; } - entryDecoratorUsage = this.entryDecoratorUsages.at(this.entryDecoratorUsageIndex)!; - this.report({ - node: entryDecoratorUsage, - message: this.messages.duplicateEntry, - fix: () => { - return { - range: [entryDecoratorUsage.startPosition, entryDecoratorUsage.endPosition], - code: '', - }; - } - }); - this.entryDecoratorUsageIndex++; - } } -export default NoDuplicateEntryRule; \ No newline at end of file +export default NoDuplicateEntryRule; 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 47774e2db..eef2c658f 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts @@ -41,6 +41,7 @@ export type UISyntaxRulePhaseHandler = (node: arkts.AstNode) => void; export type UISyntaxRuleHandler = { parsed?: UISyntaxRulePhaseHandler; + checked?: UISyntaxRulePhaseHandler; }; export type UISyntaxRule = { @@ -69,8 +70,10 @@ export abstract class AbstractUISyntaxRule { this.messages = this.setup(); } + public beforeTransform(): void {} + public afterTransform(): void {} public parsed(node: arkts.AstNode): void {} - public binded(node: arkts.AstNode): void {} + public checked(node: arkts.AstNode): void {} public abstract setup(): Record; protected report(options: UISyntaxRuleReportOptions): void { diff --git a/arkui-plugins/ui-syntax-plugins/transformers/parsed-ui-syntax-linter-transformer.ts b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-transformer.ts similarity index 76% rename from arkui-plugins/ui-syntax-plugins/transformers/parsed-ui-syntax-linter-transformer.ts rename to arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-transformer.ts index d8c642ed2..0e86f8152 100644 --- a/arkui-plugins/ui-syntax-plugins/transformers/parsed-ui-syntax-linter-transformer.ts +++ b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-transformer.ts @@ -17,9 +17,13 @@ import * as arkts from '@koalaui/libarkts'; import { UISyntaxLinterVisitor } from './ui-syntax-linter-visitor'; export class ParsedUISyntaxLinterTransformer extends UISyntaxLinterVisitor { - visitor(node: arkts.AstNode): arkts.AstNode { - this.processor.parsed(node); - node = this.visitEachChild(node); - return node; - } + handle(node: arkts.AstNode): void { + this.processor.parsed(node); + } +} + +export class CheckedUISyntaxLinterTransformer extends UISyntaxLinterVisitor { + handle(node: arkts.AstNode): void { + this.processor.checked(node); + } } diff --git a/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-visitor.ts b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-visitor.ts index 356304f88..33e1c85f1 100644 --- a/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-visitor.ts +++ b/arkui-plugins/ui-syntax-plugins/transformers/ui-syntax-linter-visitor.ts @@ -13,11 +13,27 @@ * limitations under the License. */ +import * as arkts from '@koalaui/libarkts'; import { AbstractVisitor } from '../../common/abstract-visitor'; import { UISyntaxRuleProcessor } from '../processor'; export abstract class UISyntaxLinterVisitor extends AbstractVisitor { - constructor(protected processor: UISyntaxRuleProcessor) { - super(); - } + constructor(protected processor: UISyntaxRuleProcessor) { + super(); + } + + visitor(node: arkts.AstNode): arkts.AstNode { + this.handle(node); + node = this.visitEachChild(node); + return node; + } + + transform(node: arkts.AstNode): arkts.AstNode { + this.processor.beforeTransform(); + const transformedNode = this.visitor(node); + this.processor.afterTransform(); + return transformedNode; + } + + abstract handle(node: arkts.AstNode): void; } diff --git a/arkui-plugins/ui-syntax-plugins/utils/index.ts b/arkui-plugins/ui-syntax-plugins/utils/index.ts index 5dffe68af..133ec21b5 100644 --- a/arkui-plugins/ui-syntax-plugins/utils/index.ts +++ b/arkui-plugins/ui-syntax-plugins/utils/index.ts @@ -18,6 +18,23 @@ import * as fs from 'fs'; import * as path from 'path'; import { UISyntaxRuleContext } from 'ui-syntax-plugins/rules/ui-syntax-rule'; +export const EXCLUDE_EXTERNAL_SOURCE_PREFIXES: Array = [ + 'std', + 'escompat', + 'security', + 'application', + 'permissions', + 'bundleManager', + 'commonEvent', + 'global', + 'arkui', + /@arkts\..*/, + /@ohos\.*/, + /@system\..*/, + /@koalaui\./, + /ability\..*/, +]; + export const BUILD_NAME: string = 'build'; export const SINGLE_CHILD_COMPONENT: number = 1; @@ -28,79 +45,79 @@ export const COMPONENT_REPEAT: string = 'Repeat'; export const TEMPLATE: string = 'template'; export const PresetType = { - STRING: 'string', - NUMBER: 'number', - BOOLEAN: 'boolean', - BIGINT: 'bigint' + STRING: 'string', + NUMBER: 'number', + BOOLEAN: 'boolean', + BIGINT: 'bigint', }; export const forbiddenUseStateType: string[] = [ - 'Scroller', - 'SwiperScroller', - 'VideoController', - 'WebController', - 'CustomDialogController', - 'SwiperController', - 'TabsController', - 'CalendarController', - 'AbilityController', - 'XComponentController', - 'CanvasRenderingContext2D', - 'CanvasGradient', - 'ImageBitmap', - 'ImageData', - 'Path2D', - 'RenderingContextSettings', - 'OffscreenCanvasRenderingContext2D', - 'PatternLockController', - 'TextAreaController', - 'TextInputController', - 'TextTimerController', - 'SearchController', - 'RichEditorController', + 'Scroller', + 'SwiperScroller', + 'VideoController', + 'WebController', + 'CustomDialogController', + 'SwiperController', + 'TabsController', + 'CalendarController', + 'AbilityController', + 'XComponentController', + 'CanvasRenderingContext2D', + 'CanvasGradient', + 'ImageBitmap', + 'ImageData', + 'Path2D', + 'RenderingContextSettings', + 'OffscreenCanvasRenderingContext2D', + 'PatternLockController', + 'TextAreaController', + 'TextInputController', + 'TextTimerController', + 'SearchController', + 'RichEditorController', ]; export const PresetDecorators = { - TOGGLE: 'Toggle', - BUILDER_PARAM: 'BuilderParam', - COMPONENT_V1: 'Component', - COMPONENT_V2: 'ComponentV2', - COMPUTED: 'Computed', - CONSUME: 'Consume', - CONSUMER: 'Consumer', - CUSTOM_DIALOG: 'CustomDialog', - ENTRY: 'Entry', - EVENT: 'Event', - PREVIEW: 'Preview', - STATE: 'State', - PARAM: 'Param', - PROP: 'Prop', - PROVIDE: 'Provide', - PROVIDER: 'Provider', - LINK: 'Link', - LOCAL: 'Local', - OBJECT_LINK: 'ObjectLink', - STORAGE_PROP: 'StorageProp', - STORAGE_LINK: 'StorageLink', - LOCAL_STORAGE_PROP: 'LocalStorageProp', - LOCAL_STORAGE_LINK: 'LocalStorageLink', - REQUIRE: 'Require', - REUSABLE_V1: 'Reusable', - REUSABLE_V2: 'ReusableV2', - OBSERVED_V1: 'Observed', - OBSERVED_V2: 'ObservedV2', - TYPE: 'Type', - WATCH: 'Watch', - BUILDER: 'Builder', - TRACK: 'Track', - TRACE: 'Trace', - ONCE: 'Once', - MONITOR: 'Monitor', - LOCAL_BUILDER: 'LocalBuilder', - REGULAR: 'regular', - VARIABLE: 'variable', - PARAMETER: 'parameter', - ANIMATABLE_EXTEND: 'AnimatableExtend', + TOGGLE: 'Toggle', + BUILDER_PARAM: 'BuilderParam', + COMPONENT_V1: 'Component', + COMPONENT_V2: 'ComponentV2', + COMPUTED: 'Computed', + CONSUME: 'Consume', + CONSUMER: 'Consumer', + CUSTOM_DIALOG: 'CustomDialog', + ENTRY: 'Entry', + EVENT: 'Event', + PREVIEW: 'Preview', + STATE: 'State', + PARAM: 'Param', + PROP: 'Prop', + PROVIDE: 'Provide', + PROVIDER: 'Provider', + LINK: 'Link', + LOCAL: 'Local', + OBJECT_LINK: 'ObjectLink', + STORAGE_PROP: 'StorageProp', + STORAGE_LINK: 'StorageLink', + LOCAL_STORAGE_PROP: 'LocalStorageProp', + LOCAL_STORAGE_LINK: 'LocalStorageLink', + REQUIRE: 'Require', + REUSABLE_V1: 'Reusable', + REUSABLE_V2: 'ReusableV2', + OBSERVED_V1: 'Observed', + OBSERVED_V2: 'ObservedV2', + TYPE: 'Type', + WATCH: 'Watch', + BUILDER: 'Builder', + TRACK: 'Track', + TRACE: 'Trace', + ONCE: 'Once', + MONITOR: 'Monitor', + LOCAL_BUILDER: 'LocalBuilder', + REGULAR: 'regular', + VARIABLE: 'variable', + PARAMETER: 'parameter', + ANIMATABLE_EXTEND: 'AnimatableExtend', }; export const TOGGLE_TYPE: string = 'ToggleType'; @@ -108,8 +125,8 @@ export const TYPE: string = 'type'; export const WRAP_BUILDER: string = 'wrapBuilder'; export const ToggleType = { - CHECKBOX: 'Checkbox', - BUTTON: 'Button' + CHECKBOX: 'Checkbox', + BUTTON: 'Button', }; const PUBLIC_PROPERTY_MODIFIERS: Number = 4; @@ -117,247 +134,231 @@ const PROTECTED_PROPERTY_MODIFIERS: Number = 8; const PRIVATE_PROPERTY_MODIFIERS: Number = 16; export const ReuseConstants = { - REUSE: 'reuse', - REUSE_ID: 'reuseId', + REUSE: 'reuse', + REUSE_ID: 'reuseId', }; -const OPTIONAL_MASK = 1 << 7; - export function isClassPropertyOptional(node: arkts.ClassProperty): boolean { - if ((node.modifiers & OPTIONAL_MASK) !== 0) { - return true; - } else { - return false; - } + return arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_OPTIONAL); } export function getIdentifierName(node: arkts.AstNode): string { - if (!arkts.isIdentifier(node)) { - throw new Error(`Except a Identifier type!`); - } - return node.name; + if (!arkts.isIdentifier(node)) { + throw new Error(`Except a Identifier type!`); + } + return node.name; } export function getAnnotationName(annotation: arkts.AnnotationUsage): string { - if (!annotation.expr) { - throw new Error(`The expr property does not exist!`); - } - return getIdentifierName(annotation.expr); + if (!annotation.expr) { + throw new Error(`The expr property does not exist!`); + } + return getIdentifierName(annotation.expr); } export function getAnnotationUsage( - declaration: arkts.StructDeclaration, - annotationName: string, + declaration: arkts.StructDeclaration, + annotationName: string ): arkts.AnnotationUsage | undefined { - return declaration.definition.annotations.find( - (annotation) => - annotation.expr && - arkts.isIdentifier(annotation.expr) && - annotation.expr.name === annotationName, - ); + return declaration.definition.annotations.find( + (annotation) => + annotation.expr && arkts.isIdentifier(annotation.expr) && annotation.expr.name === annotationName + ); } export function getClassAnnotationUsage( - declaration: arkts.ClassDeclaration, - annotationName: string, + declaration: arkts.ClassDeclaration, + annotationName: string ): arkts.AnnotationUsage | undefined { - if (!declaration.definition || !declaration.definition.annotations) { - return undefined; - } - return declaration.definition.annotations.find( - (annotation) => - annotation.expr && - ((arkts.isIdentifier(annotation.expr) && annotation.expr.name === annotationName) || - (arkts.isCallExpression(annotation.expr) && - arkts.isIdentifier(annotation.expr) && - annotation.expr.name === annotationName)) - ); + if (!declaration.definition || !declaration.definition.annotations) { + return undefined; + } + return declaration.definition.annotations.find( + (annotation) => + annotation.expr && + ((arkts.isIdentifier(annotation.expr) && annotation.expr.name === annotationName) || + (arkts.isCallExpression(annotation.expr) && + arkts.isIdentifier(annotation.expr) && + annotation.expr.name === annotationName)) + ); } - - export function getClassPropertyName(property: arkts.ClassProperty): string | undefined { - if (!property.key) { - return undefined; - } - return getIdentifierName(property.key); + if (!property.key) { + return undefined; + } + return getIdentifierName(property.key); } export function getClassPropertyType(property: arkts.ClassProperty): string | undefined { - return property.typeAnnotation?.dumpSrc(); + return property.typeAnnotation?.dumpSrc(); } -export function getClassPropertyAnnotationNames( - property: arkts.ClassProperty, -): string[] { - return property.annotations.map((annotation) => - getAnnotationName(annotation), - ); +export function getClassPropertyAnnotationNames(property: arkts.ClassProperty): string[] { + return property.annotations.map((annotation) => getAnnotationName(annotation)); } export function isPublicClassProperty(property: arkts.ClassProperty): boolean { - // todo 使用接口实现 - return property.modifiers === PUBLIC_PROPERTY_MODIFIERS; + return arkts.hasModifierFlag(property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC); } export function isPrivateClassProperty(property: arkts.ClassProperty): boolean { - // todo 使用接口实现 - return property.modifiers === PRIVATE_PROPERTY_MODIFIERS; + return arkts.hasModifierFlag(property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE); } -export function isProtectedlassProperty(property: arkts.ClassProperty): boolean { - // todo 使用接口实现 - return property.modifiers === PROTECTED_PROPERTY_MODIFIERS; +export function isProtectedClassProperty(property: arkts.ClassProperty): boolean { + return arkts.hasModifierFlag(property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PROTECTED); } export function listToString(strList: string[]): string { - return strList.length > 1 ? `${strList.slice(0, -1).join(', ')} and ${strList.slice(-1)}` : strList.join(''); + return strList.length > 1 ? `${strList.slice(0, -1).join(', ')} and ${strList.slice(-1)}` : strList.join(''); } export class MultiMap { - private readonly map: Map; - constructor() { - this.map = new Map(); - } - /** - * Add key-value pairs to MultiMap - * @param key key - * @param value value - */ - add(key: K, value: V): void { - if (!this.map.has(key)) { - this.map.set(key, []); + private readonly map: Map; + constructor() { + this.map = new Map(); + } + /** + * Add key-value pairs to MultiMap + * @param key key + * @param value value + */ + add(key: K, value: V): void { + if (!this.map.has(key)) { + this.map.set(key, []); + } + this.map.get(key)!.push(value); + } + + /** + * Gets all the values of the specified key + * @param key key + * @returns An array of values, which returns an empty array if the key does not exist + */ + get(key: K): V[] { + return this.map.get(key) || []; + } + + /** + * Check if the specified key exists in the MultiMap + * @param key key + * @returns Whether it exists + */ + has(key: K): boolean { + return this.map.has(key); } - this.map.get(key)!.push(value); - } - - /** - * Gets all the values of the specified key - * @param key key - * @returns An array of values, which returns an empty array if the key does not exist - */ - get(key: K): V[] { - return this.map.get(key) || []; - } - - /** - * Check if the specified key exists in the MultiMap - * @param key key - * @returns Whether it exists - */ - has(key: K): boolean { - return this.map.has(key); - } } export function hasAnnotation(annoArray: readonly arkts.AnnotationUsage[], annotationName: string): boolean { - return (annoArray || []).some(anno => - anno.expr && getIdentifierName(anno.expr) === annotationName - ); + return (annoArray || []).some((anno) => anno.expr && getIdentifierName(anno.expr) === annotationName); } interface ComponentJson { - name: string; - atomic?: boolean; - attrs: string[]; - single?: boolean; - parents?: string[]; - children?: string[]; + name: string; + atomic?: boolean; + attrs: string[]; + single?: boolean; + parents?: string[]; + children?: string[]; } export interface UISyntaxRuleComponents { - builtInAttributes: string[]; - containerComponents: string[]; - atomicComponents: string[]; - singleChildComponents: string[]; - validParentComponent: Map; - validChildComponent: Map; + 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[] = []; - let containerComponents: string[] = []; - let atomicComponents: string[] = []; - let singleChildComponents: string[] = []; - let validParentComponent: Map = new Map(); - let validChildComponent: Map = new Map(); - - if (!fs.existsSync(absolutePath)) { - throw new Error(`Directory does not exist: ${absolutePath}`); - } - // Read all files in the directory - const files = fs.readdirSync(absolutePath); - - files.forEach(file => { - if (path.extname(file) === '.json') { - const filePath = path.join(absolutePath, file); - const fileContent = fs.readFileSync(filePath, 'utf-8'); - const componentJson: ComponentJson = JSON.parse(fileContent); - // Record the container component name - if ((!componentJson.atomic || componentJson.atomic !== true) && (componentJson.name)) { - containerComponents.push(componentJson.name); - } - // Record the atomic component name - if (componentJson.atomic && componentJson.atomic === true && componentJson.name) { - atomicComponents.push(componentJson.name); - } - // Record the name of a single subcomponent component name - if (componentJson.single && componentJson.single === true && componentJson.name) { - singleChildComponents.push(componentJson.name); - } - // Record a valid parent component name - if (componentJson.parents && componentJson.name) { - validParentComponent.set(componentJson.name, componentJson.parents); - } - // Record a valid children component name - if (componentJson.children && componentJson.name) { - validChildComponent.set(componentJson.name, componentJson.children); - } - // Document all built-in attributes - componentJson.attrs?.filter(attr => !builtInAttributes.includes(attr)) - .forEach(attr => builtInAttributes.push(attr)); + const absolutePath = path.resolve(__dirname, dirPath); + let builtInAttributes: string[] = []; + let containerComponents: string[] = []; + let atomicComponents: string[] = []; + let singleChildComponents: string[] = []; + let validParentComponent: Map = new Map(); + let validChildComponent: Map = new Map(); + + if (!fs.existsSync(absolutePath)) { + throw new Error(`Directory does not exist: ${absolutePath}`); } - }); - const componentsInfo: UISyntaxRuleComponents = { - builtInAttributes, - containerComponents, - atomicComponents, - singleChildComponents, - validParentComponent, - validChildComponent, - }; - - return componentsInfo; + // Read all files in the directory + const files = fs.readdirSync(absolutePath); + + files.forEach((file) => { + if (path.extname(file) === '.json') { + const filePath = path.join(absolutePath, file); + const fileContent = fs.readFileSync(filePath, 'utf-8'); + const componentJson: ComponentJson = JSON.parse(fileContent); + // Record the container component name + if ((!componentJson.atomic || componentJson.atomic !== true) && componentJson.name) { + containerComponents.push(componentJson.name); + } + // Record the atomic component name + if (componentJson.atomic && componentJson.atomic === true && componentJson.name) { + atomicComponents.push(componentJson.name); + } + // Record the name of a single subcomponent component name + if (componentJson.single && componentJson.single === true && componentJson.name) { + singleChildComponents.push(componentJson.name); + } + // Record a valid parent component name + if (componentJson.parents && componentJson.name) { + validParentComponent.set(componentJson.name, componentJson.parents); + } + // Record a valid children component name + if (componentJson.children && componentJson.name) { + validChildComponent.set(componentJson.name, componentJson.children); + } + // Document all built-in attributes + componentJson.attrs + ?.filter((attr) => !builtInAttributes.includes(attr)) + .forEach((attr) => builtInAttributes.push(attr)); + } + }); + const componentsInfo: UISyntaxRuleComponents = { + builtInAttributes, + containerComponents, + atomicComponents, + singleChildComponents, + validParentComponent, + validChildComponent, + }; + + return componentsInfo; } export function isBuiltInAttribute(context: UISyntaxRuleContext, attributeName: string): boolean { - return context.componentsInfo.builtInAttributes.includes(attributeName); + return context.componentsInfo.builtInAttributes.includes(attributeName); } export function isBuildInComponent(context: UISyntaxRuleContext, componentName: string): boolean { - return context.componentsInfo.containerComponents.includes(componentName) || - context.componentsInfo.atomicComponents.includes(componentName); + return ( + context.componentsInfo.containerComponents.includes(componentName) || + context.componentsInfo.atomicComponents.includes(componentName) + ); } export function isAtomicComponent(context: UISyntaxRuleContext, componentName: string): boolean { - return context.componentsInfo.atomicComponents.includes(componentName); + return context.componentsInfo.atomicComponents.includes(componentName); } export function isContainerComponent(context: UISyntaxRuleContext, componentName: string): boolean { - return context.componentsInfo.containerComponents.includes(componentName); + return context.componentsInfo.containerComponents.includes(componentName); } export function isSingleChildComponent(context: UISyntaxRuleContext, componentName: string): boolean { - return context.componentsInfo.singleChildComponents.includes(componentName); + 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 + 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; +} -- Gitee