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

wip(uts): bundle

上级 1c8a4f21
......@@ -10,18 +10,6 @@ module.exports = {
__GLOBAL__: false,
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false,
uni: {
requireNativePlugin(name) {
return {
invokeSync(args, callback) {
console.log(`invoke`, JSON.stringify(args))
},
invokeAsync(args, callback) {
console.log(`invokeAsync`, JSON.stringify(args))
},
}
},
},
},
coverageDirectory: 'coverage',
coverageReporters: ['html', 'lcov', 'text'],
......
import IUniLogin from '../interface'
import { OnConnect, RegisterOptions } from '../interface'
export default class UniLogin implements IUniLogin {
//@UniJSMethod({ uiThread: true })
override login(name, code) {
console.log(name, code)
}
// {"name":"fxy","callback1":{__type__:"fun",id:1},"callback2":{__type__:"fun",id:1}}
// => RegisterOptions
async register(opts: RegisterOptions) {
opts.callback1(true)
opts.callback2(true)
opts.abc.callback3(true)
export function login(name: string, pwd: string) {
return { name, pwd }
}
export class User {
async login(name: string, pwd: string) {
return { name, pwd }
}
override onConnect(callback: OnConnect) {}
}
export type RegisterOptions = {
name: string
callback1: (res: boolean) => void
callback2: (res: boolean) => void
abc: {
callback3: (res: boolean) => void
}
}
export type OnConnect = () => void
export default interface IUniLogin {
login: (name: string, code: number) => void
register: (opts: RegisterOptions) => Promise<void>
onConnect: (callback: OnConnect) => void
export declare function login(name: string, pwd: string): void
export declare class User {
login(name: string, pwd: string): void
}
package index;
import ...interface.interface as IUniLogin;
import ...interface.OnConnect;
import ...interface.RegisterOptions;
open class UniLogin : IUniLogin {
override fun login(name, code) {
console.log(name, code);
}
open fun async register(opts: RegisterOptions) {
opts.callback1(true);
opts.callback2(true);
opts.abc.callback3(true);
fun login(name: String, pwd: String): UtsJSONObject {
return object : () {
var name = name
var pwd = pwd
};
}
open class User {
open fun async login(name: String, pwd: String) {
return object : () {
var name = name
var pwd = pwd
};
}
override fun onConnect(callback: OnConnect) {}
}
......@@ -7,6 +7,8 @@ declare namespace NodeJS {
UNI_NODE_ENV: 'production' | 'development' | 'test'
UNI_PLATFORM: UniApp.PLATFORM
UNI_SUB_PLATFORM: 'quickapp-webview-huawei' | 'quickapp-webview-union'
UNI_APP_PLATFORM: 'app-android' | 'app-ios'
UNI_INPUT_DIR: string
UNI_OUTPUT_DIR: string
UNI_CLI_CONTEXT: string
......
......@@ -11468,6 +11468,8 @@ const API_GET_SYSTEM_SETTING = 'getSystemSetting';
const API_GET_APP_AUTHORIZE_SETTING = 'getAppAuthorizeSetting';
const API_OPEN_APP_AUTHORIZE_SETTING = 'openAppAuthorizeSetting';
const API_GET_STORAGE = 'getStorage';
const GetStorageProtocol = {
key: {
......@@ -14125,6 +14127,19 @@ const getAppAuthorizeSetting = defineSyncApi(API_GET_APP_AUTHORIZE_SETTING, () =
return appAuthorizeSetting;
});
const openAppAuthorizeSetting = defineAsyncApi(API_OPEN_APP_AUTHORIZE_SETTING, (_, { resolve, reject }) => {
const { openAppAuthorizeSetting } = weex.requireModule('plus');
const fn = openAppAuthorizeSetting;
fn((ret) => {
if (ret.type === 'success') {
resolve();
}
else {
reject();
}
});
});
const getImageInfo = defineAsyncApi(API_GET_IMAGE_INFO, (options, { resolve, reject }) => {
const path = TEMP_PATH + '/download/';
plus.io.getImageInfo(extend(options, {
......@@ -15962,7 +15977,9 @@ const chooseLocation = defineAsyncApi(API_CHOOSE_LOCATION, (options, { resolve,
let result;
const page = showPage({
url: '__uniappchooselocation',
data: options,
data: extend({}, options, {
locale: getLocale(),
}),
style: {
// @ts-expect-error
animationType: options.animationType || 'slide-in-bottom',
......@@ -15999,7 +16016,9 @@ const chooseLocation = defineAsyncApi(API_CHOOSE_LOCATION, (options, { resolve,
const openLocation = defineAsyncApi(API_OPEN_LOCATION, (data, { resolve, reject }) => {
showPage({
url: '__uniappopenlocation',
data,
data: extend({}, data, {
locale: getLocale(),
}),
style: {
titleNView: {
type: 'transparent',
......@@ -19227,6 +19246,7 @@ var uni$1 = {
getWindowInfo: getWindowInfo,
getSystemSetting: getSystemSetting,
getAppAuthorizeSetting: getAppAuthorizeSetting,
openAppAuthorizeSetting: openAppAuthorizeSetting,
getImageInfo: getImageInfo,
getVideoInfo: getVideoInfo,
previewImage: previewImage,
......
import { normalizeArg, initModule } from '../module'
import {
normalizeArg,
initUtsProxyFunction,
initUtsProxyClass,
} from '../src/uts'
describe('uts-module', () => {
test('normalize args', () => {
......@@ -33,27 +37,47 @@ describe('uts-module', () => {
success: 6,
})
})
test(`invoke`, () => {
const wifi = initModule('wifi', {
preparePermission: { async: true },
})
/**
* {"module":"wifi","method":"preparePermission","params":[{"name":"foo","age":10,"success":7,"fail":8},9]}
*/
wifi.preparePermission(
{
name: 'foo',
age: 10,
success(res: any) {
console.log('success', res)
},
fail(res: any) {
console.log('fail', res)
test(`initProxyFunction`, () => {
;[true, false].forEach((async) => {
const preparePermission = initUtsProxyFunction({
pkg: 'testPlugin',
cls: '',
method: 'preparePermission',
async,
})
/**
* {"package":"testPlugin","class":"","method":"preparePermission","params":[{"name":"foo","age":10,"success":7,"fail":8},9]}
*/
preparePermission(
{
family: {
father: 'f',
mother: 'm',
},
name: 'foo',
age: 10,
success(res: any) {
console.log('success', res)
},
fail(res: any) {
console.log('fail', res)
},
},
(res: any) => {
console.log('callback', res)
}
)
})
})
test(`initProxyClass`, () => {
const WifiManager = initUtsProxyClass({
pkg: 'testPlugin',
cls: 'WifiManager',
methods: {
preparePermission: {},
},
(res: any) => {
console.log('callback', res)
}
)
})
const wifi = new WifiManager()
wifi.preparePermission(1, 2, 3, () => {})
})
})
......@@ -11,12 +11,12 @@ function assertKey(key, shallow = false) {
throw new Error(`${shallow ? 'shallowSsrRef' : 'ssrRef'}: You must provide a key.`);
}
}
function proxy(target, track, trigger) {
function proxy$1(target, track, trigger) {
return new Proxy(target, {
get(target, prop) {
track();
if (shared.isObject(target[prop])) {
return proxy(target[prop], track, trigger);
return proxy$1(target[prop], track, trigger);
}
return Reflect.get(target, prop);
},
......@@ -48,7 +48,7 @@ const ssrServerRef = (value, key, shallow = false) => {
get: () => {
track();
if (!shallow && shared.isObject(value)) {
return proxy(value, track, customTrigger);
return proxy$1(value, track, customTrigger);
}
return value;
},
......@@ -135,8 +135,91 @@ const onNavigationBarSearchInputConfirmed =
const onNavigationBarSearchInputFocusChanged =
/*#__PURE__*/ createHook(uniShared.ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED);
let callbackId = 1;
let proxy;
const callbacks = {};
function normalizeArg(arg) {
if (typeof arg === 'function') {
const id = callbackId++;
callbacks[id] = arg;
return id;
}
else if (shared.isPlainObject(arg)) {
Object.keys(arg).forEach((name) => {
arg[name] = normalizeArg(arg[name]);
});
}
return arg;
}
function isProxyInvokeCallbackResponse(res) {
return !!res.name;
}
function initUtsProxyFunction({ pkg, cls, method, async, }) {
const invokeCallback = ({ id, name, params, keepAlive, }) => {
const callback = callbacks[id];
if (callback) {
callback(...params);
if (!keepAlive) {
delete callbacks[id];
}
}
else {
console.error(`${pkg}${cls ? '.' + cls : ''}.${method} ${name} is not found`);
}
};
return (...args) => {
if (!proxy) {
proxy = uni.requireNativePlugin('ProxyModule');
}
const params = args.map((arg) => normalizeArg(arg));
const invokeArgs = { package: pkg, class: cls, method, params };
if (async) {
return new Promise((resolve, reject) => {
proxy.invokeAsync(invokeArgs, (res) => {
if (isProxyInvokeCallbackResponse(res)) {
invokeCallback(res);
}
else {
if (res.errMsg) {
reject(res.errMsg);
}
else {
resolve(res.params);
}
}
});
});
}
return proxy.invokeSync(invokeArgs, invokeCallback);
};
}
function initUtsProxyClass({ pkg, cls, methods, }) {
return class ProxyClass {
constructor() {
const target = {};
return new Proxy(this, {
get(_, method) {
if (!target[method]) {
if (shared.hasOwn(methods, method)) {
target[method] = initUtsProxyFunction({
pkg,
cls,
method,
async: methods[method].async,
});
}
return target[method];
}
},
});
}
};
}
exports.getCurrentSubNVue = getCurrentSubNVue;
exports.getSsrGlobalData = getSsrGlobalData;
exports.initUtsProxyClass = initUtsProxyClass;
exports.initUtsProxyFunction = initUtsProxyFunction;
exports.onAddToFavorites = onAddToFavorites;
exports.onBackPress = onBackPress;
exports.onError = onError;
......
......@@ -35,6 +35,10 @@ export declare function getCurrentSubNVue(): any;
export declare function getSsrGlobalData(): any;
export declare function initUtsProxyClass({ pkg, cls, methods, }: ProxyClassOptions): any;
export declare function initUtsProxyFunction({ pkg, cls, method, async, }: ProxyFunctionOptions): (...args: unknown[]) => any;
declare type LaunchOption = LaunchShowOption;
declare interface LaunchShowOption {
......@@ -161,6 +165,26 @@ declare interface PageScrollOption {
scrollTop: number;
}
declare interface ProxyBaseOptions {
pkg: string;
cls: string;
method: string;
}
declare interface ProxyClassOptions {
pkg: string;
cls: string;
methods: {
[name: string]: {
async?: boolean;
};
};
}
declare interface ProxyFunctionOptions extends ProxyBaseOptions {
async?: boolean;
}
declare interface ReferrerInfo {
appId: string;
extraData?: any;
......
import { shallowRef, ref, getCurrentInstance, isInSSRComponentSetup, injectHook } from 'vue';
import { hasOwn, isString } from '@vue/shared';
import { hasOwn, isString, 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() {
......@@ -103,4 +103,85 @@ const onNavigationBarSearchInputConfirmed =
const onNavigationBarSearchInputFocusChanged =
/*#__PURE__*/ createHook(ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED);
export { getCurrentSubNVue, getSsrGlobalData, onAddToFavorites, onBackPress, onError, onHide, onInit, onLaunch, onLoad, onNavigationBarButtonTap, onNavigationBarSearchInputChanged, onNavigationBarSearchInputClicked, onNavigationBarSearchInputConfirmed, onNavigationBarSearchInputFocusChanged, onPageNotFound, onPageScroll, onPullDownRefresh, onReachBottom, onReady, onResize, onSaveExitState, onShareAppMessage, onShareTimeline, onShow, onTabItemTap, onThemeChange, onUnhandledRejection, onUnload, requireNativePlugin, resolveEasycom, shallowSsrRef, ssrRef };
let callbackId = 1;
let proxy;
const callbacks = {};
function normalizeArg(arg) {
if (typeof arg === 'function') {
const id = callbackId++;
callbacks[id] = arg;
return id;
}
else if (isPlainObject(arg)) {
Object.keys(arg).forEach((name) => {
arg[name] = normalizeArg(arg[name]);
});
}
return arg;
}
function isProxyInvokeCallbackResponse(res) {
return !!res.name;
}
function initUtsProxyFunction({ pkg, cls, method, async, }) {
const invokeCallback = ({ id, name, params, keepAlive, }) => {
const callback = callbacks[id];
if (callback) {
callback(...params);
if (!keepAlive) {
delete callbacks[id];
}
}
else {
console.error(`${pkg}${cls ? '.' + cls : ''}.${method} ${name} is not found`);
}
};
return (...args) => {
if (!proxy) {
proxy = uni.requireNativePlugin('ProxyModule');
}
const params = args.map((arg) => normalizeArg(arg));
const invokeArgs = { package: pkg, class: cls, method, params };
if (async) {
return new Promise((resolve, reject) => {
proxy.invokeAsync(invokeArgs, (res) => {
if (isProxyInvokeCallbackResponse(res)) {
invokeCallback(res);
}
else {
if (res.errMsg) {
reject(res.errMsg);
}
else {
resolve(res.params);
}
}
});
});
}
return proxy.invokeSync(invokeArgs, invokeCallback);
};
}
function initUtsProxyClass({ pkg, cls, methods, }) {
return class ProxyClass {
constructor() {
const target = {};
return new Proxy(this, {
get(_, method) {
if (!target[method]) {
if (hasOwn(methods, method)) {
target[method] = initUtsProxyFunction({
pkg,
cls,
method,
async: methods[method].async,
});
}
return target[method];
}
},
});
}
};
}
export { getCurrentSubNVue, getSsrGlobalData, initUtsProxyClass, initUtsProxyFunction, onAddToFavorites, onBackPress, onError, onHide, onInit, onLaunch, onLoad, onNavigationBarButtonTap, onNavigationBarSearchInputChanged, onNavigationBarSearchInputClicked, onNavigationBarSearchInputConfirmed, onNavigationBarSearchInputFocusChanged, onPageNotFound, onPageScroll, onPullDownRefresh, onReachBottom, onReady, onResize, onSaveExitState, onShareAppMessage, onShareTimeline, onShow, onTabItemTap, onThemeChange, onUnhandledRejection, onUnload, requireNativePlugin, resolveEasycom, shallowSsrRef, ssrRef };
......@@ -2,3 +2,4 @@ export * from './ssr'
export * from './api'
export * from './utils'
export * from './apiLifecycle'
export { initUtsProxyClass, initUtsProxyFunction } from './uts'
declare const uni: {
requireNativePlugin(name: string): { invoke: Function }
}
const moduleName = '__MODULE_NAME__'
const moduleDefine = '__MODULE_DEFINE__' as unknown as Record<
string,
ModuleMethodDefine
>
interface ModuleMethodDefine {
async?: boolean
}
export default initModule(moduleName, moduleDefine)
import { isPlainObject, hasOwn } from '@vue/shared'
declare const uni: any
let callbackId = 1
const objectToString = Object.prototype.toString
const toTypeString = (value: unknown): string => objectToString.call(value)
const isPlainObject = (val: unknown): val is object =>
toTypeString(val) === '[object Object]'
let proxy: any
const callbacks: Record<string, Function> = {}
export function normalizeArg(arg: unknown) {
if (typeof arg === 'function') {
......@@ -55,12 +34,30 @@ function isProxyInvokeCallbackResponse(
): res is ProxyInvokeCallbackResponse {
return !!(res as ProxyInvokeCallbackResponse).name
}
function moduleGetter(
proxy: any,
module: string,
method: string,
defines: ModuleMethodDefine
) {
interface ProxyBaseOptions {
pkg: string
cls: string
method: string
}
interface ProxyFunctionOptions extends ProxyBaseOptions {
async?: boolean
}
interface InvokeArgs {
package: string
class: string
method: string
params: unknown[]
}
export function initUtsProxyFunction({
pkg,
cls,
method,
async,
}: ProxyFunctionOptions) {
const invokeCallback = ({
id,
name,
......@@ -74,13 +71,19 @@ function moduleGetter(
delete callbacks[id]
}
} else {
console.error(`${module}.${method} ${name} is not found`)
console.error(
`${pkg}${cls ? '.' + cls : ''}.${method} ${name} is not found`
)
}
}
return (...args: unknown[]) => {
if (!proxy) {
proxy = uni.requireNativePlugin('ProxyModule') as any
}
const params = args.map((arg) => normalizeArg(arg))
const invokeArgs = { module, method, params }
if (defines.async) {
const invokeArgs: InvokeArgs = { package: pkg, class: cls, method, params }
if (async) {
return new Promise((resolve, reject) => {
proxy.invokeAsync(invokeArgs, (res: ProxyInvokeResponse) => {
if (isProxyInvokeCallbackResponse(res)) {
......@@ -99,24 +102,39 @@ function moduleGetter(
}
}
export function initModule(
name: string,
defines: Record<string, ModuleMethodDefine>,
proxyModuleName = 'ProxyModule'
) {
let proxy: any
const moduleProxy: Record<string, Function> = {}
for (const method in defines) {
Object.defineProperty(moduleProxy, method, {
enumerable: true,
configurable: true,
get: () => {
if (!proxy) {
proxy = uni.requireNativePlugin(proxyModuleName)
}
return moduleGetter(proxy, name, method, defines[method])
},
})
interface ProxyClassOptions {
pkg: string
cls: string
methods: {
[name: string]: {
async?: boolean
}
}
}
export function initUtsProxyClass({
pkg,
cls,
methods,
}: ProxyClassOptions): any {
return class ProxyClass {
constructor() {
const target: Record<string, Function> = {}
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,
})
}
return target[method as string]
}
},
})
}
}
return moduleProxy
}
......@@ -19,6 +19,22 @@ function resolveWithSymlinks(id: string, basedir: string): string {
extensions,
// necessary to work with pnpm
preserveSymlinks: true,
pathFilter(pkg, filepath, relativePath) {
if (pkg.dcloudext && (pkg.dcloudext as any).type === 'native-uts') {
if (
process.env.UNI_APP_PLATFORM === 'app-android' ||
process.env.UNI_APP_PLATFORM === 'app-ios'
) {
const file = process.env.UNI_APP_PLATFORM + '/index.uts'
if (
fs.existsSync(path.join(filepath.replace(relativePath, ''), file))
) {
return file
}
}
}
return relativePath
},
})
}
......
[
{
"input": {
"module.ts": "lib/module.js"
},
"treeshake": false,
"compilerOptions": {
"module": "ESNext"
}
}
]
const moduleName = '__MODULE_NAME__';
const moduleDefine = '__MODULE_DEFINE__';
var module = initModule(moduleName, moduleDefine);
let callbackId = 1;
const objectToString = Object.prototype.toString;
const toTypeString = (value) => objectToString.call(value);
const isPlainObject = (val) => toTypeString(val) === '[object Object]';
const callbacks = {};
function normalizeArg(arg) {
if (typeof arg === 'function') {
const id = callbackId++;
callbacks[id] = arg;
return id;
}
else if (isPlainObject(arg)) {
Object.keys(arg).forEach((name) => {
;
arg[name] = normalizeArg(arg[name]);
});
}
return arg;
}
function isProxyInvokeCallbackResponse(res) {
return !!res.name;
}
function moduleGetter(proxy, module, method, defines) {
const invokeCallback = ({ id, name, params, keepAlive, }) => {
const callback = callbacks[id];
if (callback) {
callback(...params);
if (!keepAlive) {
delete callbacks[id];
}
}
else {
console.error(`${module}.${method} ${name} is not found`);
}
};
return (...args) => {
const params = args.map((arg) => normalizeArg(arg));
const invokeArgs = { module, method, params, async: !!defines.async };
if (defines.async) {
return new Promise((resolve, reject) => {
proxy.invoke(invokeArgs, (res) => {
if (isProxyInvokeCallbackResponse(res)) {
invokeCallback(res);
}
else {
if (res.errMsg) {
reject(res.errMsg);
}
else {
resolve(res.params);
}
}
});
});
}
return proxy.invoke(invokeArgs, invokeCallback);
};
}
function initModule(name, defines, proxyModuleName = 'ProxyModule') {
let proxy;
const moduleProxy = {};
for (const method in moduleDefine) {
Object.defineProperty(moduleProxy, method, {
enumerable: true,
configurable: true,
get: () => {
if (!proxy) {
proxy = uni.requireNativePlugin(proxyModuleName);
}
return moduleGetter(proxy, name, method, defines[method]);
},
});
}
return moduleProxy;
}
export { module as default, initModule, normalizeArg };
import type { Plugin } from 'vite'
import fs from 'fs'
import path from 'path'
import { normalizePath, parseVueRequest } from '@dcloudio/uni-cli-shared'
import { camelize } from '@vue/shared'
import {
ExportDefaultDeclaration,
normalizePath,
parseVueRequest,
requireResolve,
} from '@dcloudio/uni-cli-shared'
import {
ClassDeclaration,
FunctionDeclaration,
Module,
TsFunctionType,
TsInterfaceDeclaration,
TsType,
TsTypeAliasDeclaration,
TsTypeAnnotation,
} from '../../types/types'
// 需要区分 android,iOS
export function uniUtsV1Plugin(): Plugin {
let moduleCode: string
// 目前仅支持 app-android
process.env.UNI_APP_PLATFORM = 'app-android'
return {
name: 'uni:uts-v1',
apply: 'build',
enforce: 'pre',
resolveId(id, importer) {
if (isUtsModuleRoot(id)) {
return requireResolve(
id,
(importer && path.dirname(importer)) || process.env.UNI_INPUT_DIR
)
}
},
async transform(code, id, opts) {
if (opts && opts.ssr) {
return
......@@ -24,23 +40,19 @@ export function uniUtsV1Plugin(): Plugin {
if (path.extname(filename) !== '.uts') {
return
}
const moduleName = parseModuleId(filename)
if (!moduleName) {
const pkg = parsePackage(filename)
if (!pkg) {
return
}
// 懒加载 uts 编译器
// eslint-disable-next-line no-restricted-globals
const { parse } = require('@dcloudio/uts')
const ast = await parse(code)
if (!moduleCode) {
moduleCode = fs.readFileSync(
path.resolve(__dirname, '../../lib/module.js'),
'utf8'
)
}
code = moduleCode
.replace(`__MODULE_NAME__`, moduleName)
.replace(`'__MODULE_DEFINE__'`, JSON.stringify(parseModuleDefines(ast)))
code = `
import { initUtsProxyClass, initUtsProxyFunction } from '@dcloudio/uni-app'
const pkg = '${pkg}'
${genProxyCode(ast)}
`
// TODO compile uts
return code
......@@ -48,59 +60,147 @@ export function uniUtsV1Plugin(): Plugin {
}
}
function parseModuleId(filepath: string) {
// 仅限 uni_modules/test-plugin 格式
function isUtsModuleRoot(id: string) {
const parts = normalizePath(id).split('/')
if (parts[parts.length - 2] === 'uni_modules') {
return true
}
return false
}
function parsePackage(filepath: string) {
const parts = normalizePath(filepath).split('/')
const index = parts.findIndex((part) => part === 'uni_modules')
if (index > -1) {
return parts[index + 1]
return camelize(parts[index + 1])
}
return ''
}
function parseModuleDefines(ast: Module) {
const module: Record<string, { async: boolean }> = {}
const defaultDecl = ast.body.find(
(item) => item.type === 'ExportDefaultDeclaration'
) as ExportDefaultDeclaration
if (!defaultDecl || defaultDecl.decl.type !== 'TsInterfaceDeclaration') {
return 'only support `export default interface Module {}`'
function genProxyFunctionCode(
method: string,
async: boolean,
isDefault: boolean = false
) {
if (isDefault) {
return `export default initUtsProxyFunction({ pkg, cls: '', method: '${method}', async: ${async} })`
}
const body = defaultDecl.decl.body.body
body.forEach((item) => {
if (item.type === 'TsPropertySignature') {
const { key, typeAnnotation } = item
if (key.type !== 'Identifier') {
return
}
if (!typeAnnotation) {
return
}
const functionType = typeAnnotation.typeAnnotation
if (!isFunctionType(functionType)) {
return
return `export const ${method} = initUtsProxyFunction({ pkg, cls: '', method: '${method}', async: ${async} })`
}
function genProxyClassCode(
cls: string,
methods: Record<string, any>,
isDefault: boolean = false
) {
if (isDefault) {
return `export default initUtsProxyClass({ pkg, cls: '${cls}', methods: ${JSON.stringify(
methods
)} })`
}
return `export const ${cls} = initUtsProxyClass({ pkg, cls: '${cls}', methods: ${JSON.stringify(
methods
)} })`
}
function genTsTypeAliasDeclarationCode(decl: TsTypeAliasDeclaration) {
if (isFunctionType(decl.typeAnnotation)) {
return genProxyFunctionCode(
decl.id.value,
isReturnPromise(decl.typeAnnotation.typeAnnotation)
)
}
}
function genTsInterfaceDeclarationCode(
decl: TsInterfaceDeclaration,
isDefault: boolean = false
) {
const cls = decl.id.value
const methods: Record<string, { async?: boolean }> = {}
decl.body.body.forEach((item) => {
if (item.type === 'TsMethodSignature') {
if (item.key.type === 'Identifier') {
methods[item.key.value] = {
async: isReturnPromise(item.typeAnn),
}
}
const methodName = key.value
module[methodName] = {
async: isReturnPromise(functionType.typeAnnotation),
}
})
return genProxyClassCode(cls, methods, isDefault)
}
function genFunctionDeclarationCode(
decl: FunctionDeclaration,
isDefault: boolean = false
) {
return genProxyFunctionCode(
decl.identifier.value,
decl.async || isReturnPromise(decl.returnType),
isDefault
)
}
function genClassDeclarationCode(
decl: ClassDeclaration,
isDefault: boolean = false
) {
const cls = decl.identifier.value
const methods: Record<string, { async?: boolean }> = {}
decl.body.forEach((item) => {
if (item.type === 'ClassMethod') {
if (item.key.type === 'Identifier') {
methods[item.key.value] = {
async:
item.function.async || isReturnPromise(item.function.returnType),
}
}
} else if (item.type === 'TsMethodSignature') {
if (item.key.type !== 'Identifier') {
return
}
})
return genProxyClassCode(cls, methods, isDefault)
}
function genProxyCode({ body }: Module) {
const codes: string[] = []
body.forEach((item) => {
let code: string | undefined
if (item.type === 'ExportDeclaration') {
const decl = item.declaration
switch (decl.type) {
case 'FunctionDeclaration':
code = genFunctionDeclarationCode(decl, false)
break
case 'ClassDeclaration':
code = genClassDeclarationCode(decl, false)
break
case 'TsTypeAliasDeclaration':
code = genTsTypeAliasDeclarationCode(decl)
break
case 'TsInterfaceDeclaration':
code = genTsInterfaceDeclarationCode(decl, false)
break
}
const methodName = item.key.value
module[methodName] = {
async: item.typeAnn ? isReturnPromise(item.typeAnn) : false,
} else if (item.type === 'ExportDefaultDeclaration') {
if (item.decl.type === 'TsInterfaceDeclaration') {
code = genTsInterfaceDeclarationCode(item.decl, true)
}
}
if (code) {
codes.push(code)
}
})
return module
return codes.join(`\n`)
}
function isFunctionType(type: TsType): type is TsFunctionType {
return type.type === 'TsFunctionType'
}
function isReturnPromise({ typeAnnotation }: TsTypeAnnotation) {
function isReturnPromise(anno?: TsTypeAnnotation) {
if (!anno) {
return false
}
const { typeAnnotation } = anno
return (
typeAnnotation.type === 'TsTypeReference' &&
typeAnnotation.typeName.type === 'Identifier' &&
......
import { resolve } from 'path'
import type { UtsOptions, UtsParseOptions, UtsResult } from './types'
import type {
UtsBundleOptions,
UtsOptions,
UtsParseOptions,
UtsResult,
} from './types'
import { normalizePath } from './utils'
const bindingsOverride = process.env['UTS_BINARY_PATH']
......@@ -65,6 +70,10 @@ export function toSwift(options: UtsOptions): Promise<UtsResult> {
.then((res: string) => JSON.parse(res))
}
export function bundle(options: UtsBundleOptions): Promise<UtsResult> {
return bindings.bundle(toBuffer(options)).then((res: string) => res)
}
function toBuffer(t: any): Buffer {
return Buffer.from(JSON.stringify(t))
}
......@@ -5,11 +5,12 @@ import glob from 'fast-glob'
import chokidar from 'chokidar'
import { toKotlin, toSwift } from './api'
import type {
import {
UtsInputOptions,
UtsOptions,
UtsOutputOptions,
UtsResult,
UtsTarget,
} from './types'
import {
printDone,
......@@ -19,10 +20,7 @@ import {
timeEnd,
} from './utils'
export enum UtsTarget {
KOTLIN = 'kotlin',
SWIFT = 'swift',
}
export { UtsTarget } from './types'
export type UtsMode = 'dev' | 'build'
......@@ -299,7 +297,7 @@ function buildFile(
})
}
export { parse } from './api'
export { parse, bundle } from './api'
export function runDev(target: UtsTarget, opts: ToOptions) {
opts = parseOptions('dev', target, opts)
......
export enum UtsTarget {
KOTLIN = 'kotlin',
SWIFT = 'swift',
}
export interface UtsParserConfig {
/**
* Defaults to `false`
......@@ -33,3 +37,11 @@ export interface UtsResult {
time?: number
error?: Error
}
export interface UtsBundleOptions {
target: UtsTarget
entry: Record<string, string>
output: {
path: string
}
}
......@@ -3,3 +3,15 @@
console.log('publishHandler', JSON.stringify(args))
},
}
;(global as any).uni = {
requireNativePlugin(name: string) {
return {
invokeSync(args: unknown, callback: unknown) {
console.log(`invoke`, JSON.stringify(args))
},
invokeAsync(args: unknown, callback: unknown) {
console.log(`invokeAsync`, JSON.stringify(args))
},
}
},
}
const fs = require('fs')
const path = require('path')
const { parse, runBuild, UtsTarget } = require('../packages/uts/dist')
const { parse, runBuild, bundle, UtsTarget } = require('../packages/uts/dist')
const projectDir = path.resolve(__dirname, '../packages/playground/uts')
const start = Date.now()
parse(
fs.readFileSync(
path.resolve(projectDir, 'uni_modules/test-uniplugin/interface.uts'),
path.resolve(
projectDir,
'uni_modules/test-uniplugin/app-android/index.uts'
),
'utf8'
)
).then((res) => {
......@@ -14,6 +17,23 @@ parse(
console.log(JSON.stringify(res))
})
// bundle({
// entry: {
// 'test-uniplugin': path.resolve(
// projectDir,
// 'uni_modules/test-uniplugin/app-android/index.uts'
// ),
// },
// output: {
// path: path.resolve(
// projectDir,
// 'unpackage/dist/app-plus/uni_modules/test-uniplugin/bundle'
// ),
// },
// }).then((res) => {
// console.log(res)
// })
// uts
runBuild(UtsTarget.KOTLIN, {
silent: false,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册