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