Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
DCloud
uni-app
提交
0c535ff4
U
uni-app
项目概览
DCloud
/
uni-app
6 个月 前同步成功
通知
751
Star
38709
Fork
3642
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
8
列表
看板
标记
里程碑
合并请求
1
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
U
uni-app
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
8
Issue
8
列表
看板
标记
里程碑
合并请求
1
合并请求
1
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
0c535ff4
编写于
12月 21, 2023
作者:
fxy060608
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
wip(uvue): setup
上级
233fe0c6
变更
14
隐藏空白更改
内联
并排
Showing
14 changed file
with
2555 addition
and
32 deletion
+2555
-32
packages/uni-app-uts/__tests__/android/sfc/__snapshots__/compileScript.spec.ts.snap
...ts__/android/sfc/__snapshots__/compileScript.spec.ts.snap
+779
-0
packages/uni-app-uts/__tests__/android/sfc/compileScript.spec.ts
...s/uni-app-uts/__tests__/android/sfc/compileScript.spec.ts
+1559
-0
packages/uni-app-uts/__tests__/android/sfc/utils.ts
packages/uni-app-uts/__tests__/android/sfc/utils.ts
+58
-0
packages/uni-app-uts/__tests__/android/transforms/__snapshots__/transformExpressions.spec.ts.snap
...ransforms/__snapshots__/transformExpressions.spec.ts.snap
+2
-2
packages/uni-app-uts/__tests__/android/transforms/transformExpressions.spec.ts
...__tests__/android/transforms/transformExpressions.spec.ts
+2
-3
packages/uni-app-uts/src/plugins/android/uvue/compiler/codegen.ts
.../uni-app-uts/src/plugins/android/uvue/compiler/codegen.ts
+1
-1
packages/uni-app-uts/src/plugins/android/uvue/compiler/index.ts
...es/uni-app-uts/src/plugins/android/uvue/compiler/index.ts
+0
-1
packages/uni-app-uts/src/plugins/android/uvue/compiler/options.ts
.../uni-app-uts/src/plugins/android/uvue/compiler/options.ts
+4
-5
packages/uni-app-uts/src/plugins/android/uvue/compiler/transform.ts
...ni-app-uts/src/plugins/android/uvue/compiler/transform.ts
+3
-1
packages/uni-app-uts/src/plugins/android/uvue/compiler/transforms/transformExpression.ts
...s/android/uvue/compiler/transforms/transformExpression.ts
+6
-1
packages/uni-app-uts/src/plugins/android/uvue/index.ts
packages/uni-app-uts/src/plugins/android/uvue/index.ts
+0
-1
packages/uni-app-uts/src/plugins/android/uvue/sfc/compiler/compileScript.ts
...ts/src/plugins/android/uvue/sfc/compiler/compileScript.ts
+130
-14
packages/uni-app-uts/src/plugins/android/uvue/sfc/main.ts
packages/uni-app-uts/src/plugins/android/uvue/sfc/main.ts
+9
-2
packages/uni-app-uts/src/plugins/android/uvue/sfc/template.ts
...ages/uni-app-uts/src/plugins/android/uvue/sfc/template.ts
+2
-1
未找到文件。
packages/uni-app-uts/__tests__/android/sfc/__snapshots__/compileScript.spec.ts.snap
0 → 100644
浏览文件 @
0c535ff4
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SFC analyze <script> bindings auto name inference basic 1`] = `
"const a = 1
export default {
__name: 'FooBar',
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC analyze <script> bindings auto name inference do not overwrite manual name (call) 1`] = `
"import { defineComponent } from 'vue'
const __default__ = defineComponent({
name: 'Baz'
})
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
const a = 1
return "INLINE_RENDER"
}
}"
`;
exports[`SFC analyze <script> bindings auto name inference do not overwrite manual name (object) 1`] = `
"const __default__ = {
name: 'Baz'
}
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
const a = 1
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> binding analysis for destructure 1`] = `
"export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
const { foo, b: bar, ['x' + 'y']: baz, x: { y, zz: { z }}} = {}
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> dev mode import usage check TS annotations 1`] = `
"import { Foo, Bar, Baz, Qux, Fred } from './x'
const a = 1
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
function b() {}
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> dev mode import usage check attribute expressions 1`] = `
"import { bar, baz } from './x'
const cond = true
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> dev mode import usage check components 1`] = `
"import { FooBar, FooBaz, FooQux, foo } from './x'
const fooBar: FooBar = 1
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> dev mode import usage check directive 1`] = `
"import { vMyDir } from './x'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> dev mode import usage check dynamic arguments 1`] = `
"import { FooBar, foo, bar, unused, baz } from './x'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> dev mode import usage check js template string interpolations 1`] = `
"import { VAR, VAR2, VAR3 } from './x'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> dev mode import usage check last tag 1`] = `
"import { FooBaz, Last } from './x'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> dev mode import usage check template ref 1`] = `
"import { foo, bar, Baz } from './foo'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> dev mode import usage check vue interpolations 1`] = `
"import { x, y, z, x$y } from './x'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> errors should allow defineProps/Emit() referencing imported binding 1`] = `
"import { bar } from './bar'
export default {
props: {
foo: {
default: () => bar
}
},
emits: {
foo: () => bar > 1
},
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> errors should allow defineProps/Emit() referencing scope var 1`] = `
"const bar = 1
export default {
props: {
foo: {
default: bar => bar + 1
}
},
emits: {
foo: bar => bar > 1
},
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> imports dedupe between user & helper 1`] = `
"import { ref } from 'vue'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
let foo = $ref(1)
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> imports import dedupe between <script> and <script setup> 1`] = `
"import { x } from './x'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
x()
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> imports should allow defineProps/Emit at the start of imports 1`] = `
"import { ref } from 'vue'
export default {
props: ['foo'],
emits: ['bar'],
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
const r = ref(0)
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> imports should extract comment for import or type declarations 1`] = `
"import a from 'a' // comment
import b from 'b'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> imports should hoist and expose imports 1`] = `
"import { ref } from 'vue'
import 'foo/css'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> imports should support module string names syntax 1`] = `
"import { "😏" as foo } from './foo'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> inlineTemplate mode avoid unref() when necessary 1`] = `
"import { ref } from 'vue'
import Foo, { bar } from './Foo.vue'
import other from './util'
import * as tree from './tree'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
const count = ref(0)
const constant = {}
const maybe = foo()
let lett = 1
function fn() {}
return
function GenAnonymousRender(): VNode | null {
const _component_div = resolveComponent("div")
return createElementVNode(Fragment, null, [
createVNode(Foo, null, utsMapOf({
default: withSlotCtx((): any[] => [toDisplayString(unref(bar))]),
_: 1 /* STABLE */
})),
createVNode(_component_div, utsMapOf({ onClick: fn }), utsMapOf({
default: withSlotCtx((): any[] => [toDisplayString(count.value) + " " + toDisplayString(constant) + " " + toDisplayString(unref(maybe)) + " " + toDisplayString(unref(lett)) + " " + toDisplayString(unref(other))]),
_: 1 /* STABLE */
})),
" " + toDisplayString(tree.foo())
], 64 /* STABLE_FRAGMENT */)
}
}
}"
`;
exports[`SFC compile <script setup> inlineTemplate mode referencing scope components and directives 1`] = `
"import ChildComp from './Child.vue'
import SomeOtherComp from './Other.vue'
import vMyDir from './my-dir'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return
function GenAnonymousRender(): VNode | null {
const _component_div = resolveComponent("div")
return createElementVNode(Fragment, null, [
withDirectives(createVNode(_component_div, null, null, 512 /* NEED_PATCH */), [
[unref(vMyDir)]
]),
createVNode(ChildComp),
createVNode(SomeOtherComp)
], 64 /* STABLE_FRAGMENT */)
}
}
}"
`;
exports[`SFC compile <script setup> inlineTemplate mode should work 1`] = `
"import { ref } from 'vue'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
const count = ref(0)
return
function GenAnonymousRender(): VNode | null {
const _component_div = resolveComponent("div")
return createElementVNode(Fragment, null, [
createVNode(_component_div, null, utsMapOf({
default: withSlotCtx((): any[] => [toDisplayString(count.value)]),
_: 1 /* STABLE */
})),
createVNode(_component_div, null, utsMapOf({
default: withSlotCtx((): any[] => ["static"]),
_: 1 /* STABLE */
}))
], 64 /* STABLE_FRAGMENT */)
}
}
}"
`;
exports[`SFC compile <script setup> inlineTemplate mode template assignment expression codegen 1`] = `
"import { ref } from 'vue'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
const count = ref(0)
const maybe = foo()
let lett = 1
let v = ref(1)
return
function GenAnonymousRender(): VNode | null {
const _component_div = resolveComponent("div")
return createElementVNode(Fragment, null, [
createVNode(_component_div, utsMapOf({
onClick: () => {count.value = 1}
}), null, 8 /* PROPS */, ["onClick"]),
createVNode(_component_div, utsMapOf({
onClick: () => {maybe.value = count.value}
}), null, 8 /* PROPS */, ["onClick"]),
createVNode(_component_div, utsMapOf({
onClick: () => {isRef(lett) ? lett.value = count.value : lett = count.value}
}), null, 8 /* PROPS */, ["onClick"]),
createVNode(_component_div, utsMapOf({
onClick: () => {isRef(v) ? v.value += 1 : v += 1}
}), null, 8 /* PROPS */, ["onClick"]),
createVNode(_component_div, utsMapOf({
onClick: () => {isRef(v) ? v.value -= 1 : v -= 1}
}), null, 8 /* PROPS */, ["onClick"]),
createVNode(_component_div, utsMapOf({
onClick: () => {
let a = '' + unref(lett)
isRef(v) ? v.value = a : v = a
}
}), null, 8 /* PROPS */, ["onClick"]),
createVNode(_component_div, utsMapOf({
onClick: () => {
// nested scopes
(()=>{
let x = _ctx.a
(()=>{
let z = x
let z2 = z
})
let lz = _ctx.z
})
isRef(v) ? v.value = _ctx.a : v = _ctx.a
}
}), null, 8 /* PROPS */, ["onClick"])
], 64 /* STABLE_FRAGMENT */)
}
}
}"
`;
exports[`SFC compile <script setup> inlineTemplate mode template destructure assignment codegen 1`] = `
"import { ref } from 'vue'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
const val = {}
const count = ref(0)
const maybe = foo()
let lett = 1
return
function GenAnonymousRender(): VNode | null {
const _component_div = resolveComponent("div")
return createElementVNode(Fragment, null, [
createVNode(_component_div, utsMapOf({
onClick: () => {({ count: count.value } = val)}
}), null, 8 /* PROPS */, ["onClick"]),
createVNode(_component_div, utsMapOf({
onClick: () => {[maybe.value] = val}
}), null, 8 /* PROPS */, ["onClick"]),
createVNode(_component_div, utsMapOf({
onClick: () => {({ lett: lett } = val)}
}), null, 8 /* PROPS */, ["onClick"])
], 64 /* STABLE_FRAGMENT */)
}
}
}"
`;
exports[`SFC compile <script setup> inlineTemplate mode template update expression codegen 1`] = `
"import { ref } from 'vue'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
const count = ref(0)
const maybe = foo()
let lett = 1
return
function GenAnonymousRender(): VNode | null {
const _component_div = resolveComponent("div")
return createElementVNode(Fragment, null, [
createVNode(_component_div, utsMapOf({
onClick: () => {count.value++}
}), null, 8 /* PROPS */, ["onClick"]),
createVNode(_component_div, utsMapOf({
onClick: () => {--count.value}
}), null, 8 /* PROPS */, ["onClick"]),
createVNode(_component_div, utsMapOf({
onClick: () => {maybe.value++}
}), null, 8 /* PROPS */, ["onClick"]),
createVNode(_component_div, utsMapOf({
onClick: () => {--maybe.value}
}), null, 8 /* PROPS */, ["onClick"]),
createVNode(_component_div, utsMapOf({
onClick: () => {isRef(lett) ? lett.value++ : lett++}
}), null, 8 /* PROPS */, ["onClick"]),
createVNode(_component_div, utsMapOf({
onClick: () => {isRef(lett) ? --lett.value : --lett}
}), null, 8 /* PROPS */, ["onClick"])
], 64 /* STABLE_FRAGMENT */)
}
}
}"
`;
exports[`SFC compile <script setup> inlineTemplate mode v-model codegen 1`] = `
"import { ref } from 'vue'
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
const count = ref(0)
const maybe = foo()
let lett = 1
return
function GenAnonymousRender(): VNode | null {
return createElementVNode(Fragment, null, [
createElementVNode("input", utsMapOf({
modelValue: count.value,
onInput: ($event: InputEvent) => {(count).value = $event.detail.value}
}), null, 40 /* PROPS, NEED_HYDRATION */, ["modelValue", "onInput"]),
createElementVNode("input", utsMapOf({
modelValue: unref(maybe),
onInput: ($event: InputEvent) => {isRef(maybe) ? (maybe).value = $event.detail.value : null}
}), null, 40 /* PROPS, NEED_HYDRATION */, ["modelValue", "onInput"]),
createElementVNode("input", utsMapOf({
modelValue: unref(lett),
onInput: ($event: InputEvent) => {isRef(lett) ? (lett).value = $event.detail.value : lett = $event.detail.value}
}), null, 40 /* PROPS, NEED_HYDRATION */, ["modelValue", "onInput"])
], 64 /* STABLE_FRAGMENT */)
}
}
}"
`;
exports[`SFC compile <script setup> inlineTemplate mode with defineExpose() 1`] = `
"export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
const count = ref(0)
__expose({ count })
return function GenAnonymousRender(): VNode | null { return null }
}
}"
`;
exports[`SFC compile <script setup> should compile JS syntax 1`] = `
"const a = 1
const b = 2
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> should expose top level declarations 1`] = `
"import { x } from './x'
import { xx } from './x'
let aa = 1
const bb = 2
function cc() {}
class dd {}
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
let a = 1
const b = 2
function c() {}
class d {}
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> with TypeScript const Enum 1`] = `
"const enum Foo { A = 123 }
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> with TypeScript hoist type declarations 1`] = `
"export interface Foo {}
type Bar = {}
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> with TypeScript runtime Enum 1`] = `
"enum Foo { A = 123 }
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
return "INLINE_RENDER"
}
}"
`;
exports[`SFC compile <script setup> with TypeScript runtime Enum in normal script 1`] = `
"export enum D { D = "D" }
const enum C { C = "C" }
enum B { B = "B" }
export default {
setup() {
const __ins = getCurrentInstance()!;
const _ctx = __ins.proxy;
const _cache = __ins.renderCache;
enum Foo { A = 123 }
return "INLINE_RENDER"
}
}"
`;
packages/uni-app-uts/__tests__/android/sfc/compileScript.spec.ts
0 → 100644
浏览文件 @
0c535ff4
import
{
BindingTypes
}
from
'
@vue/compiler-core
'
import
{
compileSFCScript
as
compile
,
assertCode
}
from
'
./utils
'
describe
(
'
SFC compile <script setup>
'
,
()
=>
{
test
(
'
should compile JS syntax
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup lang='js'>
const a = 1
const b = 2
</script>
`
)
// expect(content).toMatch(`return { a, b }`)
assertCode
(
content
)
})
test
(
'
should expose top level declarations
'
,
()
=>
{
const
{
content
,
bindings
}
=
compile
(
`
<script setup>
import { x } from './x'
let a = 1
const b = 2
function c() {}
class d {}
</script>
<script>
import { xx } from './x'
let aa = 1
const bb = 2
function cc() {}
class dd {}
</script>
`
)
// expect(content).toMatch(
// `return { get aa() { return aa }, set aa(v) { aa = v }, ` +
// `bb, cc, dd, get a() { return a }, set a(v) { a = v }, b, c, d, ` +
// `get xx() { return xx }, get x() { return x } }`
// )
expect
(
bindings
).
toStrictEqual
({
x
:
BindingTypes
.
SETUP_MAYBE_REF
,
a
:
BindingTypes
.
SETUP_LET
,
b
:
BindingTypes
.
SETUP_CONST
,
c
:
BindingTypes
.
SETUP_CONST
,
d
:
BindingTypes
.
SETUP_CONST
,
xx
:
BindingTypes
.
SETUP_MAYBE_REF
,
aa
:
BindingTypes
.
SETUP_LET
,
bb
:
BindingTypes
.
LITERAL_CONST
,
cc
:
BindingTypes
.
SETUP_CONST
,
dd
:
BindingTypes
.
SETUP_CONST
,
})
assertCode
(
content
)
})
test
(
'
binding analysis for destructure
'
,
()
=>
{
const
{
content
,
bindings
}
=
compile
(
`
<script setup>
const { foo, b: bar, ['x' + 'y']: baz, x: { y, zz: { z }}} = {}
</script>
`
)
// expect(content).toMatch('return { foo, bar, baz, y, z }')
expect
(
bindings
).
toStrictEqual
({
foo
:
BindingTypes
.
SETUP_MAYBE_REF
,
bar
:
BindingTypes
.
SETUP_MAYBE_REF
,
baz
:
BindingTypes
.
SETUP_MAYBE_REF
,
y
:
BindingTypes
.
SETUP_MAYBE_REF
,
z
:
BindingTypes
.
SETUP_MAYBE_REF
,
})
assertCode
(
content
)
})
// describe('<script> and <script setup> co-usage', () => {
// test('script first', () => {
// const { content } = compile(`
// <script>
// export const n = 1
// export default {}
// </script>
// <script setup>
// import { x } from './x'
// x()
// </script>
// `)
// assertCode(content)
// })
// test('script setup first', () => {
// const { content } = compile(`
// <script setup>
// import { x } from './x'
// x()
// </script>
// <script>
// export const n = 1
// export default {}
// </script>
// `)
// assertCode(content)
// })
// // #7805
// test('keep original semi style', () => {
// const { content } = compile(`
// <script setup>
// console.log('test')
// const props = defineProps(['item']);
// const emit = defineEmits(['change']);
// // (function () {})()
// </script>
// `)
// assertCode(content)
// expect(content).toMatch(`console.log('test')`)
// expect(content).toMatch(`const props = __props;`)
// expect(content).toMatch(`const emit = __emit;`)
// // expect(content).toMatch(`(function () {})()`)
// })
// test('script setup first, named default export', () => {
// const { content } = compile(`
// <script setup>
// import { x } from './x'
// x()
// </script>
// <script>
// export const n = 1
// const def = {}
// export { def as default }
// </script>
// `)
// assertCode(content)
// })
// // #4395
// test('script setup first, lang="ts", script block content export default', () => {
// const { content } = compile(`
// <script setup lang="ts">
// import { x } from './x'
// x()
// </script>
// <script lang="ts">
// export default {
// name: "test"
// }
// </script>
// `)
// // ensure __default__ is declared before used
// expect(content).toMatch(/const __default__[\S\s]*\.\.\.__default__/m)
// assertCode(content)
// })
// describe('spaces in ExportDefaultDeclaration node', () => {
// // #4371
// test('with many spaces and newline', () => {
// // #4371
// const { content } = compile(`
// <script>
// export const n = 1
// export default
// {
// some:'option'
// }
// </script>
// <script setup>
// import { x } from './x'
// x()
// </script>
// `)
// assertCode(content)
// })
// test('with minimal spaces', () => {
// const { content } = compile(`
// <script>
// export const n = 1
// export default{
// some:'option'
// }
// </script>
// <script setup>
// import { x } from './x'
// x()
// </script>
// `)
// assertCode(content)
// })
// })
// test('export call expression as default', () => {
// const { content } = compile(`
// <script>
// function fn() {
// return "hello, world";
// }
// export default fn();
// </script>
// <script setup>
// console.log('foo')
// </script>
// `)
// assertCode(content)
// })
// })
describe
(
'
imports
'
,
()
=>
{
test
(
'
should hoist and expose imports
'
,
()
=>
{
assertCode
(
compile
(
`<script setup>
import { ref } from 'vue'
import 'foo/css'
</script>`
).
content
)
})
test
(
'
should extract comment for import or type declarations
'
,
()
=>
{
assertCode
(
compile
(
`
<script setup>
import a from 'a' // comment
import b from 'b'
</script>
`
).
content
)
})
// #2740
test
(
'
should allow defineProps/Emit at the start of imports
'
,
()
=>
{
assertCode
(
compile
(
`<script setup>
import { ref } from 'vue'
defineProps(['foo'])
defineEmits(['bar'])
const r = ref(0)
</script>`
).
content
)
})
test
(
'
dedupe between user & helper
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup>
import { ref } from 'vue'
let foo = $ref(1)
</script>
`
,
{
reactivityTransform
:
true
}
)
assertCode
(
content
)
expect
(
content
).
toMatch
(
`import { ref } from 'vue'`
)
})
test
(
'
import dedupe between <script> and <script setup>
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script>
import { x } from './x'
</script>
<script setup>
import { x } from './x'
x()
</script>
`
)
assertCode
(
content
)
expect
(
content
.
indexOf
(
`import { x }`
)).
toEqual
(
content
.
lastIndexOf
(
`import { x }`
)
)
})
describe
(
'
import ref/reactive function from other place
'
,
()
=>
{
test
(
'
import directly
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script setup>
import { ref, reactive } from './foo'
const foo = ref(1)
const bar = reactive(1)
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
ref
:
BindingTypes
.
SETUP_MAYBE_REF
,
reactive
:
BindingTypes
.
SETUP_MAYBE_REF
,
foo
:
BindingTypes
.
SETUP_MAYBE_REF
,
bar
:
BindingTypes
.
SETUP_MAYBE_REF
,
})
})
test
(
'
import w/ alias
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script setup>
import { ref as _ref, reactive as _reactive } from './foo'
const foo = ref(1)
const bar = reactive(1)
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
_reactive
:
BindingTypes
.
SETUP_MAYBE_REF
,
_ref
:
BindingTypes
.
SETUP_MAYBE_REF
,
foo
:
BindingTypes
.
SETUP_MAYBE_REF
,
bar
:
BindingTypes
.
SETUP_MAYBE_REF
,
})
})
test
(
'
aliased usage before import site
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script setup>
const bar = x(1)
import { reactive as x } from 'vue'
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
bar
:
BindingTypes
.
SETUP_REACTIVE_CONST
,
x
:
BindingTypes
.
SETUP_CONST
,
})
})
})
test
(
'
should support module string names syntax
'
,
()
=>
{
const
{
content
,
bindings
}
=
compile
(
`
<script>
import { "😏" as foo } from './foo'
</script>
<script setup>
import { "😏" as foo } from './foo'
</script>
`
)
assertCode
(
content
)
expect
(
bindings
).
toStrictEqual
({
foo
:
BindingTypes
.
SETUP_MAYBE_REF
,
})
})
})
// in dev mode, declared bindings are returned as an object from setup()
// when using TS, users may import types which should not be returned as
// values, so we need to check import usage in the template to determine
// what to be returned.
describe
(
'
dev mode import usage check
'
,
()
=>
{
test
(
'
components
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup lang="ts">
import { FooBar, FooBaz, FooQux, foo } from './x'
const fooBar: FooBar = 1
</script>
<template>
<FooBaz></FooBaz>
<foo-qux/>
<foo/>
FooBar
</template>
`
)
// FooBar: should not be matched by plain text or incorrect case
// FooBaz: used as PascalCase component
// FooQux: used as kebab-case component
// foo: lowercase component
// expect(content).toMatch(
// `return { fooBar, get FooBaz() { return FooBaz }, ` +
// `get FooQux() { return FooQux }, get foo() { return foo } }`
// )
assertCode
(
content
)
})
test
(
'
directive
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup lang="ts">
import { vMyDir } from './x'
</script>
<template>
<div v-my-dir></div>
</template>
`
)
// expect(content).toMatch(`return { get vMyDir() { return vMyDir } }`)
assertCode
(
content
)
})
test
(
'
dynamic arguments
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup lang="ts">
import { FooBar, foo, bar, unused, baz } from './x'
</script>
<template>
<FooBar #[foo.slotName] />
<FooBar #unused />
<div :[bar.attrName]="15"></div>
<div unused="unused"></div>
<div #[
\`
item:
\$
{baz.key}
\`
]="{ value }"></div>
</template>
`
)
// expect(content).toMatch(
// `return { get FooBar() { return FooBar }, get foo() { return foo }, ` +
// `get bar() { return bar }, get baz() { return baz } }`
// )
assertCode
(
content
)
})
// https://github.com/vuejs/core/issues/4599
test
(
'
attribute expressions
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup lang="ts">
import { bar, baz } from './x'
const cond = true
</script>
<template>
<div :class="[cond ? '' : bar(), 'default']" :style="baz"></div>
</template>
`
)
// expect(content).toMatch(
// `return { cond, get bar() { return bar }, get baz() { return baz } }`
// )
assertCode
(
content
)
})
test
(
'
vue interpolations
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup lang="ts">
import { x, y, z, x$y } from './x'
</script>
<template>
<div :id="z + 'y'">{{ x }} {{ yy }} {{ x$y }}</div>
</template>
`
)
// x: used in interpolation
// y: should not be matched by {{ yy }} or 'y' in binding exps
// x$y: #4274 should escape special chars when creating Regex
// expect(content).toMatch(
// `return { get x() { return x }, get z() { return z }, get x$y() { return x$y } }`
// )
assertCode
(
content
)
})
// #4340 interpolations in template strings
test
(
'
js template string interpolations
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup lang="ts">
import { VAR, VAR2, VAR3 } from './x'
</script>
<template>
{{
\`\$
{VAR}VAR2
\$
{VAR3}
\`
}}
</template>
`
)
// VAR2 should not be matched
// expect(content).toMatch(
// `return { get VAR() { return VAR }, get VAR3() { return VAR3 } }`
// )
assertCode
(
content
)
})
// edge case: last tag in template
test
(
'
last tag
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup lang="ts">
import { FooBaz, Last } from './x'
</script>
<template>
<FooBaz></FooBaz>
<Last/>
</template>
`
)
// expect(content).toMatch(
// `return { get FooBaz() { return FooBaz }, get Last() { return Last } }`
// )
assertCode
(
content
)
})
test
(
'
TS annotations
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup lang="ts">
import { Foo, Bar, Baz, Qux, Fred } from './x'
const a = 1
function b() {}
</script>
<template>
{{ a as Foo }}
{{ b<Bar>() }}
{{ Baz }}
<Comp v-slot="{ data }: Qux">{{ data }}</Comp>
<div v-for="{ z = x as Qux } in list as Fred"/>
</template>
`
)
// expect(content).toMatch(`return { a, b, get Baz() { return Baz } }`)
assertCode
(
content
)
})
// vuejs/vue#12591
test
(
'
v-on inline statement
'
,
()
=>
{
// should not error
compile
(
`
<script setup lang="ts">
import { foo } from './foo'
</script>
<template>
<div @click="$emit('update:a');"></div>
</template>
`
)
})
test
(
'
template ref
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup lang="ts">
import { foo, bar, Baz } from './foo'
</script>
<template>
<div ref="foo"></div>
<div ref=""></div>
<Baz ref="bar" />
</template>
`
)
// expect(content).toMatch(
// 'return { get foo() { return foo }, get bar() { return bar }, get Baz() { return Baz } }'
// )
assertCode
(
content
)
})
})
describe
(
'
inlineTemplate mode
'
,
()
=>
{
test
(
'
should work
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<div>{{ count }}</div>
<div>static</div>
</template>
`
,
{
inlineTemplate
:
true
}
)
// check snapshot and make sure helper imports and
// hoists are placed correctly.
assertCode
(
content
)
// in inline mode, no need to call expose() since nothing is exposed
// anyway!
// expect(content).not.toMatch(`expose()`)
})
test
(
'
with defineExpose()
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup>
const count = ref(0)
defineExpose({ count })
</script>
`
,
{
inlineTemplate
:
true
}
)
assertCode
(
content
)
// expect(content).toMatch(`setup(__props, { expose: __expose })`)
// expect(content).toMatch(`expose({ count })`)
})
test
(
'
referencing scope components and directives
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup>
import ChildComp from './Child.vue'
import SomeOtherComp from './Other.vue'
import vMyDir from './my-dir'
</script>
<template>
<div v-my-dir></div>
<ChildComp/>
<some-other-comp/>
</template>
`
,
{
inlineTemplate
:
true
}
)
// expect(content).toMatch('[_unref(vMyDir)]')
expect
(
content
).
toMatch
(
'
createVNode(ChildComp)
'
)
// kebab-case component support
expect
(
content
).
toMatch
(
'
createVNode(SomeOtherComp)
'
)
assertCode
(
content
)
})
test
(
'
avoid unref() when necessary
'
,
()
=>
{
// function, const, component import
const
{
content
}
=
compile
(
`<script setup>
import { ref } from 'vue'
import Foo, { bar } from './Foo.vue'
import other from './util'
import * as tree from './tree'
const count = ref(0)
const constant = {}
const maybe = foo()
let lett = 1
function fn() {}
</script>
<template>
<Foo>{{ bar }}</Foo>
<div @click="fn">{{ count }} {{ constant }} {{ maybe }} {{ lett }} {{ other }}</div>
{{ tree.foo() }}
</template>
`
,
{
inlineTemplate
:
true
}
)
// no need to unref vue component import
expect
(
content
).
toMatch
(
`createVNode(Foo,`
)
// #2699 should unref named imports from .vue
expect
(
content
).
toMatch
(
`unref(bar)`
)
// should unref other imports
expect
(
content
).
toMatch
(
`unref(other)`
)
// no need to unref constant literals
expect
(
content
).
not
.
toMatch
(
`unref(constant)`
)
// should directly use .value for known refs
expect
(
content
).
toMatch
(
`count.value`
)
// should unref() on const bindings that may be refs
expect
(
content
).
toMatch
(
`unref(maybe)`
)
// should unref() on let bindings
expect
(
content
).
toMatch
(
`unref(lett)`
)
// no need to unref namespace import (this also preserves tree-shaking)
expect
(
content
).
toMatch
(
`tree.foo()`
)
// no need to unref function declarations
expect
(
content
).
toMatch
(
`{ onClick: fn }`
)
// no need to mark constant fns in patch flag
expect
(
content
).
not
.
toMatch
(
`PROPS`
)
assertCode
(
content
)
})
test
(
'
v-model codegen
'
,
()
=>
{
const
{
content
}
=
compile
(
`<script setup>
import { ref } from 'vue'
const count = ref(0)
const maybe = foo()
let lett = 1
</script>
<template>
<input v-model="count"/>
<input v-model="maybe"/>
<input v-model="lett"/>
</template>
`
,
{
inlineTemplate
:
true
}
)
// known const ref: set value
expect
(
content
).
toMatch
(
`(count).value = $event.detail.value`
)
// const but maybe ref: assign if ref, otherwise do nothing
expect
(
content
).
toMatch
(
`isRef(maybe) ? (maybe).value = $event.detail.value : null`
)
// let: handle both cases
expect
(
content
).
toMatch
(
`isRef(lett) ? (lett).value = $event.detail.value : lett = $event.detail.value`
)
assertCode
(
content
)
})
test
(
'
v-model should not generate ref assignment code for non-setup bindings
'
,
()
=>
{
const
{
content
}
=
compile
(
`<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<script>
export default {
data() { return { foo: 123 } }
}
</script>
<template>
<input v-model="foo"/>
</template>
`
,
{
inlineTemplate
:
true
}
)
expect
(
content
).
not
.
toMatch
(
`isRef(foo)`
)
})
test
(
'
template assignment expression codegen
'
,
()
=>
{
const
{
content
}
=
compile
(
`<script setup>
import { ref } from 'vue'
const count = ref(0)
const maybe = foo()
let lett = 1
let v = ref(1)
</script>
<template>
<div @click="count = 1"/>
<div @click="maybe = count"/>
<div @click="lett = count"/>
<div @click="v += 1"/>
<div @click="v -= 1"/>
<div @click="() => {
let a = '' + lett
v = a
}"/>
<div @click="() => {
// nested scopes
(()=>{
let x = a
(()=>{
let z = x
let z2 = z
})
let lz = z
})
v = a
}"/>
</template>
`
,
{
inlineTemplate
:
true
}
)
// known const ref: set value
expect
(
content
).
toMatch
(
`count.value = 1`
)
// const but maybe ref: only assign after check
expect
(
content
).
toMatch
(
`maybe.value = count.value`
)
// let: handle both cases
expect
(
content
).
toMatch
(
`isRef(lett) ? lett.value = count.value : lett = count.value`
)
expect
(
content
).
toMatch
(
`isRef(v) ? v.value += 1 : v += 1`
)
expect
(
content
).
toMatch
(
`isRef(v) ? v.value -= 1 : v -= 1`
)
expect
(
content
).
toMatch
(
`isRef(v) ? v.value = a : v = a`
)
expect
(
content
).
toMatch
(
`isRef(v) ? v.value = _ctx.a : v = _ctx.a`
)
assertCode
(
content
)
})
test
(
'
template update expression codegen
'
,
()
=>
{
const
{
content
}
=
compile
(
`<script setup>
import { ref } from 'vue'
const count = ref(0)
const maybe = foo()
let lett = 1
</script>
<template>
<div @click="count++"/>
<div @click="--count"/>
<div @click="maybe++"/>
<div @click="--maybe"/>
<div @click="lett++"/>
<div @click="--lett"/>
</template>
`
,
{
inlineTemplate
:
true
}
)
// known const ref: set value
expect
(
content
).
toMatch
(
`count.value++`
)
expect
(
content
).
toMatch
(
`--count.value`
)
// const but maybe ref (non-ref case ignored)
expect
(
content
).
toMatch
(
`maybe.value++`
)
expect
(
content
).
toMatch
(
`--maybe.value`
)
// let: handle both cases
expect
(
content
).
toMatch
(
`isRef(lett) ? lett.value++ : lett++`
)
expect
(
content
).
toMatch
(
`isRef(lett) ? --lett.value : --lett`
)
assertCode
(
content
)
})
test
(
'
template destructure assignment codegen
'
,
()
=>
{
const
{
content
}
=
compile
(
`<script setup>
import { ref } from 'vue'
const val = {}
const count = ref(0)
const maybe = foo()
let lett = 1
</script>
<template>
<div @click="({ count } = val)"/>
<div @click="[maybe] = val"/>
<div @click="({ lett } = val)"/>
</template>
`
,
{
inlineTemplate
:
true
}
)
// known const ref: set value
expect
(
content
).
toMatch
(
`({ count: count.value } = val)`
)
// const but maybe ref (non-ref case ignored)
expect
(
content
).
toMatch
(
`[maybe.value] = val`
)
// let: assumes non-ref
expect
(
content
).
toMatch
(
`{ lett: lett } = val`
)
assertCode
(
content
)
})
// test('ssr codegen', () => {
// const { content } = compile(
// `
// <script setup>
// import { ref } from 'vue'
// const count = ref(0)
// const style = { color: 'red' }
// </script>
// <template>
// <div>{{ count }}</div>
// <div>static</div>
// </template>
// <style>
// div { color: v-bind(count) }
// span { color: v-bind(style.color) }
// </style>
// `,
// {
// inlineTemplate: true,
// templateOptions: {
// ssr: true,
// },
// }
// )
// expect(content).toMatch(`\n __ssrInlineRender: true,\n`)
// expect(content).toMatch(`return (_ctx, _push`)
// expect(content).toMatch(`ssrInterpolate`)
// expect(content).not.toMatch(`useCssVars`)
// expect(content).toMatch(`"--${mockId}-count": (count.value)`)
// expect(content).toMatch(`"--${mockId}-style\\\\.color": (style.color)`)
// assertCode(content)
// })
test
(
'
the v-for wrapped in parentheses can be correctly parsed & inline is false
'
,
()
=>
{
expect
(()
=>
compile
(
`
<script setup lang="ts">
import { ref } from 'vue'
const stacks = ref([])
</script>
<template>
<div v-for="({ file: efile }) of stacks"></div>
</template>
`
,
{
inlineTemplate
:
false
,
}
)
).
not
.
toThrowError
()
})
})
describe
(
'
with TypeScript
'
,
()
=>
{
test
(
'
hoist type declarations
'
,
()
=>
{
const
{
content
}
=
compile
(
`
<script setup lang="ts">
export interface Foo {}
type Bar = {}
</script>`
)
assertCode
(
content
)
})
test
(
'
runtime Enum
'
,
()
=>
{
const
{
content
,
bindings
}
=
compile
(
`<script setup lang="ts">
enum Foo { A = 123 }
</script>`
)
assertCode
(
content
)
expect
(
bindings
).
toStrictEqual
({
Foo
:
BindingTypes
.
LITERAL_CONST
,
})
})
test
(
'
runtime Enum in normal script
'
,
()
=>
{
const
{
content
,
bindings
}
=
compile
(
`<script lang="ts">
export enum D { D = "D" }
const enum C { C = "C" }
enum B { B = "B" }
</script>
<script setup lang="ts">
enum Foo { A = 123 }
</script>`
)
assertCode
(
content
)
expect
(
bindings
).
toStrictEqual
({
D
:
BindingTypes
.
LITERAL_CONST
,
C
:
BindingTypes
.
LITERAL_CONST
,
B
:
BindingTypes
.
LITERAL_CONST
,
Foo
:
BindingTypes
.
LITERAL_CONST
,
})
})
test
(
'
const Enum
'
,
()
=>
{
const
{
content
,
bindings
}
=
compile
(
`<script setup lang="ts">
const enum Foo { A = 123 }
</script>`
,
{
hoistStatic
:
true
}
)
assertCode
(
content
)
expect
(
bindings
).
toStrictEqual
({
Foo
:
BindingTypes
.
LITERAL_CONST
,
})
})
// test('import type', () => {
// const { content } = compile(
// `<script setup lang="ts">
// import type { Foo } from './main.ts'
// import { type Bar, Baz } from './main.ts'
// </script>`
// )
// expect(content).toMatch(`return { get Baz() { return Baz } }`)
// assertCode(content)
// })
})
// describe('async/await detection', () => {
// function assertAwaitDetection(code: string, shouldAsync = true) {
// const { content } = compile(`<script setup>${code}</script>`, {
// reactivityTransform: true,
// })
// if (shouldAsync) {
// expect(content).toMatch(`let __temp, __restore`)
// }
// expect(content).toMatch(`${shouldAsync ? `async ` : ``}setup(`)
// assertCode(content)
// return content
// }
// test('expression statement', () => {
// assertAwaitDetection(`await foo`)
// })
// test('variable', () => {
// assertAwaitDetection(`const a = 1 + (await foo)`)
// })
// test('ref', () => {
// assertAwaitDetection(`let a = $ref(1 + (await foo))`)
// })
// // #4448
// test('nested await', () => {
// assertAwaitDetection(`await (await foo)`)
// assertAwaitDetection(`await ((await foo))`)
// assertAwaitDetection(`await (await (await foo))`)
// })
// // should prepend semicolon
// test('nested leading await in expression statement', () => {
// const code = assertAwaitDetection(`foo()\nawait 1 + await 2`)
// expect(code).toMatch(`foo()\n;(`)
// })
// // #4596 should NOT prepend semicolon
// test('single line conditions', () => {
// const code = assertAwaitDetection(`if (false) await foo()`)
// expect(code).not.toMatch(`if (false) ;(`)
// })
// test('nested statements', () => {
// assertAwaitDetection(`if (ok) { await foo } else { await bar }`)
// })
// test('multiple `if` nested statements', () => {
// assertAwaitDetection(`if (ok) {
// let a = 'foo'
// await 0 + await 1
// await 2
// } else if (a) {
// await 10
// if (b) {
// await 0 + await 1
// } else {
// let a = 'foo'
// await 2
// }
// if (b) {
// await 3
// await 4
// }
// } else {
// await 5
// }`)
// })
// test('multiple `if while` nested statements', () => {
// assertAwaitDetection(`if (ok) {
// while (d) {
// await 5
// }
// while (d) {
// await 5
// await 6
// if (c) {
// let f = 10
// 10 + await 7
// } else {
// await 8
// await 9
// }
// }
// }`)
// })
// test('multiple `if for` nested statements', () => {
// assertAwaitDetection(`if (ok) {
// for (let a of [1,2,3]) {
// await a
// }
// for (let a of [1,2,3]) {
// await a
// await a
// }
// }`)
// })
// test('should ignore await inside functions', () => {
// // function declaration
// assertAwaitDetection(`async function foo() { await bar }`, false)
// // function expression
// assertAwaitDetection(`const foo = async () => { await bar }`, false)
// // object method
// assertAwaitDetection(`const obj = { async method() { await bar }}`, false)
// // class method
// assertAwaitDetection(
// `const cls = class Foo { async method() { await bar }}`,
// false
// )
// })
// })
describe
(
'
errors
'
,
()
=>
{
// test('<script> and <script setup> must have same lang', () => {
// expect(() =>
// compile(`<script>foo()</script><script setup lang="ts">bar()</script>`)
// ).toThrow(`<script> and <script setup> must have the same language type`)
// })
const
moduleErrorMsg
=
`cannot contain ES module exports`
test
(
'
non-type named exports
'
,
()
=>
{
expect
(()
=>
compile
(
`<script setup>
export const a = 1
</script>`
)
).
toThrow
(
moduleErrorMsg
)
expect
(()
=>
compile
(
`<script setup>
export * from './foo'
</script>`
)
).
toThrow
(
moduleErrorMsg
)
expect
(()
=>
compile
(
`<script setup>
const bar = 1
export { bar as default }
</script>`
)
).
toThrow
(
moduleErrorMsg
)
})
test
(
'
defineProps/Emit() referencing local var
'
,
()
=>
{
expect
(()
=>
compile
(
`<script setup>
let bar = 1
defineProps({
foo: {
default: () => bar
}
})
</script>`
)
).
toThrow
(
`cannot reference locally declared variables`
)
expect
(()
=>
compile
(
`<script setup>
let bar = 'hello'
defineEmits([bar])
</script>`
)
).
toThrow
(
`cannot reference locally declared variables`
)
// #4644
expect
(()
=>
compile
(
`
<script>const bar = 1</script>
<script setup>
defineProps({
foo: {
default: () => bar
}
})
</script>`
)
).
not
.
toThrow
(
`cannot reference locally declared variables`
)
})
test
(
'
should allow defineProps/Emit() referencing scope var
'
,
()
=>
{
assertCode
(
compile
(
`<script setup>
const bar = 1
defineProps({
foo: {
default: bar => bar + 1
}
})
defineEmits({
foo: bar => bar > 1
})
</script>`
).
content
)
})
test
(
'
should allow defineProps/Emit() referencing imported binding
'
,
()
=>
{
assertCode
(
compile
(
`<script setup>
import { bar } from './bar'
defineProps({
foo: {
default: () => bar
}
})
defineEmits({
foo: () => bar > 1
})
</script>`
).
content
)
})
})
})
describe
(
'
SFC analyze <script> bindings
'
,
()
=>
{
it
(
'
can parse decorators syntax in typescript block
'
,
()
=>
{
const
{
scriptAst
}
=
compile
(
`
<script lang="ts">
import { Options, Vue } from 'vue-class-component';
@Options({
components: {
HelloWorld,
},
props: ['foo', 'bar']
})
export default class Home extends Vue {}
</script>
`
)
expect
(
scriptAst
).
toBeDefined
()
})
it
(
'
recognizes props array declaration
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script>
export default {
props: ['foo', 'bar']
}
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
foo
:
BindingTypes
.
PROPS
,
bar
:
BindingTypes
.
PROPS
,
})
expect
(
bindings
!
.
__isScriptSetup
).
toBe
(
false
)
})
it
(
'
recognizes props object declaration
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script>
export default {
props: {
foo: String,
bar: {
type: String,
},
baz: null,
qux: [String, Number]
}
}
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
foo
:
BindingTypes
.
PROPS
,
bar
:
BindingTypes
.
PROPS
,
baz
:
BindingTypes
.
PROPS
,
qux
:
BindingTypes
.
PROPS
,
})
expect
(
bindings
!
.
__isScriptSetup
).
toBe
(
false
)
})
it
(
'
recognizes setup return
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script>
const bar = 2
export default {
setup() {
return {
foo: 1,
bar
}
}
}
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
foo
:
BindingTypes
.
SETUP_MAYBE_REF
,
bar
:
BindingTypes
.
SETUP_MAYBE_REF
,
})
expect
(
bindings
!
.
__isScriptSetup
).
toBe
(
false
)
})
it
(
'
recognizes exported vars
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script>
export const foo = 2
</script>
<script setup>
console.log(foo)
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
foo
:
BindingTypes
.
LITERAL_CONST
,
})
})
it
(
'
recognizes async setup return
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script>
const bar = 2
export default {
async setup() {
return {
foo: 1,
bar
}
}
}
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
foo
:
BindingTypes
.
SETUP_MAYBE_REF
,
bar
:
BindingTypes
.
SETUP_MAYBE_REF
,
})
expect
(
bindings
!
.
__isScriptSetup
).
toBe
(
false
)
})
it
(
'
recognizes data return
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script>
const bar = 2
export default {
data() {
return {
foo: null,
bar
}
}
}
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
foo
:
BindingTypes
.
DATA
,
bar
:
BindingTypes
.
DATA
,
})
})
it
(
'
recognizes methods
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script>
export default {
methods: {
foo() {}
}
}
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
foo
:
BindingTypes
.
OPTIONS
})
})
it
(
'
recognizes computeds
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script>
export default {
computed: {
foo() {},
bar: {
get() {},
set() {},
}
}
}
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
foo
:
BindingTypes
.
OPTIONS
,
bar
:
BindingTypes
.
OPTIONS
,
})
})
it
(
'
recognizes injections array declaration
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script>
export default {
inject: ['foo', 'bar']
}
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
foo
:
BindingTypes
.
OPTIONS
,
bar
:
BindingTypes
.
OPTIONS
,
})
})
it
(
'
recognizes injections object declaration
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script>
export default {
inject: {
foo: {},
bar: {},
}
}
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
foo
:
BindingTypes
.
OPTIONS
,
bar
:
BindingTypes
.
OPTIONS
,
})
})
it
(
'
works for mixed bindings
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script>
export default {
inject: ['foo'],
props: {
bar: String,
},
setup() {
return {
baz: null,
}
},
data() {
return {
qux: null
}
},
methods: {
quux() {}
},
computed: {
quuz() {}
}
}
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
foo
:
BindingTypes
.
OPTIONS
,
bar
:
BindingTypes
.
PROPS
,
baz
:
BindingTypes
.
SETUP_MAYBE_REF
,
qux
:
BindingTypes
.
DATA
,
quux
:
BindingTypes
.
OPTIONS
,
quuz
:
BindingTypes
.
OPTIONS
,
})
})
it
(
'
works for script setup
'
,
()
=>
{
const
{
bindings
}
=
compile
(
`
<script setup>
import { ref as r } from 'vue'
defineProps({
foo: String
})
const a = r(1)
let b = 2
const c = 3
const { d } = someFoo()
let { e } = someBar()
</script>
`
)
expect
(
bindings
).
toStrictEqual
({
r
:
BindingTypes
.
SETUP_CONST
,
a
:
BindingTypes
.
SETUP_REF
,
b
:
BindingTypes
.
SETUP_LET
,
c
:
BindingTypes
.
LITERAL_CONST
,
d
:
BindingTypes
.
SETUP_MAYBE_REF
,
e
:
BindingTypes
.
SETUP_LET
,
foo
:
BindingTypes
.
PROPS
,
})
})
describe
(
'
auto name inference
'
,
()
=>
{
test
(
'
basic
'
,
()
=>
{
const
{
content
}
=
compile
(
`<script setup>const a = 1</script>
<template>{{ a }}</template>`
,
undefined
,
{
filename
:
'
FooBar.vue
'
,
}
)
expect
(
content
).
toMatch
(
`export default {
__name: 'FooBar'`
)
assertCode
(
content
)
})
test
(
'
do not overwrite manual name (object)
'
,
()
=>
{
const
{
content
}
=
compile
(
`<script>
export default {
name: 'Baz'
}
</script>
<script setup>const a = 1</script>
<template>{{ a }}</template>`
,
undefined
,
{
filename
:
'
FooBar.vue
'
,
}
)
expect
(
content
).
not
.
toMatch
(
`name: 'FooBar'`
)
expect
(
content
).
toMatch
(
`name: 'Baz'`
)
assertCode
(
content
)
})
test
(
'
do not overwrite manual name (call)
'
,
()
=>
{
const
{
content
}
=
compile
(
`<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Baz'
})
</script>
<script setup>const a = 1</script>
<template>{{ a }}</template>`
,
undefined
,
{
filename
:
'
FooBar.vue
'
,
}
)
expect
(
content
).
not
.
toMatch
(
`name: 'FooBar'`
)
expect
(
content
).
toMatch
(
`name: 'Baz'`
)
assertCode
(
content
)
})
})
})
// describe('SFC genDefaultAs', () => {
// test('normal <script> only', () => {
// const { content } = compile(
// `<script>
// export default {}
// </script>`,
// {
// genDefaultAs: '_sfc_',
// }
// )
// expect(content).not.toMatch('export default')
// expect(content).toMatch(`const _sfc_ = {}`)
// assertCode(content)
// })
// test('normal <script> w/ cssVars', () => {
// const { content } = compile(
// `<script>
// export default {}
// </script>
// <style>
// .foo { color: v-bind(x) }
// </style>`,
// {
// genDefaultAs: '_sfc_',
// }
// )
// expect(content).not.toMatch('export default')
// expect(content).not.toMatch('__default__')
// expect(content).toMatch(`const _sfc_ = {}`)
// assertCode(content)
// })
// test('<script> + <script setup>', () => {
// const { content } = compile(
// `<script>
// export default {}
// </script>
// <script setup>
// const a = 1
// </script>`,
// {
// genDefaultAs: '_sfc_',
// }
// )
// expect(content).not.toMatch('export default')
// expect(content).toMatch(
// `const _sfc_ = /*#__PURE__*/Object.assign(__default__`
// )
// assertCode(content)
// })
// test('<script> + <script setup>', () => {
// const { content } = compile(
// `<script>
// export default {}
// </script>
// <script setup>
// const a = 1
// </script>`,
// {
// genDefaultAs: '_sfc_',
// }
// )
// expect(content).not.toMatch('export default')
// expect(content).toMatch(
// `const _sfc_ = /*#__PURE__*/Object.assign(__default__`
// )
// assertCode(content)
// })
// test('<script setup> only', () => {
// const { content } = compile(
// `<script setup>
// const a = 1
// </script>`,
// {
// genDefaultAs: '_sfc_',
// }
// )
// expect(content).not.toMatch('export default')
// expect(content).toMatch(`const _sfc_ = {\n setup`)
// assertCode(content)
// })
// test('<script setup> only w/ ts', () => {
// const { content } = compile(
// `<script setup lang="ts">
// const a = 1
// </script>`,
// {
// genDefaultAs: '_sfc_',
// }
// )
// expect(content).not.toMatch('export default')
// expect(content).toMatch(`const _sfc_ = /*#__PURE__*/_defineComponent(`)
// assertCode(content)
// })
// test('<script> + <script setup> w/ ts', () => {
// const { content } = compile(
// `<script lang="ts">
// export default {}
// </script>
// <script setup lang="ts">
// const a = 1
// </script>`,
// {
// genDefaultAs: '_sfc_',
// }
// )
// expect(content).not.toMatch('export default')
// expect(content).toMatch(
// `const _sfc_ = /*#__PURE__*/_defineComponent({\n ...__default__`
// )
// assertCode(content)
// })
// test('binding type for edge cases', () => {
// const { bindings } = compile(
// `<script setup lang="ts">
// import { toRef } from 'vue'
// const props = defineProps<{foo: string}>()
// const foo = toRef(() => props.foo)
// </script>`
// )
// expect(bindings).toStrictEqual({
// toRef: BindingTypes.SETUP_CONST,
// props: BindingTypes.SETUP_REACTIVE_CONST,
// foo: BindingTypes.SETUP_REF,
// })
// })
// })
packages/uni-app-uts/__tests__/android/sfc/utils.ts
0 → 100644
浏览文件 @
0c535ff4
import
{
parse
,
SFCParseOptions
}
from
'
@vue/compiler-sfc
'
import
{
parse
as
babelParse
}
from
'
@babel/parser
'
import
{
compileScript
,
SFCScriptCompileOptions
,
}
from
'
../../../src/plugins/android/uvue/sfc/compiler/compileScript
'
import
{
genTemplateCode
}
from
'
../../../src/plugins/android/uvue/code/template
'
import
{
resolveGenTemplateCodeOptions
}
from
'
../../../src/plugins/android/uvue/sfc/template
'
export
const
mockId
=
'
xxxxxxxx
'
export
function
compileSFCScript
(
src
:
string
,
options
?:
Partial
<
SFCScriptCompileOptions
>
,
parseOptions
?:
SFCParseOptions
)
{
const
{
descriptor
}
=
parse
(
src
,
parseOptions
)
const
result
=
compileScript
(
descriptor
,
{
...
options
,
id
:
mockId
,
className
:
''
,
inlineTemplate
:
true
,
scriptAndScriptSetup
:
true
,
})
if
(
options
?.
inlineTemplate
)
{
const
isInline
=
!!
descriptor
.
scriptSetup
const
templateResult
=
genTemplateCode
(
descriptor
,
resolveGenTemplateCodeOptions
(
descriptor
.
filename
,
src
,
descriptor
,
{
mode
:
'
module
'
,
inline
:
isInline
,
rootDir
:
''
,
sourceMap
:
false
,
bindingMetadata
:
result
.
bindings
,
})
)
result
.
content
=
result
.
content
.
replace
(
`"INLINE_RENDER"`
,
templateResult
.
code
)
}
return
result
}
export
function
assertCode
(
code
:
string
)
{
// parse the generated code to make sure it is valid
try
{
babelParse
(
code
,
{
sourceType
:
'
module
'
,
plugins
:
[
'
typescript
'
],
})
}
catch
(
e
:
any
)
{
console
.
log
(
code
,
e
)
throw
e
}
// console.log(code)
expect
(
code
).
toMatchSnapshot
()
}
packages/uni-app-uts/__tests__/android/transforms/__snapshots__/transformExpressions.spec.ts.snap
浏览文件 @
0c535ff4
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`compiler: expression transform bindingMetadata inline mode 1`] = `"createElementVNode("view", null, toDisplayString(__props.props) + " " + toDisplayString(unref(setup)) + " " + toDisplayString(setupConst) + " " + toDisplayString(_ctx.data) + " " + toDisplayString(_ctx.options) + " " + toDisplayString(isNaN), 1 /* TEXT */)"`;
exports[`compiler: expression transform bindingMetadata inline mode 1`] = `"createElementVNode("view", null, toDisplayString(__props.props) + " " + toDisplayString(unref(setup)) + " " + toDisplayString(setupConst) + " " + toDisplayString(_ctx.data) + " " + toDisplayString(_ctx.options) + " " + toDisplayString(isNaN
.value
), 1 /* TEXT */)"`;
exports[`compiler: expression transform bindingMetadata non-inline mode 1`] = `"createElementVNode("view", null, toDisplayString($props.props) + " " + toDisplayString($setup.setup) + " " + toDisplayString($data.data) + " " + toDisplayString($options.options) + " " + toDisplayString(isNaN), 1 /* TEXT */)"`;
exports[`compiler: expression transform bindingMetadata non-inline mode 1`] = `"createElementVNode("view", null, toDisplayString($props.props) + " " + toDisplayString($setup.setup) + " " + toDisplayString($data.data) + " " + toDisplayString($options.options) + " " + toDisplayString(
$setup.
isNaN), 1 /* TEXT */)"`;
exports[`compiler: expression transform bindingMetadata should not prefix temp variable of for loop 1`] = `
"createVNode(_component_div, utsMapOf({
...
...
packages/uni-app-uts/__tests__/android/transforms/transformExpressions.spec.ts
浏览文件 @
0c535ff4
...
...
@@ -532,7 +532,7 @@ describe('compiler: expression transform', () => {
)
expect
(
code
).
toMatch
(
`$props.props`
)
expect
(
code
).
toMatch
(
`$setup.setup`
)
//
expect(code).toMatch(`$setup.isNaN`)
expect
(
code
).
toMatch
(
`$setup.isNaN`
)
expect
(
code
).
toMatch
(
`$data.data`
)
expect
(
code
).
toMatch
(
`$options.options`
)
// expect(code).toMatch(`_ctx, _cache, $props, $setup, $data, $options`)
...
...
@@ -585,8 +585,7 @@ describe('compiler: expression transform', () => {
expect
(
code
).
toMatch
(
`toDisplayString(setupConst)`
)
expect
(
code
).
toMatch
(
`_ctx.data`
)
expect
(
code
).
toMatch
(
`_ctx.options`
)
// isNaN 设置了全局方法
// expect(code).toMatch(`isNaN.value`)
expect
(
code
).
toMatch
(
`isNaN.value`
)
expect
(
code
).
toMatchSnapshot
()
})
...
...
packages/uni-app-uts/src/plugins/android/uvue/compiler/codegen.ts
浏览文件 @
0c535ff4
...
...
@@ -99,7 +99,7 @@ function createCodegenContext(
rootDir
=
''
,
targetLanguage
=
'
kotlin
'
,
mode
=
'
default
'
,
prefixIdentifiers
=
false
,
prefixIdentifiers
=
mode
===
'
module
'
,
bindingMetadata
=
{},
inline
=
false
,
sourceMap
=
false
,
...
...
packages/uni-app-uts/src/plugins/android/uvue/compiler/index.ts
浏览文件 @
0c535ff4
...
...
@@ -107,7 +107,6 @@ export function compile(
transform
(
ast
,
extend
({},
options
,
{
prefixIdentifiers
:
options
.
prefixIdentifiers
,
nodeTransforms
:
[
...
nodeTransforms
,
...
getBaseNodeTransforms
(
'
/
'
),
...
...
packages/uni-app-uts/src/plugins/android/uvue/compiler/options.ts
浏览文件 @
0c535ff4
...
...
@@ -3,6 +3,10 @@ import type { RawSourceMap } from 'source-map-js'
import
{
DirectiveTransform
,
NodeTransform
}
from
'
./transform
'
interface
SharedTransformCodegenOptions
{
/**
* @default 'default'
*/
mode
?:
'
default
'
|
'
module
'
rootDir
?:
string
targetLanguage
?:
'
kotlin
'
|
'
swift
'
/**
...
...
@@ -32,11 +36,6 @@ interface SharedTransformCodegenOptions {
className
?:
string
}
export
interface
CodegenOptions
extends
SharedTransformCodegenOptions
{
/**
* @default 'default'
*/
mode
?:
'
default
'
|
'
module
'
inMap
?:
RawSourceMap
/**
* Generate source map?
...
...
packages/uni-app-uts/src/plugins/android/uvue/compiler/transform.ts
浏览文件 @
0c535ff4
...
...
@@ -116,11 +116,12 @@ export interface TransformContext
export
function
createTransformContext
(
root
:
RootNode
,
{
mode
=
'
default
'
,
rootDir
=
''
,
targetLanguage
=
'
kotlin
'
,
filename
=
''
,
cacheHandlers
=
false
,
prefixIdentifiers
=
false
,
prefixIdentifiers
=
mode
===
'
module
'
,
nodeTransforms
=
[],
directiveTransforms
=
{},
scopeId
=
null
,
...
...
@@ -136,6 +137,7 @@ export function createTransformContext(
const
nameMatch
=
filename
.
replace
(
/
\?
.*$/
,
''
).
match
(
/
([^/\\]
+
)\.\w
+$/
)
const
context
:
TransformContext
=
{
// options
mode
,
rootDir
,
targetLanguage
,
selfName
:
nameMatch
&&
capitalize
(
camelize
(
nameMatch
[
1
])),
...
...
packages/uni-app-uts/src/plugins/android/uvue/compiler/transforms/transformExpression.ts
浏览文件 @
0c535ff4
...
...
@@ -221,7 +221,12 @@ export function processExpression(
const
isScopeVarReference
=
context
.
identifiers
[
rawExp
]
const
isAllowedGlobal
=
isGloballyWhitelisted
(
rawExp
)
const
isLiteral
=
isLiteralWhitelisted
(
rawExp
)
if
(
!
asParams
&&
!
isScopeVarReference
&&
!
isAllowedGlobal
&&
!
isLiteral
)
{
if
(
!
asParams
&&
!
isScopeVarReference
&&
!
isLiteral
&&
(
!
isAllowedGlobal
||
bindingMetadata
[
rawExp
])
)
{
// const bindings exposed from setup can be skipped for patching but
// cannot be hoisted to module scope
if
(
bindingMetadata
[
node
.
content
]
===
BindingTypes
.
SETUP_CONST
)
{
...
...
packages/uni-app-uts/src/plugins/android/uvue/index.ts
浏览文件 @
0c535ff4
...
...
@@ -258,7 +258,6 @@ export async function transformVue(
mode
:
'
module
'
,
filename
:
relativeFileName
,
className
:
className
,
prefixIdentifiers
:
true
,
// 方便测试,build模式也提供sourceMap
// sourceMap: false,
sourceMap
:
needSourceMap
,
...
...
packages/uni-app-uts/src/plugins/android/uvue/sfc/compiler/compileScript.ts
浏览文件 @
0c535ff4
...
...
@@ -13,6 +13,7 @@ import {
Identifier
,
Statement
,
CallExpression
,
ExportSpecifier
,
}
from
'
@babel/types
'
import
{
walk
}
from
'
estree-walker
'
import
type
{
RawSourceMap
}
from
'
source-map-js
'
...
...
@@ -43,12 +44,22 @@ import {
}
from
'
./script/utils
'
import
{
analyzeScriptBindings
}
from
'
./script/analyzeScriptBindings
'
import
{
parseUTSRelativeFilename
}
from
'
../../../utils
'
import
{
rewriteConsole
}
from
'
./script/rewriteConsole
'
import
{
hasConsole
,
rewriteConsole
}
from
'
./script/rewriteConsole
'
import
{
hasDebugError
,
rewriteDebugError
}
from
'
./script/rewriteDebugError
'
import
{
TypeScope
}
from
'
./script/resolveType
'
export
const
normalScriptDefaultVar
=
`__default__`
export
const
DEFAULT_FILENAME
=
'
anonymous.vue
'
export
interface
SFCScriptCompileOptions
{
/**
* 是否同时支持使用 <script> 和 <script setup>
*/
scriptAndScriptSetup
?:
boolean
/**
* Class name
*/
className
:
string
/**
* Scope ID for prefixing injected CSS variables.
...
...
@@ -162,6 +173,13 @@ export function compileScript(
const
hoistStatic
=
options
.
hoistStatic
!==
false
&&
!
script
const
scopeId
=
options
.
id
?
options
.
id
.
replace
(
/^data-v-/
,
''
)
:
''
// 目前暂不提供<script setup>和<script>同时使用
// 目前给了个开关,用于单元测试
if
(
!
options
.
scriptAndScriptSetup
)
{
if
(
script
&&
scriptSetup
)
{
throw
new
Error
(
`<script setup> and <script> cannot be used together.`
)
}
}
// TODO remove in 3.4
// const enableReactivityTransform = !!options.reactivityTransform
let
refBindings
:
string
[]
|
undefined
...
...
@@ -191,7 +209,7 @@ export function compileScript(
const
scriptBindings
:
Record
<
string
,
BindingTypes
>
=
Object
.
create
(
null
)
const
setupBindings
:
Record
<
string
,
BindingTypes
>
=
Object
.
create
(
null
)
//
let defaultExport: Node | undefined
let
defaultExport
:
Node
|
undefined
let
hasAwait
=
false
// let hasInlinedSsrRenderFn = false
...
...
@@ -296,7 +314,7 @@ export function compileScript(
startOffset
,
})
}
if
(
scriptSetupContent
.
includes
(
'
console
'
))
{
if
(
hasConsole
(
scriptSetupContent
))
{
rewriteConsole
(
scriptSetupAst
,
ctx
.
s
,
{
fileName
,
startLine
,
...
...
@@ -404,17 +422,115 @@ export function compileScript(
// 2.1 process normal <script> body
if
(
script
&&
scriptAst
)
{
const
scriptScope
=
{
offset
:
script
.
loc
.
start
.
offset
,
filename
:
ctx
.
filename
,
source
:
ctx
.
descriptor
.
source
,
}
as
TypeScope
for
(
const
node
of
scriptAst
.
body
)
{
if
(
node
.
type
===
'
ExportDefaultDeclaration
'
)
{
ctx
.
error
(
`When <script> and <script setup> are used together, export default is not supported within <script>.`
,
node
)
if
(
!
options
.
scriptAndScriptSetup
)
{
ctx
.
error
(
`When <script> and <script setup> are used together, export default is not supported within <script>.`
,
node
,
scriptScope
)
}
else
{
// export default
defaultExport
=
node
// check if user has manually specified `name` or 'render` option in
// export default
// if has name, skip name inference
// if has render and no template, generate return object instead of
// empty render function (#4980)
let
optionProperties
if
(
defaultExport
.
declaration
.
type
===
'
ObjectExpression
'
)
{
optionProperties
=
defaultExport
.
declaration
.
properties
}
else
if
(
defaultExport
.
declaration
.
type
===
'
CallExpression
'
&&
defaultExport
.
declaration
.
arguments
[
0
]
&&
defaultExport
.
declaration
.
arguments
[
0
].
type
===
'
ObjectExpression
'
)
{
optionProperties
=
defaultExport
.
declaration
.
arguments
[
0
].
properties
}
if
(
optionProperties
)
{
for
(
const
p
of
optionProperties
)
{
if
(
p
.
type
===
'
ObjectProperty
'
&&
p
.
key
.
type
===
'
Identifier
'
&&
p
.
key
.
name
===
'
name
'
)
{
ctx
.
hasDefaultExportName
=
true
}
if
(
(
p
.
type
===
'
ObjectMethod
'
||
p
.
type
===
'
ObjectProperty
'
)
&&
p
.
key
.
type
===
'
Identifier
'
&&
p
.
key
.
name
===
'
render
'
)
{
// TODO warn when we provide a better way to do it?
ctx
.
hasDefaultExportRender
=
true
}
}
}
// export default { ... } --> const __default__ = { ... }
const
start
=
node
.
start
!
+
scriptStartOffset
!
const
end
=
node
.
declaration
.
start
!
+
scriptStartOffset
!
ctx
.
s
.
overwrite
(
start
,
end
,
`const
${
normalScriptDefaultVar
}
= `
)
}
}
else
if
(
node
.
type
===
'
ExportNamedDeclaration
'
)
{
ctx
.
error
(
`When <script> and <script setup> are used together, export is not supported within <script>.`
,
node
)
if
(
!
options
.
scriptAndScriptSetup
)
{
ctx
.
error
(
`When <script> and <script setup> are used together, export is not supported within <script>.`
,
node
,
scriptScope
)
}
else
{
const
defaultSpecifier
=
node
.
specifiers
.
find
(
(
s
)
=>
s
.
exported
.
type
===
'
Identifier
'
&&
s
.
exported
.
name
===
'
default
'
)
as
ExportSpecifier
if
(
defaultSpecifier
)
{
defaultExport
=
node
// 1. remove specifier
if
(
node
.
specifiers
.
length
>
1
)
{
ctx
.
s
.
remove
(
defaultSpecifier
.
start
!
+
scriptStartOffset
!
,
defaultSpecifier
.
end
!
+
scriptStartOffset
!
)
}
else
{
ctx
.
s
.
remove
(
node
.
start
!
+
scriptStartOffset
!
,
node
.
end
!
+
scriptStartOffset
!
)
}
if
(
node
.
source
)
{
// export { x as default } from './x'
// rewrite to `import { x as __default__ } from './x'` and
// add to top
ctx
.
s
.
prepend
(
`import {
${
defaultSpecifier
.
local
.
name
}
as
${
normalScriptDefaultVar
}
} from '
${
node
.
source
.
value
}
'\n`
)
}
else
{
// export { x as default }
// rewrite to `const __default__ = x` and move to end
ctx
.
s
.
appendLeft
(
scriptEndOffset
!
,
`\nconst
${
normalScriptDefaultVar
}
=
${
defaultSpecifier
.
local
.
name
}
\n`
)
}
}
if
(
node
.
declaration
)
{
walkDeclaration
(
'
script
'
,
node
.
declaration
,
scriptBindings
,
vueImportAliases
,
hoistStatic
)
}
}
}
else
if
(
(
node
.
type
===
'
VariableDeclaration
'
||
node
.
type
===
'
FunctionDeclaration
'
||
...
...
@@ -855,9 +971,9 @@ __ins.emit(event, ...do_not_transform_spread)
startOffset
,
`\n
${
genDefaultAs
}
{
${
runtimeOptions
}
\n `
+
`
${
hasAwait
?
`async `
:
``
}
setup(
${
args
}
) {
const __ins = getCurrentInstance()!
const _ctx = __ins.proxy
as
${
options
.
className
}
const _cache = __ins.renderCache
const __ins = getCurrentInstance()!
;
const _ctx = __ins.proxy
${
options
.
className
?
` as
${
options
.
className
}
`
:
''
}
;
const _cache = __ins.renderCache
;
${
exposeCall
}
`
)
ctx
.
s
.
appendRight
(
endOffset
,
`}`
)
...
...
packages/uni-app-uts/src/plugins/android/uvue/sfc/main.ts
浏览文件 @
0c535ff4
...
...
@@ -16,7 +16,7 @@ import {
parseUTSComponent
,
removeExt
,
}
from
'
@dcloudio/uni-cli-shared
'
import
type
{
Position
}
from
'
@vue/compiler-core
'
import
type
{
BindingMetadata
,
Position
}
from
'
@vue/compiler-core
'
import
type
{
ImportSpecifier
}
from
'
es-module-lexer
'
import
{
createDescriptor
,
setSrcDescriptor
}
from
'
../descriptorCache
'
import
{
resolveScript
}
from
'
./script
'
...
...
@@ -58,7 +58,11 @@ export async function transformMain(
const
className
=
genClassName
(
relativeFileName
)
// script
const
{
code
:
scriptCode
,
map
:
scriptMap
}
=
await
genScriptCode
(
descriptor
,
{
const
{
code
:
scriptCode
,
map
:
scriptMap
,
bindingMetadata
,
}
=
await
genScriptCode
(
descriptor
,
{
...
options
,
className
,
})
...
...
@@ -79,6 +83,7 @@ export async function transformMain(
inline
:
isInline
,
rootDir
:
process
.
env
.
UNI_INPUT_DIR
,
sourceMap
:
process
.
env
.
NODE_ENV
===
'
development
'
,
bindingMetadata
,
})
)
...
...
@@ -228,6 +233,7 @@ async function genScriptCode(
):
Promise
<
{
code
:
string
map
:
RawSourceMap
|
undefined
bindingMetadata
?:
BindingMetadata
}
>
{
let
scriptCode
=
`export default {}`
let
map
:
RawSourceMap
|
undefined
...
...
@@ -240,6 +246,7 @@ async function genScriptCode(
return
{
code
:
scriptCode
,
map
,
bindingMetadata
:
script
?.
bindings
,
}
}
...
...
packages/uni-app-uts/src/plugins/android/uvue/sfc/template.ts
浏览文件 @
0c535ff4
import
type
{
BindingMetadata
,
CompilerOptions
,
SFCDescriptor
,
SFCTemplateCompileOptions
,
...
...
@@ -26,6 +27,7 @@ export function resolveGenTemplateCodeOptions(
inline
:
boolean
rootDir
:
string
sourceMap
:
boolean
bindingMetadata
?:
BindingMetadata
}
):
TemplateCompilerOptions
{
const
inputRoot
=
normalizePath
(
options
.
rootDir
)
...
...
@@ -35,7 +37,6 @@ export function resolveGenTemplateCodeOptions(
...
options
,
filename
:
relativeFileName
,
className
,
prefixIdentifiers
:
!
options
.
inline
,
inMap
:
descriptor
.
template
?.
map
,
matchEasyCom
:
(
tag
,
uts
)
=>
{
const
source
=
matchEasycom
(
tag
)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录