wip: handle script analyzed bindings when prefixing identifiers

This commit is contained in:
Evan You 2022-06-15 10:07:42 +08:00
parent 903d9b2eb6
commit 452aa9d29c
10 changed files with 92 additions and 31 deletions

View File

@ -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`)

View File

@ -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`

View File

@ -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}`
}
}

View File

@ -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: [],

View File

@ -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
src/global.d.ts vendored
View File

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

View File

@ -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 = {

View File

@ -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[]>

View File

@ -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)
})
}

View File

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