1/* 2 * Copyright (c) 2021 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 ts from 'typescript'; 17import path from 'path'; 18 19import { 20 INNER_COMPONENT_DECORATORS, 21 COMPONENT_DECORATOR_ENTRY, 22 COMPONENT_DECORATOR_PREVIEW, 23 COMPONENT_DECORATOR_COMPONENT, 24 COMPONENT_DECORATOR_CUSTOM_DIALOG, 25 NATIVE_MODULE, 26 SYSTEM_PLUGIN, 27 OHOS_PLUGIN, 28 INNER_COMPONENT_MEMBER_DECORATORS, 29 COMPONENT_FOREACH, 30 COMPONENT_LAZYFOREACH, 31 COMPONENT_STATE_DECORATOR, 32 COMPONENT_LINK_DECORATOR, 33 COMPONENT_PROP_DECORATOR, 34 COMPONENT_STORAGE_PROP_DECORATOR, 35 COMPONENT_STORAGE_LINK_DECORATOR, 36 COMPONENT_PROVIDE_DECORATOR, 37 COMPONENT_CONSUME_DECORATOR, 38 COMPONENT_OBJECT_LINK_DECORATOR, 39 COMPONENT_OBSERVED_DECORATOR, 40 COMPONENT_LOCAL_STORAGE_LINK_DECORATOR, 41 COMPONENT_LOCAL_STORAGE_PROP_DECORATOR, 42 COMPONENT_BUILDER_DECORATOR, 43 COMPONENT_CONCURRENT_DECORATOR, 44 CHECK_EXTEND_DECORATORS, 45 COMPONENT_STYLES_DECORATOR, 46 RESOURCE_NAME_TYPE, 47 COMPONENT_BUTTON, 48 COMPONENT_TOGGLE, 49 COMPONENT_BUILDERPARAM_DECORATOR, 50 ESMODULE, 51 CARD_ENABLE_DECORATORS, 52 CARD_LOG_TYPE_DECORATORS, 53 JSBUNDLE, 54 COMPONENT_DECORATOR_REUSEABLE, 55 STRUCT_DECORATORS, 56 STRUCT_CONTEXT_METHOD_DECORATORS, 57 CHECK_COMPONENT_EXTEND_DECORATOR, 58 CHECK_COMPONENT_ANIMATABLE_EXTEND_DECORATOR, 59 CLASS_TRACK_DECORATOR, 60 COMPONENT_REQUIRE_DECORATOR, 61 COMPONENT_SENDABLE_DECORATOR, 62 CLASS_MIN_TRACK_DECORATOR, 63 MIN_OBSERVED, 64 COMPONENT_NON_DECORATOR, 65 COMPONENT_DECORATOR_COMPONENT_V2, 66 OBSERVED 67} from './pre_define'; 68import { 69 INNER_COMPONENT_NAMES, 70 AUTOMIC_COMPONENT, 71 SINGLE_CHILD_COMPONENT, 72 SPECIFIC_CHILD_COMPONENT, 73 BUILDIN_STYLE_NAMES, 74 EXTEND_ATTRIBUTE, 75 GLOBAL_STYLE_FUNCTION, 76 STYLES_ATTRIBUTE, 77 CUSTOM_BUILDER_METHOD, 78 GLOBAL_CUSTOM_BUILDER_METHOD, 79 INNER_CUSTOM_BUILDER_METHOD, 80 INNER_STYLE_FUNCTION 81} from './component_map'; 82import { 83 LogType, 84 LogInfo, 85 componentInfo, 86 addLog, 87 hasDecorator, 88 storedFileInfo, 89 ExtendResult 90} from './utils'; 91import { globalProgram, projectConfig, abilityPagesFullPath } from '../main'; 92import { 93 collectExtend, 94 isExtendFunction, 95 transformLog, 96 validatorCard 97} from './process_ui_syntax'; 98import { stateObjectCollection } from './process_component_member'; 99import { collectSharedModule } from './fast_build/ark_compiler/check_shared_module'; 100import constantDefine from './constant_define'; 101import processStructComponentV2, { StructInfo } from './process_struct_componentV2'; 102 103export class ComponentCollection { 104 localStorageName: string = null; 105 localStorageNode: ts.Identifier | ts.ObjectLiteralExpression = null; 106 localSharedStorage: ts.Node = null; 107 entryComponentPos: number = null; 108 entryComponent: string = null; 109 previewComponent: Array<string> = []; 110 customDialogs: Set<string> = new Set([]); 111 customComponents: Set<string> = new Set([]); 112 currentClassName: string = null; 113} 114 115export class IComponentSet { 116 properties: Set<string> = new Set(); 117 regulars: Set<string> = new Set(); 118 states: Set<string> = new Set(); 119 links: Set<string> = new Set(); 120 props: Set<string> = new Set(); 121 storageProps: Set<string> = new Set(); 122 storageLinks: Set<string> = new Set(); 123 provides: Set<string> = new Set(); 124 consumes: Set<string> = new Set(); 125 objectLinks: Set<string> = new Set(); 126 localStorageLink: Map<string, Set<string>> = new Map(); 127 localStorageProp: Map<string, Set<string>> = new Map(); 128 builderParams: Set<string> = new Set(); 129 builderParamData: Set<string> = new Set(); 130 propData: Set<string> = new Set(); 131 regularInit: Set<string> = new Set(); 132 stateInit: Set<string> = new Set(); 133 provideInit: Set<string> = new Set(); 134 privateCollection: Set<string> = new Set(); 135} 136 137export let componentCollection: ComponentCollection = new ComponentCollection(); 138 139export const observedClassCollection: Set<string> = new Set(); 140export const enumCollection: Set<string> = new Set(); 141export const classMethodCollection: Map<string, Map<string, Set<string>>> = new Map(); 142export const dollarCollection: Set<string> = new Set(); 143 144export const propertyCollection: Map<string, Set<string>> = new Map(); 145export const stateCollection: Map<string, Set<string>> = new Map(); 146export const linkCollection: Map<string, Set<string>> = new Map(); 147export const propCollection: Map<string, Set<string>> = new Map(); 148export const regularCollection: Map<string, Set<string>> = new Map(); 149export const storagePropCollection: Map<string, Set<string>> = new Map(); 150export const storageLinkCollection: Map<string, Set<string>> = new Map(); 151export const provideCollection: Map<string, Set<string>> = new Map(); 152export const consumeCollection: Map<string, Set<string>> = new Map(); 153export const objectLinkCollection: Map<string, Set<string>> = new Map(); 154export const builderParamObjectCollection: Map<string, Set<string>> = new Map(); 155export const localStorageLinkCollection: Map<string, Map<string, Set<string>>> = new Map(); 156export const localStoragePropCollection: Map<string, Map<string, Set<string>>> = new Map(); 157export const builderParamInitialization: Map<string, Set<string>> = new Map(); 158export const propInitialization: Map<string, Set<string>> = new Map(); 159export const regularInitialization: Map<string, Set<string>> = new Map(); 160export const stateInitialization: Map<string, Set<string>> = new Map(); 161export const provideInitialization: Map<string, Set<string>> = new Map(); 162export const privateCollection: Map<string, Set<string>> = new Map(); 163 164export const isStaticViewCollection: Map<string, boolean> = new Map(); 165 166export const useOSFiles: Set<string> = new Set(); 167export const sourcemapNamesCollection: Map<string, Map<string, string>> = new Map(); 168export const originalImportNamesMap: Map<string, string> = new Map(); 169 170export function validateUISyntax(source: string, content: string, filePath: string, 171 fileQuery: string, sourceFile: ts.SourceFile = null): LogInfo[] { 172 let log: LogInfo[] = []; 173 if (process.env.compileMode === 'moduleJson' || 174 path.resolve(filePath) !== path.resolve(projectConfig.projectPath || '', 'app.ets')) { 175 componentCollection = new ComponentCollection(); 176 const res: LogInfo[] = checkComponentDecorator(source, filePath, fileQuery, sourceFile); 177 if (res) { 178 log = log.concat(res); 179 } 180 const allComponentNames: Set<string> = 181 new Set([...INNER_COMPONENT_NAMES, ...componentCollection.customComponents]); 182 checkUISyntax(filePath, allComponentNames, content, log, sourceFile, fileQuery); 183 componentCollection.customComponents.forEach(item => componentInfo.componentNames.add(item)); 184 } 185 186 if (projectConfig.compileMode === ESMODULE) { 187 collectSharedModule(source, filePath, sourceFile); 188 } 189 190 return log; 191} 192 193function checkComponentDecorator(source: string, filePath: string, 194 fileQuery: string, sourceFile: ts.SourceFile | null): LogInfo[] | null { 195 const log: LogInfo[] = []; 196 if (!sourceFile) { 197 sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS); 198 } 199 if (sourceFile && sourceFile.statements && sourceFile.statements.length) { 200 const result: DecoratorResult = { 201 entryCount: 0, 202 previewCount: 0 203 }; 204 sourceFile.statements.forEach((item, index, arr) => { 205 if (isObservedClass(item)) { 206 // @ts-ignore 207 observedClassCollection.add(item.name.getText()); 208 } 209 if (ts.isEnumDeclaration(item) && item.name) { 210 enumCollection.add(item.name.getText()); 211 } 212 if (ts.isStructDeclaration(item)) { 213 validateStructSpec(item, result, log, sourceFile); 214 } 215 if (ts.isMissingDeclaration(item)) { 216 const decorators = ts.getAllDecorators(item); 217 for (let i = 0; i < decorators.length; i++) { 218 if (decorators[i] && /struct/.test(decorators[i].getText())) { 219 const message: string = `Please use a valid decorator.`; 220 addLog(LogType.ERROR, message, item.getStart(), log, sourceFile); 221 break; 222 } 223 } 224 } 225 }); 226 if (process.env.compileTool === 'rollup') { 227 if (result.entryCount > 0) { 228 storedFileInfo.wholeFileInfo[filePath].hasEntry = true; 229 } else { 230 storedFileInfo.wholeFileInfo[filePath].hasEntry = false; 231 } 232 } 233 validateEntryAndPreviewCount(result, fileQuery, sourceFile.fileName, projectConfig.isPreview, 234 !!projectConfig.checkEntry, log); 235 } 236 237 return log.length ? log : null; 238} 239 240function validateStructSpec(item: ts.StructDeclaration, result: DecoratorResult, log: LogInfo[], 241 sourceFile: ts.SourceFile | null): void { 242 if (item.name && ts.isIdentifier(item.name)) { 243 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(item); 244 if (decorators && decorators.length) { 245 checkDecorators(decorators, result, item.name, log, sourceFile, item); 246 } else { 247 const message: string = `A struct should use decorator '@Component' or '@ComponentV2'.`; 248 addLog(LogType.WARN, message, item.getStart(), log, sourceFile); 249 } 250 } else { 251 const message: string = `A struct must have a name.`; 252 addLog(LogType.ERROR, message, item.getStart(), log, sourceFile); 253 } 254} 255 256function validateEntryAndPreviewCount(result: DecoratorResult, fileQuery: string, 257 fileName: string, isPreview: boolean, checkEntry: boolean, log: LogInfo[]): void { 258 if (result.previewCount > 10 && (fileQuery === '?entry' || process.env.watchMode === 'true')) { 259 log.push({ 260 type: LogType.ERROR, 261 message: `A page can contain at most 10 '@Preview' decorators.`, 262 fileName: fileName 263 }); 264 } 265 if (result.entryCount > 1 && fileQuery === '?entry') { 266 log.push({ 267 type: LogType.ERROR, 268 message: `A page can't contain more than one '@Entry' decorator`, 269 fileName: fileName 270 }); 271 } 272 if (isPreview && !checkEntry && result.previewCount < 1 && result.entryCount !== 1 && 273 fileQuery === '?entry') { 274 log.push({ 275 type: LogType.ERROR, 276 message: `A page which is being previewed must have one and only one '@Entry' ` + 277 `decorator, or at least one '@Preview' decorator.`, 278 fileName: fileName 279 }); 280 } else if ((!isPreview || isPreview && checkEntry) && result.entryCount !== 1 && fileQuery === '?entry' && 281 !abilityPagesFullPath.includes(path.resolve(fileName).toLowerCase())) { 282 log.push({ 283 type: LogType.ERROR, 284 message: `A page configured in '${projectConfig.pagesJsonFileName} or build-profile.json5' must have one and only one '@Entry' decorator.` + 285 `Solutions:>Please make sure that the splash page has one and only one '@Entry' decorator.`, 286 fileName: fileName 287 }); 288 } 289} 290 291export function isObservedClass(node: ts.Node): boolean { 292 if (ts.isClassDeclaration(node) && hasDecorator(node, COMPONENT_OBSERVED_DECORATOR)) { 293 return true; 294 } 295 return false; 296} 297 298export function isCustomDialogClass(node: ts.Node): boolean { 299 if (ts.isStructDeclaration(node) && hasDecorator(node, COMPONENT_DECORATOR_CUSTOM_DIALOG)) { 300 return true; 301 } 302 return false; 303} 304 305interface DecoratorResult { 306 entryCount: number; 307 previewCount: number; 308} 309 310function checkDecorators(decorators: readonly ts.Decorator[], result: DecoratorResult, 311 component: ts.Identifier, log: LogInfo[], sourceFile: ts.SourceFile, node: ts.StructDeclaration): void { 312 let hasComponentDecorator: boolean = false; 313 const componentName: string = component.getText(); 314 const structInfo: StructInfo = processStructComponentV2.getOrCreateStructInfo(componentName); 315 decorators.forEach((element) => { 316 let name: string = element.getText().replace(/\([^\(\)]*\)/, '').trim(); 317 if (element.expression && element.expression.expression && ts.isIdentifier(element.expression.expression)) { 318 name = '@' + element.expression.expression.getText(); 319 } 320 if (INNER_COMPONENT_DECORATORS.has(name)) { 321 componentCollection.customComponents.add(componentName); 322 switch (name) { 323 case COMPONENT_DECORATOR_ENTRY: 324 checkEntryComponent(node, log, sourceFile); 325 result.entryCount++; 326 componentCollection.entryComponent = componentName; 327 componentCollection.entryComponentPos = node.getStart(); 328 collectLocalStorageName(element); 329 break; 330 case COMPONENT_DECORATOR_PREVIEW: 331 result.previewCount++; 332 componentCollection.previewComponent.push(componentName); 333 break; 334 case COMPONENT_DECORATOR_COMPONENT_V2: 335 hasComponentDecorator = true; 336 structInfo.isComponentV2 = true; 337 break; 338 case COMPONENT_DECORATOR_COMPONENT: 339 hasComponentDecorator = true; 340 structInfo.isComponentV1 = true; 341 break; 342 case COMPONENT_DECORATOR_CUSTOM_DIALOG: 343 componentCollection.customDialogs.add(componentName); 344 hasComponentDecorator = true; 345 structInfo.isCustomDialog = true; 346 break; 347 case COMPONENT_DECORATOR_REUSEABLE: 348 storedFileInfo.getCurrentArkTsFile().recycleComponents.add(componentName); 349 hasComponentDecorator = true; 350 structInfo.isReusable = true; 351 break; 352 } 353 } else { 354 validateInvalidStructDecorator(element, componentName, log, sourceFile); 355 } 356 }); 357 validateStruct(hasComponentDecorator, componentName, component, log, sourceFile, structInfo); 358} 359 360function validateInvalidStructDecorator(element: ts.Decorator, componentName: string, log: LogInfo[], 361 sourceFile: ts.SourceFile): void { 362 const pos: number = element.expression ? element.expression.pos : element.pos; 363 const message: string = `The struct '${componentName}' use invalid decorator.`; 364 addLog(LogType.WARN, message, pos, log, sourceFile); 365} 366 367function validateStruct(hasComponentDecorator: boolean, componentName: string, component: ts.Identifier, 368 log: LogInfo[], sourceFile: ts.SourceFile, structInfo: StructInfo): void { 369 if (!hasComponentDecorator) { 370 const message: string = `The struct '${componentName}' should use decorator '@Component'.`; 371 addLog(LogType.WARN, message, component.pos, log, sourceFile); 372 } else if (structInfo.isComponentV2 && (structInfo.isComponentV1 || structInfo.isReusable || structInfo.isCustomDialog) ) { 373 const message: string = `The struct '${componentName}' can not be decorated with '@ComponentV2' ` + 374 `and '@Component', '@Reusable', '@CustomDialog' at the same time.`; 375 addLog(LogType.ERROR, message, component.pos, log, sourceFile); 376 } 377 if (BUILDIN_STYLE_NAMES.has(componentName)) { 378 const message: string = `The struct '${componentName}' cannot have the same name ` + 379 `as the built-in attribute '${componentName}'.`; 380 addLog(LogType.ERROR, message, component.pos, log, sourceFile); 381 } 382 if (INNER_COMPONENT_NAMES.has(componentName)) { 383 const message: string = `The struct '${componentName}' cannot have the same name ` + 384 `as the built-in component '${componentName}'.`; 385 addLog(LogType.ERROR, message, component.pos, log, sourceFile); 386 } 387} 388 389function checkConcurrentDecorator(node: ts.FunctionDeclaration | ts.MethodDeclaration, log: LogInfo[], 390 sourceFile: ts.SourceFile): void { 391 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node); 392 if (projectConfig.compileMode === JSBUNDLE) { 393 const message: string = `@Concurrent can only be used in ESMODULE compile mode.`; 394 addLog(LogType.ERROR, message, decorators![0].pos, log, sourceFile); 395 } 396 if (ts.isMethodDeclaration(node)) { 397 const message: string = `@Concurrent can not be used on method. please use it on function declaration.`; 398 addLog(LogType.ERROR, message, decorators![0].pos, log, sourceFile); 399 } 400 if (node.asteriskToken) { 401 let hasAsync: boolean = false; 402 const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined; 403 const checkAsyncModifier = (modifier: ts.Modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword; 404 modifiers && (hasAsync = modifiers.some(checkAsyncModifier)); 405 const funcKind: string = hasAsync ? 'Async generator' : 'Generator'; 406 const message: string = `@Concurrent can not be used on ${funcKind} function declaration.`; 407 addLog(LogType.ERROR, message, decorators![0].pos, log, sourceFile); 408 } 409} 410 411function collectLocalStorageName(node: ts.Decorator): void { 412 if (node && node.expression && ts.isCallExpression(node.expression)) { 413 if (node.expression.arguments && node.expression.arguments.length) { 414 node.expression.arguments.forEach((item: ts.Node, index: number) => { 415 if (ts.isIdentifier(item) && index === 0) { 416 componentCollection.localStorageName = item.getText(); 417 componentCollection.localStorageNode = item; 418 } else if (ts.isObjectLiteralExpression(item) && index === 0) { 419 componentCollection.localStorageName = null; 420 componentCollection.localStorageNode = item; 421 } else { 422 componentCollection.localSharedStorage = item; 423 } 424 }); 425 } 426 } else { 427 componentCollection.localStorageName = null; 428 componentCollection.localStorageNode = null; 429 } 430} 431 432function checkUISyntax(filePath: string, allComponentNames: Set<string>, content: string, 433 log: LogInfo[], sourceFile: ts.SourceFile | null, fileQuery: string): void { 434 if (!sourceFile) { 435 sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS); 436 } 437 visitAllNode(sourceFile, sourceFile, allComponentNames, log, false, false, false, false, fileQuery); 438} 439 440function propertyInitializeInEntry(fileQuery: string, name: string): boolean { 441 return fileQuery === '?entry' && name === componentCollection.entryComponent; 442} 443 444function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponentNames: Set<string>, 445 log: LogInfo[], structContext: boolean, classContext: boolean, isObservedClass: boolean, 446 isComponentV2: boolean, fileQuery: string): void { 447 if (ts.isStructDeclaration(node) && node.name && ts.isIdentifier(node.name)) { 448 structContext = true; 449 const structName: string = node.name.escapedText.toString(); 450 const structInfo: StructInfo = processStructComponentV2.getOrCreateStructInfo(structName); 451 if (structInfo.isComponentV2) { 452 processStructComponentV2.parseComponentProperty(node, structInfo, log, sourceFileNode); 453 isComponentV2 = true; 454 } else { 455 collectComponentProps(node, propertyInitializeInEntry(fileQuery, structName), structInfo); 456 } 457 } 458 if (ts.isClassDeclaration(node) && node.name && ts.isIdentifier(node.name)) { 459 classContext = true; 460 isObservedClass = parseClassDecorator(node, sourceFileNode, log); 461 } 462 if (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node)) { 463 methodDecoratorCollect(node); 464 if (hasDecorator(node, COMPONENT_CONCURRENT_DECORATOR)) { 465 // ark compiler's feature 466 checkConcurrentDecorator(node, log, sourceFileNode); 467 } 468 } 469 checkDecoratorCount(node, sourceFileNode, log); 470 checkDecorator(sourceFileNode, node, log, structContext, classContext, isObservedClass, isComponentV2); 471 node.getChildren().forEach((item: ts.Node) => visitAllNode(item, sourceFileNode, allComponentNames, 472 log, structContext, classContext, isObservedClass, isComponentV2, fileQuery)); 473 structContext = false; 474 classContext = false; 475 isObservedClass = false; 476} 477 478function checkDecoratorCount(node: ts.Node, sourceFileNode: ts.SourceFile, log: LogInfo[]): void { 479 if (ts.isPropertyDeclaration(node) || ts.isGetAccessor(node) || ts.isMethodDeclaration(node)) { 480 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node); 481 let innerDecoratorCount: number = 0; 482 const exludeDecorators: string[] = ['@Require', '@Once']; 483 decorators.forEach((item: ts.Decorator) => { 484 const decoratorName: string = item.getText().replace(/\([^\(\)]*\)/, ''); 485 if (!exludeDecorators.includes(decoratorName) && (constantDefine.DECORATOR_V2.includes(decoratorName) || 486 decoratorName === '@BuilderParam')) { 487 innerDecoratorCount++; 488 } 489 }); 490 if (innerDecoratorCount > 1) { 491 const message: string = 'The member property or method can not be decorated by multiple built-in decorators.'; 492 addLog(LogType.ERROR, message, node.getStart(), log, sourceFileNode); 493 } 494 } 495} 496 497function methodDecoratorCollect(node: ts.MethodDeclaration | ts.FunctionDeclaration): void { 498 const extendResult: ExtendResult = { decoratorName: '', componentName: '' }; 499 if (hasDecorator(node, COMPONENT_BUILDER_DECORATOR)) { 500 CUSTOM_BUILDER_METHOD.add(node.name.getText()); 501 if (ts.isFunctionDeclaration(node)) { 502 GLOBAL_CUSTOM_BUILDER_METHOD.add(node.name.getText()); 503 } else { 504 INNER_CUSTOM_BUILDER_METHOD.add(node.name.getText()); 505 } 506 } else if (ts.isFunctionDeclaration(node) && isExtendFunction(node, extendResult)) { 507 if (extendResult.decoratorName === CHECK_COMPONENT_EXTEND_DECORATOR) { 508 collectExtend(EXTEND_ATTRIBUTE, extendResult.componentName, node.name.getText()); 509 } 510 if (extendResult.decoratorName === CHECK_COMPONENT_ANIMATABLE_EXTEND_DECORATOR) { 511 collectExtend(storedFileInfo.getCurrentArkTsFile().animatableExtendAttribute, 512 extendResult.componentName, node.name.getText()); 513 } 514 } else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) { 515 collectStyles(node); 516 } 517} 518 519function collectStyles(node: ts.FunctionLikeDeclarationBase): void { 520 if (ts.isBlock(node.body) && node.body.statements) { 521 if (ts.isFunctionDeclaration(node)) { 522 GLOBAL_STYLE_FUNCTION.set(node.name.getText(), node.body); 523 } else { 524 INNER_STYLE_FUNCTION.set(node.name.getText(), node.body); 525 } 526 STYLES_ATTRIBUTE.add(node.name.getText()); 527 BUILDIN_STYLE_NAMES.add(node.name.getText()); 528 } 529} 530 531function checkDecorator(sourceFileNode: ts.SourceFile, node: ts.Node, 532 log: LogInfo[], structContext: boolean, classContext: boolean, isObservedClass: boolean, 533 isComponentV2: boolean): void { 534 if (ts.isIdentifier(node) && (ts.isDecorator(node.parent) || 535 (ts.isCallExpression(node.parent) && ts.isDecorator(node.parent.parent)))) { 536 const decoratorName: string = node.escapedText.toString(); 537 validateStructDecorator(sourceFileNode, node, log, structContext, decoratorName, isComponentV2); 538 validateMethodDecorator(sourceFileNode, node, log, structContext, decoratorName); 539 validateClassDecorator(sourceFileNode, node, log, classContext, decoratorName, isObservedClass); 540 return; 541 } 542 if (ts.isDecorator(node)) { 543 validateSingleDecorator(node, sourceFileNode, log); 544 } 545} 546 547function validateSingleDecorator(node: ts.Decorator, sourceFileNode: ts.SourceFile, 548 log: LogInfo[]): void { 549 const decoratorName: string = node.getText().replace(/\([^\(\)]*\)/, ''); 550 if (decoratorName === constantDefine.COMPUTED_DECORATOR && node.parent && !ts.isGetAccessor(node.parent)) { 551 const message: string = `@Computed can only decorate 'GetAccessor'.`; 552 addLog(LogType.ERROR, message, node.getStart(), log, sourceFileNode); 553 return; 554 } 555 if (decoratorName === constantDefine.MONITOR_DECORATOR && node.parent && 556 !ts.isMethodDeclaration(node.parent)) { 557 const message: string = '@Monitor can only decorate method.'; 558 addLog(LogType.ERROR, message, node.getStart(), log, sourceFileNode); 559 return; 560 } 561 if (constantDefine.COMPONENT_MEMBER_DECORATOR_V2.includes(decoratorName) && node.parent && 562 !ts.isPropertyDeclaration(node.parent)) { 563 const message: string = `'${decoratorName}' can only decorate member property.`; 564 addLog(LogType.ERROR, message, node.getStart(), log, sourceFileNode); 565 return; 566 } 567} 568 569const classDecorators: string[] = [CLASS_TRACK_DECORATOR, CLASS_MIN_TRACK_DECORATOR, MIN_OBSERVED]; 570const classMemberDecorators: string[] = [CLASS_TRACK_DECORATOR, CLASS_MIN_TRACK_DECORATOR, 571 constantDefine.MONITOR, constantDefine.COMPUTED]; 572 573function validateClassDecorator(sourceFileNode: ts.SourceFile, node: ts.Identifier, log: LogInfo[], 574 classContext: boolean, decoratorName: string, isObservedClass: boolean): void { 575 if (!classContext && classDecorators.includes(decoratorName)) { 576 const message: string = `The '@${decoratorName}' decorator can only be used in 'class'.`; 577 addLog(LogType.ERROR, message, node.pos, log, sourceFileNode); 578 } else if ('@' + decoratorName === COMPONENT_SENDABLE_DECORATOR && 579 (!node.parent || !node.parent.parent || !ts.isClassDeclaration(node.parent.parent))) { 580 const message: string = 'The \'@Sendable\' decorator can only be added to \'class\'.'; 581 addLog(LogType.ERROR, message, node.pos, log, sourceFileNode); 582 } else if (classContext && classMemberDecorators.includes(decoratorName)) { 583 validateMemberInClass(isObservedClass, decoratorName, node, log, sourceFileNode); 584 } 585} 586 587function validateMemberInClass(isObservedClass: boolean, decoratorName: string, node: ts.Identifier, 588 log: LogInfo[], sourceFileNode: ts.SourceFile): void { 589 if (decoratorName === CLASS_TRACK_DECORATOR) { 590 if (isObservedClass) { 591 const message: string = `The '@${decoratorName}' decorator can not be used in a 'class' decorated with ObservedV2.`; 592 addLog(LogType.ERROR, message, node.pos, log, sourceFileNode); 593 } 594 return; 595 } 596 if (!isObservedClass || !isPropertyForTrace(node, decoratorName)) { 597 const info: string = decoratorName === CLASS_MIN_TRACK_DECORATOR ? 'variables' : 'method'; 598 const message: string = `The '@${decoratorName}' can decorate only member ${info} within a 'class' decorated with ObservedV2.`; 599 addLog(LogType.ERROR, message, node.pos, log, sourceFileNode); 600 return; 601 } 602} 603 604function isPropertyForTrace(node: ts.Identifier, decoratorName: string): boolean { 605 if (decoratorName === CLASS_MIN_TRACK_DECORATOR && ts.isDecorator(node.parent) && 606 !ts.isPropertyDeclaration(node.parent.parent)) { 607 return false; 608 } 609 return true; 610} 611 612class ClassDecoratorResult { 613 hasObserved: boolean = false; 614 hasObservedV2: boolean = false; 615} 616 617function parseClassDecorator(node: ts.ClassDeclaration, sourceFileNode: ts.SourceFile, 618 log: LogInfo[]): boolean { 619 const classResult: ClassDecoratorResult = getClassDecoratorResult(node); 620 validateMutilObserved(node, classResult, sourceFileNode, log); 621 if (classResult.hasObserved || classResult.hasObservedV2) { 622 parseInheritClass(node, classResult, sourceFileNode, log); 623 } 624 return classResult.hasObservedV2; 625} 626 627function getClassDecoratorResult(node: ts.ClassDeclaration): ClassDecoratorResult { 628 const classResult: ClassDecoratorResult = new ClassDecoratorResult(); 629 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node); 630 decorators.forEach((item: ts.Decorator) => { 631 if (ts.isIdentifier(item.expression)) { 632 const decoratorName: string = item.expression.escapedText.toString(); 633 switch (decoratorName) { 634 case MIN_OBSERVED: 635 classResult.hasObservedV2 = true; 636 break; 637 case OBSERVED: 638 classResult.hasObserved = true; 639 break; 640 } 641 } 642 }); 643 return classResult; 644} 645 646function validateMutilObserved(node: ts.ClassDeclaration, classResult: ClassDecoratorResult, 647 sourceFileNode: ts.SourceFile, log: LogInfo[]): void { 648 if (classResult.hasObserved && classResult.hasObservedV2) { 649 const message: string = `A class can not be decorated by '@Observed' and '@ObservedV2' at the same time.`; 650 addLog(LogType.ERROR, message, node.getStart(), log, sourceFileNode); 651 } 652} 653 654function parseInheritClass(node: ts.ClassDeclaration, childClassResult: ClassDecoratorResult, 655 sourceFileNode: ts.SourceFile, log: LogInfo[]): void { 656 if (globalProgram.checker && process.env.compileTool === 'rollup' && node.heritageClauses) { 657 for (const heritageClause of node.heritageClauses) { 658 if (heritageClause.token === ts.SyntaxKind.ExtendsKeyword && heritageClause.types && 659 heritageClause.types.length) { 660 getClassNode(heritageClause.types[0].expression, childClassResult, node, sourceFileNode, log); 661 } 662 } 663 } 664} 665 666function getClassNode(parentType: ts.Node, childClassResult: ClassDecoratorResult, 667 childClass: ts.ClassDeclaration, sourceFileNode: ts.SourceFile, log: LogInfo[]): void { 668 const symbol: ts.Symbol = parentType && getSymbolIfAliased(parentType); 669 if (symbol && symbol.valueDeclaration) { 670 if (ts.isClassDeclaration(symbol.valueDeclaration)) { 671 validateInheritClassDecorator(symbol.valueDeclaration, childClassResult, childClass, sourceFileNode, log); 672 return; 673 } 674 if (ts.isPropertyAssignment(symbol.valueDeclaration)) { 675 getClassNode(symbol.valueDeclaration.initializer, childClassResult, childClass, sourceFileNode, log); 676 return; 677 } 678 if (ts.isShorthandPropertyAssignment(symbol.valueDeclaration)) { 679 parseShorthandPropertyForClass(symbol.valueDeclaration, childClassResult, childClass, sourceFileNode, log); 680 return; 681 } 682 } 683} 684 685function parseShorthandPropertyForClass(node: ts.ShorthandPropertyAssignment, childClassResult: ClassDecoratorResult, 686 childClass: ts.ClassDeclaration, sourceFileNode: ts.SourceFile, log: LogInfo[]): void { 687 const shortSymbol: ts.Symbol = globalProgram.checker.getShorthandAssignmentValueSymbol(node); 688 if (shortSymbol && shortSymbol.valueDeclaration && ts.isClassDeclaration(shortSymbol.valueDeclaration)) { 689 validateInheritClassDecorator(shortSymbol.valueDeclaration, childClassResult, childClass, sourceFileNode, log); 690 } 691} 692 693export function getSymbolIfAliased(node: ts.Node): ts.Symbol { 694 const symbol: ts.Symbol = globalProgram.checker.getSymbolAtLocation(node); 695 if (symbol && (symbol.getFlags() & ts.SymbolFlags.Alias) !== 0) { 696 return globalProgram.checker.getAliasedSymbol(symbol); 697 } 698 return symbol; 699} 700 701function validateInheritClassDecorator(parentNode: ts.ClassDeclaration, childClassResult: ClassDecoratorResult, 702 childClass: ts.ClassDeclaration, sourceFileNode: ts.SourceFile, log: LogInfo[]): void { 703 const parentClassResult: ClassDecoratorResult = getClassDecoratorResult(parentNode); 704 if (childClassResult.hasObservedV2 && parentClassResult.hasObserved) { 705 const message: string = `Because the current class is decorated by '@ObservedV2', ` + 706 `it can not inherit a class decorated by '@Observed'.`; 707 addLog(LogType.ERROR, message, childClass.getStart(), log, sourceFileNode); 708 return; 709 } 710 if (childClassResult.hasObserved && parentClassResult.hasObservedV2) { 711 const message: string = `Because the current class is decorated by '@Observed', ` + 712 `it can not inherit a class decorated by '@ObservedV2'.`; 713 addLog(LogType.ERROR, message, childClass.getStart(), log, sourceFileNode); 714 return; 715 } 716} 717 718function validateStructDecorator(sourceFileNode: ts.SourceFile, node: ts.Identifier, log: LogInfo[], 719 structContext: boolean, decoratorName: string, isComponentV2: boolean): void { 720 const name: string = `@${decoratorName}`; 721 if (structContext) { 722 if (isComponentV2) { 723 if (constantDefine.COMPONENT_MEMBER_DECORATOR_V1.includes(name)) { 724 const message: string = `The '@${decoratorName}' decorator can only be used in a 'struct' decorated with '@Component'.`; 725 addLog(LogType.ERROR, message, node.pos, log, sourceFileNode); 726 } 727 } else if (constantDefine.DECORATOR_V2.includes(name)) { 728 const message: string = `The '@${decoratorName}' decorator can only be used in a 'struct' decorated with '@ComponentV2'.`; 729 addLog(LogType.ERROR, message, node.pos, log, sourceFileNode); 730 } 731 } else if (STRUCT_DECORATORS.has(name) || constantDefine.COMPONENT_MEMBER_DECORATOR_V2.includes(name)) { 732 const message: string = `The '@${decoratorName}' decorator can only be used with 'struct'.`; 733 addLog(LogType.ERROR, message, node.pos, log, sourceFileNode); 734 } 735} 736 737function validateMethodDecorator(sourceFileNode: ts.SourceFile, node: ts.Identifier, log: LogInfo[], 738 structContext: boolean, decoratorName: string): void { 739 let message: string; 740 if (ts.isMethodDeclaration(node.parent.parent) || 741 (ts.isDecorator(node.parent.parent) && ts.isMethodDeclaration(node.parent.parent.parent))) { 742 if (!structContext && STRUCT_CONTEXT_METHOD_DECORATORS.has(`@${decoratorName}`)) { 743 message = `The '@${decoratorName}' decorator can only be used in 'struct'.`; 744 } 745 if (CHECK_EXTEND_DECORATORS.includes(decoratorName)) { 746 message = `The '@${decoratorName}' decorator can not be a member property method of a 'class' or 'struct'.`; 747 } 748 if (message) { 749 addLog(LogType.ERROR, message, node.pos, log, sourceFileNode); 750 } 751 } 752} 753 754export function isSendableClassDeclaration(classDeclarationNode: ts.ClassDeclaration): boolean { 755 return hasDecorator(classDeclarationNode, COMPONENT_SENDABLE_DECORATOR) || 756 (classDeclarationNode.members && classDeclarationNode.members.some((member: ts.Node) => { 757 // Check if the constructor has "use sendable" as the first statement 758 // (Sendable classes already transformed by process_ui_syntax.ts) 759 if (ts.isConstructorDeclaration(member)) { 760 if (!(member as ts.ConstructorDeclaration).body || 761 !(member as ts.ConstructorDeclaration).body.statements) { 762 return false; 763 } 764 const constructorStatements: ts.Statement[] = (member as ts.ConstructorDeclaration).body.statements; 765 if (constructorStatements && constructorStatements[0] && 766 ts.isExpressionStatement(constructorStatements[0])) { 767 const expression: ts.Node = (constructorStatements[0] as ts.ExpressionStatement).expression; 768 return expression && ts.isStringLiteral(expression) && 769 (expression as ts.StringLiteral).text === 'use sendable'; 770 } 771 } 772 return false; 773 })); 774} 775 776export function checkAllNode( 777 node: ts.EtsComponentExpression, 778 allComponentNames: Set<string>, 779 sourceFileNode: ts.SourceFile, 780 log: LogInfo[] 781): void { 782 if (ts.isIdentifier(node.expression)) { 783 checkNoChildComponent(node, sourceFileNode, log); 784 checkOneChildComponent(node, allComponentNames, sourceFileNode, log); 785 checkSpecificChildComponent(node, allComponentNames, sourceFileNode, log); 786 } 787} 788 789interface ParamType { 790 name: string, 791 value: string, 792} 793 794function checkNoChildComponent(node: ts.EtsComponentExpression, sourceFileNode: ts.SourceFile, log: LogInfo[]): void { 795 const isCheckType: ParamType = { name: null, value: null}; 796 if (hasChild(node, isCheckType)) { 797 const componentName: string = (node.expression as ts.Identifier).escapedText.toString(); 798 const pos: number = node.expression.getStart(); 799 const message: string = isCheckType.name === null ? 800 `The component '${componentName}' can't have any child.` : 801 `When the component '${componentName}' set '${isCheckType.name}' is '${isCheckType.value}'` + 802 `, can't have any child.`; 803 addLog(LogType.ERROR, message, pos, log, sourceFileNode); 804 } 805} 806 807function hasChild(node: ts.EtsComponentExpression, isCheckType: ParamType): boolean { 808 const nodeName: ts.Identifier = node.expression as ts.Identifier; 809 if ((AUTOMIC_COMPONENT.has(nodeName.escapedText.toString()) || judgeComponentType(nodeName, node, isCheckType)) && 810 getNextNode(node)) { 811 return true; 812 } 813 return false; 814} 815 816function judgeComponentType(nodeName: ts.Identifier, etsComponentExpression: ts.EtsComponentExpression, 817 isCheckType: ParamType): boolean { 818 return COMPONENT_TOGGLE === nodeName.escapedText.toString() && 819 etsComponentExpression.arguments && etsComponentExpression.arguments[0] && 820 ts.isObjectLiteralExpression(etsComponentExpression.arguments[0]) && 821 etsComponentExpression.arguments[0].getText() && 822 judgeToggleComponentParamType(etsComponentExpression.arguments[0].getText(), isCheckType); 823} 824 825function judgeToggleComponentParamType(param: string, isCheckType: ParamType): boolean { 826 if (param.indexOf(RESOURCE_NAME_TYPE) > -1) { 827 isCheckType.name = RESOURCE_NAME_TYPE; 828 const match: string[] = param.match(/\b(Checkbox|Switch|Button)\b/); 829 if (match && match.length) { 830 isCheckType.value = match[0]; 831 if (isCheckType.value === COMPONENT_BUTTON) { 832 return false; 833 } 834 return true; 835 } 836 } 837 return false; 838} 839 840function getNextNode(node: ts.EtsComponentExpression): ts.Block { 841 if (node.body && ts.isBlock(node.body)) { 842 const statementsArray: ts.Block = node.body; 843 return statementsArray; 844 } 845} 846 847function checkOneChildComponent(node: ts.EtsComponentExpression, allComponentNames: Set<string>, 848 sourceFileNode: ts.SourceFile, log: LogInfo[]): void { 849 const isCheckType: ParamType = { name: null, value: null}; 850 if (hasNonSingleChild(node, allComponentNames, isCheckType)) { 851 const componentName: string = (node.expression as ts.Identifier).escapedText.toString(); 852 const pos: number = node.expression.getStart(); 853 const message: string = isCheckType.name === null ? 854 `The component '${componentName}' can only have a single child component.` : 855 `When the component '${componentName}' set '${isCheckType.name}' is ` + 856 `'${isCheckType.value}', can only have a single child component.`; 857 addLog(LogType.ERROR, message, pos, log, sourceFileNode); 858 } 859} 860 861function hasNonSingleChild(node: ts.EtsComponentExpression, allComponentNames: Set<string>, 862 isCheckType: ParamType): boolean { 863 const nodeName: ts.Identifier = node.expression as ts.Identifier; 864 const BlockNode: ts.Block = getNextNode(node); 865 if (SINGLE_CHILD_COMPONENT.has(nodeName.escapedText.toString()) || !judgeComponentType(nodeName, node, isCheckType) && 866 isCheckType.value === COMPONENT_BUTTON) { 867 if (!BlockNode) { 868 return false; 869 } 870 if (BlockNode && BlockNode.statements) { 871 const length: number = BlockNode.statements.length; 872 if (!length) { 873 return false; 874 } 875 if (length > 3) { 876 return true; 877 } 878 const childCount: number = getBlockChildrenCount(BlockNode, allComponentNames); 879 if (childCount > 1) { 880 return true; 881 } 882 } 883 } 884 return false; 885} 886 887function getBlockChildrenCount(blockNode: ts.Block, allComponentNames: Set<string>): number { 888 let maxCount: number = 0; 889 const length: number = blockNode.statements.length; 890 for (let i = 0; i < length; ++i) { 891 const item: ts.Node = blockNode.statements[i]; 892 if (ts.isExpressionStatement(item) && ts.isCallExpression(item.expression) && 893 isForEachComponent(item.expression)) { 894 maxCount += 2; 895 } 896 if (ts.isIfStatement(item)) { 897 maxCount += getIfChildrenCount(item, allComponentNames); 898 } 899 if (ts.isExpressionStatement(item) && ts.isEtsComponentExpression(item.expression)) { 900 maxCount += 1; 901 } 902 if (ts.isExpressionStatement(item) && ts.isCallExpression(item.expression)) { 903 let newNode: any = item.expression; 904 while (newNode.expression) { 905 if (ts.isEtsComponentExpression(newNode) || ts.isCallExpression(newNode) && 906 isComponent(newNode, allComponentNames)) { 907 maxCount += 1; 908 } 909 newNode = newNode.expression; 910 } 911 } 912 if (maxCount > 1) { 913 break; 914 } 915 } 916 return maxCount; 917} 918 919function isComponent(node: ts.EtsComponentExpression | ts.CallExpression, allComponentNames: Set<string>): boolean { 920 if (ts.isIdentifier(node.expression) && 921 allComponentNames.has(node.expression.escapedText.toString())) { 922 return true; 923 } 924 return false; 925} 926 927function isForEachComponent(node: ts.EtsComponentExpression | ts.CallExpression): boolean { 928 if (ts.isIdentifier(node.expression)) { 929 const componentName: string = node.expression.escapedText.toString(); 930 return componentName === COMPONENT_FOREACH || componentName === COMPONENT_LAZYFOREACH; 931 } 932 return false; 933} 934 935function getIfChildrenCount(ifNode: ts.IfStatement, allComponentNames: Set<string>): number { 936 const maxCount: number = 937 Math.max(getStatementCount(ifNode.thenStatement, allComponentNames), 938 getStatementCount(ifNode.elseStatement, allComponentNames)); 939 return maxCount; 940} 941 942function getStatementCount(node: ts.Node, allComponentNames: Set<string>): number { 943 let maxCount: number = 0; 944 if (!node) { 945 return maxCount; 946 } else if (ts.isBlock(node)) { 947 maxCount = getBlockChildrenCount(node, allComponentNames); 948 } else if (ts.isIfStatement(node)) { 949 maxCount = getIfChildrenCount(node, allComponentNames); 950 } else if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) && 951 isForEachComponent(node.expression)) { 952 maxCount = 2; 953 } else if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) && 954 !isForEachComponent(node.expression) && isComponent(node.expression, allComponentNames)) { 955 maxCount = 1; 956 } 957 return maxCount; 958} 959 960function checkSpecificChildComponent(node: ts.EtsComponentExpression, allComponentNames: Set<string>, 961 sourceFileNode: ts.SourceFile, log: LogInfo[]): void { 962 if (hasNonspecificChild(node, allComponentNames)) { 963 const componentName: string = (node.expression as ts.Identifier).escapedText.toString(); 964 const pos: number = node.expression.getStart(); 965 const specificChildArray: string = 966 Array.from(SPECIFIC_CHILD_COMPONENT.get(componentName)).join(' and '); 967 const message: string = 968 `The component '${componentName}' can only have the child component ${specificChildArray}.`; 969 addLog(LogType.ERROR, message, pos, log, sourceFileNode); 970 } 971} 972 973function hasNonspecificChild(node: ts.EtsComponentExpression, 974 allComponentNames: Set<string>): boolean { 975 const nodeName: ts.Identifier = node.expression as ts.Identifier; 976 const nodeNameString: string = nodeName.escapedText.toString(); 977 const blockNode: ts.Block = getNextNode(node); 978 let isNonspecific: boolean = false; 979 if (SPECIFIC_CHILD_COMPONENT.has(nodeNameString) && blockNode) { 980 const specificChildSet: Set<string> = SPECIFIC_CHILD_COMPONENT.get(nodeNameString); 981 isNonspecific = isNonspecificChildBlock(blockNode, specificChildSet, allComponentNames); 982 if (isNonspecific) { 983 return isNonspecific; 984 } 985 } 986 return isNonspecific; 987} 988 989function isNonspecificChildBlock(blockNode: ts.Block, specificChildSet: Set<string>, 990 allComponentNames: Set<string>): boolean { 991 if (blockNode.statements) { 992 const length: number = blockNode.statements.length; 993 for (let i = 0; i < length; ++i) { 994 const item: ts.Node = blockNode.statements[i]; 995 if (ts.isIfStatement(item) && isNonspecificChildIf(item, specificChildSet, allComponentNames)) { 996 return true; 997 } 998 if (ts.isExpressionStatement(item) && ts.isCallExpression(item.expression) && 999 isForEachComponent(item.expression) && 1000 isNonspecificChildForEach(item.expression, specificChildSet, allComponentNames)) { 1001 return true; 1002 } 1003 if (ts.isBlock(item) && isNonspecificChildBlock(item, specificChildSet, allComponentNames)) { 1004 return true; 1005 } 1006 if (ts.isExpressionStatement(item)) { 1007 let newNode: any = item.expression; 1008 while (newNode.expression) { 1009 if (ts.isEtsComponentExpression(newNode) && ts.isIdentifier(newNode.expression) && 1010 !isForEachComponent(newNode) && isComponent(newNode, allComponentNames)) { 1011 const isNonspecific: boolean = 1012 isNonspecificChildNonForEach(newNode, specificChildSet); 1013 if (isNonspecific) { 1014 return isNonspecific; 1015 } 1016 if (i + 1 < length && ts.isBlock(blockNode.statements[i + 1])) { 1017 ++i; 1018 } 1019 } 1020 newNode = newNode.expression; 1021 } 1022 } 1023 } 1024 } 1025 return false; 1026} 1027 1028function isNonspecificChildIf(node: ts.IfStatement, specificChildSet: Set<string>, 1029 allComponentNames: Set<string>): boolean { 1030 return isNonspecificChildIfStatement(node.thenStatement, specificChildSet, allComponentNames) || 1031 isNonspecificChildIfStatement(node.elseStatement, specificChildSet, allComponentNames); 1032} 1033 1034function isNonspecificChildForEach(node: ts.EtsComponentExpression, specificChildSet: Set<string>, 1035 allComponentNames: Set<string>): boolean { 1036 if (ts.isCallExpression(node) && node.arguments && 1037 node.arguments.length > 1 && ts.isArrowFunction(node.arguments[1])) { 1038 const arrowFunction: ts.ArrowFunction = node.arguments[1] as ts.ArrowFunction; 1039 const body: ts.Block | ts.EtsComponentExpression | ts.IfStatement = 1040 arrowFunction.body as ts.Block | ts.EtsComponentExpression | ts.IfStatement; 1041 if (!body) { 1042 return false; 1043 } 1044 if (ts.isBlock(body) && isNonspecificChildBlock(body, specificChildSet, allComponentNames)) { 1045 return true; 1046 } 1047 if (ts.isIfStatement(body) && isNonspecificChildIf(body, specificChildSet, allComponentNames)) { 1048 return true; 1049 } 1050 if (ts.isCallExpression(body) && isForEachComponent(body) && 1051 isNonspecificChildForEach(body, specificChildSet, allComponentNames)) { 1052 return true; 1053 } 1054 if (ts.isEtsComponentExpression(body) && !isForEachComponent(body) && 1055 isComponent(body, allComponentNames) && 1056 isNonspecificChildNonForEach(body, specificChildSet)) { 1057 return true; 1058 } 1059 } 1060 return false; 1061} 1062 1063function isNonspecificChildNonForEach(node: ts.EtsComponentExpression, 1064 specificChildSet: Set<string>): boolean { 1065 if (ts.isIdentifier(node.expression) && 1066 !specificChildSet.has(node.expression.escapedText.toString())) { 1067 return true; 1068 } 1069 return false; 1070} 1071 1072function isNonspecificChildIfStatement(node: ts.Node, specificChildSet: Set<string>, 1073 allComponentNames: Set<string>): boolean { 1074 if (!node) { 1075 return false; 1076 } 1077 if (ts.isBlock(node) && isNonspecificChildBlock(node, specificChildSet, allComponentNames)) { 1078 return true; 1079 } 1080 if (ts.isIfStatement(node) && isNonspecificChildIf(node, specificChildSet, allComponentNames)) { 1081 return true; 1082 } 1083 if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) && 1084 isForEachComponent(node.expression) && 1085 isNonspecificChildForEach(node.expression, specificChildSet, allComponentNames)) { 1086 return true; 1087 } 1088 if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) && 1089 !isForEachComponent(node.expression) && isComponent(node.expression, allComponentNames) && 1090 isNonspecificChildNonForEach(node.expression, specificChildSet)) { 1091 return true; 1092 } 1093 return false; 1094} 1095 1096function collectComponentProps(node: ts.StructDeclaration, judgeInitializeInEntry: boolean, 1097 structInfo: StructInfo): void { 1098 const componentName: string = node.name.getText(); 1099 const componentSet: IComponentSet = getComponentSet(node, judgeInitializeInEntry, true); 1100 propertyCollection.set(componentName, componentSet.properties); 1101 stateCollection.set(componentName, componentSet.states); 1102 linkCollection.set(componentName, componentSet.links); 1103 storedFileInfo.overallLinkCollection.set(componentName, componentSet.links); 1104 propCollection.set(componentName, componentSet.props); 1105 regularCollection.set(componentName, componentSet.regulars); 1106 storagePropCollection.set(componentName, componentSet.storageProps); 1107 storageLinkCollection.set(componentName, componentSet.storageLinks); 1108 provideCollection.set(componentName, componentSet.provides); 1109 consumeCollection.set(componentName, componentSet.consumes); 1110 objectLinkCollection.set(componentName, componentSet.objectLinks); 1111 storedFileInfo.overallObjectLinkCollection.set(componentName, componentSet.objectLinks); 1112 localStorageLinkCollection.set(componentName, componentSet.localStorageLink); 1113 localStoragePropCollection.set(componentName, componentSet.localStorageProp); 1114 builderParamObjectCollection.set(componentName, componentSet.builderParams); 1115 builderParamInitialization.set(componentName, componentSet.builderParamData); 1116 propInitialization.set(componentName, componentSet.propData); 1117 regularInitialization.set(componentName, componentSet.regularInit); 1118 stateInitialization.set(componentName, componentSet.stateInit); 1119 provideInitialization.set(componentName, componentSet.provideInit); 1120 privateCollection.set(componentName, componentSet.privateCollection); 1121 structInfo.updatePropsDecoratorsV1.push( 1122 ...componentSet.states, ...componentSet.props, ...componentSet.links, 1123 ...componentSet.provides, ...componentSet.objectLinks 1124 ); 1125} 1126 1127export function getComponentSet(node: ts.StructDeclaration, judgeInitializeInEntry: boolean, 1128 uiCheck: boolean = false): IComponentSet { 1129 const componentSet: IComponentSet = new IComponentSet(); 1130 traversalComponentProps(node, judgeInitializeInEntry, componentSet, uiCheck); 1131 return componentSet; 1132} 1133 1134class RecordRequire { 1135 hasRequire: boolean = false; 1136 hasProp: boolean = false; 1137 hasBuilderParam: boolean = false; 1138 hasRegular: boolean = false; 1139 hasState: boolean = false; 1140 hasProvide: boolean = false; 1141} 1142 1143function traversalComponentProps(node: ts.StructDeclaration, judgeInitializeInEntry: boolean, 1144 componentSet: IComponentSet, uiCheck: boolean = false): void { 1145 let isStatic: boolean = true; 1146 if (node.members) { 1147 const currentMethodCollection: Set<string> = new Set(); 1148 node.members.forEach(item => { 1149 if (ts.isPropertyDeclaration(item) && ts.isIdentifier(item.name)) { 1150 const propertyName: string = item.name.getText(); 1151 componentSet.properties.add(propertyName); 1152 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(item); 1153 const accessQualifierResult: AccessQualifierResult = getAccessQualifier(item, uiCheck); 1154 if (!decorators || !decorators.length) { 1155 componentSet.regulars.add(propertyName); 1156 setPrivateCollection(componentSet, accessQualifierResult, propertyName, COMPONENT_NON_DECORATOR); 1157 } else { 1158 isStatic = false; 1159 let hasValidatePrivate: boolean = false; 1160 const recordRequire: RecordRequire = new RecordRequire(); 1161 for (let i = 0; i < decorators.length; i++) { 1162 const decoratorName: string = decorators[i].getText().replace(/\(.*\)$/, '').trim(); 1163 if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { 1164 dollarCollection.add('$' + propertyName); 1165 collectionStates(decorators[i], judgeInitializeInEntry, decoratorName, propertyName, 1166 componentSet, recordRequire); 1167 setPrivateCollection(componentSet, accessQualifierResult, propertyName, decoratorName); 1168 validateAccessQualifier(item, propertyName, decoratorName, accessQualifierResult, 1169 recordRequire, uiCheck, hasValidatePrivate); 1170 hasValidatePrivate = true; 1171 } 1172 } 1173 regularAndRequire(decorators, componentSet, recordRequire, propertyName, accessQualifierResult); 1174 checkRequire(propertyName, componentSet, recordRequire); 1175 } 1176 } 1177 if (ts.isMethodDeclaration(item) && item.name && ts.isIdentifier(item.name)) { 1178 validateStateVariable(item); 1179 currentMethodCollection.add(item.name.getText()); 1180 } 1181 }); 1182 collectCurrentClassMethod(node, currentMethodCollection); 1183 } 1184 isStaticViewCollection.set(node.name.getText(), isStatic); 1185} 1186 1187function collectCurrentClassMethod(node: ts.StructDeclaration, currentMethodCollection: Set<string>): void { 1188 const componentMethodCollection: Map<string, Set<string>> = new Map(); 1189 componentMethodCollection.set(node.name.getText(), currentMethodCollection); 1190 const sourceFile: ts.SourceFile = node.getSourceFile(); 1191 const filePath: string = sourceFile ? sourceFile.fileName : undefined; 1192 if (filePath) { 1193 const pageMethodCollection: Map<string, Set<string>> = classMethodCollection.get(filePath); 1194 if (!pageMethodCollection) { 1195 classMethodCollection.set(filePath, componentMethodCollection); 1196 } else if (!pageMethodCollection.get(node.name.getText())) { 1197 pageMethodCollection.set(node.name.getText(), currentMethodCollection); 1198 } 1199 } 1200} 1201 1202const FORBIDDEN_PUBLIC_ACCESS: string[] = [COMPONENT_STORAGE_PROP_DECORATOR, 1203 COMPONENT_STORAGE_LINK_DECORATOR, COMPONENT_LOCAL_STORAGE_LINK_DECORATOR, 1204 COMPONENT_LOCAL_STORAGE_PROP_DECORATOR, COMPONENT_CONSUME_DECORATOR 1205]; 1206const FORBIDDEN_PRIVATE_ACCESS: string[] = [COMPONENT_LINK_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR]; 1207 1208function validateAccessQualifier(node: ts.PropertyDeclaration, propertyName: string, 1209 decoratorName: string, accessQualifierResult: AccessQualifierResult, 1210 recordRequire: RecordRequire, uiCheck: boolean = false, hasValidatePrivate: boolean): void { 1211 if (uiCheck) { 1212 if (accessQualifierResult.hasPublic && FORBIDDEN_PUBLIC_ACCESS.includes(decoratorName)) { 1213 transformLog.errors.push({ 1214 type: LogType.WARN, 1215 message: `Property '${propertyName}' can not be decorated with both ${decoratorName} and public.`, 1216 pos: node.getStart() 1217 }); 1218 } 1219 if (accessQualifierResult.hasPrivate) { 1220 if (FORBIDDEN_PRIVATE_ACCESS.includes(decoratorName)) { 1221 transformLog.errors.push({ 1222 type: LogType.WARN, 1223 message: `Property '${propertyName}' can not be decorated with both ${decoratorName} and private.`, 1224 pos: node.getStart() 1225 }); 1226 } 1227 if (recordRequire.hasRequire && !hasValidatePrivate) { 1228 transformLog.errors.push({ 1229 type: LogType.WARN, 1230 message: `Property '${propertyName}' can not be decorated with both @Require and private.`, 1231 pos: node.getStart() 1232 }); 1233 } 1234 } 1235 } 1236} 1237 1238const SUPPORT_PRIVATE_PROPS: string[] = [COMPONENT_NON_DECORATOR, COMPONENT_STATE_DECORATOR, 1239 COMPONENT_PROP_DECORATOR, COMPONENT_PROVIDE_DECORATOR, COMPONENT_BUILDERPARAM_DECORATOR 1240]; 1241 1242function setPrivateCollection(componentSet: IComponentSet, accessQualifierResult: AccessQualifierResult, 1243 propertyName: string, decoratorName: string): void { 1244 if (accessQualifierResult.hasPrivate && SUPPORT_PRIVATE_PROPS.includes(decoratorName)) { 1245 componentSet.privateCollection.add(propertyName); 1246 } 1247} 1248 1249class AccessQualifierResult { 1250 hasPrivate: boolean = false; 1251 hasPublic: boolean = false; 1252} 1253 1254function getAccessQualifier(node: ts.PropertyDeclaration, uiCheck: boolean = false): AccessQualifierResult { 1255 const modifiers: readonly ts.Modifier[] = ts.getModifiers(node); 1256 const accessQualifierResult: AccessQualifierResult = new AccessQualifierResult(); 1257 if (modifiers && modifiers.length) { 1258 modifiers.forEach((item) => { 1259 if (item.kind === ts.SyntaxKind.PrivateKeyword) { 1260 accessQualifierResult.hasPrivate = true; 1261 } 1262 if (item.kind === ts.SyntaxKind.PublicKeyword) { 1263 accessQualifierResult.hasPublic = true; 1264 } 1265 if (uiCheck && item.kind === ts.SyntaxKind.ProtectedKeyword) { 1266 transformLog.errors.push({ 1267 type: LogType.WARN, 1268 message: `The member attributes of a struct can not be protected.`, 1269 pos: node.getStart() 1270 }); 1271 } 1272 }); 1273 } 1274 return accessQualifierResult; 1275} 1276 1277function regularAndRequire(decorators: readonly ts.Decorator[], componentSet: IComponentSet, 1278 recordRequire: RecordRequire, propertyName: string, accessQualifierResult: AccessQualifierResult): void { 1279 if (decorators && decorators.length === 1 && decorators[0].getText() === COMPONENT_REQUIRE_DECORATOR) { 1280 componentSet.regulars.add(propertyName); 1281 recordRequire.hasRegular = true; 1282 setPrivateCollection(componentSet, accessQualifierResult, propertyName, COMPONENT_NON_DECORATOR); 1283 } 1284} 1285 1286function checkRequire(name: string, componentSet: IComponentSet, recordRequire: RecordRequire): void { 1287 if (recordRequire.hasRequire) { 1288 setInitValue('hasProp', 'propData', name, componentSet, recordRequire); 1289 setInitValue('hasBuilderParam', 'builderParamData', name, componentSet, recordRequire); 1290 setInitValue('hasRegular', 'regularInit', name, componentSet, recordRequire); 1291 setInitValue('hasState', 'stateInit', name, componentSet, recordRequire); 1292 setInitValue('hasProvide', 'provideInit', name, componentSet, recordRequire); 1293 } 1294} 1295 1296function setInitValue(requirekey: string, initKey: string, name: string, componentSet: IComponentSet, 1297 recordRequire: RecordRequire): void { 1298 if (recordRequire[requirekey]) { 1299 componentSet[initKey].add(name); 1300 } 1301} 1302 1303function collectionStates(node: ts.Decorator, judgeInitializeInEntry: boolean, decorator: string, name: string, 1304 componentSet: IComponentSet, recordRequire: RecordRequire): void { 1305 switch (decorator) { 1306 case COMPONENT_STATE_DECORATOR: 1307 componentSet.states.add(name); 1308 recordRequire.hasState = true; 1309 break; 1310 case COMPONENT_LINK_DECORATOR: 1311 componentSet.links.add(name); 1312 break; 1313 case COMPONENT_PROP_DECORATOR: 1314 recordRequire.hasProp = true; 1315 componentSet.props.add(name); 1316 break; 1317 case COMPONENT_STORAGE_PROP_DECORATOR: 1318 componentSet.storageProps.add(name); 1319 break; 1320 case COMPONENT_STORAGE_LINK_DECORATOR: 1321 componentSet.storageLinks.add(name); 1322 break; 1323 case COMPONENT_PROVIDE_DECORATOR: 1324 recordRequire.hasProvide = true; 1325 componentSet.provides.add(name); 1326 break; 1327 case COMPONENT_CONSUME_DECORATOR: 1328 componentSet.consumes.add(name); 1329 break; 1330 case COMPONENT_OBJECT_LINK_DECORATOR: 1331 componentSet.objectLinks.add(name); 1332 break; 1333 case COMPONENT_BUILDERPARAM_DECORATOR: 1334 if (judgeInitializeInEntry) { 1335 validateInitializeInEntry(node, name); 1336 } 1337 recordRequire.hasBuilderParam = true; 1338 componentSet.builderParams.add(name); 1339 break; 1340 case COMPONENT_LOCAL_STORAGE_LINK_DECORATOR : 1341 collectionlocalStorageParam(node, name, componentSet.localStorageLink); 1342 break; 1343 case COMPONENT_LOCAL_STORAGE_PROP_DECORATOR: 1344 collectionlocalStorageParam(node, name, componentSet.localStorageProp); 1345 break; 1346 case COMPONENT_REQUIRE_DECORATOR: 1347 recordRequire.hasRequire = true; 1348 break; 1349 } 1350} 1351 1352function validateInitializeInEntry(node: ts.Decorator, name: string): void { 1353 transformLog.errors.push({ 1354 type: LogType.WARN, 1355 message: `'${name}' should be initialized in @Entry Component`, 1356 pos: node.getStart() 1357 }); 1358} 1359 1360function collectionlocalStorageParam(node: ts.Decorator, name: string, 1361 localStorage: Map<string, Set<string>>): void { 1362 const localStorageParam: Set<string> = new Set(); 1363 if (node && ts.isCallExpression(node.expression) && node.expression.arguments && 1364 node.expression.arguments.length) { 1365 localStorage.set(name, localStorageParam.add( 1366 node.expression.arguments[0].getText())); 1367 } 1368} 1369 1370export interface ReplaceResult { 1371 content: string, 1372 log: LogInfo[] 1373} 1374 1375export function sourceReplace(source: string, sourcePath: string): ReplaceResult { 1376 let content: string = source; 1377 const log: LogInfo[] = []; 1378 content = preprocessExtend(content); 1379 content = preprocessNewExtend(content); 1380 // process @system. 1381 content = processSystemApi(content, false, sourcePath); 1382 collectImportNames(content, sourcePath); 1383 1384 return { 1385 content: content, 1386 log: log 1387 }; 1388} 1389 1390export function preprocessExtend(content: string, extendCollection?: Set<string>): string { 1391 const REG_EXTEND: RegExp = /@Extend(\s+)([^\.\s]+)\.([^\(]+)\(/gm; 1392 return content.replace(REG_EXTEND, (item, item1, item2, item3) => { 1393 collectExtend(EXTEND_ATTRIBUTE, item2, '__' + item2 + '__' + item3); 1394 collectExtend(EXTEND_ATTRIBUTE, item2, item3); 1395 if (extendCollection) { 1396 extendCollection.add(item3); 1397 } 1398 return `@Extend(${item2})${item1}function __${item2}__${item3}(`; 1399 }); 1400} 1401 1402export function preprocessNewExtend(content: string, extendCollection?: Set<string>): string { 1403 const REG_EXTEND: RegExp = /@Extend\s*\([^\)]+\)\s*function\s+([^\(\s]+)\s*\(/gm; 1404 return content.replace(REG_EXTEND, (item, item1) => { 1405 if (extendCollection) { 1406 extendCollection.add(item1); 1407 } 1408 return item; 1409 }); 1410} 1411 1412function replaceSystemApi(item: string, systemValue: string, moduleType: string, systemKey: string): string { 1413 // if change format, please update regexp in transformModuleSpecifier 1414 if (NATIVE_MODULE.has(`${moduleType}.${systemKey}`)) { 1415 item = `var ${systemValue} = globalThis.requireNativeModule('${moduleType}.${systemKey}')`; 1416 } else if (moduleType === SYSTEM_PLUGIN || moduleType === OHOS_PLUGIN) { 1417 item = `var ${systemValue} = globalThis.requireNapi('${systemKey}')`; 1418 } 1419 return item; 1420} 1421 1422function replaceLibSo(importValue: string, libSoKey: string, sourcePath: string = null): string { 1423 if (sourcePath) { 1424 useOSFiles.add(sourcePath); 1425 } 1426 // if change format, please update regexp in transformModuleSpecifier 1427 return projectConfig.bundleName && projectConfig.moduleName ? 1428 `var ${importValue} = globalThis.requireNapi("${libSoKey}", true, "${projectConfig.bundleName}/${projectConfig.moduleName}");` : 1429 `var ${importValue} = globalThis.requireNapi("${libSoKey}", true);`; 1430} 1431 1432export function processSystemApi(content: string, isProcessAllowList: boolean = false, 1433 sourcePath: string = null, isSystemModule: boolean = false): string { 1434 if (isProcessAllowList && projectConfig.compileMode === ESMODULE) { 1435 // remove the unused system api import decl like following when compile as [esmodule] 1436 // in the result_process phase 1437 // e.g. import "@ohos.application.xxx" 1438 const REG_UNUSED_SYSTEM_IMPORT: RegExp = /import(?:\s*)['"]@(system|ohos)\.(\S+)['"]/g; 1439 content = content.replace(REG_UNUSED_SYSTEM_IMPORT, ''); 1440 } 1441 1442 const REG_IMPORT_DECL: RegExp = isProcessAllowList ? projectConfig.compileMode === ESMODULE ? 1443 /import\s+(.+)\s+from\s+['"]@(system|ohos)\.(\S+)['"]/g : 1444 /(import|const)\s+(.+)\s*=\s*(\_\_importDefault\()?require\(\s*['"]@(system|ohos)\.(\S+)['"]\s*\)(\))?/g : 1445 /(import|export)\s+(?:(.+)|\{([\s\S]+)\})\s+from\s+['"](\S+)['"]|import\s+(.+)\s*=\s*require\(\s*['"](\S+)['"]\s*\)/g; 1446 1447 const systemValueCollection: Set<string> = new Set(); 1448 const processedContent: string = content.replace(REG_IMPORT_DECL, (item, item1, item2, item3, item4, item5, item6) => { 1449 const importValue: string = isProcessAllowList ? projectConfig.compileMode === ESMODULE ? item1 : item2 : item2 || item5; 1450 1451 if (isProcessAllowList) { 1452 systemValueCollection.add(importValue); 1453 if (projectConfig.compileMode !== ESMODULE) { 1454 collectSourcemapNames(sourcePath, importValue, item5); 1455 return replaceSystemApi(item, importValue, item4, item5); 1456 } 1457 collectSourcemapNames(sourcePath, importValue, item3); 1458 return replaceSystemApi(item, importValue, item2, item3); 1459 } 1460 1461 const moduleRequest: string = item4 || item6; 1462 if (/^@(system|ohos)\./.test(moduleRequest)) { // ohos/system.api 1463 // ets & ts file need compile with .d.ts, so do not replace at the phase of pre_process 1464 if (!isSystemModule) { 1465 return item; 1466 } 1467 const result: RegExpMatchArray = moduleRequest.match(/^@(system|ohos)\.(\S+)$/); 1468 const moduleType: string = result[1]; 1469 const apiName: string = result[2]; 1470 return replaceSystemApi(item, importValue, moduleType, apiName); 1471 } else if (/^lib(\S+)\.so$/.test(moduleRequest)) { // libxxx.so 1472 const result: RegExpMatchArray = moduleRequest.match(/^lib(\S+)\.so$/); 1473 const libSoKey: string = result[1]; 1474 return replaceLibSo(importValue, libSoKey, sourcePath); 1475 } 1476 return item; 1477 }); 1478 return processInnerModule(processedContent, systemValueCollection); 1479} 1480 1481function collectSourcemapNames(sourcePath: string, changedName: string, originalName: string): void { 1482 if (sourcePath == null) { 1483 return; 1484 } 1485 const cleanSourcePath: string = sourcePath.replace('.ets', '.js').replace('.ts', '.js'); 1486 if (!sourcemapNamesCollection.has(cleanSourcePath)) { 1487 return; 1488 } 1489 1490 const map: Map<string, string> = sourcemapNamesCollection.get(cleanSourcePath); 1491 if (map.has(changedName)) { 1492 return; 1493 } 1494 1495 for (const entry of originalImportNamesMap.entries()) { 1496 const key: string = entry[0]; 1497 const value: string = entry[1]; 1498 if (value === '@ohos.' + originalName || value === '@system.' + originalName) { 1499 map.set(changedName.trim(), key); 1500 sourcemapNamesCollection.set(cleanSourcePath, map); 1501 originalImportNamesMap.delete(key); 1502 break; 1503 } 1504 } 1505} 1506 1507export function collectImportNames(content: string, sourcePath: string = null): void { 1508 const REG_IMPORT_DECL: RegExp = 1509 /(import|export)\s+(.+)\s+from\s+['"](\S+)['"]|import\s+(.+)\s*=\s*require\(\s*['"](\S+)['"]\s*\)/g; 1510 1511 const decls: string[] = content.match(REG_IMPORT_DECL); 1512 if (decls != undefined) { 1513 decls.forEach(decl => { 1514 const parts: string[] = decl.split(' '); 1515 if (parts.length === 4 && parts[0] === 'import' && parts[2] === 'from' && !parts[3].includes('.so')) { 1516 originalImportNamesMap.set(parts[1], parts[3].replace(/'/g, '')); 1517 } 1518 }); 1519 } 1520 1521 if (sourcePath && sourcePath !== null) { 1522 const cleanSourcePath: string = sourcePath.replace('.ets', '.js').replace('.ts', '.js'); 1523 if (!sourcemapNamesCollection.has(cleanSourcePath)) { 1524 sourcemapNamesCollection.set(cleanSourcePath, new Map()); 1525 } 1526 } 1527} 1528 1529function processInnerModule(content: string, systemValueCollection: Set<string>): string { 1530 systemValueCollection.forEach(element => { 1531 const target: string = element.trim() + '.default'; 1532 while (content.includes(target)) { 1533 content = content.replace(target, element.trim()); 1534 } 1535 }); 1536 return content; 1537} 1538 1539export function resetComponentCollection(): void { 1540 componentCollection.entryComponent = null; 1541 componentCollection.entryComponentPos = null; 1542 componentCollection.previewComponent = []; 1543 stateObjectCollection.clear(); 1544 builderParamInitialization.clear(); 1545 propInitialization.clear(); 1546 propCollection.clear(); 1547 objectLinkCollection.clear(); 1548 linkCollection.clear(); 1549 storedFileInfo.overallLinkCollection.clear(); 1550 storedFileInfo.overallObjectLinkCollection.clear(); 1551 regularInitialization.clear(); 1552 stateInitialization.clear(); 1553 provideInitialization.clear(); 1554 privateCollection.clear(); 1555} 1556 1557function checkEntryComponent(node: ts.StructDeclaration, log: LogInfo[], sourceFile: ts.SourceFile): void { 1558 const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined; 1559 if (modifiers) { 1560 for (let i = 0; i < modifiers.length; i++) { 1561 if (modifiers[i].kind === ts.SyntaxKind.ExportKeyword) { 1562 const message: string = `It's not a recommended way to export struct with @Entry decorator, ` + 1563 `which may cause ACE Engine error in component preview mode.`; 1564 addLog(LogType.WARN, message, node.getStart(), log, sourceFile); 1565 break; 1566 } 1567 } 1568 } 1569} 1570 1571function validateStateVariable(node: ts.MethodDeclaration): void { 1572 const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node); 1573 if (decorators && decorators.length) { 1574 for (let i = 0; i < decorators.length; i++) { 1575 const decoratorName: string = decorators[i].getText().replace(/\(.*\)$/, '').trim(); 1576 if (CARD_ENABLE_DECORATORS[decoratorName]) { 1577 validatorCard(transformLog.errors, CARD_LOG_TYPE_DECORATORS, 1578 decorators[i].getStart(), decoratorName); 1579 } 1580 if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { 1581 transformLog.errors.push({ 1582 type: LogType.ERROR, 1583 message: `'${decorators[i].getText()}' can not decorate the method.`, 1584 pos: decorators[i].getStart() 1585 }); 1586 } 1587 } 1588 } 1589} 1590 1591export function getObservedPropertyCollection(className: string): Set<string> { 1592 const observedProperthCollection: Set<string> = new Set([ 1593 ...stateCollection.get(className), 1594 ...linkCollection.get(className), 1595 ...propCollection.get(className), 1596 ...storageLinkCollection.get(className), 1597 ...storageLinkCollection.get(className), 1598 ...provideCollection.get(className), 1599 ...consumeCollection.get(className), 1600 ...objectLinkCollection.get(className) 1601 ]); 1602 getLocalStorageCollection(className, observedProperthCollection); 1603 return observedProperthCollection; 1604} 1605 1606export function getLocalStorageCollection(componentName: string, collection: Set<string>): void { 1607 if (localStorageLinkCollection.get(componentName)) { 1608 for (const key of localStorageLinkCollection.get(componentName).keys()) { 1609 collection.add(key); 1610 } 1611 } 1612 if (localStoragePropCollection.get(componentName)) { 1613 for (const key of localStoragePropCollection.get(componentName).keys()) { 1614 collection.add(key); 1615 } 1616 } 1617} 1618 1619export function resetValidateUiSyntax(): void { 1620 observedClassCollection.clear(); 1621 enumCollection.clear(); 1622 classMethodCollection.clear(); 1623 dollarCollection.clear(); 1624 stateCollection.clear(); 1625 regularCollection.clear(); 1626 storagePropCollection.clear(); 1627 storageLinkCollection.clear(); 1628 provideCollection.clear(); 1629 consumeCollection.clear(); 1630 builderParamObjectCollection.clear(); 1631 localStorageLinkCollection.clear(); 1632 localStoragePropCollection.clear(); 1633 isStaticViewCollection.clear(); 1634 useOSFiles.clear(); 1635 sourcemapNamesCollection.clear(); 1636 originalImportNamesMap.clear(); 1637} 1638