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