• 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}
178
179interface WarnCheckerResult {
180  count: number
181}
182
183interface WholeCache {
184  runtimeOS: string,
185  sdkInfo: string,
186  fileList: Cache
187}
188type Cache = Record<string, CacheFileName>;
189export let cache: Cache = {};
190export const hotReloadSupportFiles: Set<string> = new Set();
191export const shouldResolvedFiles: Set<string> = new Set();
192const allResolvedModules: Set<string> = new Set();
193
194let fastBuildLogger = null;
195
196export const checkerResult: CheckerResult = {count: 0};
197export const warnCheckerResult: WarnCheckerResult = {count: 0};
198export function serviceChecker(rootFileNames: string[], newLogger: any = null): void {
199  fastBuildLogger = newLogger;
200  let languageService: ts.LanguageService = null;
201  let cacheFile: string = null;
202  if (projectConfig.xtsMode) {
203    languageService = createLanguageService(rootFileNames);
204  } else {
205    cacheFile = path.resolve(projectConfig.cachePath, '../.ts_checker_cache');
206    const wholeCache: WholeCache = fs.existsSync(cacheFile) ?
207      JSON.parse(fs.readFileSync(cacheFile).toString()) :
208      {'runtimeOS': projectConfig.runtimeOS, 'sdkInfo': projectConfig.sdkInfo, 'fileList': {}};
209    if (wholeCache.runtimeOS === projectConfig.runtimeOS && wholeCache.sdkInfo === projectConfig.sdkInfo) {
210      cache = wholeCache.fileList;
211    } else {
212      cache = {};
213    }
214    const filterFiles: string[] = filterInput(rootFileNames);
215    languageService = createLanguageService(filterFiles);
216  }
217  globalProgram.program = languageService.getProgram();
218  const allDiagnostics: ts.Diagnostic[] = globalProgram.program
219    .getSyntacticDiagnostics()
220    .concat(globalProgram.program.getSemanticDiagnostics())
221    .concat(globalProgram.program.getDeclarationDiagnostics());
222  allDiagnostics.forEach((diagnostic: ts.Diagnostic) => {
223    printDiagnostic(diagnostic);
224  });
225  if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) {
226    fs.writeFileSync(cacheFile, JSON.stringify({
227      'runtimeOS': projectConfig.runtimeOS,
228      'sdkInfo': projectConfig.sdkInfo,
229      'fileList': cache
230    }, null, 2));
231  }
232  if (projectConfig.compileHar || projectConfig.compileShared) {
233    [...allResolvedModules, ...rootFileNames].forEach(moduleFile => {
234      if (!(moduleFile.match(new RegExp(projectConfig.packageDir)) && projectConfig.compileHar)) {
235        try {
236          const emit: any = languageService.getEmitOutput(moduleFile, true, true);
237          if (emit.outputFiles[0]) {
238            generateSourceFilesInHar(moduleFile, emit.outputFiles[0].text, '.d' + path.extname(moduleFile),
239              projectConfig);
240          } else {
241            console.warn(this.yellow,
242              "ArkTS:WARN doesn't generate .d" + path.extname(moduleFile) + ' for ' + moduleFile, this.reset);
243          }
244        } catch (err) {}
245      }
246    });
247  }
248}
249
250function isCardFile(file: string): boolean {
251  for (const key in projectConfig.cardEntryObj) {
252    if (path.normalize(projectConfig.cardEntryObj[key]) === path.normalize(file)) {
253      return true;
254    }
255  }
256  return false;
257}
258
259function containFormError(message: string): boolean {
260  if (/can't support form application./.test(message)) {
261    return true;
262  }
263  return false;
264}
265
266export function printDiagnostic(diagnostic: ts.Diagnostic): void {
267  const message: string = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
268  if (validateError(message)) {
269    if (process.env.watchMode !== 'true' && !projectConfig.xtsMode) {
270      updateErrorFileCache(diagnostic);
271    }
272
273    if (containFormError(message) && !isCardFile(diagnostic.file.fileName)) {
274        return;
275    }
276
277    const logPrefix: string = diagnostic.category === ts.DiagnosticCategory.Error ? 'ERROR' : 'WARN';
278    const etsCheckerLogger = fastBuildLogger ? fastBuildLogger : logger;
279    let logMessage: string;
280    if (logPrefix === 'ERROR') {
281      checkerResult.count += 1;
282    } else {
283      warnCheckerResult.count += 1;
284    }
285    if (diagnostic.file) {
286      const { line, character }: ts.LineAndCharacter =
287        diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
288      logMessage = `ArkTS:${logPrefix} File: ${diagnostic.file.fileName}:${line + 1}:${character + 1}\n ${message}\n`;
289    } else {
290      logMessage = `ArkTS:${logPrefix}: ${message}`;
291    }
292
293    if (diagnostic.category === ts.DiagnosticCategory.Error) {
294      etsCheckerLogger.error('\u001b[31m' + logMessage);
295    } else {
296      etsCheckerLogger.warn('\u001b[33m' + logMessage);
297    }
298  }
299}
300
301function validateError(message: string): boolean {
302  const propInfoReg: RegExp = /Cannot find name\s*'(\$?\$?[_a-zA-Z0-9]+)'/;
303  const stateInfoReg: RegExp = /Property\s*'(\$?[_a-zA-Z0-9]+)' does not exist on type/;
304  if (matchMessage(message, props, propInfoReg) ||
305    matchMessage(message, props, stateInfoReg)) {
306    return false;
307  }
308  return true;
309}
310function matchMessage(message: string, nameArr: any, reg: RegExp): boolean {
311  if (reg.test(message)) {
312    const match: string[] = message.match(reg);
313    if (match[1] && nameArr.includes(match[1])) {
314      return true;
315    }
316  }
317  return false;
318}
319
320function updateErrorFileCache(diagnostic: ts.Diagnostic): void {
321  if (diagnostic.file && cache[path.resolve(diagnostic.file.fileName)]) {
322    cache[path.resolve(diagnostic.file.fileName)].error = true;
323  }
324}
325
326function filterInput(rootFileNames: string[]): string[] {
327  return rootFileNames.filter((file: string) => {
328    const needUpdate: NeedUpdateFlag = { flag: false };
329    const alreadyCheckedFiles: Set<string> = new Set();
330    checkNeedUpdateFiles(path.resolve(file), needUpdate, alreadyCheckedFiles);
331    return needUpdate.flag;
332  });
333}
334
335function checkNeedUpdateFiles(file: string, needUpdate: NeedUpdateFlag, alreadyCheckedFiles: Set<string>): void {
336  if (alreadyCheckedFiles.has(file)) {
337    return;
338  } else {
339    alreadyCheckedFiles.add(file);
340  }
341
342  if (needUpdate.flag) {
343    return;
344  }
345
346  const value: CacheFileName = cache[file];
347  const mtimeMs: number = fs.statSync(file).mtimeMs;
348  if (value) {
349    if (value.error || value.mtimeMs !== mtimeMs) {
350      needUpdate.flag = true;
351      return;
352    }
353    for (let i = 0; i < value.children.length; ++i) {
354      if (fs.existsSync(value.children[i])) {
355        checkNeedUpdateFiles(value.children[i], needUpdate, alreadyCheckedFiles);
356      } else {
357        needUpdate.flag = true;
358      }
359    }
360  } else {
361    cache[file] = { mtimeMs, children: [], parent: [], error: false };
362    needUpdate.flag = true;
363  }
364}
365
366const resolvedModulesCache: Map<string, ts.ResolvedModuleFull[]> = new Map();
367
368export function resolveModuleNames(moduleNames: string[], containingFile: string): ts.ResolvedModuleFull[] {
369  const resolvedModules: ts.ResolvedModuleFull[] = [];
370  if (![...shouldResolvedFiles].length || shouldResolvedFiles.has(path.resolve(containingFile))
371    || !(resolvedModulesCache[path.resolve(containingFile)] &&
372      resolvedModulesCache[path.resolve(containingFile)].length === moduleNames.length)) {
373    for (const moduleName of moduleNames) {
374      const result = ts.resolveModuleName(moduleName, containingFile, compilerOptions, {
375        fileExists(fileName: string): boolean {
376          return ts.sys.fileExists(fileName);
377        },
378        readFile(fileName: string): string | undefined {
379          return ts.sys.readFile(fileName);
380        },
381        realpath(path: string): string {
382          return ts.sys.realpath(path);
383        }
384      });
385      if (result.resolvedModule) {
386        if (result.resolvedModule.resolvedFileName &&
387          path.extname(result.resolvedModule.resolvedFileName) === EXTNAME_JS) {
388          const resultDETSPath: string =
389            result.resolvedModule.resolvedFileName.replace(EXTNAME_JS, EXTNAME_D_ETS);
390          if (ts.sys.fileExists(resultDETSPath)) {
391            resolvedModules.push(getResolveModule(resultDETSPath, EXTNAME_D_ETS));
392          } else {
393            resolvedModules.push(result.resolvedModule);
394          }
395        } else {
396          resolvedModules.push(result.resolvedModule);
397        }
398      } else if (/^@(system|ohos)\./i.test(moduleName.trim())) {
399        const modulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ts');
400        if (systemModules.includes(moduleName + '.d.ts') && ts.sys.fileExists(modulePath)) {
401          resolvedModules.push(getResolveModule(modulePath, '.d.ts'));
402        } else {
403          resolvedModules.push(null);
404        }
405      } else if (/\.ets$/.test(moduleName) && !/\.d\.ets$/.test(moduleName)) {
406        const modulePath: string = path.resolve(path.dirname(containingFile), moduleName);
407        if (ts.sys.fileExists(modulePath)) {
408          resolvedModules.push(getResolveModule(modulePath, '.ets'));
409        } else {
410          resolvedModules.push(null);
411        }
412      } else if (/\.ts$/.test(moduleName)) {
413        const modulePath: string = path.resolve(path.dirname(containingFile), moduleName);
414        if (ts.sys.fileExists(modulePath)) {
415          resolvedModules.push(getResolveModule(modulePath, '.ts'));
416        } else {
417          resolvedModules.push(null);
418        }
419      } else {
420        const modulePath: string = path.resolve(__dirname, '../../../api', moduleName + '.d.ts');
421        const suffix: string = /\.js$/.test(moduleName) ? '' : '.js';
422        const jsModulePath: string = path.resolve(__dirname, '../node_modules', moduleName + suffix);
423        const fileModulePath: string =
424          path.resolve(__dirname, '../node_modules', moduleName + '/index.js');
425        const DETSModulePath: string = path.resolve(path.dirname(containingFile),
426          /\.d\.ets$/.test(moduleName) ? moduleName : moduleName + EXTNAME_D_ETS);
427        if (ts.sys.fileExists(modulePath)) {
428          resolvedModules.push(getResolveModule(modulePath, '.d.ts'));
429        } else if (ts.sys.fileExists(jsModulePath)) {
430          resolvedModules.push(getResolveModule(jsModulePath, '.js'));
431        } else if (ts.sys.fileExists(fileModulePath)) {
432          resolvedModules.push(getResolveModule(fileModulePath, '.js'));
433        } else if (ts.sys.fileExists(DETSModulePath)) {
434          resolvedModules.push(getResolveModule(DETSModulePath, '.d.ets'));
435        } else {
436          const srcIndex: number = projectConfig.projectPath.indexOf('src' + path.sep + 'main');
437          let DETSModulePathFromModule: string;
438          if (srcIndex > 0) {
439            DETSModulePathFromModule = path.resolve(
440              projectConfig.projectPath.substring(0, srcIndex), moduleName + path.sep + 'index' + EXTNAME_D_ETS);
441            if (DETSModulePathFromModule && ts.sys.fileExists(DETSModulePathFromModule)) {
442              resolvedModules.push(getResolveModule(DETSModulePathFromModule, '.d.ets'));
443            } else {
444              resolvedModules.push(null);
445            }
446          } else {
447            resolvedModules.push(null);
448          }
449        }
450      }
451      if (projectConfig.hotReload && resolvedModules.length &&
452        resolvedModules[resolvedModules.length - 1]) {
453        hotReloadSupportFiles.add(path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName));
454      }
455      if ((projectConfig.compileHar || projectConfig.compileShared) && resolvedModules[resolvedModules.length - 1] &&
456        path.resolve(resolvedModules[resolvedModules.length - 1].resolvedFileName).match(/(\.[^d]|[^\.]d|[^\.][^d])\.e?ts$/)) {
457        allResolvedModules.add(resolvedModules[resolvedModules.length - 1].resolvedFileName);
458      }
459    }
460    if (!projectConfig.xtsMode) {
461      createOrUpdateCache(resolvedModules, path.resolve(containingFile));
462    }
463    resolvedModulesCache[path.resolve(containingFile)] = resolvedModules;
464    return resolvedModules;
465  }
466  return resolvedModulesCache[path.resolve(containingFile)];
467}
468
469function createOrUpdateCache(resolvedModules: ts.ResolvedModuleFull[], containingFile: string): void {
470  const children: string[] = [];
471  const error: boolean = false;
472  resolvedModules.forEach(moduleObj => {
473    if (moduleObj && moduleObj.resolvedFileName && /(?<!\.d)\.(ets|ts)$/.test(moduleObj.resolvedFileName)) {
474      const file: string = path.resolve(moduleObj.resolvedFileName);
475      const mtimeMs: number = fs.statSync(file).mtimeMs;
476      children.push(file);
477      const value: CacheFileName = cache[file];
478      if (value) {
479        value.mtimeMs = mtimeMs;
480        value.error = error;
481        value.parent = value.parent || [];
482        value.parent.push(path.resolve(containingFile));
483        value.parent = [...new Set(value.parent)];
484      } else {
485        cache[file] = { mtimeMs, children: [], parent: [containingFile], error };
486      }
487    }
488  });
489  cache[path.resolve(containingFile)] = { mtimeMs: fs.statSync(containingFile).mtimeMs, children,
490    parent: cache[path.resolve(containingFile)] && cache[path.resolve(containingFile)].parent ?
491      cache[path.resolve(containingFile)].parent : [], error };
492}
493
494export function createWatchCompilerHost(rootFileNames: string[],
495  reportDiagnostic: ts.DiagnosticReporter, delayPrintLogCount: Function, resetErrorCount: Function,
496  isPipe: boolean = false): ts.WatchCompilerHostOfFilesAndCompilerOptions<ts.BuilderProgram> {
497  if (projectConfig.hotReload) {
498    rootFileNames.forEach(fileName => {
499      hotReloadSupportFiles.add(fileName);
500    });
501  }
502  setCompilerOptions();
503  const createProgram = ts.createSemanticDiagnosticsBuilderProgram;
504  const host = ts.createWatchCompilerHost(
505    [...rootFileNames, ...readDeaclareFiles()], compilerOptions,
506    ts.sys, createProgram, reportDiagnostic,
507    (diagnostic: ts.Diagnostic) => {
508      if ([6031, 6032].includes(diagnostic.code)) {
509        if (!isPipe) {
510          process.env.watchTs = 'start';
511          resetErrorCount();
512        }
513      }
514      // End of compilation in watch mode flag.
515      if ([6193, 6194].includes(diagnostic.code)) {
516        if (!isPipe) {
517          process.env.watchTs = 'end';
518          if (fastBuildLogger) {
519            fastBuildLogger.debug(TS_WATCH_END_MSG);
520            tsWatchEmitter.emit(TS_WATCH_END_MSG);
521          }
522        }
523        delayPrintLogCount();
524      }
525    });
526  host.readFile = (fileName: string) => {
527    if (!fs.existsSync(fileName)) {
528      return undefined;
529    }
530    if (/(?<!\.d)\.(ets|ts)$/.test(fileName)) {
531      let content: string = processContent(fs.readFileSync(fileName).toString(), fileName);
532      const extendFunctionInfo: extendInfo[] = [];
533      content = instanceInsteadThis(content, fileName, extendFunctionInfo);
534      return content;
535    }
536    return fs.readFileSync(fileName).toString();
537  };
538  host.resolveModuleNames = resolveModuleNames;
539  return host;
540}
541
542export function watchChecker(rootFileNames: string[], newLogger: any = null): void {
543  fastBuildLogger = newLogger;
544  globalProgram.watchProgram = ts.createWatchProgram(
545    createWatchCompilerHost(rootFileNames, printDiagnostic, () => {}, () => {}));
546}
547
548export function instanceInsteadThis(content: string, fileName: string, extendFunctionInfo: extendInfo[]): string {
549  checkUISyntax(content, fileName, extendFunctionInfo);
550  extendFunctionInfo.reverse().forEach((item) => {
551    const subStr: string = content.substring(item.start, item.end);
552    const insert: string = subStr.replace(/(\s)\$(\.)/g, (origin, item1, item2) => {
553      return item1 + item.compName + 'Instance' + item2;
554    });
555    content = content.slice(0, item.start) + insert + content.slice(item.end);
556  });
557  return content;
558}
559
560function getResolveModule(modulePath: string, type): ts.ResolvedModuleFull {
561  return {
562    resolvedFileName: modulePath,
563    isExternalLibraryImport: false,
564    extension: type
565  };
566}
567
568export const dollarCollection: Set<string> = new Set();
569export const decoratorParamsCollection: Set<string> = new Set();
570export const extendCollection: Set<string> = new Set();
571export const importModuleCollection: Set<string> = new Set();
572
573function checkUISyntax(source: string, fileName: string, extendFunctionInfo: extendInfo[]): void {
574  if (/\.ets$/.test(fileName)) {
575    if (process.env.compileMode === 'moduleJson' ||
576      path.resolve(fileName) !== path.resolve(projectConfig.projectPath, 'app.ets')) {
577      const sourceFile: ts.SourceFile = ts.createSourceFile(fileName, source,
578        ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS);
579      parseAllNode(sourceFile, sourceFile, extendFunctionInfo);
580      props.push(...dollarCollection, ...decoratorParamsCollection, ...extendCollection);
581    }
582  }
583}
584
585function parseAllNode(node: ts.Node, sourceFileNode: ts.SourceFile, extendFunctionInfo: extendInfo[]): void {
586  if (ts.isStructDeclaration(node)) {
587    if (node.members) {
588      node.members.forEach(item => {
589        if (ts.isPropertyDeclaration(item) && ts.isIdentifier(item.name)) {
590          const propertyName: string = item.name.getText();
591          if (item.decorators && item.decorators.length) {
592            for (let i = 0; i < item.decorators.length; i++) {
593              const decoratorName: string = item.decorators[i].getText().replace(/\(.*\)$/, '').trim();
594              if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) {
595                dollarCollection.add('$' + propertyName);
596              }
597              if (isDecoratorCollection(item.decorators[i], decoratorName)) {
598                decoratorParamsCollection.add(item.decorators[i].expression.arguments[0].getText());
599              }
600            }
601          }
602        }
603      });
604    }
605  }
606  if (ts.isMethodDeclaration(node) && node.name.getText() === COMPONENT_BUILD_FUNCTION ||
607    (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node)) &&
608    hasDecorator(node, COMPONENT_BUILDER_DECORATOR)) {
609    if (node.body && node.body.statements && node.body.statements.length) {
610      const checkProp: ts.NodeArray<ts.Statement> = node.body.statements;
611      checkProp.forEach((item, index) => {
612        traverseBuild(item, index);
613      });
614    }
615  }
616  if (ts.isFunctionDeclaration(node) && hasDecorator(node, COMPONENT_EXTEND_DECORATOR)) {
617    if (node.body && node.body.statements && node.body.statements.length &&
618      !isOriginalExtend(node.body)) {
619      extendFunctionInfo.push({start: node.pos, end: node.end, compName: isExtendFunction(node)});
620    }
621  }
622  node.getChildren().forEach((item: ts.Node) => parseAllNode(item, sourceFileNode, extendFunctionInfo));
623}
624
625function isForeachAndLzayForEach(node: ts.Node): boolean {
626  return ts.isCallExpression(node) && node.expression && ts.isIdentifier(node.expression) &&
627    FOREACH_LAZYFOREACH.has(node.expression.escapedText.toString()) && node.arguments && node.arguments[1] &&
628    ts.isArrowFunction(node.arguments[1]) && node.arguments[1].body && ts.isBlock(node.arguments[1].body);
629}
630
631function traverseBuild(node: ts.Node, index: number): void {
632  if (ts.isExpressionStatement(node)) {
633    let parentComponentName: string = getName(node);
634    if (!INNER_COMPONENT_NAMES.has(parentComponentName) && node.parent && node.parent.statements && index >= 1 &&
635      node.parent.statements[index - 1].expression && node.parent.statements[index - 1].expression.expression) {
636      parentComponentName = node.parent.statements[index - 1].expression.expression.escapedText;
637    }
638    node = node.expression;
639    if (ts.isEtsComponentExpression(node) && node.body && ts.isBlock(node.body) &&
640      ts.isIdentifier(node.expression) && !$$_BLOCK_INTERFACE.has(node.expression.escapedText.toString())) {
641      node.body.statements.forEach((item: ts.Statement, indexBlock: number) => {
642        traverseBuild(item, indexBlock);
643      });
644    } else if (isForeachAndLzayForEach(node)) {
645      node.arguments[1].body.statements.forEach((item: ts.Statement, indexBlock: number) => {
646        traverseBuild(item, indexBlock);
647      });
648    } else {
649      loopNodeFindDoubleDollar(node, parentComponentName);
650    }
651  } else if (ts.isIfStatement(node)) {
652    if (node.thenStatement && ts.isBlock(node.thenStatement) && node.thenStatement.statements) {
653      node.thenStatement.statements.forEach((item, indexIfBlock) => {
654        traverseBuild(item, indexIfBlock);
655      });
656    }
657    if (node.elseStatement && ts.isBlock(node.elseStatement) && node.elseStatement.statements) {
658      node.elseStatement.statements.forEach((item, indexElseBlock) => {
659        traverseBuild(item, indexElseBlock);
660      });
661    }
662  }
663}
664
665function isPropertiesAddDoubleDollar(node: ts.Node): boolean {
666  if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.arguments && node.arguments.length) {
667    return true;
668  } else if (ts.isEtsComponentExpression(node) && node.body && ts.isBlock(node.body) &&
669    ts.isIdentifier(node.expression) && $$_BLOCK_INTERFACE.has(node.expression.escapedText.toString())) {
670    return true;
671  } else {
672    return false;
673  }
674}
675function loopNodeFindDoubleDollar(node: ts.Node, parentComponentName: string): void {
676  while (node) {
677    if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
678      const argument: ts.NodeArray<ts.Node> = node.arguments;
679      const propertyName: ts.Identifier | ts.PrivateIdentifier = node.expression.name;
680      if (isCanAddDoubleDollar(propertyName.getText(), parentComponentName)) {
681        argument.forEach((item: ts.Node) => {
682          doubleDollarCollection(item);
683        });
684      }
685    } else if (isPropertiesAddDoubleDollar(node)) {
686      node.arguments.forEach((item: ts.Node) => {
687        if (ts.isObjectLiteralExpression(item) && item.properties && item.properties.length) {
688          item.properties.forEach((param: ts.Node) => {
689            if (isObjectPram(param, parentComponentName)) {
690              doubleDollarCollection(param.initializer);
691            }
692          });
693        }
694        if (STYLE_ADD_DOUBLE_DOLLAR.has(node.expression.getText()) && ts.isPropertyAccessExpression(item)) {
695          doubleDollarCollection(item);
696        }
697      });
698    }
699    node = node.expression;
700  }
701}
702
703function doubleDollarCollection(item: ts.Node): void {
704  if (item.getText().startsWith($$)) {
705    while (item.expression) {
706      item = item.expression;
707    }
708    dollarCollection.add(item.getText());
709  }
710}
711
712function isObjectPram(param: ts.Node, parentComponentName:string): boolean {
713  return ts.isPropertyAssignment(param) && param.name && ts.isIdentifier(param.name) &&
714    param.initializer && PROPERTIES_ADD_DOUBLE_DOLLAR.has(parentComponentName) &&
715    PROPERTIES_ADD_DOUBLE_DOLLAR.get(parentComponentName).has(param.name.getText());
716}
717
718function isCanAddDoubleDollar(propertyName: string, parentComponentName: string): boolean {
719  return PROPERTIES_ADD_DOUBLE_DOLLAR.has(parentComponentName) &&
720    PROPERTIES_ADD_DOUBLE_DOLLAR.get(parentComponentName).has(propertyName) ||
721    STYLE_ADD_DOUBLE_DOLLAR.has(propertyName);
722}
723
724function isDecoratorCollection(item: ts.Decorator, decoratorName: string): boolean {
725  return COMPONENT_DECORATORS_PARAMS.has(decoratorName) &&
726    // @ts-ignore
727    item.expression.arguments && item.expression.arguments.length &&
728    // @ts-ignore
729    ts.isIdentifier(item.expression.arguments[0]);
730}
731
732function processContent(source: string, id: string): string {
733  if (fastBuildLogger) {
734    source = visualTransform(source, id, fastBuildLogger);
735  }
736  source = preprocessExtend(source, extendCollection);
737  source = preprocessNewExtend(source, extendCollection);
738  return source;
739}
740
741function judgeFileShouldResolved(file: string, shouldResolvedFiles: Set<string>): void {
742  if (shouldResolvedFiles.has(file)) {
743    return;
744  }
745  shouldResolvedFiles.add(file);
746  if (cache && cache[file] && cache[file].parent) {
747    cache[file].parent.forEach((item) => {
748      judgeFileShouldResolved(item, shouldResolvedFiles);
749    });
750    cache[file].parent = [];
751  }
752  if (cache && cache[file] && cache[file].children) {
753    cache[file].children.forEach((item) => {
754      judgeFileShouldResolved(item, shouldResolvedFiles);
755    });
756    cache[file].children = [];
757  }
758}
759
760export function incrementWatchFile(watchModifiedFiles: string[],
761  watchRemovedFiles: string[]): void {
762  const changedFiles: string[] = [...watchModifiedFiles, ...watchRemovedFiles];
763  if (changedFiles.length) {
764    shouldResolvedFiles.clear();
765  }
766  changedFiles.forEach((file) => {
767    judgeFileShouldResolved(file, shouldResolvedFiles);
768  });
769}
770