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