Skip to content

Commit

Permalink
wip: handle script analyzed bindings when prefixing identifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Jun 15, 2022
1 parent 903d9b2 commit 452aa9d
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 31 deletions.
6 changes: 5 additions & 1 deletion packages/compiler-sfc/src/compileScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`)

Expand Down
26 changes: 20 additions & 6 deletions packages/compiler-sfc/src/compileTemplate.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { VueTemplateCompiler, VueTemplateCompilerOptions } from './types'
import {
BindingMetadata,
VueTemplateCompiler,
VueTemplateCompilerOptions
} from './types'
import assetUrlsModule, {
AssetURLOptions,
TransformAssetUrlsOptions
} from './templateCompilerModules/assetUrl'
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 {
Expand All @@ -24,6 +28,7 @@ export interface TemplateCompileOptions {
optimizeSSR?: boolean
prettify?: boolean
isTS?: boolean
bindings?: BindingMetadata
}

export interface TemplateCompileResult {
Expand Down Expand Up @@ -107,7 +112,8 @@ function actuallyCompile(
isFunctional = false,
optimizeSSR = false,
prettify = true,
isTS = false
isTS = false,
bindings
} = options

const compile =
Expand Down Expand Up @@ -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`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,' +
Expand All @@ -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}`

Expand All @@ -40,21 +42,45 @@ 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;` : `;`
}`
)
}
}
}
)

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}`
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { stripWith } from '../src/stripWith'
import { prefixIdentifiers } from '../src/prefixIdentifiers'
import { compile } from 'web/entry-compiler'
import { format } from 'prettier'

Expand All @@ -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'
})
Expand All @@ -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\\" } },
Expand All @@ -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: [],
Expand Down
3 changes: 2 additions & 1 deletion scripts/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
1 change: 1 addition & 0 deletions src/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
declare const __DEV__: boolean
declare const __TEST__: boolean

interface Window {
__VUE_DEVTOOLS_GLOBAL_HOOK__: DevtoolsHook
Expand Down
4 changes: 4 additions & 0 deletions src/types/compiler.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -28,6 +30,8 @@ export type CompilerOptions = {

// for ssr optimization compiler
scopeId?: string

bindingMetadata?: BindingMetadata
}

export type WarningMessage = {
Expand Down
1 change: 1 addition & 0 deletions src/types/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[]>
Expand Down
31 changes: 20 additions & 11 deletions src/v3/apiSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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)
})
}

Expand Down
3 changes: 2 additions & 1 deletion vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export default defineConfig({
}
},
define: {
__DEV__: true
__DEV__: true,
__TEST__: true
},
test: {
globals: true,
Expand Down

0 comments on commit 452aa9d

Please sign in to comment.