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'; 18import fs from 'fs'; 19 20import { 21 INNER_COMPONENT_DECORATORS, 22 COMPONENT_DECORATOR_ENTRY, 23 COMPONENT_DECORATOR_PREVIEW, 24 COMPONENT_DECORATOR_COMPONENT, 25 COMPONENT_DECORATOR_CUSTOM_DIALOG, 26 NATIVE_MODULE, 27 SYSTEM_PLUGIN, 28 OHOS_PLUGIN, 29 INNER_COMPONENT_MEMBER_DECORATORS, 30 COMPONENT_FOREACH, 31 COMPONENT_LAZYFOREACH, 32 COMPONENT_STATE_DECORATOR, 33 COMPONENT_LINK_DECORATOR, 34 COMPONENT_PROP_DECORATOR, 35 COMPONENT_STORAGE_PROP_DECORATOR, 36 COMPONENT_STORAGE_LINK_DECORATOR, 37 COMPONENT_PROVIDE_DECORATOR, 38 COMPONENT_CONSUME_DECORATOR, 39 COMPONENT_OBJECT_LINK_DECORATOR, 40 COMPONENT_OBSERVED_DECORATOR, 41 COMPONENT_LOCAL_STORAGE_LINK_DECORATOR, 42 COMPONENT_LOCAL_STORAGE_PROP_DECORATOR, 43 STYLES, 44 VALIDATE_MODULE, 45 COMPONENT_BUILDER_DECORATOR, 46 COMPONENT_CONCURRENT_DECORATOR, 47 COMPONENT_EXTEND_DECORATOR, 48 COMPONENT_STYLES_DECORATOR, 49 RESOURCE_NAME_TYPE, 50 TTOGGLE_CHECKBOX, 51 TOGGLE_SWITCH, 52 COMPONENT_BUTTON, 53 COMPONENT_TOGGLE, 54 COMPONENT_BUILDERPARAM_DECORATOR, 55 ESMODULE, 56 CARD_ENABLE_DECORATORS, 57 CARD_LOG_TYPE_DECORATORS, 58 JSBUNDLE 59} from './pre_define'; 60import { 61 INNER_COMPONENT_NAMES, 62 AUTOMIC_COMPONENT, 63 SINGLE_CHILD_COMPONENT, 64 SPECIFIC_CHILD_COMPONENT, 65 BUILDIN_STYLE_NAMES, 66 EXTEND_ATTRIBUTE, 67 GLOBAL_STYLE_FUNCTION, 68 STYLES_ATTRIBUTE, 69 CUSTOM_BUILDER_METHOD, 70 GLOBAL_CUSTOM_BUILDER_METHOD, 71 INNER_CUSTOM_BUILDER_METHOD, 72 INNER_STYLE_FUNCTION 73} from './component_map'; 74import { 75 LogType, 76 LogInfo, 77 componentInfo, 78 addLog, 79 hasDecorator, 80} from './utils'; 81import { getPackageInfo } from './ark_utils' 82import { projectConfig, abilityPagesFullPath } from '../main'; 83import { 84 collectExtend, 85 isExtendFunction, 86 transformLog, 87 validatorCard 88} from './process_ui_syntax'; 89import { logger } from './compile_info'; 90 91export interface ComponentCollection { 92 localStorageName: string; 93 entryComponentPos: number; 94 entryComponent: string; 95 previewComponent: Array<string>; 96 customDialogs: Set<string>; 97 customComponents: Set<string>; 98 currentClassName: string; 99} 100 101export interface IComponentSet { 102 properties: Set<string>; 103 regulars: Set<string>; 104 states: Set<string>; 105 links: Set<string>; 106 props: Set<string>; 107 storageProps: Set<string>; 108 storageLinks: Set<string>; 109 provides: Set<string>; 110 consumes: Set<string>; 111 objectLinks: Set<string>; 112 localStorageLink: Map<string, Set<string>>; 113 localStorageProp: Map<string, Set<string>>; 114 builderParams: Set<string>; 115 builderParamData: Set<string>; 116} 117 118export const componentCollection: ComponentCollection = { 119 localStorageName: null, 120 entryComponentPos: null, 121 entryComponent: null, 122 previewComponent: new Array(), 123 customDialogs: new Set([]), 124 customComponents: new Set([]), 125 currentClassName: null 126}; 127 128export const observedClassCollection: Set<string> = new Set(); 129export const enumCollection: Set<string> = new Set(); 130export const classMethodCollection: Map<string, Set<string>> = new Map(); 131export const dollarCollection: Set<string> = new Set(); 132 133export const propertyCollection: Map<string, Set<string>> = new Map(); 134export const stateCollection: Map<string, Set<string>> = new Map(); 135export const linkCollection: Map<string, Set<string>> = new Map(); 136export const propCollection: Map<string, Set<string>> = new Map(); 137export const regularCollection: Map<string, Set<string>> = new Map(); 138export const storagePropCollection: Map<string, Set<string>> = new Map(); 139export const storageLinkCollection: Map<string, Set<string>> = new Map(); 140export const provideCollection: Map<string, Set<string>> = new Map(); 141export const consumeCollection: Map<string, Set<string>> = new Map(); 142export const objectLinkCollection: Map<string, Set<string>> = new Map(); 143export const builderParamObjectCollection: Map<string, Set<string>> = new Map(); 144export const localStorageLinkCollection: Map<string, Map<string, Set<string>>> = new Map(); 145export const localStoragePropCollection: Map<string, Map<string, Set<string>>> = new Map(); 146export const builderParamInitialization: Map<string, Set<string>> = new Map(); 147 148export const isStaticViewCollection: Map<string, boolean> = new Map(); 149 150export const useOSFiles: Set<string> = new Set(); 151export const sourcemapNamesCollection: Map<string, Map<string, string>> = new Map(); 152export const originalImportNamesMap: Map<string, string> = new Map(); 153 154export function validateUISyntax(source: string, content: string, filePath: string, 155 fileQuery: string): LogInfo[] { 156 let log: LogInfo[] = []; 157 if (process.env.compileMode === 'moduleJson' || 158 path.resolve(filePath) !== path.resolve(projectConfig.projectPath || '', 'app.ets')) { 159 const res: LogInfo[] = checkComponentDecorator(source, filePath, fileQuery); 160 if (res) { 161 log = log.concat(res); 162 } 163 const allComponentNames: Set<string> = 164 new Set([...INNER_COMPONENT_NAMES, ...componentCollection.customComponents]); 165 checkUISyntax(filePath, allComponentNames, content, log); 166 componentCollection.customComponents.forEach(item => componentInfo.componentNames.add(item)); 167 } 168 169 return log; 170} 171 172function checkComponentDecorator(source: string, filePath: string, 173 fileQuery: string): LogInfo[] | null { 174 const log: LogInfo[] = []; 175 const sourceFile: ts.SourceFile = ts.createSourceFile(filePath, source, 176 ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS); 177 if (sourceFile && sourceFile.statements && sourceFile.statements.length) { 178 const result: DecoratorResult = { 179 entryCount: 0, 180 previewCount: 0 181 }; 182 sourceFile.statements.forEach((item, index, arr) => { 183 if (isObservedClass(item)) { 184 // @ts-ignore 185 observedClassCollection.add(item.name.getText()); 186 } 187 if (ts.isEnumDeclaration(item) && item.name) { 188 enumCollection.add(item.name.getText()); 189 } 190 if (ts.isStructDeclaration(item)) { 191 if (item.name && ts.isIdentifier(item.name)) { 192 if (item.decorators && item.decorators.length) { 193 checkDecorators(item.decorators, result, item.name, log, sourceFile, item); 194 } else { 195 const message: string = `A struct should use decorator '@Component'.`; 196 addLog(LogType.WARN, message, item.getStart(), log, sourceFile); 197 } 198 } else { 199 const message: string = `A struct must have a name.`; 200 addLog(LogType.ERROR, message, item.getStart(), log, sourceFile); 201 } 202 } 203 if (ts.isMissingDeclaration(item)) { 204 const decorators: ts.NodeArray<ts.Decorator> = item.decorators; 205 for (let i = 0; i < decorators.length; i++) { 206 if (decorators[i] && /struct/.test(decorators[i].getText())) { 207 const message: string = `Please use a valid decorator.`; 208 addLog(LogType.ERROR, message, item.getStart(), log, sourceFile); 209 break; 210 } 211 } 212 } 213 }); 214 validateEntryAndPreviewCount(result, fileQuery, sourceFile.fileName, projectConfig.isPreview, 215 !!projectConfig.checkEntry, log); 216 } 217 218 return log.length ? log : null; 219} 220 221function validateEntryAndPreviewCount(result: DecoratorResult, fileQuery: string, 222 fileName: string, isPreview: boolean, checkEntry: boolean, log: LogInfo[]): void { 223 if (result.previewCount > 10 && fileQuery === '?entry') { 224 log.push({ 225 type: LogType.ERROR, 226 message: `A page can contain at most 10 '@Preview' decorators.`, 227 fileName: fileName 228 }); 229 } 230 if (result.entryCount > 1 && fileQuery === '?entry') { 231 log.push({ 232 type: LogType.ERROR, 233 message: `A page can't contain more than one '@Entry' decorator`, 234 fileName: fileName 235 }); 236 } 237 if (isPreview && !checkEntry && result.previewCount < 1 && result.entryCount !== 1 && 238 fileQuery === '?entry') { 239 log.push({ 240 type: LogType.ERROR, 241 message: `A page which is being previewed must have one and only one '@Entry' ` 242 + `decorator, or at least one '@Preview' decorator.`, 243 fileName: fileName 244 }); 245 } else if ((!isPreview || isPreview && checkEntry) && result.entryCount !== 1 && fileQuery === '?entry' && 246 !abilityPagesFullPath.includes(fileName)) { 247 log.push({ 248 type: LogType.ERROR, 249 message: `A page configured in '${projectConfig.pagesJsonFileName}' must have one and only one '@Entry' ` 250 + `decorator.`, 251 fileName: fileName 252 }); 253 } 254} 255 256export function isObservedClass(node: ts.Node): boolean { 257 if (ts.isClassDeclaration(node) && hasDecorator(node, COMPONENT_OBSERVED_DECORATOR)) { 258 return true; 259 } 260 return false; 261} 262 263export function isCustomDialogClass(node: ts.Node): boolean { 264 if (ts.isClassDeclaration(node) && hasDecorator(node, COMPONENT_DECORATOR_CUSTOM_DIALOG)) { 265 return true; 266 } 267 return false; 268} 269 270interface DecoratorResult { 271 entryCount: number; 272 previewCount: number; 273} 274 275function checkDecorators(decorators: ts.NodeArray<ts.Decorator>, result: DecoratorResult, 276 component: ts.Identifier, log: LogInfo[], sourceFile: ts.SourceFile, node: ts.StructDeclaration): void { 277 let hasComponentDecorator: boolean = false; 278 const componentName: string = component.getText(); 279 decorators.forEach((element) => { 280 const name: string = element.getText().replace(/\([^\(\)]*\)/, '').trim(); 281 if (INNER_COMPONENT_DECORATORS.has(name)) { 282 componentCollection.customComponents.add(componentName); 283 switch (name) { 284 case COMPONENT_DECORATOR_ENTRY: 285 checkEntryComponent(node, log, sourceFile); 286 result.entryCount++; 287 componentCollection.entryComponent = componentName; 288 componentCollection.entryComponentPos = node.getStart(); 289 collectLocalStorageName(element); 290 break; 291 case COMPONENT_DECORATOR_PREVIEW: 292 result.previewCount++; 293 componentCollection.previewComponent.push(componentName); 294 break; 295 case COMPONENT_DECORATOR_COMPONENT: 296 hasComponentDecorator = true; 297 break; 298 case COMPONENT_DECORATOR_CUSTOM_DIALOG: 299 componentCollection.customDialogs.add(componentName); 300 hasComponentDecorator = true; 301 break; 302 } 303 } else { 304 const pos: number = element.expression ? element.expression.pos : element.pos; 305 const message: string = `The struct '${componentName}' use invalid decorator.`; 306 addLog(LogType.WARN, message, pos, log, sourceFile); 307 } 308 }); 309 if (!hasComponentDecorator) { 310 const message: string = `The struct '${componentName}' should use decorator '@Component'.`; 311 addLog(LogType.WARN, message, component.pos, log, sourceFile); 312 } 313 if (BUILDIN_STYLE_NAMES.has(componentName)) { 314 const message: string = `The struct '${componentName}' cannot have the same name ` + 315 `as the built-in attribute '${componentName}'.`; 316 addLog(LogType.ERROR, message, component.pos, log, sourceFile); 317 } 318 if (INNER_COMPONENT_NAMES.has(componentName)) { 319 const message: string = `The struct '${componentName}' cannot have the same name ` + 320 `as the built-in component '${componentName}'.`; 321 addLog(LogType.ERROR, message, component.pos, log, sourceFile); 322 } 323} 324 325function checkConcurrentDecorator(node: ts.FunctionDeclaration | ts.MethodDeclaration, log: LogInfo[], 326 sourceFile: ts.SourceFile): void { 327 if (projectConfig.compileMode === JSBUNDLE) { 328 const message: string = `@Concurrent can only be used in ESMODULE compile mode.`; 329 addLog(LogType.ERROR, message, node.decorators!.pos, log, sourceFile); 330 } 331 if (ts.isMethodDeclaration(node)) { 332 const message: string = `@Concurrent can not be used on method. please use it on function declaration.`; 333 addLog(LogType.ERROR, message, node.decorators!.pos, log, sourceFile); 334 } 335 if (node.asteriskToken) { 336 let hasAsync: boolean = false; 337 const checkAsyncModifier = (modifier: ts.Modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword; 338 node.modifiers && (hasAsync = node.modifiers.some(checkAsyncModifier)); 339 const funcKind: string = hasAsync ? 'Async generator' : 'Generator'; 340 const message: string = `@Concurrent can not be used on ${funcKind} function declaration.`; 341 addLog(LogType.ERROR, message, node.decorators!.pos, log, sourceFile); 342 } 343} 344 345function collectLocalStorageName(node: ts.Decorator): void { 346 if (node && node.expression && ts.isCallExpression(node.expression)) { 347 if (node.expression.arguments && node.expression.arguments.length) { 348 node.expression.arguments.forEach((item: ts.Node, index: number) => { 349 if (ts.isIdentifier(item) && index === 0) { 350 componentCollection.localStorageName = item.getText(); 351 } 352 }); 353 } 354 } else { 355 componentCollection.localStorageName = null; 356 } 357} 358 359function checkUISyntax(filePath: string, allComponentNames: Set<string>, content: string, 360 log: LogInfo[]): void { 361 const sourceFile: ts.SourceFile = ts.createSourceFile(filePath, content, 362 ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS); 363 visitAllNode(sourceFile, sourceFile, allComponentNames, log); 364} 365 366function visitAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, allComponentNames: Set<string>, 367 log: LogInfo[]) { 368 if (ts.isStructDeclaration(node) && node.name && ts.isIdentifier(node.name)) { 369 collectComponentProps(node); 370 } 371 if (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node)) { 372 if (hasDecorator(node, COMPONENT_BUILDER_DECORATOR)) { 373 CUSTOM_BUILDER_METHOD.add(node.name.getText()); 374 if (ts.isFunctionDeclaration(node)) { 375 GLOBAL_CUSTOM_BUILDER_METHOD.add(node.name.getText()); 376 } else { 377 INNER_CUSTOM_BUILDER_METHOD.add(node.name.getText()); 378 } 379 } else if (ts.isFunctionDeclaration(node) && isExtendFunction(node)) { 380 const componentName: string = isExtendFunction(node); 381 collectExtend(EXTEND_ATTRIBUTE, componentName, node.name.getText()); 382 } else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) { 383 if (ts.isBlock(node.body) && node.body.statements && node.body.statements.length) { 384 if (ts.isFunctionDeclaration(node)) { 385 GLOBAL_STYLE_FUNCTION.set(node.name.getText(), node.body); 386 } else { 387 INNER_STYLE_FUNCTION.set(node.name.getText(), node.body); 388 } 389 STYLES_ATTRIBUTE.add(node.name.getText()); 390 BUILDIN_STYLE_NAMES.add(node.name.getText()); 391 } 392 } 393 if (hasDecorator(node, COMPONENT_CONCURRENT_DECORATOR)) { 394 // ark compiler's feature 395 checkConcurrentDecorator(node, log, sourceFileNode); 396 } 397 } 398 node.getChildren().forEach((item: ts.Node) => visitAllNode(item, sourceFileNode, allComponentNames, log)); 399} 400 401export function checkAllNode( 402 node: ts.EtsComponentExpression, 403 allComponentNames: Set<string>, 404 sourceFileNode: ts.SourceFile, 405 log: LogInfo[] 406): void { 407 if (ts.isIdentifier(node.expression)) { 408 checkNoChildComponent(node, sourceFileNode, log); 409 checkOneChildComponent(node, allComponentNames, sourceFileNode, log); 410 checkSpecificChildComponent(node, allComponentNames, sourceFileNode, log); 411 } 412} 413 414interface ParamType { 415 name: string, 416 value: string, 417} 418 419function checkNoChildComponent(node: ts.EtsComponentExpression, sourceFileNode: ts.SourceFile, log: LogInfo[]): void { 420 const isCheckType: ParamType = { name: null, value: null}; 421 if (hasChild(node, isCheckType)) { 422 const componentName: string = (node.expression as ts.Identifier).escapedText.toString(); 423 const pos: number = node.expression.getStart(); 424 const message: string = isCheckType.name === null ? 425 `The component '${componentName}' can't have any child.` : 426 `When the component '${componentName}' set '${isCheckType.name}' is '${isCheckType.value}'` + 427 `, can't have any child.`; 428 addLog(LogType.ERROR, message, pos, log, sourceFileNode); 429 } 430} 431 432function hasChild(node: ts.EtsComponentExpression, isCheckType: ParamType): boolean { 433 const nodeName: ts.Identifier = node.expression as ts.Identifier; 434 if ((AUTOMIC_COMPONENT.has(nodeName.escapedText.toString()) || judgeComponentType(nodeName, node, isCheckType)) && 435 getNextNode(node)) { 436 return true; 437 } 438 return false; 439} 440 441function judgeComponentType(nodeName: ts.Identifier, etsComponentExpression: ts.EtsComponentExpression, 442 isCheckType: ParamType): boolean { 443 return COMPONENT_TOGGLE === nodeName.escapedText.toString() && 444 etsComponentExpression.arguments && etsComponentExpression.arguments[0] && 445 ts.isObjectLiteralExpression(etsComponentExpression.arguments[0]) && 446 etsComponentExpression.arguments[0].getText() && 447 judgeToggleComponentParamType(etsComponentExpression.arguments[0].getText(), isCheckType); 448} 449 450function judgeToggleComponentParamType(param: string, isCheckType: ParamType): boolean { 451 if (param.indexOf(RESOURCE_NAME_TYPE) > -1) { 452 isCheckType.name = RESOURCE_NAME_TYPE; 453 const match: string[] = param.match(/\b(Checkbox|Switch|Button)\b/); 454 if (match && match.length) { 455 isCheckType.value = match[0]; 456 if (isCheckType.value === COMPONENT_BUTTON) { 457 return false; 458 } 459 return true; 460 } 461 } 462 return false; 463} 464 465function getNextNode(node: ts.EtsComponentExpression): ts.Block { 466 if (node.body && ts.isBlock(node.body)) { 467 const statementsArray: ts.Block = node.body; 468 return statementsArray; 469 } 470} 471 472function checkOneChildComponent(node: ts.EtsComponentExpression, allComponentNames: Set<string>, 473 sourceFileNode: ts.SourceFile, log: LogInfo[]): void { 474 const isCheckType: ParamType = { name: null, value: null}; 475 if (hasNonSingleChild(node, allComponentNames, isCheckType)) { 476 const componentName: string = (node.expression as ts.Identifier).escapedText.toString(); 477 const pos: number = node.expression.getStart(); 478 const message: string = isCheckType.name === null ? 479 `The component '${componentName}' can only have a single child component.` : 480 `When the component '${componentName}' set '${isCheckType.name}' is ` + 481 `'${isCheckType.value}', can only have a single child component.`; 482 addLog(LogType.ERROR, message, pos, log, sourceFileNode); 483 } 484} 485 486function hasNonSingleChild(node: ts.EtsComponentExpression, allComponentNames: Set<string>, 487 isCheckType: ParamType): boolean { 488 const nodeName: ts.Identifier = node.expression as ts.Identifier; 489 const BlockNode: ts.Block = getNextNode(node); 490 if (SINGLE_CHILD_COMPONENT.has(nodeName.escapedText.toString()) || !judgeComponentType(nodeName, node, isCheckType) 491 && isCheckType.value === COMPONENT_BUTTON) { 492 if (!BlockNode) { 493 return false; 494 } 495 if (BlockNode && BlockNode.statements) { 496 const length: number = BlockNode.statements.length; 497 if (!length) { 498 return false; 499 } 500 if (length > 3) { 501 return true; 502 } 503 const childCount: number = getBlockChildrenCount(BlockNode, allComponentNames); 504 if (childCount > 1) { 505 return true; 506 } 507 } 508 } 509 return false; 510} 511 512function getBlockChildrenCount(blockNode: ts.Block, allComponentNames: Set<string>): number { 513 let maxCount: number = 0; 514 const length: number = blockNode.statements.length; 515 for (let i = 0; i < length; ++i) { 516 const item: ts.Node = blockNode.statements[i]; 517 if (ts.isExpressionStatement(item) && ts.isCallExpression(item.expression) && 518 isForEachComponent(item.expression)) { 519 maxCount += 2; 520 } 521 if (ts.isIfStatement(item)) { 522 maxCount += getIfChildrenCount(item, allComponentNames); 523 } 524 if (ts.isExpressionStatement(item) && ts.isEtsComponentExpression(item.expression)) { 525 maxCount += 1; 526 } 527 if (ts.isExpressionStatement(item) && ts.isCallExpression(item.expression)) { 528 let newNode: any = item.expression; 529 while (newNode.expression) { 530 if (ts.isEtsComponentExpression(newNode) || ts.isCallExpression(newNode) && 531 isComponent(newNode, allComponentNames)) { 532 maxCount += 1; 533 } 534 newNode = newNode.expression; 535 } 536 } 537 if (maxCount > 1) { 538 break; 539 } 540 } 541 return maxCount; 542} 543 544function isComponent(node: ts.EtsComponentExpression | ts.CallExpression, allComponentNames: Set<string>): boolean { 545 if (ts.isIdentifier(node.expression) && 546 allComponentNames.has(node.expression.escapedText.toString())) { 547 return true; 548 } 549 return false; 550} 551 552function isForEachComponent(node: ts.EtsComponentExpression | ts.CallExpression): boolean { 553 if (ts.isIdentifier(node.expression)) { 554 const componentName: string = node.expression.escapedText.toString(); 555 return componentName === COMPONENT_FOREACH || componentName === COMPONENT_LAZYFOREACH; 556 } 557 return false; 558} 559 560function getIfChildrenCount(ifNode: ts.IfStatement, allComponentNames: Set<string>): number { 561 const maxCount: number = 562 Math.max(getStatementCount(ifNode.thenStatement, allComponentNames), 563 getStatementCount(ifNode.elseStatement, allComponentNames)); 564 return maxCount; 565} 566 567function getStatementCount(node: ts.Node, allComponentNames: Set<string>): number { 568 let maxCount: number = 0; 569 if (!node) { 570 return maxCount; 571 } else if (ts.isBlock(node)) { 572 maxCount = getBlockChildrenCount(node, allComponentNames); 573 } else if (ts.isIfStatement(node)) { 574 maxCount = getIfChildrenCount(node, allComponentNames); 575 } else if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) && 576 isForEachComponent(node.expression)) { 577 maxCount = 2; 578 } else if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) && 579 !isForEachComponent(node.expression) && isComponent(node.expression, allComponentNames)) { 580 maxCount = 1; 581 } 582 return maxCount; 583} 584 585function checkSpecificChildComponent(node: ts.EtsComponentExpression, allComponentNames: Set<string>, 586 sourceFileNode: ts.SourceFile, log: LogInfo[]): void { 587 if (hasNonspecificChild(node, allComponentNames)) { 588 const componentName: string = (node.expression as ts.Identifier).escapedText.toString(); 589 const pos: number = node.expression.getStart(); 590 const specificChildArray: string = 591 Array.from(SPECIFIC_CHILD_COMPONENT.get(componentName)).join(' and '); 592 const message: string = 593 `The component '${componentName}' can only have the child component ${specificChildArray}.`; 594 addLog(LogType.ERROR, message, pos, log, sourceFileNode); 595 } 596} 597 598function hasNonspecificChild(node: ts.EtsComponentExpression, 599 allComponentNames: Set<string>): boolean { 600 const nodeName: ts.Identifier = node.expression as ts.Identifier; 601 const nodeNameString: string = nodeName.escapedText.toString(); 602 const blockNode: ts.Block = getNextNode(node); 603 let isNonspecific: boolean = false; 604 if (SPECIFIC_CHILD_COMPONENT.has(nodeNameString) && blockNode) { 605 const specificChildSet: Set<string> = SPECIFIC_CHILD_COMPONENT.get(nodeNameString); 606 isNonspecific = isNonspecificChildBlock(blockNode, specificChildSet, allComponentNames); 607 if (isNonspecific) { 608 return isNonspecific; 609 } 610 } 611 return isNonspecific; 612} 613 614function isNonspecificChildBlock(blockNode: ts.Block, specificChildSet: Set<string>, 615 allComponentNames: Set<string>): boolean { 616 if (blockNode.statements) { 617 const length: number = blockNode.statements.length; 618 for (let i = 0; i < length; ++i) { 619 const item: ts.Node = blockNode.statements[i]; 620 if (ts.isIfStatement(item) && isNonspecificChildIf(item, specificChildSet, allComponentNames)) { 621 return true; 622 } 623 if (ts.isExpressionStatement(item) && ts.isCallExpression(item.expression) && 624 isForEachComponent(item.expression) && 625 isNonspecificChildForEach(item.expression, specificChildSet, allComponentNames)) { 626 return true; 627 } 628 if (ts.isBlock(item) && isNonspecificChildBlock(item, specificChildSet, allComponentNames)) { 629 return true; 630 } 631 if (ts.isExpressionStatement(item)) { 632 let newNode: any = item.expression; 633 while (newNode.expression) { 634 if (ts.isEtsComponentExpression(newNode) && ts.isIdentifier(newNode.expression) && 635 !isForEachComponent(newNode) && isComponent(newNode, allComponentNames)) { 636 const isNonspecific: boolean = 637 isNonspecificChildNonForEach(newNode, specificChildSet); 638 if (isNonspecific) { 639 return isNonspecific; 640 } 641 if (i + 1 < length && ts.isBlock(blockNode.statements[i + 1])) { 642 ++i; 643 } 644 } 645 newNode = newNode.expression; 646 } 647 } 648 } 649 } 650 return false; 651} 652 653function isNonspecificChildIf(node: ts.IfStatement, specificChildSet: Set<string>, 654 allComponentNames: Set<string>): boolean { 655 return isNonspecificChildIfStatement(node.thenStatement, specificChildSet, allComponentNames) || 656 isNonspecificChildIfStatement(node.elseStatement, specificChildSet, allComponentNames); 657} 658 659function isNonspecificChildForEach(node: ts.EtsComponentExpression, specificChildSet: Set<string>, 660 allComponentNames: Set<string>): boolean { 661 if (ts.isCallExpression(node) && node.arguments && 662 node.arguments.length > 1 && ts.isArrowFunction(node.arguments[1])) { 663 const arrowFunction: ts.ArrowFunction = node.arguments[1] as ts.ArrowFunction; 664 const body: ts.Block | ts.EtsComponentExpression | ts.IfStatement = 665 arrowFunction.body as ts.Block | ts.EtsComponentExpression | ts.IfStatement; 666 if (!body) { 667 return false; 668 } 669 if (ts.isBlock(body) && isNonspecificChildBlock(body, specificChildSet, allComponentNames)) { 670 return true; 671 } 672 if (ts.isIfStatement(body) && isNonspecificChildIf(body, specificChildSet, allComponentNames)) { 673 return true; 674 } 675 if (ts.isCallExpression(body) && isForEachComponent(body) && 676 isNonspecificChildForEach(body, specificChildSet, allComponentNames)) { 677 return true; 678 } 679 if (ts.isEtsComponentExpression(body) && !isForEachComponent(body) && 680 isComponent(body, allComponentNames) && 681 isNonspecificChildNonForEach(body, specificChildSet)) { 682 return true; 683 } 684 } 685 return false; 686} 687 688function isNonspecificChildNonForEach(node: ts.EtsComponentExpression, 689 specificChildSet: Set<string>): boolean { 690 if (ts.isIdentifier(node.expression) && 691 !specificChildSet.has(node.expression.escapedText.toString())) { 692 return true; 693 } 694 return false; 695} 696 697function isNonspecificChildIfStatement(node: ts.Node, specificChildSet: Set<string>, 698 allComponentNames: Set<string>): boolean { 699 if (!node) { 700 return false; 701 } 702 if (ts.isBlock(node) && isNonspecificChildBlock(node, specificChildSet, allComponentNames)) { 703 return true; 704 } 705 if (ts.isIfStatement(node) && isNonspecificChildIf(node, specificChildSet, allComponentNames)) { 706 return true; 707 } 708 if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) && 709 isForEachComponent(node.expression) && 710 isNonspecificChildForEach(node.expression, specificChildSet, allComponentNames)) { 711 return true; 712 } 713 if (ts.isExpressionStatement(node) && ts.isEtsComponentExpression(node.expression) && 714 !isForEachComponent(node.expression) && isComponent(node.expression, allComponentNames) && 715 isNonspecificChildNonForEach(node.expression, specificChildSet)) { 716 return true; 717 } 718 return false; 719} 720 721function collectComponentProps(node: ts.StructDeclaration): void { 722 const componentName: string = node.name.getText(); 723 const ComponentSet: IComponentSet = getComponentSet(node); 724 propertyCollection.set(componentName, ComponentSet.properties); 725 stateCollection.set(componentName, ComponentSet.states); 726 linkCollection.set(componentName, ComponentSet.links); 727 propCollection.set(componentName, ComponentSet.props); 728 regularCollection.set(componentName, ComponentSet.regulars); 729 storagePropCollection.set(componentName, ComponentSet.storageProps); 730 storageLinkCollection.set(componentName, ComponentSet.storageLinks); 731 provideCollection.set(componentName, ComponentSet.provides); 732 consumeCollection.set(componentName, ComponentSet.consumes); 733 objectLinkCollection.set(componentName, ComponentSet.objectLinks); 734 localStorageLinkCollection.set(componentName, ComponentSet.localStorageLink); 735 localStoragePropCollection.set(componentName, ComponentSet.localStorageProp); 736 builderParamObjectCollection.set(componentName, ComponentSet.builderParams); 737 builderParamInitialization.set(componentName, ComponentSet.builderParamData); 738} 739 740export function getComponentSet(node: ts.StructDeclaration): IComponentSet { 741 const properties: Set<string> = new Set(); 742 const states: Set<string> = new Set(); 743 const links: Set<string> = new Set(); 744 const props: Set<string> = new Set(); 745 const regulars: Set<string> = new Set(); 746 const storageProps: Set<string> = new Set(); 747 const storageLinks: Set<string> = new Set(); 748 const provides: Set<string> = new Set(); 749 const consumes: Set<string> = new Set(); 750 const objectLinks: Set<string> = new Set(); 751 const builderParams: Set<string> = new Set(); 752 const localStorageLink: Map<string, Set<string>> = new Map(); 753 const localStorageProp: Map<string, Set<string>> = new Map(); 754 const builderParamData: Set<string> = new Set(); 755 traversalComponentProps(node, properties, regulars, states, links, props, storageProps, 756 storageLinks, provides, consumes, objectLinks, localStorageLink, localStorageProp, builderParams, 757 builderParamData); 758 return { 759 properties, regulars, states, links, props, storageProps, storageLinks, provides, 760 consumes, objectLinks, localStorageLink, localStorageProp, builderParams, builderParamData 761 }; 762} 763 764function traversalComponentProps(node: ts.StructDeclaration, properties: Set<string>, 765 regulars: Set<string>, states: Set<string>, links: Set<string>, props: Set<string>, 766 storageProps: Set<string>, storageLinks: Set<string>, provides: Set<string>, 767 consumes: Set<string>, objectLinks: Set<string>, 768 localStorageLink: Map<string, Set<string>>, localStorageProp: Map<string, Set<string>>, 769 builderParams: Set<string>, builderParamData: Set<string>): void { 770 let isStatic: boolean = true; 771 if (node.members) { 772 const currentMethodCollection: Set<string> = new Set(); 773 node.members.forEach(item => { 774 if (ts.isPropertyDeclaration(item) && ts.isIdentifier(item.name)) { 775 const propertyName: string = item.name.getText(); 776 properties.add(propertyName); 777 if (!item.decorators || !item.decorators.length) { 778 regulars.add(propertyName); 779 } else { 780 isStatic = false; 781 for (let i = 0; i < item.decorators.length; i++) { 782 const decoratorName: string = item.decorators[i].getText().replace(/\(.*\)$/, '').trim(); 783 if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { 784 dollarCollection.add('$' + propertyName); 785 collectionStates(item.decorators[i], decoratorName, propertyName, states, links, props, storageProps, 786 storageLinks, provides, consumes, objectLinks, localStorageLink, localStorageProp, 787 builderParams, item.initializer, builderParamData); 788 } 789 } 790 } 791 } 792 if (ts.isMethodDeclaration(item) && item.name && ts.isIdentifier(item.name)) { 793 validateStateVariable(item); 794 currentMethodCollection.add(item.name.getText()); 795 } 796 }); 797 classMethodCollection.set(node.name.getText(), currentMethodCollection); 798 } 799 isStaticViewCollection.set(node.name.getText(), isStatic); 800} 801 802function collectionStates(node: ts.Decorator, decorator: string, name: string, 803 states: Set<string>, links: Set<string>, props: Set<string>, storageProps: Set<string>, 804 storageLinks: Set<string>, provides: Set<string>, consumes: Set<string>, objectLinks: Set<string>, 805 localStorageLink: Map<string, Set<string>>, localStorageProp: Map<string, Set<string>>, 806 builderParams: Set<string>, initializationtName: ts.Expression, builderParamData: Set<string>): void { 807 switch (decorator) { 808 case COMPONENT_STATE_DECORATOR: 809 states.add(name); 810 break; 811 case COMPONENT_LINK_DECORATOR: 812 links.add(name); 813 break; 814 case COMPONENT_PROP_DECORATOR: 815 props.add(name); 816 break; 817 case COMPONENT_STORAGE_PROP_DECORATOR: 818 storageProps.add(name); 819 break; 820 case COMPONENT_STORAGE_LINK_DECORATOR: 821 storageLinks.add(name); 822 break; 823 case COMPONENT_PROVIDE_DECORATOR: 824 provides.add(name); 825 break; 826 case COMPONENT_CONSUME_DECORATOR: 827 consumes.add(name); 828 break; 829 case COMPONENT_OBJECT_LINK_DECORATOR: 830 objectLinks.add(name); 831 break; 832 case COMPONENT_BUILDERPARAM_DECORATOR: 833 if (initializationtName) { 834 builderParamData.add(name); 835 } 836 builderParams.add(name); 837 break; 838 case COMPONENT_LOCAL_STORAGE_LINK_DECORATOR : 839 collectionlocalStorageParam(node, name, localStorageLink); 840 break; 841 case COMPONENT_LOCAL_STORAGE_PROP_DECORATOR: 842 collectionlocalStorageParam(node, name, localStorageProp); 843 break; 844 } 845} 846 847function collectionlocalStorageParam(node: ts.Decorator, name: string, 848 localStorage: Map<string, Set<string>>): void { 849 const localStorageParam: Set<string> = new Set(); 850 if (node && ts.isCallExpression(node.expression) && node.expression.arguments && 851 node.expression.arguments.length && ts.isStringLiteral(node.expression.arguments[0])) { 852 localStorage.set(name, localStorageParam.add( 853 node.expression.arguments[0].getText().replace(/\"|'/g, ''))); 854 } 855} 856 857export interface ReplaceResult { 858 content: string, 859 log: LogInfo[] 860} 861 862export function sourceReplace(source: string, sourcePath: string): ReplaceResult { 863 let content: string = source; 864 const log: LogInfo[] = []; 865 content = preprocessExtend(content); 866 content = preprocessNewExtend(content); 867 // process @system. 868 content = processSystemApi(content, false, sourcePath); 869 CollectImportNames(content, sourcePath); 870 871 return { 872 content: content, 873 log: log 874 }; 875} 876 877export function preprocessExtend(content: string, extendCollection?: Set<string>): string { 878 const REG_EXTEND: RegExp = /@Extend(\s+)([^\.\s]+)\.([^\(]+)\(/gm; 879 return content.replace(REG_EXTEND, (item, item1, item2, item3) => { 880 collectExtend(EXTEND_ATTRIBUTE, item2, '__' + item2 + '__' + item3); 881 collectExtend(EXTEND_ATTRIBUTE, item2, item3); 882 if (extendCollection) { 883 extendCollection.add(item3); 884 } 885 return `@Extend(${item2})${item1}function __${item2}__${item3}(`; 886 }); 887} 888 889export function preprocessNewExtend(content: string, extendCollection?: Set<string>): string { 890 const REG_EXTEND: RegExp = /@Extend\s*\([^\)]+\)\s*function\s+([^\(\s]+)\s*\(/gm; 891 return content.replace(REG_EXTEND, (item, item1) => { 892 if (extendCollection) { 893 extendCollection.add(item1); 894 } 895 return item; 896 }); 897} 898 899function replaceSystemApi(item: string, systemValue: string, moduleType: string, systemKey: string): string { 900 // if change format, please update regexp in transformModuleSpecifier 901 if (NATIVE_MODULE.has(`${moduleType}.${systemKey}`)) { 902 item = `var ${systemValue} = globalThis.requireNativeModule('${moduleType}.${systemKey}')`; 903 } else if (moduleType === SYSTEM_PLUGIN || moduleType === OHOS_PLUGIN) { 904 item = `var ${systemValue} = globalThis.requireNapi('${systemKey}')`; 905 } 906 return item; 907} 908 909function replaceLibSo(importValue: string, libSoKey: string, sourcePath: string = null): string { 910 if (sourcePath) { 911 useOSFiles.add(sourcePath); 912 } 913 // if change format, please update regexp in transformModuleSpecifier 914 return projectConfig.bundleName && projectConfig.moduleName 915 ? `var ${importValue} = globalThis.requireNapi("${libSoKey}", true, "${projectConfig.bundleName}/${projectConfig.moduleName}");` 916 : `var ${importValue} = globalThis.requireNapi("${libSoKey}", true);`; 917} 918 919function replaceOhmStartsWithBundle(url: string, item: string, importValue: string, moduleRequest: string, sourcePath: string): string { 920 const urlResult: RegExpMatchArray | null = url.match(/^(\S+)\/(\S+)\/(\S+)\/(\S+)$/); 921 if (urlResult) { 922 const moduleKind: string = urlResult[3]; 923 if (moduleKind === 'lib') { 924 const libSoKey: string = urlResult[4]; 925 item = replaceLibSo(importValue, libSoKey, sourcePath); 926 } 927 } 928 return item; 929} 930 931function replaceOhmStartsWithModule(url: string, item: string, importValue: string, moduleRequest: string, sourcePath: string): string { 932 const urlResult: RegExpMatchArray | null = url.match(/^(\S+)\/(\S+)\/(\S+)$/); 933 if (urlResult && projectConfig.aceModuleJsonPath) { 934 const moduleName: string = urlResult[1]; 935 const moduleKind: string = urlResult[2]; 936 const modulePath: string = urlResult[3]; 937 const bundleName: string = getPackageInfo(projectConfig.aceModuleJsonPath)[0]; 938 moduleRequest = `@bundle:${bundleName}/${moduleName}/${moduleKind}/${modulePath}`; 939 item = moduleKind === 'lib' ? replaceLibSo(importValue, modulePath, sourcePath) : 940 item.replace(/['"](\S+)['"]/, '\"' + moduleRequest + '\"'); 941 } 942 return item; 943} 944 945function replaceOhmStartsWithOhos(url: string, item: string, importValue:string, moduleRequest: string, isSystemModule: boolean): string { 946 url = url.replace('/', '.'); 947 const urlResult: RegExpMatchArray | null = url.match(/^system\.(\S+)/); 948 moduleRequest = urlResult ? `@${url}` : `@ohos.${url}`; 949 if (!isSystemModule) { 950 item = item.replace(/['"](\S+)['"]/, '\"' + moduleRequest + '\"'); 951 } else { 952 const moduleType: string = urlResult ? 'system' : 'ohos'; 953 const systemKey: string = urlResult ? url.substring(7) : url; 954 item = replaceSystemApi(item, importValue, moduleType, systemKey); 955 } 956 return item; 957} 958 959function replaceOhmStartsWithLocal(url: string, item: string, importValue: string, moduleRequest: string, sourcePath: string): string { 960 const result: RegExpMatchArray | null = sourcePath.match(/(\S+)(\/|\\)src(\/|\\)(?:main|ohosTest)(\/|\\)(ets|js)(\/|\\)(\S+)/); 961 if (result && projectConfig.aceModuleJsonPath) { 962 const packageInfo: string[] = getPackageInfo(projectConfig.aceModuleJsonPath); 963 const urlResult: RegExpMatchArray | null = url.match(/^\/(ets|js|lib|node_modules)\/(\S+)$/); 964 if (urlResult) { 965 const moduleKind: string = urlResult[1]; 966 const modulePath: string = urlResult[2]; 967 if (moduleKind === 'lib') { 968 item = replaceLibSo(importValue, modulePath, sourcePath); 969 } else if (moduleKind === 'node_modules') { 970 moduleRequest = `${modulePath}`; 971 item = item.replace(/['"](\S+)['"]/, '\"' + moduleRequest + '\"'); 972 } else { 973 moduleRequest = `@bundle:${packageInfo[0]}/${packageInfo[1]}/${moduleKind}/${modulePath}`; 974 item = item.replace(/['"](\S+)['"]/, '\"' + moduleRequest + '\"'); 975 } 976 } 977 } 978 return item; 979} 980 981function replaceOhmUrl(isSystemModule: boolean, item: string, importValue: string, moduleRequest: string, sourcePath: string = null): string { 982 const result: RegExpMatchArray = moduleRequest.match(/^@(\S+):(\S+)$/); 983 const urlType: string = result[1]; 984 const url: string = result[2]; 985 switch (urlType) { 986 case 'bundle': { 987 item = replaceOhmStartsWithBundle(url, item, importValue, moduleRequest, sourcePath); 988 break; 989 } 990 case 'module': { 991 item = replaceOhmStartsWithModule(url, item, importValue, moduleRequest, sourcePath); 992 break; 993 } 994 case 'ohos': { 995 item = replaceOhmStartsWithOhos(url, item, importValue, moduleRequest, isSystemModule); 996 break; 997 } 998 case 'lib': { 999 item = replaceLibSo(importValue, url, sourcePath); 1000 break; 1001 } 1002 case 'local': { 1003 item = replaceOhmStartsWithLocal(url, item, importValue, moduleRequest, sourcePath); 1004 break; 1005 } 1006 default: 1007 logger.error('\u001b[31m', `ArkTS:ERROR Incorrect OpenHarmony module kind: ${urlType}`, '\u001b[39m'); 1008 } 1009 return item; 1010} 1011 1012export function processSystemApi(content: string, isProcessAllowList: boolean = false, 1013 sourcePath: string = null, isSystemModule: boolean = false): string { 1014 if (isProcessAllowList && projectConfig.compileMode === ESMODULE) { 1015 // remove the unused system api import decl like following when compile as [esmodule] 1016 // in the result_process phase 1017 // e.g. import "@ohos.application.xxx" 1018 const REG_UNUSED_SYSTEM_IMPORT: RegExp = /import(?:\s*)['"]@(system|ohos)\.(\S+)['"]/g; 1019 content = content.replace(REG_UNUSED_SYSTEM_IMPORT, ''); 1020 } 1021 1022 const REG_IMPORT_DECL: RegExp = isProcessAllowList ? projectConfig.compileMode === ESMODULE ? 1023 /import\s+(.+)\s+from\s+['"]@(system|ohos)\.(\S+)['"]/g : 1024 /(import|const)\s+(.+)\s*=\s*(\_\_importDefault\()?require\(\s*['"]@(system|ohos)\.(\S+)['"]\s*\)(\))?/g : 1025 /(import|export)\s+(?:(.+)|\{([\s\S]+)\})\s+from\s+['"](\S+)['"]|import\s+(.+)\s*=\s*require\(\s*['"](\S+)['"]\s*\)/g; 1026 1027 const systemValueCollection: Set<string> = new Set(); 1028 const processedContent: string = content.replace(REG_IMPORT_DECL, (item, item1, item2, item3, item4, item5, item6) => { 1029 const importValue: string = isProcessAllowList ? projectConfig.compileMode === ESMODULE ? item1 : item2 : item2 || item5; 1030 1031 if (isProcessAllowList) { 1032 systemValueCollection.add(importValue); 1033 if (projectConfig.compileMode !== ESMODULE) { 1034 collectSourcemapNames(sourcePath, importValue, item5); 1035 return replaceSystemApi(item, importValue, item4, item5); 1036 } 1037 collectSourcemapNames(sourcePath, importValue, item3); 1038 return replaceSystemApi(item, importValue, item2, item3); 1039 } 1040 1041 const moduleRequest: string = item4 || item6; 1042 if (/^@(system|ohos)\./.test(moduleRequest)) { // ohos/system.api 1043 // ets & ts file need compile with .d.ts, so do not replace at the phase of pre_process 1044 if (!isSystemModule) { 1045 return item; 1046 } 1047 const result: RegExpMatchArray = moduleRequest.match(/^@(system|ohos)\.(\S+)$/); 1048 const moduleType: string = result[1]; 1049 const apiName: string = result[2]; 1050 return replaceSystemApi(item, importValue, moduleType, apiName); 1051 } else if (/^lib(\S+)\.so$/.test(moduleRequest)) { // libxxx.so 1052 const result: RegExpMatchArray = moduleRequest.match(/^lib(\S+)\.so$/); 1053 const libSoKey: string = result[1]; 1054 return replaceLibSo(importValue, libSoKey, sourcePath); 1055 } 1056 return item; 1057 }); 1058 return processInnerModule(processedContent, systemValueCollection); 1059} 1060 1061function collectSourcemapNames(sourcePath: string, changedName: string, originalName: string): void { 1062 if (sourcePath == null) { 1063 return; 1064 } 1065 const cleanSourcePath: string = sourcePath.replace('.ets', '.js').replace('.ts', '.js'); 1066 if (!sourcemapNamesCollection.has(cleanSourcePath)) { 1067 return; 1068 } 1069 1070 let map: Map<string, string> = sourcemapNamesCollection.get(cleanSourcePath); 1071 if (map.has(changedName)) { 1072 return; 1073 } 1074 1075 for (let entry of originalImportNamesMap.entries()) { 1076 const key: string = entry[0]; 1077 const value: string = entry[1]; 1078 if (value === '@ohos.' + originalName || value === '@system.' + originalName) { 1079 map.set(changedName.trim(), key); 1080 sourcemapNamesCollection.set(cleanSourcePath, map); 1081 originalImportNamesMap.delete(key); 1082 break; 1083 } 1084 } 1085} 1086 1087export function CollectImportNames(content: string, sourcePath: string = null): void { 1088 const REG_IMPORT_DECL: RegExp = 1089 /(import|export)\s+(.+)\s+from\s+['"](\S+)['"]|import\s+(.+)\s*=\s*require\(\s*['"](\S+)['"]\s*\)/g; 1090 1091 const decls: string[] = content.match(REG_IMPORT_DECL); 1092 if (decls != undefined) { 1093 decls.forEach(decl => { 1094 const parts: string[] = decl.split(' '); 1095 if (parts.length === 4 && parts[0] === 'import' && parts[2] === 'from' && !parts[3].includes('.so')) { 1096 originalImportNamesMap.set(parts[1], parts[3].replace(/'/g, '')); 1097 } 1098 }) 1099 } 1100 1101 if (sourcePath && sourcePath != null) { 1102 const cleanSourcePath: string = sourcePath.replace('.ets', '.js').replace('.ts', '.js'); 1103 if (!sourcemapNamesCollection.has(cleanSourcePath)) { 1104 sourcemapNamesCollection.set(cleanSourcePath, new Map()); 1105 } 1106 } 1107} 1108 1109function processInnerModule(content: string, systemValueCollection: Set<string>): string { 1110 systemValueCollection.forEach(element => { 1111 const target: string = element.trim() + '.default'; 1112 while (content.includes(target)) { 1113 content = content.replace(target, element.trim()); 1114 } 1115 }); 1116 return content; 1117} 1118 1119const VALIDATE_MODULE_REG: RegExp = new RegExp('^(' + VALIDATE_MODULE.join('|') + ')'); 1120function validateAllowListModule(moduleType: string, systemKey: string): boolean { 1121 return moduleType === 'ohos' && VALIDATE_MODULE_REG.test(systemKey); 1122} 1123 1124export function resetComponentCollection() { 1125 componentCollection.entryComponent = null; 1126 componentCollection.entryComponentPos = null; 1127 componentCollection.previewComponent = new Array(); 1128} 1129 1130function checkEntryComponent(node: ts.StructDeclaration, log: LogInfo[], sourceFile: ts.SourceFile): void { 1131 if (node.modifiers) { 1132 for (let i = 0; i < node.modifiers.length; i++) { 1133 if (node.modifiers[i].kind === ts.SyntaxKind.ExportKeyword) { 1134 const message: string = `It's not a recommended way to export struct with @Entry decorator, ` + 1135 `which may cause ACE Engine error in component preview mode.`; 1136 addLog(LogType.WARN, message, node.getStart(), log, sourceFile); 1137 break; 1138 } 1139 } 1140 } 1141} 1142 1143function validateStateVariable(node: ts.MethodDeclaration): void { 1144 if (node.decorators && node.decorators.length) { 1145 for (let i = 0; i < node.decorators.length; i++) { 1146 const decoratorName: string = node.decorators[i].getText().replace(/\(.*\)$/,'').trim(); 1147 if (CARD_ENABLE_DECORATORS[decoratorName]) { 1148 validatorCard(transformLog.errors, CARD_LOG_TYPE_DECORATORS, 1149 node.decorators[i].getStart(), decoratorName); 1150 } 1151 if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { 1152 transformLog.errors.push({ 1153 type: LogType.ERROR, 1154 message: `'${node.decorators[i].getText()}' can not decorate the method.`, 1155 pos: node.decorators[i].getStart() 1156 }); 1157 } 1158 } 1159 } 1160} 1161 1162export function getObservedPropertyCollection(className: string): Set<string> { 1163 const observedProperthCollection: Set<string> = new Set([ 1164 ...stateCollection.get(className), 1165 ...linkCollection.get(className), 1166 ...propCollection.get(className), 1167 ...storageLinkCollection.get(className), 1168 ...storageLinkCollection.get(className), 1169 ...provideCollection.get(className), 1170 ...consumeCollection.get(className), 1171 ...objectLinkCollection.get(className) 1172 ]); 1173 getLocalStorageCollection(className, observedProperthCollection); 1174 return observedProperthCollection; 1175} 1176 1177export function getLocalStorageCollection(componentName: string, collection: Set<string>): void { 1178 if (localStorageLinkCollection.get(componentName)) { 1179 for (const key of localStorageLinkCollection.get(componentName).keys()) { 1180 collection.add(key); 1181 } 1182 } 1183 if (localStoragePropCollection.get(componentName)) { 1184 for (const key of localStoragePropCollection.get(componentName).keys()) { 1185 collection.add(key); 1186 } 1187 } 1188} 1189