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