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

wip(uts): components

上级 8723a7c9
{"version":3,"sources":["uni_modules/test-component/utssdk/app-android/index.vue"],"sourcesContent":["\n\n\n\n\n\nimport Animator from 'android.animation.Animator'\nimport TextUtils from 'android.text.TextUtils'\nimport View from 'android.view.View'\nimport LottieAnimationView from 'com.airbnb.lottie.LottieAnimationView'\nimport LottieDrawable from 'com.airbnb.lottie.LottieDrawable'\n\n\nclass CustomAnimListener extends Animator.AnimatorListener {\n\n comp: UTSComponent<LottieAnimationView>\n constructor(com: UTSComponent<LottieAnimationView>) {\n super();\n this.comp = com\n }\n\n override onAnimationStart(animation: Animator | null) {\n }\n\n override onAnimationEnd(animation: Animator | null, isReverse: Boolean) {\n this.comp.emit(\"bindended\")\n }\n\n override onAnimationEnd(animation: Animator | null) {\n }\n\n override onAnimationCancel(animation: Animator | null) {\n }\n\n override onAnimationRepeat(animation: Animator | null) {\n }\n}\n\n//原生提供以下属性或方法的实现 \nexport default {\n name: \"animation-view\",\n /**\n * 当播放到末尾时触发 ended 事件(自然播放结束会触发回调,循环播放结束及手动停止动画不会触发)\n */\n emits: ['bindended'],\n props: {\n /**\n * 动画资源地址,目前只支持绝对路径\n */\n \"path\": {\n type: String,\n\t\t\tdefault:\"\"\n },\n /**\n * 动画是否循环播放\n */\n \"autoplay\": {\n type: Boolean,\n\t\t\tdefault:false\n },\n /**\n * 动画是否自动播放\n */\n \"loop\": {\n type: Boolean,\n\t\t\tdefault:false\n },\n /**\n * 是否隐藏动画\n */\n \"hidden\": {\n type: Boolean,\n\t\t\tdefault:false\n },\n /**\n * 动画操作,可取值 play、pause、stop\n */\n \"action\": {\n type: String,\n\t\t\tdefault:\"stop\"\n }\n\n },\n data() {\n return {\n\n }\n },\n watch: {\n \"path\": {\n handler(newPath: string, oldPath: string) {\n\n let lottieAnimationView = this.$el\n\n if (lottieAnimationView != null && !TextUtils.isEmpty(newPath)) {\n if (newPath.startsWith(\"http://\") || newPath.startsWith(\"https://\")) {\n lottieAnimationView.setAnimationFromUrl(newPath)\n } else {\n // 默认是asset了\n lottieAnimationView.setAnimation(newPath)\n }\n }\n if (this.autoplay) {\n lottieAnimationView.playAnimation()\n }\n },\n immediate: false //创建时是否通过此方法更新属性,默认值为false \n },\n \"loop\": {\n handler(newLoop: Boolean, oldLoop: Boolean) {\n\n if (newLoop) {\n this.$el.repeatCount = Int.MAX_VALUE\n } else {\n // 不循环则设置成1次\n this.$el.repeatCount = 0\n }\n\n if (this.autoplay) {\n this.$el.playAnimation()\n }\n },\n immediate: false //创建时是否通过此方法更新属性,默认值为false \n },\n\n \"autoplay\": {\n handler(newValue: boolean, oldValue: boolean) {\n\n if (newValue) {\n this.$el.playAnimation()\n }\n },\n immediate: false //创建时是否通过此方法更新属性,默认值为false \n },\n\n \"action\": {\n handler(newAction: string, oldAction: string) {\n\n if (newAction == \"play\" || newAction == \"pause\" || newAction == \"stop\") {\n\n\n if (this.action == \"play\") {\n this.$el.playAnimation()\n } else if (this.action == \"play\") {\n this.$el.pauseAnimation()\n } else if (this.action == \"stop\") {\n this.$el.cancelAnimation()\n this.$el.clearAnimation()\n }\n\n } else {\n // 非法入参,不管\n }\n },\n immediate: false //创建时是否通过此方法更新属性,默认值为false \n },\n\n \"hidden\": {\n handler(newValue: boolean, oldValue: boolean) {\n\n if (newValue) {\n this.$el.visibility = View.GONE\n } else {\n this.$el.visibility = View.VISIBLE\n }\n },\n immediate: false //创建时是否通过此方法更新属性,默认值为false \n },\n\n },\n methods: {\n setRepeatMode(repeat: string) {\n if (\"RESTART\" == repeat) {\n this.$el.repeatMode = LottieDrawable.RESTART\n } else if (\"REVERSE\" == repeat) {\n this.$el.repeatMode = LottieDrawable.RESTART\n }\n },\n privateMethod() {\t//如何定义不对外暴露的API? 暂不支持,需在export外写 \n }\n },\n created() {\t\t\t//创建组件,替换created \n\n },\n NVBeforeLoad() {\t\t//组件将要创建,对应前端beforeMount \n //可选实现,这里可以提前做一些操作 \n },\n NVLoad(): LottieAnimationView { //创建原生View,必须定义返回值类型(Android需要明确知道View类型,需特殊校验) \n //必须实现 \n let lottieAnimationView = new LottieAnimationView(getContext())\n return lottieAnimationView\n },\n NVLoaded() {\t\t\t//原生View已创建 \n //可选实现,这里可以做后续操作 \n this.$el.repeatMode = LottieDrawable.RESTART;\n this.$el.visibility = View.GONE\n this.$el.repeatCount = 0\n this.$el.addAnimatorListener(new CustomAnimListener(this))\n\n },\n NVLayouted() {\t//原生View布局完成 \n //可选实现,这里可以做布局后续操作 \n },\n NVBeforeUnload() {\t\t//原生View将释放 \n //可选实现,这里可以做释放View之前的操作 \n },\n NVUnloaded() {\t\t\t//原生View已释放 \n //可选实现,这里可以做释放View之后的操作 \n },\n unmounted() {\t//组件销毁 \n //可选实现 \n }\n}\n\n\n\n\n\n\n\n\n"],"names":[],"mappings":"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"}
\ No newline at end of file
{"version":3,"sources":["uni_modules/test-component/utssdk/app-android/index.vue"],"sourcesContent":["\n\n\n\n\n\nimport Animator from 'android.animation.Animator'\nimport TextUtils from 'android.text.TextUtils'\nimport View from 'android.view.View'\nimport LottieAnimationView from 'com.airbnb.lottie.LottieAnimationView'\nimport LottieDrawable from 'com.airbnb.lottie.LottieDrawable'\n\n\nclass CustomAnimListener extends Animator.AnimatorListener {\n\n comp: UTSComponent<LottieAnimationView>\n constructor(com: UTSComponent<LottieAnimationView>) {\n super();\n this.comp = com\n }\n\n override onAnimationStart(animation: Animator | null) {\n }\n\n override onAnimationEnd(animation: Animator | null, isReverse: Boolean) {\n this.comp.emit(\"bindended\")\n }\n\n override onAnimationEnd(animation: Animator | null) {\n }\n\n override onAnimationCancel(animation: Animator | null) {\n }\n\n override onAnimationRepeat(animation: Animator | null) {\n }\n}\n\n//原生提供以下属性或方法的实现 \nexport default {\n name: \"animation-view\",\n /**\n * 当播放到末尾时触发 ended 事件(自然播放结束会触发回调,循环播放结束及手动停止动画不会触发)\n */\n emits: ['bindended'],\n props: {\n /**\n * 动画资源地址,目前只支持绝对路径\n */\n \"path\": {\n type: String,\n\t\t\tdefault:\"\"\n },\n /**\n * 动画是否循环播放\n */\n \"autoplay\": {\n type: Boolean,\n\t\t\tdefault:false\n },\n /**\n * 动画是否自动播放\n */\n \"loop\": {\n type: Boolean,\n\t\t\tdefault:false\n },\n /**\n * 是否隐藏动画\n */\n \"hidden\": {\n type: Boolean,\n\t\t\tdefault:false\n },\n /**\n * 动画操作,可取值 play、pause、stop\n */\n \"action\": {\n type: String,\n\t\t\tdefault:\"stop\"\n }\n\n },\n data() {\n return {\n\n }\n },\n watch: {\n \"path\": {\n handler(newPath: string, oldPath: string) {\n\n let lottieAnimationView = this.$el\n\n if (lottieAnimationView != null && !TextUtils.isEmpty(newPath)) {\n if (newPath.startsWith(\"http://\") || newPath.startsWith(\"https://\")) {\n lottieAnimationView.setAnimationFromUrl(newPath)\n } else {\n // 默认是asset了\n lottieAnimationView.setAnimation(newPath)\n }\n }\n if (this.autoplay) {\n lottieAnimationView.playAnimation()\n }\n },\n immediate: false //创建时是否通过此方法更新属性,默认值为false \n },\n \"loop\": {\n handler(newLoop: Boolean, oldLoop: Boolean) {\n\n if (newLoop) {\n this.$el.repeatCount = Int.MAX_VALUE\n } else {\n // 不循环则设置成1次\n this.$el.repeatCount = 0\n }\n\n if (this.autoplay) {\n this.$el.playAnimation()\n }\n },\n immediate: false //创建时是否通过此方法更新属性,默认值为false \n },\n\n \"autoplay\": {\n handler(newValue: boolean, oldValue: boolean) {\n\n if (newValue) {\n this.$el.playAnimation()\n }\n },\n immediate: false //创建时是否通过此方法更新属性,默认值为false \n },\n\n \"action\": {\n handler(newAction: string, oldAction: string) {\n\n if (newAction == \"play\" || newAction == \"pause\" || newAction == \"stop\") {\n\n\n if (this.action == \"play\") {\n this.$el.playAnimation()\n } else if (this.action == \"play\") {\n this.$el.pauseAnimation()\n } else if (this.action == \"stop\") {\n this.$el.cancelAnimation()\n this.$el.clearAnimation()\n }\n\n } else {\n // 非法入参,不管\n }\n },\n immediate: false //创建时是否通过此方法更新属性,默认值为false \n },\n\n \"hidden\": {\n handler(newValue: boolean, oldValue: boolean) {\n\n if (newValue) {\n this.$el.visibility = View.GONE\n } else {\n this.$el.visibility = View.VISIBLE\n }\n },\n immediate: false //创建时是否通过此方法更新属性,默认值为false \n },\n\n },\n methods: {\n setRepeatMode(repeat: string) {\n if (\"RESTART\" == repeat) {\n this.$el.repeatMode = LottieDrawable.RESTART\n } else if (\"REVERSE\" == repeat) {\n this.$el.repeatMode = LottieDrawable.RESTART\n }\n },\n privateMethod() {\t//如何定义不对外暴露的API? 暂不支持,需在export外写 \n }\n },\n created() {\t\t\t//创建组件,替换created \n\n },\n NVBeforeLoad() {\t\t//组件将要创建,对应前端beforeMount \n //可选实现,这里可以提前做一些操作 \n },\n NVLoad(): LottieAnimationView { //创建原生View,必须定义返回值类型(Android需要明确知道View类型,需特殊校验) \n //必须实现 \n let lottieAnimationView = new LottieAnimationView(getContext())\n return lottieAnimationView\n },\n NVLoaded() {\t\t\t//原生View已创建 \n //可选实现,这里可以做后续操作 \n this.$el.repeatMode = LottieDrawable.RESTART;\n this.$el.visibility = View.GONE\n this.$el.repeatCount = 0\n this.$el.addAnimatorListener(new CustomAnimListener(this))\n\n },\n NVLayouted() {\t//原生View布局完成 \n //可选实现,这里可以做布局后续操作 \n },\n NVBeforeUnload() {\t\t//原生View将释放 \n //可选实现,这里可以做释放View之前的操作 \n },\n NVUnloaded() {\t\t\t//原生View已释放 \n //可选实现,这里可以做释放View之后的操作 \n },\n unmounted() {\t//组件销毁 \n //可选实现 \n }\n}\n\n\n\n\n\n\n\n\n"],"names":[],"mappings":"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"}
\ No newline at end of file
......@@ -28,7 +28,7 @@ open class CustomAnimListener : Animator.AnimatorListener {
override fun onAnimationCancel(animation: Animator?) {}
override fun onAnimationRepeat(animation: Animator?) {}
}
open class AnimationView : UTSComponent<LottieAnimationView>, IMeasureAble {
open class AnimationViewComponent : UTSComponent<LottieAnimationView>, IMeasureAble {
constructor(instance: UniSDKInstance?, parent: AbsVContainer<*>?, componentData: AbsComponentData<*>?) : super(instance, parent, componentData) ;
override fun created() {}
override fun NVBeforeLoad() {}
......@@ -57,7 +57,7 @@ open class AnimationView : UTSComponent<LottieAnimationView>, IMeasureAble {
}
open fun privateMethod() {}
override fun `$init`() {
this.`$watch`("path", fun(newPath: String, oldPath: String){
this.`$watch`<String>("path", fun(newPath, oldPath){
var lottieAnimationView = this.`$el`;
if (lottieAnimationView != null && !TextUtils.isEmpty(newPath)) {
if (newPath.startsWith("http://") || newPath.startsWith("https://")) lottieAnimationView.setAnimationFromUrl(newPath);
......@@ -66,17 +66,17 @@ open class AnimationView : UTSComponent<LottieAnimationView>, IMeasureAble {
if (this.autoplay) lottieAnimationView.playAnimation();
}
);
this.`$watch`("loop", fun(newLoop: Boolean, oldLoop: Boolean){
this.`$watch`<Boolean>("loop", fun(newLoop, oldLoop){
if (newLoop) this.`$el`.repeatCount = Int.MAX_VALUE;
else this.`$el`.repeatCount = 0;
if (this.autoplay) this.`$el`.playAnimation();
}
);
this.`$watch`("autoplay", fun(newValue: Boolean, oldValue: Boolean){
this.`$watch`<Boolean>("autoplay", fun(newValue, oldValue){
if (newValue) this.`$el`.playAnimation();
}
);
this.`$watch`("action", fun(newAction: String, oldAction: String){
this.`$watch`<String>("action", fun(newAction, oldAction){
if (newAction == "play" || newAction == "pause" || newAction == "stop") {
if (this.action == "play") this.`$el`.playAnimation();
else if (this.action == "play") this.`$el`.pauseAnimation();
......@@ -87,7 +87,7 @@ open class AnimationView : UTSComponent<LottieAnimationView>, IMeasureAble {
}
}
);
this.`$watch`("hidden", fun(newValue: Boolean, oldValue: Boolean){
this.`$watch`<Boolean>("hidden", fun(newValue, oldValue){
if (newValue) this.`$el`.visibility = View.GONE;
else this.`$el`.visibility = View.VISIBLE;
}
......
......@@ -30,6 +30,7 @@ import { transformModel } from './transforms/vModel'
import { transformShow } from './transforms/vShow'
import { transformAttrs } from './transforms/transformAttrs'
import { nvuePagesCache } from '../plugins/pagesJson'
import { transformUTSComponent } from './transforms/transformUTSComponent'
const uTags = {
text: 'u-text',
......@@ -49,6 +50,7 @@ export function initNVueNodeTransforms() {
transformAttrs,
transformText,
transformVideo,
transformUTSComponent,
// transformRenderWhole,
// transformAppendAsTree,
]
......
import { isElementNode } from '@dcloudio/uni-cli-shared'
import { NodeTransform } from '@vue/compiler-core'
import { isUTSComponent } from '../../utils'
/**
* 将uts组件保存到自定义组件列表中
* @param node
* @param context
* @returns
*/
export const transformUTSComponent: NodeTransform = (node, context) => {
if (!isElementNode(node)) {
return
}
if (isUTSComponent(node.tag)) {
if (!context.root.components.includes(node.tag)) {
context.components.add(node.tag)
}
}
}
import { matchEasycom } from '@dcloudio/uni-cli-shared'
export function external(appService: boolean) {
return appService ? ['vue'] : ['vue', 'vuex', 'pinia']
}
......@@ -21,3 +23,8 @@ export function esbuildGlobals(appService: boolean): {
pinia: 'uni.Pinia',
}
}
export function isUTSComponent(tag: string) {
const source = matchEasycom(tag)
return !!(source && source.includes('uts-proxy'))
}
import fs from 'fs-extra'
import path from 'path'
import { isAppNativeTag, isAppNVueNativeTag } from '@dcloudio/uni-shared'
import {
isAppNativeTag,
isAppNVueNativeTag as baseIsAppNVueNativeTag,
} from '@dcloudio/uni-shared'
import { compileI18nJsonStr } from '@dcloudio/uni-i18n'
import {
UniVitePlugin,
......@@ -13,6 +16,11 @@ import {
import { initNVueNodeTransforms } from '../../nvue'
import { initNVueDirectiveTransforms } from '../../nvue/plugin'
import { isUTSComponent } from '../../nvue/utils'
function isAppNVueNativeTag(tag: string) {
return isUTSComponent(tag) || baseIsAppNVueNativeTag(tag)
}
export function uniOptions(): UniVitePlugin['uni'] {
const isNVueCompiler = process.env.UNI_COMPILER === 'nvue'
......
import path from 'path'
import { Plugin } from 'vite'
import { capitalize, camelize } from '@vue/shared'
import { createFilter, FilterPattern } from '@rollup/pluginutils'
import {
......@@ -45,7 +46,10 @@ export function uniEasycomPlugin(options: UniEasycomPluginOptions): Plugin {
addImportDeclaration(
importDeclarations,
`__easycom_${i++}`,
source
source,
source.includes('uts-proxy')
? capitalize(camelize(name)) + 'Component'
: ''
)
)
}
......
......@@ -7,7 +7,6 @@ import {
} from '@dcloudio/uni-cli-shared'
const UTSProxyRE = /\?uts-proxy$/
function isUTSProxy(id: string) {
return UTSProxyRE.test(id)
}
......@@ -17,6 +16,9 @@ export function uniUtsV1Plugin(): Plugin {
apply: 'build',
enforce: 'pre',
resolveId(id, importer) {
if (isUTSProxy(id)) {
return id
}
const module = resolveUtsAppModule(
id,
importer ? path.dirname(importer) : process.env.UNI_INPUT_DIR
......@@ -35,11 +37,12 @@ export function uniUtsV1Plugin(): Plugin {
if (opts && opts.ssr) {
return
}
if (!isUTSProxy(id)) {
return
}
const { filename: module } = parseVueRequest(id.replace('\0', ''))
const result = await resolveUTSCompiler().compile(module)
const { filename: pluginDir } = parseVueRequest(id.replace('\0', ''))
const result = await resolveUTSCompiler().compile(pluginDir)
if (result) {
result.deps.forEach((dep) => {
this.addWatchFile(dep)
......
import fs from 'fs'
import path from 'path'
import debug from 'debug'
import glob from 'fast-glob'
import { extend } from '@vue/shared'
import { createFilter } from '@rollup/pluginutils'
......@@ -45,6 +47,7 @@ export function initEasycoms(
{ dirs, platform }: { dirs: string[]; platform: UniApp.PLATFORM }
) {
const componentsDir = path.resolve(inputDir, 'components')
const utssdkDir = path.resolve(inputDir, 'utssdk')
const uniModulesDir = path.resolve(inputDir, 'uni_modules')
const initEasycomOptions = (pagesJson?: UniApp.PagesJson) => {
// 初始化时,从once中读取缓存,refresh时,实时读取
......@@ -57,7 +60,21 @@ export function initEasycoms(
...dirs,
componentsDir,
...initUniModulesEasycomDirs(uniModulesDir),
],
]
.concat(
platform === 'app'
? initUniModulesUTSSDKDirs(uniModulesDir)
: []
)
.concat(
platform === 'app' && fs.existsSync(utssdkDir)
? glob.sync('*', {
cwd: utssdkDir,
absolute: true,
onlyDirectories: true,
})
: []
),
rootDir: inputDir,
autoscan: !!(easycom && easycom.autoscan),
custom: (easycom && easycom.custom) || {},
......@@ -73,6 +90,8 @@ export function initEasycoms(
[
'components/*/*.(vue|jsx|tsx)',
'uni_modules/*/components/*/*.(vue|jsx|tsx)',
'utssdk/*/**/*.vue',
'uni_modules/*/utssdk/*/*.vue',
],
[],
{
......@@ -109,6 +128,25 @@ function initUniModulesEasycomDirs(uniModulesDir: string) {
.filter<string>(Boolean as any)
}
function initUniModulesUTSSDKDirs(uniModulesDir: string) {
if (!fs.existsSync(uniModulesDir)) {
return []
}
return fs
.readdirSync(uniModulesDir)
.map((uniModuleDir) => {
const uniModuleComponentsDir = path.resolve(
uniModulesDir,
uniModuleDir,
'utssdk'
)
if (fs.existsSync(uniModuleComponentsDir)) {
return uniModuleComponentsDir
}
})
.filter<string>(Boolean as any)
}
function initEasycom({
dirs,
rootDir,
......@@ -175,24 +213,59 @@ function initAutoScanEasycom(
if (!fs.existsSync(dir)) {
return easycoms
}
fs.readdirSync(dir).forEach((name) => {
const folder = path.resolve(dir, name)
if (!isDir(folder)) {
return
}
const importDir = normalizePath(folder)
const files = fs.readdirSync(folder)
// 读取文件夹文件列表,比对文件名(fs.existsSync在大小写不敏感的系统会匹配不准确)
for (let i = 0; i < extensions.length; i++) {
const ext = extensions[i]
if (files.includes(name + ext)) {
easycoms[`^${name}$`] = `${importDir}/${name}${ext}`
break
const is_uni_modules_utssdk = dir.endsWith('utssdk')
const is_ussdk =
!is_uni_modules_utssdk && path.dirname(dir).endsWith('utssdk')
if (is_uni_modules_utssdk || is_ussdk) {
glob
.sync('**/*.vue', {
cwd: dir,
absolute: true,
})
.forEach((file) => {
let name = parseVueComponentName(file)
if (!name) {
if (file.endsWith('index.vue')) {
name = path.basename(
is_uni_modules_utssdk ? path.dirname(dir) : dir
)
}
}
if (name) {
const importDir = normalizePath(
is_uni_modules_utssdk ? path.dirname(dir) : dir
)
easycoms[`^${name}$`] = `\0${importDir}?uts-proxy`
}
})
} else {
fs.readdirSync(dir).forEach((name) => {
const folder = path.resolve(dir, name)
if (!isDir(folder)) {
return
}
}
})
const importDir = normalizePath(folder)
const files = fs.readdirSync(folder)
// 读取文件夹文件列表,比对文件名(fs.existsSync在大小写不敏感的系统会匹配不准确)
for (let i = 0; i < extensions.length; i++) {
const ext = extensions[i]
if (files.includes(name + ext)) {
easycoms[`^${name}$`] = `${importDir}/${name}${ext}`
break
}
}
})
}
return easycoms
}
const nameRE = /name\s*:\s*['|"](.*)['|"]/
function parseVueComponentName(file: string) {
const content = fs.readFileSync(file, 'utf8')
const matches = content.match(nameRE)
if (matches) {
return matches[1]
}
}
function initAutoScanEasycoms(
dirs: string[],
......@@ -248,7 +321,7 @@ function createImportDeclaration(
imported?: string
) {
if (imported) {
return `import {${imported} as ${local}} from '${source}';`
return `import { ${imported} as ${local} } from '${source}';`
}
return `import ${local} from '${source}';`
}
......
......@@ -3476,7 +3476,7 @@ const Input = /* @__PURE__ */ defineBuiltInComponent({
resetCache = null;
}
if (input.validity && !input.validity.valid) {
if (!cache.value && event.data === "-" || cache.value[0] === "-" && event.inputType === "deleteContentBackward") {
if ((!cache.value || !input.value) && event.data === "-" || cache.value[0] === "-" && event.inputType === "deleteContentBackward") {
cache.value = "-";
state2.value = "";
resetCache = () => {
......
......@@ -9565,7 +9565,7 @@ const Input = /* @__PURE__ */ defineBuiltInComponent({
resetCache = null;
}
if (input.validity && !input.validity.valid) {
if (!cache.value && event.data === "-" || cache.value[0] === "-" && event.inputType === "deleteContentBackward") {
if ((!cache.value || !input.value) && event.data === "-" || cache.value[0] === "-" && event.inputType === "deleteContentBackward") {
cache.value = "-";
state3.value = "";
resetCache = () => {
......
......@@ -8,6 +8,7 @@ const errMsg = \`\`
const is_uni_modules = false
const pkg = initUtsPackageName(name, is_uni_modules)
const cls = initUtsIndexClassName(name, is_uni_modules)
export const onMemoryWarning = initUtsProxyFunction(false, { errMsg, main: true, package: pkg, class: cls, name: 'onMemoryWarning', params: [{"name":"callback","type":"UTSCallback"}]})
export const offMemoryWarning = initUtsProxyFunction(false, { errMsg, main: true, package: pkg, class: cls, name: 'offMemoryWarning', params: [{"name":"callback","type":"UTSCallback","default":"UTSNull"}]})
"
......
......@@ -9,9 +9,11 @@ describe('code', () => {
expect(
(
await genProxyCode(pluginDir, {
id: 'test-uts',
is_uni_modules: false,
name: 'test-uts',
namespace: 'uts.sdk.testUts',
extname: '.uts',
})
).replace(ERR_MSG_PLACEHOLDER, '')
).toMatchSnapshot()
......
......@@ -23,6 +23,7 @@ describe('compiler', () => {
inputDir,
outputDir,
sourceMap: false,
components: {},
})
expect(existsSync(kotlinFile)).toBeTruthy()
})
......@@ -36,6 +37,7 @@ describe('compiler', () => {
inputDir,
outputDir,
sourceMap: false,
components: {},
})
expect(existsSync(swiftFile)).toBeTruthy()
})
......
......@@ -16,10 +16,15 @@ import {
} from '../types/types'
import { createResolveTypeReferenceName, ERR_MSG_PLACEHOLDER } from './utils'
import { isInHBuilderX } from './shared'
import { camelize, capitalize } from '@vue/shared'
interface GenProxyCodeOptions {
is_uni_modules: boolean
id: string
name: string
extname: string
namespace: string
androidComponents?: Record<string, string>
iosComponents?: Record<string, string>
}
export async function genProxyCode(
......@@ -34,30 +39,55 @@ const errMsg = \`${ERR_MSG_PLACEHOLDER}\`
const is_uni_modules = ${is_uni_modules}
const pkg = initUtsPackageName(name, is_uni_modules)
const cls = initUtsIndexClassName(name, is_uni_modules)
${genComponentsCode(
options.androidComponents || {},
options.iosComponents || {}
)}
${genModuleCode(await parseModuleDecls(module, options))}
`
}
export function genComponentsCode(
androidComponents: Record<string, string>,
iosComponents: Record<string, string>
) {
const codes: string[] = []
Object.keys(Object.assign({}, androidComponents, iosComponents)).forEach(
(name) => {
codes.push(`export const ${capitalize(camelize(name))}Component = {}`)
}
)
return codes.join('\n')
}
export function resolveRootIndex(module: string, options: GenProxyCodeOptions) {
const filename = path.resolve(
module,
options.is_uni_modules ? 'utssdk' : '',
'index.uts'
`index${options.extname}`
)
return fs.existsSync(filename) && filename
}
export function resolvePlatformIndex(
export function resolvePlatformIndexFilename(
platform: 'app-android' | 'app-ios',
module: string,
options: GenProxyCodeOptions
) {
const filename = path.resolve(
return path.resolve(
module,
options.is_uni_modules ? 'utssdk' : '',
platform,
'index.uts'
`index${options.extname}`
)
}
export function resolvePlatformIndex(
platform: 'app-android' | 'app-ios',
module: string,
options: GenProxyCodeOptions
) {
const filename = resolvePlatformIndexFilename(platform, module, options)
return fs.existsSync(filename) && filename
}
......
import { isArray } from '@vue/shared'
import { extend, isArray } from '@vue/shared'
import { join, relative } from 'path'
import {
......@@ -14,8 +14,19 @@ import {
checkIOSVersionTips,
} from './swift'
import { genProxyCode, resolvePlatformIndex, resolveRootIndex } from './code'
import { ERR_MSG_PLACEHOLDER, resolvePackage } from './utils'
import {
genProxyCode,
resolvePlatformIndex,
resolvePlatformIndexFilename,
resolveRootIndex,
} from './code'
import {
ERR_MSG_PLACEHOLDER,
genConfigJson,
resolveAndroidComponents,
resolveIOSComponents,
resolvePackage,
} from './utils'
import { parseUtsSwiftPluginStacktrace } from './stacktrace'
import { resolveUtsPluginSourceMapFile } from './sourceMap'
import { isWindows } from './shared'
......@@ -66,18 +77,30 @@ export async function compile(pluginDir: string) {
const outputDir = process.env.UNI_OUTPUT_DIR
const utsPlatform = process.env.UNI_UTS_PLATFORM
const pluginRelativeDir = relative(inputDir, pluginDir)
const androidComponents = resolveAndroidComponents(
pluginDir,
pkg.is_uni_modules
)
const iosComponents = resolveIOSComponents(pluginDir, pkg.is_uni_modules)
const env = initCheckOptionsEnv()
const deps: string[] = []
const code = await genProxyCode(pluginDir, pkg)
const code = await genProxyCode(
pluginDir,
extend({ androidComponents, iosComponents }, pkg)
)
let errMsg = ''
if (process.env.NODE_ENV !== 'development') {
// 生产模式 支持同时生成 android 和 ios 的 uts 插件
if (utsPlatform === 'app-android' || utsPlatform === 'app') {
const filename =
let filename =
resolvePlatformIndex('app-android', pluginDir, pkg) ||
resolveRootIndex(pluginDir, pkg)
if (!filename && Object.keys(androidComponents).length) {
filename = resolvePlatformIndexFilename('app-android', pluginDir, pkg)
}
if (filename) {
await getCompiler('kotlin').runProd(filename)
await getCompiler('kotlin').runProd(filename, androidComponents)
if (cacheDir) {
genManifestFile('app-android', {
pluginDir,
......@@ -90,11 +113,14 @@ export async function compile(pluginDir: string) {
}
}
if (utsPlatform === 'app-ios' || utsPlatform === 'app') {
const filename =
let filename =
resolvePlatformIndex('app-ios', pluginDir, pkg) ||
resolveRootIndex(pluginDir, pkg)
if (!filename && Object.keys(androidComponents).length) {
filename = resolvePlatformIndexFilename('app-ios', pluginDir, pkg)
}
if (filename) {
await getCompiler('swift').runProd(filename)
await getCompiler('swift').runProd(filename, iosComponents)
if (cacheDir) {
genManifestFile('app-ios', {
pluginDir,
......@@ -132,6 +158,8 @@ export async function compile(pluginDir: string) {
}
}
if (utsPlatform === 'app-android' || utsPlatform === 'app-ios') {
const components =
utsPlatform === 'app-android' ? androidComponents : iosComponents
let tips = ''
// dev 模式
if (cacheDir) {
......@@ -169,6 +197,15 @@ export async function compile(pluginDir: string) {
cacheDir,
pkg.is_uni_modules
)
// 处理 config.json
genConfigJson(
utsPlatform,
components,
pluginRelativeDir,
pkg.is_uni_modules,
inputDir,
outputDir
)
console.log(cacheTips(pkg.id))
......@@ -186,10 +223,14 @@ export async function compile(pluginDir: string) {
}
}
}
const filename =
let filename =
resolvePlatformIndex(utsPlatform, pluginDir, pkg) ||
resolveRootIndex(pluginDir, pkg)
if (!filename && Object.keys(androidComponents).length) {
filename = resolvePlatformIndexFilename(utsPlatform, pluginDir, pkg)
}
if (filename) {
deps.push(filename)
if (utsPlatform === 'app-android') {
......@@ -197,8 +238,7 @@ export async function compile(pluginDir: string) {
} else {
deps.push(...resolveIOSDepFiles(filename))
}
const res = await getCompiler(compilerType).runDev(filename)
const res = await getCompiler(compilerType).runDev(filename, components)
if (res) {
if (isArray(res.deps) && res.deps.length) {
// 添加其他文件的依赖
......@@ -230,6 +270,15 @@ export async function compile(pluginDir: string) {
}
}
if (isSuccess) {
// 处理 config.json
genConfigJson(
utsPlatform,
components,
pluginRelativeDir,
pkg.is_uni_modules,
inputDir,
outputDir
)
// 生成缓存文件
if (cacheDir) {
// 存储 sourcemap
......
......@@ -23,6 +23,8 @@ import {
resolveUTSPlatformFile,
resolveUTSSourceMapPath,
ToKotlinOptions,
genComponentsCode,
parseKotlinPackageWithPluginId,
} from './utils'
import { Module } from '../types/types'
......@@ -56,23 +58,28 @@ function parseKotlinPackage(filename: string) {
return { package: '' }
}
return {
package: 'uts.sdk.' + (res.is_uni_modules ? 'modules.' : '') + res.name,
package: parseKotlinPackageWithPluginId(res.name, res.is_uni_modules),
}
}
export async function runKotlinProd(filename: string) {
export async function runKotlinProd(
filename: string,
components: Record<string, string>
) {
// 文件有可能是 app-ios 里边的,因为编译到 android 时,为了保证不报错,可能会去读取 ios 下的 uts
if (filename.includes('app-ios')) {
return
}
const inputDir = process.env.UNI_INPUT_DIR
const outputDir = process.env.UNI_OUTPUT_DIR
await compile(filename, { inputDir, outputDir, sourceMap: true })
await compile(filename, { inputDir, outputDir, sourceMap: true, components })
genUTSPlatformResource(filename, {
inputDir,
outputDir,
platform: 'app-android',
extname: '.kt',
components,
package: parseKotlinPackage(filename).package + '.',
})
}
......@@ -82,7 +89,8 @@ export type RunKotlinDevResult = UtsResult & {
}
export async function runKotlinDev(
filename: string
filename: string,
components: Record<string, string>
): Promise<RunKotlinDevResult | undefined> {
// 文件有可能是 app-ios 里边的,因为编译到 android 时,为了保证不报错,可能会去读取 ios 下的 uts
if (filename.includes('app-ios')) {
......@@ -94,6 +102,7 @@ export async function runKotlinDev(
inputDir,
outputDir,
sourceMap: true,
components,
})) as RunKotlinDevResult
result.type = 'kotlin'
......@@ -104,6 +113,8 @@ export async function runKotlinDev(
outputDir,
platform: 'app-android',
extname: '.kt',
components,
package: '',
})
// 开发模式下,需要生成 dex
if (fs.existsSync(kotlinFile)) {
......@@ -279,7 +290,7 @@ const DEFAULT_IMPORTS = [
export async function compile(
filename: string,
{ inputDir, outputDir, sourceMap }: ToKotlinOptions
{ inputDir, outputDir, sourceMap, components }: ToKotlinOptions
) {
const { bundle, UtsTarget } = getUtsCompiler()
// let time = Date.now()
......@@ -288,11 +299,21 @@ export async function compile(
if (rClass) {
imports.push(rClass)
}
const componentsCode = genComponentsCode(filename, components)
const input: Parameters<typeof bundle>[1]['input'] = {
root: inputDir,
filename,
}
if (componentsCode) {
if (!fs.existsSync(filename)) {
input.fileContent = componentsCode
} else {
input.fileContent =
fs.readFileSync(filename, 'utf8') + `\n` + componentsCode
}
}
const result = await bundle(UtsTarget.KOTLIN, {
input: {
root: inputDir,
filename,
},
input,
output: {
isPlugin: true,
outDir: outputDir,
......@@ -310,6 +331,8 @@ export async function compile(
outputDir,
platform: 'app-android',
extname: '.kt',
components,
package: '',
})
return result
}
......
import fs from 'fs-extra'
import path, { join } from 'path'
import { capitalize } from '@vue/shared'
import {
genComponentsCode,
genUTSPlatformResource,
getCompilerServer,
getUtsCompiler,
moveRootIndexSourceMap,
parseSwiftPackageWithPluginId,
resolveIOSDir,
resolvePackage,
resolveUTSPlatformFile,
......@@ -22,26 +23,30 @@ function parseSwiftPackage(filename: string) {
namespace: '',
}
}
const namespace =
'UTSSDK' + (res.is_uni_modules ? 'Modules' : '') + capitalize(res.name)
const namespace = parseSwiftPackageWithPluginId(res.name, res.is_uni_modules)
return {
namespace,
}
}
export async function runSwiftProd(filename: string) {
export async function runSwiftProd(
filename: string,
components: Record<string, string>
) {
// 文件有可能是 app-android 里边的,因为编译到 ios 时,为了保证不报错,可能会去读取 android 下的 uts
if (filename.includes('app-android')) {
return
}
const inputDir = process.env.UNI_INPUT_DIR
const outputDir = process.env.UNI_OUTPUT_DIR
await compile(filename, { inputDir, outputDir, sourceMap: true })
await compile(filename, { inputDir, outputDir, sourceMap: true, components })
genUTSPlatformResource(filename, {
inputDir,
outputDir,
platform: 'app-ios',
extname: '.swift',
components,
package: parseSwiftPackage(filename).namespace,
})
}
......@@ -53,7 +58,10 @@ export type RunSwiftDevResult = UtsResult & {
}
let isEnvReady = true
export async function runSwiftDev(filename: string) {
export async function runSwiftDev(
filename: string,
components: Record<string, string>
) {
// 文件有可能是 app-android 里边的,因为编译到 ios 时,为了保证不报错,可能会去读取 android 下的 uts
if (filename.includes('app-android')) {
return
......@@ -82,6 +90,7 @@ export async function runSwiftDev(filename: string) {
inputDir,
outputDir,
sourceMap: true,
components,
})) as RunSwiftDevResult
result.type = 'swift'
......@@ -91,6 +100,8 @@ export async function runSwiftDev(filename: string) {
outputDir,
platform: 'app-ios',
extname: '.swift',
components,
package: '',
})
result.changed = []
// 开发模式下,需要生成 framework
......@@ -133,15 +144,24 @@ function isCliProject(projectPath: string) {
export async function compile(
filename: string,
{ inputDir, outputDir, sourceMap }: ToSwiftOptions
{ inputDir, outputDir, sourceMap, components }: ToSwiftOptions
) {
const { bundle, UtsTarget } = getUtsCompiler()
// let time = Date.now()
const componentsCode = genComponentsCode(filename, components)
const input: Parameters<typeof bundle>[1]['input'] = {
root: inputDir,
filename,
}
if (componentsCode) {
if (!fs.existsSync(filename)) {
input.fileContent = componentsCode
} else {
input.fileAppendContent = componentsCode
}
}
const result = await bundle(UtsTarget.SWIFT, {
input: {
root: inputDir,
filename,
},
input,
output: {
isPlugin: true,
outDir: outputDir,
......@@ -159,6 +179,8 @@ export async function compile(
outputDir,
platform: 'app-ios',
extname: '.swift',
components,
package: '',
})
return result
}
......
import path from 'path'
import path, { basename, resolve } from 'path'
import fs from 'fs-extra'
import type { parse, bundle, UtsTarget } from '@dcloudio/uts'
import { camelize, capitalize } from '@vue/shared'
import { camelize, capitalize, extend } from '@vue/shared'
import glob from 'fast-glob'
import { Module, ModuleItem } from '../types/types'
import {
installHBuilderXPlugin,
normalizePath,
parseJson,
resolveSourceMapPath,
runByHBuilderX,
} from './shared'
......@@ -14,6 +16,7 @@ interface ToOptions {
inputDir: string
outputDir: string
sourceMap: boolean
components: Record<string, string>
}
export type ToKotlinOptions = ToOptions
export type ToSwiftOptions = ToOptions
......@@ -48,6 +51,7 @@ export function resolvePackage(filename: string) {
name,
namespace: 'UTSSDK' + (isUniModules ? 'Modules' : '') + capitalize(name),
is_uni_modules: isUniModules,
extname: '.uts',
}
}
}
......@@ -57,6 +61,8 @@ export interface UTSPlatformResourceOptions {
outputDir: string
platform: typeof process.env.UNI_UTS_PLATFORM
extname: '.kt' | '.swift'
components: Record<string, string>
package: string
}
export function genUTSPlatformResource(
filename: string,
......@@ -67,15 +73,20 @@ export function genUTSPlatformResource(
const utsInputDir = resolveUTSPlatformDir(filename, platform)
const utsOutputDir = resolveUTSPlatformDir(platformFile, platform)
// 拷贝所有非uts文件及目录
// 拷贝所有非uts,vue文件及目录
if (fs.existsSync(utsInputDir)) {
fs.copySync(utsInputDir, utsOutputDir, {
filter(src) {
return path.extname(src) !== '.uts'
if (src.endsWith('config.json')) {
return false
}
return !['.uts', '.vue'].includes(path.extname(src))
},
})
}
copyConfigJson(utsInputDir, utsOutputDir, options.components, options.package)
// 生产模式下,需要将生成的平台文件转移到 src 下
const srcDir = path.resolve(utsOutputDir, 'src')
if (!fs.existsSync(srcDir)) {
......@@ -203,3 +214,170 @@ export function getCompilerServer<T extends CompilerServer>(
}
}
}
function resolveComponents(
platform: 'app-android' | 'app-ios',
pluginDir: string,
is_uni_modules: boolean
) {
const components: Record<string, string> = {}
const platformDir = path.resolve(
pluginDir,
is_uni_modules ? 'utssdk' : '',
platform
)
if (fs.existsSync(platformDir)) {
glob
.sync('**/*.vue', { cwd: platformDir, absolute: true })
.forEach((file) => {
let name = parseVueComponentName(file)
if (!name) {
if (file.endsWith('index.vue')) {
name = path.basename(pluginDir)
}
}
if (name && !components[name]) {
components[name] = file
}
})
}
return components
}
export function resolveAndroidComponents(
pluginDir: string,
is_uni_modules: boolean
) {
return resolveComponents('app-android', pluginDir, is_uni_modules)
}
export function resolveIOSComponents(
pluginDir: string,
is_uni_modules: boolean
) {
return resolveComponents('app-ios', pluginDir, is_uni_modules)
}
const nameRE = /name\s*:\s*['|"](.*)['|"]/
function parseVueComponentName(file: string) {
const content = fs.readFileSync(file, 'utf8')
const matches = content.match(nameRE)
if (matches) {
return matches[1]
}
}
export function genComponentsCode(
filename: string,
components: Record<string, string>
) {
const codes: string[] = []
const dirname = path.dirname(filename)
Object.keys(components).forEach((name) => {
const source = normalizePath(path.relative(dirname, components[name]))
codes.push(
`export { default as ${capitalize(camelize(name))}Component } from '${
source.startsWith('.') ? source : './' + source
}'`
)
})
return codes.join('\n')
}
export function genConfigJson(
platform: 'app-android' | 'app-ios',
components: Record<string, string>,
pluginRelativeDir: string,
is_uni_modules: boolean,
inputDir: string,
outputDir: string
) {
if (!Object.keys(components).length) {
return
}
const pluginId = basename(pluginRelativeDir)
const utsInputDir = resolve(
inputDir,
pluginRelativeDir,
is_uni_modules ? 'utssdk' : '',
platform
)
const utsOutputDir = resolve(
outputDir,
pluginRelativeDir,
is_uni_modules ? 'utssdk' : '',
platform
)
copyConfigJson(
utsInputDir,
utsOutputDir,
components,
platform === 'app-android'
? parseKotlinPackageWithPluginId(pluginId, is_uni_modules) + '.'
: parseSwiftPackageWithPluginId(pluginId, is_uni_modules)
)
}
function copyConfigJson(
inputDir: string,
outputDir: string,
componentsObj: Record<string, string>,
namespace: string
) {
const configJsonFilename = resolve(inputDir, 'config.json')
const outputConfigJsonFilename = resolve(outputDir, 'config.json')
if (Object.keys(componentsObj).length) {
//存在组件
const components = genComponentsConfigJson(componentsObj, namespace)
if (fs.existsSync(configJsonFilename)) {
fs.outputFileSync(
outputConfigJsonFilename,
JSON.stringify(
extend(
{ components },
parseJson(fs.readFileSync(configJsonFilename, 'utf8'))
),
null,
2
)
)
} else {
fs.outputFileSync(
outputConfigJsonFilename,
JSON.stringify({ components }, null, 2)
)
}
} else {
if (fs.existsSync(configJsonFilename)) {
fs.copySync(configJsonFilename, outputConfigJsonFilename)
}
}
}
function genComponentsConfigJson(
components: Record<string, string>,
namespace: string
) {
const res: { name: string; class: string }[] = []
Object.keys(components).forEach((name) => {
res.push({
name,
class: namespace + capitalize(camelize(name)) + 'Component',
})
})
return res
}
export function parseKotlinPackageWithPluginId(
id: string,
is_uni_modules: boolean
) {
return 'uts.sdk.' + (is_uni_modules ? 'modules.' : '') + camelize(id)
}
export function parseSwiftPackageWithPluginId(
id: string,
is_uni_modules: boolean
) {
return 'UTSSDK' + (is_uni_modules ? 'Modules' : '') + capitalize(camelize(id))
}
......@@ -18,6 +18,8 @@ export type UtsParseOptions = UtsParserConfig & {
export type UtsInputOptions = UtsParseOptions & {
root: string
filename: string
fileContent?: string
fileAppendContent?: string
}
export type UtsOutputOptions = {
......
......@@ -91,8 +91,9 @@ async function testKotlinComponent() {
root: projectDir,
filename: path.resolve(
projectDir,
'uni_modules/test-component/utssdk/app-android/index.vue'
'uni_modules/test-component/utssdk/app-android/index.uts'
),
fileContent: `export { default as AnimationViewComponent } from './index.vue'`
},
output: {
outDir,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册