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

wip(mp): vFor

上级 f59898be
import { assert } from './testUtils'
describe('compiler: codegen', () => {
test('module mode preamble', () => {
assert(
`<view v-for="item in items" @click="onClick"></view>`,
`<view wx:for="{{a}}" wx:for-item="item" bindtap="{{a}}"></view>`,
`import { vOn as _vOn, vFor as _vFor } from "vue"
export function render(_ctx, _cache) {
return { a: _vFor(_ctx.items, item => { return { a: _vOn(_ctx.onClick) }; }) }
}`,
{ inline: false, mode: 'module', prefixIdentifiers: false }
)
})
test('module mode preamble w/ optimizeImports: true', () => {
assert(
`<view v-for="item in items" @click="onClick"></view>`,
`<view wx:for="{{a}}" wx:for-item="item" bindtap="{{a}}"></view>`,
`import { vOn as _vOn, vFor as _vFor } from "vue"
export function render(_ctx, _cache) {
return { a: _vFor(_ctx.items, item => { return { a: _vOn(_ctx.onClick) }; }) }
}`,
{ inline: false, mode: 'module' }
)
})
test('function mode preamble', () => {
assert(
`<view v-for="item in items" @click="onClick"></view>`,
`<view wx:for="{{a}}" wx:for-item="item" bindtap="{{a}}"></view>`,
`const _Vue = Vue
return function render(_ctx, _cache) {
with (_ctx) {
const { vOn: _vOn, vFor: _vFor } = _Vue
return { a: _vFor(items, item => { return { a: _vOn(onClick) }; }) }
}
}`,
{ inline: false, mode: 'function', prefixIdentifiers: false }
)
})
test('function mode preamble w/ prefixIdentifiers: true', () => {
assert(
`<view v-for="item in items" @click="onClick"></view>`,
`<view wx:for="{{a}}" wx:for-item="item" bindtap="{{a}}"></view>`,
`const { vOn: _vOn, vFor: _vFor } = Vue
return function render(_ctx, _cache) {
return { a: _vFor(_ctx.items, item => { return { a: _vOn(_ctx.onClick) }; }) }
}`,
{ inline: false, mode: 'function' }
)
})
test('static text', () => {
assert(
`hello`,
`hello`,
`(_ctx, _cache) => {
return {}
}`
)
})
test('interpolation', () => {
assert(
`{{hello}}`,
`{{a}}`,
`(_ctx, _cache) => {
return { a: _toDisplayString(_ctx.hello) }
}`
)
})
test('comment', () => {
assert(
`<!--foo-->`,
``,
`(_ctx, _cache) => {
return {}
}`
)
})
test('compound expression', () => {
assert(
`{{foo}}{{bar}}nested`,
`{{a}}{{b}}nested`,
`(_ctx, _cache) => {
return { a: _toDisplayString(_ctx.foo), b: _toDisplayString(_ctx.bar) }
}`
)
})
})
import { compile } from '../src/index'
function assert(template: string, templateCode: string, renderCode: string) {
const res = compile(template, {
filename: 'foo.vue',
prefixIdentifiers: true,
inline: true,
emitFile({ source }) {
// console.log(source)
expect(source).toBe(templateCode)
return ''
},
})
// expect(res.template).toBe(templateCode)
// expect(res.code).toBe(renderCode)
// console.log(require('util').inspect(res.code, { colors: true, depth: null }))
// console.log(require('util').inspect(res, { colors: true, depth: null }))
expect(res.code).toBe(renderCode)
}
describe('compiler', () => {
test(`v-for directive`, () => {
assert(
`<view><view v-for="item in items" :key="item.uid" :data-title="data.title" :data-id="item.id">{{item.title}}</view></view>`,
`<view><view wx:for="{{b}}" wx:for-item="item" wx:key="b" data-title="{{a}}" data-id="{{item.c}}">{{item.a}}</view></view>`,
`(_ctx, _cache) => {
return {
a: _ctx.data.title,
b: vFor(_ctx.items, item => {
return {
a: item.title,
b: item.uid,
c: item.id
};
})
}
}`
)
})
test(`v-for directive with key`, () => {
assert(
`<view><view v-for="(item, i) in items" :data-id="item.id">{{item.title}}</view></view>`,
`<view><view wx:for="{{a}}" wx:for-item="item" wx:for-index="i" data-id="{{item.b}}">{{item.a}}</view></view>`,
`(_ctx, _cache) => {
return {
a: vFor(_ctx.items, (item, i) => {
return {
a: item.title,
b: item.id
};
})
}
}`
)
})
test(`generate v-for with v-if`, () => {
assert(
`<view v-for="item in items"><view v-if="item.show">{{item.title}}</view></view>`,
`<view wx:for="{{a}}" wx:for-item="item"><view wx:if="{{item.b}}">{{item.a}}</view></view>`,
`(_ctx, _cache) => {
return {
a: vFor(_ctx.items, item => {
return {
b: item.show,
...(item.show ? {
a: item.title
} : {})
};
})
}
}`
)
})
test(`generate v-if directive`, () => {
assert(
`<view v-if="show">hello</view>`,
`<view wx:if="{{a}}">hello</view>`,
`(_ctx, _cache) => {
return {
a: _ctx.show,
...(_ctx.show ? {} : {})
}
}`
)
})
test(`generate v-else directive`, () => {
assert(
`<view><view v-if="show">hello</view><view v-else>world</view></view>`,
`<view><view wx:if="{{a}}">hello</view><view wx:else>world</view></view>`,
`(_ctx, _cache) => {
return {
a: _ctx.show,
...(_ctx.show ? {} : {})
}
}`
)
})
test(`generate v-else-if directive`, () => {
assert(
`<view><view v-if="show">hello</view><view v-else-if="hide">world</view></view>`,
`<view><view wx:if="{{a}}">hello</view><view wx:elif="{{b}}">world</view></view>`,
`(_ctx, _cache) => {
return {
a: _ctx.show,
...(_ctx.show ? {} : _ctx.hide ? {} : {}),
b: _ctx.hide
}
}`
)
})
test(`generate v-else-if with v-else directive`, () => {
assert(
`<view><view v-if="show">hello</view><view v-else-if="hide">world</view><view v-else>bye</view></view>`,
`<view><view wx:if="{{a}}">hello</view><view wx:elif="{{b}}">world</view><view wx:else>bye</view></view>`,
`(_ctx, _cache) => {
return {
a: _ctx.show,
...(_ctx.show ? {} : _ctx.hide ? {} : {}),
b: _ctx.hide
}
}`
)
})
test(`generate multi v-else-if with v-else directive`, () => {
assert(
`<view><view v-if="show">hello</view><view v-else-if="hide">world</view><view v-else-if="3">elseif</view><view v-else>bye</view></view>`,
`<view><view wx:if="{{a}}">hello</view><view wx:elif="{{b}}">world</view><view wx:elif="{{3}}">elseif</view><view wx:else>bye</view></view>`,
`(_ctx, _cache) => {
return {
a: _ctx.show,
...(_ctx.show ? {} : _ctx.hide ? {} : 3 ? {} : {}),
b: _ctx.hide
}
}`
)
})
})
......@@ -33,12 +33,10 @@ function assert(
describe('compiler', () => {
test('should wrap as function if expression is inline statement', () => {
assert(
`<div v-on:click="foo" />`,
`<view bindtap="{{a}}"/>`,
`{{hello}}`,
`{{a}}`,
`(_ctx, _cache) => {
return {
a: _vOn(_ctx.foo)
}
return { a: _toDisplayString(_ctx.hello) }
}`,
{}
)
......
......@@ -10,6 +10,7 @@ export function assert(
options: CompilerOptions = {}
) {
const res = compile(template, {
mode: 'module',
filename: 'foo.vue',
prefixIdentifiers: true,
inline: true,
......
......@@ -29,11 +29,7 @@ describe(`compiler: v-for`, () => {
`<view v-for="index in 5" />`,
`<view wx:for="{{a}}" wx:for-item="index"/>`,
`(_ctx, _cache) => {
return {
a: _vFor([1, 2, 3, 4, 5], index => {
return {};
})
}
return { a: _vFor([1, 2, 3, 4, 5], index => { return {}; }) }
}`
)
})
......@@ -42,11 +38,7 @@ return {
`<view v-for="(item) in items" />`,
`<view wx:for="{{a}}" wx:for-item="item"/>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, item => {
return {};
})
}
return { a: _vFor(_ctx.items, item => { return {}; }) }
}`
)
})
......@@ -55,14 +47,7 @@ return {
`<view v-for="({ id, value }) in items" />`,
`<view wx:for="{{a}}" wx:for-item="v0"/>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, ({
id,
value
}) => {
return {};
})
}
return { a: _vFor(_ctx.items, ({ id, value }) => { return {}; }) }
}`
)
})
......@@ -71,11 +56,7 @@ return {
`<view v-for="([ id, value ]) in items" />`,
`<view wx:for="{{a}}" wx:for-item="v0"/>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, ([id, value]) => {
return {};
})
}
return { a: _vFor(_ctx.items, ([id, value]) => { return {}; }) }
}`
)
})
......@@ -84,11 +65,7 @@ return {
`<view v-for="(item, key) in items" />`,
`<view wx:for="{{a}}" wx:for-item="item" wx:for-index="key"/>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, (item, key) => {
return {};
})
}
return { a: _vFor(_ctx.items, (item, key) => { return {}; }) }
}`
)
})
......@@ -97,11 +74,7 @@ return {
`<view v-for="(item, key, index) in items" />`,
`<view wx:for="{{a}}" wx:for-item="item" wx:for-index="key"/>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, (item, key, index) => {
return {};
})
}
return { a: _vFor(_ctx.items, (item, key, index) => { return {}; }) }
}`
)
})
......@@ -110,11 +83,7 @@ return {
`<view v-for="(value,,index) in items" />`,
`<view wx:for="{{a}}" wx:for-item="value"/>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, (value, __, index) => {
return {};
})
}
return { a: _vFor(_ctx.items, (value, __, index) => { return {}; }) }
}`
)
})
......@@ -123,11 +92,7 @@ return {
`<view v-for="(,,index) in items" />`,
`<view wx:for="{{a}}" wx:for-item="v0"/>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, (_, __, index) => {
return {};
})
}
return { a: _vFor(_ctx.items, (_, __, index) => { return {}; }) }
}`
)
})
......@@ -136,11 +101,7 @@ return {
`<view v-for="item in items" />`,
`<view wx:for="{{a}}" wx:for-item="item"/>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, item => {
return {};
})
}
return { a: _vFor(_ctx.items, item => { return {}; }) }
}`
)
})
......@@ -149,11 +110,7 @@ return {
`<view v-for="item, key in items" />`,
`<view wx:for="{{a}}" wx:for-item="item" wx:for-index="key"/>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, (item, key) => {
return {};
})
}
return { a: _vFor(_ctx.items, (item, key) => { return {}; }) }
}`
)
})
......@@ -162,11 +119,7 @@ return {
`<view v-for="value, key, index in items" />`,
`<view wx:for="{{a}}" wx:for-item="value" wx:for-index="key"/>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, (value, key, index) => {
return {};
})
}
return { a: _vFor(_ctx.items, (value, key, index) => { return {}; }) }
}`
)
})
......@@ -175,11 +128,7 @@ return {
`<view v-for="value, , index in items" />`,
`<view wx:for="{{a}}" wx:for-item="value"/>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, (value, __, index) => {
return {};
})
}
return { a: _vFor(_ctx.items, (value, __, index) => { return {}; }) }
}`
)
})
......@@ -188,11 +137,7 @@ return {
`<view v-for=", , index in items" />`,
`<view wx:for="{{a}}" wx:for-item="v0"/>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, (_, __, index) => {
return {};
})
}
return { a: _vFor(_ctx.items, (_, __, index) => { return {}; }) }
}`
)
})
......@@ -201,11 +146,7 @@ return {
`<template v-for="item in items">hello<view/></template>`,
`<block wx:for="{{a}}" wx:for-item="item">hello<view/></block>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, item => {
return {};
})
}
return { a: _vFor(_ctx.items, item => { return {}; }) }
}`
)
})
......@@ -214,11 +155,7 @@ return {
`<template v-for="item in items"><slot/></template>`,
`<block wx:for="{{a}}" wx:for-item="item"><slot/></block>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, item => {
return {};
})
}
return { a: _vFor(_ctx.items, item => { return {}; }) }
}`
)
})
......@@ -228,14 +165,7 @@ return {
`<template v-for="item in items" :key="item.id"><view :id="item.id" /></template>`,
`<block wx:for="{{a}}" wx:for-item="item" wx:key="b"><view id="{{item.a}}"/></block>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, item => {
return {
a: item.id,
b: item.id
};
})
}
return { a: _vFor(_ctx.items, item => { return { a: item.id, b: item.id }; }) }
}`
)
})
......@@ -244,11 +174,7 @@ return {
`<slot v-for="item in items"></slot>`,
`<slot wx:for="{{a}}" wx:for-item="item"></slot>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, item => {
return {};
})
}
return { a: _vFor(_ctx.items, item => { return {}; }) }
}`
)
})
......@@ -257,11 +183,7 @@ return {
`<view v-for="(item) in items" :key="item" />`,
`<view wx:for="{{a}}" wx:for-item="item" wx:key="*this"/>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, item => {
return {};
})
}
return { a: _vFor(_ctx.items, item => { return {}; }) }
}`
)
})
......@@ -270,11 +192,7 @@ return {
`<template v-for="item in items" :key="item">hello<view/></template>`,
`<block wx:for="{{a}}" wx:for-item="item" wx:key="*this">hello<view/></block>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, item => {
return {};
})
}
return { a: _vFor(_ctx.items, item => { return {}; }) }
}`
)
})
......@@ -283,14 +201,7 @@ return {
`<view v-if="ok" v-for="i in list"/>`,
`<view wx:if="{{b}}" wx:for="{{a}}" wx:for-item="i"/>`,
`(_ctx, _cache) => {
return {
b: _ctx.ok,
...(_ctx.ok ? {
a: _vFor(_ctx.list, i => {
return {};
})
} : {})
}
return { b: _ctx.ok, ...(_ctx.ok ? { a: _vFor(_ctx.list, i => { return {}; }) } : {}) }
}`
)
})
......@@ -300,14 +211,7 @@ return {
`<template v-if="ok" v-for="i in list"/>`,
`<block wx:if="{{b}}" wx:for="{{a}}" wx:for-item="i"/>`,
`(_ctx, _cache) => {
return {
b: _ctx.ok,
...(_ctx.ok ? {
a: _vFor(_ctx.list, i => {
return {};
})
} : {})
}
return { b: _ctx.ok, ...(_ctx.ok ? { a: _vFor(_ctx.list, i => { return {}; }) } : {}) }
}`
)
})
......@@ -708,13 +612,7 @@ return {
`<view v-for="item in items" :key="itemKey(item)">test</view>`,
`<view wx:for="{{a}}" wx:for-item="item" wx:key="a">test</view>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, item => {
return {
a: _ctx.itemKey(item)
};
})
}
return { a: _vFor(_ctx.items, item => { return { a: _ctx.itemKey(item) }; }) }
}`
)
})
......@@ -725,13 +623,7 @@ return {
`<template v-for="item in items" :key="itemKey(item)">test</template>`,
`<block wx:for="{{a}}" wx:for-item="item" wx:key="a">test</block>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, item => {
return {
a: _ctx.itemKey(item)
};
})
}
return { a: _vFor(_ctx.items, item => { return { a: _ctx.itemKey(item) }; }) }
}`
)
})
......@@ -741,11 +633,7 @@ return {
`<template v-for="item in items" key="key">test</template>`,
`<block wx:for="{{a}}" wx:for-item="item" key="key">test</block>`,
`(_ctx, _cache) => {
return {
a: _vFor(_ctx.items, item => {
return {};
})
}
return { a: _vFor(_ctx.items, item => { return {}; }) }
}`
)
})
......
......@@ -29,10 +29,7 @@ describe(`compiler: v-if`, () => {
`<view v-if="ok"/>`,
`<view wx:if="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _ctx.ok,
...(_ctx.ok ? {} : {})
}
return { a: _ctx.ok, ...(_ctx.ok ? {} : {}) }
}`
)
})
......@@ -41,10 +38,7 @@ return {
`<template v-if="ok"><view/>hello<view/></template>`,
`<block wx:if="{{a}}"><view/>hello<view/></block>`,
`(_ctx, _cache) => {
return {
a: _ctx.ok,
...(_ctx.ok ? {} : {})
}
return { a: _ctx.ok, ...(_ctx.ok ? {} : {}) }
}`
)
})
......@@ -53,10 +47,7 @@ return {
`<template v-if="ok"><slot/></template>`,
`<block wx:if="{{a}}"><slot/></block>`,
`(_ctx, _cache) => {
return {
a: _ctx.ok,
...(_ctx.ok ? {} : {})
}
return { a: _ctx.ok, ...(_ctx.ok ? {} : {}) }
}`
)
})
......@@ -65,10 +56,7 @@ return {
`<slot v-if="ok"/>`,
`<slot wx:if="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _ctx.ok,
...(_ctx.ok ? {} : {})
}
return { a: _ctx.ok, ...(_ctx.ok ? {} : {}) }
}`
)
})
......@@ -77,10 +65,7 @@ return {
`<Component v-if="ok"></Component>`,
`<Component wx:if="{{a}}"></Component>`,
`(_ctx, _cache) => {
return {
a: _ctx.ok,
...(_ctx.ok ? {} : {})
}
return { a: _ctx.ok, ...(_ctx.ok ? {} : {}) }
}`
)
})
......@@ -89,10 +74,7 @@ return {
`<view v-if="ok"/><view v-else/>`,
`<view wx:if="{{a}}"/><view wx:else/>`,
`(_ctx, _cache) => {
return {
a: _ctx.ok,
...(_ctx.ok ? {} : {})
}
return { a: _ctx.ok, ...(_ctx.ok ? {} : {}) }
}`
)
})
......@@ -101,11 +83,7 @@ return {
`<view v-if="ok"/><view v-else-if="orNot"/>`,
`<view wx:if="{{a}}"/><view wx:elif="{{b}}"/>`,
`(_ctx, _cache) => {
return {
a: _ctx.ok,
...(_ctx.ok ? {} : _ctx.orNot ? {} : {}),
b: _ctx.orNot
}
return { a: _ctx.ok, ...(_ctx.ok ? {} : _ctx.orNot ? {} : {}), b: _ctx.orNot }
}`
)
})
......@@ -114,11 +92,7 @@ return {
`<view v-if="ok"/><view v-else-if="orNot"/><template v-else>fine</template>`,
`<view wx:if="{{a}}"/><view wx:elif="{{b}}"/><block wx:else>fine</block>`,
`(_ctx, _cache) => {
return {
a: _ctx.ok,
...(_ctx.ok ? {} : _ctx.orNot ? {} : {}),
b: _ctx.orNot
}
return { a: _ctx.ok, ...(_ctx.ok ? {} : _ctx.orNot ? {} : {}), b: _ctx.orNot }
}`
)
})
......@@ -145,11 +119,7 @@ return {
`<view v-if="ok"/><view v-else-if="orNot"/><view v-else-if="3"/><template v-else>fine</template>`,
`<view wx:if="{{a}}"/><view wx:elif="{{b}}"/><view wx:elif="{{3}}"/><block wx:else>fine</block>`,
`(_ctx, _cache) => {
return {
a: _ctx.ok,
...(_ctx.ok ? {} : _ctx.orNot ? {} : 3 ? {} : {}),
b: _ctx.orNot
}
return { a: _ctx.ok, ...(_ctx.ok ? {} : _ctx.orNot ? {} : 3 ? {} : {}), b: _ctx.orNot }
}`
)
})
......@@ -164,11 +134,7 @@ return {
`,
`<view wx:if="{{a}}"/><view wx:elif="{{b}}"/><block wx:else>fine</block>`,
`(_ctx, _cache) => {
return {
a: _ctx.ok,
...(_ctx.ok ? {} : _ctx.orNot ? {} : {}),
b: _ctx.orNot
}
return { a: _ctx.ok, ...(_ctx.ok ? {} : _ctx.orNot ? {} : {}), b: _ctx.orNot }
}`
)
})
......@@ -177,11 +143,7 @@ return {
`<view v-if="ok"/> <view v-else-if="no"/> <view v-else/>`,
`<view wx:if="{{a}}"/><view wx:elif="{{b}}"/><view wx:else/>`,
`(_ctx, _cache) => {
return {
a: _ctx.ok,
...(_ctx.ok ? {} : _ctx.no ? {} : {}),
b: _ctx.no
}
return { a: _ctx.ok, ...(_ctx.ok ? {} : _ctx.no ? {} : {}), b: _ctx.no }
}`
)
})
......@@ -201,13 +163,7 @@ return {
`,
`<block wx:if="{{b}}"><view wx:if="{{a}}"></view><view wx:else/><view/></block>`,
`(_ctx, _cache) => {
return {
b: _ctx.ok,
...(_ctx.ok ? {
a: _ctx.ok2,
...(_ctx.ok2 ? {} : {})
} : {})
}
return { b: _ctx.ok, ...(_ctx.ok ? { a: _ctx.ok2, ...(_ctx.ok2 ? {} : {}) } : {}) }
}`
)
})
......
......@@ -17,9 +17,7 @@ describe('compiler: transform v-on', () => {
`<view v-on:click="onClick"/>`,
`<view bindtap="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn(_ctx.onClick)
}
return { a: _vOn(_ctx.onClick) }
}`
)
})
......@@ -37,9 +35,7 @@ return {
`<view @click="i++"/>`,
`<view bindtap="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn($event => _ctx.i++)
}
return { a: _vOn($event => _ctx.i++) }
}`
)
})
......@@ -48,13 +44,7 @@ return {
`<view @click="foo();bar()"/>`,
`<view bindtap="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn($event => {
_ctx.foo();
_ctx.bar();
})
}
return { a: _vOn($event => { _ctx.foo(); _ctx.bar(); }) }
}`
)
})
......@@ -63,14 +53,13 @@ return {
`<view @click="\nfoo();\nbar()\n"/>`,
`<view bindtap="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn($event => {
foo();
bar();
})
}
with (_ctx) {
const { vOn: _vOn } = _Vue
return { a: _vOn($event => { foo(); bar(); }) }
}
}`,
{ prefixIdentifiers: false }
{ prefixIdentifiers: false, mode: 'function' }
)
})
test('inline statement w/ prefixIdentifiers: true', () => {
......@@ -78,9 +67,7 @@ return {
`<view @click="foo($event)"/>`,
`<view bindtap="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn($event => _ctx.foo($event))
}
return { a: _vOn($event => _ctx.foo($event)) }
}`
)
})
......@@ -89,13 +76,7 @@ return {
`<view @click="foo($event);bar()"/>`,
`<view bindtap="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn($event => {
_ctx.foo($event);
_ctx.bar();
})
}
return { a: _vOn($event => { _ctx.foo($event); _ctx.bar(); }) }
}`
)
})
......@@ -104,9 +85,7 @@ return {
`<view @click="$event => foo($event)"/>`,
`<view bindtap="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn($event => _ctx.foo($event))
}
return { a: _vOn($event => _ctx.foo($event)) }
}`
)
})
......@@ -119,11 +98,7 @@ return {
"/>`,
`<view bindtap="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn($event => {
_ctx.foo($event);
})
}
return { a: _vOn($event => { _ctx.foo($event); }) }
}`
)
})
......@@ -136,11 +111,7 @@ return {
"/>`,
`<view bindtap="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn(function ($event) {
_ctx.foo($event);
})
}
return { a: _vOn(function ($event) { _ctx.foo($event); }) }
}`
)
})
......@@ -149,12 +120,15 @@ return {
`<view @click="a['b' + c]"/>`,
`<view bindtap="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn(a['b' + c])
}
with (_ctx) {
const { vOn: _vOn } = _Vue
return { a: _vOn(a['b' + c]) }
}
}`,
{
prefixIdentifiers: false,
mode: 'function',
}
)
})
......@@ -163,9 +137,7 @@ return {
`<view @click="a['b' + c]"/>`,
`<view bindtap="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn(_ctx.a['b' + _ctx.c])
}
return { a: _vOn(_ctx.a['b' + _ctx.c]) }
}`
)
})
......@@ -174,9 +146,7 @@ return {
`<view @click="e => foo(e)"/>`,
`<view bindtap="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn(e => _ctx.foo(e))
}
return { a: _vOn(e => _ctx.foo(e)) }
}`
)
})
......@@ -208,9 +178,7 @@ return {
`<view v-on:foo-bar="onMount"/>`,
`<view bind:foo-bar="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn(_ctx.onMount)
}
return { a: _vOn(_ctx.onMount) }
}`
)
})
......@@ -220,9 +188,7 @@ return {
`<view v-on:vnode-mounted="onMount"/>`,
`<view bind:vnode-mounted="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn(_ctx.onMount)
}
return { a: _vOn(_ctx.onMount) }
}`
)
})
......@@ -233,9 +199,7 @@ return {
`<view v-on:click.prevent />`,
`<view catchtap="{{a}}"/>`,
`(_ctx, _cache) => {
return {
a: _vOn(() => {})
}
return { a: _vOn(() => {}) }
}`
)
})
......
......@@ -5,6 +5,7 @@ import {
helperNameMap,
InterpolationNode,
NodeTypes,
RootNode,
SimpleExpressionNode,
TextNode,
TO_DISPLAY_STRING,
......@@ -13,11 +14,40 @@ import { default as babelGenerate } from '@babel/generator'
import { CodegenOptions, CodegenScope } from './options'
import { createObjectExpression } from './ast'
interface CodegenContext extends CodegenOptions {
code: string
indentLevel: number
push(code: string, node?: CodegenNode): void
indent(): void
deindent(withoutNewLine?: boolean): void
newline(): void
}
export function generate(
ast: RootNode,
scope: CodegenScope,
options: CodegenOptions
): Omit<CodegenResult, 'ast'> {
const context = createCodegenContext(ast, options)
const { mode, push, indent, deindent, newline, prefixIdentifiers } = context
const hasHelpers = ast.helpers.length > 0
const useWithBlock = !prefixIdentifiers && mode !== 'module'
const isSetupInlined = !!options.inline
// preambles
// in setup() inline mode, the preamble is generated in a sub context
// and returned separately.
const preambleContext = isSetupInlined
? createCodegenContext(ast, options)
: context
if (mode === 'module') {
genModulePreamble(ast, preambleContext, isSetupInlined)
} else {
genFunctionPreamble(ast, preambleContext)
}
// enter render function
const functionName = `render`
const args = ['_ctx', '_cache']
......@@ -28,23 +58,126 @@ export function generate(
const signature = options.isTS
? args.map((arg) => `${arg}: any`).join(',')
: args.join(', ')
const codes: string[] = []
if (isSetupInlined) {
codes.push(`(${signature}) => {`)
push(`(${signature}) => {`)
} else {
codes.push(`\nexport function ${functionName}(${signature}) {`)
push(`function ${functionName}(${signature}) {`)
}
indent()
if (useWithBlock) {
push(`with (_ctx) {`)
indent()
if (hasHelpers) {
push(
`const { ${ast.helpers
.map((s) => `${helperNameMap[s]}: _${helperNameMap[s]}`)
.join(', ')} } = _Vue`
)
push(`\n`)
newline()
}
}
codes.push(
`return ` +
babelGenerate(createObjectExpression(scope.properties), {
// concise: true,
}).code
)
codes.push(`}`)
push(`return `)
push(
babelGenerate(createObjectExpression(scope.properties), {
concise: true,
}).code
)
if (useWithBlock) {
deindent()
push(`}`)
}
deindent()
push(`}`)
return {
code: codes.join('\n'),
preamble: '',
code: context.code,
preamble: isSetupInlined ? preambleContext.code : ``,
}
}
function createCodegenContext(
ast: RootNode,
{
mode = 'function',
prefixIdentifiers = mode === 'module',
filename = `template.vue.html`,
scopeId = null,
runtimeGlobalName = `Vue`,
runtimeModuleName = `vue`,
isTS = false,
}: CodegenOptions
): CodegenContext {
const context: CodegenContext = {
mode,
prefixIdentifiers,
filename,
scopeId,
runtimeGlobalName,
runtimeModuleName,
isTS,
code: ``,
indentLevel: 0,
push(code, node) {
context.code += code
},
indent() {
newline(++context.indentLevel)
},
deindent(withoutNewLine = false) {
if (withoutNewLine) {
--context.indentLevel
} else {
newline(--context.indentLevel)
}
},
newline() {
newline(context.indentLevel)
},
}
function newline(n: number) {
context.push('\n' + ` `.repeat(n))
}
return context
}
function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
const { prefixIdentifiers, push, newline, runtimeGlobalName } = context
const VueBinding = runtimeGlobalName
const aliasHelper = (s: symbol) => `${helperNameMap[s]}: _${helperNameMap[s]}`
if (ast.helpers.length > 0) {
if (prefixIdentifiers) {
push(
`const { ${ast.helpers.map(aliasHelper).join(', ')} } = ${VueBinding}\n`
)
} else {
push(`const _Vue = ${VueBinding}\n`)
}
}
newline()
push(`return `)
}
function genModulePreamble(
ast: RootNode,
context: CodegenContext,
inline?: boolean
) {
const { push, newline, runtimeModuleName } = context
if (ast.helpers.length) {
push(
`import { ${ast.helpers
.map((s) => `${helperNameMap[s]} as _${helperNameMap[s]}`)
.join(', ')} } from ${JSON.stringify(runtimeModuleName)}\n`
)
}
newline()
if (!inline) {
push(`export `)
}
}
......
......@@ -55,7 +55,7 @@ export function baseCompile(template: string, options: CompilerOptions = {}) {
),
})
)
const result = extend(generate(context.scope, options), { ast })
const result = extend(generate(ast, context.scope, options), { ast })
if (options.filename && options.miniProgram?.emitFile) {
genTemplate(ast, {
filename: options.filename,
......
......@@ -105,6 +105,10 @@ export function isVForScope(scope: CodegenScope): scope is CodegenVForScope {
export function transform(root: RootNode, options: TransformOptions) {
const context = createTransformContext(root, options)
traverseNode(root, context)
// finalize meta information
root.helpers = [...context.helpers.keys()]
root.components = [...context.components]
root.cached = context.cached
return context
}
......
......@@ -95,7 +95,6 @@ function processProps(node: ElementNode, context: TransformContext) {
if (directiveTransform) {
const { props } = directiveTransform(prop, node, context)
prop.exp = props[0].value as ExpressionNode
console.log('prop', prop)
}
}
}
......
......@@ -2,10 +2,12 @@ import { BaseNode } from 'estree'
import { walk } from 'estree-walker'
import { Expression, isIdentifier, isReferenced } from '@babel/types'
import {
createCompoundExpression,
createSimpleExpression,
ExpressionNode,
NodeTypes,
SimpleExpressionNode,
TO_DISPLAY_STRING,
} from '@vue/compiler-core'
import { createObjectProperty, parseExpr } from '../ast'
import { genExpr } from '../codegen'
......@@ -22,7 +24,14 @@ import { isForElementNode } from './vFor'
export const transformIdentifier: NodeTransform = (node, context) => {
return () => {
if (node.type === NodeTypes.INTERPOLATION) {
node.content = rewriteExpression(node.content, context)
node.content = rewriteExpression(
createCompoundExpression([
`${context.helperString(TO_DISPLAY_STRING)}(`,
node.content,
`)`,
]),
context
)
} else if (node.type === NodeTypes.ELEMENT) {
const vFor = isForElementNode(node) && node.vFor
for (let i = 0; i < node.props.length; i++) {
......
......@@ -4650,6 +4650,7 @@ const getFunctionalFallthrough = (attrs) => {
};
function renderComponentRoot(instance) {
const { type: Component, vnode, proxy, withProxy, props, slots, attrs, emit, render, renderCache, data, setupState, ctx } = instance;
instance.$ei = 0;
let result;
const prev = setCurrentRenderingInstance(instance);
try {
......@@ -4951,6 +4952,46 @@ var plugin = {
},
};
/**
* Actual implementation
*/
function vFor(source, renderItem) {
let ret;
if (isArray(source) || isString(source)) {
ret = new Array(source.length);
for (let i = 0, l = source.length; i < l; i++) {
ret[i] = renderItem(source[i], i, undefined);
}
}
else if (typeof source === 'number') {
if ((process.env.NODE_ENV !== 'production') && !Number.isInteger(source)) {
warn$1(`The v-for range expect an integer value but got ${source}.`);
return [];
}
ret = new Array(source);
for (let i = 0; i < source; i++) {
ret[i] = renderItem(i + 1, i, undefined);
}
}
else if (isObject(source)) {
if (source[Symbol.iterator]) {
ret = Array.from(source, (item, i) => renderItem(item, i, undefined));
}
else {
const keys = Object.keys(source);
ret = new Array(keys.length);
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i];
ret[i] = renderItem(source[key], key, i);
}
}
}
else {
ret = [];
}
return ret;
}
function vOn(value) {
const instance = getCurrentInstance();
const name = 'e' + instance.$ei++;
......@@ -4998,4 +5039,4 @@ function createApp(rootComponent, rootProps = null) {
}
const createSSRApp = createApp;
export { EffectScope, ReactiveEffect, callWithAsyncErrorHandling, callWithErrorHandling, computed, createApp, createSSRApp, createVNode$1 as createVNode, createVueApp, customRef, defineComponent, defineEmits, defineExpose, defineProps, effect, effectScope, getCurrentInstance, getCurrentScope, inject, injectHook, isInSSRComponentSetup, isProxy, isReactive, isReadonly, isRef, logError, markRaw, mergeDefaults, mergeProps, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onScopeDispose, onUnmounted, onUpdated, provide, proxyRefs, queuePostFlushCb, reactive, readonly, ref, resolveComponent, resolveDirective, resolveFilter, shallowReactive, shallowReadonly, shallowRef, stop, toHandlers, toRaw, toRef, toRefs, triggerRef, unref, useAttrs, useSSRContext, useSlots, vOn, version, warn$1 as warn, watch, watchEffect, watchPostEffect, watchSyncEffect, withAsyncContext, withCtx, withDefaults, withDirectives, withModifiers, withScopeId };
export { EffectScope, ReactiveEffect, callWithAsyncErrorHandling, callWithErrorHandling, computed, createApp, createSSRApp, createVNode$1 as createVNode, createVueApp, customRef, defineComponent, defineEmits, defineExpose, defineProps, effect, effectScope, getCurrentInstance, getCurrentScope, inject, injectHook, isInSSRComponentSetup, isProxy, isReactive, isReadonly, isRef, logError, markRaw, mergeDefaults, mergeProps, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onScopeDispose, onUnmounted, onUpdated, provide, proxyRefs, queuePostFlushCb, reactive, readonly, ref, resolveComponent, resolveDirective, resolveFilter, shallowReactive, shallowReadonly, shallowRef, stop, toHandlers, toRaw, toRef, toRefs, triggerRef, unref, useAttrs, useSSRContext, useSlots, vFor, vOn, version, warn$1 as warn, watch, watchEffect, watchPostEffect, watchSyncEffect, withAsyncContext, withCtx, withDefaults, withDirectives, withModifiers, withScopeId };
......@@ -4582,6 +4582,7 @@ const getFunctionalFallthrough = (attrs) => {
};
function renderComponentRoot(instance) {
const { type: Component, vnode, proxy, withProxy, props, slots, attrs, emit, render, renderCache, data, setupState, ctx } = instance;
instance.$ei = 0;
let result;
const prev = setCurrentRenderingInstance(instance);
try {
......
export { vFor } from './vFor'
export { vOn } from './vOn'
import { isArray, isObject, isString } from '@vue/shared'
import { warn } from 'vue'
type VForItem = Record<string, unknown>
/**
* v-for string
* @private
*/
export function vFor(
source: string,
renderItem: (value: string, index: number) => VForItem
): VForItem[]
/**
* v-for number
*/
export function vFor(
source: number,
renderItem: (value: number, index: number) => VForItem
): VForItem[]
/**
* v-for array
*/
export function vFor<T>(
source: T[],
renderItem: (value: T, index: number) => VForItem
): VForItem[]
/**
* v-for iterable
*/
export function vFor<T>(
source: Iterable<T>,
renderItem: (value: T, index: number) => VForItem
): VForItem[]
/**
* v-for object
*/
export function vFor<T>(
source: T,
renderItem: <K extends keyof T>(
value: T[K],
key: K,
index: number
) => VForItem
): VForItem[]
/**
* Actual implementation
*/
export function vFor(
source: any,
renderItem: (...args: any[]) => VForItem
): VForItem[] {
let ret: VForItem[]
if (isArray(source) || isString(source)) {
ret = new Array(source.length)
for (let i = 0, l = source.length; i < l; i++) {
ret[i] = renderItem(source[i], i, undefined)
}
} else if (typeof source === 'number') {
if (__DEV__ && !Number.isInteger(source)) {
warn(`The v-for range expect an integer value but got ${source}.`)
return []
}
ret = new Array(source)
for (let i = 0; i < source; i++) {
ret[i] = renderItem(i + 1, i, undefined)
}
} else if (isObject(source)) {
if (source[Symbol.iterator as any]) {
ret = Array.from(source as Iterable<any>, (item, i) =>
renderItem(item, i, undefined)
)
} else {
const keys = Object.keys(source)
ret = new Array(keys.length)
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i]
ret[i] = renderItem(source[key], key, i)
}
}
} else {
ret = []
}
return ret
}
......@@ -5,7 +5,7 @@ export function createApp(rootComponent: unknown, rootProps = null) {
rootComponent && ((rootComponent as any).mpType = 'app')
return createVueApp(rootComponent, rootProps).use(plugin)
}
export { vOn } from './helpers/vOn'
export const createSSRApp = createApp
export * from './helpers'
// @ts-ignore
export * from '../lib/vue.runtime.esm.js'
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册