feat(compiler-sfc): <script setup>
defineProps destructure transform (#4690)
This commit is contained in:
parent
d84d5ecdbd
commit
467e113b95
@ -82,6 +82,11 @@ export const enum BindingTypes {
|
|||||||
* declared as a prop
|
* declared as a prop
|
||||||
*/
|
*/
|
||||||
PROPS = 'props',
|
PROPS = 'props',
|
||||||
|
/**
|
||||||
|
* a local alias of a `<script setup>` destructured prop.
|
||||||
|
* the original is stored in __propsAliases of the bindingMetadata object.
|
||||||
|
*/
|
||||||
|
PROPS_ALIASED = 'props-aliased',
|
||||||
/**
|
/**
|
||||||
* a let binding (may or may not be a ref)
|
* a let binding (may or may not be a ref)
|
||||||
*/
|
*/
|
||||||
@ -110,6 +115,7 @@ export type BindingMetadata = {
|
|||||||
[key: string]: BindingTypes | undefined
|
[key: string]: BindingTypes | undefined
|
||||||
} & {
|
} & {
|
||||||
__isScriptSetup?: boolean
|
__isScriptSetup?: boolean
|
||||||
|
__propsAliases?: Record<string, string>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SharedTransformCodegenOptions {
|
interface SharedTransformCodegenOptions {
|
||||||
|
@ -188,11 +188,16 @@ export function processExpression(
|
|||||||
// use __props which is generated by compileScript so in ts mode
|
// use __props which is generated by compileScript so in ts mode
|
||||||
// it gets correct type
|
// it gets correct type
|
||||||
return `__props.${raw}`
|
return `__props.${raw}`
|
||||||
|
} else if (type === BindingTypes.PROPS_ALIASED) {
|
||||||
|
// prop with a different local alias (from defineProps() destructure)
|
||||||
|
return `__props.${bindingMetadata.__propsAliases![raw]}`
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (type && type.startsWith('setup')) {
|
if (type && type.startsWith('setup')) {
|
||||||
// setup bindings in non-inline mode
|
// setup bindings in non-inline mode
|
||||||
return `$setup.${raw}`
|
return `$setup.${raw}`
|
||||||
|
} else if (type === BindingTypes.PROPS_ALIASED) {
|
||||||
|
return `$props.${bindingMetadata.__propsAliases![raw]}`
|
||||||
} else if (type) {
|
} else if (type) {
|
||||||
return `$${type}.${raw}`
|
return `$${type}.${raw}`
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,128 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`sfc props transform aliasing 1`] = `
|
||||||
|
"import { toDisplayString as _toDisplayString } from \\"vue\\"
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ['foo'],
|
||||||
|
setup(__props) {
|
||||||
|
|
||||||
|
|
||||||
|
let x = foo
|
||||||
|
let y = __props.foo
|
||||||
|
|
||||||
|
return (_ctx, _cache) => {
|
||||||
|
return _toDisplayString(__props.foo + __props.foo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`sfc props transform basic usage 1`] = `
|
||||||
|
"import { toDisplayString as _toDisplayString } from \\"vue\\"
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ['foo'],
|
||||||
|
setup(__props) {
|
||||||
|
|
||||||
|
|
||||||
|
console.log(__props.foo)
|
||||||
|
|
||||||
|
return (_ctx, _cache) => {
|
||||||
|
return _toDisplayString(__props.foo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`sfc props transform default values w/ runtime declaration 1`] = `
|
||||||
|
"import { mergeDefaults as _mergeDefaults } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: _mergeDefaults(['foo', 'bar'], {
|
||||||
|
foo: 1,
|
||||||
|
bar: () => {}
|
||||||
|
}),
|
||||||
|
setup(__props) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return () => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`sfc props transform default values w/ type declaration 1`] = `
|
||||||
|
"import { defineComponent as _defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default /*#__PURE__*/_defineComponent({
|
||||||
|
props: {
|
||||||
|
foo: { type: Number, required: false, default: 1 },
|
||||||
|
bar: { type: Object, required: false, default: () => {} }
|
||||||
|
},
|
||||||
|
setup(__props: any) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return () => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
})"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`sfc props transform default values w/ type declaration, prod mode 1`] = `
|
||||||
|
"import { defineComponent as _defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default /*#__PURE__*/_defineComponent({
|
||||||
|
props: {
|
||||||
|
foo: { default: 1 },
|
||||||
|
bar: { default: () => {} },
|
||||||
|
baz: null
|
||||||
|
},
|
||||||
|
setup(__props: any) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return () => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
})"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`sfc props transform nested scope 1`] = `
|
||||||
|
"export default {
|
||||||
|
props: ['foo', 'bar'],
|
||||||
|
setup(__props) {
|
||||||
|
|
||||||
|
|
||||||
|
function test(foo) {
|
||||||
|
console.log(foo)
|
||||||
|
console.log(__props.bar)
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`sfc props transform rest spread 1`] = `
|
||||||
|
"import { createPropsRestProxy as _createPropsRestProxy } from 'vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: ['foo', 'bar', 'baz'],
|
||||||
|
setup(__props) {
|
||||||
|
|
||||||
|
const rest = _createPropsRestProxy(__props, [\\"foo\\",\\"bar\\"])
|
||||||
|
|
||||||
|
|
||||||
|
return () => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
}"
|
||||||
|
`;
|
@ -0,0 +1,191 @@
|
|||||||
|
import { BindingTypes } from '@vue/compiler-core'
|
||||||
|
import { SFCScriptCompileOptions } from '../src'
|
||||||
|
import { compileSFCScript, assertCode } from './utils'
|
||||||
|
|
||||||
|
describe('sfc props transform', () => {
|
||||||
|
function compile(src: string, options?: Partial<SFCScriptCompileOptions>) {
|
||||||
|
return compileSFCScript(src, {
|
||||||
|
inlineTemplate: true,
|
||||||
|
propsDestructureTransform: true,
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
test('basic usage', () => {
|
||||||
|
const { content, bindings } = compile(`
|
||||||
|
<script setup>
|
||||||
|
const { foo } = defineProps(['foo'])
|
||||||
|
console.log(foo)
|
||||||
|
</script>
|
||||||
|
<template>{{ foo }}</template>
|
||||||
|
`)
|
||||||
|
expect(content).not.toMatch(`const { foo } =`)
|
||||||
|
expect(content).toMatch(`console.log(__props.foo)`)
|
||||||
|
expect(content).toMatch(`_toDisplayString(__props.foo)`)
|
||||||
|
assertCode(content)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
foo: BindingTypes.PROPS
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('nested scope', () => {
|
||||||
|
const { content, bindings } = compile(`
|
||||||
|
<script setup>
|
||||||
|
const { foo, bar } = defineProps(['foo', 'bar'])
|
||||||
|
function test(foo) {
|
||||||
|
console.log(foo)
|
||||||
|
console.log(bar)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
`)
|
||||||
|
expect(content).not.toMatch(`const { foo, bar } =`)
|
||||||
|
expect(content).toMatch(`console.log(foo)`)
|
||||||
|
expect(content).toMatch(`console.log(__props.bar)`)
|
||||||
|
assertCode(content)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
foo: BindingTypes.PROPS,
|
||||||
|
bar: BindingTypes.PROPS,
|
||||||
|
test: BindingTypes.SETUP_CONST
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('default values w/ runtime declaration', () => {
|
||||||
|
const { content } = compile(`
|
||||||
|
<script setup>
|
||||||
|
const { foo = 1, bar = {} } = defineProps(['foo', 'bar'])
|
||||||
|
</script>
|
||||||
|
`)
|
||||||
|
// literals can be used as-is, non-literals are always returned from a
|
||||||
|
// function
|
||||||
|
expect(content).toMatch(`props: _mergeDefaults(['foo', 'bar'], {
|
||||||
|
foo: 1,
|
||||||
|
bar: () => {}
|
||||||
|
})`)
|
||||||
|
assertCode(content)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('default values w/ type declaration', () => {
|
||||||
|
const { content } = compile(`
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { foo = 1, bar = {} } = defineProps<{ foo?: number, bar?: object }>()
|
||||||
|
</script>
|
||||||
|
`)
|
||||||
|
// literals can be used as-is, non-literals are always returned from a
|
||||||
|
// function
|
||||||
|
expect(content).toMatch(`props: {
|
||||||
|
foo: { type: Number, required: false, default: 1 },
|
||||||
|
bar: { type: Object, required: false, default: () => {} }
|
||||||
|
}`)
|
||||||
|
assertCode(content)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('default values w/ type declaration, prod mode', () => {
|
||||||
|
const { content } = compile(
|
||||||
|
`
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { foo = 1, bar = {} } = defineProps<{ foo?: number, bar?: object, baz?: any }>()
|
||||||
|
</script>
|
||||||
|
`,
|
||||||
|
{ isProd: true }
|
||||||
|
)
|
||||||
|
// literals can be used as-is, non-literals are always returned from a
|
||||||
|
// function
|
||||||
|
expect(content).toMatch(`props: {
|
||||||
|
foo: { default: 1 },
|
||||||
|
bar: { default: () => {} },
|
||||||
|
baz: null
|
||||||
|
}`)
|
||||||
|
assertCode(content)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('aliasing', () => {
|
||||||
|
const { content, bindings } = compile(`
|
||||||
|
<script setup>
|
||||||
|
const { foo: bar } = defineProps(['foo'])
|
||||||
|
let x = foo
|
||||||
|
let y = bar
|
||||||
|
</script>
|
||||||
|
<template>{{ foo + bar }}</template>
|
||||||
|
`)
|
||||||
|
expect(content).not.toMatch(`const { foo: bar } =`)
|
||||||
|
expect(content).toMatch(`let x = foo`) // should not process
|
||||||
|
expect(content).toMatch(`let y = __props.foo`)
|
||||||
|
// should convert bar to __props.foo in template expressions
|
||||||
|
expect(content).toMatch(`_toDisplayString(__props.foo + __props.foo)`)
|
||||||
|
assertCode(content)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
x: BindingTypes.SETUP_LET,
|
||||||
|
y: BindingTypes.SETUP_LET,
|
||||||
|
foo: BindingTypes.PROPS,
|
||||||
|
bar: BindingTypes.PROPS_ALIASED,
|
||||||
|
__propsAliases: {
|
||||||
|
bar: 'foo'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('rest spread', () => {
|
||||||
|
const { content, bindings } = compile(`
|
||||||
|
<script setup>
|
||||||
|
const { foo, bar, ...rest } = defineProps(['foo', 'bar', 'baz'])
|
||||||
|
</script>
|
||||||
|
`)
|
||||||
|
expect(content).toMatch(
|
||||||
|
`const rest = _createPropsRestProxy(__props, ["foo","bar"])`
|
||||||
|
)
|
||||||
|
assertCode(content)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
foo: BindingTypes.PROPS,
|
||||||
|
bar: BindingTypes.PROPS,
|
||||||
|
baz: BindingTypes.PROPS,
|
||||||
|
rest: BindingTypes.SETUP_CONST
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('errors', () => {
|
||||||
|
test('should error on deep destructure', () => {
|
||||||
|
expect(() =>
|
||||||
|
compile(
|
||||||
|
`<script setup>const { foo: [bar] } = defineProps(['foo'])</script>`
|
||||||
|
)
|
||||||
|
).toThrow(`destructure does not support nested patterns`)
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
compile(
|
||||||
|
`<script setup>const { foo: { bar } } = defineProps(['foo'])</script>`
|
||||||
|
)
|
||||||
|
).toThrow(`destructure does not support nested patterns`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should error on computed key', () => {
|
||||||
|
expect(() =>
|
||||||
|
compile(
|
||||||
|
`<script setup>const { [foo]: bar } = defineProps(['foo'])</script>`
|
||||||
|
)
|
||||||
|
).toThrow(`destructure cannot use computed key`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should error when used with withDefaults', () => {
|
||||||
|
expect(() =>
|
||||||
|
compile(
|
||||||
|
`<script setup lang="ts">
|
||||||
|
const { foo } = withDefaults(defineProps<{ foo: string }>(), { foo: 'foo' })
|
||||||
|
</script>`
|
||||||
|
)
|
||||||
|
).toThrow(`withDefaults() is unnecessary when using destructure`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should error if destructure reference local vars', () => {
|
||||||
|
expect(() =>
|
||||||
|
compile(
|
||||||
|
`<script setup>
|
||||||
|
const x = 1
|
||||||
|
const {
|
||||||
|
foo = () => x
|
||||||
|
} = defineProps(['foo'])
|
||||||
|
</script>`
|
||||||
|
)
|
||||||
|
).toThrow(`cannot reference locally declared variables`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -39,7 +39,9 @@ import {
|
|||||||
TSInterfaceBody,
|
TSInterfaceBody,
|
||||||
AwaitExpression,
|
AwaitExpression,
|
||||||
Program,
|
Program,
|
||||||
ObjectMethod
|
ObjectMethod,
|
||||||
|
LVal,
|
||||||
|
Expression
|
||||||
} from '@babel/types'
|
} from '@babel/types'
|
||||||
import { walk } from 'estree-walker'
|
import { walk } from 'estree-walker'
|
||||||
import { RawSourceMap } from 'source-map'
|
import { RawSourceMap } from 'source-map'
|
||||||
@ -88,9 +90,15 @@ export interface SFCScriptCompileOptions {
|
|||||||
/**
|
/**
|
||||||
* (Experimental) Enable syntax transform for using refs without `.value`
|
* (Experimental) Enable syntax transform for using refs without `.value`
|
||||||
* https://github.com/vuejs/rfcs/discussions/369
|
* https://github.com/vuejs/rfcs/discussions/369
|
||||||
* @default true
|
* @default false
|
||||||
*/
|
*/
|
||||||
refTransform?: boolean
|
refTransform?: boolean
|
||||||
|
/**
|
||||||
|
* (Experimental) Enable syntax transform for destructuring from defineProps()
|
||||||
|
* https://github.com/vuejs/rfcs/discussions/394
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
propsDestructureTransform?: boolean
|
||||||
/**
|
/**
|
||||||
* @deprecated use `refTransform` instead.
|
* @deprecated use `refTransform` instead.
|
||||||
*/
|
*/
|
||||||
@ -131,6 +139,8 @@ export function compileScript(
|
|||||||
let { script, scriptSetup, source, filename } = sfc
|
let { script, scriptSetup, source, filename } = sfc
|
||||||
// feature flags
|
// feature flags
|
||||||
const enableRefTransform = !!options.refSugar || !!options.refTransform
|
const enableRefTransform = !!options.refSugar || !!options.refTransform
|
||||||
|
const enablePropsTransform = !!options.propsDestructureTransform
|
||||||
|
const isProd = !!options.isProd
|
||||||
const genSourceMap = options.sourceMap !== false
|
const genSourceMap = options.sourceMap !== false
|
||||||
let refBindings: string[] | undefined
|
let refBindings: string[] | undefined
|
||||||
|
|
||||||
@ -203,7 +213,7 @@ export function compileScript(
|
|||||||
cssVars,
|
cssVars,
|
||||||
bindings,
|
bindings,
|
||||||
scopeId,
|
scopeId,
|
||||||
!!options.isProd
|
isProd
|
||||||
)
|
)
|
||||||
content += `\nexport default __default__`
|
content += `\nexport default __default__`
|
||||||
}
|
}
|
||||||
@ -248,6 +258,8 @@ export function compileScript(
|
|||||||
let hasDefineExposeCall = false
|
let hasDefineExposeCall = false
|
||||||
let propsRuntimeDecl: Node | undefined
|
let propsRuntimeDecl: Node | undefined
|
||||||
let propsRuntimeDefaults: ObjectExpression | undefined
|
let propsRuntimeDefaults: ObjectExpression | undefined
|
||||||
|
let propsDestructureDecl: Node | undefined
|
||||||
|
let propsDestructureRestId: string | undefined
|
||||||
let propsTypeDecl: TSTypeLiteral | TSInterfaceBody | undefined
|
let propsTypeDecl: TSTypeLiteral | TSInterfaceBody | undefined
|
||||||
let propsTypeDeclRaw: Node | undefined
|
let propsTypeDeclRaw: Node | undefined
|
||||||
let propsIdentifier: string | undefined
|
let propsIdentifier: string | undefined
|
||||||
@ -266,6 +278,14 @@ export function compileScript(
|
|||||||
const typeDeclaredEmits: Set<string> = new Set()
|
const typeDeclaredEmits: Set<string> = new Set()
|
||||||
// record declared types for runtime props type generation
|
// record declared types for runtime props type generation
|
||||||
const declaredTypes: Record<string, string[]> = {}
|
const declaredTypes: Record<string, string[]> = {}
|
||||||
|
// props destructure data
|
||||||
|
const propsDestructuredBindings: Record<
|
||||||
|
string, // public prop key
|
||||||
|
{
|
||||||
|
local: string // local identifier, may be different
|
||||||
|
default?: Expression
|
||||||
|
}
|
||||||
|
> = Object.create(null)
|
||||||
|
|
||||||
// magic-string state
|
// magic-string state
|
||||||
const s = new MagicString(source)
|
const s = new MagicString(source)
|
||||||
@ -337,7 +357,7 @@ export function compileScript(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processDefineProps(node: Node): boolean {
|
function processDefineProps(node: Node, declId?: LVal): boolean {
|
||||||
if (!isCallOf(node, DEFINE_PROPS)) {
|
if (!isCallOf(node, DEFINE_PROPS)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -374,14 +394,62 @@ export function compileScript(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (declId) {
|
||||||
|
if (enablePropsTransform && declId.type === 'ObjectPattern') {
|
||||||
|
propsDestructureDecl = declId
|
||||||
|
// props destructure - handle compilation sugar
|
||||||
|
for (const prop of declId.properties) {
|
||||||
|
if (prop.type === 'ObjectProperty') {
|
||||||
|
if (prop.computed) {
|
||||||
|
error(
|
||||||
|
`${DEFINE_PROPS}() destructure cannot use computed key.`,
|
||||||
|
prop.key
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const propKey = (prop.key as Identifier).name
|
||||||
|
if (prop.value.type === 'AssignmentPattern') {
|
||||||
|
// default value { foo = 123 }
|
||||||
|
const { left, right } = prop.value
|
||||||
|
if (left.type !== 'Identifier') {
|
||||||
|
error(
|
||||||
|
`${DEFINE_PROPS}() destructure does not support nested patterns.`,
|
||||||
|
left
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// store default value
|
||||||
|
propsDestructuredBindings[propKey] = {
|
||||||
|
local: left.name,
|
||||||
|
default: right
|
||||||
|
}
|
||||||
|
} else if (prop.value.type === 'Identifier') {
|
||||||
|
// simple destucture
|
||||||
|
propsDestructuredBindings[propKey] = {
|
||||||
|
local: prop.value.name
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error(
|
||||||
|
`${DEFINE_PROPS}() destructure does not support nested patterns.`,
|
||||||
|
prop.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// rest spread
|
||||||
|
propsDestructureRestId = (prop.argument as Identifier).name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
propsIdentifier = scriptSetup!.content.slice(declId.start!, declId.end!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
function processWithDefaults(node: Node): boolean {
|
function processWithDefaults(node: Node, declId?: LVal): boolean {
|
||||||
if (!isCallOf(node, WITH_DEFAULTS)) {
|
if (!isCallOf(node, WITH_DEFAULTS)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (processDefineProps(node.arguments[0])) {
|
if (processDefineProps(node.arguments[0], declId)) {
|
||||||
if (propsRuntimeDecl) {
|
if (propsRuntimeDecl) {
|
||||||
error(
|
error(
|
||||||
`${WITH_DEFAULTS} can only be used with type-based ` +
|
`${WITH_DEFAULTS} can only be used with type-based ` +
|
||||||
@ -389,6 +457,13 @@ export function compileScript(
|
|||||||
node
|
node
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (propsDestructureDecl) {
|
||||||
|
error(
|
||||||
|
`${WITH_DEFAULTS}() is unnecessary when using destructure with ${DEFINE_PROPS}().\n` +
|
||||||
|
`Prefer using destructure default values, e.g. const { foo = 1 } = defineProps(...).`,
|
||||||
|
node.callee
|
||||||
|
)
|
||||||
|
}
|
||||||
propsRuntimeDefaults = node.arguments[1] as ObjectExpression
|
propsRuntimeDefaults = node.arguments[1] as ObjectExpression
|
||||||
if (
|
if (
|
||||||
!propsRuntimeDefaults ||
|
!propsRuntimeDefaults ||
|
||||||
@ -408,7 +483,7 @@ export function compileScript(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
function processDefineEmits(node: Node): boolean {
|
function processDefineEmits(node: Node, declId?: LVal): boolean {
|
||||||
if (!isCallOf(node, DEFINE_EMITS)) {
|
if (!isCallOf(node, DEFINE_EMITS)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -440,6 +515,11 @@ export function compileScript(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (declId) {
|
||||||
|
emitIdentifier = scriptSetup!.content.slice(declId.start!, declId.end!)
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,7 +645,7 @@ export function compileScript(
|
|||||||
* static properties, we can directly generate more optimzied default
|
* static properties, we can directly generate more optimzied default
|
||||||
* declarations. Otherwise we will have to fallback to runtime merging.
|
* declarations. Otherwise we will have to fallback to runtime merging.
|
||||||
*/
|
*/
|
||||||
function checkStaticDefaults() {
|
function hasStaticWithDefaults() {
|
||||||
return (
|
return (
|
||||||
propsRuntimeDefaults &&
|
propsRuntimeDefaults &&
|
||||||
propsRuntimeDefaults.type === 'ObjectExpression' &&
|
propsRuntimeDefaults.type === 'ObjectExpression' &&
|
||||||
@ -582,13 +662,16 @@ export function compileScript(
|
|||||||
if (!keys.length) {
|
if (!keys.length) {
|
||||||
return ``
|
return ``
|
||||||
}
|
}
|
||||||
const hasStaticDefaults = checkStaticDefaults()
|
const hasStaticDefaults = hasStaticWithDefaults()
|
||||||
const scriptSetupSource = scriptSetup!.content
|
const scriptSetupSource = scriptSetup!.content
|
||||||
let propsDecls = `{
|
let propsDecls = `{
|
||||||
${keys
|
${keys
|
||||||
.map(key => {
|
.map(key => {
|
||||||
let defaultString: string | undefined
|
let defaultString: string | undefined
|
||||||
if (hasStaticDefaults) {
|
const destructured = genDestructuredDefaultValue(key)
|
||||||
|
if (destructured) {
|
||||||
|
defaultString = `default: ${destructured}`
|
||||||
|
} else if (hasStaticDefaults) {
|
||||||
const prop = propsRuntimeDefaults!.properties.find(
|
const prop = propsRuntimeDefaults!.properties.find(
|
||||||
(node: any) => node.key.name === key
|
(node: any) => node.key.name === key
|
||||||
) as ObjectProperty | ObjectMethod
|
) as ObjectProperty | ObjectMethod
|
||||||
@ -608,7 +691,7 @@ export function compileScript(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__DEV__) {
|
if (!isProd) {
|
||||||
const { type, required } = props[key]
|
const { type, required } = props[key]
|
||||||
return `${key}: { type: ${toRuntimeTypeString(
|
return `${key}: { type: ${toRuntimeTypeString(
|
||||||
type
|
type
|
||||||
@ -632,9 +715,21 @@ export function compileScript(
|
|||||||
return `\n props: ${propsDecls},`
|
return `\n props: ${propsDecls},`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function genDestructuredDefaultValue(key: string): string | undefined {
|
||||||
|
const destructured = propsDestructuredBindings[key]
|
||||||
|
if (destructured && destructured.default) {
|
||||||
|
const value = scriptSetup!.content.slice(
|
||||||
|
destructured.default.start!,
|
||||||
|
destructured.default.end!
|
||||||
|
)
|
||||||
|
const isLiteral = destructured.default.type.endsWith('Literal')
|
||||||
|
return isLiteral ? value : `() => ${value}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function genSetupPropsType(node: TSTypeLiteral | TSInterfaceBody) {
|
function genSetupPropsType(node: TSTypeLiteral | TSInterfaceBody) {
|
||||||
const scriptSetupSource = scriptSetup!.content
|
const scriptSetupSource = scriptSetup!.content
|
||||||
if (checkStaticDefaults()) {
|
if (hasStaticWithDefaults()) {
|
||||||
// if withDefaults() is used, we need to remove the optional flags
|
// if withDefaults() is used, we need to remove the optional flags
|
||||||
// on props that have default values
|
// on props that have default values
|
||||||
let res = `{ `
|
let res = `{ `
|
||||||
@ -754,7 +849,7 @@ export function compileScript(
|
|||||||
|
|
||||||
// apply ref transform
|
// apply ref transform
|
||||||
if (enableRefTransform && shouldTransformRef(script.content)) {
|
if (enableRefTransform && shouldTransformRef(script.content)) {
|
||||||
const { rootVars, importedHelpers } = transformRefAST(
|
const { rootRefs: rootVars, importedHelpers } = transformRefAST(
|
||||||
scriptAst,
|
scriptAst,
|
||||||
s,
|
s,
|
||||||
scriptStartOffset!
|
scriptStartOffset!
|
||||||
@ -900,20 +995,9 @@ export function compileScript(
|
|||||||
if (decl.init) {
|
if (decl.init) {
|
||||||
// defineProps / defineEmits
|
// defineProps / defineEmits
|
||||||
const isDefineProps =
|
const isDefineProps =
|
||||||
processDefineProps(decl.init) || processWithDefaults(decl.init)
|
processDefineProps(decl.init, decl.id) ||
|
||||||
if (isDefineProps) {
|
processWithDefaults(decl.init, decl.id)
|
||||||
propsIdentifier = scriptSetup.content.slice(
|
const isDefineEmits = processDefineEmits(decl.init, decl.id)
|
||||||
decl.id.start!,
|
|
||||||
decl.id.end!
|
|
||||||
)
|
|
||||||
}
|
|
||||||
const isDefineEmits = processDefineEmits(decl.init)
|
|
||||||
if (isDefineEmits) {
|
|
||||||
emitIdentifier = scriptSetup.content.slice(
|
|
||||||
decl.id.start!,
|
|
||||||
decl.id.end!
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (isDefineProps || isDefineEmits) {
|
if (isDefineProps || isDefineEmits) {
|
||||||
if (left === 1) {
|
if (left === 1) {
|
||||||
s.remove(node.start! + startOffset, node.end! + startOffset)
|
s.remove(node.start! + startOffset, node.end! + startOffset)
|
||||||
@ -1004,14 +1088,19 @@ export function compileScript(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. Apply ref sugar transform
|
// 3. Apply ref sugar transform
|
||||||
if (enableRefTransform && shouldTransformRef(scriptSetup.content)) {
|
if (
|
||||||
const { rootVars, importedHelpers } = transformRefAST(
|
(enableRefTransform && shouldTransformRef(scriptSetup.content)) ||
|
||||||
|
propsDestructureDecl
|
||||||
|
) {
|
||||||
|
const { rootRefs, importedHelpers } = transformRefAST(
|
||||||
scriptSetupAst,
|
scriptSetupAst,
|
||||||
s,
|
s,
|
||||||
startOffset,
|
startOffset,
|
||||||
refBindings
|
refBindings,
|
||||||
|
propsDestructuredBindings,
|
||||||
|
!enableRefTransform
|
||||||
)
|
)
|
||||||
refBindings = refBindings ? [...refBindings, ...rootVars] : rootVars
|
refBindings = refBindings ? [...refBindings, ...rootRefs] : rootRefs
|
||||||
for (const h of importedHelpers) {
|
for (const h of importedHelpers) {
|
||||||
helperImports.add(h)
|
helperImports.add(h)
|
||||||
}
|
}
|
||||||
@ -1019,7 +1108,7 @@ export function compileScript(
|
|||||||
|
|
||||||
// 4. extract runtime props/emits code from setup context type
|
// 4. extract runtime props/emits code from setup context type
|
||||||
if (propsTypeDecl) {
|
if (propsTypeDecl) {
|
||||||
extractRuntimeProps(propsTypeDecl, typeDeclaredProps, declaredTypes)
|
extractRuntimeProps(propsTypeDecl, typeDeclaredProps, declaredTypes, isProd)
|
||||||
}
|
}
|
||||||
if (emitsTypeDecl) {
|
if (emitsTypeDecl) {
|
||||||
extractRuntimeEmits(emitsTypeDecl, typeDeclaredEmits)
|
extractRuntimeEmits(emitsTypeDecl, typeDeclaredEmits)
|
||||||
@ -1029,6 +1118,7 @@ export function compileScript(
|
|||||||
// variables
|
// variables
|
||||||
checkInvalidScopeReference(propsRuntimeDecl, DEFINE_PROPS)
|
checkInvalidScopeReference(propsRuntimeDecl, DEFINE_PROPS)
|
||||||
checkInvalidScopeReference(propsRuntimeDefaults, DEFINE_PROPS)
|
checkInvalidScopeReference(propsRuntimeDefaults, DEFINE_PROPS)
|
||||||
|
checkInvalidScopeReference(propsDestructureDecl, DEFINE_PROPS)
|
||||||
checkInvalidScopeReference(emitsRuntimeDecl, DEFINE_PROPS)
|
checkInvalidScopeReference(emitsRuntimeDecl, DEFINE_PROPS)
|
||||||
|
|
||||||
// 6. remove non-script content
|
// 6. remove non-script content
|
||||||
@ -1062,6 +1152,20 @@ export function compileScript(
|
|||||||
for (const key in typeDeclaredProps) {
|
for (const key in typeDeclaredProps) {
|
||||||
bindingMetadata[key] = BindingTypes.PROPS
|
bindingMetadata[key] = BindingTypes.PROPS
|
||||||
}
|
}
|
||||||
|
// props aliases
|
||||||
|
if (propsDestructureDecl) {
|
||||||
|
if (propsDestructureRestId) {
|
||||||
|
bindingMetadata[propsDestructureRestId] = BindingTypes.SETUP_CONST
|
||||||
|
}
|
||||||
|
for (const key in propsDestructuredBindings) {
|
||||||
|
const { local } = propsDestructuredBindings[key]
|
||||||
|
if (local !== key) {
|
||||||
|
bindingMetadata[local] = BindingTypes.PROPS_ALIASED
|
||||||
|
;(bindingMetadata.__propsAliases ||
|
||||||
|
(bindingMetadata.__propsAliases = {}))[local] = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for (const [key, { isType, imported, source }] of Object.entries(
|
for (const [key, { isType, imported, source }] of Object.entries(
|
||||||
userImports
|
userImports
|
||||||
)) {
|
)) {
|
||||||
@ -1090,12 +1194,7 @@ export function compileScript(
|
|||||||
helperImports.add('unref')
|
helperImports.add('unref')
|
||||||
s.prependRight(
|
s.prependRight(
|
||||||
startOffset,
|
startOffset,
|
||||||
`\n${genCssVarsCode(
|
`\n${genCssVarsCode(cssVars, bindingMetadata, scopeId, isProd)}\n`
|
||||||
cssVars,
|
|
||||||
bindingMetadata,
|
|
||||||
scopeId,
|
|
||||||
!!options.isProd
|
|
||||||
)}\n`
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1118,6 +1217,14 @@ export function compileScript(
|
|||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (propsDestructureRestId) {
|
||||||
|
s.prependRight(
|
||||||
|
startOffset,
|
||||||
|
`\nconst ${propsDestructureRestId} = ${helper(
|
||||||
|
`createPropsRestProxy`
|
||||||
|
)}(__props, ${JSON.stringify(Object.keys(propsDestructuredBindings))})`
|
||||||
|
)
|
||||||
|
}
|
||||||
// inject temp variables for async context preservation
|
// inject temp variables for async context preservation
|
||||||
if (hasAwait) {
|
if (hasAwait) {
|
||||||
const any = isTS ? `: any` : ``
|
const any = isTS ? `: any` : ``
|
||||||
@ -1235,9 +1342,22 @@ export function compileScript(
|
|||||||
runtimeOptions += `\n __ssrInlineRender: true,`
|
runtimeOptions += `\n __ssrInlineRender: true,`
|
||||||
}
|
}
|
||||||
if (propsRuntimeDecl) {
|
if (propsRuntimeDecl) {
|
||||||
runtimeOptions += `\n props: ${scriptSetup.content
|
let declCode = scriptSetup.content
|
||||||
.slice(propsRuntimeDecl.start!, propsRuntimeDecl.end!)
|
.slice(propsRuntimeDecl.start!, propsRuntimeDecl.end!)
|
||||||
.trim()},`
|
.trim()
|
||||||
|
if (propsDestructureDecl) {
|
||||||
|
const defaults: string[] = []
|
||||||
|
for (const key in propsDestructuredBindings) {
|
||||||
|
const d = genDestructuredDefaultValue(key)
|
||||||
|
if (d) defaults.push(`${key}: ${d}`)
|
||||||
|
}
|
||||||
|
if (defaults.length) {
|
||||||
|
declCode = `${helper(
|
||||||
|
`mergeDefaults`
|
||||||
|
)}(${declCode}, {\n ${defaults.join(',\n ')}\n})`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runtimeOptions += `\n props: ${declCode},`
|
||||||
} else if (propsTypeDecl) {
|
} else if (propsTypeDecl) {
|
||||||
runtimeOptions += genRuntimeProps(typeDeclaredProps)
|
runtimeOptions += genRuntimeProps(typeDeclaredProps)
|
||||||
}
|
}
|
||||||
@ -1313,6 +1433,7 @@ export function compileScript(
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.trim()
|
s.trim()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...scriptSetup,
|
...scriptSetup,
|
||||||
bindings: bindingMetadata,
|
bindings: bindingMetadata,
|
||||||
@ -1376,12 +1497,18 @@ function walkDeclaration(
|
|||||||
bindingType = BindingTypes.SETUP_LET
|
bindingType = BindingTypes.SETUP_LET
|
||||||
}
|
}
|
||||||
registerBinding(bindings, id, bindingType)
|
registerBinding(bindings, id, bindingType)
|
||||||
} else if (id.type === 'ObjectPattern') {
|
} else {
|
||||||
|
if (isCallOf(init, DEFINE_PROPS)) {
|
||||||
|
// skip walking props destructure
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (id.type === 'ObjectPattern') {
|
||||||
walkObjectPattern(id, bindings, isConst, isDefineCall)
|
walkObjectPattern(id, bindings, isConst, isDefineCall)
|
||||||
} else if (id.type === 'ArrayPattern') {
|
} else if (id.type === 'ArrayPattern') {
|
||||||
walkArrayPattern(id, bindings, isConst, isDefineCall)
|
walkArrayPattern(id, bindings, isConst, isDefineCall)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if (
|
} else if (
|
||||||
node.type === 'FunctionDeclaration' ||
|
node.type === 'FunctionDeclaration' ||
|
||||||
node.type === 'ClassDeclaration'
|
node.type === 'ClassDeclaration'
|
||||||
@ -1488,7 +1615,8 @@ function recordType(node: Node, declaredTypes: Record<string, string[]>) {
|
|||||||
function extractRuntimeProps(
|
function extractRuntimeProps(
|
||||||
node: TSTypeLiteral | TSInterfaceBody,
|
node: TSTypeLiteral | TSInterfaceBody,
|
||||||
props: Record<string, PropTypeData>,
|
props: Record<string, PropTypeData>,
|
||||||
declaredTypes: Record<string, string[]>
|
declaredTypes: Record<string, string[]>,
|
||||||
|
isProd: boolean
|
||||||
) {
|
) {
|
||||||
const members = node.type === 'TSTypeLiteral' ? node.members : node.body
|
const members = node.type === 'TSTypeLiteral' ? node.members : node.body
|
||||||
for (const m of members) {
|
for (const m of members) {
|
||||||
@ -1497,7 +1625,7 @@ function extractRuntimeProps(
|
|||||||
m.key.type === 'Identifier'
|
m.key.type === 'Identifier'
|
||||||
) {
|
) {
|
||||||
let type
|
let type
|
||||||
if (__DEV__) {
|
if (!isProd) {
|
||||||
if (m.type === 'TSMethodSignature') {
|
if (m.type === 'TSMethodSignature') {
|
||||||
type = ['Function']
|
type = ['Function']
|
||||||
} else if (m.typeAnnotation) {
|
} else if (m.typeAnnotation) {
|
||||||
|
@ -64,7 +64,9 @@ const {
|
|||||||
// @babel/parser plugins to enable.
|
// @babel/parser plugins to enable.
|
||||||
// 'typescript' and 'jsx' will be auto-inferred from filename if provided,
|
// 'typescript' and 'jsx' will be auto-inferred from filename if provided,
|
||||||
// so in most cases explicit parserPlugins are not necessary
|
// so in most cases explicit parserPlugins are not necessary
|
||||||
parserPlugins: [/* ... */]
|
parserPlugins: [
|
||||||
|
/* ... */
|
||||||
|
]
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -93,7 +95,7 @@ const ast = parse(src, { sourceType: 'module' })
|
|||||||
const s = new MagicString(src)
|
const s = new MagicString(src)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
rootVars, // ['a']
|
rootRefs, // ['a']
|
||||||
importedHelpers // ['ref']
|
importedHelpers // ['ref']
|
||||||
} = transformAST(ast, s)
|
} = transformAST(ast, s)
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ function assertCode(code: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test('$ unwrapping', () => {
|
test('$ unwrapping', () => {
|
||||||
const { code, rootVars } = transform(`
|
const { code, rootRefs } = transform(`
|
||||||
import { ref, shallowRef } from 'vue'
|
import { ref, shallowRef } from 'vue'
|
||||||
let foo = $(ref())
|
let foo = $(ref())
|
||||||
let a = $(ref(1))
|
let a = $(ref(1))
|
||||||
@ -40,12 +40,12 @@ test('$ unwrapping', () => {
|
|||||||
// normal declarations left untouched
|
// normal declarations left untouched
|
||||||
expect(code).toMatch(`let c = () => {}`)
|
expect(code).toMatch(`let c = () => {}`)
|
||||||
expect(code).toMatch(`let d`)
|
expect(code).toMatch(`let d`)
|
||||||
expect(rootVars).toStrictEqual(['foo', 'a', 'b'])
|
expect(rootRefs).toStrictEqual(['foo', 'a', 'b'])
|
||||||
assertCode(code)
|
assertCode(code)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('$ref & $shallowRef declarations', () => {
|
test('$ref & $shallowRef declarations', () => {
|
||||||
const { code, rootVars, importedHelpers } = transform(`
|
const { code, rootRefs, importedHelpers } = transform(`
|
||||||
let foo = $ref()
|
let foo = $ref()
|
||||||
let a = $ref(1)
|
let a = $ref(1)
|
||||||
let b = $shallowRef({
|
let b = $shallowRef({
|
||||||
@ -70,13 +70,13 @@ test('$ref & $shallowRef declarations', () => {
|
|||||||
// normal declarations left untouched
|
// normal declarations left untouched
|
||||||
expect(code).toMatch(`let c = () => {}`)
|
expect(code).toMatch(`let c = () => {}`)
|
||||||
expect(code).toMatch(`let d`)
|
expect(code).toMatch(`let d`)
|
||||||
expect(rootVars).toStrictEqual(['foo', 'a', 'b'])
|
expect(rootRefs).toStrictEqual(['foo', 'a', 'b'])
|
||||||
expect(importedHelpers).toStrictEqual(['ref', 'shallowRef'])
|
expect(importedHelpers).toStrictEqual(['ref', 'shallowRef'])
|
||||||
assertCode(code)
|
assertCode(code)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('multi $ref declarations', () => {
|
test('multi $ref declarations', () => {
|
||||||
const { code, rootVars, importedHelpers } = transform(`
|
const { code, rootRefs, importedHelpers } = transform(`
|
||||||
let a = $ref(1), b = $ref(2), c = $ref({
|
let a = $ref(1), b = $ref(2), c = $ref({
|
||||||
count: 0
|
count: 0
|
||||||
})
|
})
|
||||||
@ -86,31 +86,31 @@ test('multi $ref declarations', () => {
|
|||||||
count: 0
|
count: 0
|
||||||
})
|
})
|
||||||
`)
|
`)
|
||||||
expect(rootVars).toStrictEqual(['a', 'b', 'c'])
|
expect(rootRefs).toStrictEqual(['a', 'b', 'c'])
|
||||||
expect(importedHelpers).toStrictEqual(['ref'])
|
expect(importedHelpers).toStrictEqual(['ref'])
|
||||||
assertCode(code)
|
assertCode(code)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('$computed declaration', () => {
|
test('$computed declaration', () => {
|
||||||
const { code, rootVars, importedHelpers } = transform(`
|
const { code, rootRefs, importedHelpers } = transform(`
|
||||||
let a = $computed(() => 1)
|
let a = $computed(() => 1)
|
||||||
`)
|
`)
|
||||||
expect(code).toMatch(`
|
expect(code).toMatch(`
|
||||||
let a = _computed(() => 1)
|
let a = _computed(() => 1)
|
||||||
`)
|
`)
|
||||||
expect(rootVars).toStrictEqual(['a'])
|
expect(rootRefs).toStrictEqual(['a'])
|
||||||
expect(importedHelpers).toStrictEqual(['computed'])
|
expect(importedHelpers).toStrictEqual(['computed'])
|
||||||
assertCode(code)
|
assertCode(code)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('mixing $ref & $computed declarations', () => {
|
test('mixing $ref & $computed declarations', () => {
|
||||||
const { code, rootVars, importedHelpers } = transform(`
|
const { code, rootRefs, importedHelpers } = transform(`
|
||||||
let a = $ref(1), b = $computed(() => a + 1)
|
let a = $ref(1), b = $computed(() => a + 1)
|
||||||
`)
|
`)
|
||||||
expect(code).toMatch(`
|
expect(code).toMatch(`
|
||||||
let a = _ref(1), b = _computed(() => a.value + 1)
|
let a = _ref(1), b = _computed(() => a.value + 1)
|
||||||
`)
|
`)
|
||||||
expect(rootVars).toStrictEqual(['a', 'b'])
|
expect(rootRefs).toStrictEqual(['a', 'b'])
|
||||||
expect(importedHelpers).toStrictEqual(['ref', 'computed'])
|
expect(importedHelpers).toStrictEqual(['ref', 'computed'])
|
||||||
assertCode(code)
|
assertCode(code)
|
||||||
})
|
})
|
||||||
@ -201,7 +201,7 @@ test('should not rewrite scope variable', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('object destructure', () => {
|
test('object destructure', () => {
|
||||||
const { code, rootVars } = transform(`
|
const { code, rootRefs } = transform(`
|
||||||
let n = $ref(1), { a, b: c, d = 1, e: f = 2, ...g } = $(useFoo())
|
let n = $ref(1), { a, b: c, d = 1, e: f = 2, ...g } = $(useFoo())
|
||||||
let { foo } = $(useSomthing(() => 1));
|
let { foo } = $(useSomthing(() => 1));
|
||||||
console.log(n, a, c, d, f, g, foo)
|
console.log(n, a, c, d, f, g, foo)
|
||||||
@ -221,12 +221,12 @@ test('object destructure', () => {
|
|||||||
expect(code).toMatch(
|
expect(code).toMatch(
|
||||||
`console.log(n.value, a.value, c.value, d.value, f.value, g.value, foo.value)`
|
`console.log(n.value, a.value, c.value, d.value, f.value, g.value, foo.value)`
|
||||||
)
|
)
|
||||||
expect(rootVars).toStrictEqual(['n', 'a', 'c', 'd', 'f', 'g', 'foo'])
|
expect(rootRefs).toStrictEqual(['n', 'a', 'c', 'd', 'f', 'g', 'foo'])
|
||||||
assertCode(code)
|
assertCode(code)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('array destructure', () => {
|
test('array destructure', () => {
|
||||||
const { code, rootVars } = transform(`
|
const { code, rootRefs } = transform(`
|
||||||
let n = $ref(1), [a, b = 1, ...c] = $(useFoo())
|
let n = $ref(1), [a, b = 1, ...c] = $(useFoo())
|
||||||
console.log(n, a, b, c)
|
console.log(n, a, b, c)
|
||||||
`)
|
`)
|
||||||
@ -235,12 +235,12 @@ test('array destructure', () => {
|
|||||||
expect(code).toMatch(`\nconst b = _shallowRef(__b);`)
|
expect(code).toMatch(`\nconst b = _shallowRef(__b);`)
|
||||||
expect(code).toMatch(`\nconst c = _shallowRef(__c);`)
|
expect(code).toMatch(`\nconst c = _shallowRef(__c);`)
|
||||||
expect(code).toMatch(`console.log(n.value, a.value, b.value, c.value)`)
|
expect(code).toMatch(`console.log(n.value, a.value, b.value, c.value)`)
|
||||||
expect(rootVars).toStrictEqual(['n', 'a', 'b', 'c'])
|
expect(rootRefs).toStrictEqual(['n', 'a', 'b', 'c'])
|
||||||
assertCode(code)
|
assertCode(code)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('nested destructure', () => {
|
test('nested destructure', () => {
|
||||||
const { code, rootVars } = transform(`
|
const { code, rootRefs } = transform(`
|
||||||
let [{ a: { b }}] = $(useFoo())
|
let [{ a: { b }}] = $(useFoo())
|
||||||
let { c: [d, e] } = $(useBar())
|
let { c: [d, e] } = $(useBar())
|
||||||
console.log(b, d, e)
|
console.log(b, d, e)
|
||||||
@ -252,7 +252,7 @@ test('nested destructure', () => {
|
|||||||
expect(code).toMatch(`\nconst b = _shallowRef(__b);`)
|
expect(code).toMatch(`\nconst b = _shallowRef(__b);`)
|
||||||
expect(code).toMatch(`\nconst d = _shallowRef(__d);`)
|
expect(code).toMatch(`\nconst d = _shallowRef(__d);`)
|
||||||
expect(code).toMatch(`\nconst e = _shallowRef(__e);`)
|
expect(code).toMatch(`\nconst e = _shallowRef(__e);`)
|
||||||
expect(rootVars).toStrictEqual(['b', 'd', 'e'])
|
expect(rootRefs).toStrictEqual(['b', 'd', 'e'])
|
||||||
assertCode(code)
|
assertCode(code)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -270,7 +270,7 @@ test('$$', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('nested scopes', () => {
|
test('nested scopes', () => {
|
||||||
const { code, rootVars } = transform(`
|
const { code, rootRefs } = transform(`
|
||||||
let a = $ref(0)
|
let a = $ref(0)
|
||||||
let b = $ref(0)
|
let b = $ref(0)
|
||||||
let c = 0
|
let c = 0
|
||||||
@ -303,7 +303,7 @@ test('nested scopes', () => {
|
|||||||
return $$({ a, b, c, d })
|
return $$({ a, b, c, d })
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
expect(rootVars).toStrictEqual(['a', 'b', 'bar'])
|
expect(rootRefs).toStrictEqual(['a', 'b', 'bar'])
|
||||||
|
|
||||||
expect(code).toMatch('a.value++ // outer a')
|
expect(code).toMatch('a.value++ // outer a')
|
||||||
expect(code).toMatch('b.value++ // outer b')
|
expect(code).toMatch('b.value++ // outer b')
|
||||||
|
@ -31,7 +31,7 @@ export function shouldTransform(src: string): boolean {
|
|||||||
return transformCheckRE.test(src)
|
return transformCheckRE.test(src)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Scope = Record<string, boolean>
|
type Scope = Record<string, boolean | 'prop'>
|
||||||
|
|
||||||
export interface RefTransformOptions {
|
export interface RefTransformOptions {
|
||||||
filename?: string
|
filename?: string
|
||||||
@ -43,7 +43,7 @@ export interface RefTransformOptions {
|
|||||||
export interface RefTransformResults {
|
export interface RefTransformResults {
|
||||||
code: string
|
code: string
|
||||||
map: SourceMap | null
|
map: SourceMap | null
|
||||||
rootVars: string[]
|
rootRefs: string[]
|
||||||
importedHelpers: string[]
|
importedHelpers: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,13 +99,23 @@ export function transformAST(
|
|||||||
ast: Program,
|
ast: Program,
|
||||||
s: MagicString,
|
s: MagicString,
|
||||||
offset = 0,
|
offset = 0,
|
||||||
knownRootVars?: string[]
|
knownRefs?: string[],
|
||||||
|
knownProps?: Record<
|
||||||
|
string, // public prop key
|
||||||
|
{
|
||||||
|
local: string // local identifier, may be different
|
||||||
|
default?: any
|
||||||
|
}
|
||||||
|
>,
|
||||||
|
rewritePropsOnly = false
|
||||||
): {
|
): {
|
||||||
rootVars: string[]
|
rootRefs: string[]
|
||||||
importedHelpers: string[]
|
importedHelpers: string[]
|
||||||
} {
|
} {
|
||||||
// TODO remove when out of experimental
|
// TODO remove when out of experimental
|
||||||
|
if (!rewritePropsOnly) {
|
||||||
warnExperimental()
|
warnExperimental()
|
||||||
|
}
|
||||||
|
|
||||||
const importedHelpers = new Set<string>()
|
const importedHelpers = new Set<string>()
|
||||||
const rootScope: Scope = {}
|
const rootScope: Scope = {}
|
||||||
@ -113,14 +123,23 @@ export function transformAST(
|
|||||||
let currentScope: Scope = rootScope
|
let currentScope: Scope = rootScope
|
||||||
const excludedIds = new WeakSet<Identifier>()
|
const excludedIds = new WeakSet<Identifier>()
|
||||||
const parentStack: Node[] = []
|
const parentStack: Node[] = []
|
||||||
|
const propsLocalToPublicMap = Object.create(null)
|
||||||
|
|
||||||
if (knownRootVars) {
|
if (knownRefs) {
|
||||||
for (const key of knownRootVars) {
|
for (const key of knownRefs) {
|
||||||
rootScope[key] = true
|
rootScope[key] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (knownProps) {
|
||||||
|
for (const key in knownProps) {
|
||||||
|
const { local } = knownProps[key]
|
||||||
|
rootScope[local] = 'prop'
|
||||||
|
propsLocalToPublicMap[local] = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function error(msg: string, node: Node) {
|
function error(msg: string, node: Node) {
|
||||||
|
if (rewritePropsOnly) return
|
||||||
const e = new Error(msg)
|
const e = new Error(msg)
|
||||||
;(e as any).node = node
|
;(e as any).node = node
|
||||||
throw e
|
throw e
|
||||||
@ -145,17 +164,19 @@ export function transformAST(
|
|||||||
|
|
||||||
const registerRefBinding = (id: Identifier) => registerBinding(id, true)
|
const registerRefBinding = (id: Identifier) => registerBinding(id, true)
|
||||||
|
|
||||||
function walkScope(node: Program | BlockStatement) {
|
function walkScope(node: Program | BlockStatement, isRoot = false) {
|
||||||
for (const stmt of node.body) {
|
for (const stmt of node.body) {
|
||||||
if (stmt.type === 'VariableDeclaration') {
|
if (stmt.type === 'VariableDeclaration') {
|
||||||
if (stmt.declare) continue
|
if (stmt.declare) continue
|
||||||
for (const decl of stmt.declarations) {
|
for (const decl of stmt.declarations) {
|
||||||
let toVarCall
|
let toVarCall
|
||||||
if (
|
const isCall =
|
||||||
decl.init &&
|
decl.init &&
|
||||||
decl.init.type === 'CallExpression' &&
|
decl.init.type === 'CallExpression' &&
|
||||||
decl.init.callee.type === 'Identifier' &&
|
decl.init.callee.type === 'Identifier'
|
||||||
(toVarCall = isToVarCall(decl.init.callee.name))
|
if (
|
||||||
|
isCall &&
|
||||||
|
(toVarCall = isToVarCall((decl as any).init.callee.name))
|
||||||
) {
|
) {
|
||||||
processRefDeclaration(
|
processRefDeclaration(
|
||||||
toVarCall,
|
toVarCall,
|
||||||
@ -164,11 +185,21 @@ export function transformAST(
|
|||||||
stmt
|
stmt
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
const isProps =
|
||||||
|
isRoot &&
|
||||||
|
isCall &&
|
||||||
|
(decl as any).init.callee.name === 'defineProps'
|
||||||
for (const id of extractIdentifiers(decl.id)) {
|
for (const id of extractIdentifiers(decl.id)) {
|
||||||
|
if (isProps) {
|
||||||
|
// for defineProps destructure, only exclude them since they
|
||||||
|
// are already passed in as knownProps
|
||||||
|
excludedIds.add(id)
|
||||||
|
} else {
|
||||||
registerBinding(id)
|
registerBinding(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else if (
|
} else if (
|
||||||
stmt.type === 'FunctionDeclaration' ||
|
stmt.type === 'FunctionDeclaration' ||
|
||||||
stmt.type === 'ClassDeclaration'
|
stmt.type === 'ClassDeclaration'
|
||||||
@ -303,35 +334,57 @@ export function transformAST(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkRefId(
|
function rewriteId(
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
id: Identifier,
|
id: Identifier,
|
||||||
parent: Node,
|
parent: Node,
|
||||||
parentStack: Node[]
|
parentStack: Node[]
|
||||||
): boolean {
|
): boolean {
|
||||||
if (hasOwn(scope, id.name)) {
|
if (hasOwn(scope, id.name)) {
|
||||||
if (scope[id.name]) {
|
const bindingType = scope[id.name]
|
||||||
|
if (bindingType) {
|
||||||
|
const isProp = bindingType === 'prop'
|
||||||
|
if (rewritePropsOnly && !isProp) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// ref
|
||||||
if (isStaticProperty(parent) && parent.shorthand) {
|
if (isStaticProperty(parent) && parent.shorthand) {
|
||||||
// let binding used in a property shorthand
|
// let binding used in a property shorthand
|
||||||
// { foo } -> { foo: foo.value }
|
// { foo } -> { foo: foo.value }
|
||||||
|
// { prop } -> { prop: __prop.prop }
|
||||||
// skip for destructure patterns
|
// skip for destructure patterns
|
||||||
if (
|
if (
|
||||||
!(parent as any).inPattern ||
|
!(parent as any).inPattern ||
|
||||||
isInDestructureAssignment(parent, parentStack)
|
isInDestructureAssignment(parent, parentStack)
|
||||||
) {
|
) {
|
||||||
|
if (isProp) {
|
||||||
|
s.appendLeft(
|
||||||
|
id.end! + offset,
|
||||||
|
`: __props.${propsLocalToPublicMap[id.name]}`
|
||||||
|
)
|
||||||
|
} else {
|
||||||
s.appendLeft(id.end! + offset, `: ${id.name}.value`)
|
s.appendLeft(id.end! + offset, `: ${id.name}.value`)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isProp) {
|
||||||
|
s.overwrite(
|
||||||
|
id.start! + offset,
|
||||||
|
id.end! + offset,
|
||||||
|
`__props.${propsLocalToPublicMap[id.name]}`
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
s.appendLeft(id.end! + offset, '.value')
|
s.appendLeft(id.end! + offset, '.value')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// check root scope first
|
// check root scope first
|
||||||
walkScope(ast)
|
walkScope(ast, true)
|
||||||
;(walk as any)(ast, {
|
;(walk as any)(ast, {
|
||||||
enter(node: Node, parent?: Node) {
|
enter(node: Node, parent?: Node) {
|
||||||
parent && parentStack.push(parent)
|
parent && parentStack.push(parent)
|
||||||
@ -371,7 +424,7 @@ export function transformAST(
|
|||||||
// walk up the scope chain to check if id should be appended .value
|
// walk up the scope chain to check if id should be appended .value
|
||||||
let i = scopeStack.length
|
let i = scopeStack.length
|
||||||
while (i--) {
|
while (i--) {
|
||||||
if (checkRefId(scopeStack[i], node, parent!, parentStack)) {
|
if (rewriteId(scopeStack[i], node, parent!, parentStack)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -424,7 +477,7 @@ export function transformAST(
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rootVars: Object.keys(rootScope).filter(key => rootScope[key]),
|
rootRefs: Object.keys(rootScope).filter(key => rootScope[key] === true),
|
||||||
importedHelpers: [...importedHelpers]
|
importedHelpers: [...importedHelpers]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,8 @@ import {
|
|||||||
SetupContext,
|
SetupContext,
|
||||||
Suspense,
|
Suspense,
|
||||||
computed,
|
computed,
|
||||||
ComputedRef
|
ComputedRef,
|
||||||
|
shallowReactive
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
import {
|
import {
|
||||||
defineEmits,
|
defineEmits,
|
||||||
@ -21,7 +22,8 @@ import {
|
|||||||
useAttrs,
|
useAttrs,
|
||||||
useSlots,
|
useSlots,
|
||||||
mergeDefaults,
|
mergeDefaults,
|
||||||
withAsyncContext
|
withAsyncContext,
|
||||||
|
createPropsRestProxy
|
||||||
} from '../src/apiSetupHelpers'
|
} from '../src/apiSetupHelpers'
|
||||||
|
|
||||||
describe('SFC <script setup> helpers', () => {
|
describe('SFC <script setup> helpers', () => {
|
||||||
@ -77,27 +79,63 @@ describe('SFC <script setup> helpers', () => {
|
|||||||
expect(attrs).toBe(ctx!.attrs)
|
expect(attrs).toBe(ctx!.attrs)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('mergeDefaults', () => {
|
describe('mergeDefaults', () => {
|
||||||
|
test('object syntax', () => {
|
||||||
const merged = mergeDefaults(
|
const merged = mergeDefaults(
|
||||||
{
|
{
|
||||||
foo: null,
|
foo: null,
|
||||||
bar: { type: String, required: false }
|
bar: { type: String, required: false },
|
||||||
|
baz: String
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
foo: 1,
|
foo: 1,
|
||||||
bar: 'baz'
|
bar: 'baz',
|
||||||
|
baz: 'qux'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
expect(merged).toMatchObject({
|
expect(merged).toMatchObject({
|
||||||
foo: { default: 1 },
|
foo: { default: 1 },
|
||||||
bar: { type: String, required: false, default: 'baz' }
|
bar: { type: String, required: false, default: 'baz' },
|
||||||
|
baz: { type: String, default: 'qux' }
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('array syntax', () => {
|
||||||
|
const merged = mergeDefaults(['foo', 'bar', 'baz'], {
|
||||||
|
foo: 1,
|
||||||
|
bar: 'baz',
|
||||||
|
baz: 'qux'
|
||||||
|
})
|
||||||
|
expect(merged).toMatchObject({
|
||||||
|
foo: { default: 1 },
|
||||||
|
bar: { default: 'baz' },
|
||||||
|
baz: { default: 'qux' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should warn missing', () => {
|
||||||
mergeDefaults({}, { foo: 1 })
|
mergeDefaults({}, { foo: 1 })
|
||||||
expect(
|
expect(
|
||||||
`props default key "foo" has no corresponding declaration`
|
`props default key "foo" has no corresponding declaration`
|
||||||
).toHaveBeenWarned()
|
).toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('createPropsRestProxy', () => {
|
||||||
|
const original = shallowReactive({
|
||||||
|
foo: 1,
|
||||||
|
bar: 2,
|
||||||
|
baz: 3
|
||||||
|
})
|
||||||
|
const rest = createPropsRestProxy(original, ['foo', 'bar'])
|
||||||
|
expect('foo' in rest).toBe(false)
|
||||||
|
expect('bar' in rest).toBe(false)
|
||||||
|
expect(rest.baz).toBe(3)
|
||||||
|
expect(Object.keys(rest)).toEqual(['baz'])
|
||||||
|
|
||||||
|
original.baz = 4
|
||||||
|
expect(rest.baz).toBe(4)
|
||||||
|
})
|
||||||
|
|
||||||
describe('withAsyncContext', () => {
|
describe('withAsyncContext', () => {
|
||||||
// disable options API because applyOptions() also resets currentInstance
|
// disable options API because applyOptions() also resets currentInstance
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { isPromise } from '../../shared/src'
|
import { ComponentPropsOptions } from '@vue/runtime-core'
|
||||||
|
import { isArray, isPromise, isFunction } from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
getCurrentInstance,
|
getCurrentInstance,
|
||||||
setCurrentInstance,
|
setCurrentInstance,
|
||||||
@ -7,11 +8,7 @@ import {
|
|||||||
unsetCurrentInstance
|
unsetCurrentInstance
|
||||||
} from './component'
|
} from './component'
|
||||||
import { EmitFn, EmitsOptions } from './componentEmits'
|
import { EmitFn, EmitsOptions } from './componentEmits'
|
||||||
import {
|
import { ComponentObjectPropsOptions, ExtractPropTypes } from './componentProps'
|
||||||
ComponentObjectPropsOptions,
|
|
||||||
PropOptions,
|
|
||||||
ExtractPropTypes
|
|
||||||
} from './componentProps'
|
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
|
|
||||||
// dev only
|
// dev only
|
||||||
@ -195,15 +192,24 @@ function getContext(): SetupContext {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export function mergeDefaults(
|
export function mergeDefaults(
|
||||||
// the base props is compiler-generated and guaranteed to be in this shape.
|
raw: ComponentPropsOptions,
|
||||||
props: Record<string, PropOptions | null>,
|
|
||||||
defaults: Record<string, any>
|
defaults: Record<string, any>
|
||||||
) {
|
): ComponentObjectPropsOptions {
|
||||||
|
const props = isArray(raw)
|
||||||
|
? raw.reduce(
|
||||||
|
(normalized, p) => ((normalized[p] = {}), normalized),
|
||||||
|
{} as ComponentObjectPropsOptions
|
||||||
|
)
|
||||||
|
: raw
|
||||||
for (const key in defaults) {
|
for (const key in defaults) {
|
||||||
const val = props[key]
|
const opt = props[key]
|
||||||
if (val) {
|
if (opt) {
|
||||||
val.default = defaults[key]
|
if (isArray(opt) || isFunction(opt)) {
|
||||||
} else if (val === null) {
|
props[key] = { type: opt, default: defaults[key] }
|
||||||
|
} else {
|
||||||
|
opt.default = defaults[key]
|
||||||
|
}
|
||||||
|
} else if (opt === null) {
|
||||||
props[key] = { default: defaults[key] }
|
props[key] = { default: defaults[key] }
|
||||||
} else if (__DEV__) {
|
} else if (__DEV__) {
|
||||||
warn(`props default key "${key}" has no corresponding declaration.`)
|
warn(`props default key "${key}" has no corresponding declaration.`)
|
||||||
@ -212,6 +218,27 @@ export function mergeDefaults(
|
|||||||
return props
|
return props
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to create a proxy for the rest element when destructuring props with
|
||||||
|
* defineProps().
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export function createPropsRestProxy(
|
||||||
|
props: any,
|
||||||
|
excludedKeys: string[]
|
||||||
|
): Record<string, any> {
|
||||||
|
const ret: Record<string, any> = {}
|
||||||
|
for (const key in props) {
|
||||||
|
if (!excludedKeys.includes(key)) {
|
||||||
|
Object.defineProperty(ret, key, {
|
||||||
|
enumerable: true,
|
||||||
|
get: () => props[key]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `<script setup>` helper for persisting the current instance context over
|
* `<script setup>` helper for persisting the current instance context over
|
||||||
* async/await flows.
|
* async/await flows.
|
||||||
|
@ -70,6 +70,7 @@ export {
|
|||||||
withDefaults,
|
withDefaults,
|
||||||
// internal
|
// internal
|
||||||
mergeDefaults,
|
mergeDefaults,
|
||||||
|
createPropsRestProxy,
|
||||||
withAsyncContext
|
withAsyncContext
|
||||||
} from './apiSetupHelpers'
|
} from './apiSetupHelpers'
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"vite": "^2.5.10"
|
"vite": "^2.5.10"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/repl": "^0.4.1",
|
"@vue/repl": "^0.4.2",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"jszip": "^3.6.0"
|
"jszip": "^3.6.0"
|
||||||
}
|
}
|
||||||
|
@ -16,13 +16,27 @@ const store = new ReplStore({
|
|||||||
: `${location.origin}/src/vue-dev-proxy`
|
: `${location.origin}/src/vue-dev-proxy`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// enable experimental features
|
||||||
|
const sfcOptions = {
|
||||||
|
script: {
|
||||||
|
refTransform: true,
|
||||||
|
propsDestructureTransform: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// persist state
|
// persist state
|
||||||
watchEffect(() => history.replaceState({}, '', store.serialize()))
|
watchEffect(() => history.replaceState({}, '', store.serialize()))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Header :store="store" />
|
<Header :store="store" />
|
||||||
<Repl :store="store" :showCompileOutput="true" :autoResize="true" />
|
<Repl
|
||||||
|
:store="store"
|
||||||
|
:showCompileOutput="true"
|
||||||
|
:autoResize="true"
|
||||||
|
:sfcOptions="sfcOptions"
|
||||||
|
:clearConsole="false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -920,10 +920,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@vue/consolidate/-/consolidate-0.17.3.tgz#9614d25a2eb263fa5df18ce98b0a576142e0ec83"
|
resolved "https://registry.yarnpkg.com/@vue/consolidate/-/consolidate-0.17.3.tgz#9614d25a2eb263fa5df18ce98b0a576142e0ec83"
|
||||||
integrity sha512-nl0SWcTMzaaTnJ5G6V8VlMDA1CVVrNnaQKF1aBZU3kXtjgU9jtHMsEAsgjoRUx+T0EVJk9TgbmxGhK3pOk22zw==
|
integrity sha512-nl0SWcTMzaaTnJ5G6V8VlMDA1CVVrNnaQKF1aBZU3kXtjgU9jtHMsEAsgjoRUx+T0EVJk9TgbmxGhK3pOk22zw==
|
||||||
|
|
||||||
"@vue/repl@^0.4.1":
|
"@vue/repl@^0.4.2":
|
||||||
version "0.4.1"
|
version "0.4.2"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/repl/-/repl-0.4.1.tgz#b2062bea2baa077520eb01b87df51fff357961be"
|
resolved "https://registry.yarnpkg.com/@vue/repl/-/repl-0.4.2.tgz#594d36061201195222bc91d187cd766d692f2046"
|
||||||
integrity sha512-Rq9q0MHRA0YRGBE2VSFL5ZkllqLK5HnFK9/+6Iu75M39BZAacwf808fHPTN0bhYGkJ63ur2i0sEYnnNzWzluPg==
|
integrity sha512-Spg+M7dENa0jfjEhb2odLP5uqy28fBAMmURL+vFQUIkxcvtFaMkKY3viklhFqacfQqD6YTZfnLWSXyB+F6r/eQ==
|
||||||
|
|
||||||
"@zeit/schemas@2.6.0":
|
"@zeit/schemas@2.6.0":
|
||||||
version "2.6.0"
|
version "2.6.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user