• 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 path from 'path';
18import fs from 'fs';
19
20import {
21  PAGE_ENTRY_FUNCTION_NAME,
22  PREVIEW_COMPONENT_FUNCTION_NAME,
23  STORE_PREVIEW_COMPONENTS,
24  GET_PREVIEW_FLAG_FUNCTION_NAME,
25  COMPONENT_CONSTRUCTOR_UNDEFINED,
26  BUILD_ON,
27  COMPONENT_BUILDER_DECORATOR,
28  COMPONENT_CONCURRENT_DECORATOR,
29  COMPONENT_EXTEND_DECORATOR,
30  COMPONENT_STYLES_DECORATOR,
31  RESOURCE,
32  RESOURCE_TYPE,
33  WORKER_OBJECT,
34  RESOURCE_NAME_ID,
35  RESOURCE_NAME_TYPE,
36  RESOURCE_NAME_PARAMS,
37  RESOURCE_RAWFILE,
38  RESOURCE_NAME_BUNDLE,
39  RESOURCE_NAME_MODULE,
40  ATTRIBUTE_ANIMATETO,
41  GLOBAL_CONTEXT,
42  CHECK_COMPONENT_EXTEND_DECORATOR,
43  INSTANCE,
44  SET_CONTROLLER_CTR_TYPE,
45  SET_CONTROLLER_METHOD,
46  JS_DIALOG,
47  CUSTOM_DIALOG_CONTROLLER_BUILDER,
48  ESMODULE,
49  ARK,
50  EXTNAME_ETS,
51  GENERATE_ID,
52  _GENERATE_ID,
53  VIEWSTACKPROCESSOR,
54  STARTGETACCESSRECORDINGFOR,
55  ALLOCATENEWELMETIDFORNEXTCOMPONENT,
56  STOPGETACCESSRECORDING,
57  CARD_ENTRY_FUNCTION_NAME,
58  CARD_LOG_TYPE_COMPONENTS,
59  CARD_LOG_TYPE_DECORATORS,
60  CARD_LOG_TYPE_IMPORT,
61  COMPONENT_ANIMATABLE_EXTEND_DECORATOR,
62  CHECK_EXTEND_DECORATORS,
63  ELMTID,
64  ROUTENAME_NODE,
65  STORAGE_NODE,
66  STORAGE,
67  REGISTER_NAMED_ROUTE,
68  ROUTE_NAME,
69  PAGE_PATH,
70  ISINITIALRENDER,
71  CREATE_ANIMATABLE_PROPERTY,
72  COMPONENT_CREATE_FUNCTION,
73  COMPONENT_POP_FUNCTION,
74  UPDATE_ANIMATABLE_PROPERTY,
75  VIEW_STACK_PROCESSOR,
76  GET_AND_PUSH_FRAME_NODE,
77  COMPONENT_CONSTRUCTOR_PARENT,
78  FINISH_UPDATE_FUNC,
79} from './pre_define';
80import {
81  componentInfo,
82  LogInfo,
83  LogType,
84  hasDecorator,
85  FileLog,
86  getPossibleBuilderTypeParameter,
87  storedFileInfo,
88  ExtendResult
89} from './utils';
90import { writeFileSyncByNode } from './process_module_files';
91import {
92  componentCollection,
93  localStorageLinkCollection,
94  localStoragePropCollection,
95} from './validate_ui_syntax';
96import {
97  processComponentClass,
98  createParentParameter,
99  processBuildMember
100} from './process_component_class';
101import processImport, {
102  processImportModule
103} from './process_import';
104import {
105  processComponentBlock,
106  bindComponentAttr,
107  getName,
108  createViewStackProcessorStatement,
109  createFunction
110} from './process_component_build';
111import {
112  BUILDIN_STYLE_NAMES,
113  CUSTOM_BUILDER_METHOD,
114  EXTEND_ATTRIBUTE,
115  INNER_STYLE_FUNCTION,
116  GLOBAL_STYLE_FUNCTION,
117  INTERFACE_NODE_SET,
118  ID_ATTRS
119} from './component_map';
120import {
121  resources,
122  projectConfig,
123  partialUpdateConfig
124} from '../main';
125import { createCustomComponentNewExpression, createViewCreate } from './process_component_member';
126import { assignComponentParams } from './process_custom_component';
127import { ModuleSourceFile } from './fast_build/ark_compiler/module/module_source_file';
128
129export const transformLog: FileLog = new FileLog();
130export let contextGlobal: ts.TransformationContext;
131export let resourceFileName: string = '';
132export const builderTypeParameter: {params: string[]} = {params: []};
133
134export let hasTsNoCheckOrTsIgnoreFiles: string[] = [];
135export let compilingEtsOrTsFiles: string[] = [];
136
137export function processUISyntax(program: ts.Program, ut = false): Function {
138  let entryNodeKey: ts.Expression;
139  return (context: ts.TransformationContext) => {
140    contextGlobal = context;
141    let pagesDir: string;
142    return (node: ts.SourceFile) => {
143      const hasTsNoCheckOrTsIgnore = ts.hasTsNoCheckOrTsIgnoreFlag(node);
144      compilingEtsOrTsFiles.push(path.normalize(node.fileName));
145      pagesDir = path.resolve(path.dirname(node.fileName));
146      resourceFileName = path.resolve(node.fileName);
147      if (process.env.compiler === BUILD_ON || process.env.compileTool === 'rollup') {
148        storedFileInfo.transformCacheFiles[node.fileName] = {
149          mtimeMs: fs.existsSync(node.fileName) ? fs.statSync(node.fileName).mtimeMs : 0,
150          children: []
151        }
152        transformLog.sourceFile = node;
153        preprocessIdAttrs(node.fileName);
154        if (!ut && (process.env.compileMode !== 'moduleJson' &&
155          path.resolve(node.fileName) === path.resolve(projectConfig.projectPath, 'app.ets') ||
156          /\.ts$/.test(node.fileName))) {
157          node = ts.visitEachChild(node, processResourceNode, context);
158          if (projectConfig.compileMode === ESMODULE) {
159            if (projectConfig.processTs === true) {
160              const processedNode: ts.SourceFile = ts.getTypeExportImportAndConstEnumTransformer(context)(node);
161              if (process.env.compileTool === 'rollup') {
162                hasTsNoCheckOrTsIgnore ? hasTsNoCheckOrTsIgnoreFiles.push(path.normalize(processedNode.fileName)) :
163                  ModuleSourceFile.newSourceFile(path.normalize(processedNode.fileName), processedNode);
164              } else {
165                writeFileSyncByNode(processedNode, projectConfig);
166              }
167            }
168          }
169          return node;
170        }
171        const id: number = ++componentInfo.id;
172        node = ts.visitEachChild(node, processAllNodes, context);
173        node = createEntryNode(node, context, entryNodeKey, id);
174        GLOBAL_STYLE_FUNCTION.forEach((block, styleName) => {
175          BUILDIN_STYLE_NAMES.delete(styleName);
176        });
177        GLOBAL_STYLE_FUNCTION.clear();
178        const statements: ts.Statement[] = Array.from(node.statements);
179        if (!partialUpdateConfig.partialUpdateMode) {
180          generateId(statements, node);
181        }
182        INTERFACE_NODE_SET.forEach(item => {
183          statements.unshift(item);
184        });
185        node = ts.factory.updateSourceFile(node, statements);
186        INTERFACE_NODE_SET.clear();
187        if (projectConfig.compileMode === ESMODULE && projectConfig.processTs === true) {
188          const processedNode: ts.SourceFile = ts.getTypeExportImportAndConstEnumTransformer(context)(node);
189          if (process.env.compileTool === 'rollup') {
190            hasTsNoCheckOrTsIgnore ? hasTsNoCheckOrTsIgnoreFiles.push(path.normalize(processedNode.fileName)) :
191              ModuleSourceFile.newSourceFile(path.normalize(processedNode.fileName), processedNode);
192          } else {
193            writeFileSyncByNode(processedNode, projectConfig);
194          }
195        }
196        return node;
197      } else {
198        return node;
199      }
200    };
201
202    function entryKeyNode(node: ts.Node): ts.Expression {
203      if (node && node.decorators && node.decorators.length) {
204        node.decorators.forEach(item => {
205          if (item.expression && ts.isCallExpression(item.expression) && ts.isIdentifier(item.expression.expression) &&
206            item.expression.expression.escapedText.toString() === 'Entry' && item.expression.arguments &&
207            item.expression.arguments.length && ts.isIdentifier(item.expression.arguments[0])) {
208            entryNodeKey = item.expression.arguments[0];
209          }
210        });
211      }
212      return entryNodeKey;
213    }
214
215    function isESObjectNode(node: ts.Node): boolean {
216      if (node.kind === ts.SyntaxKind.TypeReference) {
217        const n: TypeReferenceNode = node as TypeReferenceNode;
218        if (n.typeName?.kind === ts.SyntaxKind.Identifier && (n.typeName as ts.Identifier).escapedText === 'ESObject') {
219          return true;
220        }
221      }
222      return false;
223    }
224
225    function processAllNodes(node: ts.Node): ts.Node {
226      if (projectConfig.compileMode === 'esmodule' && process.env.compileTool === 'rollup' &&
227        ts.isImportDeclaration(node)) {
228        processImportModule(node);
229      } else if ((projectConfig.compileMode !== 'esmodule' || process.env.compileTool !== 'rollup') &&
230        (ts.isImportDeclaration(node) || ts.isImportEqualsDeclaration(node) ||
231        ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier))) {
232        processImport(node, pagesDir, transformLog.errors);
233      }
234      if (ts.isStructDeclaration(node)) {
235        componentCollection.currentClassName = node.name.getText();
236        componentCollection.entryComponent === componentCollection.currentClassName && entryKeyNode(node);
237        node = processComponentClass(node, context, transformLog.errors, program);
238        componentCollection.currentClassName = null;
239        INNER_STYLE_FUNCTION.forEach((block, styleName) => {
240          BUILDIN_STYLE_NAMES.delete(styleName);
241        });
242        INNER_STYLE_FUNCTION.clear();
243      } else if (ts.isFunctionDeclaration(node)) {
244        if (hasDecorator(node, COMPONENT_EXTEND_DECORATOR, null, transformLog.errors)) {
245          node = processExtend(node, transformLog.errors, COMPONENT_EXTEND_DECORATOR);
246        } else if (hasDecorator(node, COMPONENT_BUILDER_DECORATOR) && node.name && node.body &&
247          ts.isBlock(node.body)) {
248          CUSTOM_BUILDER_METHOD.add(node.name.getText());
249          builderTypeParameter.params = getPossibleBuilderTypeParameter(node.parameters);
250          let parameters: ts.NodeArray<ts.ParameterDeclaration> =
251            ts.factory.createNodeArray(Array.from(node.parameters));
252          parameters.push(createParentParameter());
253          node = ts.factory.updateFunctionDeclaration(node, undefined, node.modifiers,
254            node.asteriskToken, node.name, node.typeParameters, parameters, node.type,
255            processComponentBlock(node.body, false, transformLog.errors, false, true,
256              node.name.getText(), undefined, true));
257          builderTypeParameter.params = [];
258          node = processBuildMember(node, context, transformLog.errors, true);
259        } else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) {
260          if (node.parameters.length === 0) {
261            node = undefined;
262          } else {
263            transformLog.errors.push({
264              type: LogType.ERROR,
265              message: `@Styles can't have parameters.`,
266              pos: node.getStart()
267            });
268          }
269        } else if (hasDecorator(node, COMPONENT_CONCURRENT_DECORATOR)) {
270          // ark compiler's feature
271          node = processConcurrent(node);
272        } else if (hasDecorator(node, COMPONENT_ANIMATABLE_EXTEND_DECORATOR, null, transformLog.errors)) {
273          node = processExtend(node, transformLog.errors, COMPONENT_ANIMATABLE_EXTEND_DECORATOR);
274        }
275      } else if (isResource(node)) {
276        node = processResourceData(node as ts.CallExpression);
277      } else if (isWorker(node)) {
278        node = processWorker(node as ts.NewExpression);
279      } else if (isAnimateTo(node)) {
280        node = processAnimateTo(node as ts.CallExpression);
281      } else if (isCustomDialogController(node)) {
282        node = createCustomDialogController(node.parent, node, transformLog.errors);
283      } else if (isESObjectNode(node)) {
284        node = ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
285      }
286      return ts.visitEachChild(node, processAllNodes, context);
287    }
288    function processResourceNode(node: ts.Node): ts.Node {
289      if (isResource(node)) {
290        node = processResourceData(node as ts.CallExpression);
291      }
292      return ts.visitEachChild(node, processResourceNode, context);
293    }
294  };
295}
296
297function generateId(statements: ts.Statement[], node: ts.SourceFile): void {
298  statements.unshift(
299    ts.factory.createVariableStatement(
300      undefined,
301      ts.factory.createVariableDeclarationList(
302        [ts.factory.createVariableDeclaration(
303          ts.factory.createIdentifier(_GENERATE_ID),
304          undefined,
305          ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
306          ts.factory.createNumericLiteral('0')
307        )],
308        ts.NodeFlags.Let
309      )
310    ),
311    ts.factory.createFunctionDeclaration(
312      undefined,
313      undefined,
314      undefined,
315      ts.factory.createIdentifier(GENERATE_ID),
316      undefined,
317      [],
318      ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
319      ts.factory.createBlock(
320        [ts.factory.createReturnStatement(ts.factory.createBinaryExpression(
321          ts.factory.createStringLiteral(path.basename(node.fileName, EXTNAME_ETS) + '_'),
322          ts.factory.createToken(ts.SyntaxKind.PlusToken), ts.factory.createPrefixUnaryExpression(
323            ts.SyntaxKind.PlusPlusToken,
324            ts.factory.createIdentifier(_GENERATE_ID)
325          )))],
326        true
327      )
328    )
329  );
330}
331
332function preprocessIdAttrs(fileName: string): void {
333  for (const [id, idInfo] of ID_ATTRS) {
334    if (fileName === idInfo.get('path')) {
335      ID_ATTRS.delete(id);
336    }
337  }
338}
339
340function isCustomDialogController(node: ts.Expression) {
341  const tempParent: ts.Node = node.parent;
342  // @ts-ignore
343  if (!node.parent && node.original) {
344    // @ts-ignore
345    node.parent = node.original.parent;
346  }
347  if (ts.isNewExpression(node) && node.expression && ts.isIdentifier(node.expression) &&
348    node.expression.escapedText.toString() === SET_CONTROLLER_CTR_TYPE) {
349    return true;
350  } else {
351    // @ts-ignore
352    node.parent = tempParent;
353    return false;
354  }
355}
356
357function createCustomDialogController(parent: ts.Expression, node: ts.NewExpression,
358  log: LogInfo[]): ts.NewExpression {
359  if (node.arguments && node.arguments.length === 1 &&
360    ts.isObjectLiteralExpression(node.arguments[0]) && node.arguments[0].properties) {
361    const newproperties: ts.ObjectLiteralElementLike[] = node.arguments[0].properties.map((item) => {
362      const componentName: string = isCustomDialogControllerPropertyAssignment(item, log);
363      if (componentName !== null) {
364        item = processCustomDialogControllerPropertyAssignment(parent,
365          item as ts.PropertyAssignment, componentName);
366      }
367      return item;
368    });
369    return ts.factory.createNewExpression(node.expression, node.typeArguments,
370      [ts.factory.createObjectLiteralExpression(newproperties, true), ts.factory.createThis()]);
371  } else {
372    return node;
373  }
374}
375
376function isCustomDialogControllerPropertyAssignment(node: ts.ObjectLiteralElementLike,
377  log: LogInfo[]): string {
378  if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) &&
379    node.name.getText() === CUSTOM_DIALOG_CONTROLLER_BUILDER) {
380    if (node.initializer) {
381      const componentName: string = getName(node.initializer);
382      if (componentCollection.customDialogs.has(componentName)) {
383        return componentName;
384      }
385    } else {
386      validateCustomDialogControllerBuilderInit(node, log);
387    }
388  }
389  return null;
390}
391
392function validateCustomDialogControllerBuilderInit(node: ts.ObjectLiteralElementLike,
393  log: LogInfo[]): void {
394  log.push({
395    type: LogType.ERROR,
396    message: 'The builder should be initialized with a @CustomDialog Component.',
397    pos: node.getStart()
398  });
399}
400
401function processCustomDialogControllerPropertyAssignment(parent: ts.Expression,
402  node: ts.PropertyAssignment, componentName: string): ts.PropertyAssignment {
403  if (ts.isCallExpression(node.initializer)) {
404    return ts.factory.updatePropertyAssignment(node, node.name,
405      processCustomDialogControllerBuilder(parent, node.initializer, componentName));
406  }
407}
408
409function processCustomDialogControllerBuilder(parent: ts.Expression,
410  node: ts.CallExpression, componentName: string): ts.ArrowFunction {
411  const newExp: ts.Expression = createCustomComponentNewExpression(node, componentName, false, false, true);
412  const jsDialog: ts.Identifier = ts.factory.createIdentifier(JS_DIALOG);
413  return createCustomComponentBuilderArrowFunction(node, parent, jsDialog, newExp);
414}
415
416function createCustomComponentBuilderArrowFunction(node: ts.CallExpression, parent: ts.Expression,
417  jsDialog: ts.Identifier, newExp: ts.Expression): ts.ArrowFunction {
418  let mountNodde: ts.PropertyAccessExpression;
419  if (ts.isBinaryExpression(parent)) {
420    mountNodde = parent.left;
421  } else if (ts.isVariableDeclaration(parent) || ts.isPropertyDeclaration(parent)) {
422    mountNodde = ts.factory.createPropertyAccessExpression(ts.factory.createThis(),
423      parent.name as ts.Identifier);
424  }
425  return ts.factory.createArrowFunction(
426    undefined,
427    undefined,
428    [],
429    undefined,
430    ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
431    ts.factory.createBlock(
432      [
433        partialUpdateConfig.partialUpdateMode ? assignComponentParams(node) : undefined,
434        ts.factory.createVariableStatement(
435          undefined,
436          ts.factory.createVariableDeclarationList(
437            [ts.factory.createVariableDeclaration(jsDialog, undefined, undefined, newExp)],
438            ts.NodeFlags.Let
439          )
440        ),
441        ts.factory.createExpressionStatement(
442          ts.factory.createCallExpression(
443            ts.factory.createPropertyAccessExpression(
444              jsDialog,
445              ts.factory.createIdentifier(SET_CONTROLLER_METHOD)
446            ),
447            undefined,
448            mountNodde ? [mountNodde] : undefined
449          )
450        ),
451        ts.factory.createExpressionStatement(createViewCreate(jsDialog))
452      ],
453      true
454    )
455  );
456}
457
458export function isResource(node: ts.Node): boolean {
459  return ts.isCallExpression(node) && ts.isIdentifier(node.expression) &&
460    (node.expression.escapedText.toString() === RESOURCE ||
461    node.expression.escapedText.toString() === RESOURCE_RAWFILE) && node.arguments.length > 0;
462}
463
464export function isAnimateTo(node: ts.Node): boolean {
465  return ts.isCallExpression(node) && ts.isIdentifier(node.expression) &&
466    node.expression.escapedText.toString() === ATTRIBUTE_ANIMATETO;
467}
468
469export function processResourceData(node: ts.CallExpression,
470  previewLog: {isAcceleratePreview: boolean, log: LogInfo[]} = {isAcceleratePreview: false, log: []}): ts.Node {
471  if (ts.isStringLiteral(node.arguments[0])) {
472    if (node.expression.getText() === RESOURCE_RAWFILE) {
473      isResourcefile(node, previewLog);
474      return createResourceParam(0, RESOURCE_TYPE.rawfile, [node.arguments[0]]);
475    } else {
476      return getResourceDataNode(node, previewLog);
477    }
478  }
479  return node;
480}
481
482function getResourceDataNode(node: ts.CallExpression,
483  previewLog: {isAcceleratePreview: boolean, log: LogInfo[]}): ts.Node {
484  const resourceData: string[] = (node.arguments[0] as ts.StringLiteral).text.trim().split('.');
485  if (preCheckResourceData(resourceData, resources, node.arguments[0].getStart(), previewLog)) {
486    const resourceType: number = RESOURCE_TYPE[resourceData[1]];
487    if (resourceType === undefined) {
488      transformLog.errors.push({
489        type: LogType.ERROR,
490        message: `The resource type ${resourceData[1]} is not supported.`,
491        pos: node.getStart()
492      });
493      return node;
494    }
495    const resourceValue: number = resources[resourceData[0]][resourceData[1]][resourceData[2]];
496    return createResourceParam(resourceValue, resourceType,
497      projectConfig.compileHar ? Array.from(node.arguments) : Array.from(node.arguments).slice(1));
498  }
499  return node;
500}
501
502function isResourcefile(node: ts.CallExpression, previewLog: {isAcceleratePreview: boolean, log: LogInfo[]}): void {
503  if (process.env.rawFileResource && !storedFileInfo.resourcesArr.has(node.arguments[0].text) &&
504    !previewLog.isAcceleratePreview && process.env.compileMode === 'moduleJson') {
505    transformLog.errors.push({
506      type: LogType.WARN,
507      message: `No such '${node.arguments[0].text}' resource in current module.`,
508      pos: node.getStart()
509    });
510  }
511}
512
513function createResourceParam(resourceValue: number, resourceType: number, argsArr: ts.Expression[]):
514  ts.ObjectLiteralExpression {
515  if (projectConfig.compileHar) {
516    projectConfig.bundleName = '';
517    projectConfig.moduleName = '';
518    resourceValue = -1;
519  }
520
521  const propertyArray: Array<ts.PropertyAssignment> = [
522    ts.factory.createPropertyAssignment(
523      ts.factory.createStringLiteral(RESOURCE_NAME_ID),
524      ts.factory.createNumericLiteral(resourceValue)
525    ),
526    ts.factory.createPropertyAssignment(
527      ts.factory.createStringLiteral(RESOURCE_NAME_TYPE),
528      ts.factory.createNumericLiteral(resourceType)
529    ),
530    ts.factory.createPropertyAssignment(
531      ts.factory.createIdentifier(RESOURCE_NAME_PARAMS),
532      ts.factory.createArrayLiteralExpression(argsArr, false)
533    )
534  ];
535
536  if (projectConfig.bundleName || projectConfig.bundleName === '') {
537    propertyArray.push(ts.factory.createPropertyAssignment(
538      ts.factory.createStringLiteral(RESOURCE_NAME_BUNDLE),
539      ts.factory.createStringLiteral(projectConfig.bundleName)
540    ));
541  }
542
543  if (projectConfig.moduleName || projectConfig.moduleName === '') {
544    propertyArray.push(ts.factory.createPropertyAssignment(
545      ts.factory.createStringLiteral(RESOURCE_NAME_MODULE),
546      ts.factory.createStringLiteral(projectConfig.moduleName)
547    ));
548  }
549
550  const resourceParams: ts.ObjectLiteralExpression = ts.factory.createObjectLiteralExpression(
551    propertyArray, false);
552  return resourceParams;
553}
554
555function preCheckResourceData(resourceData: string[], resources: object, pos: number,
556  previewLog: {isAcceleratePreview: boolean, log: LogInfo[]}): boolean {
557  if (previewLog.isAcceleratePreview) {
558    return validateResourceData(resourceData, resources, pos, previewLog.log, true);
559  } else {
560    return validateResourceData(resourceData, resources, pos, transformLog.errors, false);
561  }
562}
563
564function validateResourceData(resourceData: string[], resources: object, pos: number, log: LogInfo[], isAcceleratePreview: boolean): boolean {
565  if (resourceData.length !== 3) {
566    log.push({
567      type: LogType.ERROR,
568      message: 'The input parameter is not supported.',
569      pos: pos
570    });
571  } else {
572    if (!isAcceleratePreview && process.env.compileTool === 'rollup' && process.env.compileMode === 'moduleJson') {
573      storedFileInfo.collectResourceInFile(resourceData[1] + '_' + resourceData[2], path.resolve(resourceFileName));
574    }
575    if (!resources[resourceData[0]]) {
576      log.push({
577        type: LogType.ERROR,
578        message: `Unknown resource source '${resourceData[0]}'.`,
579        pos: pos
580      });
581    } else if (!resources[resourceData[0]][resourceData[1]]) {
582      log.push({
583        type: LogType.ERROR,
584        message: `Unknown resource type '${resourceData[1]}'.`,
585        pos: pos
586      });
587    } else if (!resources[resourceData[0]][resourceData[1]][resourceData[2]]) {
588      log.push({
589        type: LogType.ERROR,
590        message: `Unknown resource name '${resourceData[2]}'.`,
591        pos: pos
592      });
593    } else {
594      return true;
595    }
596  }
597  return false;
598}
599
600function isWorker(node: ts.Node): boolean {
601  return ts.isNewExpression(node) && ts.isPropertyAccessExpression(node.expression) &&
602    ts.isIdentifier(node.expression.name) &&
603    node.expression.name.escapedText.toString() === WORKER_OBJECT;
604}
605
606function processWorker(node: ts.NewExpression): ts.Node {
607  if (node.arguments.length && ts.isStringLiteral(node.arguments[0])) {
608    const args: ts.Expression[] = Array.from(node.arguments);
609    // @ts-ignore
610    const workerPath: string = node.arguments[0].text;
611    const stringNode: ts.StringLiteral = ts.factory.createStringLiteral(
612      workerPath.replace(/\.ts$/, '.js'));
613    args.splice(0, 1, stringNode);
614    return ts.factory.updateNewExpression(node, node.expression, node.typeArguments, args);
615  }
616  return node;
617}
618
619export function processAnimateTo(node: ts.CallExpression): ts.CallExpression {
620  return ts.factory.updateCallExpression(node, ts.factory.createPropertyAccessExpression(
621    ts.factory.createIdentifier(GLOBAL_CONTEXT), ts.factory.createIdentifier(ATTRIBUTE_ANIMATETO)),
622  node.typeArguments, node.arguments);
623}
624
625function processExtend(node: ts.FunctionDeclaration, log: LogInfo[],
626  decoratorName: string): ts.FunctionDeclaration {
627  const componentName: string = isExtendFunction(node, { decoratorName: '', componentName: '' }, true);
628  if (componentName && node.body && node.body.statements.length) {
629    const statementArray: ts.Statement[] = [];
630    let bodynode: ts.Block;
631    if (decoratorName === COMPONENT_EXTEND_DECORATOR) {
632      const attrSet: ts.CallExpression = node.body.statements[0].expression;
633      if (isOriginalExtend(node.body)) {
634        const changeCompName: ts.ExpressionStatement = ts.factory.createExpressionStatement(processExtendBody(attrSet));
635        bindComponentAttr(changeCompName as ts.ExpressionStatement,
636          ts.factory.createIdentifier(componentName), statementArray, log);
637      } else {
638        bodynode = ts.visitEachChild(node.body, traverseExtendExpression, contextGlobal);
639      }
640      let extendFunctionName: string;
641      if (node.name.getText().startsWith('__' + componentName + '__')) {
642        extendFunctionName = node.name.getText();
643      } else {
644        extendFunctionName = '__' + componentName + '__' + node.name.getText();
645        collectExtend(EXTEND_ATTRIBUTE, componentName, node.name.escapedText.toString());
646      }
647      return ts.factory.updateFunctionDeclaration(node, undefined, node.modifiers, node.asteriskToken,
648        ts.factory.createIdentifier(extendFunctionName), node.typeParameters,
649        node.parameters, ts.factory.createToken(ts.SyntaxKind.VoidKeyword), isOriginalExtend(node.body) ?
650          ts.factory.updateBlock(node.body, statementArray) : bodynode);
651    }
652    if (decoratorName === COMPONENT_ANIMATABLE_EXTEND_DECORATOR) {
653      bindComponentAttr(node.body.statements[0],
654        ts.factory.createIdentifier(componentName), statementArray, log);
655      return ts.factory.updateFunctionDeclaration(node, undefined, node.modifiers, node.asteriskToken,
656        node.name, node.typeParameters,
657        [...node.parameters, ...createAnimatableParameterNode()], ts.factory.createToken(ts.SyntaxKind.VoidKeyword),
658        ts.factory.updateBlock(node.body, createAnimatableBody(componentName, node.name,
659          node.parameters, statementArray)));
660    }
661  }
662  function traverseExtendExpression(node: ts.Node): ts.Node {
663    if (ts.isExpressionStatement(node) && isDollarNode(node, componentName)) {
664      const changeCompName: ts.ExpressionStatement =
665        ts.factory.createExpressionStatement(processExtendBody(node.expression, componentName));
666      const statementArray: ts.Statement[] = [];
667      bindComponentAttr(changeCompName, ts.factory.createIdentifier(componentName), statementArray, []);
668      return ts.factory.createBlock(statementArray, true);
669    }
670    return ts.visitEachChild(node, traverseExtendExpression, contextGlobal);
671  }
672}
673
674function createAnimatableParameterNode(): ts.ParameterDeclaration[] {
675  return [
676    ts.factory.createParameterDeclaration(
677      undefined, undefined, undefined, ts.factory.createIdentifier(ELMTID)),
678    ts.factory.createParameterDeclaration(
679      undefined, undefined, undefined, ts.factory.createIdentifier(ISINITIALRENDER)),
680    ts.factory.createParameterDeclaration(
681      undefined, undefined, undefined, ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARENT))
682  ];
683}
684
685function createAnimatableBody(componentName: string, funcName: ts.Identifier,
686  parameters: ts.NodeArray<ts.ParameterDeclaration>, attrArray: ts.Statement[]): ts.Statement[] {
687  const paramNode: ts.Identifier[] = [];
688  parameters.forEach((item: ts.ParameterDeclaration) => {
689    if (item.name && ts.isIdentifier(item.name)) {
690      paramNode.push(item.name);
691    }
692  });
693  return [
694    ts.factory.createIfStatement(
695      ts.factory.createIdentifier(ISINITIALRENDER),
696      ts.factory.createBlock([
697        createAnimatableProperty(componentName, funcName, parameters, paramNode, attrArray),
698        ...attrArray
699      ], true),
700      ts.factory.createBlock([
701        ts.factory.createExpressionStatement(ts.factory.createCallExpression(
702          ts.factory.createPropertyAccessExpression(
703            ts.factory.createIdentifier(componentName),
704            ts.factory.createIdentifier(UPDATE_ANIMATABLE_PROPERTY)
705          ), undefined,
706          [ ts.factory.createStringLiteral(funcName.escapedText.toString()), ...paramNode ]
707        ))
708      ])
709    )
710  ];
711}
712
713function createAnimatableProperty(componentName: string, funcName: ts.Identifier,
714  parameters: ts.NodeArray<ts.ParameterDeclaration>,
715  paramNode: ts.Identifier[], attrArray: ts.Statement[]) {
716  const componentIdentifier: ts.Identifier = ts.factory.createIdentifier(componentName);
717  return ts.factory.createExpressionStatement(
718    ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(
719      componentIdentifier,
720      ts.factory.createIdentifier(CREATE_ANIMATABLE_PROPERTY)),
721    undefined, [
722      ts.factory.createStringLiteral(funcName.escapedText.toString()),
723      ...paramNode,
724      ts.factory.createArrowFunction(undefined, undefined, parameters, undefined,
725        ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
726        ts.factory.createBlock([
727          createViewStackProcessorStatement(STARTGETACCESSRECORDINGFOR, ELMTID),
728          createAnimatableFrameNode(componentName),
729          ...attrArray,
730          createViewStackProcessorStatement(STOPGETACCESSRECORDING),
731          createAnimatableUpdateFunc()
732        ], true))
733    ]
734    )
735  );
736}
737
738function createAnimatableFrameNode(componentName: string): ts.ExpressionStatement {
739  return ts.factory.createExpressionStatement(ts.factory.createCallExpression(
740    ts.factory.createPropertyAccessExpression(
741      ts.factory.createIdentifier(VIEW_STACK_PROCESSOR),
742      ts.factory.createIdentifier(GET_AND_PUSH_FRAME_NODE),
743    ), undefined,
744    [
745      ts.factory.createStringLiteral(componentName),
746      ts.factory.createIdentifier(ELMTID)
747    ]
748  ));
749}
750
751function createAnimatableUpdateFunc(): ts.ExpressionStatement {
752  return ts.factory.createExpressionStatement(ts.factory.createCallExpression(
753    ts.factory.createPropertyAccessExpression(
754      ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARENT),
755      ts.factory.createIdentifier(FINISH_UPDATE_FUNC),
756    ), undefined, [ts.factory.createIdentifier(ELMTID)]
757  ));
758}
759
760function processConcurrent(node: ts.FunctionDeclaration): ts.FunctionDeclaration {
761  if (node.body) {
762    const statementArray: ts.Statement[]
763      = [ts.factory.createExpressionStatement(ts.factory.createStringLiteral('use concurrent')),
764        ...node.body.statements];
765    return ts.factory.updateFunctionDeclaration(node, undefined, node.modifiers, node.asteriskToken, node.name,
766      node.typeParameters, node.parameters, node.type, ts.factory.updateBlock(node.body, statementArray));
767  }
768  return node;
769}
770
771export function isOriginalExtend(node: ts.Block): boolean {
772  let innerNode: ts.Node = node.statements[0];
773  if (node.statements.length === 1 && ts.isExpressionStatement(innerNode)) {
774    while (innerNode.expression) {
775      innerNode = innerNode.expression;
776    }
777    if (ts.isIdentifier(innerNode) && innerNode.pos && innerNode.end && innerNode.pos === innerNode.end &&
778      innerNode.escapedText.toString().match(/Instance$/)) {
779      return true;
780    }
781  }
782  return false;
783}
784
785function isDollarNode(node: ts.ExpressionStatement, componentName: string): boolean {
786  let innerNode: ts.Node = node;
787  while (innerNode.expression) {
788    innerNode = innerNode.expression;
789  }
790  let changedIdentifier: string = '$';
791  if (process.env.compileTool === 'rollup' && storedFileInfo.reUseProgram) {
792    changedIdentifier = `${componentName}Instance`;
793  }
794  if (ts.isIdentifier(innerNode) && innerNode.getText() === changedIdentifier) {
795    return true;
796  } else {
797    return false;
798  }
799}
800
801function processExtendBody(node: ts.Node, componentName?: string): ts.Expression {
802  switch (node.kind) {
803    case ts.SyntaxKind.CallExpression:
804      return ts.factory.createCallExpression(processExtendBody(node.expression, componentName),
805        undefined, node.arguments);
806    case ts.SyntaxKind.PropertyAccessExpression:
807      return ts.factory.createPropertyAccessExpression(
808        processExtendBody(node.expression, componentName), node.name);
809    case ts.SyntaxKind.Identifier:
810      if (!componentName) {
811        return ts.factory.createIdentifier(node.escapedText.toString().replace(INSTANCE, ''));
812      } else {
813        return ts.factory.createIdentifier(componentName);
814      }
815  }
816}
817
818export function collectExtend(collectionSet: Map<string, Set<string>>, component: string, attribute: string): void {
819  if (collectionSet.has(component)) {
820    collectionSet.get(component).add(attribute);
821  } else {
822    collectionSet.set(component, new Set([attribute]));
823  }
824}
825
826export function isExtendFunction(node: ts.FunctionDeclaration, extendResult: ExtendResult,
827  checkArguments: boolean = false): string {
828  if (node.decorators && node.decorators.length) {
829    for (let i = 0, len = node.decorators.length; i < len; i++) {
830      if (ts.isCallExpression(node.decorators[i].expression)) {
831        parseExtendNode(node.decorators[i].expression as ts.CallExpression, extendResult, checkArguments);
832        if (CHECK_EXTEND_DECORATORS.includes(extendResult.decoratorName) && extendResult.componentName) {
833          return extendResult.componentName;
834        }
835      }
836    }
837  }
838  return null;
839}
840
841function parseExtendNode(node: ts.CallExpression, extendResult: ExtendResult, checkArguments: boolean): void {
842  if (ts.isIdentifier(node.expression)) {
843    extendResult.decoratorName = node.expression.escapedText.toString();
844    if (checkArguments && CHECK_EXTEND_DECORATORS.includes(extendResult.decoratorName) &&
845      node.arguments && node.arguments.length !== 1) {
846      transformLog.errors.push({
847        type: LogType.ERROR,
848        message: `@${extendResult.decoratorName} should have one and only one parameter`,
849        pos: node.getStart()
850      });
851    }
852  }
853  if (node.arguments.length && ts.isIdentifier(node.arguments[0])) {
854    extendResult.componentName = node.arguments[0].escapedText.toString();
855  }
856}
857
858function createEntryNode(node: ts.SourceFile, context: ts.TransformationContext,
859  entryNodeKey: ts.Expression, id: number): ts.SourceFile {
860  let cardRelativePath: string;
861  if (projectConfig && projectConfig.cardObj) {
862    cardRelativePath = projectConfig.cardObj[resourceFileName];
863  }
864  if (componentCollection.previewComponent.length === 0 || !projectConfig.isPreview) {
865    if (componentCollection.entryComponent) {
866      if (!partialUpdateConfig.partialUpdateMode) {
867        const entryNode: ts.ExpressionStatement =
868          createEntryFunction(componentCollection.entryComponent, context,
869            cardRelativePath, entryNodeKey, id) as ts.ExpressionStatement;
870        return context.factory.updateSourceFile(node, [...node.statements, entryNode]);
871      } else {
872        const entryNodes: ts.ExpressionStatement[] =
873          createEntryFunction(componentCollection.entryComponent, context,
874            cardRelativePath, entryNodeKey, id) as ts.ExpressionStatement[];
875        return entryNodes ?
876          context.factory.updateSourceFile(node, [...node.statements, ...entryNodes]) :
877          context.factory.updateSourceFile(node, [...node.statements]);
878      }
879    } else {
880      return node;
881    }
882  } else {
883    const statementsArray: ts.Statement =
884      createPreviewComponentFunction(componentCollection.entryComponent, context, cardRelativePath, entryNodeKey, id);
885    return context.factory.updateSourceFile(node, [...node.statements, statementsArray]);
886  }
887}
888
889function createEntryFunction(name: string, context: ts.TransformationContext, cardRelativePath: string,
890  entryNodeKey: ts.Expression, id: number): ts.ExpressionStatement | (ts.ExpressionStatement | ts.Block | ts.IfStatement)[] {
891  const newArray: ts.Expression[] = [
892    context.factory.createStringLiteral(id.toString()),
893    context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
894    context.factory.createObjectLiteralExpression([], false)
895  ];
896  const [localStorageName, entryOptionNode]: [string, ts.Expression] = addStorageParam(name);
897  if (localStorageName && entryNodeKey) {
898    newArray.push(entryNodeKey);
899  }
900  const newExpressionParams: any[] = [
901    context.factory.createNewExpression(
902      context.factory.createIdentifier(name), undefined, newArray)];
903  addCardStringliteral(newExpressionParams, context, cardRelativePath);
904  if (!partialUpdateConfig.partialUpdateMode) {
905    const newExpressionStatement: ts.ExpressionStatement =
906      context.factory.createExpressionStatement(context.factory.createCallExpression(
907        context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME :
908          PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams));
909    return newExpressionStatement;
910  } else {
911    if (cardRelativePath) {
912      if (entryOptionNode && ts.isObjectLiteralExpression(entryOptionNode)) {
913        transformLog.errors.push({
914          type: LogType.ERROR,
915          message: `@Entry doesn't support {} parameter in card`,
916          pos: componentCollection.entryComponentPos
917        });
918      }
919      return [
920        createStartGetAccessRecording(context),
921        createLoadDocument(context, name, cardRelativePath, localStorageName, entryNodeKey),
922        createStopGetAccessRecording(context)
923      ];
924    } else {
925      return createLoadPageConditionalJudgMent(context, name, cardRelativePath, localStorageName, entryOptionNode);
926    }
927  }
928}
929
930function createLoadPageConditionalJudgMent(context: ts.TransformationContext, name: string,
931  cardRelativePath: string, localStorageName: string, entryOptionNode: ts.Expression,
932  argsArr: ts.Expression[] = undefined, isComponentPreview: boolean = false)
933  : (ts.ExpressionStatement | ts.Block | ts.IfStatement)[] {
934  let isObject: boolean = false;
935  let routeNameNode: ts.Expression;
936  let storageNode: ts.Expression;
937  if (!entryOptionNode) {
938    const originArray: ts.ExpressionStatement[] = [
939      createStartGetAccessRecording(context),
940      createLoadDocument(context, name, cardRelativePath, localStorageName, entryOptionNode),
941      createStopGetAccessRecording(context)
942    ];
943    return originArray;
944  }
945  if (ts.isObjectLiteralExpression(entryOptionNode)) {
946    isObject = true;
947    if (entryOptionNode.properties) {
948      entryOptionNode.properties.forEach((property) => {
949        if (ts.isPropertyAssignment(property) && property.name && ts.isIdentifier(property.name)) {
950          if (property.name.escapedText.toString() === ROUTE_NAME) {
951            routeNameNode = property.initializer;
952          } else if (property.name.escapedText.toString() === STORAGE) {
953            storageNode = property.initializer;
954          }
955        }
956      });
957    }
958  } else {
959    isObject = false;
960  }
961  if (isObject) {
962    if (routeNameNode && !storageNode) {
963      return isComponentPreview ? [
964        ...assignRouteNameAndStorage(routeNameNode, storageNode, true, false),
965        ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
966          routeNameNode, storageNode, true, false, false, argsArr)
967      ] : [ts.factory.createBlock([
968        ...assignRouteNameAndStorage(routeNameNode, storageNode, true, false),
969        ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
970          routeNameNode, storageNode, true, false, false, argsArr)
971      ])];
972    } else if (!routeNameNode && !storageNode) {
973      return isComponentPreview ? [
974        ...assignRouteNameAndStorage(routeNameNode, storageNode, false, false),
975        ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
976          routeNameNode, storageNode, false, false, true, argsArr)
977      ] : [ts.factory.createBlock([
978        ...assignRouteNameAndStorage(routeNameNode, storageNode, false, false),
979        ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
980          routeNameNode, storageNode, false, false, true, argsArr)
981      ])];
982    } else if (!routeNameNode && storageNode) {
983      return isComponentPreview ? [
984        ...assignRouteNameAndStorage(routeNameNode, storageNode, false, true),
985        ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
986          routeNameNode, storageNode, false, true, true, argsArr)
987      ] : [ts.factory.createBlock([
988        ...assignRouteNameAndStorage(routeNameNode, storageNode, false, true),
989        ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
990          routeNameNode, storageNode, false, true, true, argsArr)
991      ])];
992    } else {
993      return isComponentPreview ? [
994        ...assignRouteNameAndStorage(routeNameNode, storageNode, true, true),
995        judgeRouteNameAndStorage(context, name, cardRelativePath, isObject, entryOptionNode, routeNameNode,
996          storageNode, argsArr)
997      ] : [ts.factory.createBlock([
998        ...assignRouteNameAndStorage(routeNameNode, storageNode, true, true),
999        judgeRouteNameAndStorage(context, name, cardRelativePath, isObject, entryOptionNode, routeNameNode,
1000          storageNode, argsArr)
1001      ])];
1002    }
1003  } else {
1004    return [
1005      judgeRouteNameAndStorage(context, name, cardRelativePath, isObject, entryOptionNode, routeNameNode,
1006        storageNode, argsArr)];
1007  }
1008}
1009
1010function judgeRouteNameAndStorage(context: ts.TransformationContext, name: string,
1011  cardRelativePath: string, isObject: boolean, entryOptionNode: ts.Expression, routeNameNode: ts.Expression,
1012  storageNode: ts.Expression, argsArr: ts.Expression[] = undefined): ts.IfStatement {
1013  return ts.factory.createIfStatement(
1014    isObject ? judgeRouteAndStorageForObject(true, true) :
1015      judgeRouteAndStorageForIdentifier(entryOptionNode as ts.Identifier, true, true),
1016    ts.factory.createBlock(
1017      [
1018        ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
1019          routeNameNode, storageNode, true, true, false, argsArr)
1020      ],
1021      true
1022    ),
1023    ts.factory.createIfStatement(
1024      isObject ? judgeRouteAndStorageForObject(true, false) :
1025        judgeRouteAndStorageForIdentifier(entryOptionNode as ts.Identifier, true, false),
1026      ts.factory.createBlock(
1027        [
1028          ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
1029            routeNameNode, storageNode, true, false, false, argsArr)
1030        ],
1031        true
1032      ),
1033      ts.factory.createIfStatement(
1034        isObject ? judgeRouteAndStorageForObject(false, true) :
1035          judgeRouteAndStorageForIdentifier(entryOptionNode as ts.Identifier, false, true),
1036        ts.factory.createBlock(
1037          [
1038            ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
1039              routeNameNode, storageNode, false, true, true, argsArr)
1040          ],
1041          true
1042        ),
1043        ts.factory.createBlock(
1044          [
1045            ...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
1046              routeNameNode, storageNode, false, false, true, argsArr)
1047          ],
1048          true
1049        )
1050      )
1051    )
1052  );
1053}
1054
1055function judgeRouteAndStorageForObject(hasRouteName: boolean, hasStorage: boolean): ts.BinaryExpression {
1056  return ts.factory.createBinaryExpression(
1057    ts.factory.createBinaryExpression(
1058      ts.factory.createIdentifier(ROUTENAME_NODE),
1059      ts.factory.createToken(hasRouteName ? ts.SyntaxKind.ExclamationEqualsToken : ts.SyntaxKind.EqualsEqualsToken),
1060      ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)
1061    ),
1062    ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
1063    ts.factory.createBinaryExpression(
1064      ts.factory.createIdentifier(STORAGE_NODE),
1065      ts.factory.createToken(hasStorage ? ts.SyntaxKind.ExclamationEqualsToken : ts.SyntaxKind.EqualsEqualsToken),
1066      ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)
1067    )
1068  );
1069}
1070
1071function judgeRouteAndStorageForIdentifier(entryOptionNode: ts.Identifier, hasRouteName: boolean,
1072  hasStorage: boolean): ts.BinaryExpression {
1073  return ts.factory.createBinaryExpression(
1074    ts.factory.createBinaryExpression(
1075      entryOptionNode,
1076      ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
1077      ts.factory.createBinaryExpression(
1078        ts.factory.createPropertyAccessExpression(
1079          entryOptionNode,
1080          ts.factory.createIdentifier(ROUTE_NAME)
1081        ),
1082        ts.factory.createToken(hasRouteName ? ts.SyntaxKind.ExclamationEqualsToken : ts.SyntaxKind.EqualsEqualsToken),
1083        ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)
1084      )
1085    ),
1086    ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
1087    ts.factory.createBinaryExpression(
1088      ts.factory.createPropertyAccessExpression(
1089        entryOptionNode,
1090        ts.factory.createIdentifier(STORAGE)
1091      ),
1092      ts.factory.createToken(hasStorage ? ts.SyntaxKind.ExclamationEqualsToken : ts.SyntaxKind.EqualsEqualsToken),
1093      ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)
1094    )
1095  );
1096}
1097
1098function assignRouteNameAndStorage(routeNameNode, storageNode, hasRouteName, hasStorage): ts.ExpressionStatement[] {
1099  const assignOperation: ts.VariableStatement[] = [];
1100  if (hasRouteName) {
1101    assignOperation.push(ts.factory.createVariableStatement(
1102      undefined,
1103      ts.factory.createVariableDeclarationList(
1104        [ts.factory.createVariableDeclaration(
1105          ts.factory.createIdentifier(ROUTENAME_NODE),
1106          undefined,
1107          undefined,
1108          routeNameNode
1109        )],
1110        ts.NodeFlags.Let
1111      )
1112    ));
1113  }
1114  if (hasStorage) {
1115    assignOperation.push(ts.factory.createVariableStatement(
1116      undefined,
1117      ts.factory.createVariableDeclarationList(
1118        [ts.factory.createVariableDeclaration(
1119          ts.factory.createIdentifier(STORAGE_NODE),
1120          undefined,
1121          undefined,
1122          storageNode
1123        )],
1124        ts.NodeFlags.Let
1125      )
1126    ));
1127  }
1128  return assignOperation;
1129}
1130
1131function createLoadDocumentWithRoute(context: ts.TransformationContext, name: string,
1132  cardRelativePath: string, isObject: boolean, entryOptionNode: ts.Expression,
1133  routeNameNode: ts.Node, storageNode: ts.Node, hasRouteName: boolean, hasStorage: boolean,
1134  shouldCreateAccsessRecording: boolean, argsArr: ts.Expression[]): ts.ExpressionStatement[] {
1135  const newArray: ts.Expression[] = [
1136    context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
1137    context.factory.createObjectLiteralExpression([], false)
1138  ];
1139  if (entryOptionNode) {
1140    if (!isObject) {
1141      if (hasStorage) {
1142        newArray.push(ts.factory.createPropertyAccessExpression(
1143          entryOptionNode,
1144          ts.factory.createIdentifier(STORAGE)
1145        ));
1146      } else if (!hasRouteName) {
1147        newArray.push(entryOptionNode);
1148      }
1149    } else if (storageNode && hasStorage) {
1150      newArray.push(ts.factory.createIdentifier(STORAGE_NODE));
1151    }
1152  }
1153  const newExpressionParams: any[] = [
1154    context.factory.createNewExpression(
1155      context.factory.createIdentifier(name),
1156      undefined, newArray)];
1157  if (argsArr) {
1158    argsArr = [];
1159    componentCollection.previewComponent.forEach((componentName: string) => {
1160      const newExpression: ts.Expression = context.factory.createNewExpression(
1161        context.factory.createIdentifier(componentName),
1162        undefined,
1163        componentName === name ? newArray : newArray.slice(0, newArray.length - 1)
1164      );
1165      argsArr.push(context.factory.createStringLiteral(componentName), newExpression);
1166    });
1167  }
1168  if (hasRouteName) {
1169    return [
1170      shouldCreateAccsessRecording ? createStartGetAccessRecording(context) : undefined,
1171      context.factory.createExpressionStatement(context.factory.createCallExpression(
1172        context.factory.createIdentifier(REGISTER_NAMED_ROUTE),
1173        undefined,
1174        [
1175          context.factory.createArrowFunction(
1176            undefined,
1177            undefined,
1178            [],
1179            undefined,
1180            context.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
1181            newExpressionParams[0]
1182          ),
1183          isObject ? ts.factory.createIdentifier(ROUTENAME_NODE) : context.factory.createPropertyAccessExpression(
1184            entryOptionNode,
1185            context.factory.createIdentifier(ROUTE_NAME)
1186          ),
1187          context.factory.createObjectLiteralExpression(
1188            [
1189              context.factory.createPropertyAssignment(
1190                context.factory.createIdentifier(RESOURCE_NAME_BUNDLE),
1191                context.factory.createStringLiteral(projectConfig.bundleName || '')
1192              ),
1193              context.factory.createPropertyAssignment(
1194                context.factory.createIdentifier(RESOURCE_NAME_MODULE),
1195                context.factory.createStringLiteral(projectConfig.moduleName || '')
1196              ),
1197              context.factory.createPropertyAssignment(
1198                context.factory.createIdentifier(PAGE_PATH),
1199                context.factory.createStringLiteral(
1200                  projectConfig.compileHar ? '' :
1201                    path.relative(projectConfig.projectPath || '', resourceFileName).replace(/\\/g, '/').replace(/\.ets$/, '')
1202                )
1203              )
1204            ],
1205            false
1206          )
1207        ]
1208      )),
1209      shouldCreateAccsessRecording ? createStopGetAccessRecording(context) : undefined];
1210  } else {
1211    return [
1212      shouldCreateAccsessRecording ? createStartGetAccessRecording(context) : undefined,
1213      context.factory.createExpressionStatement(
1214        context.factory.createCallExpression(
1215          context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME :
1216            PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams)),
1217      shouldCreateAccsessRecording ? createStopGetAccessRecording(context) : undefined];
1218  }
1219}
1220
1221function createStartGetAccessRecording(context: ts.TransformationContext): ts.ExpressionStatement {
1222  return context.factory.createExpressionStatement(
1223    context.factory.createCallExpression(
1224      context.factory.createPropertyAccessExpression(
1225        context.factory.createIdentifier(VIEWSTACKPROCESSOR),
1226        context.factory.createIdentifier(STARTGETACCESSRECORDINGFOR)
1227      ),
1228      undefined,
1229      [context.factory.createCallExpression(
1230        context.factory.createPropertyAccessExpression(
1231          context.factory.createIdentifier(VIEWSTACKPROCESSOR),
1232          context.factory.createIdentifier(ALLOCATENEWELMETIDFORNEXTCOMPONENT)
1233        ),
1234        undefined,
1235        []
1236      )]
1237    )
1238  );
1239}
1240
1241function createLoadDocument(context: ts.TransformationContext, name: string,
1242  cardRelativePath: string, localStorageName: string, entryNodeKey: ts.Expression): ts.ExpressionStatement {
1243  const newArray: ts.Expression[] = [
1244    context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
1245    context.factory.createObjectLiteralExpression([], false)
1246  ];
1247  if (localStorageName && entryNodeKey) {
1248    newArray.push(entryNodeKey);
1249  }
1250  const newExpressionParams: any[] = [
1251    context.factory.createNewExpression(
1252      context.factory.createIdentifier(name),
1253      undefined, newArray)];
1254  addCardStringliteral(newExpressionParams, context, cardRelativePath);
1255  return context.factory.createExpressionStatement(
1256    context.factory.createCallExpression(
1257      context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME :
1258        PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams)
1259  );
1260}
1261
1262function createStopGetAccessRecording(context: ts.TransformationContext): ts.ExpressionStatement {
1263  return context.factory.createExpressionStatement(
1264    context.factory.createCallExpression(
1265      context.factory.createPropertyAccessExpression(
1266        context.factory.createIdentifier(VIEWSTACKPROCESSOR),
1267        context.factory.createIdentifier(STOPGETACCESSRECORDING)
1268      ),
1269      undefined,
1270      []
1271    )
1272  );
1273}
1274
1275function addStorageParam(name: string): [string, ts.Expression] {
1276  let localStorageName: string;
1277  let localStorageNode: ts.Identifier | ts.ObjectLiteralExpression;
1278  const localStorageNum: number = (localStorageLinkCollection.get(name) || new Set()).size +
1279    (localStoragePropCollection.get(name) || new Set()).size;
1280  if (componentCollection.entryComponent === name && componentCollection.localStorageNode) {
1281    localStorageNode = componentCollection.localStorageNode;
1282  }
1283  if (componentCollection.entryComponent === name && componentCollection.localStorageName) {
1284    localStorageName = componentCollection.localStorageName;
1285  } else if (componentCollection.entryComponent === name && !componentCollection.localStorageName &&
1286    !componentCollection.localStorageNode && localStorageNum) {
1287    transformLog.errors.push({
1288      type: LogType.WARN,
1289      message: `@Entry should have a parameter, like '@Entry (storage)'.`,
1290      pos: componentCollection.entryComponentPos
1291    });
1292  }
1293  return [localStorageName, localStorageNode];
1294}
1295
1296function createPreviewComponentFunction(name: string, context: ts.TransformationContext,
1297  cardRelativePath: string, entryNodeKey: ts.Expression, id: number): ts.Statement {
1298  const newArray: ts.Expression[] = partialUpdateConfig.partialUpdateMode ?
1299    [
1300      context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
1301      context.factory.createObjectLiteralExpression([], false)
1302    ] :
1303    [
1304      context.factory.createStringLiteral(id.toString()),
1305      context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
1306      context.factory.createObjectLiteralExpression([], false)
1307    ];
1308  const [localStorageName, entryOptionNode]: [string, ts.Expression] = addStorageParam(name);
1309  if (entryOptionNode) {
1310    newArray.push(entryOptionNode);
1311  }
1312  const argsArr: ts.Expression[] = [];
1313  componentCollection.previewComponent.forEach(componentName => {
1314    const newExpression: ts.Expression = context.factory.createNewExpression(
1315      context.factory.createIdentifier(componentName),
1316      undefined,
1317      newArray
1318    );
1319    argsArr.push(context.factory.createStringLiteral(componentName));
1320    argsArr.push(newExpression);
1321  });
1322  const newExpressionParams: any[] = name ? [context.factory.createNewExpression(
1323    context.factory.createIdentifier(name), undefined, newArray)] : [];
1324  addCardStringliteral(newExpressionParams, context, cardRelativePath);
1325  const ifStatement: ts.Statement = context.factory.createIfStatement(
1326    context.factory.createCallExpression(
1327      context.factory.createIdentifier(GET_PREVIEW_FLAG_FUNCTION_NAME),
1328      undefined,
1329      []
1330    ),
1331    context.factory.createBlock(
1332      [...storePreviewComponents(name, entryOptionNode, argsArr),
1333        context.factory.createExpressionStatement(context.factory.createCallExpression(
1334          context.factory.createIdentifier(PREVIEW_COMPONENT_FUNCTION_NAME),
1335          undefined,
1336          []
1337        ))],
1338      true
1339    ),
1340    context.factory.createBlock(
1341      createPreviewElseBlock(name, context, cardRelativePath, localStorageName, entryOptionNode,
1342        newExpressionParams, argsArr),
1343      true
1344    )
1345  );
1346  return ifStatement;
1347}
1348
1349function storePreviewComponents(name: string, entryOptionNode: ts.Expression, argsArr: ts.Expression[]):
1350  (ts.ExpressionStatement|ts.VariableStatement|ts.IfStatement)[] {
1351  let isObject: boolean = false;
1352  let storageNode: ts.Expression;
1353  if (!entryOptionNode) {
1354    return [ts.factory.createExpressionStatement(ts.factory.createCallExpression(
1355      ts.factory.createIdentifier(STORE_PREVIEW_COMPONENTS),
1356      undefined,
1357      [
1358        ts.factory.createNumericLiteral(componentCollection.previewComponent.length),
1359        ...argsArr
1360      ]
1361    ))];
1362  }
1363  if (ts.isObjectLiteralExpression(entryOptionNode)) {
1364    isObject = true;
1365    if (entryOptionNode.properties) {
1366      entryOptionNode.properties.forEach((property) => {
1367        if (ts.isPropertyAssignment(property) && property.name && ts.isIdentifier(property.name) &&
1368          property.name.escapedText.toString() === STORAGE) {
1369          storageNode = property.initializer;
1370        }
1371      });
1372    }
1373  } else {
1374    isObject = false;
1375  }
1376  const newArray: ts.Expression[] = [
1377    ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
1378    ts.factory.createObjectLiteralExpression([], false)
1379  ];
1380  const newArgsArr: ts.Expression[] = [];
1381  if (isObject) {
1382    return processObjectStorage(storageNode, newArray, name, newArgsArr);
1383  } else {
1384    return [ts.factory.createIfStatement(
1385      ts.factory.createBinaryExpression(
1386        entryOptionNode,
1387        ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
1388        ts.factory.createBinaryExpression(
1389          ts.factory.createPropertyAccessExpression(
1390            entryOptionNode,
1391            ts.factory.createIdentifier(STORAGE)
1392          ),
1393          ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsToken),
1394          ts.factory.createIdentifier('undefined')
1395        )
1396      ),
1397      ts.factory.createBlock(
1398        [returnStorePreview(entryOptionNode, true, name)],
1399        true
1400      ),
1401      ts.factory.createBlock(
1402        [returnStorePreview(entryOptionNode, false, name)],
1403        true
1404      )
1405    )];
1406  }
1407}
1408
1409function processObjectStorage(storageNode: ts.Expression, newArray: ts.Expression[], name: string,
1410  newArgsArr: ts.Expression[]): (ts.ExpressionStatement|ts.VariableStatement)[] {
1411  if (storageNode) {
1412    newArray.push(ts.factory.createIdentifier(STORAGE_NODE));
1413    componentCollection.previewComponent.forEach(componentName => {
1414      const newExpression: ts.Expression = ts.factory.createNewExpression(
1415        ts.factory.createIdentifier(componentName),
1416        undefined,
1417        componentName === name ? newArray : newArray.slice(0, newArray.length - 1)
1418      );
1419      newArgsArr.push(ts.factory.createStringLiteral(componentName));
1420      newArgsArr.push(newExpression);
1421    });
1422    return [ts.factory.createVariableStatement(
1423      undefined,
1424      ts.factory.createVariableDeclarationList(
1425        [ts.factory.createVariableDeclaration(
1426          ts.factory.createIdentifier(STORAGE_NODE),
1427          undefined,
1428          undefined,
1429          storageNode
1430        )],
1431        ts.NodeFlags.Let
1432      )
1433    ), ts.factory.createExpressionStatement(ts.factory.createCallExpression(
1434      ts.factory.createIdentifier(STORE_PREVIEW_COMPONENTS),
1435      undefined,
1436      [
1437        ts.factory.createNumericLiteral(componentCollection.previewComponent.length),
1438        ...newArgsArr
1439      ]
1440    ))];
1441  } else {
1442    componentCollection.previewComponent.forEach(componentName => {
1443      const newExpression: ts.Expression = ts.factory.createNewExpression(
1444        ts.factory.createIdentifier(componentName),
1445        undefined,
1446        newArray
1447      );
1448      newArgsArr.push(ts.factory.createStringLiteral(componentName));
1449      newArgsArr.push(newExpression);
1450    });
1451    return [ts.factory.createExpressionStatement(ts.factory.createCallExpression(
1452      ts.factory.createIdentifier(STORE_PREVIEW_COMPONENTS),
1453      undefined,
1454      [
1455        ts.factory.createNumericLiteral(componentCollection.previewComponent.length),
1456        ...newArgsArr
1457      ]
1458    ))];
1459  }
1460}
1461
1462function returnStorePreview(entryOptionNode: ts.Expression, hasStorage: boolean, name: string): ts.ExpressionStatement {
1463  const newArray: ts.Expression[] = [
1464    ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
1465    ts.factory.createObjectLiteralExpression([], false)
1466  ];
1467  const newArgsArr: ts.Expression[] = [];
1468  newArray.push(hasStorage ? ts.factory.createPropertyAccessExpression(
1469    entryOptionNode,
1470    ts.factory.createIdentifier(STORAGE)
1471  ) : entryOptionNode);
1472  componentCollection.previewComponent.forEach(componentName => {
1473    const newExpression: ts.Expression = ts.factory.createNewExpression(
1474      ts.factory.createIdentifier(componentName),
1475      undefined,
1476      componentName === name ? newArray : newArray.slice(0, newArray.length - 1)
1477    );
1478    newArgsArr.push(ts.factory.createStringLiteral(componentName));
1479    newArgsArr.push(newExpression);
1480  });
1481  return ts.factory.createExpressionStatement(ts.factory.createCallExpression(
1482    ts.factory.createIdentifier(STORE_PREVIEW_COMPONENTS),
1483    undefined,
1484    [
1485      ts.factory.createNumericLiteral(componentCollection.previewComponent.length),
1486      ...newArgsArr
1487    ]
1488  ));
1489}
1490
1491function createPreviewElseBlock(name: string, context: ts.TransformationContext, cardRelativePath: string,
1492  localStorageName: string, entryOptionNode: ts.Expression, newExpressionParams: ts.Expression[],
1493  argsArr: ts.Expression[]): (ts.ExpressionStatement | ts.IfStatement | ts.Block)[] {
1494  if (name) {
1495    if (!partialUpdateConfig.partialUpdateMode) {
1496      return [context.factory.createExpressionStatement(context.factory.createCallExpression(
1497        context.factory.createIdentifier(STORE_PREVIEW_COMPONENTS),
1498        undefined,
1499        [
1500          context.factory.createNumericLiteral(componentCollection.previewComponent.length),
1501          ...argsArr
1502        ]
1503      )),
1504      context.factory.createExpressionStatement(context.factory.createCallExpression(
1505        context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME :
1506          PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams
1507      ))];
1508    } else {
1509      if (cardRelativePath) {
1510        if (entryOptionNode && ts.isObjectLiteralExpression(entryOptionNode)) {
1511          transformLog.errors.push({
1512            type: LogType.ERROR,
1513            message: `@Entry doesn't support {} parameter in card`,
1514            pos: componentCollection.entryComponentPos
1515          });
1516        }
1517        return [
1518          name ? createStartGetAccessRecording(context) : undefined,
1519          name ? context.factory.createExpressionStatement(context.factory.createCallExpression(
1520            context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME :
1521              PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams
1522          )) : undefined,
1523          name ? createStopGetAccessRecording(context) : undefined
1524        ];
1525      }
1526      return createLoadPageConditionalJudgMent(context, name, cardRelativePath, localStorageName,
1527        entryOptionNode, argsArr, true);
1528    }
1529  }
1530}
1531
1532export function resetLog(): void {
1533  transformLog.errors = [];
1534}
1535
1536function addCardStringliteral(newExpressionParams: any[], context: ts.TransformationContext,
1537  cardRelativePath: string): void {
1538  if (cardRelativePath) {
1539    newExpressionParams.push(context.factory.createStringLiteral(
1540      projectConfig.bundleName + '/' + projectConfig.moduleName + '/' +
1541      cardRelativePath));
1542  }
1543}
1544
1545export function validatorCard(log: any[], type: number, pos: number,
1546  name: string = ''): void {
1547  if (projectConfig && projectConfig.cardObj && resourceFileName
1548    && projectConfig.cardObj[resourceFileName]) {
1549    const logInfo: object = {
1550      type: LogType.ERROR,
1551      message: '',
1552      pos: pos
1553    };
1554    switch (type) {
1555      case CARD_LOG_TYPE_COMPONENTS:
1556        logInfo.message = `Card page cannot use the component ${name}.`;
1557        break;
1558      case CARD_LOG_TYPE_DECORATORS:
1559        logInfo.message = `Card page cannot use ${name}`;
1560        break;
1561      case CARD_LOG_TYPE_IMPORT:
1562        logInfo.message = `Card page cannot use import.`;
1563        break;
1564    }
1565    log.push(logInfo);
1566  }
1567}
1568