• 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  NODE_MODULES,
25  INDEX_ETS,
26  INDEX_TS,
27  PACKAGE_JSON,
28  STRUCT,
29  CLASS,
30  CUSTOM_COMPONENT_DEFAULT,
31  CUSTOM_DECORATOR_NAME,
32  COMPONENT_DECORATOR_ENTRY,
33  COMPONENT_BUILDER_DECORATOR,
34  CARD_LOG_TYPE_IMPORT
35} from './pre_define';
36import {
37  propertyCollection,
38  linkCollection,
39  componentCollection,
40  preprocessExtend,
41  preprocessNewExtend,
42  processSystemApi,
43  propCollection,
44  isObservedClass,
45  isCustomDialogClass,
46  observedClassCollection,
47  enumCollection,
48  getComponentSet,
49  IComponentSet,
50  builderParamObjectCollection,
51  stateCollection,
52  regularCollection,
53  storagePropCollection,
54  storageLinkCollection,
55  provideCollection,
56  consumeCollection,
57  objectLinkCollection,
58  localStorageLinkCollection,
59  localStoragePropCollection,
60  builderParamInitialization
61} from './validate_ui_syntax';
62import {
63  getExtensionIfUnfullySpecifiedFilepath,
64  hasDecorator,
65  LogInfo,
66  LogType,
67  repeatLog
68} from './utils';
69import { projectConfig } from '../main';
70import {
71  CUSTOM_BUILDER_METHOD,
72  INNER_COMPONENT_NAMES,
73  GLOBAL_CUSTOM_BUILDER_METHOD
74} from './component_map';
75import { validatorCard } from './process_ui_syntax';
76
77export const IMPORT_FILE_ASTCACHE: Map<string, ts.SourceFile> = new Map();
78
79export default function processImport(node: ts.ImportDeclaration | ts.ImportEqualsDeclaration |
80  ts.ExportDeclaration, pagesDir: string, log: LogInfo[], asName: Map<string, string> = new Map(),
81isEntryPage: boolean = true, pathCollection: Set<string> = new Set()): void {
82  let filePath: string;
83  let defaultName: string;
84  if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
85    filePath = node.moduleSpecifier.getText().replace(/'|"/g, '');
86    if (ts.isImportDeclaration(node) && node.importClause && node.importClause.name &&
87      ts.isIdentifier(node.importClause.name)) {
88      defaultName = node.importClause.name.escapedText.toString();
89      if (isEntryPage) {
90        asName.set(defaultName, defaultName);
91      }
92    }
93    if (ts.isImportDeclaration(node) && node.importClause && node.importClause.namedBindings &&
94      ts.isNamedImports(node.importClause.namedBindings) &&
95      node.importClause.namedBindings.elements && isEntryPage) {
96      node.importClause.namedBindings.elements.forEach(item => {
97        if (item.name && ts.isIdentifier(item.name)) {
98          validateModuleName(item.name, log);
99          if (item.propertyName && ts.isIdentifier(item.propertyName) && asName) {
100            asName.set(item.propertyName.escapedText.toString(), item.name.escapedText.toString());
101          } else {
102            asName.set(item.name.escapedText.toString(), item.name.escapedText.toString());
103          }
104        }
105      });
106    }
107  } else {
108    if (node.moduleReference && ts.isExternalModuleReference(node.moduleReference) &&
109      node.moduleReference.expression && ts.isStringLiteral(node.moduleReference.expression)) {
110      filePath = node.moduleReference.expression.text;
111      defaultName = node.name.escapedText.toString();
112      if (isEntryPage) {
113        asName.set(defaultName, defaultName);
114      }
115    }
116  }
117
118  if (filePath) {
119    validatorCard(log, CARD_LOG_TYPE_IMPORT, node.getStart());
120  }
121
122  try {
123    let fileResolvePath: string = getFileFullPath(filePath, pagesDir);
124    if (fs.existsSync(fileResolvePath) && fs.statSync(fileResolvePath).isFile() &&
125      !pathCollection.has(fileResolvePath)) {
126      let sourceFile: ts.SourceFile;
127      pathCollection.add(fileResolvePath);
128      if (IMPORT_FILE_ASTCACHE.has(fileResolvePath)) {
129        sourceFile = IMPORT_FILE_ASTCACHE.get(fileResolvePath);
130      } else {
131        sourceFile = generateSourceFileAST(fileResolvePath, filePath);
132        IMPORT_FILE_ASTCACHE[fileResolvePath] = sourceFile;
133      }
134      visitAllNode(sourceFile, sourceFile, defaultName, asName, path.dirname(fileResolvePath), log,
135        new Set(), new Set(), new Set(), new Map(), pathCollection, fileResolvePath);
136    }
137  } catch (e) {
138    // ignore
139  }
140}
141
142export function generateSourceFileAST(fileResolvePath: string, filePath: string): ts.SourceFile {
143  const originContent: string = fs.readFileSync(fileResolvePath, { encoding: 'utf-8' });
144  const content: string = path.extname(fileResolvePath) === EXTNAME_ETS ?
145    preprocessNewExtend(preprocessExtend(processSystemApi(originContent.replace(
146      new RegExp('\\b' + STRUCT + '\\b.+\\{', 'g'), item => {
147        return item.replace(new RegExp('\\b' + STRUCT + '\\b', 'g'), `${CLASS} `);
148      })))) : originContent;
149  return ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
150}
151
152function visitAllNode(node: ts.Node, sourceFile: ts.SourceFile, defaultNameFromParent: string,
153  asNameFromParent: Map<string, string>, pagesDir: string, log: LogInfo[], entryCollection: Set<string>,
154  exportCollection: Set<string>, defaultCollection: Set<string>, asExportCollection: Map<string, string>,
155  pathCollection: Set<string>, fileResolvePath: string) {
156  if (isObservedClass(node)) {
157    collectSpecialFunctionNode(node as ts.ClassDeclaration, asNameFromParent, defaultNameFromParent, defaultCollection,
158      asExportCollection, observedClassCollection);
159    // @ts-ignore
160    observedClassCollection.add(node.name.getText());
161  }
162  if (isCustomDialogClass(node)) {
163    collectSpecialFunctionNode(node as ts.StructDeclaration, asNameFromParent, defaultNameFromParent, defaultCollection,
164      asExportCollection, componentCollection.customDialogs);
165    // @ts-ignore
166    componentCollection.customDialogs.add(node.name.getText());
167  }
168  if (ts.isEnumDeclaration(node) && node.name) {
169    enumCollection.add(node.name.getText());
170  }
171  if ((ts.isClassDeclaration(node) || ts.isStructDeclaration(node)) && ts.isIdentifier(node.name) &&
172    isCustomComponent(node)) {
173    addDependencies(node, defaultNameFromParent, asNameFromParent);
174    isExportEntry(node, log, entryCollection, exportCollection, defaultCollection, fileResolvePath, sourceFile);
175    if (asExportCollection.has(node.name.getText())) {
176      componentCollection.customComponents.add(asExportCollection.get(node.name.getText()));
177    }
178    if (node.modifiers && node.modifiers.length >= 2 && node.modifiers[0] &&
179      node.modifiers[0].kind === ts.SyntaxKind.ExportKeyword && node.modifiers[1] &&
180      node.modifiers[1].kind === ts.SyntaxKind.DefaultKeyword) {
181      if (!defaultNameFromParent && hasCollection(node.name)) {
182        addDefaultExport(node);
183      } else if (defaultNameFromParent && asNameFromParent.has(defaultNameFromParent)) {
184        componentCollection.customComponents.add(asNameFromParent.get(defaultNameFromParent));
185      }
186    }
187    if (defaultCollection.has(node.name.getText())) {
188      componentCollection.customComponents.add(CUSTOM_COMPONENT_DEFAULT);
189    }
190  }
191  if (ts.isFunctionDeclaration(node) && hasDecorator(node, COMPONENT_BUILDER_DECORATOR)) {
192    collectSpecialFunctionNode(node, asNameFromParent, defaultNameFromParent, defaultCollection,
193      asExportCollection, CUSTOM_BUILDER_METHOD);
194    collectSpecialFunctionNode(node, asNameFromParent, defaultNameFromParent, defaultCollection,
195      asExportCollection, GLOBAL_CUSTOM_BUILDER_METHOD);
196  }
197  if (ts.isExportAssignment(node) && node.expression && ts.isIdentifier(node.expression) &&
198    hasCollection(node.expression)) {
199    if (defaultNameFromParent) {
200      const propertiesName: string = node.expression.escapedText.toString();
201      setDependencies(defaultNameFromParent, linkCollection.get(propertiesName),
202        propertyCollection.get(propertiesName), propCollection.get(propertiesName),
203        builderParamObjectCollection.get(propertiesName), stateCollection.get(propertiesName),
204        regularCollection.get(propertiesName), storagePropCollection.get(propertiesName),
205        storageLinkCollection.get(propertiesName), provideCollection.get(propertiesName),
206        consumeCollection.get(propertiesName), objectLinkCollection.get(propertiesName),
207        localStorageLinkCollection.get(propertiesName), localStoragePropCollection.get(propertiesName),
208        builderParamInitialization.get(propertiesName));
209    }
210    addDefaultExport(node);
211  }
212  if (ts.isExportAssignment(node) && node.expression && ts.isIdentifier(node.expression)) {
213    if (defaultNameFromParent) {
214      asNameFromParent.set(node.expression.getText(), asNameFromParent.get(defaultNameFromParent));
215    }
216    defaultCollection.add(node.expression.getText());
217  }
218  if (ts.isExportDeclaration(node) && node.exportClause &&
219    ts.isNamedExports(node.exportClause) && node.exportClause.elements) {
220    node.exportClause.elements.forEach(item => {
221      if (process.env.watchMode === 'true') {
222        exportCollection.add((item.propertyName ? item.propertyName : item.name).escapedText.toString());
223      }
224      if (item.name && ts.isIdentifier(item.name)) {
225        if (!item.propertyName) {
226          asExportCollection.set(item.name.escapedText.toString(), item.name.escapedText.toString());
227        } else if (item.propertyName && ts.isIdentifier(item.propertyName)) {
228          validateModuleName(item.name, log, sourceFile, fileResolvePath);
229          if (hasCollection(item.propertyName)) {
230            let asExportName: string = item.name.escapedText.toString();
231            const asExportPropertyName: string = item.propertyName.escapedText.toString();
232            if (asNameFromParent.has(asExportName)) {
233              asExportName = asNameFromParent.get(asExportName);
234            }
235            setDependencies(asExportName, linkCollection.get(asExportPropertyName),
236              propertyCollection.get(asExportPropertyName),
237              propCollection.get(asExportPropertyName),
238              builderParamObjectCollection.get(asExportPropertyName),
239              stateCollection.get(asExportPropertyName), regularCollection.get(asExportPropertyName),
240              storagePropCollection.get(asExportPropertyName), storageLinkCollection.get(asExportPropertyName),
241              provideCollection.get(asExportPropertyName), consumeCollection.get(asExportPropertyName),
242              objectLinkCollection.get(asExportPropertyName), localStorageLinkCollection.get(asExportPropertyName),
243              localStoragePropCollection.get(asExportPropertyName), builderParamInitialization.get(asExportPropertyName));
244          }
245          asExportCollection.set(item.propertyName.escapedText.toString(), item.name.escapedText.toString());
246        }
247      }
248      if (item.name && ts.isIdentifier(item.name) && asNameFromParent.has(item.name.escapedText.toString()) &&
249        item.propertyName && ts.isIdentifier(item.propertyName)) {
250        asNameFromParent.set(item.propertyName.escapedText.toString(),
251          asNameFromParent.get(item.name.escapedText.toString()));
252      }
253    });
254  }
255  if (ts.isExportDeclaration(node) && node.moduleSpecifier &&
256    ts.isStringLiteral(node.moduleSpecifier)) {
257    if (process.env.watchMode === 'true' && node.exportClause && ts.isNamedExports(node.exportClause) &&
258      node.exportClause.elements) {
259      node.exportClause.elements.forEach(item => {
260        exportCollection.add((item.propertyName ? item.propertyName : item.name).escapedText.toString());
261        if (item.propertyName && ts.isIdentifier(item.propertyName) && item.name &&
262          ts.isIdentifier(item.name) && asNameFromParent.has(item.name.escapedText.toString())) {
263          asNameFromParent.set(item.propertyName.escapedText.toString(),
264            asNameFromParent.get(item.name.escapedText.toString()));
265          defaultCollection.add(item.name.escapedText.toString());
266        }
267      });
268    }
269    processImport(node, pagesDir, log, asNameFromParent, true, new Set(pathCollection));
270  }
271  if (ts.isImportDeclaration(node)) {
272    if (node.importClause && node.importClause.name && ts.isIdentifier(node.importClause.name) &&
273      asNameFromParent.has(node.importClause.name.getText())) {
274      processImport(node, pagesDir, log, asNameFromParent, false, new Set(pathCollection));
275    } else if (node.importClause && node.importClause.namedBindings &&
276      ts.isNamedImports(node.importClause.namedBindings) && node.importClause.namedBindings.elements) {
277      let nested: boolean = false;
278      node.importClause.namedBindings.elements.forEach(item => {
279        if (item.name && ts.isIdentifier(item.name) && asNameFromParent.has(item.name.escapedText.toString())) {
280          nested = true;
281          if (item.propertyName && ts.isIdentifier(item.propertyName)) {
282            asNameFromParent.set(item.propertyName.escapedText.toString(),
283              asNameFromParent.get(item.name.escapedText.toString()));
284          }
285        }
286      });
287      if (nested) {
288        processImport(node, pagesDir, log, asNameFromParent, false, new Set(pathCollection));
289      }
290    }
291  }
292  node.getChildren().reverse().forEach((item: ts.Node) => visitAllNode(item, sourceFile,
293    defaultNameFromParent, asNameFromParent, pagesDir, log, entryCollection, exportCollection,
294    defaultCollection, asExportCollection, pathCollection, fileResolvePath));
295}
296
297function collectSpecialFunctionNode(node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.StructDeclaration,
298  asNameFromParent: Map<string, string>, defaultNameFromParent: string, defaultCollection: Set<string>,
299  asExportCollection: Map<string, string>, collection: Set<string>): void {
300  const name: string = node.name.getText();
301  let componentName: string;
302  if (asNameFromParent.has(name)) {
303    collection.add(asNameFromParent.get(name));
304  } else if (node.modifiers && node.modifiers.length >= 1 && node.modifiers[0] &&
305    node.modifiers[0].kind === ts.SyntaxKind.ExportKeyword) {
306    if (node.modifiers.length == 1) {
307      collection.add(name);
308    } else if (node.modifiers.length >= 2 && node.modifiers[1] && node.modifiers[1].kind ===
309      ts.SyntaxKind.DefaultKeyword) {
310      collection.add(CUSTOM_COMPONENT_DEFAULT);
311      if (defaultNameFromParent && asNameFromParent.has(defaultNameFromParent)) {
312        collection.add(asNameFromParent.get(defaultNameFromParent));
313      }
314    }
315  } else if (defaultCollection.has(name)) {
316    collection.add(CUSTOM_COMPONENT_DEFAULT);
317  } else if (asExportCollection.has(name)) {
318    collection.add(asExportCollection.get(name));
319  }
320}
321
322function isExportEntry(node: ts.ClassDeclaration, log: LogInfo[], entryCollection: Set<string>,
323  exportCollection: Set<string>, defaultCollection: Set<string>, fileResolvePath: string,
324  sourceFile: ts.SourceFile): void {
325  if (process.env.watchMode === 'true' && node && node.decorators) {
326    let existExport: boolean = false;
327    let existEntry: boolean = false;
328    if (node.modifiers) {
329      for (let i = 0; i < node.modifiers.length; i++) {
330        if (node.modifiers[i].kind === ts.SyntaxKind.ExportKeyword) {
331          existExport = true;
332          break;
333        }
334      }
335    }
336    for (let i = 0; i < node.decorators.length; i++) {
337      if (node.decorators[i].getText() === COMPONENT_DECORATOR_ENTRY) {
338        entryCollection.add(node.name.escapedText.toString());
339        existEntry = true;
340        break;
341      }
342    }
343  }
344}
345
346function remindExportEntryComponent(node: ts.Node, log: LogInfo[], fileResolvePath: string,
347  sourceFile: ts.SourceFile): void {
348  const posOfNode: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition(node.getStart());
349  const line: number = posOfNode.line + 1;
350  const column: number = posOfNode.character + 1;
351  const warnInfo: LogInfo = {
352    type: LogType.WARN,
353    message: `It's not a recommended way to export struct with @Entry decorator, ` +
354      `which may cause ACE Engine error in component preview mode.`,
355    pos: node.getStart(),
356    fileName: fileResolvePath,
357    line: line,
358    column: column
359  };
360  if (!repeatLog.has(fileResolvePath)) {
361    log.push(warnInfo);
362    repeatLog.set(fileResolvePath, warnInfo);
363  }
364}
365
366function addDependencies(node: ts.ClassDeclaration, defaultNameFromParent: string,
367  asNameFromParent: Map<string, string>): void {
368  const componentName: string = node.name.getText();
369  const ComponentSet: IComponentSet = getComponentSet(node);
370  if (defaultNameFromParent && node.modifiers && node.modifiers.length >= 2 && node.modifiers[0] &&
371    node.modifiers[1] && node.modifiers[0].kind === ts.SyntaxKind.ExportKeyword &&
372    node.modifiers[1].kind === ts.SyntaxKind.DefaultKeyword) {
373    setDependencies(defaultNameFromParent, ComponentSet.links, ComponentSet.properties,
374      ComponentSet.props, ComponentSet.builderParams, ComponentSet.states, ComponentSet.regulars,
375      ComponentSet.storageProps, ComponentSet.storageLinks, ComponentSet.provides,
376      ComponentSet.consumes, ComponentSet.objectLinks, ComponentSet.localStorageLink,
377      ComponentSet.localStorageProp, ComponentSet.builderParamData);
378  } else if (asNameFromParent.has(componentName)) {
379    setDependencies(asNameFromParent.get(componentName), ComponentSet.links, ComponentSet.properties,
380      ComponentSet.props, ComponentSet.builderParams, ComponentSet.states, ComponentSet.regulars,
381      ComponentSet.storageProps, ComponentSet.storageLinks, ComponentSet.provides,
382      ComponentSet.consumes, ComponentSet.objectLinks, ComponentSet.localStorageLink,
383      ComponentSet.localStorageProp, ComponentSet.builderParamData);
384  } else {
385    setDependencies(componentName, ComponentSet.links, ComponentSet.properties, ComponentSet.props,
386      ComponentSet.builderParams, ComponentSet.states, ComponentSet.regulars,
387      ComponentSet.storageProps, ComponentSet.storageLinks, ComponentSet.provides,
388      ComponentSet.consumes, ComponentSet.objectLinks, ComponentSet.localStorageLink,
389      ComponentSet.localStorageProp, ComponentSet.builderParamData);
390  }
391}
392
393function addDefaultExport(node: ts.ClassDeclaration | ts.ExportAssignment): void {
394  let name: string;
395  if (ts.isClassDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
396    name = node.name.escapedText.toString();
397  } else if (ts.isExportAssignment(node) && node.expression && ts.isIdentifier(node.expression)) {
398    name = node.expression.escapedText.toString();
399  } else {
400    return;
401  }
402  setDependencies(CUSTOM_COMPONENT_DEFAULT,
403    linkCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
404      new Set([...linkCollection.get(CUSTOM_COMPONENT_DEFAULT), ...linkCollection.get(name)]) :
405      linkCollection.get(name),
406    propertyCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
407      new Set([...propertyCollection.get(CUSTOM_COMPONENT_DEFAULT),
408        ...propertyCollection.get(name)]) : propertyCollection.get(name),
409    propCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
410      new Set([...propCollection.get(CUSTOM_COMPONENT_DEFAULT), ...propCollection.get(name)]) :
411      propCollection.get(name),
412    builderParamObjectCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
413      new Set([...builderParamObjectCollection.get(CUSTOM_COMPONENT_DEFAULT),
414        ...builderParamObjectCollection.get(name)]) : builderParamObjectCollection.get(name),
415    stateCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
416      new Set([...stateCollection.get(CUSTOM_COMPONENT_DEFAULT),
417        ...stateCollection.get(name)]) : stateCollection.get(name),
418    regularCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
419      new Set([...regularCollection.get(CUSTOM_COMPONENT_DEFAULT),
420        ...regularCollection.get(name)]) : regularCollection.get(name),
421    storagePropCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
422      new Set([...storagePropCollection.get(CUSTOM_COMPONENT_DEFAULT),
423        ...storagePropCollection.get(name)]) : storagePropCollection.get(name),
424    storageLinkCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
425      new Set([...storageLinkCollection.get(CUSTOM_COMPONENT_DEFAULT),
426        ...storageLinkCollection.get(name)]) : storageLinkCollection.get(name),
427    provideCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
428      new Set([...provideCollection.get(CUSTOM_COMPONENT_DEFAULT),
429        ...provideCollection.get(name)]) : provideCollection.get(name),
430    consumeCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
431      new Set([...consumeCollection.get(CUSTOM_COMPONENT_DEFAULT),
432        ...consumeCollection.get(name)]) : consumeCollection.get(name),
433    objectLinkCollection.has(CUSTOM_COMPONENT_DEFAULT) ?
434      new Set([...objectLinkCollection.get(CUSTOM_COMPONENT_DEFAULT),
435        ...objectLinkCollection.get(name)]) : objectLinkCollection.get(name),
436    getNewLocalStorageMap(localStorageLinkCollection, name),
437    getNewLocalStorageMap(localStoragePropCollection, name),
438    builderParamInitialization.has(CUSTOM_COMPONENT_DEFAULT) ?
439      new Set([...builderParamInitialization.get(CUSTOM_COMPONENT_DEFAULT),
440        ...builderParamInitialization.get(name)]) : builderParamInitialization.get(name)
441  );
442}
443
444function getNewLocalStorageMap(collection: Map<string, Map<string, Set<string>>>, name: string)
445  : Map<string, Set<string>> {
446  let localStorageLinkMap: Map<string, Set<string>> = new Map();
447  if (collection.has(CUSTOM_COMPONENT_DEFAULT)) {
448    const tempSet: Set<string> = new Set();
449    if (collection.get(CUSTOM_COMPONENT_DEFAULT)) {
450      for (const key of collection.get(CUSTOM_COMPONENT_DEFAULT).keys()) {
451        tempSet.add(key);
452      }
453    }
454    if (collection.get(name)) {
455      for (const key of collection.get(name).keys()) {
456        tempSet.add(key);
457      }
458    }
459    localStorageLinkMap.set(name, tempSet);
460  } else {
461    localStorageLinkMap = collection.get(name);
462  }
463  return localStorageLinkMap;
464}
465
466function setDependencies(component: string, linkArray: Set<string>, propertyArray: Set<string>,
467  propArray: Set<string>, builderParamArray: Set<string>, stateArray: Set<string>,
468  regularArray: Set<string>, storagePropsArray: Set<string>, storageLinksArray: Set<string>,
469  providesArray: Set<string>, consumesArray: Set<string>, objectLinksArray: Set<string>,
470  localStorageLinkMap: Map<string, Set<string>>, localStoragePropMap: Map<string, Set<string>>,
471  builderParamData: Set<string>): void {
472  linkCollection.set(component, linkArray);
473  propertyCollection.set(component, propertyArray);
474  propCollection.set(component, propArray);
475  builderParamObjectCollection.set(component, builderParamArray);
476  objectLinkCollection.set(component, objectLinksArray);
477  componentCollection.customComponents.add(component);
478  stateCollection.set(component, stateArray);
479  regularCollection.set(component, regularArray);
480  storagePropCollection.set(component, storagePropsArray);
481  storageLinkCollection.set(component, storageLinksArray);
482  provideCollection.set(component, providesArray);
483  consumeCollection.set(component, consumesArray);
484  objectLinkCollection.set(component, objectLinksArray);
485  localStorageLinkCollection.set(component, localStorageLinkMap);
486  localStoragePropCollection.set(component, localStoragePropMap);
487  builderParamInitialization.set(component, builderParamData);
488}
489
490function hasCollection(node: ts.Identifier): boolean {
491  const name: string = node.escapedText.toString();
492  return linkCollection.has(name) ||
493    propCollection.has(name) ||
494    propertyCollection.has(name) ||
495    builderParamObjectCollection.has(name) ||
496    stateCollection.has(name) ||
497    regularCollection.has(name) ||
498    storagePropCollection.has(name) ||
499    storageLinkCollection.has(name) ||
500    provideCollection.has(name) ||
501    consumeCollection.has(name) ||
502    objectLinkCollection.has(name) ||
503    localStorageLinkCollection.has(name) ||
504    localStoragePropCollection.has(name)
505}
506
507function isModule(filePath: string): boolean {
508  return !/^(\.|\.\.)?\//.test(filePath) || filePath.indexOf(projectConfig.packageDir) > -1;
509}
510
511function isCustomComponent(node: ts.ClassDeclaration | ts.StructDeclaration): boolean {
512  if (node.decorators && node.decorators.length) {
513    for (let i = 0; i < node.decorators.length; ++i) {
514      const decoratorName: ts.Identifier = node.decorators[i].expression as ts.Identifier;
515      if (ts.isIdentifier(decoratorName) &&
516        CUSTOM_DECORATOR_NAME.has(decoratorName.escapedText.toString())) {
517        return true;
518      }
519    }
520  }
521  return false;
522}
523
524let packageJsonEntry: string = '';
525
526function isPackageJsonEntry(filePath: string): boolean {
527  const packageJsonPath: string = path.join(filePath, projectConfig.packageJson);
528  if (fs.existsSync(packageJsonPath)) {
529    let entryTypes: string;
530    let entryMain: string;
531    try {
532      const packageJson: Object =
533        (projectConfig.packageManagerType === 'npm' ? JSON : JSON5).parse(fs.readFileSync(packageJsonPath).toString());
534      entryTypes = packageJson.types;
535      entryMain = packageJson.main;
536    } catch (e) {
537      return false;
538    }
539    if (entryExist(filePath, entryTypes)) {
540      packageJsonEntry = path.resolve(filePath, entryTypes);
541      return true;
542    } else if (entryExist(filePath, entryMain)) {
543      packageJsonEntry = path.resolve(filePath, entryMain);
544      return true;
545    }
546  }
547}
548
549function entryExist(filePath: string, entry: string): boolean {
550  return typeof entry === 'string' && fs.existsSync(path.resolve(filePath, entry)) &&
551    fs.statSync(path.resolve(filePath, entry)).isFile();
552}
553
554function getModuleFilePath(filePath: string): string {
555  if (filePath && path.extname(filePath) !== EXTNAME_ETS && isModule(filePath)) {
556    filePath += EXTNAME_ETS;
557  }
558  return filePath;
559}
560
561function getFileResolvePath(fileResolvePath: string, pagesDir: string, filePath: string,
562  projectPath: string): string {
563  const moduleFilePath: string = getModuleFilePath(filePath);
564  const defaultModule: string = path.join(projectPath, moduleFilePath);
565  if (fs.existsSync(defaultModule)) {
566    return defaultModule;
567  }
568  let entryModule: string;
569  let etsModule: string;
570  if (!projectConfig.aceModuleJsonPath) {
571    entryModule = path.join(projectPath, '../../../../../', moduleFilePath);
572    etsModule = path.join(projectPath, '../../', moduleFilePath);
573  } else {
574    entryModule = path.join(projectPath, '../../../../', moduleFilePath);
575    etsModule = path.join(projectPath, '../', moduleFilePath);
576  }
577  if (fs.existsSync(entryModule)) {
578    return entryModule;
579  }
580  if (fs.existsSync(etsModule)) {
581    return etsModule;
582  }
583  let curPageDir: string = pagesDir;
584  while (!fs.existsSync(fileResolvePath)) {
585    if (filePath.indexOf(projectConfig.packageDir) > -1) {
586      fileResolvePath = path.join(curPageDir, filePath);
587    } else {
588      fileResolvePath = path.join(curPageDir, projectConfig.packageDir, filePath);
589    }
590    if (fs.existsSync(fileResolvePath + EXTNAME_ETS)) {
591      fileResolvePath = fileResolvePath + EXTNAME_ETS;
592    } else if (isPackageJsonEntry(fileResolvePath)) {
593      fileResolvePath = packageJsonEntry;
594      if (fs.statSync(fileResolvePath).isDirectory()) {
595        if (fs.existsSync(path.join(fileResolvePath, INDEX_ETS))) {
596          fileResolvePath = path.join(fileResolvePath, INDEX_ETS);
597        } else if (fs.existsSync(path.join(fileResolvePath, INDEX_TS))) {
598          fileResolvePath = path.join(fileResolvePath, INDEX_TS);
599        }
600      }
601    } else if (fs.existsSync(path.join(fileResolvePath, INDEX_ETS))) {
602      fileResolvePath = path.join(fileResolvePath, INDEX_ETS);
603    } else if (fs.existsSync(path.join(fileResolvePath, INDEX_TS))) {
604      fileResolvePath = path.join(fileResolvePath, INDEX_TS);
605    }
606    if (curPageDir === path.parse(curPageDir).root) {
607      break;
608    }
609    curPageDir = path.dirname(curPageDir);
610  }
611  return fileResolvePath;
612}
613
614export function getFileFullPath(filePath: string, pagesDir: string): string {
615  if (filePath && path.extname(filePath) !== EXTNAME_ETS && path.extname(filePath) !== EXTNAME_TS &&
616      !isModule(filePath)) {
617    const dirIndexEtsPath: string = path.resolve(path.resolve(pagesDir, filePath), INDEX_ETS);
618    const dirIndexTsPath: string = path.resolve(path.resolve(pagesDir, filePath), INDEX_TS);
619    if (/^(\.|\.\.)\//.test(filePath) && !fs.existsSync(path.resolve(pagesDir, filePath + EXTNAME_ETS)) &&
620      fs.existsSync(dirIndexEtsPath)) {
621      filePath = dirIndexEtsPath;
622    } else if (/^(\.|\.\.)\//.test(filePath) && !fs.existsSync(path.resolve(pagesDir, filePath + EXTNAME_TS)) &&
623      fs.existsSync(dirIndexTsPath)) {
624      filePath = dirIndexTsPath;
625    } else {
626      filePath += getExtensionIfUnfullySpecifiedFilepath(path.resolve(pagesDir, filePath));
627    }
628  }
629
630  let fileResolvePath: string;
631  if (/^(\.|\.\.)\//.test(filePath) && filePath.indexOf(projectConfig.packageDir) < 0) {
632    fileResolvePath = path.resolve(pagesDir, filePath);
633  } else if (/^\//.test(filePath) && filePath.indexOf(projectConfig.packageDir) < 0 ||
634    fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
635    fileResolvePath = filePath;
636  } else {
637    fileResolvePath = getFileResolvePath(fileResolvePath, pagesDir, filePath, projectConfig.projectPath);
638  }
639
640  return fileResolvePath;
641}
642
643function validateModuleName(moduleNode: ts.Identifier, log: LogInfo[], sourceFile?: ts.SourceFile,
644  fileResolvePath?: string): void {
645  const moduleName: string = moduleNode.escapedText.toString();
646  if (INNER_COMPONENT_NAMES.has(moduleName)) {
647    const error: LogInfo = {
648      type: LogType.ERROR,
649      message: `The module name '${moduleName}' can not be the same as the inner component name.`,
650      pos: moduleNode.getStart()
651    };
652    if (sourceFile && fileResolvePath) {
653      const posOfNode: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition(moduleNode.getStart());
654      const line: number = posOfNode.line + 1;
655      const column: number = posOfNode.character + 1;
656      Object.assign(error, {
657        fileName: fileResolvePath,
658        line: line,
659        column: column
660      });
661    }
662    log.push(error);
663  }
664}
665