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

feat: renderjs

上级 c45940ca
...@@ -9,12 +9,13 @@ export function initAppConfig(appConfig: AppConfig) { ...@@ -9,12 +9,13 @@ export function initAppConfig(appConfig: AppConfig) {
const globalProperties = appConfig.globalProperties const globalProperties = appConfig.globalProperties
extend(globalProperties, instance) extend(globalProperties, instance)
if (__UNI_FEATURE_WXS__) { if (__UNI_FEATURE_WXS__) {
globalProperties.getComponentDescriptor = getComponentDescriptor //$getComponentDescriptor
Object.defineProperty(globalProperties, '$ownerInstance', { globalProperties.$gcd = getComponentDescriptor
get() { // Object.defineProperty(globalProperties, '$ownerInstance', {
return this.$getComponentDescriptor(this) // get() {
}, // return getComponentDescriptor(this)
}) // },
// })
// globalProperties.$handleWxsEvent = handleWxsEvent // globalProperties.$handleWxsEvent = handleWxsEvent
} }
} }
import { ComponentPublicInstance } from 'vue' import { ComponentPublicInstance } from 'vue'
import { isFunction, isPlainObject } from '@vue/shared' import { hyphenate, isFunction, isPlainObject } from '@vue/shared'
import { isBuiltInComponent } from '@dcloudio/uni-shared'
// import { normalizeEvent, findUniTarget } from './componentEvents' // import { normalizeEvent, findUniTarget } from './componentEvents'
interface WxsElement extends HTMLElement { interface WxsElement extends HTMLElement {
...@@ -192,7 +193,7 @@ function createComponentDescriptor( ...@@ -192,7 +193,7 @@ function createComponentDescriptor(
isOwnerInstance && isOwnerInstance &&
vm && vm &&
vm.$options.name && vm.$options.name &&
vm.$options.name.indexOf('VUni') === 0 isBuiltInComponent(hyphenate(vm.$options.name))
) { ) {
// ownerInstance 内置组件需要使用父 vm // ownerInstance 内置组件需要使用父 vm
vm = vm.$parent! vm = vm.$parent!
...@@ -206,11 +207,10 @@ function createComponentDescriptor( ...@@ -206,11 +207,10 @@ function createComponentDescriptor(
} }
export function getComponentDescriptor( export function getComponentDescriptor(
this: ComponentPublicInstance,
instance: ComponentPublicInstance, instance: ComponentPublicInstance,
isOwnerInstance: boolean isOwnerInstance: boolean
) { ) {
return createComponentDescriptor(instance || this, isOwnerInstance) return createComponentDescriptor(instance, isOwnerInstance)
} }
// export function handleWxsEvent(this: ComponentPublicInstance, $event: Event) { // export function handleWxsEvent(this: ComponentPublicInstance, $event: Event) {
......
...@@ -8424,9 +8424,32 @@ function patchStopImmediatePropagation(e, value) { ...@@ -8424,9 +8424,32 @@ function patchStopImmediatePropagation(e, value) {
} }
} }
function patchWxs(el, rawName, nextValue, instance = null) {
if (!el.__wxsWatches) {
el.__wxsWatches = {};
}
if (!nextValue) {
return el.__wxsWatches[rawName] && el.__wxsWatches[rawName]();
}
if (!el.__wxsWatches[rawName] && instance && instance.proxy) {
const proxy = instance.proxy;
const name = rawName.split(':')[1];
el.__wxsWatches[rawName] = proxy.$watch(() => instance.attrs[name], (value, oldValue) => {
// TODO ownerInstance,instance
nextValue(value, oldValue, proxy.$gcd(proxy, true), proxy.$gcd(proxy, true));
}, {
deep: true
});
}
}
const nativeOnRE = /^on[a-z]/; const nativeOnRE = /^on[a-z]/;
const forcePatchProp = (_, key) => key === 'value'; const forcePatchProp = (_, key) => key === 'value';
const patchProp = (el, key, prevValue, nextValue, isSVG = false, prevChildren, parentComponent, parentSuspense, unmountChildren) => { const patchProp = (el, key, prevValue, nextValue, isSVG = false, prevChildren, parentComponent, parentSuspense, unmountChildren) => {
// @ts-expect-error fixed by xxxxxx
if (__UNI_FEATURE_WXS__ && key.indexOf('change:') === 0) {
patchWxs(el, key, nextValue, parentComponent);
}
switch (key) { switch (key) {
// special // special
case 'class': case 'class':
......
...@@ -8424,9 +8424,32 @@ function patchStopImmediatePropagation(e, value) { ...@@ -8424,9 +8424,32 @@ function patchStopImmediatePropagation(e, value) {
} }
} }
function patchWxs(el, rawName, nextValue, instance = null) {
if (!el.__wxsWatches) {
el.__wxsWatches = {};
}
if (!nextValue) {
return el.__wxsWatches[rawName] && el.__wxsWatches[rawName]();
}
if (!el.__wxsWatches[rawName] && instance && instance.proxy) {
const proxy = instance.proxy;
const name = rawName.split(':')[1];
el.__wxsWatches[rawName] = proxy.$watch(() => instance.attrs[name], (value, oldValue) => {
// TODO ownerInstance,instance
nextValue(value, oldValue, proxy.$gcd(proxy, true), proxy.$gcd(proxy, true));
}, {
deep: true
});
}
}
const nativeOnRE = /^on[a-z]/; const nativeOnRE = /^on[a-z]/;
const forcePatchProp = (_, key) => key === 'value'; const forcePatchProp = (_, key) => key === 'value';
const patchProp = (el, key, prevValue, nextValue, isSVG = false, prevChildren, parentComponent, parentSuspense, unmountChildren) => { const patchProp = (el, key, prevValue, nextValue, isSVG = false, prevChildren, parentComponent, parentSuspense, unmountChildren) => {
// @ts-expect-error fixed by xxxxxx
if (__UNI_FEATURE_WXS__ && key.indexOf('change:') === 0) {
patchWxs(el, key, nextValue, parentComponent);
}
switch (key) { switch (key) {
// special // special
case 'class': case 'class':
......
import {isFunction, extend, isPlainObject, isString, isArray, hasOwn as hasOwn$1, isObject as isObject$1, capitalize, toRawType, makeMap as makeMap$1, isPromise, invokeArrayFns as invokeArrayFns$1, hyphenate} from "@vue/shared"; import {isFunction, extend, hyphenate, isPlainObject, isString, isArray, hasOwn as hasOwn$1, isObject as isObject$1, capitalize, toRawType, makeMap as makeMap$1, isPromise, invokeArrayFns as invokeArrayFns$1} from "@vue/shared";
import {injectHook, withModifiers, createVNode, inject, provide, reactive, computed, nextTick, getCurrentInstance, onBeforeMount, onMounted, onBeforeActivate, onBeforeDeactivate, openBlock, createBlock, mergeProps, toDisplayString, ref, defineComponent, resolveComponent, toHandlers, renderSlot, watch, onUnmounted, onBeforeUnmount, onActivated, withDirectives, vShow, createTextVNode, createCommentVNode, renderList, onDeactivated, createApp, watchEffect, Transition, withCtx, KeepAlive, resolveDynamicComponent, Fragment} from "vue"; import {injectHook, withModifiers, createVNode, inject, provide, reactive, computed, nextTick, getCurrentInstance, onBeforeMount, onMounted, onBeforeActivate, onBeforeDeactivate, openBlock, createBlock, mergeProps, toDisplayString, ref, defineComponent, resolveComponent, toHandlers, renderSlot, watch, onUnmounted, onBeforeUnmount, onActivated, withDirectives, vShow, createTextVNode, createCommentVNode, renderList, onDeactivated, createApp, watchEffect, Transition, withCtx, KeepAlive, resolveDynamicComponent, Fragment} from "vue";
import {once, passive, normalizeTarget, invokeArrayFns, NAVBAR_HEIGHT, parseQuery, PRIMARY_COLOR, removeLeadingSlash, getLen, ON_REACH_BOTTOM_DISTANCE, decodedQuery, debounce, updateElementStyle, addFont, scrollTo} from "@dcloudio/uni-shared"; import {once, passive, normalizeTarget, isBuiltInComponent, invokeArrayFns, NAVBAR_HEIGHT, parseQuery, PRIMARY_COLOR, removeLeadingSlash, getLen, ON_REACH_BOTTOM_DISTANCE, decodedQuery, debounce, updateElementStyle, addFont, scrollTo} from "@dcloudio/uni-shared";
import {useRoute, createRouter, createWebHistory, createWebHashHistory, useRouter, isNavigationFailure, RouterView} from "vue-router"; import {useRoute, createRouter, createWebHistory, createWebHashHistory, useRouter, isNavigationFailure, RouterView} from "vue-router";
function applyOptions(options, instance2, publicThis) { function applyOptions(options, instance2, publicThis) {
Object.keys(options).forEach((name) => { Object.keys(options).forEach((name) => {
...@@ -1087,7 +1087,7 @@ class ComponentDescriptor { ...@@ -1087,7 +1087,7 @@ class ComponentDescriptor {
} }
} }
function createComponentDescriptor(vm, isOwnerInstance = true) { function createComponentDescriptor(vm, isOwnerInstance = true) {
if (isOwnerInstance && vm && vm.$options.name && vm.$options.name.indexOf("VUni") === 0) { if (isOwnerInstance && vm && vm.$options.name && isBuiltInComponent(hyphenate(vm.$options.name))) {
vm = vm.$parent; vm = vm.$parent;
} }
if (vm && vm.$el) { if (vm && vm.$el) {
...@@ -1098,18 +1098,13 @@ function createComponentDescriptor(vm, isOwnerInstance = true) { ...@@ -1098,18 +1098,13 @@ function createComponentDescriptor(vm, isOwnerInstance = true) {
} }
} }
function getComponentDescriptor(instance2, isOwnerInstance) { function getComponentDescriptor(instance2, isOwnerInstance) {
return createComponentDescriptor(instance2 || this, isOwnerInstance); return createComponentDescriptor(instance2, isOwnerInstance);
} }
function initAppConfig$1(appConfig) { function initAppConfig$1(appConfig) {
const globalProperties = appConfig.globalProperties; const globalProperties = appConfig.globalProperties;
extend(globalProperties, instance); extend(globalProperties, instance);
if (__UNI_FEATURE_WXS__) { if (__UNI_FEATURE_WXS__) {
globalProperties.getComponentDescriptor = getComponentDescriptor; globalProperties.$gcd = getComponentDescriptor;
Object.defineProperty(globalProperties, "$ownerInstance", {
get() {
return this.$getComponentDescriptor(this);
}
});
} }
} }
function initView(app) { function initView(app) {
...@@ -13144,7 +13139,7 @@ function useToastIcon(props2) { ...@@ -13144,7 +13139,7 @@ function useToastIcon(props2) {
let showToastState; let showToastState;
let showType = ""; let showType = "";
let timeoutId; let timeoutId;
const onHidePopupOnce = once(() => { const onHidePopupOnce = /* @__PURE__ */ once(() => {
UniServiceJSBridge.on("onHidePopup", () => hidePopup("onHidePopup")); UniServiceJSBridge.on("onHidePopup", () => hidePopup("onHidePopup"));
}); });
function createToast(args) { function createToast(args) {
......
...@@ -30,7 +30,7 @@ import { once } from '@dcloudio/uni-shared' ...@@ -30,7 +30,7 @@ import { once } from '@dcloudio/uni-shared'
let showToastState: ToastProps let showToastState: ToastProps
let showType: 'onShowToast' | 'onShowLoading' | '' = '' let showType: 'onShowToast' | 'onShowLoading' | '' = ''
let timeoutId: number let timeoutId: number
const onHidePopupOnce = once(() => { const onHidePopupOnce = /*#__PURE__*/ once(() => {
UniServiceJSBridge.on('onHidePopup', () => hidePopup('onHidePopup')) UniServiceJSBridge.on('onHidePopup', () => hidePopup('onHidePopup'))
}) })
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`wxs normalizeWxsCode 1`] = `
Array [
Array [],
"<template><view></view><view></view></template>
<script>
export default {}
</script>
<renderjs name=\\"echarts\\">
export default{
mounted(){
console.log('mounted')
}
}
</renderjs>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
</style>
",
]
`;
exports[`wxs normalizeWxsCode 2`] = `
Array [
Array [],
"<template><view></view><view></view></template>
<script>
export default {}
</script>
<wxs name=\\"echarts\\">
export default{
mounted(){
console.log('mounted')
}
}
</wxs>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
</style>
",
]
`;
import { normalizeWxsCode } from '../src/configResolved/plugins/preVue'
describe('wxs', () => {
test('normalizeWxsCode', () => {
expect(
normalizeWxsCode(`<template><view></view><view></view></template>
<script>
export default {}
</script>
<script lang="renderjs" module="echarts">
export default{
mounted(){
console.log('mounted')
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
</style>
`)
).toMatchSnapshot()
expect(
normalizeWxsCode(`<template><view></view><view></view></template>
<script>
export default {}
</script>
<script lang="wxs" module="echarts">
export default{
mounted(){
console.log('mounted')
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
</style>
`)
).toMatchSnapshot()
})
})
...@@ -68,7 +68,10 @@ export function uniEasycomPlugin(options: UniPluginFilterOptions): Plugin { ...@@ -68,7 +68,10 @@ export function uniEasycomPlugin(options: UniPluginFilterOptions): Plugin {
return return
} }
const { filename, query } = parseVueRequest(id) const { filename, query } = parseVueRequest(id)
if (query.vue || !EXTNAME_VUE.includes(path.extname(filename))) { if (
query.type !== 'template' &&
(query.vue || !EXTNAME_VUE.includes(path.extname(filename)))
) {
return return
} }
debugEasycom(id) debugEasycom(id)
......
...@@ -15,6 +15,8 @@ import { uniPageVuePlugin } from './pageVue' ...@@ -15,6 +15,8 @@ import { uniPageVuePlugin } from './pageVue'
import { uniCopyPlugin } from './copy' import { uniCopyPlugin } from './copy'
import { uniStaticPlugin } from './static' import { uniStaticPlugin } from './static'
import { uniCssScopedPlugin } from './cssScoped' import { uniCssScopedPlugin } from './cssScoped'
import { uniRenderjsPlugin } from './renderjs'
import { uniPreVuePlugin } from './preVue'
const debugPlugin = debug('uni:plugin') const debugPlugin = debug('uni:plugin')
...@@ -28,14 +30,13 @@ const UNI_H5_RE = /@dcloudio\/uni-h5/ ...@@ -28,14 +30,13 @@ const UNI_H5_RE = /@dcloudio\/uni-h5/
const COMMON_EXCLUDE = [ const COMMON_EXCLUDE = [
/pages\.json\.js$/, /pages\.json\.js$/,
/manifest\.json\.js$/, /manifest\.json\.js$/,
/vue&type=/,
/vite\//, /vite\//,
/\/@vue\//, /\/@vue\//,
/\/vue-router\//, /\/vue-router\//,
/\/vuex\//, /\/vuex\//,
/@dcloudio\/uni-h5-vue/, /@dcloudio\/uni-h5-vue/,
/@dcloudio\/uni-shared/, /@dcloudio\/uni-shared/,
/\.html$/, /@dcloudio\/uni-components\/style/,
] ]
const APP_VUE_RE = /App.vue$/ const APP_VUE_RE = /App.vue$/
...@@ -112,6 +113,9 @@ export function initPlugins( ...@@ -112,6 +113,9 @@ export function initPlugins(
uniPreCssPlugin(Object.assign(uniPreCssPluginOptions, options)), uniPreCssPlugin(Object.assign(uniPreCssPluginOptions, options)),
'vite:css' 'vite:css'
) )
addPlugin(plugins, uniPreVuePlugin(), 'vite:vue', 'pre')
addPlugin(plugins, uniRenderjsPlugin(), 'vite:vue')
if (command === 'build') { if (command === 'build') {
addPlugin( addPlugin(
plugins, plugins,
......
...@@ -26,11 +26,12 @@ export function uniPrePlugin(options: UniPluginFilterOptions): Plugin { ...@@ -26,11 +26,12 @@ export function uniPrePlugin(options: UniPluginFilterOptions): Plugin {
return code return code
} }
const { filename, query } = parseVueRequest(id) const { filename, query } = parseVueRequest(id)
if (query.vue) { if (query.vue && query.type !== 'template') {
return code return code
} }
const extname = path.extname(filename) const extname = path.extname(filename)
const isHtml = PRE_HTML_EXTNAME.includes(extname) const isHtml =
query.type === 'template' || PRE_HTML_EXTNAME.includes(extname)
const isJs = PRE_JS_EXTNAME.includes(extname) const isJs = PRE_JS_EXTNAME.includes(extname)
const isPre = isHtml || isJs const isPre = isHtml || isJs
if (isPre) { if (isPre) {
...@@ -49,8 +50,8 @@ export function uniPrePlugin(options: UniPluginFilterOptions): Plugin { ...@@ -49,8 +50,8 @@ export function uniPrePlugin(options: UniPluginFilterOptions): Plugin {
// 读取sourcemap时,需要移除?mpType=page等参数,否则读取不到提示文件不存在 // 读取sourcemap时,需要移除?mpType=page等参数,否则读取不到提示文件不存在
const map = this.getCombinedSourcemap() const map = this.getCombinedSourcemap()
if (map) { if (map) {
map.sources = map.sources.map((source) => map.sources = map.sources.map(
source.replace('?mpType=page', '') (source) => parseVueRequest(source).filename
) )
} }
return { return {
......
import path from 'path'
import debug from 'debug'
import { Plugin } from 'vite'
import {
TextModes,
NodeTypes,
ElementNode,
AttributeNode,
} from '@vue/compiler-core'
import { parse } from '@vue/compiler-dom'
import { MagicString } from '@vue/compiler-sfc'
import { EXTNAME_VUE, parseVueRequest } from '@dcloudio/uni-cli-shared'
const debugPreVue = debug('uni:pre-vue')
const WXS_LANG_RE = /lang=["|'](renderjs|wxs)["|']/
const WXS_ATTRS = ['wxs', 'renderjs']
const sourceToSFC = new Map<string, string>()
export function uniPreVuePlugin(): Plugin {
return {
name: 'vite:uni-pre-vue',
transform(code, id) {
const { filename, query } = parseVueRequest(id)
if (query.vue) {
return
}
if (!EXTNAME_VUE.includes(path.extname(filename))) {
return
}
if (!WXS_LANG_RE.test(code)) {
return
}
const sourceKey = code + filename
const cache = sourceToSFC.get(sourceKey)
if (cache) {
debugPreVue('cache', id)
return cache
}
debugPreVue(id)
const [errors, wxsCode] = normalizeWxsCode(code)
if (errors.length) {
this.error(errors.join('\n'))
}
sourceToSFC.set(sourceKey, wxsCode)
return wxsCode
},
}
}
export function normalizeWxsCode(code: string): [SyntaxError[], string] {
const errors: SyntaxError[] = []
const ast = parse(code, {
isNativeTag: () => true,
isPreTag: () => true,
getTextMode: () => TextModes.DATA,
onError: (e) => {
errors.push(e)
},
})
const wxsNode = ast.children.find(
(node) =>
node.type === NodeTypes.ELEMENT &&
node.tag === 'script' &&
node.props.find(
(prop) =>
prop.name === 'lang' &&
prop.type === NodeTypes.ATTRIBUTE &&
prop.value &&
WXS_ATTRS.includes(prop.value.content)
)
)
if (wxsNode) {
code = normalizeWxsNode(code, wxsNode as ElementNode)
}
return [errors, code]
}
const SCRIPT_END_LEN = '</script>'.length
const SCRIPT_START_LEN = '<script'.length
function normalizeWxsNode(code: string, { loc, props }: ElementNode) {
const magicString = new MagicString(code)
const langAttr = props.find((prop) => prop.name === 'lang') as AttributeNode
const moduleAttr = props.find(
(prop) => prop.name === 'module'
) as AttributeNode
const startOffset = loc.start.offset
const endOffset = loc.end.offset
const lang = langAttr.value!.content
const langStartOffset = langAttr.loc.start.offset
magicString.overwrite(startOffset, startOffset + SCRIPT_START_LEN, '<' + lang) // <renderjs or <wxs
magicString.overwrite(
langStartOffset,
langStartOffset + ('lang="' + lang + '"').length,
''
) // remove lang="renderjs" or lang="wxs"
magicString.overwrite(
endOffset - SCRIPT_END_LEN,
endOffset,
'</' + lang + '>'
) //</renderjs> or </wxs>
if (moduleAttr) {
const moduleStartOffset = moduleAttr.loc.start.offset
magicString.overwrite(
moduleStartOffset,
moduleStartOffset + 'module'.length,
'name'
) // module="echarts" => name="echarts"
}
return magicString.toString()
}
import debug from 'debug'
import { Plugin } from 'vite'
import { rewriteDefault } from '@vue/compiler-sfc'
import { parseVueRequest } from '@dcloudio/uni-cli-shared'
const debugRenderjs = debug('uni:renderjs')
export function uniRenderjsPlugin(): Plugin {
return {
name: 'vite:uni-renderjs',
transform(code, id) {
const isWxs = /vue&type=wxs/.test(id)
const isRenderjs = /vue&type=renderjs/.test(id)
if (!isWxs && !isRenderjs) {
return
}
const type = isWxs ? 'wxs' : 'renderjs'
const { query } = parseVueRequest(id)
debugRenderjs(id)
if (!(query as any).name) {
this.error(
`<script module="missing module name" lang="${type}">
${code}
</script>`
)
}
return `${rewriteDefault(code, '_sfc_' + type)}
export default Comp => {
if(!Comp.mixins){Comp.mixins = []}
Comp.mixins.push({beforeCreate(){ this['${(query as any).name}'] = this }})
Comp.mixins.push(_sfc_${type})
}`
},
}
}
import { ElementNode, NodeTransform } from '@vue/compiler-core'
import { CompilerOptions, SFCTemplateCompileOptions } from '@vue/compiler-sfc' import { CompilerOptions, SFCTemplateCompileOptions } from '@vue/compiler-sfc'
import { isNativeTag } from '@dcloudio/uni-shared' import { isNativeTag } from '@dcloudio/uni-shared'
const transform: NodeTransform = (node, ctx) => { import { matchMedia } from './transforms/matchMedia'
if (
process.env.UNI_PLATFORM !== 'mp-weixin' &&
(node as ElementNode).tag === 'match-media'
) {
;(node as ElementNode).tag = 'uni-match-media'
}
}
export const uniVueCompilerOptions: CompilerOptions = { export const uniVueCompilerOptions: CompilerOptions = {
isNativeTag, isNativeTag,
nodeTransforms: [transform], nodeTransforms: [matchMedia],
} }
export const uniVueTransformAssetUrls: SFCTemplateCompileOptions['transformAssetUrls'] = { export const uniVueTransformAssetUrls: SFCTemplateCompileOptions['transformAssetUrls'] = {
......
import { ElementNode, NodeTransform } from '@vue/compiler-core'
export const matchMedia: NodeTransform = (node) => {
if (
process.env.UNI_PLATFORM !== 'mp-weixin' &&
(node as ElementNode).tag === 'match-media'
) {
;(node as ElementNode).tag = 'uni-match-media'
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册