提交 9854d2cc 编写于 作者: fxy060608's avatar fxy060608

fix(uts): 支持class属性赋值

上级 465b4101
......@@ -106,11 +106,13 @@ describe('uts-module', () => {
},
props: ['count'],
staticProps: ['staticCount'],
setters: { count: { name: 'count', type: 'number' } },
staticSetters: { staticCount: { name: 'staticCount', type: 'number' } },
})
const wifi = new WifiManager()
wifi.preparePermission(1, 2, 3, () => {})
wifi.count
WifiManager.staticCount
wifi.count = 1
WifiManager.staticCount = 2
WifiManager.staticPreparePermission(1)
const errMsg = 'xx插件编译失败,无法使用'
......
......@@ -56,7 +56,7 @@ function initUTSInstanceMethod(
instanceId: number,
proxy: unknown
) {
return initProxyFunction(async, opts, instanceId, proxy)
return initProxyFunction('method', async, opts, instanceId, proxy)
}
interface Parameter {
......@@ -117,6 +117,8 @@ interface ProxyInterfaceOptions extends ModuleOptions {
package: string
class: string
props: string[]
setters: Record<string, Parameter>
staticSetters: Record<string, Parameter>
methods: {
[name: string]: {
async?: boolean
......@@ -138,6 +140,8 @@ interface ProxyClassOptions extends ModuleOptions {
}
props: string[]
staticProps: string[]
setters: Record<string, Parameter>
staticSetters: Record<string, Parameter>
methods: {
[name: string]: {
async?: boolean
......@@ -160,8 +164,21 @@ interface ProxyClassOptions extends ModuleOptions {
interface InvokeInstanceArgs extends ModuleOptions {
id: number
/**
* 属性名或方法名
*/
name: string
/**
* 属性|方法
*/
type: 'property' | 'method'
/**
* 执行方法时的真实参数列表
*/
params?: unknown[]
/**
* 方法定义的参数列表
*/
method?: Parameter[]
/**
* 运行时提示的错误信息
......@@ -181,6 +198,10 @@ interface InvokeStaticArgs extends ModuleOptions {
* 属性名或方法名
*/
name: string
/**
* 属性|方法
*/
type: 'property' | 'method'
/**
* 执行方法时的真实参数列表
*/
......@@ -318,13 +339,14 @@ function invokePropGetter(args: InvokeArgs) {
}
function initProxyFunction(
type: 'method' | 'property',
async: boolean,
{
moduleName,
moduleType,
package: pkg,
class: cls,
name: propOrMethod,
name: methodName,
method,
companion,
params: methodParams,
......@@ -347,7 +369,7 @@ function initProxyFunction(
delete callbacks[id]
}
} else {
console.error(`${pkg}${cls}.${propOrMethod} ${name} is not found`)
console.error(`${pkg}${cls}.${methodName} ${name} is not found`)
}
}
const baseArgs: InvokeArgs = instanceId
......@@ -355,7 +377,8 @@ function initProxyFunction(
moduleName,
moduleType,
id: instanceId,
name: propOrMethod,
type,
name: methodName,
method: methodParams,
}
: {
......@@ -363,7 +386,8 @@ function initProxyFunction(
moduleType,
package: pkg,
class: cls,
name: method || propOrMethod,
name: method || methodName,
type,
companion,
method: methodParams,
}
......@@ -414,7 +438,7 @@ function initUTSStaticMethod(async: boolean, opts: ProxyFunctionOptions) {
opts.method = 's_' + opts.name
}
}
return initProxyFunction(async, opts, 0)
return initProxyFunction('method', async, opts, 0)
}
export const initUTSProxyFunction = initUTSStaticMethod
......@@ -439,6 +463,10 @@ function isProxyInterfaceOptions(
return !isUndefined((options as any).instanceId)
}
function parseClassPropertySetter(name: string) {
return '__$set' + capitalize(name)
}
export function initUTSProxyClass(
options: ProxyClassOptions | ProxyInterfaceOptions
): any {
......@@ -449,6 +477,7 @@ export function initUTSProxyClass(
class: cls,
methods,
props,
setters,
errMsg,
} = options
......@@ -464,6 +493,7 @@ export function initUTSProxyClass(
let constructorParams: Parameter[] = []
let staticMethods: ProxyClassOptions['staticMethods'] = {}
let staticProps: ProxyClassOptions['staticProps'] = []
let staticSetters: ProxyClassOptions['staticSetters'] = {}
let isProxyInterface = false
if (isProxyInterfaceOptions(options)) {
......@@ -473,6 +503,7 @@ export function initUTSProxyClass(
constructorParams = options.constructor.params
staticMethods = options.staticMethods
staticProps = options.staticProps
staticSetters = options.staticSetters
}
// iOS 需要为 ByJs 的 class 构造函数(如果包含JSONObject或UTSCallback类型)补充最后一个参数
......@@ -496,6 +527,7 @@ export function initUTSProxyClass(
if (!isProxyInterface) {
// 初始化未指定时,每次都要创建instanceId
this.__instanceId = initProxyFunction(
'method',
false,
extend(
{ name: 'constructor', params: constructorParams },
......@@ -540,6 +572,7 @@ export function initUTSProxyClass(
moduleName,
moduleType,
id: instance.__instanceId,
type: 'property',
name: name as string,
errMsg,
})
......@@ -547,10 +580,37 @@ export function initUTSProxyClass(
}
return target[name as string]
},
set(_, name, newValue) {
if (props.includes(name as string)) {
const setter = parseClassPropertySetter(name as string)
if (!target[setter]) {
const param = setters[name as string]
if (param) {
target[setter] = initProxyFunction(
'property',
false,
extend(
{
name: name as string,
params: [param],
},
baseOptions
),
instance.__instanceId,
proxy
)
}
}
target[parseClassPropertySetter(name as string)](newValue)
return true
}
return false
},
})
return proxy
}
}
const staticPropSetterCache: Record<string, Function> = {}
const staticMethodCache: Record<string, Function> = {}
return new Proxy(ProxyClass, {
get(target, name, receiver) {
......@@ -562,7 +622,12 @@ export function initUTSProxyClass(
staticMethodCache[name] = initUTSStaticMethod(
!!async,
extend(
{ name, companion: true, params, return: returnOptions },
{
name,
companion: true,
params,
return: returnOptions,
},
baseOptions
)
)
......@@ -570,13 +635,47 @@ export function initUTSProxyClass(
return staticMethodCache[name]
}
if (staticProps.includes(name as string)) {
// 静态属性
return invokePropGetter(
extend({ name: name as string, companion: true }, baseOptions)
extend(
{
name: name as string,
companion: true,
type: 'property' as const,
},
baseOptions
)
)
}
return Reflect.get(target, name, receiver)
},
set(_, name, newValue) {
if (staticProps.includes(name as string)) {
// 静态属性
const setter = parseClassPropertySetter(name as string)
if (!staticPropSetterCache[setter]) {
const param = staticSetters[name as string]
if (param) {
staticPropSetterCache[setter] = initProxyFunction(
'property',
false,
extend(
{
name: name as string,
params: [param],
},
baseOptions
),
0
)
}
}
staticPropSetterCache[parseClassPropertySetter(name as string)](
newValue
)
return true
}
return false
},
})
}
......
const path = require('path')
const {
findEncryptUniModules,
packUploadEncryptUniModules,
} = require('../dist/uni_modules')
console.log(
packUploadEncryptUniModules(
findEncryptUniModules(
path.resolve(__dirname, '../../playground/uni_modules/src')
),
'app-android',
path.resolve(__dirname, '../../playground/uni_modules/src'),
path.resolve(__dirname)
)
)
......@@ -21,7 +21,7 @@ describe('uni_modules:uni-ext-api', () => {
findEncryptUniModules(inputDir),
platform,
inputDir
)
).map((item) => item.replace(inputDir, '').slice(1))
).toMatchSnapshot()
})
})
......
......@@ -482,7 +482,7 @@ export function findUploadEncryptUniModulesFiles(
return files
}
function packUploadEncryptUniModules(
export function packUploadEncryptUniModules(
uniModules: Record<string, EncryptPackageJson | undefined>,
platform: typeof process.env.UNI_UTS_PLATFORM,
inputDir: string,
......@@ -494,7 +494,7 @@ function packUploadEncryptUniModules(
const AdmZip = require('adm-zip')
const zip = new AdmZip()
files.forEach((file) => {
zip.addLocalFile(file)
zip.addLocalFile(file, path.dirname(path.relative(inputDir, file)))
})
const zipFile = path.resolve(cacheDir, 'uni_modules.upload.zip')
zip.writeZip(zipFile)
......@@ -594,6 +594,7 @@ function findUniModuleFiles(
) {
return sync(`uni_modules/${id}/**/*`, {
cwd: inputDir,
absolute: true,
ignore: [
'**/*.md',
...(platform !== 'app-android' // 非 android 平台不需要扫描 assets
......
......@@ -13,10 +13,10 @@ const cls = /*#__PURE__*/ initUTSIndexClassName(name, is_uni_modules)
export const TestUTSComponent = {}
registerUTSInterface('RequestTaskOptions',Object.assign({ moduleName, moduleType, errMsg, package: pkg, class: initUTSClassName(name, 'RequestTaskByJsProxy', is_uni_modules) }, {"methods":{"startByJs":{"async":false,"params":[],"return":{}},"abortByJs":{"async":false,"params":[],"return":{"type":"interface","options":"RequestTaskOptions"}}},"props":[]} ))
registerUTSInterface('RequestTaskOptions',Object.assign({ moduleName, moduleType, errMsg, package: pkg, class: initUTSClassName(name, 'RequestTaskByJsProxy', is_uni_modules) }, {"methods":{"startByJs":{"async":false,"params":[],"return":{}},"abortByJs":{"async":false,"params":[],"return":{"type":"interface","options":"RequestTaskOptions"}}},"props":[],"setters":{}} ))
export const onMemoryWarning = /*#__PURE__*/ initUTSProxyFunction(false, { moduleName, moduleType, errMsg, main: true, package: pkg, class: cls, name: 'onMemoryWarningByJs', params: [{"name":"callback","type":"UTSCallback"}], return: ""})
export const offMemoryWarning = /*#__PURE__*/ initUTSProxyFunction(false, { moduleName, moduleType, errMsg, main: true, package: pkg, class: cls, name: 'offMemoryWarningByJs', params: [{"name":"callback","type":"UTSCallback","default":"UTSNull"}], return: ""})
export default /*#__PURE__*/ initUTSProxyClass(Object.assign({ moduleName, moduleType, errMsg, package: pkg, class: initUTSClassName(name, 'UserByJs', is_uni_modules) }, {"constructor":{"params":[]},"methods":{"registerByJs":{"async":false,"params":[],"returnOptions":{}},"loginByJs":{"async":false,"params":[],"returnOptions":{}}},"staticMethods":{},"props":[],"staticProps":[]} ))
export default /*#__PURE__*/ initUTSProxyClass(Object.assign({ moduleName, moduleType, errMsg, package: pkg, class: initUTSClassName(name, 'UserByJs', is_uni_modules) }, {"constructor":{"params":[]},"methods":{"registerByJs":{"async":false,"params":[],"returnOptions":{}},"loginByJs":{"async":false,"params":[],"returnOptions":{}}},"staticMethods":{},"props":[],"staticProps":[],"setters":{},"staticSetters":{}} ))
export const a = 1
export const showToast1 = /*#__PURE__*/ initUTSProxyFunction(false, { moduleName, moduleType, errMsg, main: true, package: pkg, class: cls, name: 'showToast1ByJs', params: [{"name":"msg","type":"string"}], return: ""})
export const showToast2 = /*#__PURE__*/ initUTSProxyFunction(false, { moduleName, moduleType, errMsg, main: true, package: pkg, class: cls, name: 'showToast2ByJs', params: [{"name":"msg","type":"string"}], return: ""})
......@@ -327,10 +327,10 @@ const exports = { __esModule: true }
exports.TestUTSComponent = {}
registerUTSInterface('RequestTaskOptions',Object.assign({ moduleName, moduleType, errMsg, package: pkg, class: initUTSClassName(name, 'RequestTaskByJsProxy', is_uni_modules) }, {"methods":{"startByJs":{"async":false,"params":[],"return":{}},"abortByJs":{"async":false,"params":[],"return":{"type":"interface","options":"RequestTaskOptions"}}},"props":[]} ))
registerUTSInterface('RequestTaskOptions',Object.assign({ moduleName, moduleType, errMsg, package: pkg, class: initUTSClassName(name, 'RequestTaskByJsProxy', is_uni_modules) }, {"methods":{"startByJs":{"async":false,"params":[],"return":{}},"abortByJs":{"async":false,"params":[],"return":{"type":"interface","options":"RequestTaskOptions"}}},"props":[],"setters":{}} ))
exports.onMemoryWarning = /*#__PURE__*/ initUTSProxyFunction(false, { moduleName, moduleType, errMsg, main: true, package: pkg, class: cls, name: 'onMemoryWarningByJs', params: [{"name":"callback","type":"UTSCallback"}], return: ""})
exports.offMemoryWarning = /*#__PURE__*/ initUTSProxyFunction(false, { moduleName, moduleType, errMsg, main: true, package: pkg, class: cls, name: 'offMemoryWarningByJs', params: [{"name":"callback","type":"UTSCallback","default":"UTSNull"}], return: ""})
exports.default = initUTSProxyClass(Object.assign({ moduleName, moduleType, errMsg, package: pkg, class: initUTSClassName(name, 'UserByJs', is_uni_modules) }, {"constructor":{"params":[]},"methods":{"registerByJs":{"async":false,"params":[],"returnOptions":{}},"loginByJs":{"async":false,"params":[],"returnOptions":{}}},"staticMethods":{},"props":[],"staticProps":[]} ))
exports.default = initUTSProxyClass(Object.assign({ moduleName, moduleType, errMsg, package: pkg, class: initUTSClassName(name, 'UserByJs', is_uni_modules) }, {"constructor":{"params":[]},"methods":{"registerByJs":{"async":false,"params":[],"returnOptions":{}},"loginByJs":{"async":false,"params":[],"returnOptions":{}}},"staticMethods":{},"props":[],"staticProps":[],"setters":{},"staticSetters":{}} ))
const a = 1
exports.a = a
exports.showToast1 = /*#__PURE__*/ initUTSProxyFunction(false, { moduleName, moduleType, errMsg, main: true, package: pkg, class: cls, name: 'showToast1ByJs', params: [{"name":"msg","type":"string"}], return: ""})
......@@ -343,3 +343,21 @@ exports.addUsers4 = /*#__PURE__*/ initUTSProxyFunction(false, { moduleName, modu
uni.registerUTSPlugin('utssdk/test-uts', exports)
"
`;
exports[`code genProxyCode uni_modules 1`] = `
"
const { registerUTSInterface, initUTSProxyClass, initUTSProxyFunction, initUTSPackageName, initUTSIndexClassName, initUTSClassName } = uni
const name = 'test-uts'
const moduleName = ''
const moduleType = ''
const errMsg = \`\`
const is_uni_modules = true
const pkg = /*#__PURE__*/ initUTSPackageName(name, is_uni_modules)
const cls = /*#__PURE__*/ initUTSIndexClassName(name, is_uni_modules)
registerUTSInterface('ITestOptions',Object.assign({ moduleName, moduleType, errMsg, package: pkg, class: initUTSClassName(name, 'ITestByJsProxy', is_uni_modules) }, {"methods":{},"props":["name"],"setters":{"name":{"name":"name","type":"string"}}} ))
export const createTest = /*#__PURE__*/ initUTSProxyFunction(false, { moduleName, moduleType, errMsg, main: true, package: pkg, class: cls, name: 'createTestByJs', params: [], return: {"type":"interface","options":"ITestOptions"}})
"
`;
......@@ -4,6 +4,7 @@ import { ERR_MSG_PLACEHOLDER } from '../src/utils'
const inputDir = resolve(__dirname, 'examples/uts')
const pluginDir = resolve(__dirname, 'examples/uts/utssdk/test-uts')
const uniModuleDir = resolve(__dirname, 'examples/uts/uni_modules/test-uts')
describe('code', () => {
test('genProxyCode', async () => {
......@@ -40,4 +41,20 @@ describe('code', () => {
).replace(ERR_MSG_PLACEHOLDER, '')
).toMatchSnapshot()
})
test('genProxyCode uni_modules', async () => {
expect(
(
await genProxyCode(uniModuleDir, {
id: 'test-uts',
is_uni_modules: true,
name: 'test-uts',
namespace: 'uts.sdk.testUTS',
extname: '.uts',
androidComponents: {},
inputDir,
})
).replace(ERR_MSG_PLACEHOLDER, '')
).toMatchSnapshot()
})
})
interface ITest {
name: string
}
class Test implements ITest {
private _name: string = ''
get name(): string {
return this._name
}
set name(value: string) {
this._name = value
}
}
export function createTest(): ITest {
return new Test()
}
interface ITest {
name: string
}
class Test implements ITest {
private _name: string = ''
get name(): string {
return this._name
}
set name(value: string) {
this._name = value
}
}
export function createTest(): ITest {
return new Test()
}
......@@ -5,7 +5,7 @@
},
"files": {
"utssdk/app-android/index.uts": {
"md5": "d41d8cd98f00b204e9800998ecf8427e"
"md5": "bbffc9c64c25154cbc65f849a9f403f4"
},
"utssdk/common/utils.uts": {
"md5": "d41d8cd98f00b204e9800998ecf8427e"
......
......@@ -5,7 +5,7 @@
},
"files": {
"utssdk/app-ios/index.uts": {
"md5": "d41d8cd98f00b204e9800998ecf8427e"
"md5": "bbffc9c64c25154cbc65f849a9f403f4"
},
"utssdk/common/utils.uts": {
"md5": "d41d8cd98f00b204e9800998ecf8427e"
......
......@@ -699,6 +699,7 @@ interface ProxyInterface {
options: {
methods: Record<string, any>
props: string[]
setters: Record<string, Parameter>
}
}
......@@ -724,6 +725,8 @@ interface ProxyClass {
staticMethods: Record<string, any>
props: string[]
staticProps: string[]
setters: Record<string, Parameter>
staticSetters: Record<string, Parameter>
}
isDefault: boolean
isVar: boolean
......@@ -857,6 +860,8 @@ function genProxyClass(
staticMethods: Record<string, any>
props: string[]
staticProps: string[]
setters: Record<string, Parameter>
staticSetters: Record<string, Parameter>
},
isDefault = false,
isVar = false,
......@@ -1060,6 +1065,7 @@ function genInterfaceDeclaration(
const cls = decl.id.value
const methods: ProxyClass['options']['methods'] = {}
const props: string[] = []
const setters: Record<string, Parameter> = {}
decl.body.body.forEach((item) => {
if (item.type === 'TsMethodSignature') {
if (item.key.type === 'Identifier') {
......@@ -1092,6 +1098,18 @@ function genInterfaceDeclaration(
} else if (item.type === 'TsPropertySignature') {
if (item.key.type === 'Identifier') {
props.push(item.key.value)
if (item.typeAnnotation) {
const params = resolveFunctionParams(
types,
tsParamsToParams([
createBindingIdentifier(item.key.value, item.typeAnnotation),
]),
resolveTypeReferenceName
)
if (params.length) {
setters[item.key.value] = params[0]
}
}
}
}
})
......@@ -1101,6 +1119,7 @@ function genInterfaceDeclaration(
options: {
methods,
props,
setters,
},
}
}
......@@ -1131,6 +1150,8 @@ function genClassDeclaration(
const staticMethods: ProxyClass['options']['staticMethods'] = {}
const props: string[] = []
const staticProps: string[] = []
const setters: Record<string, Parameter> = {}
const staticSetters: Record<string, Parameter> = {}
const isHook = decl.implements.some(
(implement) =>
implement.expression.type === 'Identifier' &&
......@@ -1145,35 +1166,53 @@ function genClassDeclaration(
)
} else if (item.type === 'ClassMethod') {
if (item.key.type === 'Identifier') {
let returnOptions = {}
if (item.function.returnType) {
let returnInterface = parseReturnInterface(
types,
item.function.returnType.typeAnnotation
)
if (returnInterface) {
returnOptions = {
type: 'interface',
options: returnInterface,
if (item.kind === 'getter' || item.kind === 'setter') {
const curProps = item.isStatic ? staticProps : props
if (!curProps.includes(item.key.value)) {
curProps.push(item.key.value)
}
if (item.kind === 'setter') {
const params = resolveFunctionParams(
types,
item.function.params,
resolveTypeReferenceName
)
if (params.length) {
;(item.isStatic ? staticSetters : setters)[item.key.value] =
params[0]
}
}
}
const name = item.key.value
const value = {
async:
item.function.async || isReturnPromise(item.function.returnType),
params: resolveFunctionParams(
types,
item.function.params,
resolveTypeReferenceName
),
returnOptions,
}
if (item.isStatic) {
staticMethods[name + 'ByJs'] = value
} else {
methods[name + 'ByJs'] = value
let returnOptions = {}
if (item.function.returnType) {
let returnInterface = parseReturnInterface(
types,
item.function.returnType.typeAnnotation
)
if (returnInterface) {
returnOptions = {
type: 'interface',
options: returnInterface,
}
}
}
const name = item.key.value
const value = {
async:
item.function.async || isReturnPromise(item.function.returnType),
params: resolveFunctionParams(
types,
item.function.params,
resolveTypeReferenceName
),
returnOptions,
}
if (item.isStatic) {
staticMethods[name + 'ByJs'] = value
} else {
methods[name + 'ByJs'] = value
}
}
}
} else if (item.type === 'ClassProperty') {
......@@ -1183,12 +1222,33 @@ function genClassDeclaration(
} else {
props.push(item.key.value)
}
if (item.typeAnnotation) {
const params = resolveFunctionParams(
types,
tsParamsToParams([
createBindingIdentifier(item.key.value, item.typeAnnotation),
]),
resolveTypeReferenceName
)
if (params.length) {
;(item.isStatic ? staticSetters : setters)[item.key.value] =
params[0]
}
}
}
}
})
return genProxyClass(
cls,
{ constructor, methods, staticMethods, props, staticProps },
{
constructor,
methods,
staticMethods,
props,
staticProps,
setters,
staticSetters,
},
isDefault,
false,
isHook
......@@ -1264,15 +1324,18 @@ function genVariableDeclaration(
}
}
// function createBindingIdentifier(name: string, typeAnnotation?: TsTypeAnnotation): BindingIdentifier {
// return {
// type: 'Identifier',
// value: name,
// optional: false,
// span: {} as Span,
// typeAnnotation
// }
// }
function createBindingIdentifier(
name: string,
typeAnnotation?: TsTypeAnnotation
): BindingIdentifier {
return {
type: 'Identifier',
value: name,
optional: false,
span: {} as Span,
typeAnnotation,
}
}
function createIdentifier(name: string): Identifier {
return {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册