1/* 2 * Copyright (c) 2022 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 fs from 'fs'; 17import path from 'path'; 18import * as ts from 'typescript'; 19 20import { 21 projectConfig, 22 systemModules, 23 globalProgram 24} from '../main'; 25import { 26 processSystemApi, 27 preprocessExtend, 28 preprocessNewExtend 29} from './validate_ui_syntax'; 30import { 31 INNER_COMPONENT_MEMBER_DECORATORS, 32 COMPONENT_DECORATORS_PARAMS, 33 COMPONENT_BUILD_FUNCTION, 34 STYLE_ADD_DOUBLE_DOLLAR, 35 $$, 36 PROPERTIES_ADD_DOUBLE_DOLLAR, 37 $$_BLOCK_INTERFACE, 38 COMPONENT_EXTEND_DECORATOR, 39 COMPONENT_BUILDER_DECORATOR, 40 FOREACH_LAZYFOREACH, 41 EXTNAME_D_ETS, 42 EXTNAME_JS 43} from './pre_define'; 44import { getName } from './process_component_build'; 45import { INNER_COMPONENT_NAMES } from './component_map'; 46import { 47 props, 48 logger 49} from './compile_info'; 50import { hasDecorator } from './utils'; 51import { generateSourceFilesInHar } from './utils'; 52import { isExtendFunction, isOriginalExtend } from './process_ui_syntax'; 53 54export function readDeaclareFiles(): string[] { 55 const declarationsFileNames: string[] = []; 56 fs.readdirSync(path.resolve(__dirname, '../declarations')) 57 .forEach((fileName: string) => { 58 if (/\.d\.ts$/.test(fileName)) { 59 declarationsFileNames.push(path.resolve(__dirname, '../declarations', fileName)); 60 } 61 }); 62 return declarationsFileNames; 63} 64 65const compilerOptions: ts.CompilerOptions = ts.readConfigFile( 66 path.resolve(__dirname, '../tsconfig.json'), ts.sys.readFile).config.compilerOptions; 67function setCompilerOptions() { 68 const allPath: Array<string> = [ 69 '*' 70 ]; 71 if (!projectConfig.aceModuleJsonPath) { 72 allPath.push('../../../../../*'); 73 allPath.push('../../*'); 74 } else { 75 allPath.push('../../../../*'); 76 allPath.push('../*'); 77 } 78 Object.assign(compilerOptions, { 79 'allowJs': false, 80 'importsNotUsedAsValues': ts.ImportsNotUsedAsValues.Preserve, 81 'module': ts.ModuleKind.CommonJS, 82 'moduleResolution': ts.ModuleResolutionKind.NodeJs, 83 'noEmit': true, 84 'target': ts.ScriptTarget.ES2017, 85 'baseUrl': path.resolve(projectConfig.projectPath), 86 'paths': { 87 '*': allPath 88 }, 89 'lib': [ 90 'lib.es2020.d.ts' 91 ] 92 }); 93 if (projectConfig.packageDir === 'oh_modules') { 94 Object.assign(compilerOptions, { 95 'packageManagerType': 'ohpm' 96 }); 97 } 98} 99 100interface extendInfo { 101 start: number, 102 end: number, 103 compName: string 104} 105 106export function createLanguageService(rootFileNames: string[]): ts.LanguageService { 107 setCompilerOptions(); 108 const files: ts.MapLike<{ version: number }> = {}; 109 const servicesHost: ts.LanguageServiceHost = { 110 getScriptFileNames: () => [...rootFileNames, ...readDeaclareFiles()], 111 getScriptVersion: fileName => 112 files[fileName] && files[fileName].version.toString(), 113 getScriptSnapshot: fileName => { 114 if (!fs.existsSync(fileName)) { 115 return undefined; 116 } 117 if (/(?<!\.d)\.(ets|ts)$/.test(fileName)) { 118 let content: string = processContent(fs.readFileSync(fileName).toString()); 119 const extendFunctionInfo: extendInfo[] = []; 120 content = instanceInsteadThis(content, fileName, extendFunctionInfo); 121 return ts.ScriptSnapshot.fromString(content); 122 } 123 return ts.ScriptSnapshot.fromString(fs.readFileSync(fileName).toString()); 124 }, 125 getCurrentDirectory: () => process.cwd(), 126 getCompilationSettings: () => compilerOptions, 127 getDefaultLibFileName: options => ts.getDefaultLibFilePath(options), 128 fileExists: ts.sys.fileExists, 129 readFile: ts.sys.readFile, 130 readDirectory: ts.sys.readDirectory, 131 resolveModuleNames: resolveModuleNames, 132 directoryExists: ts.sys.directoryExists, 133 getDirectories: ts.sys.getDirectories, 134 getTagNameNeededCheckByFile: (fileName, sourceFileName) => { 135 let needCheckResult: boolean = false; 136 if ((/compiler\/declarations/.test(sourceFileName) || /ets-loader\/declarations/.test(sourceFileName)) && 137 isCardFile(fileName)) { 138 needCheckResult = true; 139 } 140 return { 141 needCheck: needCheckResult, 142 checkConfig: [{ 143 tagName: "form", 144 message: "'{0}' can't support form application.", 145 needConditionCheck: false, 146 type: ts.DiagnosticCategory.Error, 147 specifyCheckConditionFuncName: '', 148 tagNameShouldExisted: true 149 }] 150 } 151 } 152 }; 153 return ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); 154} 155 156interface CacheFileName { 157 mtimeMs: number, 158 children: string[], 159 parent: string[], 160 error: boolean 161} 162interface NeedUpdateFlag { 163 flag: boolean; 164} 165interface CheckerResult { 166 count: number 167} 168interface WholeCache { 169 runtimeOS: string, 170 sdkInfo: string, 171 fileList: Cache 172} 173type Cache = Record<string, CacheFileName>; 174export let cache: Cache = {}; 175export const hotReloadSupportFiles: Set<string> = new Set(); 176export const shouldResolvedFiles: Set<string> = new Set(); 177const allResolvedModules: Set<string> = new Set(); 178 179let fastBuildLogger = null; 180 181export const checkerResult: CheckerResult = {count: 0}; 182export function serviceChecker(rootFileNames: string[], newLogger: any = null): void { 183 fastBuildLogger = newLogger; 184 let languageService: ts.LanguageService = null; 185 let cacheFile: string = null; 186 if (projectConfig.xtsMode) { 187 languageService = createLanguageService(rootFileNames); 188 } else { 189 cacheFile = path.resolve(projectConfig.cachePath, '../.ts_checker_cache'); 190 const wholeCache: WholeCache = fs.existsSync(cacheFile) ? 191 JSON.parse(fs.readFileSync(cacheFile).toString()) : 192 {'runtimeOS': projectConfig.runtimeOS, 'sdkInfo': projectConfig.sdkInfo, 'fileList': {}}; 193 if (wholeCache.runtimeOS === projectConfig.runtimeOS && wholeCache.sdkInfo === projectConfig.sdkInfo) { 194 cache = wholeCache.fileList; 195 } else { 196 cache = {}; 197 } 198 const filterFiles: string[] = filterInput(rootFileNames); 199 languageService = createLanguageService(filterFiles); 200 } 201 globalProgram.program = languageService.getProgram(); 202 const allDiagnostics: ts.Diagnostic[] = globalProgram.program 203 .getSyntacticDiagnostics() 204 .concat(globalProgram.program.getSemanticDiagnostics()) 205 .concat(globalProgram.program.getDeclarationDiagnostics()); 206 allDiagnostics.forEach((diagnostic: ts.Diagnostic) => { 207 printDiagnostic(diagnostic); 208 }); 209 if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) { 210 fs.writeFileSync(cacheFile, JSON.stringify({ 211 'runtimeOS': projectConfig.runtimeOS, 212 'sdkInfo': projectConfig.sdkInfo, 213 'fileList': cache 214 }, null, 2)); 215 } 216 if (projectConfig.compileHar || projectConfig.compileShared) { 217 [...allResolvedModules, ...rootFileNames].forEach(moduleFile => { 218 if (!(moduleFile.match(new RegExp(projectConfig.packageDir)) && projectConfig.compileHar)) { 219 try { 220 const emit: any = languageService.getEmitOutput(moduleFile, true, true); 221 if (emit.outputFiles[0]) { 222 generateSourceFilesInHar(moduleFile, emit.outputFiles[0].text, '.d' + path.extname(moduleFile), 223 projectConfig); 224 } else { 225 console.warn(this.yellow, 226 "ArkTS:WARN doesn't generate .d" + path.extname(moduleFile) + ' for ' + moduleFile, this.reset); 227 } 228 } catch (err) {} 229 } 230 }); 231 } 232} 233 234function isCardFile(file: string): boolean { 235 for (const key in projectConfig.cardEntryObj) { 236 if (path.normalize(projectConfig.cardEntryObj[key]) === path.normalize(file)) { 237 return true; 238 } 239 } 240 return false; 241} 242 243function containFormError(message: string): boolean { 244 if (/can't support form application./.test(message)) { 245 return true; 246 } 247 return false; 248} 249 250export function printDiagnostic(diagnostic: ts.Diagnostic): void { 251 const message: string = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); 252 if (validateError(message)) { 253 if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) { 254 updateErrorFileCache(diagnostic); 255 } 256 257 if (containFormError(message) && !isCardFile(diagnostic.file.fileName)) { 258 return; 259 } 260 261 checkerResult.count += 1; 262 if (diagnostic.file) { 263 const { line, character }: ts.LineAndCharacter = 264 diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!); 265 fastBuildLogger ? 266 fastBuildLogger.error('\u001b[31m' + 267 `ArkTS:ERROR File: ${diagnostic.file.fileName}:${line + 1}:${character + 1}\n ${message}\n`) : 268 logger.error('\u001b[31m', 269 `ArkTS:ERROR File: ${diagnostic.file.fileName}:${line + 1}:${character + 1}\n ${message}\n`); 270 } else { 271 fastBuildLogger ? fastBuildLogger.error('\u001b[31m' + `ArkTS:ERROR: ${message}`) : 272 logger.error('\u001b[31m', `ArkTS:ERROR: ${message}`); 273 } 274 } 275} 276 277function validateError(message: string): boolean { 278 const propInfoReg: RegExp = /Cannot find name\s*'(\$?\$?[_a-zA-Z0-9]+)'/; 279 const stateInfoReg: RegExp = /Property\s*'(\$?[_a-zA-Z0-9]+)' does not exist on type/; 280 if (matchMessage(message, props, propInfoReg) || 281 matchMessage(message, props, stateInfoReg)) { 282 return false; 283 } 284 return true; 285} 286function matchMessage(message: string, nameArr: any, reg: RegExp): boolean { 287 if (reg.test(message)) { 288 const match: string[] = message.match(reg); 289 if (match[1] && nameArr.includes(match[1])) { 290 return true; 291 } 292 } 293 return false; 294} 295 296function updateErrorFileCache(diagnostic: ts.Diagnostic): void { 297 if (diagnostic.file && cache[path.resolve(diagnostic.file.fileName)]) { 298 cache[path.resolve(diagnostic.file.fileName)].error = true; 299 } 300} 301 302function filterInput(rootFileNames: string[]): string[] { 303 return rootFileNames.filter((file: string) => { 304 const needUpdate: NeedUpdateFlag = { flag: false }; 305 const alreadyCheckedFiles: Set<string> = new Set(); 306 checkNeedUpdateFiles(path.resolve(file), needUpdate, alreadyCheckedFiles); 307 return needUpdate.flag; 308 }); 309} 310 311function checkNeedUpdateFiles(file: string, needUpdate: NeedUpdateFlag, alreadyCheckedFiles: Set<string>): void { 312 if (alreadyCheckedFiles.has(file)) { 313 return; 314 } else { 315 alreadyCheckedFiles.add(file); 316 } 317 318 if (needUpdate.flag) { 319 return; 320 } 321 322 const value: CacheFileName = cache[file]; 323 const mtimeMs: number = fs.statSync(file).mtimeMs; 324 if (value) { 325 if (value.error || value.mtimeMs !== mtimeMs) { 326 needUpdate.flag = true; 327 return; 328 } 329 for (let i = 0; i < value.children.length; ++i) { 330 if (fs.existsSync(value.children[i])) { 331 checkNeedUpdateFiles(value.children[i], needUpdate, alreadyCheckedFiles); 332 } else { 333 needUpdate.flag = true; 334 } 335 } 336 } else { 337 cache[file] = { mtimeMs, children: [], parent: [], error: false }; 338 needUpdate.flag = true; 339 } 340} 341 342const resolvedModulesCache: Map<string, ts.ResolvedModuleFull[]> = new Map(); 343 344function resolveModuleNames(moduleNames: string[], containingFile: string): ts.ResolvedModuleFull[] { 345 const resolvedModules: ts.ResolvedModuleFull[] = []; 346 if (![...shouldResolvedFiles].length || shouldResolvedFiles.has(path.resolve(containingFile)) 347 || !(resolvedModulesCache[path.resolve(containingFile)] && 348 resolvedModulesCache[path.resolve(containingFile)].length === moduleNames.length)) { 349 for (const moduleName of moduleNames) { 350 const result = ts.resolveModuleName(moduleName, containingFile, compilerOptions, { 351 fileExists(fileName: string): boolean { 352 return ts.sys.fileExists(fileName); 353 }, 354 readFile(fileName: string): string | undefined { 355 return ts.sys.readFile(fileName); 356 }, 357 realpath(path: string): string { 358 return ts.sys.realpath(path); 359 } 360 }); 361 if (result.resolvedModule) { 362 if (result.resolvedModule.resolvedFileName && 363 path.extname(result.resolvedModule.resolvedFileName) === EXTNAME_JS) { 364 const resultDETSPath: string = 365 result.resolvedModule.resolvedFileName.replace(EXTNAME_JS, EXTNAME_D_ETS); 366 if (ts.sys.fileExists(resultDETSPath)) { 367 resolvedModules.push(getResolveModule(resultDETSPath, EXTNAME_D_ETS)); 368 } else { 369 resolvedModules.push(result.resolvedModule); 370 } 371 } else { 372 resolvedModules.push(result.resolvedModule); 373 } 374 } else if (/^@(system|ohos)\./i.test(moduleName.trim())) { 375 const modulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ts'); 376 if (systemModules.includes(moduleName + '.d.ts') && ts.sys.fileExists(modulePath)) { 377 resolvedModules.push(getResolveModule(modulePath, '.d.ts')); 378 } else { 379 resolvedModules.push(null); 380 } 381 } else if (/\.ets$/.test(moduleName) && !/\.d\.ets$/.test(moduleName)) { 382 const modulePath: string = path.resolve(path.dirname(containingFile), moduleName); 383 if (ts.sys.fileExists(modulePath)) { 384 resolvedModules.push(getResolveModule(modulePath, '.ets')); 385 } else { 386 resolvedModules.push(null); 387 } 388 } else if (/\.ts$/.test(moduleName)) { 389 const modulePath: string = path.resolve(path.dirname(containingFile), moduleName); 390 if (ts.sys.fileExists(modulePath)) { 391 resolvedModules.push(getResolveModule(modulePath, '.ts')); 392 } else { 393 resolvedModules.push(null); 394 } 395 } else { 396 const modulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ts'); 397 const suffix: string = /\.js$/.test(moduleName) ? '' : '.js'; 398 const jsModulePath: string = path.resolve(__dirname, '../node_modules', moduleName + suffix); 399 const fileModulePath: string = 400 path.resolve(__dirname, '../node_modules', moduleName + '/index.js'); 401 const DETSModulePath: string = path.resolve(path.dirname(containingFile), 402 /\.d\.ets$/.test(moduleName) ? moduleName : moduleName + EXTNAME_D_ETS); 403 if (ts.sys.fileExists(modulePath)) { 404 resolvedModules.push(getResolveModule(modulePath, '.d.ts')); 405 } else if (ts.sys.fileExists(jsModulePath)) { 406 resolvedModules.push(getResolveModule(jsModulePath, '.js')); 407 } else if (ts.sys.fileExists(fileModulePath)) { 408 resolvedModules.push(getResolveModule(fileModulePath, '.js')); 409 } else if (ts.sys.fileExists(DETSModulePath)) { 410 resolvedModules.push(getResolveModule(DETSModulePath, '.d.ets')); 411 } else { 412 const srcIndex: number = projectConfig.projectPath.indexOf('src' + path.sep + 'main'); 413 let DETSModulePathFromModule: string; 414 if (srcIndex > 0) { 415 DETSModulePathFromModule = path.resolve( 416 projectConfig.projectPath.substring(0, srcIndex), moduleName + path.sep + 'index' + EXTNAME_D_ETS); 417 if (DETSModulePathFromModule && ts.sys.fileExists(DETSModulePathFromModule)) { 418 resolvedModules.push(getResolveModule(DETSModulePathFromModule, '.d.ets')); 419 } else { 420 resolvedModules.push(null); 421 } 422 } else { 423 resolvedModules.push(null); 424 } 425 } 426 } 427 if (projectConfig.hotReload && resolvedModules.length && 428 resolvedModules[resolvedModules.length - 1]) { 429 hotReloadSupportFiles.add(path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName)); 430 } 431 if ((projectConfig.compileHar || projectConfig.compileShared) && resolvedModules[resolvedModules.length - 1] && 432 path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName).match(/(\.[^d]|[^\.]d|[^\.][^d])\.e?ts$/)) { 433 allResolvedModules.add(resolvedModules[resolvedModules.length - 1].resolvedFileName); 434 } 435 } 436 if (!projectConfig.xtsMode) { 437 createOrUpdateCache(resolvedModules, path.resolve(containingFile)); 438 } 439 resolvedModulesCache[path.resolve(containingFile)] = resolvedModules; 440 return resolvedModules; 441 } 442 return resolvedModulesCache[path.resolve(containingFile)]; 443} 444 445function createOrUpdateCache(resolvedModules: ts.ResolvedModuleFull[], containingFile: string): void { 446 const children: string[] = []; 447 const error: boolean = false; 448 resolvedModules.forEach(moduleObj => { 449 if (moduleObj && moduleObj.resolvedFileName && /(?<!\.d)\.(ets|ts)$/.test(moduleObj.resolvedFileName)) { 450 const file: string = path.resolve(moduleObj.resolvedFileName); 451 const mtimeMs: number = fs.statSync(file).mtimeMs; 452 children.push(file); 453 const value: CacheFileName = cache[file]; 454 if (value) { 455 value.mtimeMs = mtimeMs; 456 value.error = error; 457 value.parent = value.parent || []; 458 value.parent.push(path.resolve(containingFile)); 459 value.parent = [...new Set(value.parent)]; 460 } else { 461 cache[file] = { mtimeMs, children: [], parent: [containingFile], error }; 462 } 463 } 464 }); 465 cache[path.resolve(containingFile)] = { mtimeMs: fs.statSync(containingFile).mtimeMs, children, 466 parent: cache[path.resolve(containingFile)] && cache[path.resolve(containingFile)].parent ? 467 cache[path.resolve(containingFile)].parent : [], error }; 468} 469 470export function createWatchCompilerHost(rootFileNames: string[], 471 reportDiagnostic: ts.DiagnosticReporter, delayPrintLogCount: Function, resetErrorCount: Function, 472 isPipe: boolean = false): ts.WatchCompilerHostOfFilesAndCompilerOptions<ts.BuilderProgram> { 473 if (projectConfig.hotReload) { 474 rootFileNames.forEach(fileName => { 475 hotReloadSupportFiles.add(fileName); 476 }); 477 } 478 setCompilerOptions(); 479 const createProgram = ts.createSemanticDiagnosticsBuilderProgram; 480 const host = ts.createWatchCompilerHost( 481 [...rootFileNames, ...readDeaclareFiles()], compilerOptions, 482 ts.sys, createProgram, reportDiagnostic, 483 (diagnostic: ts.Diagnostic) => { 484 if ([6031, 6032].includes(diagnostic.code)) { 485 if (!isPipe) { 486 process.env.watchTs = 'start'; 487 resetErrorCount(); 488 } 489 } 490 // End of compilation in watch mode flag. 491 if ([6193, 6194].includes(diagnostic.code)) { 492 if (!isPipe) { 493 process.env.watchTs = 'end'; 494 if (fastBuildLogger) { 495 fastBuildLogger.debug('TS Watch End'); 496 } 497 } 498 delayPrintLogCount(); 499 } 500 }); 501 host.readFile = (fileName: string) => { 502 if (!fs.existsSync(fileName)) { 503 return undefined; 504 } 505 if (/(?<!\.d)\.(ets|ts)$/.test(fileName)) { 506 let content: string = processContent(fs.readFileSync(fileName).toString()); 507 const extendFunctionInfo: extendInfo[] = []; 508 content = instanceInsteadThis(content, fileName, extendFunctionInfo); 509 return content; 510 } 511 return fs.readFileSync(fileName).toString(); 512 }; 513 host.resolveModuleNames = resolveModuleNames; 514 return host; 515} 516 517export function watchChecker(rootFileNames: string[], newLogger: any = null): void { 518 fastBuildLogger = newLogger; 519 globalProgram.watchProgram = ts.createWatchProgram( 520 createWatchCompilerHost(rootFileNames, printDiagnostic, () => {}, () => {})); 521} 522 523function instanceInsteadThis(content: string, fileName: string, extendFunctionInfo: extendInfo[]): string { 524 checkUISyntax(content, fileName, extendFunctionInfo); 525 extendFunctionInfo.reverse().forEach((item) => { 526 const subStr: string = content.substring(item.start, item.end); 527 const insert: string = subStr.replace(/(\s)\$(\.)/g, (origin, item1, item2) => { 528 return item1 + item.compName + 'Instance' + item2; 529 }); 530 content = content.slice(0, item.start) + insert + content.slice(item.end); 531 }); 532 return content; 533} 534 535function getResolveModule(modulePath: string, type): ts.ResolvedModuleFull { 536 return { 537 resolvedFileName: modulePath, 538 isExternalLibraryImport: false, 539 extension: type 540 }; 541} 542 543export const dollarCollection: Set<string> = new Set(); 544export const decoratorParamsCollection: Set<string> = new Set(); 545export const extendCollection: Set<string> = new Set(); 546export const importModuleCollection: Set<string> = new Set(); 547 548function checkUISyntax(source: string, fileName: string, extendFunctionInfo: extendInfo[]): void { 549 if (/\.ets$/.test(fileName)) { 550 if (process.env.compileMode === 'moduleJson' || 551 path.resolve(fileName) !== path.resolve(projectConfig.projectPath, 'app.ets')) { 552 const sourceFile: ts.SourceFile = ts.createSourceFile(fileName, source, 553 ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS); 554 parseAllNode(sourceFile, sourceFile, extendFunctionInfo); 555 props.push(...dollarCollection, ...decoratorParamsCollection, ...extendCollection); 556 } 557 } 558} 559 560function parseAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, extendFunctionInfo: extendInfo[]): void { 561 if (ts.isStructDeclaration(node)) { 562 if (node.members) { 563 node.members.forEach(item => { 564 if (ts.isPropertyDeclaration(item) && ts.isIdentifier(item.name)) { 565 const propertyName: string = item.name.getText(); 566 if (item.decorators && item.decorators.length) { 567 for (let i = 0; i < item.decorators.length; i++) { 568 const decoratorName: string = item.decorators[i].getText().replace(/\(.*\)$/, '').trim(); 569 if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { 570 dollarCollection.add('$' + propertyName); 571 } 572 if (isDecoratorCollection(item.decorators[i], decoratorName)) { 573 decoratorParamsCollection.add(item.decorators[i].expression.arguments[0].getText()); 574 } 575 } 576 } 577 } 578 }); 579 } 580 } 581 if (ts.isMethodDeclaration(node) && node.name.getText() === COMPONENT_BUILD_FUNCTION || 582 (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node)) && 583 hasDecorator(node, COMPONENT_BUILDER_DECORATOR)) { 584 if (node.body && node.body.statements && node.body.statements.length) { 585 const checkProp: ts.NodeArray<ts.Statement> = node.body.statements; 586 checkProp.forEach((item, index) => { 587 traverseBuild(item, index); 588 }); 589 } 590 } 591 if (ts.isFunctionDeclaration(node) && hasDecorator(node, COMPONENT_EXTEND_DECORATOR)) { 592 if (node.body && node.body.statements && node.body.statements.length && 593 !isOriginalExtend(node.body)) { 594 extendFunctionInfo.push({start: node.pos, end: node.end, compName: isExtendFunction(node)}); 595 } 596 } 597 node.getChildren().forEach((item: ts.Node) => parseAllNode(item, sourceFileNode, extendFunctionInfo)); 598} 599 600function isForeachAndLzayForEach(node: ts.Node): boolean { 601 return ts.isCallExpression(node) && node.expression && ts.isIdentifier(node.expression) && 602 FOREACH_LAZYFOREACH.has(node.expression.escapedText.toString()) && node.arguments && node.arguments[1] && 603 ts.isArrowFunction(node.arguments[1]) && node.arguments[1].body && ts.isBlock(node.arguments[1].body); 604} 605 606function traverseBuild(node: ts.Node, index: number): void { 607 if (ts.isExpressionStatement(node)) { 608 let parentComponentName: string = getName(node); 609 if (!INNER_COMPONENT_NAMES.has(parentComponentName) && node.parent && node.parent.statements && index >= 1 && 610 node.parent.statements[index - 1].expression && node.parent.statements[index - 1].expression.expression) { 611 parentComponentName = node.parent.statements[index - 1].expression.expression.escapedText; 612 } 613 node = node.expression; 614 if (ts.isEtsComponentExpression(node) && node.body && ts.isBlock(node.body) && 615 ts.isIdentifier(node.expression) && !$$_BLOCK_INTERFACE.has(node.expression.escapedText.toString())) { 616 node.body.statements.forEach((item: ts.Statement, indexBlock: number) => { 617 traverseBuild(item, indexBlock); 618 }); 619 } else if (isForeachAndLzayForEach(node)) { 620 node.arguments[1].body.statements.forEach((item: ts.Statement, indexBlock: number) => { 621 traverseBuild(item, indexBlock); 622 }); 623 } else { 624 loopNodeFindDoubleDollar(node, parentComponentName); 625 } 626 } else if (ts.isIfStatement(node)) { 627 if (node.thenStatement && ts.isBlock(node.thenStatement) && node.thenStatement.statements) { 628 node.thenStatement.statements.forEach((item, indexIfBlock) => { 629 traverseBuild(item, indexIfBlock); 630 }); 631 } 632 if (node.elseStatement && ts.isBlock(node.elseStatement) && node.elseStatement.statements) { 633 node.elseStatement.statements.forEach((item, indexElseBlock) => { 634 traverseBuild(item, indexElseBlock); 635 }); 636 } 637 } 638} 639 640function isPropertiesAddDoubleDollar(node: ts.Node): boolean { 641 if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.arguments && node.arguments.length) { 642 return true; 643 } else if (ts.isEtsComponentExpression(node) && node.body && ts.isBlock(node.body) && 644 ts.isIdentifier(node.expression) && $$_BLOCK_INTERFACE.has(node.expression.escapedText.toString())) { 645 return true; 646 } else { 647 return false; 648 } 649} 650function loopNodeFindDoubleDollar(node: ts.Node, parentComponentName: string): void { 651 while (node) { 652 if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) { 653 const argument: ts.NodeArray<ts.Node> = node.arguments; 654 const propertyName: ts.Identifier | ts.PrivateIdentifier = node.expression.name; 655 if (isCanAddDoubleDollar(propertyName.getText(), parentComponentName)) { 656 argument.forEach((item: ts.Node) => { 657 doubleDollarCollection(item); 658 }); 659 } 660 } else if (isPropertiesAddDoubleDollar(node)) { 661 node.arguments.forEach((item: ts.Node) => { 662 if (ts.isObjectLiteralExpression(item) && item.properties && item.properties.length) { 663 item.properties.forEach((param: ts.Node) => { 664 if (isObjectPram(param, parentComponentName)) { 665 doubleDollarCollection(param.initializer); 666 } 667 }); 668 } 669 if (STYLE_ADD_DOUBLE_DOLLAR.has(node.expression.getText()) && ts.isPropertyAccessExpression(item)) { 670 doubleDollarCollection(item); 671 } 672 }); 673 } 674 node = node.expression; 675 } 676} 677 678function doubleDollarCollection(item: ts.Node): void { 679 if (item.getText().startsWith($$)) { 680 while (item.expression) { 681 item = item.expression; 682 } 683 dollarCollection.add(item.getText()); 684 } 685} 686 687function isObjectPram(param: ts.Node, parentComponentName:string): boolean { 688 return ts.isPropertyAssignment(param) && param.name && ts.isIdentifier(param.name) && 689 param.initializer && PROPERTIES_ADD_DOUBLE_DOLLAR.has(parentComponentName) && 690 PROPERTIES_ADD_DOUBLE_DOLLAR.get(parentComponentName).has(param.name.getText()); 691} 692 693function isCanAddDoubleDollar(propertyName: string, parentComponentName: string): boolean { 694 return PROPERTIES_ADD_DOUBLE_DOLLAR.has(parentComponentName) && 695 PROPERTIES_ADD_DOUBLE_DOLLAR.get(parentComponentName).has(propertyName) || 696 STYLE_ADD_DOUBLE_DOLLAR.has(propertyName); 697} 698 699function isDecoratorCollection(item: ts.Decorator, decoratorName: string): boolean { 700 return COMPONENT_DECORATORS_PARAMS.has(decoratorName) && 701 // @ts-ignore 702 item.expression.arguments && item.expression.arguments.length && 703 // @ts-ignore 704 ts.isIdentifier(item.expression.arguments[0]); 705} 706 707function processContent(source: string): string { 708 source = processSystemApi(source, false); 709 source = preprocessExtend(source, extendCollection); 710 source = preprocessNewExtend(source, extendCollection); 711 return source; 712} 713 714function judgeFileShouldResolved(file: string, shouldResolvedFiles: Set<string>): void { 715 if (shouldResolvedFiles.has(file)) { 716 return; 717 } 718 shouldResolvedFiles.add(file); 719 if (cache && cache[file] && cache[file].parent) { 720 cache[file].parent.forEach((item) => { 721 judgeFileShouldResolved(item, shouldResolvedFiles); 722 }); 723 cache[file].parent = []; 724 } 725 if (cache && cache[file] && cache[file].children) { 726 cache[file].children.forEach((item) => { 727 judgeFileShouldResolved(item, shouldResolvedFiles); 728 }); 729 cache[file].children = []; 730 } 731} 732 733export function incrementWatchFile(watchModifiedFiles: string[], 734 watchRemovedFiles: string[]): void { 735 const changedFiles: string[] = [...watchModifiedFiles, ...watchRemovedFiles]; 736 if (changedFiles.length) { 737 shouldResolvedFiles.clear(); 738 } 739 changedFiles.forEach((file) => { 740 judgeFileShouldResolved(file, shouldResolvedFiles); 741 }); 742} 743