diff --git a/BUILD.gn b/BUILD.gn index 6d18dff1..7b33b93b 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -33,6 +33,7 @@ action("gen_snapshot") { script = "//third_party/jsframework/js_framework_build.sh" js_framework = "//third_party/jsframework/runtime" + js_framework_mock_generate = "//third_party/jsframework/mock-generate" is_mac = "false" use_mac = host_os == "mac" @@ -83,6 +84,7 @@ action("gen_snapshot") { rebase_path("//prebuilts", root_build_dir), rebase_path(buildfile_native_min, root_build_dir), rebase_path(css_what, root_build_dir), + rebase_path(js_framework_mock_generate, root_build_dir), ] inputs = [ diff --git a/js_framework_build.sh b/js_framework_build.sh index e624f36c..cb2ffd11 100755 --- a/js_framework_build.sh +++ b/js_framework_build.sh @@ -21,8 +21,10 @@ prebuilts_path=${12} # copy runtime to target out, and runtime/css-what is solt link, copy it always follow symbolic links in SOURCE if [ "${11}" == 'true' ];then cp -R -L $3 $9 + cp -R ${15} $9 else cp -r -L $3 $9 + cp -r ${15} $9 fi # $2 => node $4 => node_modules @@ -55,12 +57,14 @@ if [ -d "$prebuilts_path" ]; then cp -r $2 $9 cd $9 if [ "${11}" == 'true' ];then + ./node-v12.18.4-darwin-x64/bin/node ./mock-generate/build.js ./node-v12.18.4-darwin-x64/bin/node build_jsmock_system_plugin.js || exit 1 & ./node-v12.18.4-darwin-x64/bin/node build_strip_native_min.js || exit 1 & # run unit test ./node-v12.18.4-darwin-x64/bin/node node_modules/.bin/mocha -r ts-node/register test/lib.ts test/ut/**/*.ts test/ut/*.ts || exit 1 & wait else + ./node-v12.18.4-linux-x64/bin/node ./mock-generate/build.js ./node-v12.18.4-linux-x64/bin/node build_jsmock_system_plugin.js || exit 1 & ./node-v12.18.4-linux-x64/bin/node build_strip_native_min.js || exit 1 & # run unit test @@ -88,3 +92,4 @@ rm -rf ./test rm -rf ./.eslintrc rm -rf ./.babelrc rm -rf ./package.json +rm -rf ./mock-generate diff --git a/mock-generate/.eslintrc b/mock-generate/.eslintrc new file mode 100644 index 00000000..e5056f61 --- /dev/null +++ b/mock-generate/.eslintrc @@ -0,0 +1,102 @@ +{ + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + + "env": { + "es6": true, + "node": true, + "mocha": true + }, + + "globals": { + "ace": false, + "aceConsole": false, + "aceapp": false, + "markupState": false, + "notifyTrimMemory": false, + "i18nPluralRules": false, + "compileAndRunBundle": false, + "language": false + }, + + "extends": "eslint:recommended", + + "parser": "@typescript-eslint/parser", + + "rules": { + "camelcase": "off", + "accessor-pairs": 2, + "arrow-spacing": 2, + "block-spacing": 2, + "brace-style": 2, + "comma-dangle": 2, + "comma-spacing": 2, + "comma-style": 2, + "curly": 2, + "dot-location": [2, "property"], + "eol-last": 2, + "eqeqeq": 2, + "indent": [2, 2, { "SwitchCase": 1 }], + "key-spacing": 2, + "keyword-spacing": 2, + "new-cap": 2, + "new-parens": 2, + "no-array-constructor": 2, + "no-caller": 2, + "no-eval": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-extra-parens": 2, + "no-floating-decimal": 2, + "no-implied-eval": 2, + "no-iterator": 2, + "no-label-var": 2, + "no-labels": 2, + "no-lone-blocks": 2, + "no-multi-spaces": 2, + "no-multi-str": 2, + "no-multiple-empty-lines": [2, { "max": 1 }], + "no-new-object": 2, + "no-new-wrappers": 2, + "no-octal-escape": 2, + "no-proto": 2, + "no-return-assign": 2, + "no-self-compare": 2, + "no-sequences": 2, + "func-call-spacing": 2, + "no-throw-literal": 2, + "no-trailing-spaces": 2, + "no-undef-init": "off", + "no-unmodified-loop-condition": 2, + "no-unneeded-ternary": [2, { "defaultAssignment": false }], + "no-unused-vars": "off", + "no-useless-computed-key": 2, + "no-useless-constructor": "off", + "no-whitespace-before-property": 2, + "one-var": [2, { "initialized": "never" }], + "padded-blocks": [2, "never"], + "quotes": [2, "single", {"avoidEscape": true, "allowTemplateLiterals": true}], + "semi": 2, + "semi-spacing": 2, + "space-before-blocks": 2, + "space-before-function-paren": [2, "never"], + "space-in-parens": 2, + "space-infix-ops": 2, + "space-unary-ops": [2, { "words": true, "nonwords": false }], + "spaced-comment": 2, + "template-curly-spacing": 2, + "wrap-iife": [2, "any"], + "no-var": 2, + "prefer-const": 2, + "array-bracket-spacing": 2, + "no-useless-escape": 0 + }, + + "settings": { + "flowtype": { + "onlyFilesWithFlowAnnotation": true + } + } +} diff --git a/mock-generate/build.js b/mock-generate/build.js new file mode 100644 index 00000000..cfcbbc53 --- /dev/null +++ b/mock-generate/build.js @@ -0,0 +1,22 @@ +const fs = require('fs'); +const { spawnSync } = require('child_process'); +const path = require('path'); +const os = require('os'); +function compileMock() { + const mockJsPath = path.join(__dirname, '..', './runtime/main/extend/systemplugin'); + let nodeDir = ''; + if (os.platform() === 'linux') { + nodeDir = './node-v12.18.4-linux-x64/bin/node'; + } else { + nodeDir = './node-v12.18.4-darwin-x64/bin/node'; + } + + const bat = spawnSync(`${path.join(__dirname, '..', nodeDir)} ${path.join(__dirname, '..','./node_modules/typescript/bin/tsc')} && + ${path.join(__dirname, '..', nodeDir)} ${path.join(__dirname, 'dist')}/main.js && + ${path.join(__dirname, '..', nodeDir)} ${path.join(__dirname, '..', './node_modules/eslint/bin/eslint.js')} -c .eslintrc --fix ${mockJsPath}/**/*.js`, { + cwd: __dirname, + shell: true + }); +} + +compileMock(); diff --git a/mock-generate/package.json b/mock-generate/package.json new file mode 100644 index 00000000..25a24faa --- /dev/null +++ b/mock-generate/package.json @@ -0,0 +1,19 @@ +{ + "name": "mock-generate", + "version": "1.0.0", + "description": "generate mock", + "main": "main.js", + "scripts": { + "build": "npm install && node ../node_modules/typescript/bin/tsc && cd dist && node main.js && cd .. && eslint -c .eslintrc --fix ../runtime/**/systemplugin/**/*.js" + }, + "keywords": [ + "mock" + ], + "license": "Apache 2.0", + "devDependencies": { + "@types/node": "^14.18.21", + "typescript": "4.1.3", + "eslint": "^7.14.0", + "@typescript-eslint/parser": "^4.8.2" + } +} diff --git a/mock-generate/src/common/commonUtils.ts b/mock-generate/src/common/commonUtils.ts new file mode 100644 index 00000000..b764a273 --- /dev/null +++ b/mock-generate/src/common/commonUtils.ts @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2022 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 path from 'path'; +import { + CallSignatureDeclaration, ComputedPropertyName, FunctionDeclaration, Identifier, isClassDeclaration, + isComputedPropertyName, isIdentifier, isModuleBlock, isModuleDeclaration, isPrivateIdentifier, MethodDeclaration, + MethodSignature, ModifiersArray, ModuleDeclaration, NodeArray, ParameterDeclaration, PropertyName, SourceFile +} from 'typescript'; + +const allLegalImports = new Set(); +const fileNameList = new Set(); +const allClassSet = new Set(); + +/** + * get all legal imports + * @returns + */ +export function getAllLegalImports(): Set { + return allLegalImports; +} + +/** + * get all legal imports + * @param element + */ +export function collectAllLegalImports(element: string) { + allLegalImports.add(element); +} + +/** + * collect all mock js file path + * @returns + */ +export function getAllFileNameList(): Set { + return fileNameList; +} + +/** + * collect all file name + */ +export function collectAllFileName(filePath: string) { + const fileName = path.basename(filePath).split('.d.ts')[0]; + let outputFileName = ''; + if (fileName.includes('@')) { + outputFileName = fileName.split('@')[1].replace(/\./g, '_'); + } else { + outputFileName = fileName; + } + fileNameList.add(outputFileName); +} + +/** + * get all class name set + * @returns + */ +export function getClassNameSet(): Set { + return allClassSet; +} + +/** + * get all class declaration + * @param sourceFile + * @returns + */ +export function getAllClassDeclaration(sourceFile: SourceFile): Set { + sourceFile.forEachChild(node => { + if (isClassDeclaration(node)) { + if (node.name !== undefined) { + allClassSet.add(node.name.escapedText.toString()); + } + } else if (isModuleDeclaration(node)) { + const moduleDeclaration = node as ModuleDeclaration; + const moduleBody = moduleDeclaration.body; + if (moduleBody !== undefined && isModuleBlock(moduleBody)) { + moduleBody.statements.forEach(value => { + if (isClassDeclaration(value)) { + if (value.name !== undefined) { + allClassSet.add(firstCharacterToUppercase(value.name?.escapedText.toString())); + } + } + }); + } + } + }); + return allClassSet; +} + +/** + * get keywords + * @param modifiers + * @returns + */ +export function getModifiers(modifiers: ModifiersArray): Array { + const modifiersArray: Array = []; + modifiers.forEach(value => modifiersArray.push(value.kind)); + return modifiersArray; +} + +/** + * get property name + * @param node property node + * @param sourceFile + * @returns + */ +export function getPropertyName(node: PropertyName, sourceFile: SourceFile): string { + let propertyName = ''; + if (isIdentifier(node) || isPrivateIdentifier(node)) { + const newNameNode = node as Identifier; + propertyName = newNameNode.escapedText.toString(); + } else if (isComputedPropertyName(node)) { + const newNameNode = node as ComputedPropertyName; + propertyName = sourceFile.text.substring(newNameNode.expression.pos, newNameNode.expression.end).trimStart().trimEnd(); + } else { + propertyName = sourceFile.text.substring(node.pos, node.end).trimStart().trimEnd(); + } + return propertyName; +} + +/** + * get parameter declaration + * @param parameter + * @param sourceFile + * @returns + */ +export function getParameter(parameter: ParameterDeclaration, sourceFile: SourceFile): ParameterEntity { + let paramName = ''; + let paramTypeString = ''; + const paramTypeKind = parameter.type?.kind === undefined ? -1 : parameter.type.kind; + if (isIdentifier(parameter.name)) { + paramName = parameter.name.escapedText === undefined ? '' : parameter.name.escapedText.toString(); + } else { + const start = parameter.name.pos === undefined ? 0 : parameter.name.pos; + const end = parameter.name.end === undefined ? 0 : parameter.name.end; + paramName = sourceFile.text.substring(start, end).trimStart().trimEnd(); + } + + const start = parameter.type?.pos === undefined ? 0 : parameter.type.pos; + const end = parameter.type?.end === undefined ? 0 : parameter.type.end; + paramTypeString = sourceFile.text.substring(start, end).trimStart().trimEnd(); + return { + paramName: paramName, + paramTypeString: paramTypeString, + paramTypeKind: paramTypeKind + }; +} + +/** + * get method or function return info + * @param node + * @param sourceFile + * @returns + */ +export function getFunctionAndMethodReturnInfo(node: FunctionDeclaration | MethodDeclaration | + MethodSignature | CallSignatureDeclaration, sourceFile: SourceFile): ReturnTypeEntity { + const returnInfo = { returnKindName: '', returnKind: -1 }; + if (node.type !== undefined) { + const start = node.type.pos === undefined ? 0 : node.type.pos; + const end = node.type.end === undefined ? 0 : node.type.end; + returnInfo.returnKindName = sourceFile.text.substring(start, end).trimStart().trimEnd(); + returnInfo.returnKind = node.type.kind; + } + return returnInfo; +} + +/** + * get export modifiers + * @param modifiers + * @returns + */ +export function getExportKeyword(modifiers: ModifiersArray): Array { + const modifiersArray: Array = []; + modifiers.forEach(value => { + modifiersArray.push(value.kind); + }); + return modifiersArray; +} + +/** + * + * @param str first letter capitalization + * @returns + */ +export function firstCharacterToUppercase(str: string): string { + return str.slice(0, 1).toUpperCase() + str.slice(1); +} + +/** + * parameters entity + */ +export interface ParameterEntity { + paramName: string, + paramTypeString: string, + paramTypeKind: number +} + +/** + * return type entity + */ +export interface ReturnTypeEntity { + returnKindName: string, + returnKind: number +} diff --git a/mock-generate/src/declaration-node/callSignatureDeclaration.ts b/mock-generate/src/declaration-node/callSignatureDeclaration.ts new file mode 100644 index 00000000..885032f2 --- /dev/null +++ b/mock-generate/src/declaration-node/callSignatureDeclaration.ts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 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 { CallSignatureDeclaration, Node, SourceFile } from 'typescript'; +import { + getFunctionAndMethodReturnInfo, getParameter, getPropertyName, + ParameterEntity, ReturnTypeEntity +} from '../common/commonUtils'; + +/** + * get nameless function info + * @param node + * @param sourceFile + * @returns + */ +export function getCallSignatureDeclaration(node: Node, sourceFile: SourceFile): CallSignatureEntity { + const callSignatureNode = node as CallSignatureDeclaration; + let functionName = ''; + const args: Array = []; + const returnType = getFunctionAndMethodReturnInfo(callSignatureNode, sourceFile); + if (callSignatureNode.name !== undefined) { + functionName = getPropertyName(callSignatureNode.name, sourceFile); + } + + callSignatureNode.parameters.forEach(value => { + args.push(getParameter(value, sourceFile)); + }); + + return { + functionName: functionName, + returnType: returnType, + args: args + }; +} + +export interface CallSignatureEntity { + functionName: string, + returnType: ReturnTypeEntity, + args: Array +} diff --git a/mock-generate/src/declaration-node/classDeclaration.ts b/mock-generate/src/declaration-node/classDeclaration.ts new file mode 100644 index 00000000..8f1592bc --- /dev/null +++ b/mock-generate/src/declaration-node/classDeclaration.ts @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2022 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 { + ClassDeclaration, isConstructorDeclaration, + isMethodDeclaration, isPropertyDeclaration, + isTypeParameterDeclaration, SourceFile, SyntaxKind +} from 'typescript'; +import { getExportKeyword } from '../common/commonUtils'; +import { ConstructorEntity, getConstructorDeclaration } from './constructorDeclaration'; +import { getHeritageClauseDeclaration, HeritageClauseEntity } from './heritageClauseDeclaration'; +import { getMethodDeclaration, MethodEntity, StaticMethodEntity } from './methodDeclaration'; +import { getPropertyDeclaration, PropertyEntity } from './propertyDeclaration'; +import { getTypeParameterDeclaration, TypeParameterEntity } from './typeParameterDeclaration'; + +/** + * get class info + * @param classNode + * @param sourceFile + * @returns + */ +export function getClassDeclaration(classNode: ClassDeclaration, sourceFile: SourceFile): ClassEntity { + let exportModifiers: Array = []; + if (classNode.modifiers !== undefined) { + exportModifiers = getExportKeyword(classNode.modifiers); + } + + const className = classNode.name === undefined ? '' : classNode.name.escapedText.toString(); + const heritageClauses: Array = []; + const classConstructor: Array> = []; + const classMethod: Map> = new Map>(); + const classProperty: Array = []; + const typeParameters: Array = []; + const staticMethods: Array = []; + + if (classNode.heritageClauses !== undefined) { + classNode.heritageClauses.forEach(value => { + heritageClauses.push(getHeritageClauseDeclaration(value, sourceFile)); + }); + } + + classNode.members.forEach(value => { + if (isMethodDeclaration(value)) { + const methodEntity = getMethodDeclaration(value, sourceFile); + if (methodEntity.modifiers.includes(SyntaxKind.StaticKeyword)) { + staticMethods.push({ className: className, methodEntity: methodEntity }); + } else { + if (classMethod.get(methodEntity.functionName.name) !== undefined) { + classMethod.get(methodEntity.functionName.name)?.push(methodEntity); + } else { + const methodArray: Array = []; + methodArray.push(methodEntity); + classMethod.set(methodEntity.functionName.name, methodArray); + } + } + } else if (isPropertyDeclaration(value)) { + classProperty.push(getPropertyDeclaration(value, sourceFile)); + } else if (isConstructorDeclaration(value)) { + classConstructor.push(getConstructorDeclaration(value, sourceFile)); + } else if (isTypeParameterDeclaration(value)) { + typeParameters.push(getTypeParameterDeclaration(value, sourceFile)); + } else { + console.log('--------------------------- uncaught class type start -----------------------'); + console.log('className: ' + className); + console.log(value); + console.log('--------------------------- uncaught class type end -----------------------'); + } + }); + + return { + className: className, + typeParameters: typeParameters, + heritageClauses: heritageClauses, + classConstructor: classConstructor, + classMethod: classMethod, + classProperty: classProperty, + exportModifiers: exportModifiers, + staticMethods: staticMethods + }; +} + +export interface ClassEntity { + className: string, + typeParameters: Array, + heritageClauses: Array, + classConstructor: Array>, + classMethod: Map>, + classProperty: Array, + exportModifiers: Array, + staticMethods: Array +} diff --git a/mock-generate/src/declaration-node/constructorDeclaration.ts b/mock-generate/src/declaration-node/constructorDeclaration.ts new file mode 100644 index 00000000..ee544768 --- /dev/null +++ b/mock-generate/src/declaration-node/constructorDeclaration.ts @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022 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 { ConstructorDeclaration, isIdentifier, Node, SourceFile } from 'typescript'; + +/** + * get constructors info + * @param node + * @param sourceFile + * @returns + */ +export function getConstructorDeclaration(node: Node, sourceFile: SourceFile): Array { + const constructorNode = node as ConstructorDeclaration; + const constructors: Array = []; + constructorNode.parameters.forEach(value => { + const paramElement = value.name; + let name = ''; + let typeName = ''; + let typeKind = -1; + if (isIdentifier(paramElement)) { + name = paramElement.escapedText.toString(); + } else { + name = sourceFile.text.substring(paramElement.pos, paramElement.end).trimStart().trimEnd(); + } + + const paramTypeElement = value.type; + if (paramTypeElement !== undefined) { + typeName = sourceFile.text.substring(paramTypeElement.pos, paramTypeElement.end).trimStart().trimEnd(); + typeKind = paramTypeElement.kind; + } + + constructors.push( + { + name: name, + typeName: typeName, + typeKind: typeKind + } + ); + }); + return constructors; +} + +export interface ConstructorEntity { + name: string, + typeName: string, + typeKind: number +} diff --git a/mock-generate/src/declaration-node/enumDeclaration.ts b/mock-generate/src/declaration-node/enumDeclaration.ts new file mode 100644 index 00000000..abac4762 --- /dev/null +++ b/mock-generate/src/declaration-node/enumDeclaration.ts @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 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 { EnumDeclaration, SourceFile } from 'typescript'; +import { getExportKeyword, getPropertyName } from '../common/commonUtils'; + +/** + * get enum info + * @param node + * @param sourceFile + * @returns + */ +export function getEnumDeclaration(node: EnumDeclaration, sourceFile: SourceFile): EnumEntity { + const enumName = node.name.escapedText.toString(); + const enumMembers: Array = []; + let exportModifiers: Array = []; + + if (node.modifiers !== undefined) { + exportModifiers = getExportKeyword(node.modifiers); + } + + node.members.forEach(value => { + const enumValueName = getPropertyName(value.name, sourceFile); + let enumValue = ''; + if (value.initializer !== undefined) { + enumValue = sourceFile.text.substring(value.initializer.pos, value.initializer.end).trimEnd().trimStart(); + } + const enumKind = value.initializer?.kind === undefined ? -1 : value.initializer?.kind; + enumMembers.push({ enumValueName: enumValueName, enumValue: enumValue, enumKind: enumKind }); + }); + + return { + enumName: enumName, + enumMembers: enumMembers, + exportModifiers: exportModifiers + }; +} + +export interface EnumEntity { + enumName: string, + enumMembers: Array, + exportModifiers: Array +} + +export interface MemberEntity { + enumValueName: string, + enumValue: string, + enumKind: number, +} diff --git a/mock-generate/src/declaration-node/functionDeclaration.ts b/mock-generate/src/declaration-node/functionDeclaration.ts new file mode 100644 index 00000000..990fbb7e --- /dev/null +++ b/mock-generate/src/declaration-node/functionDeclaration.ts @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 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 { FunctionDeclaration, Node, SourceFile } from 'typescript'; +import { getFunctionAndMethodReturnInfo, getParameter, ParameterEntity, ReturnTypeEntity } from '../common/commonUtils'; + +/** + * get function info + * @param node + * @param sourceFile + * @returns + */ +export function getFunctionDeclaration(node: Node, sourceFile: SourceFile): FunctionEntity { + const funcNode = node as FunctionDeclaration; + let functionName = ''; + const args: Array = []; + const returnType = getFunctionAndMethodReturnInfo(funcNode, sourceFile); + functionName = funcNode.name?.escapedText === undefined ? 'undefind' : funcNode.name.escapedText.toString(); + funcNode.parameters.forEach(value => { + args.push(getParameter(value, sourceFile)); + }); + + return { + functionName: functionName, + returnType: returnType, + args: args + }; +} + +export interface FunctionEntity { + functionName: string, + returnType: ReturnTypeEntity, + args: Array +} diff --git a/mock-generate/src/declaration-node/heritageClauseDeclaration.ts b/mock-generate/src/declaration-node/heritageClauseDeclaration.ts new file mode 100644 index 00000000..39b6f2da --- /dev/null +++ b/mock-generate/src/declaration-node/heritageClauseDeclaration.ts @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 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 { HeritageClause, Node, SourceFile, SyntaxKind } from 'typescript'; + +/** + * get heritage info + * @param node + * @param sourceFile + * @returns + */ +export function getHeritageClauseDeclaration(node: Node, sourceFile: SourceFile): HeritageClauseEntity { + const HeritageClauseNode = node as HeritageClause; + const clauseToken = HeritageClauseNode.token === SyntaxKind.ExtendsKeyword ? 'extends' : 'implements'; + const types: Array = []; + + HeritageClauseNode.types.forEach(value => { + types.push(sourceFile.text.substring(value.pos, value.end).trimStart().trimEnd()); + }); + + return { + clauseToken: clauseToken, + types: types + }; +} + +export interface HeritageClauseEntity { + clauseToken: string, + types: Array +} diff --git a/mock-generate/src/declaration-node/importAndExportDeclaration.ts b/mock-generate/src/declaration-node/importAndExportDeclaration.ts new file mode 100644 index 00000000..514a5b35 --- /dev/null +++ b/mock-generate/src/declaration-node/importAndExportDeclaration.ts @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022 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 { ExportAssignment, ExportDeclaration, ImportDeclaration, + ImportEqualsDeclaration, isImportDeclaration, Node, SourceFile } from 'typescript'; + +/** + * get current sourceFile all imports + * @param sourceFile + * @returns + */ +export function getImportDeclarationArray(sourceFile: SourceFile): Array { + const importDeclarations: Array = []; + sourceFile.forEachChild(node => { + if (isImportDeclaration(node)) { + importDeclarations.push(getImportDeclaration(node, sourceFile)); + } + }); + return importDeclarations; +} + +/** + * get module inner import info + * @param importEqualNode + * @param sourceFile + * @returns + */ +export function getModuleImportEqual(importEqualNode: ImportEqualsDeclaration, sourceFile: SourceFile): ImportEuqalEntity { + return { + importEqualName: importEqualNode.name.escapedText.toString(), + importEqualTypeName: sourceFile.text.substring(importEqualNode.moduleReference.pos, importEqualNode.moduleReference.end).trimStart().trimEnd(), + importEqualTypeKind: importEqualNode.moduleReference.kind + }; +} + +/** + * get export info + * @param exportNode + * @param sourceFile + * @returns + */ +export function getExportDeclaration(exportNode: ExportDeclaration, sourceFile: SourceFile): string { + return sourceFile.text.substring(exportNode.pos, exportNode.end).trimStart().trimEnd(); +} + +/** + * get import info + * @param node + * @param sourceFile + * @returns + */ +export function getImportDeclaration(node: Node, sourceFile: SourceFile): ImportElementEntity { + let importElements = ''; + const importNode = node as ImportDeclaration; + const importPath = sourceFile.text.substring(importNode.moduleSpecifier.pos, importNode.moduleSpecifier.end).trimStart().trimEnd(); + const importClause = importNode.importClause; + if (importClause !== undefined) { + importElements = sourceFile.text.substring(importClause.pos, importClause.end).trimStart().trimEnd(); + } + + return { + importPath: importPath, + importElements: importElements + }; +} + +/** + * get export info + * @param exportAssigment + * @param sourceFile + * @returns + */ +export function getExportAssignment(exportAssigment: ExportAssignment, sourceFile: SourceFile): Array { + const exportAssignments: Array = []; + if (exportAssigment.expression !== undefined) { + exportAssignments.push(sourceFile.text.substring(exportAssigment.expression.pos, exportAssigment.expression.end).trimStart().trimEnd()); + } + return exportAssignments; +} + +export interface ImportElementEntity { + importPath: string, + importElements: string +} + +export interface ExportElementEntity { + exportName: string +} + +export interface ImportEuqalEntity { + importEqualName: string, + importEqualTypeName: string, + importEqualTypeKind: number +} diff --git a/mock-generate/src/declaration-node/indexSignatureDeclaration.ts b/mock-generate/src/declaration-node/indexSignatureDeclaration.ts new file mode 100644 index 00000000..461d2ae1 --- /dev/null +++ b/mock-generate/src/declaration-node/indexSignatureDeclaration.ts @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2022 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 { IndexSignatureDeclaration, SourceFile } from 'typescript'; + +/** + * get index signature info + * @param indexSignature + * @param sourceFile + * @returns + */ +export function getIndexSignatureDeclaration(indexSignature: IndexSignatureDeclaration, sourceFile: SourceFile): IndexSignatureEntity { + return { + indexSignatureKey: 'key', + indexSignatureKind: indexSignature.type.kind, + indexSignatureTypeName: sourceFile.text.substring(indexSignature.type.pos, indexSignature.type.end).trimStart().trimEnd() + }; +} + +export interface IndexSignatureEntity { + indexSignatureKey: string, + indexSignatureKind: number, + indexSignatureTypeName: string +} diff --git a/mock-generate/src/declaration-node/interfaceDeclaration.ts b/mock-generate/src/declaration-node/interfaceDeclaration.ts new file mode 100644 index 00000000..31477b52 --- /dev/null +++ b/mock-generate/src/declaration-node/interfaceDeclaration.ts @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2022 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 { + InterfaceDeclaration, isCallSignatureDeclaration, isConstructSignatureDeclaration, + isHeritageClause, isIndexSignatureDeclaration, isMethodSignature, isPropertySignature, + isTypeParameterDeclaration, SourceFile +} from 'typescript'; +import { getExportKeyword } from '../common/commonUtils'; +import { CallSignatureEntity, getCallSignatureDeclaration } from './callSignatureDeclaration'; +import { ConstructorEntity, getConstructorDeclaration } from './constructorDeclaration'; +import { getHeritageClauseDeclaration, HeritageClauseEntity } from './heritageClauseDeclaration'; +import { getIndexSignatureDeclaration, IndexSignatureEntity } from './indexSignatureDeclaration'; +import { getMethodSignatureDeclaration, MethodSignatureEntity } from './methodSignatureDeclaration'; +import { getPropertySignatureDeclaration, PropertySignatureEntity } from './propertySignatureDeclaration'; +import { getTypeParameterDeclaration, TypeParameterEntity } from './typeParameterDeclaration'; + +/** + * get interface info + * @param interfaceNode + * @param sourceFile + * @returns + */ +export function getInterfaceDeclaration(interfaceNode: InterfaceDeclaration, sourceFile: SourceFile): InterfaceEntity { + let exportModifiers: Array = []; + if (interfaceNode.modifiers !== undefined) { + exportModifiers = getExportKeyword(interfaceNode.modifiers); + } + + const interfaceName = interfaceNode.name.escapedText.toString(); + const heritageClauses: Array = []; + const interfaceConstructors: Array> = []; + const interfaceMethodSignature: Map> = new Map>(); + const interfacePropertySignatures: Array = []; + const callSignature: Array = []; + const typeParameters: Array = []; + const indexSignature: Array = []; + + if (interfaceNode.heritageClauses !== undefined) { + interfaceNode.heritageClauses.forEach(value => { + heritageClauses.push(getHeritageClauseDeclaration(value, sourceFile)); + }); + } + + interfaceNode.members.forEach(value => { + if (isPropertySignature(value)) { + interfacePropertySignatures.push(getPropertySignatureDeclaration(value, sourceFile)); + } else if (isMethodSignature(value)) { + const methodSignature = getMethodSignatureDeclaration(value, sourceFile); + if (interfaceMethodSignature.get(methodSignature.functionName) !== undefined) { + interfaceMethodSignature.get(methodSignature.functionName)?.push(methodSignature); + } else { + const methodSignatureArray: Array = []; + methodSignatureArray.push(methodSignature); + interfaceMethodSignature.set(methodSignature.functionName, methodSignatureArray); + } + } else if (isHeritageClause(value)) { + heritageClauses.push(getHeritageClauseDeclaration(value, sourceFile)); + } else if (isConstructSignatureDeclaration(value)) { + interfaceConstructors.push(getConstructorDeclaration(value, sourceFile)); + } else if (isCallSignatureDeclaration(value)) { + callSignature.push(getCallSignatureDeclaration(value, sourceFile)); + } else if (isTypeParameterDeclaration(value)) { + typeParameters.push(getTypeParameterDeclaration(value, sourceFile)); + } else if (isIndexSignatureDeclaration(value)) { + indexSignature.push(getIndexSignatureDeclaration(value, sourceFile)); + } else { + console.log('--------------------------- uncaught interface type start -----------------------'); + console.log('interfaceName: ' + interfaceName); + console.log(value); + console.log('--------------------------- uncaught interface type end -----------------------'); + } + }); + + return { + interfaceName: interfaceName, + typeParameters: typeParameters, + heritageClauses: heritageClauses, + interfaceConstructors: interfaceConstructors, + interfaceMethodSignature: interfaceMethodSignature, + interfacePropertySignatures: interfacePropertySignatures, + callSignatures: callSignature, + exportModifiers: exportModifiers, + indexSignature: indexSignature + }; +} + +export interface InterfaceEntity { + interfaceName: string, + typeParameters: Array, + heritageClauses: Array, + interfaceConstructors: Array>, + interfaceMethodSignature: Map>, + interfacePropertySignatures: Array, + callSignatures: Array, + exportModifiers: Array, + indexSignature: Array +} diff --git a/mock-generate/src/declaration-node/methodDeclaration.ts b/mock-generate/src/declaration-node/methodDeclaration.ts new file mode 100644 index 00000000..dec63959 --- /dev/null +++ b/mock-generate/src/declaration-node/methodDeclaration.ts @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022 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 { isComputedPropertyName, MethodDeclaration, Node, SourceFile } from 'typescript'; +import { + getFunctionAndMethodReturnInfo, getModifiers, getParameter, + getPropertyName, ParameterEntity, ReturnTypeEntity +} from '../common/commonUtils'; + +/** + * get method info + * @param node + * @param sourceFile + * @returns + */ +export function getMethodDeclaration(node: Node, sourceFile: SourceFile): MethodEntity { + const methodNode = node as MethodDeclaration; + const functionName = { + name: '', + expressionKind: -1, + kind: -1 + }; + + const args: Array = []; + let modifiers: Array = []; + + if (methodNode.modifiers !== undefined) { + modifiers = getModifiers(methodNode.modifiers); + } + + const returnType = getFunctionAndMethodReturnInfo(methodNode, sourceFile); + functionName.name = getPropertyName(methodNode.name, sourceFile); + if (isComputedPropertyName(methodNode.name)) { + functionName.expressionKind = methodNode.name.expression.kind; + } + + functionName.kind = methodNode.name.kind; + + methodNode.parameters.forEach(value => { + args.push(getParameter(value, sourceFile)); + }); + + return { + modifiers: modifiers, + functionName: functionName, + returnType: returnType, + args: args + }; +} + +export interface StaticMethodEntity { + className: string, + methodEntity: MethodEntity +} + +export interface MethodEntity { + modifiers: Array, + functionName: FunctionNameEntity, + returnType: ReturnTypeEntity, + args: Array +} + +export interface FunctionNameEntity { + name: string, + expressionKind: number, + kind: number +} diff --git a/mock-generate/src/declaration-node/methodSignatureDeclaration.ts b/mock-generate/src/declaration-node/methodSignatureDeclaration.ts new file mode 100644 index 00000000..15f7d14c --- /dev/null +++ b/mock-generate/src/declaration-node/methodSignatureDeclaration.ts @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022 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 { MethodSignature, Node, SourceFile } from 'typescript'; +import { + getFunctionAndMethodReturnInfo, getParameter, getPropertyName, + ParameterEntity, ReturnTypeEntity +} from '../common/commonUtils'; + +/** + * get interface signature info + * @param node + * @param sourceFile + * @returns + */ +export function getMethodSignatureDeclaration(node: Node, sourceFile: SourceFile): MethodSignatureEntity { + const methodSignatureNode = node as MethodSignature; + let functionName = ''; + const args: Array = []; + const returnType = getFunctionAndMethodReturnInfo(methodSignatureNode, sourceFile); + functionName = getPropertyName(methodSignatureNode.name, sourceFile); + methodSignatureNode.parameters.forEach(value => { + args.push(getParameter(value, sourceFile)); + }); + + return { + functionName: functionName, + returnType: returnType, + args: args + }; +} + +export interface MethodSignatureEntity { + functionName: string, + returnType: ReturnTypeEntity, + args: Array +} diff --git a/mock-generate/src/declaration-node/moduleDeclaration.ts b/mock-generate/src/declaration-node/moduleDeclaration.ts new file mode 100644 index 00000000..27b9673a --- /dev/null +++ b/mock-generate/src/declaration-node/moduleDeclaration.ts @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2022 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 { + isClassDeclaration, isEnumDeclaration, isExportDeclaration, isFunctionDeclaration, isIdentifier, + isImportEqualsDeclaration, isInterfaceDeclaration, isModuleBlock, isModuleDeclaration, isTypeAliasDeclaration, + isVariableStatement, ModuleDeclaration, Node, SourceFile +} from 'typescript'; +import { getExportKeyword } from '../common/commonUtils'; +import { ClassEntity, getClassDeclaration } from './classDeclaration'; +import { EnumEntity, getEnumDeclaration } from './enumDeclaration'; +import { FunctionEntity, getFunctionDeclaration } from './functionDeclaration'; +import { getExportDeclaration, getModuleImportEqual, ImportEuqalEntity } from './importAndExportDeclaration'; +import { getInterfaceDeclaration, InterfaceEntity } from './interfaceDeclaration'; +import { getTypeAliasDeclaration, TypeAliasEntity } from './typeAliasDeclaration'; +import { getVariableStatementDeclaration, StatementEntity } from './variableStatementResolve'; + +/** + * get module info + * @param node + * @param sourceFile + * @param fileName + * @returns + */ +export function getModuleDeclaration(node: Node, sourceFile: SourceFile, fileName: string): ModuleBlockEntity { + const moduleNode = node as ModuleDeclaration; + let moduleName = ''; + if (isIdentifier(moduleNode.name)) { + moduleName = moduleNode.name.escapedText.toString(); + } else { + moduleName = sourceFile.text.substring(moduleNode.name.pos, moduleNode.name.end).trimStart().trimEnd(); + } + + let exportModifiers: Array = []; + const modifiers = moduleNode.modifiers; + if (modifiers !== undefined) { + exportModifiers = getExportKeyword(modifiers); + } + + const typeAliasDeclarations: Array = []; + const classDeclarations: Array = []; + const interfaceDeclarations: Array = []; + const functionDeclarations: Map> = new Map>(); + const enumDeclarations: Array = []; + const moduleDeclarations: Array = []; + const variableStatements: Array> = []; + const moduleImportEquaqls: Array = []; + const exportDeclarations: Array = []; + const moduleBody = moduleNode.body; + + if (moduleBody !== undefined && isModuleBlock(moduleBody)) { + moduleBody.statements.forEach(value => { + if (isFunctionDeclaration(value)) { + const FunctionEntity = getFunctionDeclaration(value, sourceFile); + if (functionDeclarations.get(FunctionEntity.functionName) !== undefined) { + functionDeclarations.get(FunctionEntity.functionName)?.push(FunctionEntity); + } else { + const functionArray: Array = []; + functionArray.push(FunctionEntity); + functionDeclarations.set(FunctionEntity.functionName, functionArray); + } + } else if (isTypeAliasDeclaration(value)) { + typeAliasDeclarations.push(getTypeAliasDeclaration(value, sourceFile)); + } else if (isEnumDeclaration(value)) { + enumDeclarations.push(getEnumDeclaration(value, sourceFile)); + } else if (isClassDeclaration(value)) { + classDeclarations.push(getClassDeclaration(value, sourceFile)); + } else if (isInterfaceDeclaration(value)) { + interfaceDeclarations.push(getInterfaceDeclaration(value, sourceFile)); + } else if (isModuleDeclaration(value)) { + moduleDeclarations.push(getModuleDeclaration(value, sourceFile, fileName)); + } else if (isVariableStatement(value)) { + variableStatements.push(getVariableStatementDeclaration(value, sourceFile)); + } else if (isImportEqualsDeclaration(value)) { + moduleImportEquaqls.push(getModuleImportEqual(value, sourceFile)); + } else if (isExportDeclaration(value)) { + exportDeclarations.push(getExportDeclaration(value, sourceFile)); + } else { + console.log('--------------------------- uncaught module type start -----------------------'); + console.log('fileName: ' + fileName); + console.log(value); + console.log('--------------------------- uncaught module type end -----------------------'); + } + }); + } + + return { + moduleName: moduleName, + exportModifiers: exportModifiers, + typeAliasDeclarations: typeAliasDeclarations, + classDeclarations: classDeclarations, + interfaceDeclarations: interfaceDeclarations, + functionDeclarations: functionDeclarations, + enumDeclarations: enumDeclarations, + moduleDeclarations: moduleDeclarations, + variableStatements: variableStatements, + moduleImportEquaqls: moduleImportEquaqls, + exportDeclarations: exportDeclarations + }; +} + +export interface ModuleBlockEntity { + moduleName: string, + exportModifiers: Array, + typeAliasDeclarations: Array, + classDeclarations: Array, + interfaceDeclarations: Array, + functionDeclarations: Map>, + enumDeclarations: Array, + moduleDeclarations: Array, + variableStatements: Array>, + moduleImportEquaqls: Array, + exportDeclarations: Array +} diff --git a/mock-generate/src/declaration-node/propertyDeclaration.ts b/mock-generate/src/declaration-node/propertyDeclaration.ts new file mode 100644 index 00000000..c22f8a66 --- /dev/null +++ b/mock-generate/src/declaration-node/propertyDeclaration.ts @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2022 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 { PropertyDeclaration, SourceFile } from 'typescript'; +import { getPropertyName } from '../common/commonUtils'; + +/** + * get property node info + * @param node + * @param sourceFile + * @returns + */ +export function getPropertyDeclaration(node: PropertyDeclaration, sourceFile: SourceFile): PropertyEntity { + let propertyName = ''; + let propertyTypeName = ''; + let kind = -1; + let isInitializer = false; + let initializer = ''; + const modifiers: Array = []; + + if (node.modifiers !== undefined) { + node.modifiers.forEach(value => { + modifiers.push(sourceFile.text.substring(value.pos, value.end)); + }); + } + + if (node.initializer !== undefined) { + isInitializer = true; + initializer = sourceFile.text.substring(node.initializer.pos, node.initializer.end).trimStart().trimEnd(); + } + + propertyName = getPropertyName(node.name, sourceFile); + + const propertyType = node.type; + if (propertyType !== undefined) { + propertyTypeName = sourceFile.text.substring(propertyType.pos, propertyType.end).trimStart().trimEnd(); + kind = propertyType.kind; + } + + return { + modifiers: modifiers, + propertyName: propertyName, + propertyTypeName: propertyTypeName, + kind: kind, + isInitializer: isInitializer, + initializer: initializer + }; +} + +export interface PropertyEntity { + modifiers: Array, + propertyName: string, + propertyTypeName: string, + kind: number, + isInitializer: boolean, + initializer: string +} diff --git a/mock-generate/src/declaration-node/propertySignatureDeclaration.ts b/mock-generate/src/declaration-node/propertySignatureDeclaration.ts new file mode 100644 index 00000000..f137d478 --- /dev/null +++ b/mock-generate/src/declaration-node/propertySignatureDeclaration.ts @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2022 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 { PropertySignature, SourceFile } from 'typescript'; +import { getPropertyName } from '../common/commonUtils'; + +/** + * get interface property signature info + * @param node + * @param sourceFile + * @returns + */ +export function getPropertySignatureDeclaration(node: PropertySignature, sourceFile: SourceFile): PropertySignatureEntity { + let propertyName = ''; + let propertyTypeName = ''; + let kind = -1; + + propertyName = getPropertyName(node.name, sourceFile); + const propertyType = node.type; + const modifiers: Array = []; + if (node.modifiers !== undefined) { + node.modifiers.forEach(value => { + modifiers.push(sourceFile.text.substring(value.pos, value.end)); + }); + } + + if (propertyType !== undefined) { + propertyTypeName = sourceFile.text.substring(propertyType.pos, propertyType.end).trimStart().trimEnd(); + kind = propertyType.kind; + } + + return { + modifiers: modifiers, + propertyName: propertyName, + propertyTypeName: propertyTypeName, + kind: kind + }; +} + +export interface PropertySignatureEntity { + modifiers: Array, + propertyName: string, + propertyTypeName: string, + kind: number +} diff --git a/mock-generate/src/declaration-node/sourceFileElementsAssemply.ts b/mock-generate/src/declaration-node/sourceFileElementsAssemply.ts new file mode 100644 index 00000000..d889f954 --- /dev/null +++ b/mock-generate/src/declaration-node/sourceFileElementsAssemply.ts @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2022 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 { + isClassDeclaration, isEnumDeclaration, isExportAssignment, isExportDeclaration, isFunctionDeclaration, + isImportDeclaration, isInterfaceDeclaration, isModuleDeclaration, isTypeAliasDeclaration, isVariableStatement, + SourceFile, SyntaxKind +} from 'typescript'; +import { ClassEntity, getClassDeclaration } from './classDeclaration'; +import { EnumEntity, getEnumDeclaration } from './enumDeclaration'; +import { FunctionEntity, getFunctionDeclaration } from './functionDeclaration'; +import { getExportAssignment, getImportDeclaration, ImportElementEntity } from './importAndExportDeclaration'; +import { getInterfaceDeclaration, InterfaceEntity } from './interfaceDeclaration'; +import { StaticMethodEntity } from './methodDeclaration'; +import { getModuleDeclaration, ModuleBlockEntity } from './moduleDeclaration'; +import { getTypeAliasDeclaration, TypeAliasEntity } from './typeAliasDeclaration'; +import { getVariableStatementDeclaration, StatementEntity } from './variableStatementResolve'; + +/** + * assembly all sourceFile node info + * @param sourceFile + * @param fileName + * @returns + */ +export function getSourceFileAssembly(sourceFile: SourceFile, fileName: string): SourceFileEntity { + const importDeclarations: Array = []; + const moduleDeclarations: Array = []; + const typeAliasDeclarations: Array = []; + const classDeclarations: Array = []; + const interfaceDeclarations: Array = []; + const enumDeclarations: Array = []; + let exportAssignment: Array = []; + const staticMethods: Array> = []; + const exportDeclarations: Array = []; + + sourceFile.forEachChild(node => { + if (isImportDeclaration(node)) { + importDeclarations.push(getImportDeclaration(node, sourceFile)); + } else if (isModuleDeclaration(node)) { + moduleDeclarations.push(getModuleDeclaration(node, sourceFile, fileName)); + } else if (isTypeAliasDeclaration(node)) { + typeAliasDeclarations.push(getTypeAliasDeclaration(node, sourceFile)); + } else if (isClassDeclaration(node)) { + let isDefaultExportClass = false; + if (node.modifiers !== undefined) { + node.modifiers.forEach(value => { + if (value.kind === SyntaxKind.DefaultKeyword) { + isDefaultExportClass = true; + } + }); + } + if (isDefaultExportClass) { + const classDeclarationEntity = getClassDeclaration(node, sourceFile); + classDeclarations.push(classDeclarationEntity); + if (classDeclarationEntity.staticMethods.length > 0) { + staticMethods.push(classDeclarationEntity.staticMethods); + } + } + } else if (isInterfaceDeclaration(node)) { + interfaceDeclarations.push(getInterfaceDeclaration(node, sourceFile)); + } else if (isExportAssignment(node)) { + exportAssignment = getExportAssignment(node, sourceFile); + } else if (isEnumDeclaration(node)) { + enumDeclarations.push(getEnumDeclaration(node, sourceFile)); + } else if (isExportDeclaration(node)) { + exportDeclarations.push(sourceFile.text.substring(node.pos, node.end).trimStart().trimEnd()); + } else { + if (node.kind !== SyntaxKind.EndOfFileToken && !isFunctionDeclaration(node) && !isVariableStatement(node)) { + console.log('--------------------------- uncaught sourceFile type start -----------------------'); + console.log('fileName: ' + fileName); + console.log(node); + console.log('--------------------------- uncaught sourceFile type end -----------------------'); + } + } + }); + + return { + importDeclarations: importDeclarations, + moduleDeclarations: moduleDeclarations, + typeAliasDeclarations: typeAliasDeclarations, + classDeclarations: classDeclarations, + interfaceDeclarations: interfaceDeclarations, + enumDeclarations: enumDeclarations, + exportAssignment: exportAssignment, + staticMethods: staticMethods, + exportDeclarations: exportDeclarations + }; +} + +/** + * get default export class + * @param sourceFile + * @returns + */ +export function getDefaultExportClassDeclaration(sourceFile: SourceFile): Array { + const defaultExportClass: Array = []; + sourceFile.forEachChild(node => { + if (isClassDeclaration(node)) { + defaultExportClass.push(getClassDeclaration(node, sourceFile)); + } + }); + return defaultExportClass; +} + +/** + * get sourceFile const variable statement + * @param sourceFile + * @returns + */ +export function getSourceFileVariableStatements(sourceFile: SourceFile): Array> { + const variableStatements: Array> = []; + sourceFile.forEachChild(node => { + if (isVariableStatement(node)) { + variableStatements.push(getVariableStatementDeclaration(node, sourceFile)); + } + }); + return variableStatements; +} + +/** + * get sourcefile functions + * @param sourceFile + * @returns + */ +export function getSourceFileFunctions(sourceFile: SourceFile): Map> { + const functionDeclarations: Map> = new Map>(); + sourceFile.forEachChild(node => { + if (isFunctionDeclaration(node)) { + const functionEntity = getFunctionDeclaration(node, sourceFile); + if (functionDeclarations.get(functionEntity.functionName) !== undefined) { + functionDeclarations.get(functionEntity.functionName)?.push(functionEntity); + } else { + const functionArray: Array = []; + functionArray.push(functionEntity); + functionDeclarations.set(functionEntity.functionName, functionArray); + } + } + }); + return functionDeclarations; +} + +export interface SourceFileEntity { + importDeclarations: Array, + moduleDeclarations: Array, + typeAliasDeclarations: Array, + classDeclarations: Array, + interfaceDeclarations: Array, + enumDeclarations: Array, + exportAssignment: Array, + staticMethods: Array>, + exportDeclarations: Array +} diff --git a/mock-generate/src/declaration-node/typeAliasDeclaration.ts b/mock-generate/src/declaration-node/typeAliasDeclaration.ts new file mode 100644 index 00000000..2a5acc81 --- /dev/null +++ b/mock-generate/src/declaration-node/typeAliasDeclaration.ts @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022 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 { + isTypeLiteralNode, isTypeReferenceNode, + isUnionTypeNode, Node, SourceFile, TypeAliasDeclaration +} from 'typescript'; + +/** + * get type alias info + * @param node + * @param sourceFile + * @returns + */ +export function getTypeAliasDeclaration(node: Node, sourceFile: SourceFile): TypeAliasEntity { + const typeAliasNode = node as TypeAliasDeclaration; + const typeAliasName = typeAliasNode.name.escapedText.toString(); + const typeAliasTypeKind = typeAliasNode.type.kind; + const typeAliasTypeElements: Array = []; + const modifiers: Array = []; + + const modifiersNode = typeAliasNode.modifiers; + if (modifiersNode !== undefined) { + modifiersNode.forEach(value => { + modifiers.push(value.kind); + }); + } + + const typeAliasTypeElementsNode = typeAliasNode.type; + if (typeAliasTypeElementsNode !== undefined) { + if (isUnionTypeNode(typeAliasTypeElementsNode)) { + typeAliasTypeElementsNode.types.forEach(value => { + const typeName = sourceFile.text.substring(value.pos, value.end).trimStart().trimEnd(); + const typeKind = value.kind; + typeAliasTypeElements.push({ typeName: typeName, typeKind: typeKind }); + }); + } else if (isTypeReferenceNode(typeAliasTypeElementsNode)) { + const typeName = sourceFile.text.substring(typeAliasTypeElementsNode.typeName.pos, typeAliasTypeElementsNode.typeName.end).trimStart().trimEnd(); + typeAliasTypeElements.push({ typeName: typeName, typeKind: typeAliasTypeElementsNode.typeName.kind }); + } else if (isTypeLiteralNode(typeAliasTypeElementsNode)) { + typeAliasTypeElementsNode.members.forEach(value => { + const typeName = sourceFile.text.substring(value.pos, value.end).trimStart().trimEnd(); + const typeKind = value.kind; + typeAliasTypeElements.push({ typeName: typeName, typeKind: typeKind }); + }); + } else { + typeAliasTypeElements.push( + { + typeName: sourceFile.text.substring(typeAliasTypeElementsNode.pos, typeAliasTypeElementsNode.end), + typeKind: typeAliasTypeElementsNode.kind + } + ); + } + } + + return { + typeAliasName: typeAliasName, + typeAliasTypeKind: typeAliasTypeKind, + typeAliasTypeElements: typeAliasTypeElements, + modifiers: modifiers + }; +} + +export interface TypeAliasEntity { + typeAliasName: string, + typeAliasTypeKind: number, + typeAliasTypeElements: Array, + modifiers: Array +} + +export interface TypeAliasTypeEntity { + typeName: string, + typeKind: number +} diff --git a/mock-generate/src/declaration-node/typeParameterDeclaration.ts b/mock-generate/src/declaration-node/typeParameterDeclaration.ts new file mode 100644 index 00000000..f4a1d7fb --- /dev/null +++ b/mock-generate/src/declaration-node/typeParameterDeclaration.ts @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 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 { Node, SourceFile, TypeParameterDeclaration } from 'typescript'; + +/** + * get generic type node info + * @param node + * @param sourceFile + * @returns + */ +export function getTypeParameterDeclaration(node: Node, sourceFile: SourceFile): TypeParameterEntity { + const typeParameterNode = node as TypeParameterDeclaration; + const typeParameterName = typeParameterNode.name.escapedText.toString(); + let constraitValue = ''; + let constraintDefaultValue = ''; + const constraint = typeParameterNode.constraint; + if (constraint !== undefined) { + constraitValue = sourceFile.text.substring(constraint.pos, constraint.end).trimStart().trimEnd(); + } + + const defaultValue = typeParameterNode.default; + if (defaultValue !== undefined) { + constraintDefaultValue = sourceFile.text.substring(defaultValue.pos, defaultValue.end).trimStart().trimEnd(); + } + + return { + typeParameterName: typeParameterName, + constraitValue: constraitValue, + constraintDefaultValue: constraintDefaultValue + }; +} + +export interface TypeParameterEntity { + typeParameterName: string, + constraitValue: string, + constraintDefaultValue: string +} diff --git a/mock-generate/src/declaration-node/variableStatementResolve.ts b/mock-generate/src/declaration-node/variableStatementResolve.ts new file mode 100644 index 00000000..b16d84a4 --- /dev/null +++ b/mock-generate/src/declaration-node/variableStatementResolve.ts @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 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 { isIdentifier, SourceFile, VariableStatement } from 'typescript'; + +/** + * get const declaration variable + * @param variableStatement + * @param sourceFile + * @returns + */ +export function getVariableStatementDeclaration(variableStatement: VariableStatement, sourceFile: SourceFile): Array { + const statementsArray: Array = []; + variableStatement.declarationList.declarations.forEach(value => { + let statementName = ''; + let initializer = ''; + let typeName = ''; + let typeKind = -1; + + if (isIdentifier(value.name)) { + statementName = value.name.escapedText.toString(); + } else { + statementName = sourceFile.text.substring(value.pos, value.end).trimStart().trimEnd(); + } + if (value.initializer !== undefined) { + initializer = sourceFile.text.substring(value.initializer.pos, value.initializer.end); + } + if (value.type !== undefined) { + typeName = sourceFile.text.substring(value.type.pos, value.type.end); + typeKind = value.type.kind; + } + statementsArray.push({ statementName: statementName, typeName: typeName, typeKind: typeKind, initializer: initializer }); + }); + return statementsArray; +} + +export interface StatementEntity { + statementName: string, + typeName: string, + typeKind: number, + initializer: string +} diff --git a/mock-generate/src/generate/generateClassDeclaration.ts b/mock-generate/src/generate/generateClassDeclaration.ts new file mode 100644 index 00000000..7f0bdb3b --- /dev/null +++ b/mock-generate/src/generate/generateClassDeclaration.ts @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2022 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 { SourceFile, SyntaxKind } from 'typescript'; +import { firstCharacterToUppercase } from '../common/commonUtils'; +import { ClassEntity } from '../declaration-node/classDeclaration'; +import { generateCommonMethod } from './generateCommonMethod'; +import { getWarnConsole } from './generateCommonUtil'; +import { generatePropertyDeclaration } from './generatePropertyDeclaration'; +import { generateStaticFunction } from './generateStaticFunction'; + +/** + * generate class + * @param rootName + * @param classEntity + * @param isSystem + * @param globalName + * @param filename + * @param sourceFile + * @param isInnerMockFunction + * @returns + */ +export function generateClassDeclaration(rootName: string, classEntity: ClassEntity, isSystem: boolean, globalName: string, + filename: string, sourceFile: SourceFile, isInnerMockFunction: boolean): string { + if (isSystem) { + return ''; + } + + const className = firstCharacterToUppercase(classEntity.className); + let classBody = ''; + if (classEntity.exportModifiers.includes(SyntaxKind.ExportKeyword) && !isInnerMockFunction) { + classBody += `export const ${className} = class ${className} `; + } else { + classBody += `const ${className} = class ${className} `; + } + + let isExtend = false; + + if (classEntity.heritageClauses.length > 0) { + classEntity.heritageClauses.forEach(value => { + if (value.clauseToken === 'extends') { + isExtend = true; + classBody += `${value.clauseToken} `; + value.types.forEach((val, index) => { + if (index !== value.types.length - 1) { + classBody += `${val},`; + } else { + classBody += `${val}`; + } + }); + } + }); + } + + if (!isSystem) { + classBody += '{'; + if (classEntity.classConstructor.length > 1) { + classBody += `constructor(...arg) { `; + } else { + classBody += `constructor() { `; + } + if (isExtend) { + classBody += `super();`; + } + classBody += getWarnConsole(className, 'constructor'); + } + if (classEntity.classProperty.length > 0) { + classEntity.classProperty.forEach(value => { + classBody += generatePropertyDeclaration(className, value, sourceFile) + '\n'; + }); + } + + if (classEntity.classMethod.size > 0) { + classEntity.classMethod.forEach(value => { + classBody += generateCommonMethod(className, value, sourceFile); + }); + } + + classBody += '}\n};'; + if (!filename.startsWith('system_')) { + if (classEntity.staticMethods.length > 0) { + let staticMethodBody = ''; + classEntity.staticMethods.forEach(value => { + staticMethodBody += generateStaticFunction(value, false, sourceFile) + '\n'; + }); + classBody += staticMethodBody; + } + } + return classBody; +} diff --git a/mock-generate/src/generate/generateCommonFunction.ts b/mock-generate/src/generate/generateCommonFunction.ts new file mode 100644 index 00000000..9185c17f --- /dev/null +++ b/mock-generate/src/generate/generateCommonFunction.ts @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022 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 { SourceFile, SyntaxKind } from 'typescript'; +import { FunctionEntity } from '../declaration-node/functionDeclaration'; +import { getCallbackStatement, getReturnStatement, getWarnConsole } from './generateCommonUtil'; + +/** + * generate function + * @param rootName + * @param functionArray + * @param sourceFile + * @returns + */ +export function generateCommonFunction(rootName: string, functionArray: Array, sourceFile: SourceFile): string { + let functionBody = ''; + const functionEntity = functionArray[0]; + functionBody = `${functionEntity.functionName}: function(...args) {`; + functionBody += getWarnConsole(rootName, functionEntity.functionName); + + if (functionArray.length === 1) { + const args = functionEntity.args; + const len = args.length; + if (args.length > 0 && args[len - 1].paramName.toLowerCase().includes('callback')) { + functionBody += getCallbackStatement(); + } + if (functionEntity.returnType.returnKind !== SyntaxKind.VoidKeyword) { + if (rootName === 'featureAbility' && functionEntity.returnType.returnKindName === 'Context') { + functionBody += 'return _Context;'; + } else { + functionBody += getReturnStatement(functionEntity.returnType, sourceFile); + } + } + } else { + const argSet: Set = new Set(); + const returnSet: Set = new Set(); + let isCallBack = false; + functionArray.forEach(value => { + returnSet.add(value.returnType.returnKindName); + value.args.forEach(arg => { + argSet.add(arg.paramName); + if (arg.paramName.toLowerCase().includes('callback')) { + isCallBack = true; + } + }); + }); + if (isCallBack) { + functionBody += getCallbackStatement(); + } + let isReturnPromise = false; + returnSet.forEach(value => { + if (value.startsWith('Promise')) { + isReturnPromise = true; + } + }); + + if (isReturnPromise && isCallBack) { + functionBody += `else { + return new Promise((resolve, reject) => { + resolve('[PC Preview] unknow boolean'); + }) + }`; + } + } + functionBody += '},'; + return functionBody; +} diff --git a/mock-generate/src/generate/generateCommonMethod.ts b/mock-generate/src/generate/generateCommonMethod.ts new file mode 100644 index 00000000..d733fa32 --- /dev/null +++ b/mock-generate/src/generate/generateCommonMethod.ts @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2022 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 { SourceFile, SyntaxKind } from 'typescript'; +import { MethodEntity } from '../declaration-node/methodDeclaration'; +import { + generateSymbolIterator, getCallbackStatement, + getReturnStatement, getWarnConsole +} from './generateCommonUtil'; + +/** + * generate class method + * @param rootName + * @param methodArray + * @param sourceFile + * @returns + */ +export function generateCommonMethod(rootName: string, methodArray: Array, sourceFile: SourceFile): string { + let methodBody = ''; + const methodEntity = methodArray[0]; + if (methodEntity.functionName.name === 'Symbol.iterator') { + methodBody += `this[${methodEntity.functionName.name}] = function(...args) {`; + methodBody += getWarnConsole(rootName, methodEntity.functionName.name); + methodBody += generateSymbolIterator(methodEntity); + methodBody += '};\n'; + return methodBody; + } else { + methodBody += `this.${methodEntity.functionName.name} = function(...args) {`; + methodBody += getWarnConsole(rootName, methodEntity.functionName.name); + } + + if (methodArray.length === 1) { + const args = methodEntity.args; + const len = args.length; + if (args.length > 0 && args[len - 1].paramName.toLowerCase().includes('callback')) { + methodBody += getCallbackStatement(); + } + if (methodEntity.returnType.returnKind !== SyntaxKind.VoidKeyword) { + if (methodEntity.functionName.name === 'getApplicationContext') { + methodBody += 'return new Context();'; + } else { + methodBody += getReturnStatement(methodEntity.returnType, sourceFile); + } + } + } else { + const argSet: Set = new Set(); + const returnSet: Set = new Set(); + let isCallBack = false; + methodArray.forEach(value => { + returnSet.add(value.returnType.returnKindName); + value.args.forEach(arg => { + argSet.add(arg.paramName); + if (arg.paramName.toLowerCase().includes('callback')) { + isCallBack = true; + } + }); + }); + if (isCallBack) { + methodBody += getCallbackStatement(); + } + let isReturnPromise = false; + returnSet.forEach(value => { + if (value.startsWith('Promise')) { + isReturnPromise = true; + } + }); + + if (isReturnPromise && isCallBack) { + methodBody += `else { + return new Promise((resolve, reject) => { + resolve('[PC Preview] unknow boolean'); + }) + }`; + } else { + methodBody += `return new Promise((resolve, reject) => { + resolve('[PC preview] unknown type'); + });`; + } + } + methodBody += '};\n'; + return methodBody; +} diff --git a/mock-generate/src/generate/generateCommonMethodSignature.ts b/mock-generate/src/generate/generateCommonMethodSignature.ts new file mode 100644 index 00000000..048b9072 --- /dev/null +++ b/mock-generate/src/generate/generateCommonMethodSignature.ts @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022 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 { SourceFile, SyntaxKind } from 'typescript'; +import { MethodSignatureEntity } from '../declaration-node/methodSignatureDeclaration'; +import { getCallbackStatement, getReturnStatement, getWarnConsole } from './generateCommonUtil'; + +/** + * generate interface signature method + * @param rootName + * @param methodSignatureArray + * @param sourceFile + * @returns + */ +export function generateCommonMethodSignature(rootName: string, methodSignatureArray: Array, sourceFile: SourceFile): string { + let methodSignatureBody = ''; + const methodEntity = methodSignatureArray[0]; + methodSignatureBody += `${methodEntity.functionName}: function(...args) {`; + methodSignatureBody += getWarnConsole(rootName, methodEntity.functionName); + if (methodSignatureArray.length === 1) { + const args = methodEntity.args; + const len = args.length; + if (args.length > 0 && args[len - 1].paramName.toLowerCase().includes('callback')) { + methodSignatureBody += getCallbackStatement(); + } + if (methodEntity.returnType.returnKind !== SyntaxKind.VoidKeyword) { + if (rootName === 'Context' && methodEntity.returnType.returnKindName === 'Context') { + methodSignatureBody += 'return Context;'; + } else { + methodSignatureBody += getReturnStatement(methodEntity.returnType, sourceFile); + } + } + } else { + const argSet: Set = new Set(); + const returnSet: Set = new Set(); + let isCallBack = false; + methodSignatureArray.forEach(value => { + returnSet.add(value.returnType.returnKindName); + value.args.forEach(arg => { + argSet.add(arg.paramName); + if (arg.paramName.toLowerCase().includes('callback')) { + isCallBack = true; + } + }); + }); + if (isCallBack) { + methodSignatureBody += getCallbackStatement(); + } + let isReturnPromise = false; + returnSet.forEach(value => { + if (value.startsWith('Promise')) { + isReturnPromise = true; + } + }); + if (isReturnPromise && isCallBack) { + methodSignatureBody += `else { + return new Promise((resolve, reject) => { + resolve('[PC Preview] unknow boolean'); + }) + }`; + } + } + methodSignatureBody += '},\n'; + return methodSignatureBody; +} diff --git a/mock-generate/src/generate/generateCommonUtil.ts b/mock-generate/src/generate/generateCommonUtil.ts new file mode 100644 index 00000000..2ba72095 --- /dev/null +++ b/mock-generate/src/generate/generateCommonUtil.ts @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2022 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 { SourceFile, SyntaxKind } from 'typescript'; +import { firstCharacterToUppercase, getClassNameSet, ReturnTypeEntity } from '../common/commonUtils'; +import { getImportDeclarationArray, ImportElementEntity } from '../declaration-node/importAndExportDeclaration'; +import { MethodEntity } from '../declaration-node/methodDeclaration'; + +/** + * get warn console template + * @param interfaceNameOrClassName + * @param functionNameOrPropertyName + * @returns + */ +export function getWarnConsole(interfaceNameOrClassName: string, functionNameOrPropertyName: string): string { + return `console.warn('${interfaceNameOrClassName}.${functionNameOrPropertyName} interface mocked in the Previewer. How this interface works on the Previewer may be different from that on a real device.');\n`; +} + +/** + * generate return statement; + * @param returnType + * @param sourceFile + * @returns + */ +export function getReturnStatement(returnType: ReturnTypeEntity, sourceFile: SourceFile): string { + if (returnType.returnKind === SyntaxKind.TypeReference) { + if (returnType.returnKindName.startsWith('Promise')) { + return `return new Promise((resolve, reject) => { + resolve('[PC Preview] unkonwn type'); + })`; + } else if (returnType.returnKindName === 'T') { + return `return '[PC Preview] unkonwn type'`; + } else if (returnType.returnKindName === 'String') { + return `return ${returnType.returnKindName}(...args)`; + } else if (returnType.returnKindName.startsWith('Array')) { + return `return [${generateGenericTypeToMockValue(returnType.returnKindName)}]`; + } else if (returnType.returnKindName.startsWith('Readonly')) { + return `return ${returnType.returnKindName.split('<')[1].split('>')[0]}`; + } else if (checkIsGenericSymbol(returnType.returnKindName)) { + return `return '[PC Preview] unkonwn iterableiterator_${returnType.returnKindName}'`; + } else if (returnType.returnKindName.startsWith('Uint8Array')) { + return `return new ${returnType.returnKindName}()`; + } else if (returnType.returnKindName.startsWith('IterableIterator')) { + if (returnType.returnKindName.includes(',')) { + return `const IteratorEntriesMock = { + *[Symbol.iterator]() { + yield ['[PC Preview] unkonwn paramIterMock_K', '[PC Preview] unkonwn paramIterMock_V']; + } + }; + return IteratorEntriesMock;`; + } else { + return `const IteratorStringMock = { + *[Symbol.iterator]() { + yield '[PC Preview] unkonwn string'; + } + }; + return IteratorStringMock;`; + } + } else if (returnType.returnKindName.includes('')) { + const tmpReturn = returnType.returnKindName.split('<')[0]; + if (tmpReturn.startsWith('Array')) { + return `return []`; + } else { + `return new ${tmpReturn}()`; + } + } else if (returnType.returnKindName.includes('<')) { + return `return new ${returnType.returnKindName.split('<')[0]}()`; + } else { + if (getClassNameSet().has(returnType.returnKindName)) { + if (returnType.returnKindName === 'Want') { + return `return mockWant().Want`; + } else { + return `return new ${returnType.returnKindName}()`; + } + } else if (propertyTypeWhiteList(returnType.returnKindName) === returnType.returnKindName) { + return `return ${getTheRealReferenceFromImport(sourceFile, returnType.returnKindName)}`; + } else { + return `return ${propertyTypeWhiteList(returnType.returnKindName)}`; + } + } + } else if (returnType.returnKind === SyntaxKind.UnionType) { + const returnNames = returnType.returnKindName.split('|'); + let returnName = returnNames[0]; + for (let i = 0; i < returnNames.length; i++) { + if (!returnNames[i].includes('[]') && !returnNames[i].includes('<')) { + returnName = returnNames[i]; + break; + } + } + if (getClassNameSet().has(returnName)) { + return `return new ${returnName}()`; + } else { + return `return ${getBaseReturnValue(returnName.trimStart().trimEnd())}`; + } + } else { + return `return '[PC Preview] unkonwn type'`; + } + return `return '[PC Preview] unkonwn type'`; +} + +/** + * special property whitelist + * @param propertyTypeName + * @returns + */ +export function propertyTypeWhiteList(propertyTypeName: string): any { + const whiteList = ['GLboolean', 'GLuint', 'GLenum', 'GLint', 'NotificationFlags']; + if (whiteList.includes(propertyTypeName)) { + if (propertyTypeName === 'NotificationFlags' || propertyTypeName === 'GLenum') { + return `'[PC Preview] unkonwn ${propertyTypeName}'`; + } else if (propertyTypeName === 'GLboolean') { + return true; + } else { + return 0; + } + } else { + return propertyTypeName; + } +} + +/** + * get basic return value + * @param value + * @returns + */ +export function getBaseReturnValue(value: string): string | number | boolean { + if (value === 'string') { + return `''`; + } else if (value === 'number') { + return 0; + } else if (value === 'boolean') { + return true; + } else if (value === 'Object' || value === 'object') { + return `{}`; + } else if (checkIsGenericSymbol(value)) { + return `'[PC Preview] unkonwn type'`; + } else if (value === 'WebGLActiveInfo') { + return `{size: '[PC Preview] unkonwn GLint', type: 0, name: '[PC Preview] unkonwn name'}`; + } else { + return value; + } +} + +/** + * get current sourceFile import data + * @param sourceFile + * @param typeName + * @returns + */ +export function getTheRealReferenceFromImport(sourceFile: SourceFile, typeName: string): string { + const importArray = getImportDeclarationArray(sourceFile); + let returnName = ''; + let isFromImport = false; + let isOhos = false; + let mockMockName = ''; + importArray.forEach(value => { + if (typeName.includes('.') && typeName.split('.')[0] === value.importElements) { + isFromImport = true; + if (value.importPath.includes('@ohos')) { + isOhos = true; + } + if (value.importElements.trimStart().trimEnd() === typeName.split('.')[0]) { + const tmpArr = value.importPath.split('.'); + mockMockName = tmpArr[tmpArr.length - 1].replace(/'/g, '').replace(/"/g, ''); + } + } + }); + if (isFromImport) { + const splitReturnKindName = typeName.split('.'); + let left = ''; + for (let i = 1; i < splitReturnKindName.length; i++) { + left += `.${splitReturnKindName[i]}`; + } + if (isOhos) { + returnName = `mock${firstCharacterToUppercase(mockMockName)}()${left}`; + } + } else { + returnName = getImportTypeAliasNameFromImportElements(importArray, typeName); + } + return returnName; +} + +/** + * get return type alias, for example: {Context as _Context} return _Context + * @param importElementEntity + * @param typeName + * @returns + */ + function getImportTypeAliasNameFromImportElements(importElementEntity: ImportElementEntity[], typeName: string): string { + for (let i = 0; i < importElementEntity.length; i++) { + if (importElementEntity[i].importElements.includes('_')) { + const importElements = importElementEntity[i].importElements.replace('{', '').replace('}', '').split(','); + for (let j = 0; j < importElements.length; j++) { + const element = importElements[j].trimStart().trimEnd(); + if (`_${typeName}` === element.split('as')[1].trimStart().trimEnd()) { + return `_${typeName}`; + } + } + } + } + if (typeName === 'Want') { + typeName = `mockWant().Want`; + } else if (typeName === 'InputMethodExtensionContext') { + typeName = `mockInputmethodextensioncontext().InputMethodExtensionContext`; + } + return typeName; +} + +/** + * check is generic symbol + * @param type + * @returns + */ +export function checkIsGenericSymbol(type: string): boolean { + const words = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; + return words.includes(type); +} + +/** + * generate basic type default value + * @param kindName + * @returns + */ +export function generateGenericTypeToMockValue(kindName: string): any { + const genericTypeName = kindName.split('<')[1].split('>')[0]; + if (genericTypeName === 'string') { + return `''`; + } else if (genericTypeName === 'number') { + return 0; + } else if (genericTypeName === 'boolean') { + return true; + } else if (genericTypeName === 'Object' || genericTypeName === 'object') { + return '{}'; + } else { + return ``; + } +} + +/** + * get callback statement + * @returns + */ +export function getCallbackStatement(): string { + return `const len = args.length; + if (typeof args[len - 1] === 'function') { + args[len - 1].call(this, null, '[PC Preview] unkonwn type') + }`; +} + +/** + * get iterator template string + * @param methodEntity + * @returns + */ +export function generateSymbolIterator(methodEntity: MethodEntity): string { + let iteratorMethod = ''; + if (methodEntity.returnType.returnKindName.includes('<[')) { + iteratorMethod += `let index = 0; + const IteratorMock = { + next: () => { + if (index < 1) { + const returnValue = ['[PC Previwe] unkonwn iterableiterator_k', '[PC Previwe] unkonwn iterableiterator_v']; + index++; + return { + value: returnValue, + done: false + }; + } else { + return { + done: true + }; + } + } + }; + return IteratorMock;`; + } else { + iteratorMethod += `let index = 0; + const IteratorMock = { + next: () => { + if (index < 1) { + index++; + return { + value: '[PC Preview] unkonwn any', + done: false + }; + } else { + return { + done: true + }; + } + } + }; + return IteratorMock;`; + } + + return iteratorMethod; +} diff --git a/mock-generate/src/generate/generateEntry.ts b/mock-generate/src/generate/generateEntry.ts new file mode 100644 index 00000000..e91aa650 --- /dev/null +++ b/mock-generate/src/generate/generateEntry.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 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. + */ + +/** + * generate entry + * @returns + */ +export function generateEntry(): string { + return `import { mockSystemPlugin } from './index'; + + mockSystemPlugin()`; +} diff --git a/mock-generate/src/generate/generateEnumDeclaration.ts b/mock-generate/src/generate/generateEnumDeclaration.ts new file mode 100644 index 00000000..e97ad98c --- /dev/null +++ b/mock-generate/src/generate/generateEnumDeclaration.ts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 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 { SyntaxKind } from 'typescript'; +import { EnumEntity } from '../declaration-node/enumDeclaration'; + +/** + * generate enum + * @param rootName + * @param enumDeclaration + * @returns + */ +export function generateEnumDeclaration(rootName: string, enumDeclaration: EnumEntity): string { + let enumBody = ''; + if (enumDeclaration.exportModifiers.length !== 0) { + enumBody += `export const ${enumDeclaration.enumName} = {\n`; + } else { + enumBody += `const ${enumDeclaration.enumName} = {\n`; + } + + let defaultValue = 0; + enumDeclaration.enumMembers.forEach(member => { + if (member.enumKind === SyntaxKind.TypeReference) { + enumBody += `${member.enumValueName}: new ${member.enumValue},\n`; + } else if (member.enumKind === SyntaxKind.NumericLiteral) { + enumBody += `${member.enumValueName}: ${member.enumValue.replace(/"/g, '')},\n`; + } else if (member.enumKind === SyntaxKind.StringLiteral) { + enumBody += `${member.enumValueName}: ${member.enumValue},\n`; + } else { + if (member.enumValue === '' || member.enumValue === null || member.enumValue === undefined) { + enumBody += `${member.enumValueName}: ${defaultValue},\n`; + defaultValue++; + } else { + enumBody += `${member.enumValueName}: ${member.enumValue},\n`; + } + } + }); + enumBody += '}\n'; + return enumBody; +} diff --git a/mock-generate/src/generate/generateImportEqual.ts b/mock-generate/src/generate/generateImportEqual.ts new file mode 100644 index 00000000..b47cc100 --- /dev/null +++ b/mock-generate/src/generate/generateImportEqual.ts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 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 { ImportEuqalEntity } from '../declaration-node/importAndExportDeclaration'; + +/** + * generate import equal node info + * @param importEqualEntity + * @returns + */ +export function generateImportEqual(importEqualEntity: ImportEuqalEntity): string { + if (importEqualEntity.importEqualTypeName === 'connection.NetAddress') { + return `const ${importEqualEntity.importEqualName} = mockConnection().NetAddress`; + } else { + return `const ${importEqualEntity.importEqualName} = ${importEqualEntity.importEqualTypeName}`; + } +} diff --git a/mock-generate/src/generate/generateIndex.ts b/mock-generate/src/generate/generateIndex.ts new file mode 100644 index 00000000..7b0fca69 --- /dev/null +++ b/mock-generate/src/generate/generateIndex.ts @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2022 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 { firstCharacterToUppercase } from "../common/commonUtils"; + +/** + * save all mock function + */ +const indexArray: Array = []; + +export function addToIndexArray(indexEntity: IndexEntity) { + indexArray.push(indexEntity); +} + +export function getIndexArray(): Array { + return indexArray; +} + +/** + * generate index + * @returns + */ +export function generateIndex(): string { + let indexBody = ''; + let caseBody = ''; + const filterSet: Set = new Set(); + + indexArray.forEach(value => { + let functionName = value.mockFunctionName; + let isHasSameValue = false; + if (filterSet.has(value.mockFunctionName)) { + isHasSameValue = true; + const tmpArr = value.fileName.split('_'); + let tmpName = tmpArr[0]; + for (let i = 1; i < tmpArr.length; i++) { + tmpName += firstCharacterToUppercase(tmpArr[i]); + } + functionName = `${tmpName}`; + } + filterSet.add(functionName); + if (isHasSameValue) { + indexBody += `import { ${value.mockFunctionName} as ${functionName} } from './${value.fileName}';\n`; + } else { + indexBody += `import { ${functionName} } from './${value.fileName}';\n`; + } + + if (value.fileName.startsWith('ohos_')) { + caseBody += `case '${value.fileName.split('ohos_')[1].replace('_', '.')}':\n\treturn ${functionName}();\n`; + } else { + caseBody += `case '${value.fileName}':\n\treturn ${functionName}();\n`; + } + }); + + indexBody += `export function mockRequireNapiFun() { + global.requireNapi = function (...args) { + const globalNapi = global.requireNapiPreview(...args); + if (globalNapi !== undefined) { + return globalNapi; + } + switch (args[0]) {`; + indexBody += caseBody; + const endBody = `} + } + }`; + indexBody += endBody; + return indexBody; +} + +interface IndexEntity { + fileName: string, + mockFunctionName: string +} diff --git a/mock-generate/src/generate/generateIndexSignature.ts b/mock-generate/src/generate/generateIndexSignature.ts new file mode 100644 index 00000000..6582e265 --- /dev/null +++ b/mock-generate/src/generate/generateIndexSignature.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 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 { SyntaxKind } from 'typescript'; +import { IndexSignatureEntity } from '../declaration-node/indexSignatureDeclaration'; + +/** + * generate index signature + * @param signatureEntity + * @returns + */ +export function generateIndexSignature(signatureEntity: IndexSignatureEntity): string { + const signatureKey = signatureEntity.indexSignatureKey; + let signatureTypeName = ''; + if (signatureEntity.indexSignatureKind === SyntaxKind.TypeReference) { + signatureTypeName = signatureEntity.indexSignatureTypeName; + } else { + signatureTypeName = `'[PC Preview] unkonwn type',\n`; + } + return `${signatureKey}: ${signatureTypeName}`; +} diff --git a/mock-generate/src/generate/generateInterfaceDeclaration.ts b/mock-generate/src/generate/generateInterfaceDeclaration.ts new file mode 100644 index 00000000..05c1cbc8 --- /dev/null +++ b/mock-generate/src/generate/generateInterfaceDeclaration.ts @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2022 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 { SourceFile } from 'typescript'; +import { InterfaceEntity } from '../declaration-node/interfaceDeclaration'; +import { generateCommonMethodSignature } from './generateCommonMethodSignature'; +import { generateIndexSignature } from './generateIndexSignature'; +import { generatePropertySignatureDeclaration } from './generatePropertySignatureDeclaration'; + +/** + * generate interface + * @param rootName + * @param interfaceEntity + * @param sourceFile + * @param isSourceFile + * @returns + */ +export function generateInterfaceDeclaration(rootName: string, interfaceEntity: InterfaceEntity, sourceFile: SourceFile, isSourceFile: boolean, + currentSourceInterfaceArray: InterfaceEntity[]): string { + const interfaceName = interfaceEntity.interfaceName; + let interfaceBody = ''; + const interfaceElementSet = new Set(); + if (interfaceEntity.exportModifiers.length > 0 || isSourceFile) { + interfaceBody += `export const ${interfaceName} = { \n`; + } else { + interfaceBody += `const ${interfaceName} = { \n`; + } + + if (interfaceEntity.interfacePropertySignatures.length > 0) { + interfaceEntity.interfacePropertySignatures.forEach(value => { + interfaceBody += generatePropertySignatureDeclaration(interfaceName, value, sourceFile) + '\n'; + interfaceElementSet.add(value.propertyName); + }); + } + + if (interfaceEntity.interfaceMethodSignature.size > 0) { + interfaceEntity.interfaceMethodSignature.forEach(value => { + interfaceBody += generateCommonMethodSignature(interfaceName, value, sourceFile) + '\n'; + interfaceElementSet.add(value[0].functionName); + }); + } + + if (interfaceEntity.indexSignature.length > 0) { + interfaceEntity.indexSignature.forEach(value => { + interfaceBody += generateIndexSignature(value) + '\n'; + interfaceElementSet.add(value.indexSignatureKey); + }); + } + + if (interfaceEntity.heritageClauses.length > 0) { + interfaceEntity.heritageClauses.forEach(value => { + currentSourceInterfaceArray.forEach(currentInterface => { + if (value.types.includes(currentInterface.interfaceName)) { + interfaceBody += generateHeritageInterface(currentInterface, sourceFile, interfaceElementSet); + } + }); + }); + } + + interfaceBody += '}'; + return interfaceBody; +} + +function generateHeritageInterface(interfaceEntity: InterfaceEntity, sourceFile: SourceFile, elements: Set): string { + const interfaceName = interfaceEntity.interfaceName; + let interfaceBody = ''; + if (interfaceEntity.interfacePropertySignatures.length > 0) { + interfaceEntity.interfacePropertySignatures.forEach(value => { + if (!elements.has(value.propertyName)) { + interfaceBody += generatePropertySignatureDeclaration(interfaceName, value, sourceFile) + '\n'; + } + }); + } + + if (interfaceEntity.interfaceMethodSignature.size > 0) { + interfaceEntity.interfaceMethodSignature.forEach(value => { + if (!elements.has(value[0].functionName)) { + interfaceBody += generateCommonMethodSignature(interfaceName, value, sourceFile) + '\n'; + } + }); + } + + if (interfaceEntity.indexSignature.length > 0) { + interfaceEntity.indexSignature.forEach(value => { + if (elements.has(value.indexSignatureKey)) { + interfaceBody += generateIndexSignature(value) + '\n'; + } + }); + } + return interfaceBody; +} diff --git a/mock-generate/src/generate/generateMockJsFile.ts b/mock-generate/src/generate/generateMockJsFile.ts new file mode 100644 index 00000000..f3775f59 --- /dev/null +++ b/mock-generate/src/generate/generateMockJsFile.ts @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2022 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 { SourceFile, SyntaxKind } from 'typescript'; +import { collectAllLegalImports, firstCharacterToUppercase, getAllFileNameList } from '../common/commonUtils'; +import { ImportElementEntity } from '../declaration-node/importAndExportDeclaration'; +import { getDefaultExportClassDeclaration, getSourceFileFunctions, getSourceFileVariableStatements, SourceFileEntity } from '../declaration-node/sourceFileElementsAssemply'; +import { generateClassDeclaration } from './generateClassDeclaration'; +import { generateCommonFunction } from './generateCommonFunction'; +import { generateEnumDeclaration } from './generateEnumDeclaration'; +import { addToIndexArray } from './generateIndex'; +import { generateInterfaceDeclaration } from './generateInterfaceDeclaration'; +import { generateModuleDeclaration } from './generateModuleDeclaration'; +import { generateStaticFunction } from './generateStaticFunction'; +import { addToSystemIndexArray } from './generateSystemIndex'; +import { generateTypeAliasDeclaration } from './generateTypeAlias'; +import { generateVariableStatementDelcatation } from './generateVariableStatementDeclaration'; + +/** + * generate mock file string + * @param rootName + * @param sourceFileEntity + * @param sourceFile + * @param fileName + * @returns + */ +export function generateSourceFileElements(rootName: string, sourceFileEntity: SourceFileEntity, sourceFile: SourceFile, fileName: string): string { + let mockApi = ''; + const mockFunctionElements: Array = []; + const heritageClausesArray = getCurrentApiHeritageArray(sourceFileEntity, sourceFile); + if (sourceFileEntity.importDeclarations.length > 0) { + sourceFileEntity.importDeclarations.forEach(value => { + mockApi += generateImportDeclaration(value, fileName, heritageClausesArray); + }); + } + + if (sourceFileEntity.moduleDeclarations.length > 0) { + sourceFileEntity.moduleDeclarations.forEach(value => { + mockApi += generateModuleDeclaration('', value, sourceFile, fileName) + '\n'; + }); + } + + if (sourceFileEntity.classDeclarations.length > 0) { + sourceFileEntity.classDeclarations.forEach(value => { + if (!fileName.startsWith('system_') && !value.exportModifiers.includes(SyntaxKind.DefaultKeyword)) { + mockApi += generateClassDeclaration('', value, false, '', fileName, sourceFile, false) + '\n'; + mockFunctionElements.push({ elementName: value.className, type: 'class' }); + } + }); + } + + if (sourceFileEntity.interfaceDeclarations.length > 0) { + sourceFileEntity.interfaceDeclarations.forEach(value => { + mockApi += generateInterfaceDeclaration('', value, sourceFile, true, sourceFileEntity.interfaceDeclarations) + '\n'; + mockFunctionElements.push({ elementName: value.interfaceName, type: 'interface' }); + }); + } + + if (sourceFileEntity.enumDeclarations.length > 0) { + sourceFileEntity.enumDeclarations.forEach(value => { + mockApi += generateEnumDeclaration('', value) + '\n'; + mockFunctionElements.push({ elementName: value.enumName, type: 'enum' }); + }); + } + + if (sourceFileEntity.typeAliasDeclarations.length > 0) { + sourceFileEntity.typeAliasDeclarations.forEach(value => { + mockApi += generateTypeAliasDeclaration(value, false) + '\n'; + mockFunctionElements.push({ elementName: value.typeAliasName, type: 'typeAlias' }); + }); + } + + if (sourceFileEntity.moduleDeclarations.length === 0 && (fileName.startsWith('ohos_') || fileName.startsWith('system_') || fileName.startsWith('webgl'))) { + const mockNameArr = fileName.split('_'); + const mockName = mockNameArr[mockNameArr.length - 1]; + const defaultExportClass = getDefaultExportClassDeclaration(sourceFile); + if (defaultExportClass.length > 0) { + defaultExportClass.forEach(value => { + mockApi += generateClassDeclaration(rootName, value, false, mockName, '', sourceFile, false) + '\n'; + mockFunctionElements.push({ elementName: value.className, type: 'class' }); + }); + } + mockApi += `export function mock${firstCharacterToUppercase(mockName)}() {\n`; + if (fileName.startsWith('system_')) { + addToSystemIndexArray({ + filename: fileName, + mockFunctionName: `mock${firstCharacterToUppercase(mockName)}` + }); + mockApi += `global.systemplugin.${mockName} = {`; + const defaultClass = getDefaultExportClassDeclaration(sourceFile); + let staticMethodBody = ''; + if (defaultClass.length > 0) { + defaultClass.forEach(value => { + value.staticMethods.forEach(val => { + staticMethodBody += generateStaticFunction(val, true, sourceFile); + }); + }); + } + mockApi += staticMethodBody; + mockApi += '}'; + } else { + if (!fileName.startsWith('webgl')) { + addToIndexArray({ fileName: fileName, mockFunctionName: `mock${firstCharacterToUppercase(mockName)}` }); + } + } + mockApi += `\nconst mockModule${firstCharacterToUppercase(mockName)} = {`; + mockFunctionElements.forEach(val => { + mockApi += `${val.elementName}: ${val.elementName},`; + }); + mockApi += '}\n'; + mockApi += `return mockModule${firstCharacterToUppercase(mockName)}\n`; + mockApi += '}'; + } else { + const defaultExportClass = getDefaultExportClassDeclaration(sourceFile); + if (defaultExportClass.length > 0) { + const mockNameArr = fileName.split('_'); + const mockName = mockNameArr[mockNameArr.length - 1]; + defaultExportClass.forEach(value => { + mockApi += generateClassDeclaration(rootName, value, false, mockName, '', sourceFile, false) + '\n'; + }); + } + } + if (sourceFileEntity.exportDeclarations.length > 0) { + sourceFileEntity.exportDeclarations.forEach(value => { + if (!value.includes('export {')) { + mockApi += `${value}\n`; + } + }); + } + return mockApi; +} + +/** + * generate import definition + * @param importEntity + * @param sourceFileName + * @returns + */ +export function generateImportDeclaration(importEntity: ImportElementEntity, sourceFileName: string, heritageClausesArray: string[]): string { + let importPathName = ''; + const importPathSplit = importEntity.importPath.split('/'); + let fileName = importPathSplit[importPathSplit.length - 1]; + if (fileName.endsWith('.d.ts')) { + fileName = fileName.split('.d.')[0]; + } + if (fileName.includes('@')) { + importPathName = fileName.replace('@', '').replace(/\./g, '_'); + } else { + importPathName = fileName.replace(/\./g, '_'); + } + let importPath = ''; + for (let i = 0; i < importPathSplit.length - 1; i++) { + importPath += importPathSplit[i] + '/'; + } + importPath += importPathName; + let importElements = importEntity.importElements; + if (!importElements.includes('{') && !importElements.includes('* as') && !heritageClausesArray.includes(importElements)) { + if (importEntity.importPath.includes('@ohos')) { + const tmpArr = importEntity.importPath.split('.'); + importElements = `{ mock${firstCharacterToUppercase(tmpArr[tmpArr.length - 1].replace('"', '').replace('\'', ''))} }`; + } else { + importElements = `{ ${importElements} }`; + } + } + if (checIsDefaultExportClass(importEntity.importElements)) { + importElements = `{ ${importEntity.importElements} }`; + } + const testPath = importPath.replace(/"/g, '').replace(/'/g, '').split('/'); + if (getAllFileNameList().has(testPath[testPath.length - 1]) || testPath[testPath.length - 1] === 'ohos_application_want') { + const tmpImportPath = importPath.replace(/'/g, '').replace(/"/g, ''); + if (!tmpImportPath.startsWith('./') && !tmpImportPath.startsWith('../')) { + importPath = `'./${tmpImportPath}'`; + } + if (sourceFileName === 'tagSession' && importPath === `'./basic'` || sourceFileName === 'notificationContent' && importPath === `'./ohos_multimedia_image'`) { + importPath = `'.${importPath.replace(/'/g, '')}'`; + } + + // adapt no rules .d.ts + if (importElements.trimRight().trimEnd() === 'AccessibilityExtensionContext, { AccessibilityElement }') { + importElements = '{ AccessibilityExtensionContext, AccessibilityElement }'; + } + if (importElements.trimRight().trimEnd() === '{ image }') { + importElements = '{ mockImage as image }'; + } + if (sourceFileName === 'AbilityContext' && importPath === `'../ohos_application_Ability'` || + sourceFileName === 'Context' && importPath === `"./ApplicationContext"`) { + return ''; + } + collectAllLegalImports(importElements); + return `import ${importElements} from ${importPath}\n`; + } else { + return ''; + } +} + +/** + * adapter default export + * @param importName + * @returns + */ + function checIsDefaultExportClass(importName: string): boolean { + const defaultExportClass = ['Context', 'BaseContext', 'ExtensionContext', 'ApplicationContext']; + return defaultExportClass.includes(importName); +} + +/** + * get heritage elements + * @param sourceFileEntity + * @param sourceFile + * @returns + */ +function getCurrentApiHeritageArray(sourceFileEntity: SourceFileEntity, sourceFile: SourceFile): string[] { + const heritageClausesArray = []; + const defaultClassArray = getDefaultExportClassDeclaration(sourceFile); + sourceFileEntity.classDeclarations.forEach(value => { + value.heritageClauses.forEach(val => { + val.types.forEach(v => { + heritageClausesArray.push(v); + }); + }); + }); + defaultClassArray.forEach(value => { + value.heritageClauses.forEach(val => { + val.types.forEach(v => { + heritageClausesArray.push(v); + }); + }); + }); + return heritageClausesArray; +} + +interface MockFunctionElementEntity { + elementName: string, + type: string +} diff --git a/mock-generate/src/generate/generateModuleDeclaration.ts b/mock-generate/src/generate/generateModuleDeclaration.ts new file mode 100644 index 00000000..8c5f9289 --- /dev/null +++ b/mock-generate/src/generate/generateModuleDeclaration.ts @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2022 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 { SourceFile, SyntaxKind } from 'typescript'; +import { firstCharacterToUppercase } from '../common/commonUtils'; +import { ModuleBlockEntity } from '../declaration-node/moduleDeclaration'; +import { + getDefaultExportClassDeclaration, getSourceFileFunctions, + getSourceFileVariableStatements +} from '../declaration-node/sourceFileElementsAssemply'; +import { generateClassDeclaration } from './generateClassDeclaration'; +import { generateCommonFunction } from './generateCommonFunction'; +import { generateEnumDeclaration } from './generateEnumDeclaration'; +import { generateImportEqual } from './generateImportEqual'; +import { addToIndexArray } from './generateIndex'; +import { generateInterfaceDeclaration } from './generateInterfaceDeclaration'; +import { generateStaticFunction } from './generateStaticFunction'; +import { addToSystemIndexArray } from './generateSystemIndex'; +import { generateTypeAliasDeclaration } from './generateTypeAlias'; +import { generateVariableStatementDelcatation } from './generateVariableStatementDeclaration'; + +/** + * generate declare + * @param rootName + * @param moduleEntity + * @param sourceFile + * @param filename + * @returns + */ +export function generateModuleDeclaration(rootName: string, moduleEntity: ModuleBlockEntity, sourceFile: SourceFile, filename: string): string { + const moduleName = moduleEntity.moduleName; + const mockNameArr = filename.split('_'); + const mockName = mockNameArr[mockNameArr.length - 1]; + let moduleBody = `export function mock${firstCharacterToUppercase(mockName)}() {\n`; + addToIndexArray({ fileName: filename, mockFunctionName: `mock${firstCharacterToUppercase(mockName)}` }); + let outBody = ''; + const defaultExportClass = getDefaultExportClassDeclaration(sourceFile); + + if (defaultExportClass.length > 0) { + defaultExportClass.forEach(value => { + if (value.exportModifiers.includes(SyntaxKind.DefaultKeyword) && value.exportModifiers.includes(SyntaxKind.ExportKeyword)) { + if (filename.startsWith('system_')) { + const mockNameArr = filename.split('_'); + const mockName = mockNameArr[mockNameArr.length - 1]; + addToSystemIndexArray({ + filename: filename, + mockFunctionName: `mock${firstCharacterToUppercase(mockName)}` + }); + + moduleBody += `global.systemplugin.${mockName} = {`; + if (value.staticMethods.length > 0) { + let staticMethodBody = ''; + value.staticMethods.forEach(val => { + staticMethodBody += generateStaticFunction(val, true, sourceFile) + '\n'; + }); + moduleBody += staticMethodBody; + } + moduleBody += '}'; + } else { + outBody += generateClassDeclaration('', value, false, '', filename, sourceFile, false); + } + } + }); + } + + if (moduleEntity.typeAliasDeclarations.length > 0) { + moduleEntity.typeAliasDeclarations.forEach(value => { + outBody += generateTypeAliasDeclaration(value, true) + '\n'; + }); + } + + if (moduleEntity.moduleImportEquaqls.length > 0) { + moduleEntity.moduleImportEquaqls.forEach(value => { + outBody += generateImportEqual(value) + '\n'; + }); + } + + if (moduleEntity.classDeclarations.length > 0) { + moduleEntity.classDeclarations.forEach(value => { + if (value.exportModifiers.length > 0 && value.exportModifiers.includes(SyntaxKind.ExportKeyword)) { + outBody += generateClassDeclaration(moduleName, value, false, '', '', sourceFile, false) + '\n'; + } else { + moduleBody += '\t' + generateClassDeclaration(moduleName, value, false, '', '', sourceFile, true) + '\n'; + } + }); + } + + if (moduleEntity.interfaceDeclarations.length > 0) { + moduleEntity.interfaceDeclarations.forEach(value => { + if (value.exportModifiers.length > 0) { + outBody += generateInterfaceDeclaration(moduleName, value, sourceFile, false, moduleEntity.interfaceDeclarations) + ';\n'; + } else { + moduleBody += '\t' + generateInterfaceDeclaration(moduleName, value, sourceFile, false, moduleEntity.interfaceDeclarations) + ';\n'; + } + }); + } + + if (moduleEntity.enumDeclarations.length > 0) { + moduleEntity.enumDeclarations.forEach(value => { + if (value.exportModifiers.length > 0) { + outBody += generateEnumDeclaration(moduleName, value) + '\n'; + } else { + moduleBody += '\t' + generateEnumDeclaration(moduleName, value) + '\n'; + } + }); + } + + if (moduleEntity.moduleDeclarations.length > 0) { + moduleEntity.moduleDeclarations.forEach(value => { + moduleBody += generateInnerModule(value, sourceFile) + '\n'; + }); + } + + let functionBody = ''; + if (moduleEntity.functionDeclarations.size > 0) { + moduleEntity.functionDeclarations.forEach(value => { + functionBody += '\t' + generateCommonFunction(moduleName, value, sourceFile) + '\n'; + }); + } + + moduleBody += '\t' + `const ${moduleName} = {`; + if (moduleEntity.variableStatements.length > 0) { + moduleEntity.variableStatements.forEach(value => { + value.forEach(val => { + moduleBody += generateVariableStatementDelcatation(val) + '\n'; + }); + }); + } + + const sourceFileFunctions = getSourceFileFunctions(sourceFile); + let sourceFileFunctionBody = ''; + if (sourceFileFunctions.size > 0) { + sourceFileFunctions.forEach(value => { + sourceFileFunctionBody += generateCommonFunction(moduleName, value, sourceFile); + }); + } + + const sourceFileVariableStatements = getSourceFileVariableStatements(sourceFile); + let sourceFileStatementBody = ''; + if (sourceFileVariableStatements.length > 0) { + sourceFileVariableStatements.forEach(value => { + value.forEach(val => { + sourceFileStatementBody += generateVariableStatementDelcatation(val); + }); + }); + } + + moduleBody += sourceFileFunctionBody + '\n'; + moduleBody += sourceFileStatementBody + '\n'; + moduleBody += functionBody + '\n'; + + const exports = getModuleExportElements(moduleEntity); + let exportString = ''; + exports.forEach(value => { + exportString += `${value.name}: ${value.name},\n`; + }); + if (exportString !== '') { + moduleBody += '\t' + exportString; + } + + moduleBody += '\t};'; + moduleBody += `\n\treturn ${moduleName};}\n`; + moduleBody += outBody; + return moduleBody; +} + +/** + * generate inner module + * @param moduleEntity + * @param sourceFile + * @returns + */ +function generateInnerModule(moduleEntity: ModuleBlockEntity, sourceFile: SourceFile): string { + const moduleName = moduleEntity.moduleName; + let innerModuleBody = `const ${moduleName} = {`; + + if (moduleEntity.typeAliasDeclarations.length > 0) { + moduleEntity.typeAliasDeclarations.forEach(value => { + innerModuleBody += generateTypeAliasDeclaration(value, true) + '\n'; + }); + } + + if (moduleEntity.moduleImportEquaqls.length > 0) { + moduleEntity.moduleImportEquaqls.forEach(value => { + innerModuleBody += generateImportEqual(value) + '\n'; + }); + } + + if (moduleEntity.interfaceDeclarations.length > 0) { + moduleEntity.interfaceDeclarations.forEach(value => { + innerModuleBody += generateInterfaceDeclaration(moduleName, value, sourceFile, false, moduleEntity.interfaceDeclarations) + '\n'; + }); + } + + if (moduleEntity.enumDeclarations.length > 0) { + moduleEntity.enumDeclarations.forEach(value => { + innerModuleBody += generateEnumDeclaration(moduleName, value) + '\n'; + }); + } + + let functionBody = ''; + if (moduleEntity.functionDeclarations.size > 0) { + moduleEntity.functionDeclarations.forEach(value => { + functionBody += generateCommonFunction(moduleName, value, sourceFile) + '\n'; + }); + } + + if (moduleEntity.variableStatements.length > 0) { + moduleEntity.variableStatements.forEach(value => { + value.forEach(val => { + innerModuleBody += generateVariableStatementDelcatation(val) + '\n'; + }); + }); + } + innerModuleBody += functionBody + '\n'; + + const exports = getModuleExportElements(moduleEntity); + let exportString = ''; + exports.forEach(value => { + exportString += `${value.name}: ${value.name},\n`; + }); + if (exportString !== '') { + innerModuleBody += '\t' + exportString; + } + innerModuleBody += '\t};'; + return innerModuleBody; +} + +/** + * get all export elements + * @param moduleEntity + * @returns + */ +function getModuleExportElements(moduleEntity: ModuleBlockEntity): Array { + const exportElements: Array = []; + if (moduleEntity.classDeclarations.length > 0) { + moduleEntity.classDeclarations.forEach(value => { + exportElements.push({ name: firstCharacterToUppercase(value.className), type: 'class' }); + }); + } + + if (moduleEntity.interfaceDeclarations.length > 0) { + moduleEntity.interfaceDeclarations.forEach(value => { + exportElements.push({ name: value.interfaceName, type: 'interface' }); + }); + } + + if (moduleEntity.enumDeclarations.length > 0) { + moduleEntity.enumDeclarations.forEach(value => { + exportElements.push({ name: value.enumName, type: 'enum' }); + }); + } + + if (moduleEntity.moduleDeclarations.length > 0) { + moduleEntity.moduleDeclarations.forEach(value => { + exportElements.push({ name: value.moduleName, type: 'module' }); + }); + } + + if (moduleEntity.typeAliasDeclarations.length > 0) { + moduleEntity.typeAliasDeclarations.forEach(value => { + exportElements.push({ name: value.typeAliasName, type: 'type' }); + }); + } + return exportElements; +} + +interface ModuleExportEntity { + type: string, + name: string +} diff --git a/mock-generate/src/generate/generatePropertyDeclaration.ts b/mock-generate/src/generate/generatePropertyDeclaration.ts new file mode 100644 index 00000000..12e56923 --- /dev/null +++ b/mock-generate/src/generate/generatePropertyDeclaration.ts @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 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 { SourceFile, SyntaxKind } from 'typescript'; +import { getClassNameSet } from '../common/commonUtils'; +import { PropertyEntity } from '../declaration-node/propertyDeclaration'; +import { getTheRealReferenceFromImport } from './generateCommonUtil'; + +/** + * generate class property + * @param rootName + * @param propertyDeclaration + * @param sourceFile + * @returns + */ +export function generatePropertyDeclaration(rootName: string, propertyDeclaration: PropertyEntity, sourceFile: SourceFile): string { + let propertyBody = ''; + if (propertyDeclaration.isInitializer) { + propertyBody = `this.${propertyDeclaration.propertyName} = ${propertyDeclaration.initializer};`; + } else { + if (propertyDeclaration.propertyTypeName.startsWith('{')) { + propertyBody = `this.${propertyDeclaration.propertyName} = {};`; + } else if (propertyDeclaration.kind === SyntaxKind.LiteralType) { + propertyBody = `this.${propertyDeclaration.propertyName} = ${propertyDeclaration.propertyTypeName};`; + } else if (propertyDeclaration.kind === SyntaxKind.NumberKeyword) { + propertyBody = `this.${propertyDeclaration.propertyName} = 0;`; + } else if (propertyDeclaration.kind === SyntaxKind.StringKeyword) { + propertyBody = `this.${propertyDeclaration.propertyName} = ''`; + } else if (propertyDeclaration.kind === SyntaxKind.BooleanKeyword) { + propertyBody = `this.${propertyDeclaration.propertyName} = true`; + } else if (propertyDeclaration.propertyTypeName.startsWith('Array')) { + propertyBody = `this.${propertyDeclaration.propertyName} = [];`; + } else if (propertyDeclaration.propertyTypeName.startsWith('Map')) { + propertyBody = `this.${propertyDeclaration.propertyName} = {key: {}};`; + } else if (propertyDeclaration.kind === SyntaxKind.TypeReference) { + propertyBody = `this.${propertyDeclaration.propertyName} = `; + if (getClassNameSet().has(propertyDeclaration.propertyTypeName)) { + if (propertyDeclaration.propertyTypeName !== 'Want' && propertyDeclaration.propertyTypeName !== 'InputMethodExtensionContext') { + propertyBody += `new ${getTheRealReferenceFromImport(sourceFile, propertyDeclaration.propertyTypeName)}();`; + } else { + propertyBody += `${getTheRealReferenceFromImport(sourceFile, propertyDeclaration.propertyTypeName)};`; + } + } else { + propertyBody += `${getTheRealReferenceFromImport(sourceFile, propertyDeclaration.propertyTypeName)};`; + } + } else if (propertyDeclaration.kind === SyntaxKind.NumericLiteral || propertyDeclaration.kind === SyntaxKind.StringLiteral) { + propertyBody = `this.${propertyDeclaration.propertyName} = ${propertyDeclaration.propertyTypeName};`; + } else { + propertyBody = `this.${propertyDeclaration.propertyName} = '[PC Previwe] unkonwn ${propertyDeclaration.propertyName}';`; + } + } + return propertyBody; +} diff --git a/mock-generate/src/generate/generatePropertySignatureDeclaration.ts b/mock-generate/src/generate/generatePropertySignatureDeclaration.ts new file mode 100644 index 00000000..c69fefba --- /dev/null +++ b/mock-generate/src/generate/generatePropertySignatureDeclaration.ts @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2022 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 { SourceFile, SyntaxKind } from 'typescript'; +import { PropertySignatureEntity } from '../declaration-node/propertySignatureDeclaration'; +import { + checkIsGenericSymbol, getCallbackStatement, getTheRealReferenceFromImport, + getWarnConsole, propertyTypeWhiteList +} from './generateCommonUtil'; + +/** + * generate interface signature property + * @param rootName + * @param propertySignature + * @param sourceFile + * @returns + */ +export function generatePropertySignatureDeclaration(rootName: string, propertySignature: PropertySignatureEntity, sourceFile: SourceFile): string { + let propertySignatureBody = ''; + if (propertySignature.kind === SyntaxKind.FunctionType) { + propertySignatureBody += `${propertySignature.propertyName}: function(...args) {`; + propertySignatureBody += getWarnConsole(rootName, propertySignature.propertyName); + propertySignatureBody += getCallbackStatement(); + propertySignatureBody += '},\n'; + } else { + if (propertySignature.propertyTypeName.startsWith('{')) { + propertySignatureBody = `${propertySignature.propertyName}: {},`; + } else if (propertySignature.kind === SyntaxKind.TypeReference) { + if (propertySignature.propertyTypeName.startsWith('Array')) { + propertySignatureBody = `${propertySignature.propertyName}: [],`; + } else if (propertySignature.propertyTypeName.startsWith('Map')) { + propertySignatureBody = `${propertySignature.propertyName}: {key: {}},`; + } else if (propertySignature.propertyTypeName === 'string' || checkIsGenericSymbol(propertySignature.propertyTypeName) || + propertySignature.propertyTypeName === 'bool' || propertySignature.propertyTypeName === 'Data') { + propertySignatureBody = `${propertySignature.propertyName}: '[PC Preview] unkonwn ${propertySignature.propertyName}',`; + } else { + if (propertySignature.propertyTypeName.includes('<')) { + const preSplit = propertySignature.propertyTypeName.split('<'); + const genericArg = preSplit[preSplit.length - 1].split('>')[0]; + propertySignatureBody = `${propertySignature.propertyName}: ${genericArg},`; + } else { + if (propertyTypeWhiteList(propertySignature.propertyTypeName) === propertySignature.propertyTypeName) { + propertySignatureBody = `${propertySignature.propertyName}: ${getTheRealReferenceFromImport(sourceFile, propertySignature.propertyTypeName)},`; + } else { + propertySignatureBody = `${propertySignature.propertyName}: ${propertyTypeWhiteList(propertySignature.propertyTypeName)},`; + } + } + } + } else if (propertySignature.kind === SyntaxKind.NumberKeyword) { + propertySignatureBody = `${propertySignature.propertyName}: 0,`; + } else if (propertySignature.kind === SyntaxKind.StringKeyword) { + propertySignatureBody = `${propertySignature.propertyName}: '[PC Preview] unkonwn ${propertySignature.propertyName}',`; + } else if (propertySignature.kind === SyntaxKind.BooleanKeyword) { + propertySignatureBody = `${propertySignature.propertyName}: true,`; + } else if (propertySignature.kind === SyntaxKind.UnionType) { + const unionFirstElement = propertySignature.propertyTypeName.split('|')[0].trimStart().trimEnd(); + if (unionFirstElement.startsWith('"')) { + propertySignatureBody = `${propertySignature.propertyName}: ${unionFirstElement},`; + } else if (unionFirstElement === 'string') { + propertySignatureBody = `${propertySignature.propertyName}: '[PC Preview] unkonwn ${propertySignature.propertyName}',`; + } else if (unionFirstElement === 'number') { + propertySignatureBody = `${propertySignature.propertyName}: 0,`; + } else if (unionFirstElement === 'boolean') { + propertySignatureBody = `${propertySignature.propertyName}: true,`; + } else if (unionFirstElement === 'Uint8Array') { + propertySignatureBody = `${propertySignature.propertyName}: new ${unionFirstElement}(),`; + } else { + let element = unionFirstElement; + if (element === 'HTMLCanvasElement') { + element = `'[PC Preview] unkonwn ${propertySignature.propertyName}'`; + } else if (element === 'WebGLActiveInfo') { + element = `{size: '[PC Preview] unkonwn GLint', type: 0, name: '[PC Preview] unkonwn name'}`; + } else if (element === 'accessibility.EventType') { + element = `mockAccessibility().EventType`; + } + propertySignatureBody = `${propertySignature.propertyName}: ${element},`; + } + } else if (propertySignature.kind === SyntaxKind.ArrayType) { + propertySignatureBody = `${propertySignature.propertyName}: [],`; + } else { + propertySignatureBody = `${propertySignature.propertyName}: '[PC Preview] unkonwn ${propertySignature.propertyName}',`; + } + } + return propertySignatureBody; +} diff --git a/mock-generate/src/generate/generateStaticFunction.ts b/mock-generate/src/generate/generateStaticFunction.ts new file mode 100644 index 00000000..fda79e06 --- /dev/null +++ b/mock-generate/src/generate/generateStaticFunction.ts @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 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 { SourceFile, SyntaxKind } from 'typescript'; +import { firstCharacterToUppercase } from '../common/commonUtils'; +import { StaticMethodEntity } from '../declaration-node/methodDeclaration'; +import { generateSymbolIterator, getCallbackStatement, getReturnStatement, getWarnConsole } from './generateCommonUtil'; + +/** + * generate static method + * @param staticMethod + * @param isSystem + * @param sourceFile + * @returns + */ +export function generateStaticFunction(staticMethod: StaticMethodEntity, isSystem: boolean, sourceFile: SourceFile): string { + let methodBody = ''; + const rootName = staticMethod.className; + const methodEntity = staticMethod.methodEntity; + if (isSystem) { + methodBody += `${methodEntity.functionName.name}: function(...args) {`; + } else { + methodBody += `${firstCharacterToUppercase(staticMethod.className)}.${methodEntity.functionName.name} = function(...args) {`; + } + + methodBody += getWarnConsole(rootName, methodEntity.functionName.name); + if (methodEntity.functionName.name === 'Symbol.iterator') { + methodBody += generateSymbolIterator(methodEntity); + methodBody += '}'; + return methodBody; + } + + const args = methodEntity.args; + const len = args.length; + if (args.length > 0 && args[len - 1].paramName === 'callback') { + methodBody += getCallbackStatement(); + } + + if (methodEntity.returnType.returnKind !== SyntaxKind.VoidKeyword) { + methodBody += getReturnStatement(methodEntity.returnType, sourceFile); + } + methodBody += '}'; + + if (isSystem) { + methodBody += ','; + } else { + methodBody += ';'; + } + return methodBody; +} diff --git a/mock-generate/src/generate/generateSystemIndex.ts b/mock-generate/src/generate/generateSystemIndex.ts new file mode 100644 index 00000000..335b9fca --- /dev/null +++ b/mock-generate/src/generate/generateSystemIndex.ts @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 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. + */ + +const systemIndexArray: Array = []; + +export function addToSystemIndexArray(systemIndexEntity: SystemIndexEntity) { + systemIndexArray.push(systemIndexEntity); +} + +export function getSystemIndexArray(): Array { + return systemIndexArray; +} + +/** + * generate startswith 'system_' + * @returns + */ +export function generateSystemIndex(): string { + let systemIndex = `import regeneratorRuntime from 'babel-runtime/regenerator'\n`; + let exportFunction = ''; + systemIndexArray.forEach(value => { + systemIndex += `import { ${value.mockFunctionName} } from './${value.filename}'\n`; + exportFunction += `${value.mockFunctionName}();\n`; + }); + systemIndex += `import {mockRequireNapiFun} from './napi/index';\n`; + systemIndex += `export function mockSystemPlugin() { + global.regeneratorRuntime = regeneratorRuntime + global.systemplugin = {} + global.ohosplugin = {}\n`; + systemIndex += exportFunction; + systemIndex += `mockRequireNapiFun();\n`; + systemIndex += '}'; + return systemIndex; +} + +export interface SystemIndexEntity { + filename: string, + mockFunctionName: string +} diff --git a/mock-generate/src/generate/generateTypeAlias.ts b/mock-generate/src/generate/generateTypeAlias.ts new file mode 100644 index 00000000..96c987c7 --- /dev/null +++ b/mock-generate/src/generate/generateTypeAlias.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 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 { TypeAliasEntity } from '../declaration-node/typeAliasDeclaration'; + +/** + * generate type alias + * @param typeAliasEntity + * @param isInner + * @returns + */ +export function generateTypeAliasDeclaration(typeAliasEntity: TypeAliasEntity, isInner: boolean): string { + let typeAliasName = ''; + if (!isInner) { + typeAliasName += `export const ${typeAliasEntity.typeAliasName} = `; + } else { + typeAliasName += `const ${typeAliasEntity.typeAliasName} = `; + } + let typeAliasValue = ''; + typeAliasValue += `'[PC Preview] unkonwn ${typeAliasEntity.typeAliasName}'`; + return typeAliasName + typeAliasValue + ';'; +} diff --git a/mock-generate/src/generate/generateVariableStatementDeclaration.ts b/mock-generate/src/generate/generateVariableStatementDeclaration.ts new file mode 100644 index 00000000..9c0db497 --- /dev/null +++ b/mock-generate/src/generate/generateVariableStatementDeclaration.ts @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2022 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 { SyntaxKind } from 'typescript'; +import { getClassNameSet } from '../common/commonUtils'; +import { StatementEntity } from '../declaration-node/variableStatementResolve'; + +/** + * generate const variable statement + * @param statementEntity + * @returns + */ +export function generateVariableStatementDelcatation(statementEntity: StatementEntity): string { + let statementBody = `${statementEntity.statementName}: `; + let statementValue; + if (statementEntity.typeKind === SyntaxKind.StringKeyword) { + statementValue = `''`; + } else if (statementEntity.typeKind === SyntaxKind.LiteralType || statementEntity.typeKind === SyntaxKind.StringLiteral || + statementEntity.typeKind === SyntaxKind.NumericLiteral) { + if (statementEntity.initializer === '') { + if (statementEntity.typeName.endsWith('n')) { + statementValue = statementEntity.typeName.replace('n', ''); + } else { + statementValue = statementEntity.typeName; + } + } else { + statementValue = statementEntity.initializer; + } + } else if (statementEntity.typeKind === SyntaxKind.NumberKeyword) { + statementValue = 0; + } else if (statementEntity.typeKind === SyntaxKind.UnionType) { + statementValue = statementEntity.typeName.split('|')[0]; + } else if (statementEntity.typeKind === SyntaxKind.TypeReference) { + if (statementEntity.typeName.includes('<')) { + const tmpTypeName = statementEntity.typeName.split('<')[0]; + if (getClassNameSet().has(tmpTypeName)) { + statementValue = `new ${tmpTypeName}()`; + } else { + statementValue = `${tmpTypeName}`; + } + } else { + statementValue = statementEntity.typeName; + } + } else if (statementEntity.typeKind === SyntaxKind.BooleanKeyword) { + statementValue = 'true'; + } else if (statementEntity.initializer !== '') { + statementValue = statementEntity.initializer.endsWith('n'); + } else { + statementValue = `'[PC Preivew] unknown ${statementEntity.statementName}'`; + } + statementBody += statementValue; + statementBody += ','; + return statementBody; +} diff --git a/mock-generate/src/main.ts b/mock-generate/src/main.ts new file mode 100644 index 00000000..14a9fc71 --- /dev/null +++ b/mock-generate/src/main.ts @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2022 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 fs from 'fs'; +import path from 'path'; +import os from 'os'; +import { createSourceFile, ScriptTarget } from 'typescript'; +import { collectAllFileName, getAllClassDeclaration } from './common/commonUtils'; +import { getSourceFileAssembly } from './declaration-node/sourceFileElementsAssemply'; +import { generateEntry } from './generate/generateEntry'; +import { generateIndex } from './generate/generateIndex'; +import { generateSourceFileElements } from './generate/generateMockJsFile'; +import { generateSystemIndex } from './generate/generateSystemIndex'; + +const dtsFileList: Array = []; + +/** + * get all api .d.ts file path + * @param dir + * @returns + */ +function getAllDtsFile(dir: string): Array { + const arr = fs.readdirSync(dir); + if (!dir.toString().includes('node_modules') && !dir.toString().includes('/@internal/component/')) { + arr.forEach(value => { + const fullPath = path.join(dir, value); + const stats = fs.statSync(fullPath); + if (stats.isDirectory()) { + getAllDtsFile(fullPath); + } else { + dtsFileList.push(fullPath); + } + }); + } + return dtsFileList; +} + +/** + * delete the old mock file befor generate new mock file + * @param outDir + */ + function deleteOldMockJsFile(outDir: string) { + const arr = fs.readdirSync(outDir); + arr.forEach(value => { + const currPath = path.join(outDir, value); + const stas = fs.statSync(currPath); + if (stas.isDirectory()) { + deleteOldMockJsFile(currPath); + } else { + fs.unlink(currPath, function(err) { + if (err) { + console.log(err); + } + }); + } + }); +} + +/** + * mkdir + * @param dirname + * @returns + */ +function mkdirsSync(dirname) { + if (fs.existsSync(dirname)) { + return true; + } else { + if (mkdirsSync(path.dirname(dirname))) { + fs.mkdirSync(dirname); + return true; + } + } +} + +function main() { + let interfaceRootDir = ''; + if (os.platform() === 'linux' || os.platform() === 'darwin') { + interfaceRootDir = __dirname.split('/out')[0]; + } else { + interfaceRootDir = __dirname.split('\\out')[0]; + } + const dtsDir = path.join(interfaceRootDir, './interface/sdk-js/api'); + const outMockJsFileDir = path.join(__dirname, '../../runtime/main/extend/systemplugin'); + deleteOldMockJsFile(outMockJsFileDir); + getAllDtsFile(dtsDir); + + dtsFileList.forEach(value => { + collectAllFileName(value); + if (value.endsWith('.d.ts')) { + const code = fs.readFileSync(value); + const sourceFile = createSourceFile('', code.toString(), ScriptTarget.Latest); + getAllClassDeclaration(sourceFile); + } + }); + + dtsFileList.forEach(value => { + if (value.endsWith('.d.ts')) { + const code = fs.readFileSync(value); + const sourceFile = createSourceFile('', code.toString(), ScriptTarget.Latest); + const fileName = path.basename(value).split('.d.ts')[0]; + let outputFileName = ''; + if (fileName.includes('@')) { + outputFileName = fileName.split('@')[1].replace(/\./g, '_'); + } else { + outputFileName = fileName; + } + + let tmpOutputMockJsFileDir = outMockJsFileDir; + if (!outputFileName.startsWith('system_')) { + tmpOutputMockJsFileDir = path.join(outMockJsFileDir, 'napi'); + } + let dirName = ''; + if (os.platform() === 'linux' || os.platform() === 'darwin') { + dirName = path.join(tmpOutputMockJsFileDir, path.dirname(value).split('/api')[1]); + } else { + dirName = path.join(tmpOutputMockJsFileDir, path.dirname(value).split('\\api')[1]); + } + if (!fs.existsSync(dirName)) { + mkdirsSync(dirName); + } + const sourceFileEntity = getSourceFileAssembly(sourceFile, fileName); + const filePath = path.join(dirName, outputFileName + '.js'); + fs.writeFileSync(filePath, ''); + fs.appendFileSync(path.join(filePath), generateSourceFileElements('', sourceFileEntity, sourceFile, outputFileName)); + } + }); + if (!fs.existsSync(path.join(outMockJsFileDir, 'napi'))) { + mkdirsSync(path.join(outMockJsFileDir, 'napi')); + } + fs.writeFileSync(path.join(outMockJsFileDir, 'napi', 'index.js'), generateIndex()); + fs.writeFileSync(path.join(outMockJsFileDir, 'index.js'), generateSystemIndex()); + fs.writeFileSync(path.join(outMockJsFileDir, 'entry.js'), generateEntry()); +} + +main(); diff --git a/mock-generate/tsconfig.json b/mock-generate/tsconfig.json new file mode 100644 index 00000000..459b2605 --- /dev/null +++ b/mock-generate/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "allowJs": true, + "moduleResolution": "node", + "module": "commonjs", + "target": "ES2019", + "outDir": "dist", + "esModuleInterop": true, + "types": [ + "node" + ], + "typeRoots": [ + "./node_modules/@types" + ] + }, + "include": [ + "./src/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/package.json b/package.json index b978e480..00308395 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "scripts": { "test:lint": "eslint -c .eslintrc --fix test/**/*.ts", "test:unit": "mocha --timeout 5000 -r ts-node/register test/lib.ts test/ut/**/*.ts test/ut/*.ts", - "build": "node build_jsmock_system_plugin.js && node build_strip_native_min.js" + "build:mock": "cd ./mock-generate && npm run build", + "build": "npm run build:mock && node build_jsmock_system_plugin.js && node build_strip_native_min.js" }, "keywords": [ "javascript", diff --git a/runtime/main/model/selector.ts b/runtime/main/model/selector.ts index 2290bb19..cde3e3d0 100644 --- a/runtime/main/model/selector.ts +++ b/runtime/main/model/selector.ts @@ -19,7 +19,7 @@ import { Log } from '../../utils/index'; import { watch } from './directive'; -import { parse, Selector } from '../../css-what/src/index'; +import { parse, Selector } from '../../css-what/src/index'; import Element from '../../vdom/Element'; import Vm from './index'; import { cssType } from './vmOptions'; diff --git a/runtime/vdom/Element.ts b/runtime/vdom/Element.ts index 6ca28f43..68083815 100644 --- a/runtime/vdom/Element.ts +++ b/runtime/vdom/Element.ts @@ -34,8 +34,8 @@ import { FragBlockInterface, import Vm from '../main/model'; import { CSS_INHERITANCE } from '../main/app/bundle'; import {interceptCallback} from '../main/manage/event/callbackIntercept'; -import {mockwebgl} from '../main/extend/systemplugin/napi/webgl'; -import {mockwebgl2} from '../main/extend/systemplugin/napi/webgl2'; +import {mockWebgl} from '../main/extend/systemplugin/napi/webgl/webgl'; +import {mockWebgl2} from '../main/extend/systemplugin/napi/webgl/webgl2'; import { VmOptions } from '../main/model/vmOptions'; /** * Element is a basic class to describe a tree node in vdom. @@ -84,9 +84,9 @@ class Element extends Node { // support aceapp callback style args = interceptCallback(args); if (args[0] === 'webgl') { - return mockwebgl(); + return mockWebgl(); } else if (args[0] === 'webgl2') { - return mockwebgl2(); + return mockWebgl2(); } const ret = taskCenter.send('component', { ref: this.ref,