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