* Copyright (c) 2021-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import ts from 'typescript';
import path from 'path';
import fs from 'fs';
import { fileInfoCache } from './file_info_cache';
import {
PAGE_ENTRY_FUNCTION_NAME,
PREVIEW_COMPONENT_FUNCTION_NAME,
STORE_PREVIEW_COMPONENTS,
GET_PREVIEW_FLAG_FUNCTION_NAME,
COMPONENT_CONSTRUCTOR_UNDEFINED,
BUILD_ON,
COMPONENT_BUILDER_DECORATOR,
COMPONENT_CONCURRENT_DECORATOR,
COMPONENT_SENDABLE_DECORATOR,
COMPONENT_EXTEND_DECORATOR,
COMPONENT_STYLES_DECORATOR,
RESOURCE,
RESOURCE_TYPE,
WORKER_OBJECT,
RESOURCE_NAME_ID,
RESOURCE_NAME_TYPE,
RESOURCE_NAME_PARAMS,
RESOURCE_RAWFILE,
RESOURCE_NAME_BUNDLE,
RESOURCE_NAME_MODULE,
ATTRIBUTE_ANIMATETO_SET,
GLOBAL_CONTEXT,
INSTANCE,
SET_CONTROLLER_CTR_TYPE,
SET_CONTROLLER_METHOD,
JS_DIALOG,
CUSTOM_DIALOG_CONTROLLER_BUILDER,
ESMODULE,
EXTNAME_ETS,
GENERATE_ID,
_GENERATE_ID,
VIEWSTACKPROCESSOR,
STARTGETACCESSRECORDINGFOR,
ALLOCATENEWELMETIDFORNEXTCOMPONENT,
STOPGETACCESSRECORDING,
CARD_ENTRY_FUNCTION_NAME,
CARD_LOG_TYPE_COMPONENTS,
CARD_LOG_TYPE_DECORATORS,
CARD_LOG_TYPE_IMPORT,
COMPONENT_ANIMATABLE_EXTEND_DECORATOR,
CHECK_EXTEND_DECORATORS,
ELMTID,
ROUTENAME_NODE,
STORAGE_NODE,
STORAGE,
REGISTER_NAMED_ROUTE,
ROUTE_NAME,
PAGE_PATH,
ISINITIALRENDER,
CREATE_ANIMATABLE_PROPERTY,
UPDATE_ANIMATABLE_PROPERTY,
MY_IDS,
VIEW_STACK_PROCESSOR,
GET_AND_PUSH_FRAME_NODE,
COMPONENT_CONSTRUCTOR_PARENT,
WRAPBUILDER_FUNCTION,
FINISH_UPDATE_FUNC,
INTEGRATED_HSP,
FUNCTION,
PAGE_FULL_PATH,
LENGTH,
PUV2_VIEW_BASE,
CONTEXT_STACK,
CHECK_COMPONENT_EXTEND_DECORATOR,
MUTABLEBUILDER_FUNCTION,
ATTRIBUTE_ID,
__GETRESOURCEID__,
__RESOURCEIDHAR__,
APIVERSION
} from './pre_define';
import {
componentInfo,
LogInfo,
LogType,
hasDecorator,
IFileLog,
getPossibleBuilderTypeParameter,
storedFileInfo,
ExtendResult,
getStoredFileInfo,
ProcessFileInfo,
RouterInfo,
EntryOptionValue,
judgeUseSharedStorageForExpresion,
createGetSharedForVariable,
createGetShared
} from './utils';
import { writeFileSyncByNode } from './process_module_files';
import {
componentCollection,
localStorageLinkCollection,
localStoragePropCollection
} from './validate_ui_syntax';
import {
processComponentClass,
createParentParameter,
processBuildMember,
checkFinalizeConstruction,
checkContextStack
} from './process_component_class';
import processImport, {
processImportModule
} from './process_import';
import {
processComponentBlock,
bindComponentAttr,
getName,
createViewStackProcessorStatement,
parseGlobalBuilderParams,
BuilderParamsResult
} from './process_component_build';
import {
BUILDIN_STYLE_NAMES,
CUSTOM_BUILDER_METHOD,
EXTEND_ATTRIBUTE,
INNER_STYLE_FUNCTION,
GLOBAL_STYLE_FUNCTION,
INTERFACE_NODE_SET,
ID_ATTRS,
GLOBAL_CUSTOM_BUILDER_METHOD,
INNER_COMPONENT_NAMES
} from './component_map';
import {
resources,
projectConfig,
partialUpdateConfig,
rawfileResources
} from '../main';
import {
createCustomComponentNewExpression,
createViewCreate
} from './process_component_member';
import {
assignComponentParams,
assignmentFunction
} from './process_custom_component';
import { processDecorator } from './fast_build/ark_compiler/process_decorator';
import { hasArkDecorator } from './fast_build/ark_compiler/utils';
import {
checkTypeReference,
validateModuleSpecifier,
isApiAvailableStatement
} from './fast_build/system_api/api_check_utils';
import constantDefine from './constant_define';
import processStructComponentV2 from './process_struct_componentV2';
import createAstNodeUtils from './create_ast_node_utils';
import {
processSendableClass,
processSendableFunction,
processSendableType
} from './process_sendable';
import {
routerOrNavPathWrite,
integratedHspType,
routerModuleType,
routerBundleOrModule
} from './process_module_package';
import {
CompileEvent,
createAndStartEvent,
stopEvent
} from './performance';
import parseIntent from './userIntents_parser/parseUserIntents';
import { createHelperFunctionDeclaration, isPointVersion, processAvailableStatement } from './process_available_statement';
export let transformLog: IFileLog = new createAstNodeUtils.FileLog();
export let contextGlobal: ts.TransformationContext;
export let resourceFileName: string = '';
export const builderTypeParameter: { params: string[] } = { params: [] };
export function processUISyntax(program: ts.Program, ut = false,
parentEvent?: CompileEvent, filePath: string = '', share: object = null, metaInfo: Object = {}): Function {
let entryNodeKey: ts.Expression;
let eventProcessUISyntax: CompileEvent = undefined;
return (context: ts.TransformationContext) => {
contextGlobal = context;
let pagesDir: string;
let pageFile: string;
let hasUseResource: boolean = false;
let hasStruct: boolean = false;
let StateManagementV2: { hasReusableV2: boolean } = { hasReusableV2: false };
let ReusePool: { hasReusePool: boolean } = { hasReusePool: false };
let needProcessAvailable: boolean = false;
let needsAvailableHelper = false;
return (node: ts.SourceFile) => {
eventProcessUISyntax = createAndStartEvent(parentEvent, 'processUISyntax');
pagesDir = path.resolve(path.dirname(node.fileName));
resourceFileName = path.resolve(node.fileName);
pageFile = path.resolve(filePath !== '' ? filePath : node.fileName);
needProcessAvailable = /\.apiAvailable/.test(node.getText());
if (process.env.compiler === BUILD_ON || process.env.compileTool === 'rollup') {
const fileHash = share?.getHashByFilePath ? share?.getHashByFilePath(pageFile) : '';
storedFileInfo.transformCacheFiles[pageFile] = {
mtimeMs: fileInfoCache.getMtimeMs(pageFile),
hash: fileHash,
children: []
};
transformLog.sourceFile = node;
preprocessIdAttrs(node.fileName);
if (!ut && (process.env.compileMode !== 'moduleJson' &&
path.resolve(node.fileName) === path.resolve(projectConfig.projectPath, 'app.ets') ||
/\.ts$/.test(node.fileName))) {
node = ts.visitEachChild(node, processResourceNode, context);
const statements: ts.Statement[] = Array.from(node.statements);
if (needsAvailableHelper) {
statements.push(createHelperFunctionDeclaration());
}
insertImportModuleNode(statements, hasUseResource);
node = ts.factory.updateSourceFile(node, statements);
if (projectConfig.compileMode === ESMODULE && projectConfig.processTs === true) {
if (process.env.compileTool !== 'rollup') {
const processedNode: ts.SourceFile = ts.getTypeExportImportAndConstEnumTransformer(context)(node);
writeFileSyncByNode(processedNode, projectConfig, undefined);
}
}
const visitEachChildNode: ts.SourceFile = ts.visitEachChild(node, visitor, context);
stopEvent(eventProcessUISyntax);
return visitEachChildNode;
}
const id: number = ++componentInfo.id;
parseIntent.recordCompiledFile(pageFile, metaInfo);
node = ts.visitEachChild(node, processAllNodes, context);
if (context.getCompilerOptions().etsAnnotationsEnable) {
node = ts.getAnnotationTransformer()(context)(node);
}
node = createEntryNode(node, context, entryNodeKey, id);
GLOBAL_STYLE_FUNCTION.forEach((block, styleName) => {
BUILDIN_STYLE_NAMES.delete(styleName);
});
GLOBAL_STYLE_FUNCTION.clear();
const statements: ts.Statement[] = Array.from(node.statements);
if (needsAvailableHelper) {
statements.push(createHelperFunctionDeclaration());
}
if (!partialUpdateConfig.partialUpdateMode) {
generateId(statements, node);
}
INTERFACE_NODE_SET.forEach(item => {
statements.unshift(item);
});
if (StateManagementV2.hasReusableV2) {
statements.unshift(processStructComponentV2.createReusableV2ReflectFunction());
}
if (partialUpdateConfig.partialUpdateMode && hasStruct) {
if (storedFileInfo.hasLocalBuilderInFile) {
statements.unshift(checkContextStack());
}
statements.unshift(checkFinalizeConstruction());
}
createNavigationInit(resourceFileName, statements);
insertImportModuleNode(statements, hasUseResource);
node = ts.factory.updateSourceFile(node, statements);
INTERFACE_NODE_SET.clear();
if (projectConfig.compileMode === ESMODULE && projectConfig.processTs === true) {
if (process.env.compileTool !== 'rollup') {
const processedNode: ts.SourceFile = ts.getTypeExportImportAndConstEnumTransformer(context)(node);
writeFileSyncByNode(processedNode, projectConfig, undefined);
}
}
const visitEachChildNode: ts.SourceFile = ts.visitEachChild(node, visitor, context);
stopEvent(eventProcessUISyntax);
return visitEachChildNode;
} else {
stopEvent(eventProcessUISyntax);
return node;
}
};
function entryKeyNode(node: ts.Node): ts.Expression {
const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
if (node && decorators && decorators.length) {
decorators.forEach(item => {
if (item.expression && ts.isCallExpression(item.expression) && ts.isIdentifier(item.expression.expression) &&
item.expression.expression.escapedText.toString() === 'Entry' && item.expression.arguments &&
item.expression.arguments.length && ts.isIdentifier(item.expression.arguments[0])) {
entryNodeKey = item.expression.arguments[0];
}
});
}
return entryNodeKey;
}
function isESObjectNode(node: ts.Node): boolean {
if (node.kind === ts.SyntaxKind.TypeReference) {
const n: TypeReferenceNode = node as TypeReferenceNode;
if (n.typeName?.kind === ts.SyntaxKind.Identifier && (n.typeName as ts.Identifier).escapedText === 'ESObject') {
return true;
}
}
return false;
}
function processAllNodes(node: ts.Node): ts.Node {
if (projectConfig.compileMode === 'esmodule' && process.env.compileTool === 'rollup' &&
ts.isImportDeclaration(node)) {
const eventProcessImport = createAndStartEvent(eventProcessUISyntax, 'processImport');
processImportModule(node, pageFile, transformLog.errors, share);
stopEvent(eventProcessImport);
} else if ((projectConfig.compileMode !== 'esmodule' || process.env.compileTool !== 'rollup') &&
(ts.isImportDeclaration(node) || ts.isImportEqualsDeclaration(node) ||
ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier))) {
processImport(node, pagesDir, transformLog.errors);
}
if (ts.isStructDeclaration(node)) {
node = parseIntent.detectInsightIntent(node, metaInfo, filePath, eventProcessUISyntax, transformLog.errors);
hasStruct = true;
componentCollection.currentClassName = node.name.getText();
componentCollection.entryComponent === componentCollection.currentClassName && entryKeyNode(node);
const eventProcessComponentClass = createAndStartEvent(eventProcessUISyntax, 'processComponentClass');
node = processStructComponentV2.getOrCreateStructInfo(componentCollection.currentClassName).isComponentV2 ?
processStructComponentV2.processStructComponentV2(node, transformLog.errors, context, StateManagementV2, ReusePool) :
processComponentClass(node, context, transformLog.errors, program, ReusePool);
stopEvent(eventProcessComponentClass);
componentCollection.currentClassName = null;
INNER_STYLE_FUNCTION.forEach((block, styleName) => {
BUILDIN_STYLE_NAMES.delete(styleName);
});
INNER_STYLE_FUNCTION.clear();
} else if (ts.isFunctionDeclaration(node)) {
if (hasDecorator(node, COMPONENT_EXTEND_DECORATOR, null, transformLog.errors)) {
node = processExtend(node, transformLog.errors, COMPONENT_EXTEND_DECORATOR);
if (node && node.illegalDecorators) {
node.illegalDecorators = undefined;
}
} else if (hasDecorator(node, COMPONENT_BUILDER_DECORATOR) && node.name && node.body &&
ts.isBlock(node.body)) {
storedFileInfo.processBuilder = true;
storedFileInfo.processGlobalBuilder = true;
CUSTOM_BUILDER_METHOD.add(node.name.getText());
builderTypeParameter.params = getPossibleBuilderTypeParameter(node.parameters);
const parameters: ts.NodeArray<ts.ParameterDeclaration> =
ts.factory.createNodeArray(Array.from(node.parameters));
parameters.push(createParentParameter());
if (projectConfig.optLazyForEach) {
parameters.push(initializeMYIDS());
}
storedFileInfo.builderLikeCollection = CUSTOM_BUILDER_METHOD;
const builderParamsResult: BuilderParamsResult = { firstParam: null };
parseGlobalBuilderParams(node.parameters, builderParamsResult);
const componentBlock: ts.Block = processComponentBlock(node.body, false, transformLog.errors, false, true,
node.name.getText(), undefined, true, builderParamsResult, true);
node = ts.factory.updateFunctionDeclaration(node, ts.getModifiers(node),
node.asteriskToken, node.name, node.typeParameters, parameters, node.type,
componentBlock);
builderParamsResult.firstParam = null;
if (node && node.illegalDecorators) {
node.illegalDecorators = undefined;
}
builderTypeParameter.params = [];
node = processBuildMember(node, context, transformLog.errors, true);
storedFileInfo.processBuilder = false;
storedFileInfo.processGlobalBuilder = false;
} else if (hasDecorator(node, COMPONENT_STYLES_DECORATOR)) {
parseStylesNode(node, transformLog.errors);
if (node.parameters.length === 0) {
node = undefined;
} else {
transformLog.errors.push({
type: LogType.ERROR,
message: `'@Styles' decorated functions and methods cannot have arguments.`,
pos: node.getStart(),
code: '10905110'
});
}
} else if (hasDecorator(node, COMPONENT_CONCURRENT_DECORATOR)) {
node = processConcurrent(node);
if (node && node.illegalDecorators) {
node.illegalDecorators = undefined;
}
} else if (hasArkDecorator(node, COMPONENT_SENDABLE_DECORATOR)) {
node = processSendableFunction(node);
if (node && node.illegalDecorators) {
node.illegalDecorators = undefined;
}
} else if (hasDecorator(node, COMPONENT_ANIMATABLE_EXTEND_DECORATOR, null, transformLog.errors)) {
node = processExtend(node, transformLog.errors, COMPONENT_ANIMATABLE_EXTEND_DECORATOR);
if (node && node.illegalDecorators) {
node.illegalDecorators = undefined;
}
}
} else if (isResource(node)) {
hasUseResource = true;
node = processResourceData(node as ts.CallExpression, filePath);
} else if (isWorker(node)) {
node = processWorker(node as ts.NewExpression);
} else if (isAnimateToOrImmediately(node)) {
node = processAnimateToOrImmediately(node as ts.CallExpression);
} else if (isCustomDialogController(node)) {
node = createCustomDialogController(node.parent, node, transformLog.errors);
} else if (isESObjectNode(node)) {
node = ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
} else if (ts.isDecorator(node)) {
node = processDecorator(node);
} else if (isWrapBuilderFunction(node) || isMutableBuilderFunction(node)) {
if (node.arguments && node.arguments[0] && (!ts.isIdentifier(node.arguments[0]) ||
ts.isIdentifier(node.arguments[0]) &&
!CUSTOM_BUILDER_METHOD.has(node.arguments[0].escapedText.toString()))) {
transformLog.errors.push({
type: LogType.ERROR,
message: `The ${node.expression.escapedText.toString()}'s parameter should be '@Builder' function.`,
pos: node.getStart(),
code: '10905109'
});
}
} else if (ts.isClassDeclaration(node)) {
node = parseIntent.detectInsightIntent(node, metaInfo, filePath, eventProcessUISyntax, transformLog.errors);
if (hasDecorator(node, COMPONENT_SENDABLE_DECORATOR)) {
if (projectConfig.compileHar && !projectConfig.useTsHar) {
let warnMessage: string = 'If you use @Sendable in js har, an exception will occur during runtime.\n' +
'Please use @Sendable in ts har. You can configure {"name": "UseTsHar", ' +
'"value": "true"} in the "metadata" in the module.json5 file in har.';
transformLog.errors.push({
type: LogType.WARN,
message: warnMessage,
pos: node.getStart()
});
}
node = processSendableClass(node);
}
} else if (ts.isTypeAliasDeclaration(node) && hasArkDecorator(node, COMPONENT_SENDABLE_DECORATOR)) {
node = processSendableType(node);
if (node && node.illegalDecorators) {
node.illegalDecorators = undefined;
}
} else if (needProcessAvailable && !isPointVersion(projectConfig.originCompatibleSdkVersion) && isApiAvailableStatement(node)) {
const result = processAvailableStatement(node);
if (result !== undefined) {
node = result;
needsAvailableHelper = true;
}
}
return ts.visitEachChild(node, processAllNodes, context);
}
function processResourceNode(node: ts.Node): ts.Node {
if (ts.isImportDeclaration(node)) {
validateModuleSpecifier(node.moduleSpecifier, transformLog.errors);
} else if (isResource(node)) {
hasUseResource = true;
node = processResourceData(node as ts.CallExpression, filePath);
} else if (ts.isTypeReferenceNode(node)) {
checkTypeReference(node, transformLog);
} else if (needProcessAvailable && !isPointVersion(projectConfig.originCompatibleSdkVersion) && isApiAvailableStatement(node)) {
const result = processAvailableStatement(node);
if (result !== undefined) {
node = result;
needsAvailableHelper = true;
}
}
return ts.visitEachChild(node, processResourceNode, context);
}
function isWrapBuilderFunction(node: ts.Node): boolean {
if (ts.isCallExpression(node) && node.expression && ts.isIdentifier(node.expression) &&
node.expression.escapedText.toString() === WRAPBUILDER_FUNCTION) {
return true;
}
return false;
}
function isMutableBuilderFunction(node: ts.Node): boolean {
if (ts.isCallExpression(node) && node.expression && ts.isIdentifier(node.expression) &&
node.expression.escapedText.toString() === MUTABLEBUILDER_FUNCTION) {
return true;
}
return false;
}
function visitor(node: ts.Node): ts.VisitResult<ts.Node> {
return ts.visitEachChild(node, visitor, context);
}
};
}
export function globalBuilderParamAssignment(): ts.VariableStatement {
const contextStackCondition: ts.PropertyAccessExpression = ts.factory.createPropertyAccessExpression(
ts.factory.createIdentifier(PUV2_VIEW_BASE),
ts.factory.createIdentifier(CONTEXT_STACK)
);
return ts.factory.createVariableStatement(
undefined,
ts.factory.createVariableDeclarationList(
[ts.factory.createVariableDeclaration(
ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARENT),
undefined,
undefined,
ts.factory.createConditionalExpression(
ts.factory.createBinaryExpression(
contextStackCondition,
ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
ts.factory.createPropertyAccessExpression(
contextStackCondition,
ts.factory.createIdentifier(LENGTH)
)
),
ts.factory.createToken(ts.SyntaxKind.QuestionToken),
ts.factory.createElementAccessExpression(
contextStackCondition,
ts.factory.createBinaryExpression(
ts.factory.createPropertyAccessExpression(
contextStackCondition,
ts.factory.createIdentifier(LENGTH)
),
ts.factory.createToken(ts.SyntaxKind.MinusToken),
ts.factory.createNumericLiteral('1')
)
),
ts.factory.createToken(ts.SyntaxKind.ColonToken),
ts.factory.createNull()
)
)],
ts.NodeFlags.Const
));
}
function createNavigationInit(fileName: string, statements: ts.Statement[]): void {
const newStoredFileInfo: ProcessFileInfo = getStoredFileInfo();
if (newStoredFileInfo.routerInfo.has(fileName)) {
const routerInfoArr: Array<RouterInfo> = newStoredFileInfo.routerInfo.get(fileName);
const builderStatements: ts.Statement[] = [];
routerInfoArr.forEach((item) => {
if (GLOBAL_CUSTOM_BUILDER_METHOD.has(item.buildFunction)) {
builderStatements.push(createNavigationRegister(item));
} else {
transformLog.errors.push({
type: LogType.ERROR,
message: `The buildFunction '${item.buildFunction}' configured in the routerMap json file does not exist.`,
code: '10904336'
});
}
});
statements.push(ts.factory.createExpressionStatement(ts.factory.createCallExpression(
ts.factory.createParenthesizedExpression(ts.factory.createFunctionExpression(
undefined, undefined, undefined, undefined, [], undefined,
ts.factory.createBlock(
[ts.factory.createIfStatement(ts.factory.createBinaryExpression(
ts.factory.createTypeOfExpression(ts.factory.createIdentifier(constantDefine.NAVIGATION_BUILDER_REGISTER)),
ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.factory.createStringLiteral(FUNCTION)),
ts.factory.createBlock(builderStatements, true), undefined)], true)
)), undefined, []
)));
}
}
function createNavigationRegister(routerInfo: RouterInfo): ts.Statement {
return ts.factory.createExpressionStatement(ts.factory.createCallExpression(
ts.factory.createIdentifier(constantDefine.NAVIGATION_BUILDER_REGISTER),
undefined,
[
ts.factory.createStringLiteral(routerInfo.name),
ts.factory.createCallExpression(
ts.factory.createIdentifier(WRAPBUILDER_FUNCTION),
undefined,
[ts.factory.createIdentifier(routerInfo.buildFunction)]
)
]
));
}
export function initializeMYIDS(): ts.ParameterDeclaration {
return ts.factory.createParameterDeclaration(
undefined,
undefined,
ts.factory.createIdentifier(MY_IDS),
undefined,
undefined,
ts.factory.createArrayLiteralExpression(
[],
false
)
);
}
function generateId(statements: ts.Statement[], node: ts.SourceFile): void {
statements.unshift(
ts.factory.createVariableStatement(
undefined,
ts.factory.createVariableDeclarationList(
[ts.factory.createVariableDeclaration(
ts.factory.createIdentifier(_GENERATE_ID),
undefined,
ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
ts.factory.createNumericLiteral('0')
)],
ts.NodeFlags.Let
)
),
ts.factory.createFunctionDeclaration(
undefined,
undefined,
ts.factory.createIdentifier(GENERATE_ID),
undefined,
[],
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
ts.factory.createBlock(
[ts.factory.createReturnStatement(ts.factory.createBinaryExpression(
ts.factory.createStringLiteral(path.basename(node.fileName, EXTNAME_ETS) + '_'),
ts.factory.createToken(ts.SyntaxKind.PlusToken), ts.factory.createPrefixUnaryExpression(
ts.SyntaxKind.PlusPlusToken,
ts.factory.createIdentifier(_GENERATE_ID)
)))],
true
)
)
);
}
function preprocessIdAttrs(fileName: string): void {
for (const [id, idInfo] of ID_ATTRS) {
if (fileName === idInfo.get('path')) {
ID_ATTRS.delete(id);
}
}
}
function isCustomDialogController(node: ts.Expression) {
const tempParent: ts.Node = node.parent;
if (!node.parent && node.original) {
node.parent = node.original.parent;
}
if (ts.isNewExpression(node) && node.expression && ts.isIdentifier(node.expression) &&
node.expression.escapedText.toString() === SET_CONTROLLER_CTR_TYPE) {
return true;
} else {
node.parent = tempParent;
return false;
}
}
function createCustomDialogController(parent: ts.Expression, node: ts.NewExpression,
log: LogInfo[]): ts.NewExpression {
if (node.arguments && node.arguments.length === 1 &&
ts.isObjectLiteralExpression(node.arguments[0]) && node.arguments[0].properties) {
const newproperties: ts.ObjectLiteralElementLike[] = node.arguments[0].properties.map((item) => {
const componentName: string = isCustomDialogControllerPropertyAssignment(item, log);
if (componentName !== null) {
item = processCustomDialogControllerPropertyAssignment(parent,
item as ts.PropertyAssignment, componentName);
}
return item;
});
return ts.factory.createNewExpression(node.expression, node.typeArguments,
[ts.factory.createObjectLiteralExpression(newproperties, true), ts.factory.createThis()]);
} else {
return node;
}
}
function isCustomDialogControllerPropertyAssignment(node: ts.ObjectLiteralElementLike,
log: LogInfo[]): string {
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name) &&
node.name.getText() === CUSTOM_DIALOG_CONTROLLER_BUILDER) {
if (node.initializer) {
const componentName: string = getName(node.initializer);
if (componentCollection.customDialogs.has(componentName)) {
return componentName;
}
} else {
validateCustomDialogControllerBuilderInit(node, log);
}
}
return null;
}
function validateCustomDialogControllerBuilderInit(node: ts.ObjectLiteralElementLike,
log: LogInfo[]): void {
log.push({
type: LogType.ERROR,
message: 'The builder should be initialized with a @CustomDialog Component.',
pos: node.getStart(),
code: '10905335'
});
}
function processCustomDialogControllerPropertyAssignment(parent: ts.Expression,
node: ts.PropertyAssignment, componentName: string): ts.PropertyAssignment {
if (ts.isCallExpression(node.initializer)) {
return ts.factory.updatePropertyAssignment(node, node.name,
processCustomDialogControllerBuilder(parent, node.initializer, componentName));
}
return undefined;
}
function processCustomDialogControllerBuilder(parent: ts.Expression,
node: ts.CallExpression, componentName: string): ts.ArrowFunction {
const newExp: ts.Expression = createCustomComponentNewExpression(node, componentName, false, false, true);
const jsDialog: ts.Identifier = ts.factory.createIdentifier(JS_DIALOG);
return createCustomComponentBuilderArrowFunction(node, parent, jsDialog, newExp);
}
function createCustomComponentBuilderArrowFunction(node: ts.CallExpression, parent: ts.Expression,
jsDialog: ts.Identifier, newExp: ts.Expression): ts.ArrowFunction {
let mountNodde: ts.PropertyAccessExpression;
if (ts.isBinaryExpression(parent)) {
mountNodde = parent.left;
} else if (ts.isVariableDeclaration(parent) || ts.isPropertyDeclaration(parent)) {
mountNodde = ts.factory.createPropertyAccessExpression(ts.factory.createThis(),
parent.name as ts.Identifier);
}
return ts.factory.createArrowFunction(
undefined,
undefined,
[],
undefined,
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
ts.factory.createBlock(
[
ts.factory.createVariableStatement(
undefined,
ts.factory.createVariableDeclarationList(
[ts.factory.createVariableDeclaration(jsDialog, undefined, undefined, newExp)],
ts.NodeFlags.Let
)
),
ts.factory.createExpressionStatement(
ts.factory.createCallExpression(
ts.factory.createPropertyAccessExpression(
jsDialog,
ts.factory.createIdentifier(SET_CONTROLLER_METHOD)
),
undefined,
mountNodde ? [mountNodde] : undefined
)
),
ts.factory.createExpressionStatement(createViewCreate(jsDialog)),
partialUpdateConfig.partialUpdateMode ? assignComponentParams(node) : undefined,
partialUpdateConfig.partialUpdateMode ? assignmentFunction(jsDialog.escapedText.toString()) : undefined
],
true
)
);
}
export function isResource(node: ts.Node): boolean {
return ts.isCallExpression(node) && ts.isIdentifier(node.expression) &&
(node.expression.escapedText.toString() === RESOURCE ||
node.expression.escapedText.toString() === RESOURCE_RAWFILE) && node.arguments.length > 0;
}
export function isAnimateToOrImmediately(node: ts.Node): boolean {
return ts.isCallExpression(node) && ts.isIdentifier(node.expression) &&
ATTRIBUTE_ANIMATETO_SET.has(node.expression.escapedText.toString());
}
interface isCorrectResourcesType {
booleanValue: boolean;
}
export function processResourceData(node: ts.CallExpression, filePath: string,
previewLog: {isAcceleratePreview: boolean, log: LogInfo[]} = {isAcceleratePreview: false, log: []}): ts.Node {
let isTemplateString: boolean = false;
const isCorrectResources: isCorrectResourcesType = { booleanValue: false };
if (ts.isNoSubstitutionTemplateLiteral(node.arguments[0])) {
isTemplateString = true;
}
if (ts.isStringLiteral(node.arguments[0]) || isTemplateString) {
const resourceData: string[] = (node.arguments[0] as ts.StringLiteral).text.trim().split('.');
const isResourceModule: boolean = resourceData.length && /^\[.*\]$/g.test(resourceData[0]);
if (node.expression.getText() === RESOURCE_RAWFILE) {
isResourcefile(node, previewLog, isResourceModule, isTemplateString, isCorrectResources, filePath);
if (isCorrectResources.booleanValue) {
resourcePreviewMessage(previewLog);
return createResourceParamWithVariable(node, -1, RESOURCE_TYPE.rawfile, true, false);
}
if (resourceData && resourceData[0] && isResourceModule) {
return createResourceParam(-1, RESOURCE_TYPE.rawfile, [node.arguments[0]], resourceData[0], true);
} else {
return createResourceParam(0, RESOURCE_TYPE.rawfile, [node.arguments[0]], '', false);
}
} else {
const resourceDataNode: ts.Node = getResourceDataNode(node, previewLog, resourceData, isResourceModule, filePath, isTemplateString, isCorrectResources);
if (isCorrectResources.booleanValue) {
resourcePreviewMessage(previewLog);
return createResourceParamWithVariable(node, -1, -1, true, false);
}
return resourceDataNode;
}
} else if (node.expression.getText() === RESOURCE && node.arguments && node.arguments.length) {
resourcePreviewMessage(previewLog);
return createResourceParamWithVariable(node, -1, -1, false, true);
} else if (node.expression.getText() === RESOURCE_RAWFILE && node.arguments && node.arguments.length) {
resourcePreviewMessage(previewLog);
return createResourceParamWithVariable(node, -1, RESOURCE_TYPE.rawfile, false, false);
}
return node;
}
function resourcePreviewMessage(previewLog: {isAcceleratePreview: boolean, log: LogInfo[]}): void {
if (previewLog.isAcceleratePreview) {
previewLog.log.push({
type: LogType.ERROR,
message: 'not support AcceleratePreview',
code: '10906401'
});
}
}
function getResourceDataNode(node: ts.CallExpression, previewLog: {isAcceleratePreview: boolean, log: LogInfo[]},
resourceData: string[], isResourceModule: boolean, filePath: string, isTemplateString: boolean, isCorrectResources: isCorrectResourcesType): ts.Node {
let resourceValue: number;
if (preCheckResourceData(resourceData, resources, node.arguments[0].getStart(), previewLog, isResourceModule,
filePath, isTemplateString, isCorrectResources)) {
let resourceType: number = RESOURCE_TYPE[resourceData[1]];
if (resourceType === undefined && !previewLog.isAcceleratePreview) {
isTemplateString && (isCorrectResources.booleanValue = true);
transformLog.errors.push({
type: isTemplateString ? LogType.WARN : LogType.ERROR,
message: `The resource type '${resourceData[1]}' is not supported.`,
pos: node.getStart(),
code: '10906334'
});
return node;
}
if (isResourceModule) {
resourceValue = -1;
resourceType = -1;
} else {
resourceValue = resources[resourceData[0]][resourceData[1]][resourceData[2]];
}
return createResourceParam(resourceValue, resourceType,
projectConfig.compileHar || isResourceModule ? Array.from(node.arguments) : Array.from(node.arguments).slice(1),
resourceData.length && resourceData[0], isResourceModule, resourceData[0]);
}
return node;
}
function isResourcefile(node: ts.CallExpression, previewLog: {isAcceleratePreview: boolean, log: LogInfo[]}, isResourceModule: boolean,
isTemplateString: boolean, isCorrectResources: isCorrectResourcesType, filePath: string): void {
if (!ts.isStringLiteral(node.arguments[0]) && !ts.isNoSubstitutionTemplateLiteral(node.arguments[0])) {
return;
}
const resourceText: string = node.arguments[0].text;
if (!isResourceModule && process.env.rawFileResource && !storedFileInfo.resourcesArr.has(resourceText) &&
!previewLog.isAcceleratePreview && process.env.compileMode === 'moduleJson') {
isTemplateString && (isCorrectResources.booleanValue = true);
transformLog.errors.push({
type: isTemplateString ? LogType.WARN : LogType.ERROR,
message: `No such '${resourceText}' resource in current module.`,
pos: node.getStart(),
code: '10904333'
});
} else if (!isResourceModule && process.env.rawFileResource) {
if (!storedFileInfo.resourcesForFiles.has(filePath)) {
storedFileInfo.resourcesForFiles.set(filePath, []);
}
storedFileInfo.resourcesForFiles.get(filePath).push(resourceText);
}
if (isResourceModule && projectConfig.hspResourcesMap && rawfileResources.keys() &&
!previewLog.isAcceleratePreview && process.env.compileMode === 'moduleJson') {
const resourceData: string[] = resourceText.trim().split('.');
const resourceDataFirst: string = resourceData[0].replace(/^\[/, '').replace(/\]$/, '').trim();
const needCheckResource: boolean = checkHspRawfileSource(resourceDataFirst, node);
const resourceParam: string = getAfterFirstDotRegex(resourceText);
needCheckResource && checkHspRawfileParam(resourceDataFirst, resourceParam, node);
}
}
function checkHspRawfileParam(resourceDataFirst: string, resourceParam: string, node: ts.CallExpression): void {
if (!rawfileResources.has(resourceDataFirst)) {
return;
}
const hspRawfiles: string[] | undefined = rawfileResources.get(resourceDataFirst);
if (hspRawfiles && Array.isArray(hspRawfiles) &&
!hspRawfiles.includes(resourceParam)) {
transformLog.errors.push({
message: `No such '[${resourceDataFirst}].${resourceParam}' resource.`,
type: LogType.WARN,
pos: node.getStart()
});
}
}
function checkHspRawfileSource(resourceDataFirst: string, node: ts.CallExpression): boolean {
const collectedHspNames: string[] = Array.from(rawfileResources.keys());
if (!collectedHspNames.length || !collectedHspNames.includes(resourceDataFirst)) {
transformLog.errors.push({
message: `Unknown resource source '[${resourceDataFirst}]'.`,
type: LogType.WARN,
pos: node.getStart()
});
return false;
}
return true;
}
function getAfterFirstDotRegex(str: string): string {
const regex = /^[^.]*\./;
return str.replace(regex, '');
}
function addBundleAndModuleParam(propertyArray: Array<ts.PropertyAssignment | ts.GetAccessorDeclaration>,
resourceModuleName: string, isResourceModule: boolean): void {
if (projectConfig.compileHar) {
projectConfig.bundleName = '__harDefaultBundleName__';
projectConfig.moduleName = '__harDefaultModuleName__';
}
const isDynamicBundleOrModule: boolean = isDynamic();
const isResetAndNoReplace: boolean = projectConfig.resetBundleName && !getGetCurrentBundleName();
const moduleNameNode: ts.Expression = createResourceModuleNode(resourceModuleName, isResourceModule, isDynamicBundleOrModule);
if (projectConfig.bundleName || projectConfig.bundleName === '') {
propertyArray.push(ts.factory.createPropertyAssignment(
ts.factory.createStringLiteral(RESOURCE_NAME_BUNDLE),
(isResetAndNoReplace || projectConfig.allowEmptyBundleName) ? ts.factory.createStringLiteral('') :
createBundleOrModuleNode(isDynamicBundleOrModule, 'bundleName')
));
}
if (projectConfig.moduleName || projectConfig.moduleName === '' || moduleNameNode) {
propertyArray.push(ts.factory.createPropertyAssignment(
ts.factory.createStringLiteral(RESOURCE_NAME_MODULE),
isResourceModule ? moduleNameNode : createBundleOrModuleNode(isDynamicBundleOrModule, 'moduleName')
));
}
}
function createResourceModuleNode(resourceModuleName: string, isResourceModule: boolean,
isDynamicBundleOrModule: boolean): ts.Expression {
if (isResourceModule) {
if (resourceModuleName) {
const moduleName: string = resourceModuleName.replace(/^\[|\]$/g, '');
return ts.factory.createStringLiteral(moduleName);
}
if (isDynamicBundleOrModule) {
return ts.factory.createIdentifier('__MODULE_NAME__');
}
return projectConfig.moduleName ? ts.factory.createStringLiteral(projectConfig.moduleName) : undefined;
}
return undefined;
}
function createBundleOrModuleNode(isDynamicBundleOrModule: boolean, type: string): ts.Expression {
if (isDynamicBundleOrModule) {
return ts.factory.createIdentifier(type === 'bundleName' ? '__BUNDLE_NAME__' : '__MODULE_NAME__');
}
return ts.factory.createStringLiteral(type === 'bundleName' ? projectConfig.bundleName :
projectConfig.moduleName);
}
function createResourceParamWithVariable(node: ts.CallExpression, resourceValue: number, resourceType: number,
isTemplateStringOrString: boolean, isVariableParamR: boolean): ts.ObjectLiteralExpression {
const propertyArray: Array<ts.PropertyAssignment | ts.GetAccessorDeclaration> = [
(isTemplateStringOrString || !isVariableParamR || projectConfig.minAPIVersion%1000 < APIVERSION.apiVersionTwentyTwo) ?
ts.factory.createPropertyAssignment(
ts.factory.createStringLiteral(RESOURCE_NAME_ID),
ts.factory.createNumericLiteral(resourceValue)
) : resourceIdName(__GETRESOURCEID__),
ts.factory.createPropertyAssignment(
ts.factory.createStringLiteral(RESOURCE_NAME_TYPE),
ts.factory.createNumericLiteral(resourceType)
),
ts.factory.createPropertyAssignment(
ts.factory.createIdentifier(RESOURCE_NAME_PARAMS),
ts.factory.createArrayLiteralExpression(Array.from(node.arguments), false)
)
];
addBundleAndModuleParam(propertyArray, '', true);
const resourceParams: ts.ObjectLiteralExpression = ts.factory.createObjectLiteralExpression(
propertyArray, false);
return resourceParams;
}
function resourceIdName(funcName: string): ts.GetAccessorDeclaration {
return ts.factory.createGetAccessorDeclaration(
undefined, ts.factory.createIdentifier(ATTRIBUTE_ID), [], undefined,
ts.factory.createBlock([ts.factory.createReturnStatement(ts.factory.createConditionalExpression(
ts.factory.createBinaryExpression(
ts.factory.createTypeOfExpression(ts.factory.createIdentifier(funcName)),
ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
ts.factory.createStringLiteral(FUNCTION)
),
ts.factory.createToken(ts.SyntaxKind.QuestionToken),
ts.factory.createCallExpression(
ts.factory.createIdentifier(funcName),
undefined,
[ts.factory.createThis()]
),
ts.factory.createToken(ts.SyntaxKind.ColonToken),
ts.factory.createPrefixUnaryExpression(
ts.SyntaxKind.MinusToken,
ts.factory.createNumericLiteral('1')
)
))
], true));
}
function createResourceParam(resourceValue: number, resourceType: number, argsArr: ts.Expression[],
resourceModuleName: string, isResourceModule: boolean, resourceSource: string = ''): ts.ObjectLiteralExpression {
if (projectConfig.compileHar) {
if (projectConfig.minAPIVersion%1000 < APIVERSION.apiVersiontwentyThree) {
resourceValue = -1;
} else if (projectConfig.minAPIVersion%1000 >= APIVERSION.apiVersiontwentyThree && resourceSource !== 'sys') {
resourceValue = -1;
}
}
const propertyArray: Array<ts.PropertyAssignment | ts.GetAccessorDeclaration> = [];
const resourceIdKeyValue: ts.PropertyAssignment = ts.factory.createPropertyAssignment(
ts.factory.createStringLiteral(RESOURCE_NAME_ID),
ts.factory.createNumericLiteral(resourceValue)
);
if (resourceType === RESOURCE_TYPE.rawfile) {
propertyArray.push(resourceIdKeyValue);
} else if (isResourceModule && projectConfig.minAPIVersion%1000 >= APIVERSION.apiVersionTwentyTwo) {
propertyArray.push(resourceIdName(__GETRESOURCEID__));
} else if (isCompileHar(isResourceModule, resourceSource)) {
propertyArray.push(resourceIdName(__RESOURCEIDHAR__));
} else {
propertyArray.push(resourceIdKeyValue);
}
propertyArray.push(ts.factory.createPropertyAssignment(
ts.factory.createStringLiteral(RESOURCE_NAME_TYPE),
ts.factory.createNumericLiteral(resourceType)
),
ts.factory.createPropertyAssignment(
ts.factory.createIdentifier(RESOURCE_NAME_PARAMS),
ts.factory.createArrayLiteralExpression(argsArr, false)
));
addBundleAndModuleParam(propertyArray, resourceModuleName, isResourceModule);
const resourceParams: ts.ObjectLiteralExpression = ts.factory.createObjectLiteralExpression(
propertyArray, false);
return resourceParams;
}
function isCompileHar(isResourceModule: boolean, resourceSource: string): boolean {
return projectConfig.compileHar && !isResourceModule && resourceSource !== 'sys' &&
projectConfig.minAPIVersion%1000 >= APIVERSION.apiVersiontwentyThree;
}
function preCheckResourceData(resourceData: string[], resources: object, pos: number, previewLog: {isAcceleratePreview: boolean, log: LogInfo[]},
isResourceModule: boolean, filePath: string, isTemplateString: boolean, isCorrectResources: isCorrectResourcesType): boolean {
if (previewLog.isAcceleratePreview) {
return validateResourceData(resourceData, resources, pos, previewLog.log, true, isResourceModule, filePath, isTemplateString, isCorrectResources);
} else {
return validateResourceData(resourceData, resources, pos, transformLog.errors, false, isResourceModule, filePath, isTemplateString, isCorrectResources);
}
}
function validateResourceData(resourceData: string[], resources: object, pos: number, log: LogInfo[], isAcceleratePreview: boolean,
isResourceModule: boolean, filePath: string, isTemplateString: boolean, isCorrectResources: isCorrectResourcesType): boolean {
if (resourceData.length !== 3) {
isTemplateString && (isCorrectResources.booleanValue = true);
log.push({
type: isTemplateString ? LogType.WARN : LogType.ERROR,
message: `Invalid resource file parameter. Enter a value in the format of 'xxx.yyy.zzz'.`,
pos,
code: '10905332'
});
} else {
if (!isAcceleratePreview && process.env.compileTool === 'rollup' && process.env.compileMode === 'moduleJson') {
storedFileInfo.collectResourceInFile(resourceData[1] + '_' + resourceData[2], path.resolve(filePath));
}
if (isResourceModule) {
if (/^\[.*\]$/.test(resourceData[0]) && projectConfig.hspResourcesMap) {
const resourceDataFirst: string = resourceData[0].replace(/^\[/, '').replace(/\]$/, '').trim();
return resourceCheck(resourceData, resources, pos, log, true, resourceDataFirst, false, isTemplateString, isCorrectResources);
} else {
return resourceCheck(resourceData, resources, pos, log, false, resourceData[0], true, isTemplateString, isCorrectResources);
}
} else {
return resourceCheck(resourceData, resources, pos, log, false, resourceData[0], false, isTemplateString, isCorrectResources);
}
}
return false;
}
function resourceCheck(resourceData: string[], resources: object, pos: number, log: LogInfo[], isHspResourceModule: boolean,
resourceDataFirst: string, faOrNoHspResourcesMap: boolean, isTemplateString: boolean, isCorrectResources: isCorrectResourcesType): boolean {
const logType: LogType = isHspResourceModule || isTemplateString ? LogType.WARN : LogType.ERROR;
if (!faOrNoHspResourcesMap && !resources[resourceDataFirst]) {
isTemplateString && (isCorrectResources.booleanValue = true);
log.push({
type: logType,
message: `Unknown resource source '${resourceDataFirst}'.`,
pos,
code: '10903331'
});
} else if (!faOrNoHspResourcesMap && !resources[resourceDataFirst][resourceData[1]]) {
isTemplateString && (isCorrectResources.booleanValue = true);
log.push({
type: logType,
message: `Unknown resource type '${resourceData[1]}'.`,
pos,
code: '10903330'
});
} else if (!faOrNoHspResourcesMap && !resources[resourceDataFirst][resourceData[1]][resourceData[2]]) {
isTemplateString && (isCorrectResources.booleanValue = true);
log.push({
type: logType,
message: `Unknown resource name '${resourceData[2]}'.`,
pos,
code: '10903329'
});
} else {
return true;
}
return false;
}
function isWorker(node: ts.Node): boolean {
return ts.isNewExpression(node) && ts.isPropertyAccessExpression(node.expression) &&
ts.isIdentifier(node.expression.name) &&
node.expression.name.escapedText.toString() === WORKER_OBJECT;
}
function processWorker(node: ts.NewExpression): ts.Node {
if (node.arguments.length && ts.isStringLiteral(node.arguments[0])) {
const args: ts.Expression[] = Array.from(node.arguments);
const workerPath: string = node.arguments[0].text;
const stringNode: ts.StringLiteral = ts.factory.createStringLiteral(
workerPath.replace(/\.ts$/, '.js'));
args.splice(0, 1, stringNode);
return ts.factory.updateNewExpression(node, node.expression, node.typeArguments, args);
}
return node;
}
export function processAnimateToOrImmediately(node: ts.CallExpression): ts.CallExpression {
return ts.factory.updateCallExpression(node, ts.factory.createPropertyAccessExpression(
ts.factory.createIdentifier(GLOBAL_CONTEXT),
ts.factory.createIdentifier(node.expression.escapedText.toString())),
node.typeArguments, node.arguments);
}
function processExtend(node: ts.FunctionDeclaration, log: LogInfo[],
decoratorName: string): ts.FunctionDeclaration {
const componentName: string = isExtendFunction(node, { decoratorName: '', componentName: '' }, true);
checkExtendNode(node, componentName, log);
if (componentName && node.body && !node.body.statements.length && decoratorName === COMPONENT_EXTEND_DECORATOR) {
const statementArray: ts.Statement[] = [];
const bodynode: ts.Block = ts.visitEachChild(node.body, traverseExtendExpression, contextGlobal);
let extendFunctionName: string;
if (node.name.getText().startsWith('__' + componentName + '__')) {
extendFunctionName = node.name.getText();
} else {
extendFunctionName = '__' + componentName + '__' + node.name.getText();
collectExtend(EXTEND_ATTRIBUTE, componentName, node.name.escapedText.toString());
}
return ts.factory.updateFunctionDeclaration(node, ts.getModifiers(node), node.asteriskToken,
ts.factory.createIdentifier(extendFunctionName), node.typeParameters,
node.parameters, ts.factory.createToken(ts.SyntaxKind.VoidKeyword), isOriginalExtend(node.body) ?
ts.factory.updateBlock(node.body, statementArray) : bodynode);
}
if (componentName && node.body) {
const statementArray: ts.Statement[] = [];
let bodynode: ts.Block;
if (decoratorName === COMPONENT_EXTEND_DECORATOR) {
if (isOriginalExtend(node.body) && node.body.statements.length) {
const attrSet: ts.CallExpression = node.body.statements[0].expression;
const changeCompName: ts.ExpressionStatement = ts.factory.createExpressionStatement(processExtendBody(attrSet));
bindComponentAttr(changeCompName as ts.ExpressionStatement,
ts.factory.createIdentifier(componentName), statementArray, log,
true, false, null, false, null, false, false);
} else {
bodynode = ts.visitEachChild(node.body, traverseExtendExpression, contextGlobal);
}
let extendFunctionName: string;
if (node.name.getText().startsWith('__' + componentName + '__')) {
extendFunctionName = node.name.getText();
} else {
extendFunctionName = '__' + componentName + '__' + node.name.getText();
collectExtend(EXTEND_ATTRIBUTE, componentName, node.name.escapedText.toString());
}
return ts.factory.updateFunctionDeclaration(node, ts.getModifiers(node), node.asteriskToken,
ts.factory.createIdentifier(extendFunctionName), node.typeParameters,
node.parameters, ts.factory.createToken(ts.SyntaxKind.VoidKeyword), isOriginalExtend(node.body) ?
ts.factory.updateBlock(node.body, statementArray) : bodynode);
}
if (decoratorName === COMPONENT_ANIMATABLE_EXTEND_DECORATOR) {
return processAnimatableExtend(node, statementArray, componentName, log);
}
}
function traverseExtendExpression(node: ts.Node): ts.Node {
if (ts.isExpressionStatement(node) && isDollarNode(node, componentName)) {
const changeCompName: ts.ExpressionStatement =
ts.factory.createExpressionStatement(processExtendBody(node.expression, componentName));
const statementArray: ts.Statement[] = [];
bindComponentAttr(changeCompName, ts.factory.createIdentifier(componentName), statementArray, [],
true, false, null, false, null, false, false);
return ts.factory.createBlock(statementArray, true);
}
return ts.visitEachChild(node, traverseExtendExpression, contextGlobal);
}
return undefined;
}
function processAnimatableExtend(node: ts.FunctionDeclaration, statementArray: ts.Statement[], componentName: string, log: LogInfo[]): ts.FunctionDeclaration {
if (node.body.statements.length) {
bindComponentAttr(node.body.statements[0],
ts.factory.createIdentifier(componentName), statementArray, log,
true, false, null, false, null, false, false);
}
return ts.factory.updateFunctionDeclaration(node, ts.getModifiers(node), node.asteriskToken,
node.name, node.typeParameters,
[...node.parameters, ...createAnimatableParameterNode()],
ts.factory.createToken(ts.SyntaxKind.VoidKeyword),
ts.factory.updateBlock(node.body,
node.body.statements.length ? createAnimatableBody(componentName, node.name, node.parameters, statementArray) : []
));
}
function createAnimatableParameterNode(): ts.ParameterDeclaration[] {
return [
ts.factory.createParameterDeclaration(
undefined, undefined, ts.factory.createIdentifier(ELMTID)),
ts.factory.createParameterDeclaration(
undefined, undefined, ts.factory.createIdentifier(ISINITIALRENDER)),
ts.factory.createParameterDeclaration(
undefined, undefined, ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARENT))
];
}
function createAnimatableBody(componentName: string, funcName: ts.Identifier,
parameters: ts.NodeArray<ts.ParameterDeclaration>, attrArray: ts.Statement[]): ts.Statement[] {
const paramNode: ts.Identifier[] = [];
parameters.forEach((item: ts.ParameterDeclaration) => {
if (item.name && ts.isIdentifier(item.name)) {
paramNode.push(item.name);
}
});
return [
ts.factory.createIfStatement(
ts.factory.createIdentifier(ISINITIALRENDER),
ts.factory.createBlock([
createAnimatableProperty(componentName, funcName, parameters, paramNode, attrArray),
...attrArray
], true),
ts.factory.createBlock([
ts.factory.createExpressionStatement(ts.factory.createCallExpression(
ts.factory.createPropertyAccessExpression(
ts.factory.createIdentifier(componentName),
ts.factory.createIdentifier(UPDATE_ANIMATABLE_PROPERTY)
), undefined,
[ts.factory.createStringLiteral(funcName.escapedText.toString()), ...paramNode]
))
])
)
];
}
function createAnimatableProperty(componentName: string, funcName: ts.Identifier,
parameters: ts.NodeArray<ts.ParameterDeclaration>,
paramNode: ts.Identifier[], attrArray: ts.Statement[]) {
const componentIdentifier: ts.Identifier = ts.factory.createIdentifier(componentName);
return ts.factory.createExpressionStatement(
ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(
componentIdentifier,
ts.factory.createIdentifier(CREATE_ANIMATABLE_PROPERTY)),
undefined, [
ts.factory.createStringLiteral(funcName.escapedText.toString()),
...paramNode,
ts.factory.createArrowFunction(undefined, undefined, parameters, undefined,
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
ts.factory.createBlock([
createViewStackProcessorStatement(STARTGETACCESSRECORDINGFOR, ELMTID),
createAnimatableFrameNode(componentName),
...attrArray,
createViewStackProcessorStatement(STOPGETACCESSRECORDING),
createAnimatableUpdateFunc()
], true))
]
)
);
}
function createAnimatableFrameNode(componentName: string): ts.ExpressionStatement {
return ts.factory.createExpressionStatement(ts.factory.createCallExpression(
ts.factory.createPropertyAccessExpression(
ts.factory.createIdentifier(VIEW_STACK_PROCESSOR),
ts.factory.createIdentifier(GET_AND_PUSH_FRAME_NODE)
), undefined,
[
ts.factory.createStringLiteral(componentName),
ts.factory.createIdentifier(ELMTID)
]
));
}
function createAnimatableUpdateFunc(): ts.ExpressionStatement {
return ts.factory.createExpressionStatement(ts.factory.createCallExpression(
ts.factory.createPropertyAccessExpression(
ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARENT),
ts.factory.createIdentifier(FINISH_UPDATE_FUNC)
), undefined, [ts.factory.createIdentifier(ELMTID)]
));
}
function processConcurrent(node: ts.FunctionDeclaration): ts.FunctionDeclaration {
if (node.body) {
const statementArray: ts.Statement[] =
[ts.factory.createExpressionStatement(ts.factory.createStringLiteral('use concurrent')),
...node.body.statements];
return ts.factory.updateFunctionDeclaration(node, ts.getModifiers(node), node.asteriskToken, node.name,
node.typeParameters, node.parameters, node.type, ts.factory.updateBlock(node.body, statementArray));
}
return node;
}
export function isOriginalExtend(node: ts.Block): boolean {
let innerNode: ts.Node = node.statements[0];
if (node.statements.length === 1 && ts.isExpressionStatement(innerNode)) {
while (innerNode.expression) {
innerNode = innerNode.expression;
}
if (ts.isIdentifier(innerNode) && innerNode.pos && innerNode.end && innerNode.pos === innerNode.end &&
innerNode.escapedText.toString().match(/Instance$/)) {
return true;
}
}
return false;
}
function isDollarNode(node: ts.ExpressionStatement, componentName: string): boolean {
let innerNode: ts.Node = node;
while (innerNode.expression) {
innerNode = innerNode.expression;
}
let changedIdentifier: string = '$';
if (process.env.compileTool === 'rollup' && storedFileInfo.reUseProgram) {
changedIdentifier = `${componentName}Instance`;
}
if (ts.isIdentifier(innerNode) && innerNode.getText() === changedIdentifier) {
return true;
} else {
return false;
}
}
function processExtendBody(node: ts.Node, componentName?: string): ts.Expression {
switch (node.kind) {
case ts.SyntaxKind.CallExpression:
return ts.factory.createCallExpression(processExtendBody(node.expression, componentName),
undefined, node.arguments);
case ts.SyntaxKind.PropertyAccessExpression:
return ts.factory.createPropertyAccessExpression(
processExtendBody(node.expression, componentName), node.name);
case ts.SyntaxKind.Identifier:
if (!componentName) {
return ts.factory.createIdentifier(node.escapedText.toString().replace(INSTANCE, ''));
} else {
return ts.factory.createIdentifier(componentName);
}
}
return undefined;
}
export function collectExtend(collectionSet: Map<string, Set<string>>, component: string, attribute: string): void {
if (collectionSet.has(component)) {
collectionSet.get(component).add(attribute);
} else {
collectionSet.set(component, new Set([attribute]));
}
}
export function isExtendFunction(node: ts.FunctionDeclaration, extendResult: ExtendResult,
checkArguments: boolean = false): string {
const decorators: readonly ts.Decorator[] = ts.getAllDecorators(node);
if (decorators && decorators.length) {
for (let i = 0, len = decorators.length; i < len; i++) {
if (ts.isCallExpression(decorators[i].expression)) {
parseExtendNode(decorators[i].expression as ts.CallExpression, extendResult, checkArguments);
if (CHECK_EXTEND_DECORATORS.includes(extendResult.decoratorName) && extendResult.componentName) {
return extendResult.componentName;
}
}
}
}
return null;
}
function parseExtendNode(node: ts.CallExpression, extendResult: ExtendResult, checkArguments: boolean): void {
if (ts.isIdentifier(node.expression)) {
extendResult.decoratorName = node.expression.escapedText.toString();
if (checkArguments && CHECK_EXTEND_DECORATORS.includes(extendResult.decoratorName) &&
node.arguments && node.arguments.length !== 1) {
transformLog.errors.push({
type: LogType.ERROR,
message: `'@${extendResult.decoratorName}' should have one and only one parameter.`,
pos: node.getStart(),
code: '10905108'
});
}
if (checkArguments && CHECK_EXTEND_DECORATORS.includes(extendResult.decoratorName) &&
node.arguments && node.arguments.length === 1 && ts.isIdentifier(node.arguments[0]) &&
!INNER_COMPONENT_NAMES.has(node.arguments[0].getText())) {
transformLog.errors.push({
type: LogType.WARN,
message: `'${node.arguments[0].getText()}' parameter cannot be` +
` passed in the '@${extendResult.decoratorName}' decorator.`,
pos: node.getStart()
});
}
}
if (node.arguments.length && ts.isIdentifier(node.arguments[0])) {
extendResult.componentName = node.arguments[0].escapedText.toString();
}
}
function checkExtendNode(node: ts.FunctionDeclaration, componentName: string,
log: LogInfo[]): void {
const componentInstance: string = `${componentName}Instance`;
if (node.body && ts.isBlock(node.body) && node.body.statements &&
node.body.statements.length > 1) {
validateUIFunctionFormat(log, node.body.statements[0]);
return;
}
if (node.body && ts.isBlock(node.body) && node.body.statements &&
node.body.statements.length === 1 && ts.isExpressionStatement(node.body.statements[0])) {
!validateComponentInstance(node.body.statements[0], componentInstance) &&
validateUIFunctionFormat(log, node.body.statements[0]);
return;
}
if (node.body && ts.isBlock(node.body) && node.body.statements &&
node.body.statements.length === 1 && !ts.isExpressionStatement(node.body.statements[0])) {
validateUIFunctionFormat(log, node.body.statements[0]);
return;
}
}
function validateComponentInstance(node: ts.ExpressionStatement, targetInstanceName: string): boolean {
if (!ts.isCallExpression(node.expression)) {
return false;
}
const instanceName: string = findInstanceIdentifier(node.expression);
return instanceName === targetInstanceName;
}
function findInstanceIdentifier(node: ts.CallExpression): string {
let instanceName: string = '';
if (!ts.isPropertyAccessExpression(node.expression)) {
return instanceName;
}
const newNode: ts.PropertyAccessExpression = node.expression;
if (ts.isIdentifier(newNode.expression)) {
instanceName = newNode.expression.escapedText.toString();
return instanceName;
} else if (ts.isCallExpression(newNode.expression)) {
instanceName = findInstanceIdentifier(newNode.expression);
}
return instanceName;
}
function validateUIFunctionFormat(log: LogInfo[], block: ts.Statement): void {
log.push({
message: `Only UI component syntax can be written here.`,
type: LogType.WARN,
pos: block.getStart()
});
}
export function parseStylesNode(node: ts.FunctionDeclaration | ts.MethodDeclaration, log: LogInfo[]): void {
const commonInstance: string = 'CommonInstance';
if (node.body && ts.isBlock(node.body) && node.body.statements && node.body.statements.length) {
if (node.body.statements.length === 1 && ts.isExpressionStatement(node.body.statements[0])) {
!validateComponentInstance(node.body.statements[0], commonInstance) &&
validateUIFunctionFormat(log, node.body.statements[0]);
return;
}
validateUIFunctionFormat(log, node.body.statements[0]);
}
}
function createEntryNode(node: ts.SourceFile, context: ts.TransformationContext,
entryNodeKey: ts.Expression, id: number): ts.SourceFile {
let cardRelativePath: string;
if (projectConfig && projectConfig.cardObj) {
cardRelativePath = projectConfig.cardObj[resourceFileName];
}
if (componentCollection.previewComponent.length === 0 || !projectConfig.isPreview) {
if (componentCollection.entryComponent) {
if (!partialUpdateConfig.partialUpdateMode) {
const entryNode: ts.ExpressionStatement =
createEntryFunction(componentCollection.entryComponent, context,
cardRelativePath, entryNodeKey, id) as ts.ExpressionStatement;
return context.factory.updateSourceFile(node, [...node.statements, entryNode]);
} else {
const entryNodes: ts.ExpressionStatement[] =
createEntryFunction(componentCollection.entryComponent, context,
cardRelativePath, entryNodeKey, id) as ts.ExpressionStatement[];
return entryNodes ?
context.factory.updateSourceFile(node, [...node.statements, ...entryNodes]) :
context.factory.updateSourceFile(node, [...node.statements]);
}
} else {
return node;
}
} else {
const statementsArray: ts.Statement =
createPreviewComponentFunction(componentCollection.entryComponent, context, cardRelativePath, entryNodeKey, id);
return context.factory.updateSourceFile(node, [...node.statements, statementsArray]);
}
}
function createEntryFunction(name: string, context: ts.TransformationContext, cardRelativePath: string,
entryNodeKey: ts.Expression, id: number): ts.ExpressionStatement | (ts.Statement | ts.Block)[] {
const newArray: ts.Expression[] = [
context.factory.createStringLiteral(id.toString()),
context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
context.factory.createObjectLiteralExpression([], false)
];
const [localStorageName, entryOptionNode]: [string, ts.Expression] = addStorageParam(name);
if (localStorageName && entryNodeKey) {
newArray.push(entryNodeKey);
}
const newExpressionParams: any[] = [
context.factory.createNewExpression(
context.factory.createIdentifier(name), undefined, newArray)];
addCardStringliteral(newExpressionParams, context, cardRelativePath);
if (!partialUpdateConfig.partialUpdateMode) {
const newExpressionStatement: ts.ExpressionStatement =
context.factory.createExpressionStatement(context.factory.createCallExpression(
context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME :
PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams));
return newExpressionStatement;
} else {
if (cardRelativePath) {
if (entryOptionNode && ts.isObjectLiteralExpression(entryOptionNode)) {
transformLog.errors.push({
type: LogType.ERROR,
message: `'@Entry' doesn't support {} parameter in card`,
pos: componentCollection.entryComponentPos,
code: '10906218'
});
}
return [
createStartGetAccessRecording(context),
createLoadDocument(context, name, cardRelativePath, localStorageName, entryNodeKey),
createStopGetAccessRecording(context)
];
} else {
return createLoadPageConditionalJudgMent(context, name, cardRelativePath, localStorageName, entryOptionNode);
}
}
}
function createLoadPageConditionalJudgMent(context: ts.TransformationContext, name: string,
cardRelativePath: string, localStorageName: string, entryOptionNode: ts.Expression,
argsArr: ts.Expression[] = undefined, isComponentPreview: boolean = false): (ts.Statement | ts.Block)[] {
let isObject: boolean = false;
const entryOptionValue: EntryOptionValue = new EntryOptionValue();
if (!entryOptionNode) {
let originArray: ts.ExpressionStatement[];
if (projectConfig.minAPIVersion > APIVERSION.apiVersionTen) {
if (componentCollection.entryComponent === name && componentCollection.localSharedStorage) {
return [judgeRouteNameAndStorageForIdentifier(context, name,
cardRelativePath, isObject, componentCollection.localSharedStorage, undefined, undefined, argsArr)];
}
const newArray: ts.Expression[] = [
context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
context.factory.createObjectLiteralExpression([], false)
];
const newExpressionParams: any[] = [
context.factory.createNewExpression(context.factory.createIdentifier(name), undefined, newArray)];
originArray = [
createRegisterNamedRoute(context, newExpressionParams, false, undefined, false)
];
} else {
originArray = [
createStartGetAccessRecording(context),
createLoadDocument(context, name, cardRelativePath, localStorageName, entryOptionNode),
createStopGetAccessRecording(context)
];
}
return originArray;
}
if (ts.isObjectLiteralExpression(entryOptionNode)) {
isObject = true;
if (entryOptionNode.properties) {
entryOptionNode.properties.forEach((property) => {
if (ts.isPropertyAssignment(property) && property.name && ts.isIdentifier(property.name)) {
entryOptionValue[property.name.escapedText.toString()] = property.initializer;
}
});
if (entryOptionValue.useSharedStorage) {
entryOptionValue.storage = createGetShared(entryOptionValue);
}
}
} else {
isObject = false;
}
return generateLoadDocumentEntrance(isObject, entryOptionValue.routeName, entryOptionValue.storage,
isComponentPreview, context, name, cardRelativePath, entryOptionNode, argsArr);
}
function generateLoadDocumentEntrance(isObject: boolean, routeNameNode: ts.Expression,
storageNode: ts.Expression, isComponentPreview: boolean, context: ts.TransformationContext,
name: string, cardRelativePath: string, entryOptionNode: ts.Expression,
argsArr: ts.Expression[]): (ts.ExpressionStatement | ts.Block | ts.IfStatement)[] {
if (isObject) {
if (routeNameNode && !storageNode) {
return isComponentPreview ? [
...assignRouteNameAndStorage(routeNameNode),
...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
routeNameNode, storageNode, true, false, false, argsArr)
] : [ts.factory.createBlock([
...assignRouteNameAndStorage(routeNameNode),
...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
routeNameNode, storageNode, true, false, false, argsArr)
])];
} else if (!routeNameNode && !storageNode) {
return isComponentPreview ? [
...assignRouteNameAndStorage(routeNameNode),
...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
routeNameNode, storageNode, false, false, true, argsArr)
] : [ts.factory.createBlock([
...assignRouteNameAndStorage(routeNameNode),
...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
routeNameNode, storageNode, false, false, true, argsArr)
])];
} else if (!routeNameNode && storageNode) {
return isComponentPreview ? [
...assignRouteNameAndStorage(routeNameNode),
...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
routeNameNode, storageNode, false, true, true, argsArr)
] : [ts.factory.createBlock([
...assignRouteNameAndStorage(routeNameNode),
...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
routeNameNode, storageNode, false, true, true, argsArr)
])];
} else {
return isComponentPreview ? [
...assignRouteNameAndStorage(routeNameNode),
judgeRouteNameAndStorage(context, name, cardRelativePath, isObject, entryOptionNode, routeNameNode, storageNode, argsArr)
] : [ts.factory.createBlock([
...assignRouteNameAndStorage(routeNameNode),
judgeRouteNameAndStorage(context, name, cardRelativePath, isObject, entryOptionNode, routeNameNode, storageNode, argsArr)
])];
}
} else {
return [
judgeRouteNameAndStorage(context, name, cardRelativePath, isObject, entryOptionNode, routeNameNode, storageNode, argsArr)];
}
}
function judgeRouteNameAndStorage(context: ts.TransformationContext, name: string,
cardRelativePath: string, isObject: boolean, entryOptionNode: ts.Expression, routeNameNode: ts.Expression,
storageNode: ts.Expression, argsArr: ts.Expression[] = undefined): ts.IfStatement {
return isObject ? judgeRouteNameAndStorageForObj(context, name, cardRelativePath, isObject, entryOptionNode,
routeNameNode, storageNode, argsArr) : judgeRouteNameAndStorageForIdentifier(context, name,
cardRelativePath, isObject, entryOptionNode, routeNameNode, storageNode, argsArr);
}
function judgeRouteNameAndStorageForObj(context: ts.TransformationContext, name: string,
cardRelativePath: string, isObject: boolean, entryOptionNode: ts.Expression, routeNameNode: ts.Expression,
storageNode: ts.Expression, argsArr: ts.Expression[] = undefined): ts.IfStatement {
return ts.factory.createIfStatement(
judgeRouteAndStorageForObject(true),
ts.factory.createBlock(
[
...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
routeNameNode, storageNode, true, true, false, argsArr)
],
true
), ts.factory.createBlock(
[
...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
routeNameNode, storageNode, false, false, true, argsArr)
],
true
));
}
function judgeRouteNameAndStorageForIdentifier(context: ts.TransformationContext, name: string,
cardRelativePath: string, isObject: boolean, entryOptionNode: ts.Expression, routeNameNode: ts.Expression,
storageNode: ts.Expression, argsArr: ts.Expression[] = undefined): ts.IfStatement {
return ts.factory.createIfStatement(
judgeRouteAndStorageForIdentifier(entryOptionNode as ts.Identifier, true, true),
ts.factory.createBlock(
[
...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
routeNameNode, storageNode, true, true, false, argsArr)
],
true
),
ts.factory.createIfStatement(
judgeRouteAndStorageForIdentifier(entryOptionNode as ts.Identifier, true, false),
ts.factory.createBlock(
[
...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
routeNameNode, storageNode, true, false, false, argsArr)
],
true
),
ts.factory.createIfStatement(
judgeRouteAndStorageForIdentifier(entryOptionNode as ts.Identifier, false, true),
ts.factory.createBlock(
[
...createLoadDocumentWithRoute(context, name, cardRelativePath, isObject, entryOptionNode,
routeNameNode, storageNode, false, true, true, argsArr)
],
true
),
ts.factory.createIfStatement(
judgeUseSharedStorageForExpresion(entryOptionNode),
ts.factory.createBlock(
[...createSharedStorageWithRoute(context, name, cardRelativePath, entryOptionNode, true, argsArr)], true),
ts.factory.createBlock(
[...createLoadDocumentWithRoute(context, name, cardRelativePath, false, entryOptionNode,
null, null, false, false, true, argsArr)
], true)
)
)
)
);
}
function judgeRouteAndStorageForObject(hasRouteName: boolean): ts.BinaryExpression {
return ts.factory.createBinaryExpression(
ts.factory.createIdentifier(ROUTENAME_NODE),
ts.factory.createToken(hasRouteName ? ts.SyntaxKind.ExclamationEqualsToken : ts.SyntaxKind.EqualsEqualsToken),
ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)
);
}
function judgeRouteAndStorageForIdentifier(entryOptionNode: ts.Identifier, hasRouteName: boolean,
hasStorage: boolean): ts.BinaryExpression {
return ts.factory.createBinaryExpression(
ts.factory.createBinaryExpression(
entryOptionNode,
ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
ts.factory.createBinaryExpression(
ts.factory.createPropertyAccessExpression(
entryOptionNode,
ts.factory.createIdentifier(ROUTE_NAME)
),
ts.factory.createToken(hasRouteName ? ts.SyntaxKind.ExclamationEqualsToken : ts.SyntaxKind.EqualsEqualsToken),
ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)
)
),
ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
ts.factory.createBinaryExpression(
ts.factory.createPropertyAccessExpression(
entryOptionNode,
ts.factory.createIdentifier(STORAGE)
),
ts.factory.createToken(hasStorage ? ts.SyntaxKind.ExclamationEqualsToken : ts.SyntaxKind.EqualsEqualsToken),
ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)
)
);
}
function assignRouteNameAndStorage(routeNameNode): ts.ExpressionStatement[] {
const assignOperation: ts.VariableStatement[] = [];
if (routeNameNode) {
assignOperation.push(ts.factory.createVariableStatement(
undefined,
ts.factory.createVariableDeclarationList(
[ts.factory.createVariableDeclaration(
ts.factory.createIdentifier(ROUTENAME_NODE),
undefined,
undefined,
routeNameNode
)],
ts.NodeFlags.Let
)
));
}
return assignOperation;
}
function createLoadDocumentWithRoute(context: ts.TransformationContext, name: string,
cardRelativePath: string, isObject: boolean, entryOptionNode: ts.Expression,
routeNameNode: ts.Node, storageNode: ts.Node, hasRouteName: boolean, hasStorage: boolean,
shouldCreateAccsessRecording: boolean, argsArr: ts.Expression[]): ts.ExpressionStatement[] {
const newArray: ts.Expression[] = [
context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
context.factory.createObjectLiteralExpression([], false)
];
if (entryOptionNode) {
if (!isObject) {
if (routeNameNode === null && storageNode === null) {
newArray.push(entryOptionNode);
} else {
newArray.push(createGetSharedForVariable(entryOptionNode));
}
} else if (storageNode) {
newArray.push(storageNode as ts.Expression);
}
}
return loadDocumentWithRoute(context, name, newArray, argsArr, hasRouteName, shouldCreateAccsessRecording,
isObject, entryOptionNode, cardRelativePath);
}
function loadDocumentWithRoute(context: ts.TransformationContext, name: string, newArray: ts.Expression[],
argsArr: ts.Expression[], hasRouteName: boolean, shouldCreateAccsessRecording: boolean,
isObject: boolean, entryOptionNode: ts.Expression, cardRelativePath: string): ts.ExpressionStatement[] {
const newExpressionParams = [context.factory.createNewExpression(
context.factory.createIdentifier(name), undefined, newArray)];
if (argsArr) {
argsArr = [];
componentCollection.previewComponent.forEach((componentName: string) => {
const newExpression: ts.Expression = context.factory.createNewExpression(
context.factory.createIdentifier(componentName), undefined,
componentName === name ? newArray : newArray.slice(0, newArray.length - 1)
);
argsArr.push(context.factory.createStringLiteral(componentName), newExpression);
});
}
if (hasRouteName) {
return [
shouldCreateAccsessRecording ? createStartGetAccessRecording(context) : undefined,
createRegisterNamedRoute(context, newExpressionParams, isObject, entryOptionNode, hasRouteName),
shouldCreateAccsessRecording ? createStopGetAccessRecording(context) : undefined];
} else {
if (projectConfig.minAPIVersion > APIVERSION.apiVersionTen) {
return [createRegisterNamedRoute(context, newExpressionParams, isObject, entryOptionNode, hasRouteName)];
} else {
return [shouldCreateAccsessRecording ? createStartGetAccessRecording(context) : undefined,
context.factory.createExpressionStatement(context.factory.createCallExpression(
context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME :
PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams)),
shouldCreateAccsessRecording ? createStopGetAccessRecording(context) : undefined];
}
}
}
function createRegisterNamedRoute(context: ts.TransformationContext, newExpressionParams: ts.NewExpression[],
isObject: boolean, entryOptionNode: ts.Expression, hasRouteName: boolean): ts.ExpressionStatement {
return context.factory.createExpressionStatement(context.factory.createCallExpression(
context.factory.createIdentifier(REGISTER_NAMED_ROUTE),
undefined,
[
context.factory.createArrowFunction(
undefined,
undefined,
[],
undefined,
context.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
newExpressionParams[0]
),
hasRouteName ? isObject ? ts.factory.createIdentifier(ROUTENAME_NODE) : context.factory.createPropertyAccessExpression(
entryOptionNode,
context.factory.createIdentifier(ROUTE_NAME)
) : ts.factory.createStringLiteral(''),
context.factory.createObjectLiteralExpression(
[
routerBundleOrModule(context, RESOURCE_NAME_BUNDLE),
routerBundleOrModule(context, RESOURCE_NAME_MODULE),
routerOrNavPathWrite(context, PAGE_PATH, projectConfig.projectPath, projectConfig.projectRootPath),
routerOrNavPathWrite(context, PAGE_FULL_PATH, projectConfig.projectRootPath),
context.factory.createPropertyAssignment(
context.factory.createIdentifier(INTEGRATED_HSP),
context.factory.createStringLiteral(integratedHspType())
),
routerModuleType(context)
],
false
)
]
));
}
export function createStartGetAccessRecording(context: ts.TransformationContext): ts.ExpressionStatement {
return context.factory.createExpressionStatement(
context.factory.createCallExpression(
context.factory.createPropertyAccessExpression(
context.factory.createIdentifier(VIEWSTACKPROCESSOR),
context.factory.createIdentifier(STARTGETACCESSRECORDINGFOR)
),
undefined,
[context.factory.createCallExpression(
context.factory.createPropertyAccessExpression(
context.factory.createIdentifier(VIEWSTACKPROCESSOR),
context.factory.createIdentifier(ALLOCATENEWELMETIDFORNEXTCOMPONENT)
),
undefined,
[]
)]
)
);
}
function createLoadDocument(context: ts.TransformationContext, name: string,
cardRelativePath: string, localStorageName: string, entryNodeKey: ts.Expression): ts.ExpressionStatement {
const newArray: ts.Expression[] = [
context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
context.factory.createObjectLiteralExpression([], false)
];
if (localStorageName && entryNodeKey) {
newArray.push(entryNodeKey);
}
const newExpressionParams: any[] = [
context.factory.createNewExpression(
context.factory.createIdentifier(name),
undefined, newArray)];
addCardStringliteral(newExpressionParams, context, cardRelativePath);
return context.factory.createExpressionStatement(
context.factory.createCallExpression(
context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME :
PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams)
);
}
function createStopGetAccessRecording(context: ts.TransformationContext): ts.ExpressionStatement {
return context.factory.createExpressionStatement(
context.factory.createCallExpression(
context.factory.createPropertyAccessExpression(
context.factory.createIdentifier(VIEWSTACKPROCESSOR),
context.factory.createIdentifier(STOPGETACCESSRECORDING)
),
undefined,
[]
)
);
}
function addStorageParam(name: string): [string, ts.Expression] {
let localStorageName: string;
let localStorageNode: ts.Identifier | ts.ObjectLiteralExpression;
const localStorageNum: number = (localStorageLinkCollection.get(name) || new Set()).size +
(localStoragePropCollection.get(name) || new Set()).size;
if (componentCollection.entryComponent === name) {
if (componentCollection.localStorageNode) {
localStorageNode = componentCollection.localStorageNode;
}
if (componentCollection.localStorageName) {
localStorageName = componentCollection.localStorageName;
}
if (!hasStorage() && localStorageNum) {
transformLog.errors.push({
type: LogType.WARN,
message: `'@Entry' should have a parameter, like '@Entry (storage)'.`,
pos: componentCollection.entryComponentPos
});
}
}
return [localStorageName, localStorageNode];
}
function hasStorage(): boolean {
if (componentCollection.localStorageName || componentCollection.localStorageNode ||
componentCollection.localSharedStorage) {
return true;
}
return false;
}
function createPreviewComponentFunction(name: string, context: ts.TransformationContext,
cardRelativePath: string, entryNodeKey: ts.Expression, id: number): ts.Statement {
const newArray: ts.Expression[] = partialUpdateConfig.partialUpdateMode ?
[
context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
context.factory.createObjectLiteralExpression([], false)
] :
[
context.factory.createStringLiteral(id.toString()),
context.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
context.factory.createObjectLiteralExpression([], false)
];
const [localStorageName, entryOptionNode]: [string, ts.Expression] = addStorageParam(name);
if (entryOptionNode) {
newArray.push(entryOptionNode);
}
const argsArr: ts.Expression[] = [];
componentCollection.previewComponent.forEach(componentName => {
const newExpression: ts.Expression = context.factory.createNewExpression(
context.factory.createIdentifier(componentName),
undefined,
newArray
);
argsArr.push(context.factory.createStringLiteral(componentName));
argsArr.push(newExpression);
});
const newExpressionParams: any[] = name ? [context.factory.createNewExpression(
context.factory.createIdentifier(name), undefined, newArray)] : [];
addCardStringliteral(newExpressionParams, context, cardRelativePath);
const ifStatement: ts.Statement = context.factory.createIfStatement(
context.factory.createCallExpression(
context.factory.createIdentifier(GET_PREVIEW_FLAG_FUNCTION_NAME),
undefined,
[]
),
context.factory.createBlock(
[...storePreviewComponents(name, entryOptionNode, argsArr),
context.factory.createExpressionStatement(context.factory.createCallExpression(
context.factory.createIdentifier(PREVIEW_COMPONENT_FUNCTION_NAME),
undefined,
[]
))],
true
),
context.factory.createBlock(
createPreviewElseBlock(name, context, cardRelativePath, localStorageName, entryOptionNode,
newExpressionParams, argsArr),
true
)
);
return ifStatement;
}
function storePreviewComponents(name: string, entryOptionNode: ts.Expression, argsArr: ts.Expression[]):
(ts.ExpressionStatement | ts.VariableStatement | ts.IfStatement)[] {
let isObject: boolean = false;
let storageNode: ts.Expression;
if (!entryOptionNode) {
return [ts.factory.createExpressionStatement(ts.factory.createCallExpression(
ts.factory.createIdentifier(STORE_PREVIEW_COMPONENTS),
undefined,
[
ts.factory.createNumericLiteral(componentCollection.previewComponent.length),
...argsArr
]
))];
}
if (ts.isObjectLiteralExpression(entryOptionNode)) {
isObject = true;
if (entryOptionNode.properties) {
entryOptionNode.properties.forEach((property) => {
if (ts.isPropertyAssignment(property) && property.name && ts.isIdentifier(property.name) &&
property.name.escapedText.toString() === STORAGE) {
storageNode = property.initializer;
}
});
}
} else {
isObject = false;
}
const newArray: ts.Expression[] = [
ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
ts.factory.createObjectLiteralExpression([], false)
];
const newArgsArr: ts.Expression[] = [];
if (isObject) {
return processObjectStorage(storageNode, newArray, name, newArgsArr);
} else {
return [ts.factory.createIfStatement(
ts.factory.createBinaryExpression(
entryOptionNode,
ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
ts.factory.createBinaryExpression(
ts.factory.createPropertyAccessExpression(
entryOptionNode,
ts.factory.createIdentifier(STORAGE)
),
ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsToken),
ts.factory.createIdentifier('undefined')
)
),
ts.factory.createBlock(
[returnStorePreview(entryOptionNode, true, name)],
true
),
ts.factory.createBlock(
[returnStorePreview(entryOptionNode, false, name)],
true
)
)];
}
}
function processObjectStorage(storageNode: ts.Expression, newArray: ts.Expression[], name: string,
newArgsArr: ts.Expression[]): (ts.ExpressionStatement | ts.VariableStatement)[] {
if (storageNode) {
newArray.push(ts.factory.createIdentifier(STORAGE_NODE));
componentCollection.previewComponent.forEach(componentName => {
const newExpression: ts.Expression = ts.factory.createNewExpression(
ts.factory.createIdentifier(componentName),
undefined,
componentName === name ? newArray : newArray.slice(0, newArray.length - 1)
);
newArgsArr.push(ts.factory.createStringLiteral(componentName));
newArgsArr.push(newExpression);
});
return [ts.factory.createVariableStatement(
undefined,
ts.factory.createVariableDeclarationList(
[ts.factory.createVariableDeclaration(
ts.factory.createIdentifier(STORAGE_NODE),
undefined,
undefined,
storageNode
)],
ts.NodeFlags.Let
)
), ts.factory.createExpressionStatement(ts.factory.createCallExpression(
ts.factory.createIdentifier(STORE_PREVIEW_COMPONENTS),
undefined,
[
ts.factory.createNumericLiteral(componentCollection.previewComponent.length),
...newArgsArr
]
))];
} else {
componentCollection.previewComponent.forEach(componentName => {
const newExpression: ts.Expression = ts.factory.createNewExpression(
ts.factory.createIdentifier(componentName),
undefined,
newArray
);
newArgsArr.push(ts.factory.createStringLiteral(componentName));
newArgsArr.push(newExpression);
});
return [ts.factory.createExpressionStatement(ts.factory.createCallExpression(
ts.factory.createIdentifier(STORE_PREVIEW_COMPONENTS),
undefined,
[
ts.factory.createNumericLiteral(componentCollection.previewComponent.length),
...newArgsArr
]
))];
}
}
function returnStorePreview(entryOptionNode: ts.Expression, hasStorage: boolean, name: string): ts.ExpressionStatement {
const newArray: ts.Expression[] = [
ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
ts.factory.createObjectLiteralExpression([], false)
];
const newArgsArr: ts.Expression[] = [];
newArray.push(hasStorage ? ts.factory.createPropertyAccessExpression(
entryOptionNode,
ts.factory.createIdentifier(STORAGE)
) : entryOptionNode);
componentCollection.previewComponent.forEach(componentName => {
const newExpression: ts.Expression = ts.factory.createNewExpression(
ts.factory.createIdentifier(componentName),
undefined,
componentName === name ? newArray : newArray.slice(0, newArray.length - 1)
);
newArgsArr.push(ts.factory.createStringLiteral(componentName));
newArgsArr.push(newExpression);
});
return ts.factory.createExpressionStatement(ts.factory.createCallExpression(
ts.factory.createIdentifier(STORE_PREVIEW_COMPONENTS),
undefined,
[
ts.factory.createNumericLiteral(componentCollection.previewComponent.length),
...newArgsArr
]
));
}
function createPreviewElseBlock(name: string, context: ts.TransformationContext, cardRelativePath: string,
localStorageName: string, entryOptionNode: ts.Expression, newExpressionParams: ts.Expression[],
argsArr: ts.Expression[]): (ts.ExpressionStatement | ts.IfStatement | ts.Block | ts.Statement)[] {
if (name) {
if (!partialUpdateConfig.partialUpdateMode) {
return [context.factory.createExpressionStatement(context.factory.createCallExpression(
context.factory.createIdentifier(STORE_PREVIEW_COMPONENTS),
undefined,
[
context.factory.createNumericLiteral(componentCollection.previewComponent.length),
...argsArr
]
)),
context.factory.createExpressionStatement(context.factory.createCallExpression(
context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME :
PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams
))];
} else {
if (cardRelativePath) {
if (entryOptionNode && ts.isObjectLiteralExpression(entryOptionNode)) {
transformLog.errors.push({
type: LogType.ERROR,
message: `'@Entry' doesn't support {} parameter in card`,
pos: componentCollection.entryComponentPos,
code: '10906217'
});
}
return [
name ? createStartGetAccessRecording(context) : undefined,
name ? context.factory.createExpressionStatement(context.factory.createCallExpression(
context.factory.createIdentifier(cardRelativePath ? CARD_ENTRY_FUNCTION_NAME :
PAGE_ENTRY_FUNCTION_NAME), undefined, newExpressionParams
)) : undefined,
name ? createStopGetAccessRecording(context) : undefined
];
}
return createLoadPageConditionalJudgMent(context, name, cardRelativePath, localStorageName,
entryOptionNode, argsArr, true);
}
}
return undefined;
}
export function resetLog(): void {
transformLog.errors = [];
}
function addCardStringliteral(newExpressionParams: any[], context: ts.TransformationContext,
cardRelativePath: string): void {
const bundleName = projectConfig.allowEmptyBundleName ? '' : projectConfig.bundleName;
if (cardRelativePath) {
newExpressionParams.push(context.factory.createStringLiteral(
bundleName + '/' + projectConfig.moduleName + '/' + cardRelativePath));
}
}
export function validatorCard(log: LogInfo[], type: number, pos: number,
name: string = ''): void {
if (projectConfig && projectConfig.cardObj && resourceFileName &&
projectConfig.cardObj[resourceFileName]) {
const logInfo: LogInfo = {
type: LogType.ERROR,
message: '',
pos: pos
};
switch (type) {
case CARD_LOG_TYPE_COMPONENTS:
logInfo.message = `Card page cannot use the component ${name}.`;
logInfo.code = '10905216';
break;
case CARD_LOG_TYPE_DECORATORS:
logInfo.message = `Card page cannot use ${name}`;
logInfo.code = '10905215';
break;
case CARD_LOG_TYPE_IMPORT:
logInfo.message = `Card page cannot use import.`;
logInfo.code = '10905214';
break;
}
log.push(logInfo);
}
}
export function resetProcessUiSyntax(): void {
transformLog = new createAstNodeUtils.FileLog();
contextGlobal = undefined;
}
function createSharedStorageWithRoute(context: ts.TransformationContext, name: string, cardRelativePath: string,
entryOptionNode: ts.Expression, shouldCreateAccsessRecording: boolean, argsArr: ts.Expression[]): ts.Statement[] {
const newArray: ts.Expression[] = [
ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED),
ts.factory.createObjectLiteralExpression([], false),
createGetSharedForVariable(entryOptionNode, false)
];
return loadDocumentWithRoute(context, name, newArray, argsArr, false, shouldCreateAccsessRecording,
false, entryOptionNode, cardRelativePath);
}
function insertImportModuleNode(statements: ts.Statement[], hasUseResource: boolean): ts.Statement[] {
if (isDynamic() && (hasUseResource || componentCollection.entryComponent)) {
statements.unshift(createAstNodeUtils.createImportNodeForModuleInfo());
}
return statements;
}
export function isDynamic(): boolean {
const isIntegratedHspUseReplaceBundleName: boolean = projectConfig.integratedHsp && getGetCurrentBundleName();
const isByteCodeHar: boolean = projectConfig.compileHar && projectConfig.byteCodeHar;
const uiTransformOptimization: boolean = !!projectConfig.uiTransformOptimization;
return uiTransformOptimization || (isByteCodeHar || isIntegratedHspUseReplaceBundleName);
}
export function getGetCurrentBundleName(): boolean {
if (projectConfig.useGetCurrentBundleName) {
return projectConfig.useGetCurrentBundleName;
}
return false;
}