• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 fs from 'fs';
18import path from 'path';
19import JSON5 from 'json5';
20
21import {
22  EXTNAME_ETS,
23  EXTNAME_TS,
24  INDEX_ETS,
25  INDEX_TS,
26  CUSTOM_COMPONENT_DEFAULT,
27  CUSTOM_DECORATOR_NAME,
28  COMPONENT_DECORATOR_ENTRY,
29  COMPONENT_BUILDER_DECORATOR,
30  DECORATOR_REUSEABLE
31} from './pre_define';
32import {
33  propertyCollection,
34  linkCollection,
35  componentCollection,
36  preprocessExtend,
37  preprocessNewExtend,
38  processSystemApi,
39  propCollection,
40  isObservedClass,
41  isCustomDialogClass,
42  observedClassCollection,
43  enumCollection,
44  getComponentSet,
45  IComponentSet,
46  builderParamObjectCollection,
47  stateCollection,
48  regularCollection,
49  storagePropCollection,
50  storageLinkCollection,
51  provideCollection,
52  consumeCollection,
53  objectLinkCollection,
54  localStorageLinkCollection,
55  localStoragePropCollection,
56  builderParamInitialization,
57  propInitialization
58} from './validate_ui_syntax';
59import {
60  getExtensionIfUnfullySpecifiedFilepath,
61  hasDecorator,
62  LogInfo,
63  LogType,
64  storedFileInfo
65} from './utils';
66import {
67  projectConfig,
68  sdkConfigs,
69  sdkConfigPrefix,
70  globalProgram,
71  ohosSystemModuleSubDirPaths
72} from '../main';
73import {
74  CUSTOM_BUILDER_METHOD,
75  INNER_COMPONENT_NAMES,
76  GLOBAL_CUSTOM_BUILDER_METHOD
77} from './component_map';
78import { type ResolveModuleInfo, SOURCE_FILES, getRealModulePath } from './ets_checker';
79
80const IMPORT_FILE_ASTCACHE: Map<string, ts.SourceFile> =
81  process.env.watchMode === 'true' ? new Map() : (SOURCE_FILES || new Map());
82
83export default function processImport(node: ts.ImportDeclaration | ts.ImportEqualsDeclaration |
84  ts.ExportDeclaration, pagesDir: string, log: LogInfo[], asName: Map<string, string> = new Map(),
85isEntryPage: boolean = true, pathCollection: Set<string> = new Set()): void {
86  let filePath: string;
87  let defaultName: string;
88  if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
89    filePath = node.moduleSpecifier.getText().replace(/'|"/g, '');
90    if (ts.isImportDeclaration(node) && node.importClause && node.importClause.name &&
91      ts.isIdentifier(node.importClause.name)) {
92      defaultName = node.importClause.name.escapedText.toString();
93      if (isEntryPage) {
94        asName.set(defaultName, defaultName);
95      }
96    }
97    if (ts.isImportDeclaration(node) && node.importClause && node.importClause.namedBindings &&
98      ts.isNamedImports(node.importClause.namedBindings) &&
99      node.importClause.namedBindings.elements && isEntryPage) {
100      validateModuleSpecifier(node.moduleSpecifier, log);
101      node.importClause.namedBindings.elements.forEach(item => {
102        if (item.name && ts.isIdentifier(item.name)) {
103          validateModuleName(item.name, log);
104          if (item.propertyName && ts.isIdentifier(item.propertyName) && asName) {
105            asName.set(item.propertyName.escapedText.toString(), item.name.escapedText.toString());
106          } else {
107            asName.set(item.name.escapedText.toString(), item.name.escapedText.toString());
108          }
109        }
110      });
111    }
112  } else {
113    if (node.moduleReference && ts.isExternalModuleReference(node.moduleReference) &&
114      node.moduleReference.expression && ts.isStringLiteral(node.moduleReference.expression)) {
115      filePath = node.moduleReference.expression.text;
116      defaultName = node.name.escapedText.toString();
117      if (isEntryPage) {
118        asName.set(defaultName, defaultName);
119      }
120    }
121  }
122
123  try {
124    const fileResolvePath: string = getFileFullPath(filePath, pagesDir);
125    if (fs.existsSync(fileResolvePath) && fs.statSync(fileResolvePath).isFile() &&
126      !pathCollection.has(fileResolvePath)) {
127      let sourceFile: ts.SourceFile;
128      pathCollection.add(fileResolvePath);
129      if (IMPORT_FILE_ASTCACHE.has(fileResolvePath)) {
130        sourceFile = IMPORT_FILE_ASTCACHE.get(fileResolvePath);
131      } else {
132        sourceFile = generateSourceFileAST(fileResolvePath, filePath);
133        IMPORT_FILE_ASTCACHE[fileResolvePath] = sourceFile;
134      }
135      visitAllNode(sourceFile, sourceFile, defaultName, asName, path.dirname(fileResolvePath), log,
136        new Set(), new Set(), new Set(), new Map(), pathCollection, fileResolvePath, /\.d\.ets$/.test(fileResolvePath));
137    }
138  } catch (e) {
139    // ignore
140  }
141}
142
143function generateSourceFileAST(fileResolvePath: string, filePath: string): ts.SourceFile {
144  const originContent: string = fs.readFileSync(fileResolvePath, { encoding: 'utf-8' });
145  const content: string = path.extname(fileResolvePath) === EXTNAME_ETS ?
146    preprocessNewExtend(preprocessExtend(processSystemApi(originContent))) : originContent;
147  return ts.createSourceFile(fileResolvePath, content, ts.ScriptTarget.Latest, true, ts.ScriptKind.ETS);
148}
149
150type structDecoratorResult = {
151  hasRecycle: boolean
152}
153
154const MODIFIER_LENGTH: number = 2;
155
156function visitAllNode(node: ts.Node, sourceFile: ts.SourceFile, defaultNameFromParent: string,
157  asNameFromParent: Map<string, string>, pagesDir: string, log: LogInfo[], entryCollection: Set<string>,
158  exportCollection: Set<string>, defaultCollection: Set<string>, asExportCollection: Map<string, string>,
159  pathCollection: Set<string>, fileResolvePath: string, isDETS: boolean) {
160  if (isObservedClass(node)) {
161    collectSpecialFunctionNode(node as ts.ClassDeclaration, asNameFromParent, defaultNameFromParent, defaultCollection,
162      asExportCollection, observedClassCollection);
163    // @ts-ignore
164    observedClassCollection.add(node.name.getText());
165  }
166  if (isCustomDialogClass(node)) {
167    collectSpecialFunctionNode(node as ts.StructDeclaration, asNameFromParent, defaultNameFromParent, defaultCollection,
168      asExportCollection, componentCollection.customDialogs);
169    // @ts-ignore
170    componentCollection.customDialogs.add(node.name.getText());
171  }
172  if (ts.isEnumDeclaration(node) && node.name) {
173    enumCollection.add(node.name.getText());
174  }
175  const structDecorator: structDecoratorResult = { hasRecycle: false };
176  if (ts.isStructDeclaration(node) && ts.isIdentifier(node.name) && isCustomComponent(node, structDecorator)) {
177    addDependencies(node, defaultNameFromParent, asNameFromParent, isDETS, structDecorator);
178    isExportEntry(node, log, entryCollection, exportCollection, defaultCollection, fileResolvePath, sourceFile);
179    if (asExportCollection.has(node.name.getText())) {
180      componentCollection.customComponents.add(asExportCollection.get(node.name.getText()));
181      if (isDETS) {
182        storedFileInfo.getCurrentArkTsFile().compFromDETS.add(asExportCollection.get(node.name.getText()));
183      }
184      if (structDecorator.hasRecycle) {
185        storedFileInfo.getCurrentArkTsFile().recycleComponents.add(asExportCollection.get(node.name.getText()));
186      }
187    }
188    const modifiers: readonly ts.Modifier[] =
189      ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
190    if (modifiers && modifiers.length >= MODIFIER_LENGTH && modifiers[0] &&
191      modifiers[0].kind === ts.SyntaxKind.ExportKeyword && modifiers[1] &&
192      modifiers[1].kind === ts.SyntaxKind.DefaultKeyword) {
193      if (!defaultNameFromParent && hasCollection(node.name)) {
194        addDefaultExport(node, isDETS, structDecorator);
195      } else if (defaultNameFromParent && asNameFromParent.has(defaultNameFromParent)) {
196        componentCollection.customComponents.add(asNameFromParent.get(defaultNameFromParent));
197        if (isDETS) {
198          storedFileInfo.getCurrentArkTsFile().compFromDETS.add(asNameFromParent.get(defaultNameFromParent));
199        }
200        if (structDecorator.hasRecycle) {
201          storedFileInfo.getCurrentArkTsFile().recycleComponents.add(asNameFromParent.get(defaultNameFromParent));
202        }
203      }
204    }
205    if (defaultCollection.has(node.name.getText())) {
206      componentCollection.customComponents.add(CUSTOM_COMPONENT_DEFAULT);
207      if (isDETS) {
208        storedFileInfo.getCurrentArkTsFile().compFromDETS.add(CUSTOM_COMPONENT_DEFAULT);
209      }
210      if (structDecorator.hasRecycle) {
211        storedFileInfo.getCurrentArkTsFile().recycleComponents.add(CUSTOM_COMPONENT_DEFAULT);
212      }
213    }
214  }
215  if (ts.isFunctionDeclaration(node) && hasDecorator(node, COMPONENT_BUILDER_DECORATOR)) {
216    collectSpecialFunctionNode(node, asNameFromParent, defaultNameFromParent, defaultCollection,
217      asExportCollection, CUSTOM_BUILDER_METHOD);
218    collectSpecialFunctionNode(node, asNameFromParent, defaultNameFromParent, defaultCollection,
219      asExportCollection, GLOBAL_CUSTOM_BUILDER_METHOD);
220  }
221  if (ts.isExportAssignment(node) && node.expression && ts.isIdentifier(node.expression) &&
222    hasCollection(node.expression)) {
223    if (defaultNameFromParent) {
224      const propertiesName: string = node.expression.escapedText.toString();
225      setDependencies(defaultNameFromParent, linkCollection.get(propertiesName),
226        propertyCollection.get(propertiesName), propCollection.get(propertiesName),
227        builderParamObjectCollection.get(propertiesName), stateCollection.get(propertiesName),
228        regularCollection.get(propertiesName), storagePropCollection.get(propertiesName),
229        storageLinkCollection.get(propertiesName), provideCollection.get(propertiesName),
230        consumeCollection.get(propertiesName), objectLinkCollection.get(propertiesName),
231        localStorageLinkCollection.get(propertiesName), localStoragePropCollection.get(propertiesName),
232        builderParamInitialization.get(propertiesName), propInitialization.get(propertiesName), isDETS,
233        structDecorator);
234    }
235    addDefaultExport(node, isDETS, structDecorator);
236  }
237  if (ts.isExportAssignment(node) && node.expression && ts.isIdentifier(node.expression)) {
238    if (defaultNameFromParent) {
239      asNameFromParent.set(node.expression.getText(), asNameFromParent.get(defaultNameFromParent));
240    }
241    defaultCollection.add(node.expression.getText());
242  }
243  if (ts.isExportDeclaration(node) && node.exportClause &&
244    ts.isNamedExports(node.exportClause) && node.exportClause.elements) {
245    node.exportClause.elements.forEach(item => {
246      if (process.env.watchMode === 'true') {
247        exportCollection.add((item.propertyName ? item.propertyName : item.name).escapedText.toString());
248      }
249      if (item.name && ts.isIdentifier(item.name)) {
250        if (!item.propertyName) {
251          asExportCollection.set(item.name.escapedText.toString(), item.name.escapedText.toString());
252        } else if (item.propertyName && ts.isIdentifier(item.propertyName)) {
253          validateModuleName(item.name, log, sourceFile, fileResolvePath);
254          if (hasCollection(item.propertyName)) {
255            let asExportName: string = item.name.escapedText.toString();
256            const asExportPropertyName: string = item.propertyName.escapedText.toString();
257            if (asNameFromParent.has(asExportName)) {
258              asExportName = asNameFromParent.get(asExportName);
259            }
260            setDependencies(asExportName, linkCollection.get(asExportPropertyName),
261              propertyCollection.get(asExportPropertyName),
262              propCollection.get(asExportPropertyName),
263              builderParamObjectCollection.get(asExportPropertyName),
264              stateCollection.get(asExportPropertyName), regularCollection.get(asExportPropertyName),
265              storagePropCollection.get(asExportPropertyName), storageLinkCollection.get(asExportPropertyName),
266              provideCollection.get(asExportPropertyName), consumeCollection.get(asExportPropertyName),
267              objectLinkCollection.get(asExportPropertyName), localStorageLinkCollection.get(asExportPropertyName),
268              localStoragePropCollection.get(asExportPropertyName), builderParamInitialization.get(asExportPropertyName),
269              propInitialization.get(asExportPropertyName), isDETS, structDecorator);
270          }
271          asExportCollection.set(item.propertyName.escapedText.toString(), item.name.escapedText.toString());
272        }
273      }
274      if (item.name && ts.isIdentifier(item.name) && asNameFromParent.has(item.name.escapedText.toString()) &&
275        item.propertyName && ts.isIdentifier(item.propertyName)) {
276        asNameFromParent.set(item.propertyName.escapedText.toString(),
277          asNameFromParent.get(item.name.escapedText.toString()));
278      }
279    });
280  }
281  if (ts.isExportDeclaration(node) && node.moduleSpecifier &&
282    ts.isStringLiteral(node.moduleSpecifier)) {
283    if (process.env.watchMode === 'true' && node.exportClause && ts.isNamedExports(node.exportClause) &&
284      node.exportClause.elements) {
285      node.exportClause.elements.forEach(item => {
286        exportCollection.add((item.propertyName ? item.propertyName : item.name).escapedText.toString());
287        if (item.propertyName && ts.isIdentifier(item.propertyName) && item.name &&
288          ts.isIdentifier(item.name) && asNameFromParent.has(item.name.escapedText.toString())) {
289          asNameFromParent.set(item.propertyName.escapedText.toString(),
290            asNameFromParent.get(item.name.escapedText.toString()));
291          defaultCollection.add(item.name.escapedText.toString());
292        }
293      });
294    }
295    processImport(node, pagesDir, log, asNameFromParent, true, new Set(pathCollection));
296  }
297  if (ts.isImportDeclaration(node)) {
298    if (node.importClause && node.importClause.name && ts.isIdentifier(node.importClause.name) &&
299      asNameFromParent.has(node.importClause.name.getText())) {
300      processImport(node, pagesDir, log, asNameFromParent, false, new Set(pathCollection));
301    } else if (node.importClause && node.importClause.namedBindings &&
302      ts.isNamedImports(node.importClause.namedBindings) && node.importClause.namedBindings.elements) {
303      let nested: boolean = false;
304      node.importClause.namedBindings.elements.forEach(item => {
305        if (item.name && ts.isIdentifier(item.name) && asNameFromParent.has(item.name.escapedText.toString())) {
306          nested = true;
307          if (item.propertyName && ts.isIdentifier(item.propertyName)) {
308            asNameFromParent.set(item.propertyName.escapedText.toString(),
309              asNameFromParent.get(item.name.escapedText.toString()));
310          }
311        }
312      });
313      if (nested) {
314        processImport(node, pagesDir, log, asNameFromParent, false, new Set(pathCollection));
315      }
316    }
317  }
318  node.getChildren().reverse().forEach((item: ts.Node) => visitAllNode(item, sourceFile,
319    defaultNameFromParent, asNameFromParent, pagesDir, log, entryCollection, exportCollection,
320    defaultCollection, asExportCollection, pathCollection, fileResolvePath, isDETS));
321}
322
323function collectSpecialFunctionNode(node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.StructDeclaration,
324  asNameFromParent: Map<string, string>, defaultNameFromParent: string, defaultCollection: Set<string>,
325  asExportCollection: Map<string, string>, collection: Set<string>): void {
326  const name: string = node.name.getText();
327  const modifiers: readonly ts.Modifier[] = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
328  if (asNameFromParent.has(name)) {
329    collection.add(asNameFromParent.get(name));
330  } else if (modifiers && modifiers.length >= 1 && modifiers[0] &&
331    modifiers[0].kind === ts.SyntaxKind.ExportKeyword) {
332    if (modifiers.length === 1) {
333      collection.add(name);
334    } else if (modifiers.length >= MODIFIER_LENGTH && modifiers[1] && modifiers[1].kind ===
335      ts.SyntaxKind.DefaultKeyword) {
336      collection.add(CUSTOM_COMPONENT_DEFAULT);
337      if (defaultNameFromParent && asNameFromParent.has(defaultNameFromParent)) {
338        collection.add(asNameFromParent.get(defaultNameFromParent));
339      }
340    }
341  } else if (defaultCollection.has(name)) {
342    collection.add(CUSTOM_COMPONENT_DEFAULT);
343  } else if (asExportCollection.has(name)) {
344    collection.add(asExportCollection.get(name));
345  }
346}
347
348function isExportEntry(node: ts.StructDeclaration, log: LogInfo[], entryCollection: Set<string>,
349  exportCollection: Set<string>, defaultCollection: Set<string>, fileResolvePath: string,
350  sourceFile: ts.SourceFile): void {
351  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
352  if (process.env.watchMode === 'true' && node && decorators) {
353    let existExport: boolean = false;
354    let existEntry: boolean = false;
355    const modifiers: readonly ts.Modifier[] = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
356    if (modifiers) {
357      for (let i = 0; i < modifiers.length; i++) {
358        if (modifiers[i].kind === ts.SyntaxKind.ExportKeyword) {
359          existExport = true;
360          break;
361        }
362      }
363    }
364    for (let i = 0; i < decorators.length; i++) {
365      if (decorators[i].getText() === COMPONENT_DECORATOR_ENTRY) {
366        entryCollection.add(node.name.escapedText.toString());
367        existEntry = true;
368        break;
369      }
370    }
371  }
372}
373
374function addDependencies(node: ts.StructDeclaration, defaultNameFromParent: string,
375  asNameFromParent: Map<string, string>, isDETS: boolean, structDecorator: structDecoratorResult): void {
376  const componentName: string = node.name.getText();
377  const ComponentSet: IComponentSet = getComponentSet(node, false);
378  const modifiers: readonly ts.Modifier[] = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined;
379  if (defaultNameFromParent && modifiers && modifiers.length >= MODIFIER_LENGTH && modifiers[0] &&
380    modifiers[1] && modifiers[0].kind === ts.SyntaxKind.ExportKeyword &&
381    modifiers[1].kind === ts.SyntaxKind.DefaultKeyword) {
382    setDependencies(defaultNameFromParent, ComponentSet.links, ComponentSet.properties,
383      ComponentSet.props, ComponentSet.builderParams, ComponentSet.states, ComponentSet.regulars,
384      ComponentSet.storageProps, ComponentSet.storageLinks, ComponentSet.provides,
385      ComponentSet.consumes, ComponentSet.objectLinks, ComponentSet.localStorageLink,
386      ComponentSet.localStorageProp, ComponentSet.builderParamData, ComponentSet.propData, isDETS,
387      structDecorator);
388  } else if (asNameFromParent.has(componentName)) {
389    setDependencies(asNameFromParent.get(componentName), ComponentSet.links, ComponentSet.properties,
390      ComponentSet.props, ComponentSet.builderParams, ComponentSet.states, ComponentSet.regulars,
391      ComponentSet.storageProps, ComponentSet.storageLinks, ComponentSet.provides,
392      ComponentSet.consumes, ComponentSet.objectLinks, ComponentSet.localStorageLink,
393      ComponentSet.localStorageProp, ComponentSet.builderParamData, ComponentSet.propData, isDETS,
394      structDecorator);
395  } else {
396    setDependencies(componentName, ComponentSet.links, ComponentSet.properties, ComponentSet.props,
397      ComponentSet.builderParams, ComponentSet.states, ComponentSet.regulars,
398      ComponentSet.storageProps, ComponentSet.storageLinks, ComponentSet.provides,
399      ComponentSet.consumes, ComponentSet.objectLinks, ComponentSet.localStorageLink,
400      ComponentSet.localStorageProp, ComponentSet.builderParamData, ComponentSet.propData, isDETS,
401      structDecorator);
402  }
403}
404
405function addDefaultExport(node: ts.StructDeclaration | ts.ExportAssignment, isDETS: boolean,
406  structDecorator: structDecoratorResult): void {
407  let name: string;
408  if (ts.isStructDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
409    name = node.name.escapedText.toString();
410  } else if (ts.isExportAssignment(node) && node.expression && ts.isIdentifier(node.expression)) {
411    name = node.expression.escapedText.toString();
412  } else {
413    return;
414  }
415  setDependencies(CUSTOM_COMPONENT_DEFAULT,
416    linkCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
417      new Set([...linkCollection.get(CUSTOM_COMPONENT_DEFAULT), ...linkCollection.get(name)]) :
418      linkCollection.get(name),
419    propertyCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
420      new Set([...propertyCollection.get(CUSTOM_COMPONENT_DEFAULT),
421        ...propertyCollection.get(name)]) : propertyCollection.get(name),
422    propCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
423      new Set([...propCollection.get(CUSTOM_COMPONENT_DEFAULT), ...propCollection.get(name)]) :
424      propCollection.get(name),
425    builderParamObjectCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
426      new Set([...builderParamObjectCollection.get(CUSTOM_COMPONENT_DEFAULT),
427        ...builderParamObjectCollection.get(name)]) : builderParamObjectCollection.get(name),
428    stateCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
429      new Set([...stateCollection.get(CUSTOM_COMPONENT_DEFAULT),
430        ...stateCollection.get(name)]) : stateCollection.get(name),
431    regularCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
432      new Set([...regularCollection.get(CUSTOM_COMPONENT_DEFAULT),
433        ...regularCollection.get(name)]) : regularCollection.get(name),
434    storagePropCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
435      new Set([...storagePropCollection.get(CUSTOM_COMPONENT_DEFAULT),
436        ...storagePropCollection.get(name)]) : storagePropCollection.get(name),
437    storageLinkCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
438      new Set([...storageLinkCollection.get(CUSTOM_COMPONENT_DEFAULT),
439        ...storageLinkCollection.get(name)]) : storageLinkCollection.get(name),
440    provideCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
441      new Set([...provideCollection.get(CUSTOM_COMPONENT_DEFAULT),
442        ...provideCollection.get(name)]) : provideCollection.get(name),
443    consumeCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
444      new Set([...consumeCollection.get(CUSTOM_COMPONENT_DEFAULT),
445        ...consumeCollection.get(name)]) : consumeCollection.get(name),
446    objectLinkCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
447      new Set([...objectLinkCollection.get(CUSTOM_COMPONENT_DEFAULT),
448        ...objectLinkCollection.get(name)]) : objectLinkCollection.get(name),
449    getNewLocalStorageMap(localStorageLinkCollection, name),
450    getNewLocalStorageMap(localStoragePropCollection, name),
451    builderParamInitialization.has(CUSTOM_COMPONENT_DEFAULT) ?
452      new Set([...builderParamInitialization.get(CUSTOM_COMPONENT_DEFAULT),
453        ...builderParamInitialization.get(name)]) : builderParamInitialization.get(name),
454    propInitialization.has(CUSTOM_COMPONENT_DEFAULT) ?
455      new Set([...propInitialization.get(CUSTOM_COMPONENT_DEFAULT),
456        ...propInitialization.get(name)]) : propInitialization.get(name), isDETS,
457    structDecorator
458  );
459}
460
461function getNewLocalStorageMap(collection: Map<string, Map<string, Set<string>>>, name: string)
462  : Map<string, Set<string>> {
463  let localStorageLinkMap: Map<string, Set<string>> = new Map();
464  if (collection.has(CUSTOM_COMPONENT_DEFAULT)) {
465    const tempSet: Set<string> = new Set();
466    if (collection.get(CUSTOM_COMPONENT_DEFAULT)) {
467      for (const key of collection.get(CUSTOM_COMPONENT_DEFAULT).keys()) {
468        tempSet.add(key);
469      }
470    }
471    if (collection.get(name)) {
472      for (const key of collection.get(name).keys()) {
473        tempSet.add(key);
474      }
475    }
476    localStorageLinkMap.set(name, tempSet);
477  } else {
478    localStorageLinkMap = collection.get(name);
479  }
480  return localStorageLinkMap;
481}
482
483function setDependencies(component: string, linkArray: Set<string>, propertyArray: Set<string>,
484  propArray: Set<string>, builderParamArray: Set<string>, stateArray: Set<string>,
485  regularArray: Set<string>, storagePropsArray: Set<string>, storageLinksArray: Set<string>,
486  providesArray: Set<string>, consumesArray: Set<string>, objectLinksArray: Set<string>,
487  localStorageLinkMap: Map<string, Set<string>>, localStoragePropMap: Map<string, Set<string>>,
488  builderParamData: Set<string>, propData: Set<string>, isDETS: boolean,
489  structDecorator: structDecoratorResult): void {
490  linkCollection.set(component, linkArray);
491  propertyCollection.set(component, propertyArray);
492  if (!propCollection.get(component)) {
493    propCollection.set(component, propArray);
494  }
495  builderParamObjectCollection.set(component, builderParamArray);
496  componentCollection.customComponents.add(component);
497  if (isDETS) {
498    storedFileInfo.getCurrentArkTsFile().compFromDETS.add(component);
499  }
500  if (structDecorator.hasRecycle) {
501    storedFileInfo.getCurrentArkTsFile().recycleComponents.add(component);
502  }
503  stateCollection.set(component, stateArray);
504  regularCollection.set(component, regularArray);
505  storagePropCollection.set(component, storagePropsArray);
506  storageLinkCollection.set(component, storageLinksArray);
507  provideCollection.set(component, providesArray);
508  consumeCollection.set(component, consumesArray);
509  objectLinkCollection.set(component, objectLinksArray);
510  localStorageLinkCollection.set(component, localStorageLinkMap);
511  localStoragePropCollection.set(component, localStoragePropMap);
512  if (!builderParamInitialization.get(component)) {
513    builderParamInitialization.set(component, builderParamData);
514  }
515  if (!propInitialization.get(component)) {
516    propInitialization.set(component, propData);
517  }
518}
519
520function hasCollection(node: ts.Identifier): boolean {
521  const name: string = node.escapedText.toString();
522  return linkCollection.has(name) ||
523    propCollection.has(name) ||
524    propertyCollection.has(name) ||
525    builderParamObjectCollection.has(name) ||
526    stateCollection.has(name) ||
527    regularCollection.has(name) ||
528    storagePropCollection.has(name) ||
529    storageLinkCollection.has(name) ||
530    provideCollection.has(name) ||
531    consumeCollection.has(name) ||
532    objectLinkCollection.has(name) ||
533    localStorageLinkCollection.has(name) ||
534    localStoragePropCollection.has(name);
535}
536
537function isModule(filePath: string): boolean {
538  return !/^(\.|\.\.)?\//.test(filePath) || filePath.indexOf(projectConfig.packageDir) > -1;
539}
540
541function isCustomComponent(node: ts.StructDeclaration, structDecorator: structDecoratorResult): boolean {
542  let isComponent: boolean = false;
543  const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
544  if (decorators && decorators.length) {
545    for (let i = 0; i < decorators.length; ++i) {
546      const decoratorName: ts.Expression = decorators[i].expression;
547      if (ts.isIdentifier(decoratorName) || ts.isCallExpression(decoratorName)) {
548        let name: string = '';
549        if (ts.isCallExpression(decoratorName) && ts.isIdentifier(decoratorName.expression)) {
550          name = decoratorName.expression.escapedText.toString();
551        } else if (ts.isIdentifier(decoratorName)) {
552          name = decoratorName.escapedText.toString();
553        }
554        if (CUSTOM_DECORATOR_NAME.has(name)) {
555          isComponent = true;
556        }
557        if (name === DECORATOR_REUSEABLE) {
558          structDecorator.hasRecycle = true;
559        }
560      }
561    }
562  }
563  return isComponent;
564}
565
566let packageJsonEntry: string = '';
567
568function isPackageJsonEntry(filePath: string): boolean {
569  const packageJsonPath: string = path.join(filePath, projectConfig.packageJson);
570  if (fs.existsSync(packageJsonPath)) {
571    let entryTypes: string;
572    let entryMain: string;
573    try {
574      const packageJson: Object =
575        (projectConfig.packageManagerType === 'npm' ? JSON : JSON5).parse(fs.readFileSync(packageJsonPath).toString());
576      entryTypes = packageJson.types;
577      entryMain = packageJson.main;
578    } catch (e) {
579      return false;
580    }
581    if (entryExist(filePath, entryTypes)) {
582      packageJsonEntry = path.resolve(filePath, entryTypes);
583      return true;
584    } else if (entryExist(filePath, entryMain)) {
585      packageJsonEntry = path.resolve(filePath, entryMain);
586      return true;
587    }
588  }
589}
590
591function entryExist(filePath: string, entry: string): boolean {
592  return typeof entry === 'string' && fs.existsSync(path.resolve(filePath, entry)) &&
593    fs.statSync(path.resolve(filePath, entry)).isFile();
594}
595
596function getModuleFilePath(filePath: string): string {
597  if (filePath && path.extname(filePath) !== EXTNAME_ETS && isModule(filePath)) {
598    filePath += EXTNAME_ETS;
599  }
600  return filePath;
601}
602
603function getFileResolvePath(fileResolvePath: string, pagesDir: string, filePath: string,
604  projectPath: string): string {
605  const moduleFilePath: string = getModuleFilePath(filePath);
606  const defaultModule: string = path.join(projectPath, moduleFilePath);
607  if (fs.existsSync(defaultModule)) {
608    return defaultModule;
609  }
610  let entryModule: string;
611  let etsModule: string;
612  if (new RegExp(`^@(${sdkConfigPrefix})\\.`).test(filePath.trim())) {
613    for (let i = 0; i < sdkConfigs.length; i++) {
614      const resolveModuleInfo: ResolveModuleInfo = getRealModulePath(sdkConfigs[i].apiPath, filePath, ['.d.ts', '.d.ets']);
615      const systemModule: string = resolveModuleInfo.modulePath;
616      if (fs.existsSync(systemModule)) {
617        return systemModule;
618      }
619    }
620  }
621  if (!projectConfig.aceModuleJsonPath) {
622    entryModule = path.join(projectPath, '../../../../../', moduleFilePath);
623    etsModule = path.join(projectPath, '../../', moduleFilePath);
624  } else {
625    entryModule = path.join(projectPath, '../../../../', moduleFilePath);
626    etsModule = path.join(projectPath, '../', moduleFilePath);
627  }
628  if (fs.existsSync(entryModule)) {
629    return entryModule;
630  }
631  if (fs.existsSync(etsModule)) {
632    return etsModule;
633  }
634  let curPageDir: string = pagesDir;
635  while (!fs.existsSync(fileResolvePath)) {
636    if (filePath.indexOf(projectConfig.packageDir) > -1) {
637      fileResolvePath = path.join(curPageDir, filePath);
638    } else {
639      fileResolvePath = path.join(curPageDir, projectConfig.packageDir, filePath);
640    }
641    if (fs.existsSync(fileResolvePath + EXTNAME_ETS)) {
642      fileResolvePath = fileResolvePath + EXTNAME_ETS;
643    } else if (isPackageJsonEntry(fileResolvePath)) {
644      fileResolvePath = packageJsonEntry;
645      if (fs.statSync(fileResolvePath).isDirectory()) {
646        if (fs.existsSync(path.join(fileResolvePath, INDEX_ETS))) {
647          fileResolvePath = path.join(fileResolvePath, INDEX_ETS);
648        } else if (fs.existsSync(path.join(fileResolvePath, INDEX_TS))) {
649          fileResolvePath = path.join(fileResolvePath, INDEX_TS);
650        }
651      }
652    } else if (fs.existsSync(path.join(fileResolvePath, INDEX_ETS))) {
653      fileResolvePath = path.join(fileResolvePath, INDEX_ETS);
654    } else if (fs.existsSync(path.join(fileResolvePath, INDEX_TS))) {
655      fileResolvePath = path.join(fileResolvePath, INDEX_TS);
656    }
657    if (curPageDir === path.parse(curPageDir).root) {
658      break;
659    }
660    curPageDir = path.dirname(curPageDir);
661  }
662  return fileResolvePath;
663}
664
665function getFileFullPath(filePath: string, pagesDir: string): string {
666  if (filePath && path.extname(filePath) !== EXTNAME_ETS && path.extname(filePath) !== EXTNAME_TS &&
667      !isModule(filePath)) {
668    const dirIndexEtsPath: string = path.resolve(path.resolve(pagesDir, filePath), INDEX_ETS);
669    const dirIndexTsPath: string = path.resolve(path.resolve(pagesDir, filePath), INDEX_TS);
670    if (/^(\.|\.\.)\//.test(filePath) && !fs.existsSync(path.resolve(pagesDir, filePath + EXTNAME_ETS)) &&
671      fs.existsSync(dirIndexEtsPath)) {
672      filePath = dirIndexEtsPath;
673    } else if (/^(\.|\.\.)\//.test(filePath) && !fs.existsSync(path.resolve(pagesDir, filePath + EXTNAME_TS)) &&
674      fs.existsSync(dirIndexTsPath)) {
675      filePath = dirIndexTsPath;
676    } else {
677      filePath += getExtensionIfUnfullySpecifiedFilepath(path.resolve(pagesDir, filePath));
678    }
679  }
680
681  let fileResolvePath: string;
682  if (/^(\.|\.\.)\//.test(filePath) && filePath.indexOf(projectConfig.packageDir) < 0) {
683    fileResolvePath = path.resolve(pagesDir, filePath);
684  } else if (/^\//.test(filePath) && filePath.indexOf(projectConfig.packageDir) < 0 ||
685    fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
686    fileResolvePath = filePath;
687  } else {
688    fileResolvePath = getFileResolvePath(fileResolvePath, pagesDir, filePath, projectConfig.projectPath);
689  }
690
691  return fileResolvePath;
692}
693
694function validateModuleName(moduleNode: ts.Identifier, log: LogInfo[], sourceFile?: ts.SourceFile,
695  fileResolvePath?: string): void {
696  const moduleName: string = moduleNode.escapedText.toString();
697  if (INNER_COMPONENT_NAMES.has(moduleName)) {
698    const error: LogInfo = {
699      type: LogType.ERROR,
700      message: `The module name '${moduleName}' can not be the same as the inner component name.`,
701      pos: moduleNode.getStart()
702    };
703    if (sourceFile && fileResolvePath) {
704      const posOfNode: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition(moduleNode.getStart());
705      const line: number = posOfNode.line + 1;
706      const column: number = posOfNode.character + 1;
707      Object.assign(error, {
708        fileName: fileResolvePath,
709        line: line,
710        column: column
711      });
712    }
713    log.push(error);
714  }
715}
716
717export function validateModuleSpecifier(moduleSpecifier: ts.Expression, log: LogInfo[]): void {
718  const moduleSpecifierStr: string = moduleSpecifier.getText().replace(/'|"/g, '');
719  const hasSubDirPath: boolean = ohosSystemModuleSubDirPaths.some((filePath: string) => {
720    return filePath === moduleSpecifierStr;
721  });
722  if (hasSubDirPath) {
723    const error: LogInfo = {
724      type: LogType.WARN,
725      message: `Cannot find module '${moduleSpecifierStr}' or its corresponding type declarations.`,
726      pos: moduleSpecifier.getStart()
727    };
728    log.push(error);
729  }
730}
731
732export function processImportModule(node: ts.ImportDeclaration, pageFile: string, log: LogInfo[]): void {
733  let importSymbol: ts.Symbol;
734  let realSymbol: ts.Symbol;
735  let originNode: ts.Node;
736  validateModuleSpecifier(node.moduleSpecifier, log);
737
738  // import xxx from 'xxx'
739  if (node.importClause && node.importClause.name && ts.isIdentifier(node.importClause.name)) {
740    getDefinedNode(importSymbol, realSymbol, originNode, node.importClause.name, pageFile);
741  }
742
743  // import {xxx} from 'xxx'
744  if (node.importClause && node.importClause.namedBindings &&
745    ts.isNamedImports(node.importClause.namedBindings) &&
746    node.importClause.namedBindings.elements) {
747    node.importClause.namedBindings.elements.forEach((importSpecifier: ts.ImportSpecifier) => {
748      if (ts.isImportSpecifier(importSpecifier) && importSpecifier.name && ts.isIdentifier(importSpecifier.name)) {
749        getDefinedNode(importSymbol, realSymbol, originNode, importSpecifier.name, pageFile);
750      }
751    });
752  }
753
754  // import * as xxx from 'xxx'
755  if (node.importClause && node.importClause.namedBindings &&
756    ts.isNamespaceImport(node.importClause.namedBindings) && node.importClause.namedBindings.name &&
757    ts.isIdentifier(node.importClause.namedBindings.name)) {
758    getDefinedNode(importSymbol, realSymbol, originNode, node.importClause.namedBindings.name, pageFile);
759  }
760}
761
762function getDefinedNode(importSymbol: ts.Symbol, realSymbol: ts.Symbol, originNode: ts.Node,
763  usedNode: ts.Identifier, pageFile: string): void {
764  importSymbol = globalProgram.checker.getSymbolAtLocation(usedNode);
765  if (importSymbol) {
766    realSymbol = globalProgram.checker.getAliasedSymbol(importSymbol);
767  } else {
768    realSymbol = null;
769  }
770  if (realSymbol && realSymbol.declarations) {
771    originNode = realSymbol.declarations[0];
772  } else {
773    originNode = null;
774  }
775  if (originNode) {
776    if (ts.isSourceFile(originNode) && realSymbol.escapedName) {
777      const escapedName: string = realSymbol.escapedName.toString().replace(/^("|')/, '').replace(/("|')$/, '');
778      if (fs.existsSync(escapedName + '.ets') || fs.existsSync(escapedName + '.ts') &&
779        realSymbol.exports && realSymbol.exports instanceof Map) {
780        getIntegrationNodeInfo(originNode, usedNode, realSymbol.exports, pageFile);
781        return;
782      }
783    }
784    processImportNode(originNode, usedNode, false, null, pageFile);
785  }
786}
787
788function getIntegrationNodeInfo(originNode: ts.Node, usedNode: ts.Identifier, exportsMap: ts.SymbolTable,
789  pageFile: string): void {
790  for (const usedSymbol of exportsMap) {
791    try {
792      originNode = globalProgram.checker.getAliasedSymbol(usedSymbol[1]).declarations[0];
793    } catch (e) {
794      if (usedSymbol[1] && usedSymbol[1].declarations) {
795        originNode = usedSymbol[1].declarations[0];
796      }
797    }
798    processImportNode(originNode, usedNode, true, usedSymbol[0], pageFile);
799  }
800}
801
802function processImportNode(originNode: ts.Node, usedNode: ts.Identifier, importIntegration: boolean,
803  usedPropName: string, pageFile: string): void {
804  const structDecorator: structDecoratorResult = { hasRecycle: false };
805  let name: string;
806  if (importIntegration) {
807    name = usedPropName;
808  } else {
809    name = usedNode.escapedText.toString();
810  }
811  let needCollection: boolean = true;
812  const originFile: string = originNode.getSourceFile() ? originNode.getSourceFile().fileName : undefined;
813  if (ts.isStructDeclaration(originNode) && ts.isIdentifier(originNode.name)) {
814    if (isCustomDialogClass(originNode)) {
815      componentCollection.customDialogs.add(name);
816    }
817    if (isCustomComponent(originNode, structDecorator)) {
818      let isDETS: boolean = false;
819      componentCollection.customComponents.add(name);
820      const ComponentSet: IComponentSet = getComponentSet(originNode, false);
821      while (originNode) {
822        if (ts.isSourceFile(originNode) && /\.d\.ets$/.test(originNode.fileName)) {
823          isDETS = true;
824        }
825        originNode = originNode.parent;
826      }
827      if (isDETS) {
828        storedFileInfo.getCurrentArkTsFile().compFromDETS.add(name);
829      }
830      if (structDecorator.hasRecycle) {
831        storedFileInfo.getCurrentArkTsFile().recycleComponents.add(name);
832      }
833      setDependencies(name, ComponentSet.links, ComponentSet.properties,
834        ComponentSet.props, ComponentSet.builderParams, ComponentSet.states, ComponentSet.regulars,
835        ComponentSet.storageProps, ComponentSet.storageLinks, ComponentSet.provides,
836        ComponentSet.consumes, ComponentSet.objectLinks, ComponentSet.localStorageLink,
837        ComponentSet.localStorageProp, ComponentSet.builderParamData, ComponentSet.propData, isDETS,
838        structDecorator);
839    }
840  } else if (isObservedClass(originNode)) {
841    observedClassCollection.add(name);
842  } else if (ts.isFunctionDeclaration(originNode) && hasDecorator(originNode, COMPONENT_BUILDER_DECORATOR)) {
843    CUSTOM_BUILDER_METHOD.add(name);
844    GLOBAL_CUSTOM_BUILDER_METHOD.add(name);
845  } else if (ts.isEnumDeclaration(originNode) && originNode.name) {
846    enumCollection.add(name);
847  } else {
848    needCollection = false;
849  }
850  if (needCollection && pageFile && originFile) {
851    storedFileInfo.transformCacheFiles[pageFile].children.push({
852      fileName: originFile,
853      mtimeMs: fs.existsSync(originFile) ? fs.statSync(originFile).mtimeMs : 0
854    });
855  }
856}
857