]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: defineContext -> useOptions
authorEvan You <yyx990803@gmail.com>
Fri, 13 Nov 2020 03:51:40 +0000 (22:51 -0500)
committerEvan You <yyx990803@gmail.com>
Fri, 13 Nov 2020 03:51:40 +0000 (22:51 -0500)
packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
packages/compiler-sfc/__tests__/compileScript.spec.ts
packages/compiler-sfc/src/compileScript.ts
packages/runtime-core/src/helpers/useOptions.ts [moved from packages/runtime-core/src/apiDefineContext.ts with 58% similarity]
packages/runtime-core/src/index.ts

index 6b95ef42840a7c8bb2cc29a9858cf8db0e424cce..c48e1ff45b1d593bd15a933cda4d40e420816f6a 100644 (file)
@@ -56,25 +56,7 @@ return { color }
 }"
 `;
 
-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 {
@@ -93,7 +75,7 @@ return { bar }
 }"
 `;
 
-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: {
@@ -393,7 +375,40 @@ return { a, b, c, d, x }
 }"
 `;
 
-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'
 
 
@@ -410,7 +425,7 @@ return { props, emit }
 })"
 `;
 
-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'
 
       
@@ -431,7 +446,7 @@ return { emit }
 })"
 `;
 
-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'
 
       
@@ -452,7 +467,7 @@ return { emit }
 })"
 `;
 
-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 {}
@@ -488,21 +503,6 @@ export default defineComponent({
 
       
       
-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 {  }
 }
 
index 67882f7156e6f34a093fb31e2878180938711321..b172368764209106917139ba6e3a14613f07c854 100644 (file)
@@ -36,11 +36,11 @@ describe('SFC compile <script setup>', () => {
     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
   },
@@ -60,8 +60,8 @@ const bar = 1
       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
@@ -143,7 +143,7 @@ const bar = 1
       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)
@@ -183,11 +183,11 @@ const bar = 1
       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']
 })
@@ -200,15 +200,15 @@ const { props, emit } = defineContext({
   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
@@ -288,11 +288,11 @@ const { props, emit } = defineContext({
       })
     })
 
-    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>
@@ -302,11 +302,11 @@ const { props, emit } = defineContext({
       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>
@@ -633,21 +633,21 @@ const { props, emit } = defineContext({
       ).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
@@ -658,24 +658,24 @@ const { props, emit } = defineContext({
       ).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
@@ -686,12 +686,12 @@ const { props, emit } = defineContext({
       )
     })
 
-    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
@@ -901,8 +901,8 @@ describe('SFC analyze <script> bindings', () => {
   it('works for script setup', () => {
     const { bindings } = compile(`
       <script setup>
-      import { defineContext } from 'vue'
-      defineContext({
+      import { useOptions } from 'vue'
+      useOptions({
         props: {
           foo: String,
         }
index b90b677f7c8f68a439b6faf6631f1b435647cb59..70b502d58f3b8d3d1a8e61f6c0dd20364e781b39 100644 (file)
@@ -28,7 +28,7 @@ import { genCssVarsCode, injectCssVarsCalls } from './genCssVars'
 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 {
   /**
@@ -143,10 +143,10 @@ export function compileScript(
   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)
@@ -183,39 +183,39 @@ export function compileScript(
     )
   }
 
-  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
           )
         }
@@ -513,7 +513,7 @@ export function compileScript(
           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,
@@ -545,18 +545,15 @@ export function compileScript(
 
     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 {
@@ -649,8 +646,8 @@ export function compileScript(
   }
 
   // 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(
@@ -688,13 +685,13 @@ export function compileScript(
     }
   }
 
-  // 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 ` +
@@ -725,8 +722,8 @@ export function compileScript(
   }
 
   // 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')
     }
@@ -761,13 +758,13 @@ export function compileScript(
   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')
@@ -818,11 +815,11 @@ export function compileScript(
 
   // 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)
   }
@@ -896,18 +893,18 @@ function walkDeclaration(
     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()
@@ -915,9 +912,9 @@ function walkDeclaration(
             ? 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 (
@@ -934,7 +931,7 @@ function walkObjectPattern(
   node: ObjectPattern,
   bindings: Record<string, BindingTypes>,
   isConst: boolean,
-  isContextCall = false
+  isUseOptionsCall = false
 ) {
   for (const p of node.properties) {
     if (p.type === 'ObjectProperty') {
@@ -942,11 +939,11 @@ function walkObjectPattern(
       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 {
@@ -963,10 +960,10 @@ function walkArrayPattern(
   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)
   }
 }
 
@@ -974,10 +971,10 @@ function walkPattern(
   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') {
@@ -991,7 +988,7 @@ function walkPattern(
     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 {
similarity index 58%
rename from packages/runtime-core/src/apiDefineContext.ts
rename to packages/runtime-core/src/helpers/useOptions.ts
index 25d4bd95fcdf41af4cd3725ebe5769e9ed08a011..172b2aaad1803d689e777ff7c89ba87396a8b8fb 100644 (file)
@@ -1,5 +1,5 @@
-import { Slots } from './componentSlots'
-import { warn } from './warning'
+import { Slots } from '../componentSlots'
+import { warn } from '../warning'
 
 interface DefaultContext {
   props: Record<string, unknown>
@@ -8,7 +8,13 @@ interface DefaultContext {
   slots: Slots
 }
 
-export function defineContext<T extends Partial<DefaultContext> = {}>(
+/**
+ * Compile-time-only helper used for declaring options and retrieving props
+ * and the setup context inside <script setup>.
+ * This is stripped away in the compiled code and should never be actually
+ * called at runtime.
+ */
+export function useOptions<T extends Partial<DefaultContext> = {}>(
   opts?: any // TODO infer
 ): { [K in keyof DefaultContext]: T[K] extends {} ? T[K] : DefaultContext[K] } {
   if (__DEV__) {
index 0c4879e1fc1b9c7c63e538e60d6491ddb1c64a68..22bf9f2ee8f23148d78aaddb80eada873343412d 100644 (file)
@@ -43,7 +43,7 @@ export { provide, inject } from './apiInject'
 export { nextTick } from './scheduler'
 export { defineComponent } from './apiDefineComponent'
 export { defineAsyncComponent } from './apiAsyncComponent'
-export { defineContext } from './apiDefineContext'
+export { useOptions } from './helpers/useOptions'
 
 // Advanced API ----------------------------------------------------------------