提交 45a8193d 编写于 作者: fxy060608's avatar fxy060608

wip(uts): compiler

上级 a41a8adf
import Log from 'android.util.Log'
import { login } from './login.uts'
export class User {
async login(name: string, pwd: string) {
login(name, pwd)
Log.info('123')
}
}
package uts.modules.testUniPlugin;
import kotlinx.coroutines.*;
import io.dcloud.uts.runtime.*;
import android.util.Log;
fun login(name: String, pwd: String): UtsJSONObject {
return object : UtsJSONObject() {
var name = name
......@@ -5,7 +9,8 @@ fun login(name: String, pwd: String): UtsJSONObject {
};
}
open class User {
open fun async login(name: String, pwd: String) {
open suspend fun login(name: String, pwd: String) = CoroutineScope(Dispatchers.Default).async {
login(name, pwd);
Log.info("123");
}
}
{"version":3,"sources":["/Users/fxy/Projects/GitHub/uni-app/uni-app-next/packages/playground/uts/uni_modules/test-uniplugin/app-android/login.uts","/Users/fxy/Projects/GitHub/uni-app/uni-app-next/packages/playground/uts/uni_modules/test-uniplugin/app-android/index.uts"],"sourcesContent":["export function login(name: string, pwd: string) {\n return { name, pwd }\n}\n","import { login } from './login.uts'\nexport class User {\n async login(name: string, pwd: string) {\n login(name, pwd)\n }\n}\n"],"names":[],"mappings":"AAAO,IAAS,KAAK,CAAC,IAAY,EAAN,MAAM,EAAE,GAAW,EAAN,MAAM,iBAAE;IAC/C,OAAO;QAAE,IAAA,IAAI,GAAJ,IAAI;QAAE,IAAA,GAAG,GAAH,GAAG;KAAE,CAAA;;ACAf,WAAM,IAAI;IACf,eAAM,KAAK,CAAC,IAAY,EAAN,MAAM,EAAE,GAAW,EAAN,MAAM,EAAE;QACrC,MAAM,IAAI,EAAE,GAAG,CAAC;;CAEnB"}
\ No newline at end of file
{"version":3,"sources":["/Users/fxy/Projects/GitHub/uni-app/uni-app-next/packages/playground/uts/uni_modules/test-uniplugin/app-android/index.uts","/Users/fxy/Projects/GitHub/uni-app/uni-app-next/packages/playground/uts/uni_modules/test-uniplugin/app-android/login.uts"],"sourcesContent":["import Log from 'android.util.Log'\nimport { login } from './login.uts'\nexport class User {\n async login(name: string, pwd: string) {\n login(name, pwd)\n Log.info('123')\n }\n}\n","export function login(name: string, pwd: string) {\n return { name, pwd }\n}\n"],"names":[],"mappings":";;;AAAA,OAAgB,gBAAkB,CAAA;ACA3B,IAAS,KAAK,CAAC,IAAY,EAAN,MAAM,EAAE,GAAW,EAAN,MAAM,iBAAE;IAC/C,OAAO;QAAE,IAAA,IAAI,GAAJ,IAAI;QAAE,IAAA,GAAG,GAAH,GAAG;KAAE,CAAA;;ADCf,WAAM,IAAI;IACf,iBAAM,KAAK,CAAC,IAAY,EAAN,MAAM,EAAE,GAAW,EAAN,MAAM,8CAAE;QACrC,MAAM,IAAI,EAAE,GAAG,CAAC;QAChB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC;;CAElB"}
\ No newline at end of file
......@@ -11453,6 +11453,7 @@ const ScanCodeProtocol = {
scanType: Array,
autoDecodeCharSet: Boolean,
sound: String,
autoZoom: Boolean,
};
const SOUND = ['default', 'none'];
const ScanCodeOptions = {
......@@ -11461,6 +11462,10 @@ const ScanCodeOptions = {
if (!SOUND.includes(value))
params.sound = 'none';
},
autoZoom(value, params) {
if (typeof value === 'undefined')
params.autoZoom = true;
},
},
};
......
......@@ -39,11 +39,10 @@ describe('uts-module', () => {
})
test(`initProxyFunction`, () => {
;[true, false].forEach((async) => {
const preparePermission = initUtsProxyFunction({
pkg: 'testPlugin',
cls: '',
method: 'preparePermission',
async,
const preparePermission = initUtsProxyFunction(async, {
package: 'uts.modules.TestPlugin',
class: 'TestKt',
name: 'preparePermission',
})
/**
* {"package":"testPlugin","class":"","method":"preparePermission","params":[{"name":"foo","age":10,"success":7,"fail":8},9]}
......@@ -71,13 +70,23 @@ describe('uts-module', () => {
})
test(`initProxyClass`, () => {
const WifiManager = initUtsProxyClass({
pkg: 'testPlugin',
cls: 'WifiManager',
package: 'uni.modules.TestPlugin',
class: 'WifiManager',
methods: {
preparePermission: {},
},
staticMethods: {
staticPreparePermission: {
async: true,
},
},
props: ['count'],
staticProps: ['staticCount'],
})
const wifi = new WifiManager()
wifi.preparePermission(1, 2, 3, () => {})
wifi.count
wifi.staticCount
wifi.staticPreparePermission(1)
})
})
......@@ -151,10 +151,19 @@ function normalizeArg(arg) {
}
return arg;
}
function isProxyInvokeCallbackResponse(res) {
return !!res.name;
function initUtsInstanceMethod(async, opts) {
return initProxyFunction(async, opts);
}
function initUtsProxyFunction({ pkg, cls, method, async, }) {
function getProxy() {
if (!proxy) {
proxy = uni.requireNativePlugin('UTS-Proxy');
}
return proxy;
}
function invokePropGetter(args) {
return getProxy().invokeSync(args, () => { });
}
function initProxyFunction(async, { package: pkg, class: cls, name: propOrMethod, id: instanceId, }) {
const invokeCallback = ({ id, name, params, keepAlive, }) => {
const callback = callbacks[id];
if (callback) {
......@@ -164,19 +173,24 @@ function initUtsProxyFunction({ pkg, cls, method, async, }) {
}
}
else {
console.error(`${pkg}${cls ? '.' + cls : ''}.${method} ${name} is not found`);
console.error(`${pkg}${cls}.${propOrMethod} ${name} is not found`);
}
};
const baseArgs = instanceId
? { id: instanceId, name: propOrMethod }
: {
package: pkg,
class: cls,
name: propOrMethod,
};
return (...args) => {
if (!proxy) {
proxy = uni.requireNativePlugin('ProxyModule');
}
const params = args.map((arg) => normalizeArg(arg));
const invokeArgs = { package: pkg, class: cls, method, params };
const invokeArgs = shared.extend({}, baseArgs, {
params: args.map((arg) => normalizeArg(arg)),
});
if (async) {
return new Promise((resolve, reject) => {
proxy.invokeAsync(invokeArgs, (res) => {
if (isProxyInvokeCallbackResponse(res)) {
getProxy().invokeAsync(invokeArgs, (res) => {
if (res.type !== 'return') {
invokeCallback(res);
}
else {
......@@ -190,26 +204,47 @@ function initUtsProxyFunction({ pkg, cls, method, async, }) {
});
});
}
return proxy.invokeSync(invokeArgs, invokeCallback);
return getProxy().invokeSync(invokeArgs, invokeCallback);
};
}
function initUtsProxyClass({ pkg, cls, methods, }) {
function initUtsStaticMethod(async, opts) {
return initProxyFunction(async, opts);
}
const initUtsProxyFunction = initUtsStaticMethod;
function initUtsProxyClass({ package: pkg, class: cls, methods, props, staticProps, staticMethods, }) {
const baseOptions = {
package: pkg,
class: cls,
};
return class ProxyClass {
constructor() {
constructor(...params) {
const target = {};
// 初始化实例 ID
const instanceId = initProxyFunction(false, shared.extend({ name: 'constructor', params }, baseOptions)).apply(null, params);
return new Proxy(this, {
get(_, method) {
if (!target[method]) {
if (shared.hasOwn(methods, method)) {
target[method] = initUtsProxyFunction({
pkg,
cls,
method,
async: methods[method].async,
});
get(_, name) {
if (!target[name]) {
//实例方法
if (shared.hasOwn(methods, name)) {
target[name] = initUtsInstanceMethod(!!methods[name].async, shared.extend({
id: instanceId,
name,
}, baseOptions));
}
else if (shared.hasOwn(staticMethods, name)) {
// 静态方法
target[name] = initUtsStaticMethod(!!staticMethods[name].async, shared.extend({ name }, baseOptions));
}
else if (props.includes(name)) {
// 实例属性
return invokePropGetter({ id: instanceId, name: name });
}
else if (staticProps.includes(name)) {
// 静态属性
return invokePropGetter(shared.extend({ name: name }, baseOptions));
}
return target[method];
}
return target[name];
},
});
}
......
......@@ -35,9 +35,11 @@ export declare function getCurrentSubNVue(): any;
export declare function getSsrGlobalData(): any;
export declare function initUtsProxyClass({ pkg, cls, methods, }: ProxyClassOptions): any;
export declare function initUtsProxyClass({ package: pkg, class: cls, methods, props, staticProps, staticMethods, }: ProxyClassOptions): any;
export declare function initUtsProxyFunction({ pkg, cls, method, async, }: ProxyFunctionOptions): (...args: unknown[]) => any;
export declare const initUtsProxyFunction: typeof initUtsStaticMethod;
declare function initUtsStaticMethod(async: boolean, opts: ProxyBaseOptions): (...args: unknown[]) => unknown;
declare type LaunchOption = LaunchShowOption;
......@@ -166,23 +168,35 @@ declare interface PageScrollOption {
}
declare interface ProxyBaseOptions {
pkg: string;
cls: string;
method: string;
/**
* 包名
*/
package: string;
/**
* 类名
*/
class: string;
/**
* 属性名或方法名
*/
name: string;
}
declare interface ProxyClassOptions {
pkg: string;
cls: string;
package: string;
class: string;
props: string[];
staticProps: string[];
methods: {
[name: string]: {
async?: boolean;
};
};
}
declare interface ProxyFunctionOptions extends ProxyBaseOptions {
async?: boolean;
staticMethods: {
[name: string]: {
async?: boolean;
};
};
}
declare interface ReferrerInfo {
......
import { shallowRef, ref, getCurrentInstance, isInSSRComponentSetup, injectHook } from 'vue';
import { hasOwn, isString, isPlainObject } from '@vue/shared';
import { hasOwn, isString, extend, isPlainObject } from '@vue/shared';
import { sanitise, UNI_SSR_DATA, UNI_SSR_GLOBAL_DATA, UNI_SSR, ON_SHOW, ON_HIDE, ON_LAUNCH, ON_ERROR, ON_THEME_CHANGE, ON_PAGE_NOT_FOUND, ON_UNHANDLE_REJECTION, ON_INIT, ON_LOAD, ON_READY, ON_UNLOAD, ON_RESIZE, ON_BACK_PRESS, ON_PAGE_SCROLL, ON_TAB_ITEM_TAP, ON_REACH_BOTTOM, ON_PULL_DOWN_REFRESH, ON_SAVE_EXIT_STATE, ON_SHARE_TIMELINE, ON_ADD_TO_FAVORITES, ON_SHARE_APP_MESSAGE, ON_NAVIGATION_BAR_BUTTON_TAP, ON_NAVIGATION_BAR_SEARCH_INPUT_CHANGED, ON_NAVIGATION_BAR_SEARCH_INPUT_CLICKED, ON_NAVIGATION_BAR_SEARCH_INPUT_CONFIRMED, ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED } from '@dcloudio/uni-shared';
function getSSRDataType() {
......@@ -119,10 +119,19 @@ function normalizeArg(arg) {
}
return arg;
}
function isProxyInvokeCallbackResponse(res) {
return !!res.name;
function initUtsInstanceMethod(async, opts) {
return initProxyFunction(async, opts);
}
function initUtsProxyFunction({ pkg, cls, method, async, }) {
function getProxy() {
if (!proxy) {
proxy = uni.requireNativePlugin('UTS-Proxy');
}
return proxy;
}
function invokePropGetter(args) {
return getProxy().invokeSync(args, () => { });
}
function initProxyFunction(async, { package: pkg, class: cls, name: propOrMethod, id: instanceId, }) {
const invokeCallback = ({ id, name, params, keepAlive, }) => {
const callback = callbacks[id];
if (callback) {
......@@ -132,19 +141,24 @@ function initUtsProxyFunction({ pkg, cls, method, async, }) {
}
}
else {
console.error(`${pkg}${cls ? '.' + cls : ''}.${method} ${name} is not found`);
console.error(`${pkg}${cls}.${propOrMethod} ${name} is not found`);
}
};
const baseArgs = instanceId
? { id: instanceId, name: propOrMethod }
: {
package: pkg,
class: cls,
name: propOrMethod,
};
return (...args) => {
if (!proxy) {
proxy = uni.requireNativePlugin('ProxyModule');
}
const params = args.map((arg) => normalizeArg(arg));
const invokeArgs = { package: pkg, class: cls, method, params };
const invokeArgs = extend({}, baseArgs, {
params: args.map((arg) => normalizeArg(arg)),
});
if (async) {
return new Promise((resolve, reject) => {
proxy.invokeAsync(invokeArgs, (res) => {
if (isProxyInvokeCallbackResponse(res)) {
getProxy().invokeAsync(invokeArgs, (res) => {
if (res.type !== 'return') {
invokeCallback(res);
}
else {
......@@ -158,26 +172,47 @@ function initUtsProxyFunction({ pkg, cls, method, async, }) {
});
});
}
return proxy.invokeSync(invokeArgs, invokeCallback);
return getProxy().invokeSync(invokeArgs, invokeCallback);
};
}
function initUtsProxyClass({ pkg, cls, methods, }) {
function initUtsStaticMethod(async, opts) {
return initProxyFunction(async, opts);
}
const initUtsProxyFunction = initUtsStaticMethod;
function initUtsProxyClass({ package: pkg, class: cls, methods, props, staticProps, staticMethods, }) {
const baseOptions = {
package: pkg,
class: cls,
};
return class ProxyClass {
constructor() {
constructor(...params) {
const target = {};
// 初始化实例 ID
const instanceId = initProxyFunction(false, extend({ name: 'constructor', params }, baseOptions)).apply(null, params);
return new Proxy(this, {
get(_, method) {
if (!target[method]) {
if (hasOwn(methods, method)) {
target[method] = initUtsProxyFunction({
pkg,
cls,
method,
async: methods[method].async,
});
get(_, name) {
if (!target[name]) {
//实例方法
if (hasOwn(methods, name)) {
target[name] = initUtsInstanceMethod(!!methods[name].async, extend({
id: instanceId,
name,
}, baseOptions));
}
else if (hasOwn(staticMethods, name)) {
// 静态方法
target[name] = initUtsStaticMethod(!!staticMethods[name].async, extend({ name }, baseOptions));
}
else if (props.includes(name)) {
// 实例属性
return invokePropGetter({ id: instanceId, name: name });
}
else if (staticProps.includes(name)) {
// 静态属性
return invokePropGetter(extend({ name: name }, baseOptions));
}
return target[method];
}
return target[name];
},
});
}
......
import { isPlainObject, hasOwn } from '@vue/shared'
import { isPlainObject, hasOwn, extend } from '@vue/shared'
declare const uni: any
let callbackId = 1
let proxy: any
......@@ -15,78 +15,154 @@ export function normalizeArg(arg: unknown) {
}
return arg
}
interface ProxyInvokeAsyncResponse {
errMsg: string
params: unknown
interface ProxyBaseOptions {
/**
* 包名
*/
package: string
/**
* 类名
*/
class: string
/**
* 属性名或方法名
*/
name: string
}
interface ProxyInstanceOptions extends ProxyBaseOptions {
id: number
}
/**
* 实例方法
*/
interface ProxyInstanceMethodOptions extends ProxyInstanceOptions {}
function initUtsInstanceMethod(
async: boolean,
opts: ProxyInstanceMethodOptions
) {
return initProxyFunction(async, opts)
}
interface ProxyClassOptions {
package: string
class: string
props: string[]
staticProps: string[]
methods: {
[name: string]: {
async?: boolean
}
}
staticMethods: {
[name: string]: {
async?: boolean
}
}
}
type InvokeInstanceArgs =
// prop
| { id: number; name: string }
// method
| { id: number; name: string; params: unknown[] }
type InvokeArgs = (ProxyBaseOptions | InvokeInstanceArgs) & {
params?: unknown[]
}
interface ProxyInvokeCallbackResponse {
interface InvokeCallbackReturnRes {
type: 'return'
params?: unknown[]
errMsg?: string
}
interface InvokeCallbackParamsRes {
type: 'params'
id: number
name: string
params: unknown[]
keepAlive: boolean
keepAlive?: boolean
}
type ProxyInvokeResponse =
| ProxyInvokeAsyncResponse
| ProxyInvokeCallbackResponse
function isProxyInvokeCallbackResponse(
res: ProxyInvokeResponse
): res is ProxyInvokeCallbackResponse {
return !!(res as ProxyInvokeCallbackResponse).name
}
type InvokeSyncCallback = (res: InvokeCallbackParamsRes) => void
type InvokeAsyncCallback = (
res: InvokeCallbackReturnRes | InvokeCallbackParamsRes
) => void
interface ProxyBaseOptions {
pkg: string
cls: string
method: string
function getProxy(): {
invokeSync: (args: InvokeArgs, callback: InvokeSyncCallback) => unknown
invokeAsync: (args: InvokeArgs, callback: InvokeAsyncCallback) => void
} {
if (!proxy) {
proxy = uni.requireNativePlugin('UTS-Proxy') as any
}
return proxy
}
interface ProxyFunctionOptions extends ProxyBaseOptions {
async?: boolean
function invokePropGetter(args: InvokeArgs) {
return getProxy().invokeSync(args, () => {})
}
interface InvokeArgs {
interface InitProxyFunctionOptions {
/**
* 包名
*/
package: string
/**
* 类名
*/
class: string
method: string
params: unknown[]
/**
* 属性名或方法名
*/
name: string
/**
* 实例 ID
*/
id?: number
}
export function initUtsProxyFunction({
pkg,
cls,
method,
async,
}: ProxyFunctionOptions) {
function initProxyFunction(
async: boolean,
{
package: pkg,
class: cls,
name: propOrMethod,
id: instanceId,
}: InitProxyFunctionOptions
) {
const invokeCallback = ({
id,
name,
params,
keepAlive,
}: ProxyInvokeCallbackResponse) => {
const callback = callbacks[id]
}: InvokeCallbackParamsRes) => {
const callback = callbacks[id!]
if (callback) {
callback(...params)
if (!keepAlive) {
delete callbacks[id]
}
} else {
console.error(
`${pkg}${cls ? '.' + cls : ''}.${method} ${name} is not found`
)
console.error(`${pkg}${cls}.${propOrMethod} ${name} is not found`)
}
}
const baseArgs: InvokeArgs = instanceId
? { id: instanceId, name: propOrMethod }
: {
package: pkg,
class: cls,
name: propOrMethod,
}
return (...args: unknown[]) => {
if (!proxy) {
proxy = uni.requireNativePlugin('ProxyModule') as any
}
const params = args.map((arg) => normalizeArg(arg))
const invokeArgs: InvokeArgs = { package: pkg, class: cls, method, params }
const invokeArgs = extend({}, baseArgs, {
params: args.map((arg) => normalizeArg(arg)),
})
if (async) {
return new Promise((resolve, reject) => {
proxy.invokeAsync(invokeArgs, (res: ProxyInvokeResponse) => {
if (isProxyInvokeCallbackResponse(res)) {
getProxy().invokeAsync(invokeArgs, (res) => {
if (res.type !== 'return') {
invokeCallback(res)
} else {
if (res.errMsg) {
......@@ -98,41 +174,67 @@ export function initUtsProxyFunction({
})
})
}
return proxy.invokeSync(invokeArgs, invokeCallback)
return getProxy().invokeSync(invokeArgs, invokeCallback)
}
}
interface ProxyClassOptions {
pkg: string
cls: string
methods: {
[name: string]: {
async?: boolean
}
}
function initUtsStaticMethod(async: boolean, opts: ProxyBaseOptions) {
return initProxyFunction(async, opts)
}
export const initUtsProxyFunction = initUtsStaticMethod
export function initUtsProxyClass({
pkg,
cls,
package: pkg,
class: cls,
methods,
props,
staticProps,
staticMethods,
}: ProxyClassOptions): any {
const baseOptions = {
package: pkg,
class: cls,
}
return class ProxyClass {
constructor() {
constructor(...params: unknown[]) {
const target: Record<string, Function> = {}
// 初始化实例 ID
const instanceId = initProxyFunction(
false,
extend({ name: 'constructor', params }, baseOptions)
).apply(null, params) as number
return new Proxy(this, {
get(_, method) {
if (!target[method as string]) {
if (hasOwn(methods, method)) {
target[method] = initUtsProxyFunction({
pkg,
cls,
method,
async: methods[method].async,
})
get(_, name) {
if (!target[name as string]) {
//实例方法
if (hasOwn(methods, name)) {
target[name] = initUtsInstanceMethod(
!!methods[name].async,
extend(
{
id: instanceId,
name,
},
baseOptions
)
)
} else if (hasOwn(staticMethods, name)) {
// 静态方法
target[name] = initUtsStaticMethod(
!!staticMethods[name].async,
extend({ name }, baseOptions)
)
} else if (props.includes(name as string)) {
// 实例属性
return invokePropGetter({ id: instanceId, name: name as string })
} else if (staticProps.includes(name as string)) {
// 静态属性
return invokePropGetter(
extend({ name: name as string }, baseOptions)
)
}
return target[method as string]
}
return target[name as string]
},
})
}
......
......@@ -29,6 +29,10 @@
"@dcloudio/uni-cli-shared": "3.0.0-alpha-3050220220719003",
"@dcloudio/uni-shared": "3.0.0-alpha-3050220220719003",
"@dcloudio/uts": "3.0.0-alpha-3050220220719003",
"execa": "^5.1.1"
"execa": "^5.1.1",
"fs-extra": "^10.0.0"
},
"devDependencies": {
"@types/fs-extra": "^9.0.13"
}
}
import type { Plugin } from 'vite'
import path from 'path'
import { camelize } from '@vue/shared'
import {
normalizePath,
parseVueRequest,
......@@ -8,15 +7,20 @@ import {
} from '@dcloudio/uni-cli-shared'
import {
ClassDeclaration,
ClassExpression,
Expression,
FunctionDeclaration,
FunctionExpression,
Identifier,
Module,
TsFunctionType,
TsInterfaceDeclaration,
TsType,
TsTypeAliasDeclaration,
TsTypeAnnotation,
VariableDeclaration,
} from '../../types/types'
import { compile } from '../utils/compiler'
import { compile, parsePackage } from '../utils/compiler'
export function uniUtsV1Plugin(): Plugin {
// 目前仅支持 app-android
......@@ -52,6 +56,7 @@ export function uniUtsV1Plugin(): Plugin {
code = `
import { initUtsProxyClass, initUtsProxyFunction } from '@dcloudio/uni-app'
const pkg = '${pkg}'
const cls = 'IndexKt'
${genProxyCode(ast)}
`
await compile(id)
......@@ -69,38 +74,34 @@ function isUtsModuleRoot(id: string) {
return false
}
function parsePackage(filepath: string) {
const parts = normalizePath(filepath).split('/')
const index = parts.findIndex((part) => part === 'uni_modules')
if (index > -1) {
return camelize(parts[index + 1])
}
return ''
}
function genProxyFunctionCode(
method: string,
async: boolean,
isDefault: boolean = false
) {
if (isDefault) {
return `export default initUtsProxyFunction({ pkg, cls: '', method: '${method}', async: ${async} })`
return `export default initUtsProxyFunction(${async}, { package: pkg, class: cls, name: '${method}'})`
}
return `export const ${method} = initUtsProxyFunction({ pkg, cls: '', method: '${method}', async: ${async} })`
return `export const ${method} = initUtsProxyFunction(${async}, { package: pkg, class: cls, name: '${method}'})`
}
function genProxyClassCode(
cls: string,
methods: Record<string, any>,
options: {
methods: Record<string, any>
staticMethods: Record<string, any>
props: string[]
staticProps: string[]
},
isDefault: boolean = false
) {
if (isDefault) {
return `export default initUtsProxyClass({ pkg, cls: '${cls}', methods: ${JSON.stringify(
methods
return `export default initUtsProxyClass({ package: pkg, class: '${cls}', ...${JSON.stringify(
options
)} })`
}
return `export const ${cls} = initUtsProxyClass({ pkg, cls: '${cls}', methods: ${JSON.stringify(
methods
return `export const ${cls} = initUtsProxyClass({ package: pkg, class: '${cls}', ...${JSON.stringify(
options
)} })`
}
......@@ -127,37 +128,98 @@ function genTsInterfaceDeclarationCode(
}
}
})
return genProxyClassCode(cls, methods, isDefault)
return genProxyClassCode(
cls,
{ methods, staticMethods: {}, props: [], staticProps: [] },
isDefault
)
}
function genFunctionDeclarationCode(
decl: FunctionDeclaration,
decl: FunctionDeclaration | FunctionExpression,
isDefault: boolean = false
) {
return genProxyFunctionCode(
decl.identifier.value,
decl.identifier!.value,
decl.async || isReturnPromise(decl.returnType),
isDefault
)
}
function genClassDeclarationCode(
decl: ClassDeclaration,
decl: ClassDeclaration | ClassExpression,
isDefault: boolean = false
) {
const cls = decl.identifier.value
const cls = decl.identifier!.value
const methods: Record<string, { async?: boolean }> = {}
const staticMethods: Record<string, { async?: boolean }> = {}
const props: string[] = []
const staticProps: string[] = []
decl.body.forEach((item) => {
if (item.type === 'ClassMethod') {
if (item.key.type === 'Identifier') {
methods[item.key.value] = {
const name = item.key.value
const value = {
async:
item.function.async || isReturnPromise(item.function.returnType),
}
if (item.isStatic) {
staticMethods[name] = value
} else {
methods[name] = value
}
}
} else if (item.type === 'ClassProperty') {
if (item.key.type === 'Identifier') {
if (item.isStatic) {
staticProps.push(item.key.value)
} else {
props.push(item.key.value)
}
}
}
})
return genProxyClassCode(cls, methods, isDefault)
return genProxyClassCode(
cls,
{ methods, staticMethods, props, staticProps },
isDefault
)
}
function genInitCode(expr: Expression) {
switch (expr.type) {
case 'BooleanLiteral':
return expr.value + ''
case 'NumericLiteral':
return expr.value + ''
case 'StringLiteral':
return expr.value
}
return ''
}
function genVariableDeclarationCode(decl: VariableDeclaration) {
// 目前仅支持boolean,number,string
const lits = ['BooleanLiteral', 'NumericLiteral', 'StringLiteral']
if (
!decl.declarations.find((d) => {
if (d.id.type !== 'Identifier') {
return true
}
if (!d.init) {
return true
}
const type = d.init.type
if (!lits.includes(type)) {
return true
}
return false
})
) {
return `${decl.kind} ${decl.declarations
.map((d) => `${(d.id as Identifier).value} = ${genInitCode(d.init!)}`)
.join(', ')}`
}
}
function genProxyCode({ body }: Module) {
......@@ -179,10 +241,23 @@ function genProxyCode({ body }: Module) {
case 'TsInterfaceDeclaration':
code = genTsInterfaceDeclarationCode(decl, false)
break
case 'VariableDeclaration':
code = genVariableDeclarationCode(decl)
break
}
} else if (item.type === 'ExportDefaultDeclaration') {
if (item.decl.type === 'TsInterfaceDeclaration') {
code = genTsInterfaceDeclarationCode(item.decl, true)
const decl = item.decl
if (decl.type === 'TsInterfaceDeclaration') {
code = genTsInterfaceDeclarationCode(decl, true)
} else if (decl.type === 'ClassExpression') {
if (decl.identifier) {
// export default class test{}
code = genClassDeclarationCode(decl, false)
}
} else if (decl.type === 'FunctionExpression') {
if (decl.identifier) {
code = genFunctionDeclarationCode(decl, false)
}
}
}
if (code) {
......
import os from 'os'
import fs from 'fs'
import fs from 'fs-extra'
import path from 'path'
import execa from 'execa'
import { once } from '@dcloudio/uni-shared'
import type { parse, bundle, UtsTarget } from '@dcloudio/uts'
import { normalizePath } from '@dcloudio/uni-cli-shared'
import { camelize } from '@vue/shared'
export function getUtsCompiler(): {
parse: typeof parse
......@@ -29,21 +31,56 @@ export async function compile(filename: string) {
},
output: {
outDir: outputDir,
package: parsePackage(filename),
sourceMap: true,
extname: 'kt',
imports: ['kotlinx.coroutines.*', 'io.dcloud.uts.runtime.*'],
},
})
console.log('uts compile time: ' + (Date.now() - time) + 'ms')
const kotlinFile = resolveKotlinFile(filename, inputDir, outputDir)
if (fs.existsSync(kotlinFile)) {
time = Date.now()
await compileKotlin(kotlinFile)
console.log('kotlin compile time: ' + (Date.now() - time) + 'ms')
const jarFile = resolveJarPath(kotlinFile)
if (fs.existsSync(jarFile)) {
if (process.env.NODE_ENV === 'production') {
// 生产模式下,需要将 kt 文件转移到 src 下
fs.mkdirSync(path.resolve(kotlinFile, '../src'))
if (fs.existsSync(kotlinFile)) {
fs.moveSync(kotlinFile, path.resolve(kotlinFile, '../src/index.kt'))
}
const kotlinMapFile = kotlinFile + '.map'
if (fs.existsSync(kotlinMapFile)) {
fs.moveSync(
kotlinMapFile,
path.resolve(kotlinFile, '../src/index.map.kt')
)
}
const copies = ['assets', 'libs', 'res']
const moduleDir = path.dirname(filename)
const outputModuleDir = path.dirname(kotlinFile)
fs.readdirSync(moduleDir).forEach((file) => {
if (copies.includes(file)) {
fs.copySync(
path.join(moduleDir, file),
path.join(outputModuleDir, file)
)
}
})
} else if (process.env.NODE_ENV === 'development') {
// 开发模式下,需要生成 dex
if (fs.existsSync(kotlinFile)) {
time = Date.now()
await d8(jarFile)
console.log('d8 compile time: ' + (Date.now() - time) + 'ms')
await compileKotlin(kotlinFile)
console.log('kotlin compile time: ' + (Date.now() - time) + 'ms')
const jarFile = resolveJarPath(kotlinFile)
if (fs.existsSync(jarFile)) {
time = Date.now()
await d8(jarFile)
console.log('d8 compile time: ' + (Date.now() - time) + 'ms')
try {
fs.unlinkSync(jarFile)
// 短期内先不删除,方便排查问题
// fs.unlinkSync(kotlinFile)
} catch (e) {}
}
}
}
}
......@@ -139,3 +176,12 @@ const resolveD8Path = once(() => {
const { d8 } = resolveDirs()
return path.resolve(d8, 'd8.jar')
})
export function parsePackage(filepath: string) {
const parts = normalizePath(filepath).split('/')
const index = parts.findIndex((part) => part === 'uni_modules')
if (index > -1) {
return 'uts.modules.' + camelize(parts[index + 1])
}
return ''
}
......@@ -53,6 +53,14 @@ export interface ToOptions {
* 输出目录
*/
dir: string
/**
* 包名
*/
package?: string
/**
* 自动导入的包
*/
imports?: string[]
/**
* 是否生成 sourceMap,为 string 时,表示生成的 sourceMap 目标目录
*/
......@@ -120,11 +128,15 @@ function initInputOptions(_: UtsTarget, root: string): UtsInputOptions {
function initOutputOptions(
target: UtsTarget,
outDir: string,
pkg: string,
imports: string[] = [],
sourceMap: string | boolean | undefined,
inlineSourcesContent: boolean
): UtsOutputOptions {
return {
outDir,
package: pkg,
imports,
sourceMap: sourceMap ? sourceMap : false,
inlineSourcesContent,
extname: UtsTargetExtNames[target],
......@@ -135,7 +147,13 @@ function initOptions(
target: UtsTarget,
{
input: { dir: inputDir },
output: { dir: outputDir, sourceMap, inlineSourcesContent },
output: {
dir: outputDir,
package: pkg,
imports,
sourceMap,
inlineSourcesContent,
},
}: ToOptions
) {
const inputSrcDir = resolveSrcDir(target, inputDir)
......@@ -145,6 +163,8 @@ function initOptions(
const output = initOutputOptions(
target,
outputSrcDir,
pkg || '',
imports,
sourceMap,
!!inlineSourcesContent
)
......@@ -282,7 +302,6 @@ function buildFile(
input: {
...input,
filename,
namespace: '',
},
output: {
...output,
......
......@@ -17,11 +17,11 @@ export type UtsParseOptions = UtsParserConfig & {
export type UtsInputOptions = UtsParseOptions & {
root: string
filename: string
namespace?: string
}
export type UtsOutputOptions = {
outDir: string
package: string
imports?: string[]
sourceMap?: boolean | string
inlineSourcesContent?: boolean
......
......@@ -798,12 +798,17 @@ importers:
'@dcloudio/uni-cli-shared': 3.0.0-alpha-3050220220719003
'@dcloudio/uni-shared': 3.0.0-alpha-3050220220719003
'@dcloudio/uts': 3.0.0-alpha-3050220220719003
'@types/fs-extra': ^9.0.13
execa: ^5.1.1
fs-extra: ^10.0.0
dependencies:
'@dcloudio/uni-cli-shared': link:../uni-cli-shared
'@dcloudio/uni-shared': link:../uni-shared
'@dcloudio/uts': link:../uts
execa: 5.1.1
fs-extra: 10.1.0
devDependencies:
'@types/fs-extra': 9.0.13
packages/uni-vue:
specifiers:
......
......@@ -3,11 +3,15 @@
console.log('publishHandler', JSON.stringify(args))
},
}
let instanceId = 1
;(global as any).uni = {
requireNativePlugin(name: string) {
return {
invokeSync(args: unknown, callback: unknown) {
invokeSync(args: Record<string, any>, callback: unknown) {
console.log(`invoke`, JSON.stringify(args))
if (args.name === 'constructor') {
return instanceId++
}
},
invokeAsync(args: unknown, callback: unknown) {
console.log(`invokeAsync`, JSON.stringify(args))
......
......@@ -30,6 +30,8 @@ bundle({
projectDir,
'unpackage/dist/app-plus/uni_modules/test-uniplugin/'
),
package: 'uts.modules.testUniPlugin',
imports: ['kotlinx.coroutines.*', 'io.dcloud.uts.runtime.*'],
sourceMap: true,
extname: 'kt',
},
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册