diff --git a/compiler/src/component_map.ts b/compiler/src/component_map.ts index 6bd7a35d2db60e25138d087179260baa760f769f..1a5ec2a975df8a965208cb8347396c0afe6bf433 100644 --- a/compiler/src/component_map.ts +++ b/compiler/src/component_map.ts @@ -77,6 +77,8 @@ export interface ExtendParamterInterfance { export const EXTEND_ATTRIBUTE: Map> = new Map(); export const STYLES_ATTRIBUTE: Set = new Set(); +export const INTERFACE_NODE_SET: Set = new Set(); + export const JS_BIND_COMPONENTS: Set = new Set([ ...GESTURE_TYPE_NAMES, 'Gesture', 'PanGestureOption', 'CustomDialogController', 'Storage', 'Scroller', 'SwiperController', diff --git a/compiler/src/pre_define.ts b/compiler/src/pre_define.ts index a0a1f741c750189f39d8c620e805c0b843dfdb2c..e6f325bb3ddf2761248d712cd6d923d3648e975e 100644 --- a/compiler/src/pre_define.ts +++ b/compiler/src/pre_define.ts @@ -191,3 +191,6 @@ export const $$_VALUE: string = 'value'; export const $$_CHANGE_EVENT: string = 'changeEvent'; export const $$_THIS: string = '$$this'; export const $$_NEW_VALUE: string = 'newValue'; + +export const INTERFACE_NAME_SUFFIX:string = '_Params'; +export const OBSERVED_PROPERTY_ABSTRACT:string = 'ObservedPropertyAbstract'; diff --git a/compiler/src/process_component_class.ts b/compiler/src/process_component_class.ts index 1ac7be84bd02d9f220821ced62445995068a368c..d5a0a0b05ef50177d7aeeeaa1681f1463910d40f 100644 --- a/compiler/src/process_component_class.ts +++ b/compiler/src/process_component_class.ts @@ -16,6 +16,19 @@ import ts from 'typescript'; import { + COMPONENT_STATE_DECORATOR, + COMPONENT_PROVIDE_DECORATOR, + COMPONENT_LINK_DECORATOR, + COMPONENT_PROP_DECORATOR, + COMPONENT_STORAGE_LINK_DECORATOR, + COMPONENT_STORAGE_PROP_DECORATOR, + COMPONENT_OBJECT_LINK_DECORATOR, + COMPONENT_CONSUME_DECORATOR, + SYNCHED_PROPERTY_NESED_OBJECT, + SYNCHED_PROPERTY_SIMPLE_TWO_WAY, + SYNCHED_PROPERTY_SIMPLE_ONE_WAY, + OBSERVED_PROPERTY_OBJECT, + OBSERVED_PROPERTY_SIMPLE, COMPONENT_BUILD_FUNCTION, BASE_COMPONENT_NAME, ATTRIBUTE_ANIMATETO, @@ -37,12 +50,15 @@ import { BUILDER_ATTR_NAME, BUILDER_ATTR_BIND, COMPONENT_STYLES_DECORATOR, - STYLES + STYLES, + INTERFACE_NAME_SUFFIX, + OBSERVED_PROPERTY_ABSTRACT } from './pre_define'; import { BUILDIN_STYLE_NAMES, CUSTOM_BUILDER_METHOD, INNER_STYLE_FUNCTION, + INTERFACE_NODE_SET, STYLES_ATTRIBUTE } from './component_map'; import { @@ -59,7 +75,8 @@ import { UpdateResult, stateObjectCollection, curPropMap, - decoratorParamSet + decoratorParamSet, + isSimpleType } from './process_component_member'; import { processComponentBuild, @@ -106,11 +123,14 @@ function processMembers(members: ts.NodeArray, parentComponentN const deleteParamsStatements: ts.PropertyDeclaration[] = []; const checkController: ControllerType = { hasController: !componentCollection.customDialogs.has(parentComponentName.getText()) }; + let interfaceNode = ts.factory.createInterfaceDeclaration(undefined, undefined, + parentComponentName.getText() + INTERFACE_NAME_SUFFIX, undefined, undefined, []) members.forEach((item: ts.ClassElement) => { let updateItem: ts.ClassElement; if (ts.isPropertyDeclaration(item)) { + addPropertyMember(item, newMembers, program); const result: UpdateResult = processMemberVariableDecorators(parentComponentName, item, - ctorNode, watchMap, checkController, log, program, context, hasPreview); + ctorNode, watchMap, checkController, log, program, context, hasPreview, interfaceNode); if (result.isItemUpdate()) { updateItem = result.getProperity(); } else { @@ -143,14 +163,74 @@ function processMembers(members: ts.NodeArray, parentComponentN newMembers.push(updateItem); } }); + INTERFACE_NODE_SET.add(interfaceNode); validateBuildMethodCount(buildCount, parentComponentName, log); validateHasController(parentComponentName, checkController, log); newMembers.unshift(addDeleteParamsFunc(deleteParamsStatements)); - newMembers.unshift(addUpdateParamsFunc(updateParamsStatements)); - newMembers.unshift(addConstructor(ctorNode, watchMap)); + newMembers.unshift(addUpdateParamsFunc(updateParamsStatements, parentComponentName)); + newMembers.unshift(addConstructor(ctorNode, watchMap, parentComponentName)); return newMembers; } +function addPropertyMember(item: ts.ClassElement, newMembers: ts.ClassElement[], + program: ts.Program):void { + let propertyItem: ts.PropertyDeclaration = item as ts.PropertyDeclaration; + let decoratorName: string; + let updatePropertyItem: ts.PropertyDeclaration; + let type: ts.TypeNode = propertyItem.type; + if (!propertyItem.decorators || propertyItem.decorators.length === 0) { + updatePropertyItem = createPropertyDeclaration(propertyItem, type, true); + newMembers.push(updatePropertyItem); + } else if (propertyItem.decorators) { + for (let i = 0; i < propertyItem.decorators.length; i++) { + let newType: ts.TypeNode; + decoratorName = propertyItem.decorators[i].getText().replace(/\(.*\)$/, '').trim(); + switch (decoratorName) { + case COMPONENT_STATE_DECORATOR: + case COMPONENT_PROVIDE_DECORATOR: + newType = ts.factory.createTypeReferenceNode(isSimpleType(type, program) ? + OBSERVED_PROPERTY_SIMPLE : OBSERVED_PROPERTY_OBJECT, [type]); + break; + case COMPONENT_LINK_DECORATOR: + case COMPONENT_CONSUME_DECORATOR: + newType = ts.factory.createTypeReferenceNode(isSimpleType(type, program) ? + SYNCHED_PROPERTY_SIMPLE_TWO_WAY : SYNCHED_PROPERTY_SIMPLE_ONE_WAY, [type]); + break; + case COMPONENT_PROP_DECORATOR: + newType = ts.factory.createTypeReferenceNode(SYNCHED_PROPERTY_SIMPLE_ONE_WAY, [type]); + break; + case COMPONENT_OBJECT_LINK_DECORATOR: + newType = ts.factory.createTypeReferenceNode(SYNCHED_PROPERTY_NESED_OBJECT, [type]); + break; + case COMPONENT_STORAGE_PROP_DECORATOR: + case COMPONENT_STORAGE_LINK_DECORATOR: + newType = ts.factory.createTypeReferenceNode(OBSERVED_PROPERTY_ABSTRACT, [type]); + break; + } + updatePropertyItem = createPropertyDeclaration(propertyItem, newType, false); + if (updatePropertyItem) { + newMembers.push(updatePropertyItem); + } + } + } +} + +function createPropertyDeclaration(propertyItem: ts.PropertyDeclaration, newType: ts.TypeNode | undefined, + normalVar: boolean): ts.PropertyDeclaration { + if (typeof newType === undefined) { + return undefined; + } + let prefix: string = ''; + if (!normalVar) { + prefix = '__'; + } + const privateM: ts.ModifierToken = + ts.factory.createModifier(ts.SyntaxKind.PrivateKeyword); + return ts.factory.updatePropertyDeclaration(propertyItem, undefined, + propertyItem.modifiers || [privateM], prefix + propertyItem.name.getText(), + propertyItem.questionToken, newType, undefined); +} + function processComponentMethod(node: ts.MethodDeclaration, parentComponentName: ts.Identifier, context: ts.TransformationContext, log: LogInfo[], buildCount: BuildCount): ts.MethodDeclaration { let updateItem: ts.MethodDeclaration = node; @@ -414,8 +494,9 @@ function processAnimateTo(node: ts.CallExpression): ts.CallExpression { node.typeArguments, node.arguments); } -function addUpdateParamsFunc(statements: ts.Statement[]): ts.MethodDeclaration { - return createParamsInitBlock(COMPONENT_CONSTRUCTOR_UPDATE_PARAMS, statements); +function addUpdateParamsFunc(statements: ts.Statement[], parentComponentName: ts.Identifier): + ts.MethodDeclaration { + return createParamsInitBlock(COMPONENT_CONSTRUCTOR_UPDATE_PARAMS, statements, parentComponentName); } function addDeleteParamsFunc(statements: ts.PropertyDeclaration[]): ts.MethodDeclaration { @@ -445,13 +526,17 @@ function addDeleteParamsFunc(statements: ts.PropertyDeclaration[]): ts.MethodDec return deleteParamsMethod; } -function createParamsInitBlock(express: string, statements: ts.Statement[]): ts.MethodDeclaration { +function createParamsInitBlock(express: string, statements: ts.Statement[], + parentComponentName?: ts.Identifier): ts.MethodDeclaration { const methodDeclaration: ts.MethodDeclaration = ts.factory.createMethodDeclaration(undefined, undefined, undefined, ts.factory.createIdentifier(express), undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, undefined, express === COMPONENT_CONSTRUCTOR_DELETE_PARAMS ? undefined : - ts.factory.createIdentifier(CREATE_CONSTRUCTOR_PARAMS), undefined, undefined, undefined)], - undefined, ts.factory.createBlock(statements, true)); + ts.factory.createIdentifier(CREATE_CONSTRUCTOR_PARAMS), undefined, + express === COMPONENT_CONSTRUCTOR_DELETE_PARAMS ? undefined : + ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier(parentComponentName.getText() + INTERFACE_NAME_SUFFIX), undefined), + undefined)], undefined, ts.factory.createBlock(statements, true)); return methodDeclaration; } diff --git a/compiler/src/process_component_constructor.ts b/compiler/src/process_component_constructor.ts index fa619f07aa524e3990840075a21f738f4732a5df..79e247232feb2fc0a07b323bd5dd596b3a05ef49 100644 --- a/compiler/src/process_component_constructor.ts +++ b/compiler/src/process_component_constructor.ts @@ -20,7 +20,9 @@ import { COMPONENT_CONSTRUCTOR_PARENT, COMPONENT_CONSTRUCTOR_PARAMS, COMPONENT_CONSTRUCTOR_UPDATE_PARAMS, - COMPONENT_WATCH_FUNCTION + COMPONENT_WATCH_FUNCTION, + BASE_COMPONENT_NAME, + INTERFACE_NAME_SUFFIX } from './pre_define'; export function getInitConstructor(members: ts.NodeArray): ts.ConstructorDeclaration { @@ -35,7 +37,8 @@ export function getInitConstructor(members: ts.NodeArray): ts.Construct export function updateConstructor(ctorNode: ts.ConstructorDeclaration, para: ts.ParameterDeclaration[], addStatements: ts.Statement[], - isSuper: boolean = false): ts.ConstructorDeclaration { + isSuper: boolean = false, isAdd: boolean = false, parentComponentName?: ts.Identifier): + ts.ConstructorDeclaration { let modifyPara: ts.ParameterDeclaration[]; if (para && para.length) { modifyPara = Array.from(ctorNode.parameters); @@ -55,15 +58,50 @@ export function updateConstructor(ctorNode: ts.ConstructorDeclaration, } } if (ctorNode) { + let ctorPara: ts.ParameterDeclaration[] | ts.NodeArray = + modifyPara || ctorNode.parameters; + if (isAdd) { + ctorPara = addParamsType(ctorNode, modifyPara, parentComponentName); + } ctorNode = ts.factory.updateConstructorDeclaration(ctorNode, ctorNode.decorators, - ctorNode.modifiers, modifyPara || ctorNode.parameters, - ts.factory.createBlock(modifyBody || ctorNode.body.statements, true)); + ctorNode.modifiers, ctorPara, ts.factory.createBlock(modifyBody || ctorNode.body.statements, true)); } return ctorNode; } -export function addConstructor(ctorNode: any, watchMap: Map) - : ts.ConstructorDeclaration { +function addParamsType(ctorNode: ts.ConstructorDeclaration, modifyPara: ts.ParameterDeclaration[], + parentComponentName: ts.Identifier): ts.ParameterDeclaration[] { + const tsPara: ts.ParameterDeclaration[] | ts.NodeArray = + modifyPara || ctorNode.parameters; + const newTSPara: ts.ParameterDeclaration[] = []; + tsPara.forEach((item) => { + let parameter: ts.ParameterDeclaration = item; + switch (item.getText()) { + case COMPONENT_CONSTRUCTOR_ID + '?': + parameter = ts.factory.updateParameterDeclaration(item, item.decorators, item.modifiers, + item.dotDotDotToken, item.name, item.questionToken, + ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), item.initializer); + break; + case COMPONENT_CONSTRUCTOR_PARENT + '?': + parameter = ts.factory.createParameterDeclaration(item.decorators, item.modifiers, + item.dotDotDotToken, item.name, item.questionToken, + ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(BASE_COMPONENT_NAME), undefined), + item.initializer); + break; + case COMPONENT_CONSTRUCTOR_PARAMS + '?': + parameter = ts.factory.updateParameterDeclaration(item, item.decorators, item.modifiers, + item.dotDotDotToken, item.name, item.questionToken, + ts.factory.createTypeReferenceNode(ts.factory.createIdentifier + (parentComponentName.getText() + INTERFACE_NAME_SUFFIX), undefined), item.initializer); + break; + } + newTSPara.push(parameter); + }) + return newTSPara; +} + +export function addConstructor(ctorNode: any, watchMap: Map, + parentComponentName: ts.Identifier): ts.ConstructorDeclaration { const watchStatements: ts.ExpressionStatement[] = []; watchMap.forEach((value, key) => { const watchNode: ts.ExpressionStatement = ts.factory.createExpressionStatement( @@ -91,5 +129,5 @@ export function addConstructor(ctorNode: any, watchMap: Map) ts.factory.createThis(), ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UPDATE_PARAMS)), undefined, [ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_PARAMS)])); return updateConstructor(updateConstructor(ctorNode, [], [callSuperStatement], true), [], - [updateWithValueParamsStatement, ...watchStatements], false); + [updateWithValueParamsStatement, ...watchStatements], false, true, parentComponentName); } diff --git a/compiler/src/process_component_member.ts b/compiler/src/process_component_member.ts index 4799fe32de464adf07ea3bd0b30c6dcad2c654f9..1d72b7acb355b921d1a63c86956d35572d845dae 100644 --- a/compiler/src/process_component_member.ts +++ b/compiler/src/process_component_member.ts @@ -192,8 +192,8 @@ export const curPropMap: Map = new Map(); export function processMemberVariableDecorators(parentName: ts.Identifier, item: ts.PropertyDeclaration, ctorNode: ts.ConstructorDeclaration, watchMap: Map, - checkController: ControllerType, log: LogInfo[], program: ts.Program, - context: ts.TransformationContext, hasPreview: boolean): UpdateResult { + checkController: ControllerType, log: LogInfo[], program: ts.Program, context: ts.TransformationContext, + hasPreview: boolean, interfaceNode: ts.InterfaceDeclaration): UpdateResult { const updateResult: UpdateResult = new UpdateResult(); const name: ts.Identifier = item.name as ts.Identifier; if (!item.decorators || !item.decorators.length) { @@ -201,14 +201,15 @@ export function processMemberVariableDecorators(parentName: ts.Identifier, updateResult.setProperity(undefined); updateResult.setUpdateParams(createUpdateParams(name, COMPONENT_NON_DECORATOR)); updateResult.setCtor(updateConstructor(ctorNode, [], [ - createVariableInitStatement(item, COMPONENT_NON_DECORATOR, log, program, context, hasPreview)])); + createVariableInitStatement(item, COMPONENT_NON_DECORATOR, log, program, context, hasPreview, + interfaceNode)])); updateResult.setControllerSet(createControllerSet(item, parentName, name, checkController)); } else if (!item.type) { validatePropertyNonType(name, log); return updateResult; } else { processPropertyNodeDecorator(parentName, item, updateResult, ctorNode, name, watchMap, - log, program, context, hasPreview); + log, program, context, hasPreview, interfaceNode); } return updateResult; } @@ -234,7 +235,8 @@ function createControllerSet(node: ts.PropertyDeclaration, componentName: ts.Ide function processPropertyNodeDecorator(parentName: ts.Identifier, node: ts.PropertyDeclaration, updateResult: UpdateResult, ctorNode: ts.ConstructorDeclaration, name: ts.Identifier, watchMap: Map, log: LogInfo[], program: ts.Program, - context: ts.TransformationContext, hasPreview: boolean): void { + context: ts.TransformationContext, hasPreview: boolean, interfaceNode: ts.InterfaceDeclaration): + void { let stateManagementDecoratorCount: number = 0; for (let i = 0; i < node.decorators.length; i++) { const decoratorName: string = node.decorators[i].getText().replace(/\(.*\)$/, '').trim(); @@ -271,7 +273,8 @@ function processPropertyNodeDecorator(parentName: ts.Identifier, node: ts.Proper processWatch(node, node.decorators[i], watchMap, log); } else if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) { stateManagementDecoratorCount += 1; - processStateDecorators(node, decoratorName, updateResult, ctorNode, log, program, context, hasPreview); + processStateDecorators(node, decoratorName, updateResult, ctorNode, log, program, context, + hasPreview, interfaceNode); } } if (stateManagementDecoratorCount > 1) { @@ -282,12 +285,13 @@ function processPropertyNodeDecorator(parentName: ts.Identifier, node: ts.Proper function processStateDecorators(node: ts.PropertyDeclaration, decorator: string, updateResult: UpdateResult, ctorNode: ts.ConstructorDeclaration, log: LogInfo[], - program: ts.Program, context: ts.TransformationContext, hasPreview:boolean): void { + program: ts.Program, context: ts.TransformationContext, hasPreview:boolean, + interfaceNode: ts.InterfaceDeclaration): void { const name: ts.Identifier = node.name as ts.Identifier; updateResult.setProperity(undefined); const updateState: ts.Statement[] = []; const variableInitStatement: ts.Statement = - createVariableInitStatement(node, decorator, log, program, context, hasPreview); + createVariableInitStatement(node, decorator, log, program, context, hasPreview, interfaceNode); if (variableInitStatement) { updateState.push(variableInitStatement); } @@ -295,7 +299,7 @@ function processStateDecorators(node: ts.PropertyDeclaration, decorator: string, updateResult.setCtor(updateConstructor(ctorNode, [], [...updateState], false)); updateResult.setVariableGet(createGetAccessor(name, CREATE_GET_METHOD)); if (!immutableDecorators.has(decorator)) { - updateResult.setVariableSet(createSetAccessor(name, CREATE_SET_METHOD)); + updateResult.setVariableSet(createSetAccessor(name, CREATE_SET_METHOD, node.type)); } if (setUpdateParamsDecorators.has(decorator)) { updateResult.setUpdateParams(createUpdateParams(name, decorator)); @@ -338,8 +342,8 @@ function processWatch(node: ts.PropertyDeclaration, decorator: ts.Decorator, } function createVariableInitStatement(node: ts.PropertyDeclaration, decorator: string, - log: LogInfo[], program: ts.Program, context: ts.TransformationContext, hasPreview: boolean): - ts.Statement { + log: LogInfo[], program: ts.Program, context: ts.TransformationContext, hasPreview: boolean, + interfaceNode: ts.InterfaceDeclaration): ts.Statement { const name: ts.Identifier = node.name as ts.Identifier; let type: ts.TypeNode; let updateState: ts.ExpressionStatement; @@ -376,6 +380,12 @@ function createVariableInitStatement(node: ts.PropertyDeclaration, decorator: st updateState = updateConsumeProperty(node, name); break; } + const members = interfaceNode.members; + members.push(ts.factory.createPropertySignature(undefined, name, + ts.factory.createToken(ts.SyntaxKind.QuestionToken), type)); + interfaceNode = ts.factory.updateInterfaceDeclaration(interfaceNode, undefined, + interfaceNode.modifiers, interfaceNode.name, interfaceNode.typeParameters, + interfaceNode.heritageClauses, members); return updateState; } @@ -689,11 +699,12 @@ function createGetAccessor(item: ts.Identifier, express: string): ts.GetAccessor return getAccessorStatement; } -function createSetAccessor(item: ts.Identifier, express: string): ts.SetAccessorDeclaration { +function createSetAccessor(item: ts.Identifier, express: string, type: ts.TypeNode): + ts.SetAccessorDeclaration { const setAccessorStatement: ts.SetAccessorDeclaration = ts.factory.createSetAccessorDeclaration(undefined, undefined, item, [ts.factory.createParameterDeclaration(undefined, undefined, undefined, - ts.factory.createIdentifier(CREATE_NEWVALUE_IDENTIFIER), undefined, undefined, + ts.factory.createIdentifier(CREATE_NEWVALUE_IDENTIFIER), undefined, type, undefined)], ts.factory.createBlock([ts.factory.createExpressionStatement( ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression( createPropertyAccessExpressionWithThis(`__${item.getText()}`), @@ -710,7 +721,7 @@ function isForbiddenUseStateType(typeNode: ts.TypeNode): boolean { return false; } -function isSimpleType(typeNode: ts.TypeNode, program: ts.Program): boolean { +export function isSimpleType(typeNode: ts.TypeNode, program: ts.Program): boolean { let checker: ts.TypeChecker; if (program) { checker = program.getTypeChecker(); diff --git a/compiler/src/process_ui_syntax.ts b/compiler/src/process_ui_syntax.ts index a4628675cd769a196744c0d4ba46037d86ae7520..ed4d651abc4314ce6d6d0588b9a2b397dd1d645a 100644 --- a/compiler/src/process_ui_syntax.ts +++ b/compiler/src/process_ui_syntax.ts @@ -56,7 +56,8 @@ import { EXTEND_ATTRIBUTE, JS_BIND_COMPONENTS, INNER_STYLE_FUNCTION, - GLOBAL_STYLE_FUNCTION + GLOBAL_STYLE_FUNCTION, + INTERFACE_NODE_SET } from './component_map'; import { resources } from '../main'; @@ -81,6 +82,15 @@ export function processUISyntax(program: ts.Program, ut = false): Function { BUILDIN_STYLE_NAMES.delete(styleName); }) GLOBAL_STYLE_FUNCTION.clear(); + const statements: ts.NodeArray = node.statements; + INTERFACE_NODE_SET.forEach(item => { + statements.unshift(item); + }); + node = ts.factory.updateSourceFile(node, statements); + INTERFACE_NODE_SET.clear(); + const printer = ts.createPrinter({newLine: ts.NewLineKind.LineFeed}) + const result = printer.printNode(ts.EmitHint.Unspecified, node, node) + console.log(result) return node; } else { return node;