Browse Source

wip: handle script analyzed bindings when prefixing identifiers

Evan You 3 years ago
parent
commit
452aa9d29c

+ 5 - 1
packages/compiler-sfc/src/compileScript.ts

@@ -1209,7 +1209,11 @@ export function compileScript(
       allBindings[key] = true
     }
   }
-  const returned = `{ ${Object.keys(allBindings).join(', ')} }`
+  // __sfc marker indicates these bindings are compiled from <script setup>
+  // and should not be proxied on `this`
+  const returned = `{ ${__TEST__ ? `` : `__sfc: true,`}${Object.keys(
+    allBindings
+  ).join(', ')} }`
 
   s.appendRight(endOffset, `\nreturn ${returned}\n}\n\n`)
 

+ 20 - 6
packages/compiler-sfc/src/compileTemplate.ts

@@ -1,4 +1,8 @@
-import { VueTemplateCompiler, VueTemplateCompilerOptions } from './types'
+import {
+  BindingMetadata,
+  VueTemplateCompiler,
+  VueTemplateCompilerOptions
+} from './types'
 import assetUrlsModule, {
   AssetURLOptions,
   TransformAssetUrlsOptions
@@ -6,7 +10,7 @@ import assetUrlsModule, {
 import srcsetModule from './templateCompilerModules/srcset'
 import consolidate from '@vue/consolidate'
 import * as _compiler from 'web/entry-compiler'
-import { stripWith } from './stripWith'
+import { prefixIdentifiers } from './prefixIdentifiers'
 import { WarningMessage } from 'types/compiler'
 
 export interface TemplateCompileOptions {
@@ -24,6 +28,7 @@ export interface TemplateCompileOptions {
   optimizeSSR?: boolean
   prettify?: boolean
   isTS?: boolean
+  bindings?: BindingMetadata
 }
 
 export interface TemplateCompileResult {
@@ -107,7 +112,8 @@ function actuallyCompile(
     isFunctional = false,
     optimizeSSR = false,
     prettify = true,
-    isTS = false
+    isTS = false,
+    bindings
   } = options
 
   const compile =
@@ -144,15 +150,23 @@ function actuallyCompile(
     // transpile code with vue-template-es2015-compiler, which is a forked
     // version of Buble that applies ES2015 transforms + stripping `with` usage
     let code =
-      `var __render__ = ${stripWith(
+      `var __render__ = ${prefixIdentifiers(
         render,
         `render`,
         isFunctional,
         isTS,
-        transpileOptions
+        transpileOptions,
+        bindings
       )}\n` +
       `var __staticRenderFns__ = [${staticRenderFns.map(code =>
-        stripWith(code, ``, isFunctional, isTS, transpileOptions)
+        prefixIdentifiers(
+          code,
+          ``,
+          isFunctional,
+          isTS,
+          transpileOptions,
+          bindings
+        )
       )}]` +
       `\n`
 

+ 31 - 5
packages/compiler-sfc/src/stripWith.ts → packages/compiler-sfc/src/prefixIdentifiers.ts

@@ -2,6 +2,7 @@ import MagicString from 'magic-string'
 import { parseExpression, ParserOptions, ParserPlugin } from '@babel/parser'
 import { makeMap } from 'shared/util'
 import { walkIdentifiers } from './babelUtils'
+import { BindingMetadata } from './types'
 
 const doNotPrefix = makeMap(
   'Infinity,undefined,NaN,isFinite,isNaN,' +
@@ -16,12 +17,13 @@ const doNotPrefix = makeMap(
  * The input is expected to be the render function code directly returned from
  * `compile()` calls, e.g. `with(this){return ...}`
  */
-export function stripWith(
+export function prefixIdentifiers(
   source: string,
   fnName = '',
   isFunctional = false,
   isTS = false,
-  babelOptions: ParserOptions = {}
+  babelOptions: ParserOptions = {},
+  bindings?: BindingMetadata
 ) {
   source = `function ${fnName}(${isFunctional ? `_c,_vm` : ``}){${source}\n}`
 
@@ -40,17 +42,29 @@ export function stripWith(
   walkIdentifiers(
     ast,
     ident => {
-      if (doNotPrefix(ident.name)) {
+      const { name } = ident
+      if (doNotPrefix(name)) {
         return
       }
-      s.prependRight(ident.start!, '_vm.')
+
+      if (!bindings) {
+        s.prependRight(ident.start!, '_vm.')
+        return
+      }
+
+      s.overwrite(ident.start!, ident.end!, rewriteIdentifier(name, bindings))
     },
     node => {
       if (node.type === 'WithStatement') {
         s.remove(node.start!, node.body.start! + 1)
         s.remove(node.end! - 1, node.end!)
         if (!isFunctional) {
-          s.prependRight(node.start!, `var _vm=this;var _c=_vm._self._c;`)
+          s.prependRight(
+            node.start!,
+            `var _vm=this,_c=_vm._self._c${
+              bindings ? `,_setup=_vm._setupProxy;` : `;`
+            }`
+          )
         }
       }
     }
@@ -58,3 +72,15 @@ export function stripWith(
 
   return s.toString()
 }
+
+export function rewriteIdentifier(
+  name: string,
+  bindings: BindingMetadata
+): string {
+  const type = bindings[name]
+  if (type && type.startsWith('setup')) {
+    return `_setup.${name}`
+  } else {
+    return `_vm.${name}`
+  }
+}

+ 6 - 6
packages/compiler-sfc/test/stripWith.spec.ts → packages/compiler-sfc/test/prefixIdentifiers.spec.ts

@@ -1,4 +1,4 @@
-import { stripWith } from '../src/stripWith'
+import { prefixIdentifiers } from '../src/prefixIdentifiers'
 import { compile } from 'web/entry-compiler'
 import { format } from 'prettier'
 
@@ -11,7 +11,7 @@ it('should work', () => {
   </foo>
 </div>`)
 
-  const result = format(stripWith(render, `render`), {
+  const result = format(prefixIdentifiers(render, `render`), {
     semi: false,
     parser: 'babel'
   })
@@ -24,8 +24,8 @@ it('should work', () => {
 
   expect(result).toMatchInlineSnapshot(`
     "function render() {
-      var _vm = this
-      var _c = _vm._self._c
+      var _vm = this,
+        _c = _vm._self._c
       return _c(
         \\"div\\",
         { attrs: { id: \\"app\\" } },
@@ -39,8 +39,8 @@ it('should work', () => {
           _c(\\"foo\\", {
             inlineTemplate: {
               render: function () {
-                var _vm = this
-                var _c = _vm._self._c
+                var _vm = this,
+                  _c = _vm._self._c
                 return _c(\\"div\\", [_vm._v(_vm._s(_vm.bar))])
               },
               staticRenderFns: [],

+ 2 - 1
scripts/config.js

@@ -278,7 +278,8 @@ function genConfig(name) {
   // built-in vars
   const vars = {
     __VERSION__: version,
-    __DEV__: `process.env.NODE_ENV !== 'production'`
+    __DEV__: `process.env.NODE_ENV !== 'production'`,
+    __TEST__: false
   }
   // feature flags
   Object.keys(featureFlags).forEach(key => {

+ 1 - 0
src/global.d.ts

@@ -1,4 +1,5 @@
 declare const __DEV__: boolean
+declare const __TEST__: boolean
 
 interface Window {
   __VUE_DEVTOOLS_GLOBAL_HOOK__: DevtoolsHook

+ 4 - 0
src/types/compiler.ts

@@ -1,3 +1,5 @@
+import { BindingMetadata } from 'sfc/types'
+
 export type CompilerOptions = {
   warn?: Function // allow customizing warning in different environments; e.g. node
   modules?: Array<ModuleOptions> // platform specific modules; e.g. style; class
@@ -28,6 +30,8 @@ export type CompilerOptions = {
 
   // for ssr optimization compiler
   scopeId?: string
+
+  bindingMetadata?: BindingMetadata
 }
 
 export type WarningMessage = {

+ 1 - 0
src/types/component.ts

@@ -108,6 +108,7 @@ export declare class Component {
 
   // @v3
   _setupState?: Record<string, any>
+  _setupProxy?: Record<string, any>
   _setupContext?: SetupContext
   _attrsProxy?: Record<string, any>
   _slotsProxy?: Record<string, () => VNode[]>

+ 20 - 11
src/v3/apiSetup.ts

@@ -43,11 +43,20 @@ export function initSetup(vm: Component) {
         )
       }
       vm._setupState = setupResult
-      for (const key in setupResult) {
-        if (!isReserved(key)) {
-          proxySetupProperty(vm, setupResult, key)
-        } else if (__DEV__) {
-          warn(`Avoid using variables that start with _ or $ in setup().`)
+      // __sfc indicates compiled bindings from <script setup>
+      if (!setupResult.__sfc) {
+        for (const key in setupResult) {
+          if (!isReserved(key)) {
+            proxySetupProperty(vm, setupResult, key)
+          } else if (__DEV__) {
+            warn(`Avoid using variables that start with _ or $ in setup().`)
+          }
+        }
+      } else {
+        // exposed for compiled render fn
+        const proxy = (vm._setupProxy = {})
+        for (const key in setupResult) {
+          proxySetupProperty(proxy, setupResult, key)
         }
       }
     } else if (__DEV__ && setupResult !== undefined) {
@@ -61,17 +70,17 @@ export function initSetup(vm: Component) {
 }
 
 function proxySetupProperty(
-  vm: Component,
+  target: any,
   setupResult: Record<string, any>,
   key: string
 ) {
-  const raw = setupResult[key]
-  const unwrap = isRef(raw)
-  Object.defineProperty(vm, key, {
+  let raw = setupResult[key]
+  Object.defineProperty(target, key, {
     enumerable: true,
     configurable: true,
-    get: unwrap ? () => raw.value : () => setupResult[key],
-    set: unwrap ? v => (raw.value = v) : v => (setupResult[key] = v)
+    get: () => (isRef(raw) ? raw.value : raw),
+    set: newVal =>
+      isRef(raw) ? (raw.value = newVal) : (raw = setupResult[key] = newVal)
   })
 }
 

+ 2 - 1
vitest.config.ts

@@ -17,7 +17,8 @@ export default defineConfig({
     }
   },
   define: {
-    __DEV__: true
+    __DEV__: true,
+    __TEST__: true
   },
   test: {
     globals: true,