}"
`;
-exports[`SFC compile <script setup> defineContext() 1`] = `
-"export default {
- props: {
- foo: String
- },
- emit: ['a', 'b'],
- setup(__props, { props, emit }) {
-
-
-
-const bar = 1
-
-return { props, emit, bar }
-}
-
-}"
-`;
-
-exports[`SFC compile <script setup> errors should allow defineContext() referencing imported binding 1`] = `
+exports[`SFC compile <script setup> errors should allow useOptions() referencing imported binding 1`] = `
"import { bar } from './bar'
export default {
}"
`;
-exports[`SFC compile <script setup> errors should allow defineContext() referencing scope var 1`] = `
+exports[`SFC compile <script setup> errors should allow useOptions() referencing scope var 1`] = `
"export default {
props: {
foo: {
}"
`;
-exports[`SFC compile <script setup> with TypeScript defineContext w/ runtime options 1`] = `
+exports[`SFC compile <script setup> useOptions() 1`] = `
+"export default {
+ props: {
+ foo: String
+ },
+ emit: ['a', 'b'],
+ setup(__props, { props, emit }) {
+
+
+
+const bar = 1
+
+return { props, emit, bar }
+}
+
+}"
+`;
+
+exports[`SFC compile <script setup> with TypeScript hoist type declarations 1`] = `
+"import { defineComponent } from 'vue'
+export interface Foo {}
+ type Bar = {}
+
+export default defineComponent({
+ setup() {
+
+
+return { }
+}
+
+})"
+`;
+
+exports[`SFC compile <script setup> with TypeScript useOptions w/ runtime options 1`] = `
"import { defineComponent } from 'vue'
})"
`;
-exports[`SFC compile <script setup> with TypeScript defineContext w/ type / extract emits (union) 1`] = `
+exports[`SFC compile <script setup> with TypeScript useOptions w/ type / extract emits (union) 1`] = `
"import { Slots, defineComponent } from 'vue'
})"
`;
-exports[`SFC compile <script setup> with TypeScript defineContext w/ type / extract emits 1`] = `
+exports[`SFC compile <script setup> with TypeScript useOptions w/ type / extract emits 1`] = `
"import { Slots, defineComponent } from 'vue'
})"
`;
-exports[`SFC compile <script setup> with TypeScript defineContext w/ type / extract props 1`] = `
+exports[`SFC compile <script setup> with TypeScript useOptions w/ type / extract props 1`] = `
"import { defineComponent } from 'vue'
interface Test {}
-return { }
-}
-
-})"
-`;
-
-exports[`SFC compile <script setup> with TypeScript hoist type declarations 1`] = `
-"import { defineComponent } from 'vue'
-export interface Foo {}
- type Bar = {}
-
-export default defineComponent({
- setup() {
-
-
return { }
}
expect(content).toMatch('return { a, b, c, d, x }')
})
- test('defineContext()', () => {
+ test('useOptions()', () => {
const { content, bindings } = compile(`
<script setup>
-import { defineContext } from 'vue'
-const { props, emit } = defineContext({
+import { useOptions } from 'vue'
+const { props, emit } = useOptions({
props: {
foo: String
},
emit: 'const'
})
- // should remove defineContext import and call
- expect(content).not.toMatch('defineContext')
+ // should remove useOptions import and call
+ expect(content).not.toMatch('useOptions')
// should generate correct setup signature
expect(content).toMatch(`setup(__props, { props, emit }) {`)
// should include context options in default export
const { content } = compile(
`
<script setup>
- import { ref, defineContext } from 'vue'
+ import { ref, useOptions } from 'vue'
import Foo from './Foo.vue'
import other from './util'
const count = ref(0)
assertCode(content)
})
- test('defineContext w/ runtime options', () => {
+ test('useOptions w/ runtime options', () => {
const { content } = compile(`
<script setup lang="ts">
-import { defineContext } from 'vue'
-const { props, emit } = defineContext({
+import { useOptions } from 'vue'
+const { props, emit } = useOptions({
props: { foo: String },
emits: ['a', 'b']
})
setup(__props, { props, emit }) {`)
})
- test('defineContext w/ type / extract props', () => {
+ test('useOptions w/ type / extract props', () => {
const { content, bindings } = compile(`
<script setup lang="ts">
- import { defineContext } from 'vue'
+ import { useOptions } from 'vue'
interface Test {}
type Alias = number[]
- defineContext<{
+ useOptions<{
props: {
string: string
number: number
})
})
- test('defineContext w/ type / extract emits', () => {
+ test('useOptions w/ type / extract emits', () => {
const { content } = compile(`
<script setup lang="ts">
- import { defineContext } from 'vue'
- const { emit } = defineContext<{
+ import { useOptions } from 'vue'
+ const { emit } = useOptions<{
emit: (e: 'foo' | 'bar') => void
}>()
</script>
expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
})
- test('defineContext w/ type / extract emits (union)', () => {
+ test('useOptions w/ type / extract emits (union)', () => {
const { content } = compile(`
<script setup lang="ts">
- import { defineContext } from 'vue'
- const { emit } = defineContext<{
+ import { useOptions } from 'vue'
+ const { emit } = useOptions<{
emit: ((e: 'foo' | 'bar') => void) | ((e: 'baz', id: number) => void)
}>()
</script>
).toThrow(`ref: statements can only contain assignment expressions`)
})
- test('defineContext() w/ both type and non-type args', () => {
+ test('useOptions() w/ both type and non-type args', () => {
expect(() => {
compile(`<script setup lang="ts">
- import { defineContext } from 'vue'
- defineContext<{}>({})
+ import { useOptions } from 'vue'
+ useOptions<{}>({})
</script>`)
}).toThrow(`cannot accept both type and non-type arguments`)
})
- test('defineContext() referencing local var', () => {
+ test('useOptions() referencing local var', () => {
expect(() =>
compile(`<script setup>
- import { defineContext } from 'vue'
+ import { useOptions } from 'vue'
const bar = 1
- defineContext({
+ useOptions({
props: {
foo: {
default: () => bar
).toThrow(`cannot reference locally declared variables`)
})
- test('defineContext() referencing ref declarations', () => {
+ test('useOptions() referencing ref declarations', () => {
expect(() =>
compile(`<script setup>
- import { defineContext } from 'vue'
+ import { useOptions } from 'vue'
ref: bar = 1
- defineContext({
+ useOptions({
props: { bar }
})
</script>`)
).toThrow(`cannot reference locally declared variables`)
})
- test('should allow defineContext() referencing scope var', () => {
+ test('should allow useOptions() referencing scope var', () => {
assertCode(
compile(`<script setup>
- import { defineContext } from 'vue'
+ import { useOptions } from 'vue'
const bar = 1
- defineContext({
+ useOptions({
props: {
foo: {
default: bar => bar + 1
)
})
- test('should allow defineContext() referencing imported binding', () => {
+ test('should allow useOptions() referencing imported binding', () => {
assertCode(
compile(`<script setup>
- import { defineContext } from 'vue'
+ import { useOptions } from 'vue'
import { bar } from './bar'
- defineContext({
+ useOptions({
props: {
foo: {
default: () => bar
it('works for script setup', () => {
const { bindings } = compile(`
<script setup>
- import { defineContext } from 'vue'
- defineContext({
+ import { useOptions } from 'vue'
+ useOptions({
props: {
foo: String,
}
import { compileTemplate, SFCTemplateCompileOptions } from './compileTemplate'
import { BindingTypes } from 'packages/compiler-core/src/options'
-const CTX_FN_NAME = 'defineContext'
+const USE_OPTIONS = 'useOptions'
export interface SFCScriptCompileOptions {
/**
const refIdentifiers: Set<Identifier> = new Set()
const enableRefSugar = options.refSugar !== false
let defaultExport: Node | undefined
- let hasContextCall = false
- let setupContextExp: string | undefined
- let setupContextArg: ObjectExpression | undefined
- let setupContextType: TSTypeLiteral | undefined
+ let hasOptionsCall = false
+ let optionsExp: string | undefined
+ let optionsArg: ObjectExpression | undefined
+ let optionsType: TSTypeLiteral | undefined
let hasAwait = false
const s = new MagicString(source)
)
}
- function processContextCall(node: Node): boolean {
+ function processUseOptions(node: Node): boolean {
if (
node.type === 'CallExpression' &&
node.callee.type === 'Identifier' &&
- node.callee.name === CTX_FN_NAME
+ node.callee.name === USE_OPTIONS
) {
- if (hasContextCall) {
- error('duplicate defineContext() call', node)
+ if (hasOptionsCall) {
+ error(`duplicate ${USE_OPTIONS}() call`, node)
}
- hasContextCall = true
+ hasOptionsCall = true
const optsArg = node.arguments[0]
if (optsArg) {
if (optsArg.type === 'ObjectExpression') {
- setupContextArg = optsArg
+ optionsArg = optsArg
} else {
- error(`${CTX_FN_NAME}() argument must be an object literal.`, optsArg)
+ error(`${USE_OPTIONS}() argument must be an object literal.`, optsArg)
}
}
// context call has type parameters - infer runtime types from it
if (node.typeParameters) {
- if (setupContextArg) {
+ if (optionsArg) {
error(
- `${CTX_FN_NAME}() cannot accept both type and non-type arguments ` +
+ `${USE_OPTIONS}() cannot accept both type and non-type arguments ` +
`at the same time. Use one or the other.`,
node
)
}
const typeArg = node.typeParameters.params[0]
if (typeArg.type === 'TSTypeLiteral') {
- setupContextType = typeArg
+ optionsType = typeArg
} else {
error(
- `type argument passed to ${CTX_FN_NAME}() must be a literal type.`,
+ `type argument passed to ${USE_OPTIONS}() must be a literal type.`,
typeArg
)
}
specifier.imported.name
const source = node.source.value
const existing = userImports[local]
- if (source === 'vue' && imported === CTX_FN_NAME) {
+ if (source === 'vue' && imported === USE_OPTIONS) {
removed++
s.remove(
prev ? prev.end! + startOffset : specifier.start! + startOffset,
if (
node.type === 'ExpressionStatement' &&
- processContextCall(node.expression)
+ processUseOptions(node.expression)
) {
s.remove(node.start! + startOffset, node.end! + startOffset)
}
if (node.type === 'VariableDeclaration' && !node.declare) {
for (const decl of node.declarations) {
- if (decl.init && processContextCall(decl.init)) {
- setupContextExp = scriptSetup.content.slice(
- decl.id.start!,
- decl.id.end!
- )
+ if (decl.init && processUseOptions(decl.init)) {
+ optionsExp = scriptSetup.content.slice(decl.id.start!, decl.id.end!)
if (node.declarations.length === 1) {
s.remove(node.start! + startOffset, node.end! + startOffset)
} else {
}
// 5. extract runtime props/emits code from setup context type
- if (setupContextType) {
- for (const m of setupContextType.members) {
+ if (optionsType) {
+ for (const m of optionsType.members) {
if (m.type === 'TSPropertySignature' && m.key.type === 'Identifier') {
const typeNode = m.typeAnnotation!.typeAnnotation
const typeString = scriptSetup.content.slice(
}
}
- // 5. check useSetupContext args to make sure it doesn't reference setup scope
+ // 5. check useOptions args to make sure it doesn't reference setup scope
// variables
- if (setupContextArg) {
- walkIdentifiers(setupContextArg, id => {
+ if (optionsArg) {
+ walkIdentifiers(optionsArg, id => {
if (setupBindings[id.name]) {
error(
- `\`${CTX_FN_NAME}()\` in <script setup> cannot reference locally ` +
+ `\`${USE_OPTIONS}()\` in <script setup> cannot reference locally ` +
`declared variables because it will be hoisted outside of the ` +
`setup() function. If your component options requires initialization ` +
`in the module scope, use a separate normal <script> to export ` +
}
// 7. finalize setup argument signature.
- let args = setupContextExp ? `__props, ${setupContextExp}` : ``
- if (setupContextExp && setupContextType) {
+ let args = optionsExp ? `__props, ${optionsExp}` : ``
+ if (optionsExp && optionsType) {
if (slotsType === 'Slots') {
helperImports.add('Slots')
}
if (scriptAst) {
Object.assign(bindingMetadata, analyzeScriptBindings(scriptAst))
}
- if (setupContextType) {
+ if (optionsType) {
for (const key in typeDeclaredProps) {
bindingMetadata[key] = BindingTypes.PROPS
}
}
- if (setupContextArg) {
- Object.assign(bindingMetadata, analyzeBindingsFromOptions(setupContextArg))
+ if (optionsArg) {
+ Object.assign(bindingMetadata, analyzeBindingsFromOptions(optionsArg))
}
for (const [key, { source }] of Object.entries(userImports)) {
bindingMetadata[key] = source.endsWith('.vue')
// 12. finalize default export
let runtimeOptions = ``
- if (setupContextArg) {
+ if (optionsArg) {
runtimeOptions = `\n ${scriptSetup.content
- .slice(setupContextArg.start! + 1, setupContextArg.end! - 1)
+ .slice(optionsArg.start! + 1, optionsArg.end! - 1)
.trim()},`
- } else if (setupContextType) {
+ } else if (optionsType) {
runtimeOptions =
genRuntimeProps(typeDeclaredProps) + genRuntimeEmits(typeDeclaredEmits)
}
const isConst = node.kind === 'const'
// export const foo = ...
for (const { id, init } of node.declarations) {
- const isContextCall = !!(
+ const isUseOptionsCall = !!(
isConst &&
init &&
init.type === 'CallExpression' &&
init.callee.type === 'Identifier' &&
- init.callee.name === CTX_FN_NAME
+ init.callee.name === USE_OPTIONS
)
if (id.type === 'Identifier') {
bindings[id.name] =
// if a declaration is a const literal, we can mark it so that
// the generated render fn code doesn't need to unref() it
- isContextCall ||
+ isUseOptionsCall ||
(isConst &&
init!.type !== 'Identifier' && // const a = b
init!.type !== 'CallExpression' && // const a = ref()
? BindingTypes.CONST
: BindingTypes.SETUP
} else if (id.type === 'ObjectPattern') {
- walkObjectPattern(id, bindings, isConst, isContextCall)
+ walkObjectPattern(id, bindings, isConst, isUseOptionsCall)
} else if (id.type === 'ArrayPattern') {
- walkArrayPattern(id, bindings, isConst, isContextCall)
+ walkArrayPattern(id, bindings, isConst, isUseOptionsCall)
}
}
} else if (
node: ObjectPattern,
bindings: Record<string, BindingTypes>,
isConst: boolean,
- isContextCall = false
+ isUseOptionsCall = false
) {
for (const p of node.properties) {
if (p.type === 'ObjectProperty') {
if (p.key.type === 'Identifier') {
if (p.key === p.value) {
// const { x } = ...
- bindings[p.key.name] = isContextCall
+ bindings[p.key.name] = isUseOptionsCall
? BindingTypes.CONST
: BindingTypes.SETUP
} else {
- walkPattern(p.value, bindings, isConst, isContextCall)
+ walkPattern(p.value, bindings, isConst, isUseOptionsCall)
}
}
} else {
node: ArrayPattern,
bindings: Record<string, BindingTypes>,
isConst: boolean,
- isContextCall = false
+ isUseOptionsCall = false
) {
for (const e of node.elements) {
- e && walkPattern(e, bindings, isConst, isContextCall)
+ e && walkPattern(e, bindings, isConst, isUseOptionsCall)
}
}
node: Node,
bindings: Record<string, BindingTypes>,
isConst: boolean,
- isContextCall = false
+ isUseOptionsCall = false
) {
if (node.type === 'Identifier') {
- bindings[node.name] = isContextCall
+ bindings[node.name] = isUseOptionsCall
? BindingTypes.CONST
: BindingTypes.SETUP
} else if (node.type === 'RestElement') {
walkArrayPattern(node, bindings, isConst)
} else if (node.type === 'AssignmentPattern') {
if (node.left.type === 'Identifier') {
- bindings[node.left.name] = isContextCall
+ bindings[node.left.name] = isUseOptionsCall
? BindingTypes.CONST
: BindingTypes.SETUP
} else {