mirror of https://github.com/vuejs/core.git
refactor(compiler-sfc): destructure built-in properties ($emit,$attrs,$slots) in inline mode (#13663)
Co-authored-by: daiwei <daiwei521@126.com>
This commit is contained in:
parent
e56997f0d5
commit
57354e195a
|
@ -823,6 +823,450 @@ return (_ctx, _cache) => {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructure setup context for built-in properties > should alias __emit to $emit when defineEmits is used 1`] = `
|
||||
"import { openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
emits: ['click'],
|
||||
setup(__props, { emit: __emit }) {
|
||||
const $emit = __emit
|
||||
|
||||
const emit = __emit
|
||||
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", {
|
||||
onClick: _cache[0] || (_cache[0] = $event => ($emit('click')))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructure setup context for built-in properties > should alias __props to $props when $props is used 1`] = `
|
||||
"import { toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props) {
|
||||
const $props = __props
|
||||
/* ... */
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", null, _toDisplayString($props), 1 /* TEXT */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructure setup context for built-in properties > should extract all built-in properties when they are used 1`] = `
|
||||
"import { toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props, { emit: $emit, attrs: $attrs, slots: $slots }) {
|
||||
const $props = __props
|
||||
/* ... */
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", null, _toDisplayString($props) + _toDisplayString($slots) + _toDisplayString($emit) + _toDisplayString($attrs), 1 /* TEXT */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructure setup context for built-in properties > should extract attrs when $attrs is used 1`] = `
|
||||
"import { normalizeProps as _normalizeProps, guardReactiveProps as _guardReactiveProps, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props, { attrs: $attrs }) {
|
||||
/* ... */
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", _normalizeProps(_guardReactiveProps($attrs)), null, 16 /* FULL_PROPS */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructure setup context for built-in properties > should extract emit when $emit is used 1`] = `
|
||||
"import { openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props, { emit: $emit }) {
|
||||
/* ... */
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", {
|
||||
onClick: _cache[0] || (_cache[0] = $event => ($emit('click')))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructure setup context for built-in properties > should extract slots when $slots is used 1`] = `
|
||||
"import { resolveComponent as _resolveComponent, openBlock as _openBlock, createBlock as _createBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props, { slots: $slots }) {
|
||||
/* ... */
|
||||
return (_ctx, _cache) => {
|
||||
const _component_Comp = _resolveComponent("Comp")
|
||||
|
||||
return (_openBlock(), _createBlock(_component_Comp, {
|
||||
foo: $slots.foo
|
||||
}, null, 8 /* PROPS */, ["foo"]))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructure setup context for built-in properties > should not extract built-in properties when neither is used 1`] = `
|
||||
"import { toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props) {
|
||||
/* ... */
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", null, _toDisplayString(_ctx.msg), 1 /* TEXT */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructure setup context for built-in properties > user-defined properties override > should handle mixed defineEmits and user-defined $emit 1`] = `
|
||||
"import { unref as _unref, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
emits: ['click'],
|
||||
setup(__props, { emit: __emit }) {
|
||||
|
||||
const emit = __emit
|
||||
let $emit
|
||||
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", {
|
||||
onClick: _cache[0] || (_cache[0] = $event => (_unref($emit)('click')))
|
||||
}, "click"))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructure setup context for built-in properties > user-defined properties override > should not extract $attrs when user defines it 1`] = `
|
||||
"import { unref as _unref, normalizeProps as _normalizeProps, guardReactiveProps as _guardReactiveProps, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props) {
|
||||
let $attrs
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", _normalizeProps(_guardReactiveProps(_unref($attrs))), null, 16 /* FULL_PROPS */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructure setup context for built-in properties > user-defined properties override > should not extract $emit when user defines it 1`] = `
|
||||
"import { unref as _unref, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props) {
|
||||
let $emit
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", {
|
||||
onClick: _cache[0] || (_cache[0] = $event => (_unref($emit)('click')))
|
||||
}, "click"))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructure setup context for built-in properties > user-defined properties override > should not extract $slots when user defines it 1`] = `
|
||||
"import { unref as _unref, resolveComponent as _resolveComponent, openBlock as _openBlock, createBlock as _createBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props) {
|
||||
let $slots
|
||||
return (_ctx, _cache) => {
|
||||
const _component_Comp = _resolveComponent("Comp")
|
||||
|
||||
return (_openBlock(), _createBlock(_component_Comp, {
|
||||
foo: _unref($slots).foo
|
||||
}, null, 8 /* PROPS */, ["foo"]))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructure setup context for built-in properties > user-defined properties override > should not generate $props alias when user defines it 1`] = `
|
||||
"import { unref as _unref, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props) {
|
||||
let $props
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", null, _toDisplayString(_unref($props).msg), 1 /* TEXT */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructure setup context for built-in properties > user-defined properties override > should only extract non-user-defined properties 1`] = `
|
||||
"import { unref as _unref, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props, { emit: $emit, slots: $slots }) {
|
||||
const $props = __props
|
||||
let $attrs
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", null, _toDisplayString(_unref($attrs)) + _toDisplayString($slots) + _toDisplayString($emit) + _toDisplayString($props), 1 /* TEXT */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructuring setup context for built-in properties > should alias __emit to $emit when defineEmits is used 1`] = `
|
||||
"import { openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
emits: ['click'],
|
||||
setup(__props, { emit: __emit }) {
|
||||
const $emit = __emit
|
||||
|
||||
const emit = __emit
|
||||
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", {
|
||||
onClick: _cache[0] || (_cache[0] = $event => ($emit('click')))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructuring setup context for built-in properties > should alias __props to $props when $props is used 1`] = `
|
||||
"import { toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props) {
|
||||
const $props = __props
|
||||
/* ... */
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", null, _toDisplayString($props), 1 /* TEXT */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructuring setup context for built-in properties > should extract all built-in properties when they are used 1`] = `
|
||||
"import { toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props, { emit: $emit, attrs: $attrs, slots: $slots }) {
|
||||
const $props = __props
|
||||
/* ... */
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", null, _toDisplayString($props) + _toDisplayString($slots) + _toDisplayString($emit) + _toDisplayString($attrs), 1 /* TEXT */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructuring setup context for built-in properties > should extract attrs when $attrs is used 1`] = `
|
||||
"import { normalizeProps as _normalizeProps, guardReactiveProps as _guardReactiveProps, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props, { attrs: $attrs }) {
|
||||
/* ... */
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", _normalizeProps(_guardReactiveProps($attrs)), null, 16 /* FULL_PROPS */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructuring setup context for built-in properties > should extract emit when $emit is used 1`] = `
|
||||
"import { openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props, { emit: $emit }) {
|
||||
/* ... */
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", {
|
||||
onClick: _cache[0] || (_cache[0] = $event => ($emit('click')))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructuring setup context for built-in properties > should extract slots when $slots is used 1`] = `
|
||||
"import { resolveComponent as _resolveComponent, openBlock as _openBlock, createBlock as _createBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props, { slots: $slots }) {
|
||||
/* ... */
|
||||
return (_ctx, _cache) => {
|
||||
const _component_Comp = _resolveComponent("Comp")
|
||||
|
||||
return (_openBlock(), _createBlock(_component_Comp, {
|
||||
foo: $slots.foo
|
||||
}, null, 8 /* PROPS */, ["foo"]))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructuring setup context for built-in properties > should not extract built-in properties when neither is used 1`] = `
|
||||
"import { toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props) {
|
||||
/* ... */
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", null, _toDisplayString(_ctx.msg), 1 /* TEXT */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructuring setup context for built-in properties > user-defined properties override > should handle mixed defineEmits and user-defined $emit 1`] = `
|
||||
"import { unref as _unref, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
emits: ['click'],
|
||||
setup(__props, { emit: __emit }) {
|
||||
|
||||
const emit = __emit
|
||||
let $emit
|
||||
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", {
|
||||
onClick: _cache[0] || (_cache[0] = $event => (_unref($emit)('click')))
|
||||
}, "click"))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructuring setup context for built-in properties > user-defined properties override > should not extract $attrs when user defines it 1`] = `
|
||||
"import { unref as _unref, normalizeProps as _normalizeProps, guardReactiveProps as _guardReactiveProps, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props) {
|
||||
let $attrs
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", _normalizeProps(_guardReactiveProps(_unref($attrs))), null, 16 /* FULL_PROPS */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructuring setup context for built-in properties > user-defined properties override > should not extract $emit when user defines it 1`] = `
|
||||
"import { unref as _unref, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props) {
|
||||
let $emit
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", {
|
||||
onClick: _cache[0] || (_cache[0] = $event => (_unref($emit)('click')))
|
||||
}, "click"))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructuring setup context for built-in properties > user-defined properties override > should not extract $slots when user defines it 1`] = `
|
||||
"import { unref as _unref, resolveComponent as _resolveComponent, openBlock as _openBlock, createBlock as _createBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props) {
|
||||
let $slots
|
||||
return (_ctx, _cache) => {
|
||||
const _component_Comp = _resolveComponent("Comp")
|
||||
|
||||
return (_openBlock(), _createBlock(_component_Comp, {
|
||||
foo: _unref($slots).foo
|
||||
}, null, 8 /* PROPS */, ["foo"]))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructuring setup context for built-in properties > user-defined properties override > should not generate $props alias when user defines it 1`] = `
|
||||
"import { unref as _unref, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props) {
|
||||
let $props
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", null, _toDisplayString(_unref($props).msg), 1 /* TEXT */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > destructuring setup context for built-in properties > user-defined properties override > should only extract non-user-defined properties 1`] = `
|
||||
"import { unref as _unref, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
||||
export default {
|
||||
setup(__props, { emit: $emit, slots: $slots }) {
|
||||
const $props = __props
|
||||
let $attrs
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createElementBlock("div", null, _toDisplayString(_unref($attrs)) + _toDisplayString($slots) + _toDisplayString($emit) + _toDisplayString($props), 1 /* TEXT */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> > inlineTemplate mode > referencing scope components and directives 1`] = `
|
||||
"import { unref as _unref, createElementVNode as _createElementVNode, withDirectives as _withDirectives, createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
|
||||
|
||||
|
|
|
@ -717,6 +717,151 @@ describe('SFC compile <script setup>', () => {
|
|||
consumer.originalPositionFor(getPositionInCode(content, 'Error')),
|
||||
).toMatchObject(getPositionInCode(source, `Error`))
|
||||
})
|
||||
|
||||
describe('destructure setup context for built-in properties', () => {
|
||||
const theCompile = (template: string, setup = '/* ... */') =>
|
||||
compile(
|
||||
`<script setup>${setup}</script>\n<template>${template}</template>`,
|
||||
{ inlineTemplate: true },
|
||||
)
|
||||
|
||||
test('should extract attrs when $attrs is used', () => {
|
||||
let { content } = theCompile('<div v-bind="$attrs"></div>')
|
||||
expect(content).toMatch('setup(__props, { attrs: $attrs })')
|
||||
expect(content).not.toMatch('slots: $slots')
|
||||
expect(content).not.toMatch('emit: $emit')
|
||||
expect(content).not.toMatch('const $props = __props')
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('should extract slots when $slots is used', () => {
|
||||
let { content } = theCompile('<Comp :foo="$slots.foo"></Comp>')
|
||||
expect(content).toMatch('setup(__props, { slots: $slots })')
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('should alias __props to $props when $props is used', () => {
|
||||
let { content } = theCompile('<div>{{ $props }}</div>')
|
||||
expect(content).toMatch('setup(__props)')
|
||||
expect(content).toMatch('const $props = __props')
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('should extract emit when $emit is used', () => {
|
||||
let { content } = theCompile(`<div @click="$emit('click')"></div>`)
|
||||
expect(content).toMatch('setup(__props, { emit: $emit })')
|
||||
expect(content).not.toMatch('const $emit = __emit')
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('should alias __emit to $emit when defineEmits is used', () => {
|
||||
let { content } = compile(
|
||||
`
|
||||
<script setup>
|
||||
const emit = defineEmits(['click'])
|
||||
</script>
|
||||
<template>
|
||||
<div @click="$emit('click')"></div>
|
||||
</template>
|
||||
`,
|
||||
{ inlineTemplate: true },
|
||||
)
|
||||
expect(content).toMatch('setup(__props, { emit: __emit })')
|
||||
expect(content).toMatch('const $emit = __emit')
|
||||
expect(content).toMatch('const emit = __emit')
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('should extract all built-in properties when they are used', () => {
|
||||
let { content } = theCompile(
|
||||
'<div>{{ $props }}{{ $slots }}{{ $emit }}{{ $attrs }}</div>',
|
||||
)
|
||||
expect(content).toMatch(
|
||||
'setup(__props, { emit: $emit, attrs: $attrs, slots: $slots })',
|
||||
)
|
||||
expect(content).toMatch('const $props = __props')
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('should not extract built-in properties when neither is used', () => {
|
||||
let { content } = theCompile('<div>{{ msg }}</div>')
|
||||
expect(content).toMatch('setup(__props)')
|
||||
expect(content).not.toMatch('attrs: $attrs')
|
||||
expect(content).not.toMatch('slots: $slots')
|
||||
expect(content).not.toMatch('emit: $emit')
|
||||
expect(content).not.toMatch('props: $props')
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
describe('user-defined properties override', () => {
|
||||
test('should not extract $attrs when user defines it', () => {
|
||||
let { content } = theCompile(
|
||||
'<div v-bind="$attrs"></div>',
|
||||
'let $attrs',
|
||||
)
|
||||
expect(content).toMatch('setup(__props)')
|
||||
expect(content).not.toMatch('attrs: $attrs')
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('should not extract $slots when user defines it', () => {
|
||||
let { content } = theCompile(
|
||||
'<Comp :foo="$slots.foo"></Comp>',
|
||||
'let $slots',
|
||||
)
|
||||
expect(content).toMatch('setup(__props)')
|
||||
expect(content).not.toMatch('slots: $slots')
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('should not extract $emit when user defines it', () => {
|
||||
let { content } = theCompile(
|
||||
`<div @click="$emit('click')">click</div>`,
|
||||
'let $emit',
|
||||
)
|
||||
expect(content).toMatch('setup(__props)')
|
||||
expect(content).not.toMatch('emit: $emit')
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('should not generate $props alias when user defines it', () => {
|
||||
let { content } = theCompile(
|
||||
'<div>{{ $props.msg }}</div>',
|
||||
'let $props',
|
||||
)
|
||||
expect(content).toMatch('setup(__props)')
|
||||
expect(content).not.toMatch('const $props = __props')
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('should only extract non-user-defined properties', () => {
|
||||
let { content } = theCompile(
|
||||
'<div>{{ $attrs }}{{ $slots }}{{ $emit }}{{ $props }}</div>',
|
||||
'let $attrs',
|
||||
)
|
||||
expect(content).toMatch(
|
||||
'setup(__props, { emit: $emit, slots: $slots })',
|
||||
)
|
||||
expect(content).not.toMatch('attrs: $attrs')
|
||||
expect(content).toMatch('const $props = __props')
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('should handle mixed defineEmits and user-defined $emit', () => {
|
||||
let { content } = theCompile(
|
||||
`<div @click="$emit('click')">click</div>`,
|
||||
`
|
||||
const emit = defineEmits(['click'])
|
||||
let $emit
|
||||
`,
|
||||
)
|
||||
expect(content).toMatch('setup(__props, { emit: __emit })')
|
||||
expect(content).toMatch('const emit = __emit')
|
||||
expect(content).not.toMatch('const $emit = __emit')
|
||||
assertCode(content)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with TypeScript', () => {
|
||||
|
|
|
@ -59,7 +59,7 @@ import { DEFINE_SLOTS, processDefineSlots } from './script/defineSlots'
|
|||
import { DEFINE_MODEL, processDefineModel } from './script/defineModel'
|
||||
import { getImportedName, isCallOf, isLiteralNode } from './script/utils'
|
||||
import { analyzeScriptBindings } from './script/analyzeScriptBindings'
|
||||
import { isImportUsed } from './script/importUsageCheck'
|
||||
import { isUsedInTemplate } from './script/importUsageCheck'
|
||||
import { processAwait } from './script/topLevelAwait'
|
||||
|
||||
export interface SFCScriptCompileOptions {
|
||||
|
@ -181,6 +181,7 @@ export function compileScript(
|
|||
const scriptSetupLang = scriptSetup && scriptSetup.lang
|
||||
const vapor = sfc.vapor || options.vapor
|
||||
const ssr = options.templateOptions?.ssr
|
||||
const setupPreambleLines = [] as string[]
|
||||
|
||||
if (!scriptSetup) {
|
||||
if (!script) {
|
||||
|
@ -246,7 +247,7 @@ export function compileScript(
|
|||
) {
|
||||
// template usage check is only needed in non-inline mode, so we can skip
|
||||
// the work if inlineTemplate is true.
|
||||
let isUsedInTemplate = needTemplateUsageCheck
|
||||
let isImportUsed = needTemplateUsageCheck
|
||||
if (
|
||||
needTemplateUsageCheck &&
|
||||
ctx.isTS &&
|
||||
|
@ -254,7 +255,7 @@ export function compileScript(
|
|||
!sfc.template.src &&
|
||||
!sfc.template.lang
|
||||
) {
|
||||
isUsedInTemplate = isImportUsed(local, sfc)
|
||||
isImportUsed = isUsedInTemplate(local, sfc)
|
||||
}
|
||||
|
||||
ctx.userImports[local] = {
|
||||
|
@ -263,7 +264,7 @@ export function compileScript(
|
|||
local,
|
||||
source,
|
||||
isFromSetup,
|
||||
isUsedInTemplate,
|
||||
isUsedInTemplate: isImportUsed,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,8 +285,42 @@ export function compileScript(
|
|||
})
|
||||
}
|
||||
|
||||
function buildDestructureElements() {
|
||||
if (!sfc.template || !sfc.template.ast) return
|
||||
|
||||
const builtins = {
|
||||
$props: {
|
||||
bindingType: BindingTypes.SETUP_REACTIVE_CONST,
|
||||
setup: () => setupPreambleLines.push(`const $props = __props`),
|
||||
},
|
||||
$emit: {
|
||||
bindingType: BindingTypes.SETUP_CONST,
|
||||
setup: () =>
|
||||
ctx.emitDecl
|
||||
? setupPreambleLines.push(`const $emit = __emit`)
|
||||
: destructureElements.push('emit: $emit'),
|
||||
},
|
||||
$attrs: {
|
||||
bindingType: BindingTypes.SETUP_REACTIVE_CONST,
|
||||
setup: () => destructureElements.push('attrs: $attrs'),
|
||||
},
|
||||
$slots: {
|
||||
bindingType: BindingTypes.SETUP_REACTIVE_CONST,
|
||||
setup: () => destructureElements.push('slots: $slots'),
|
||||
},
|
||||
}
|
||||
|
||||
for (const [name, config] of Object.entries(builtins)) {
|
||||
if (isUsedInTemplate(name, sfc) && !ctx.bindingMetadata[name]) {
|
||||
config.setup()
|
||||
ctx.bindingMetadata[name] = config.bindingType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const scriptAst = ctx.scriptAst
|
||||
const scriptSetupAst = ctx.scriptSetupAst!
|
||||
const inlineMode = options.inlineTemplate
|
||||
|
||||
// 1.1 walk import declarations of <script>
|
||||
if (scriptAst) {
|
||||
|
@ -302,7 +337,7 @@ export function compileScript(
|
|||
(specifier.type === 'ImportSpecifier' &&
|
||||
specifier.importKind === 'type'),
|
||||
false,
|
||||
!options.inlineTemplate,
|
||||
!inlineMode,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -370,7 +405,7 @@ export function compileScript(
|
|||
(specifier.type === 'ImportSpecifier' &&
|
||||
specifier.importKind === 'type'),
|
||||
true,
|
||||
!options.inlineTemplate,
|
||||
!inlineMode,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -811,12 +846,16 @@ export function compileScript(
|
|||
}
|
||||
|
||||
const destructureElements =
|
||||
ctx.hasDefineExposeCall || !options.inlineTemplate
|
||||
? [`expose: __expose`]
|
||||
: []
|
||||
ctx.hasDefineExposeCall || !inlineMode ? [`expose: __expose`] : []
|
||||
if (ctx.emitDecl) {
|
||||
destructureElements.push(`emit: __emit`)
|
||||
}
|
||||
|
||||
// destructure built-in properties (e.g. $emit, $attrs, $slots)
|
||||
if (inlineMode) {
|
||||
buildDestructureElements()
|
||||
}
|
||||
|
||||
if (destructureElements.length) {
|
||||
args += `, { ${destructureElements.join(', ')} }`
|
||||
}
|
||||
|
@ -824,10 +863,7 @@ export function compileScript(
|
|||
let templateMap
|
||||
// 9. generate return statement
|
||||
let returned
|
||||
if (
|
||||
!options.inlineTemplate ||
|
||||
(!sfc.template && ctx.hasDefaultExportRender)
|
||||
) {
|
||||
if (!inlineMode || (!sfc.template && ctx.hasDefaultExportRender)) {
|
||||
// non-inline mode, or has manual render in normal <script>
|
||||
// return bindings from script and script setup
|
||||
const allBindings: Record<string, any> = {
|
||||
|
@ -927,7 +963,7 @@ export function compileScript(
|
|||
}
|
||||
}
|
||||
|
||||
if (!options.inlineTemplate && !__TEST__) {
|
||||
if (!inlineMode && !__TEST__) {
|
||||
// in non-inline mode, the `__isScriptSetup: true` flag is used by
|
||||
// componentPublicInstance proxy to allow properties that start with $ or _
|
||||
ctx.s.appendRight(
|
||||
|
@ -976,8 +1012,12 @@ export function compileScript(
|
|||
|
||||
// <script setup> components are closed by default. If the user did not
|
||||
// explicitly call `defineExpose`, call expose() with no args.
|
||||
const exposeCall =
|
||||
ctx.hasDefineExposeCall || options.inlineTemplate ? `` : ` __expose();\n`
|
||||
if (!ctx.hasDefineExposeCall && !inlineMode)
|
||||
setupPreambleLines.push(`__expose();`)
|
||||
|
||||
const setupPreamble = setupPreambleLines.length
|
||||
? ` ${setupPreambleLines.join('\n ')}\n`
|
||||
: ''
|
||||
// wrap setup code with function.
|
||||
if (ctx.isTS) {
|
||||
// for TS, make sure the exported type is still valid type with
|
||||
|
@ -994,7 +1034,7 @@ export function compileScript(
|
|||
vapor && !ssr ? `defineVaporComponent` : `defineComponent`,
|
||||
)}({${def}${runtimeOptions}\n ${
|
||||
hasAwait ? `async ` : ``
|
||||
}setup(${args}) {\n${exposeCall}`,
|
||||
}setup(${args}) {\n${setupPreamble}`,
|
||||
)
|
||||
ctx.s.appendRight(endOffset, `})`)
|
||||
} else {
|
||||
|
@ -1010,14 +1050,14 @@ export function compileScript(
|
|||
`\n${genDefaultAs} /*@__PURE__*/Object.assign(${
|
||||
defaultExport ? `${normalScriptDefaultVar}, ` : ''
|
||||
}${definedOptions ? `${definedOptions}, ` : ''}{${runtimeOptions}\n ` +
|
||||
`${hasAwait ? `async ` : ``}setup(${args}) {\n${exposeCall}`,
|
||||
`${hasAwait ? `async ` : ``}setup(${args}) {\n${setupPreamble}`,
|
||||
)
|
||||
ctx.s.appendRight(endOffset, `})`)
|
||||
} else {
|
||||
ctx.s.prependLeft(
|
||||
startOffset,
|
||||
`\n${genDefaultAs} {${runtimeOptions}\n ` +
|
||||
`${hasAwait ? `async ` : ``}setup(${args}) {\n${exposeCall}`,
|
||||
`${hasAwait ? `async ` : ``}setup(${args}) {\n${setupPreamble}`,
|
||||
)
|
||||
ctx.s.appendRight(endOffset, `}`)
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import type { TemplateCompiler } from './compileTemplate'
|
|||
import { parseCssVars } from './style/cssVars'
|
||||
import { createCache } from './cache'
|
||||
import type { ImportBinding } from './compileScript'
|
||||
import { isImportUsed } from './script/importUsageCheck'
|
||||
import { isUsedInTemplate } from './script/importUsageCheck'
|
||||
import type { LRUCache } from 'lru-cache'
|
||||
import { genCacheKey } from '@vue/shared'
|
||||
|
||||
|
@ -449,7 +449,7 @@ export function hmrShouldReload(
|
|||
for (const key in prevImports) {
|
||||
// if an import was previous unused, but now is used, we need to force
|
||||
// reload so that the script now includes that import.
|
||||
if (!prevImports[key].isUsedInTemplate && isImportUsed(key, next)) {
|
||||
if (!prevImports[key].isUsedInTemplate && isUsedInTemplate(key, next)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,12 +11,16 @@ import { createCache } from '../cache'
|
|||
import { camelize, capitalize, isBuiltInDirective } from '@vue/shared'
|
||||
|
||||
/**
|
||||
* Check if an import is used in the SFC's template. This is used to determine
|
||||
* the properties that should be included in the object returned from setup()
|
||||
* when not using inline mode.
|
||||
* Check if an identifier is used in the SFC's template.
|
||||
* - 1.used to determine the properties that should be included in the object returned from setup()
|
||||
* when not using inline mode.
|
||||
* - 2.check whether the built-in properties such as $attrs, $slots, $emit are used in the template
|
||||
*/
|
||||
export function isImportUsed(local: string, sfc: SFCDescriptor): boolean {
|
||||
return resolveTemplateUsedIdentifiers(sfc).has(local)
|
||||
export function isUsedInTemplate(
|
||||
identifier: string,
|
||||
sfc: SFCDescriptor,
|
||||
): boolean {
|
||||
return resolveTemplateUsedIdentifiers(sfc).has(identifier)
|
||||
}
|
||||
|
||||
const templateUsageCheckCache = createCache<Set<string>>()
|
||||
|
|
Loading…
Reference in New Issue