wip: compileScript

This commit is contained in:
Evan You 2022-06-10 23:46:47 +08:00
parent 4306cc67b9
commit ba08e96877
7 changed files with 2473 additions and 420 deletions

View File

@ -0,0 +1,423 @@
// https://github.com/vuejs/core/blob/main/packages/compiler-core/src/babelUtils.ts
// should only use types from @babel/types
// do not import runtime methods
import type {
Identifier,
Node,
Function,
ObjectProperty,
BlockStatement,
Program
} from '@babel/types'
import { walk } from 'estree-walker'
export function walkIdentifiers(
root: Node,
onIdentifier: (
node: Identifier,
parent: Node,
parentStack: Node[],
isReference: boolean,
isLocal: boolean
) => void,
onNode?: (node: Node) => void
) {
const includeAll = false
const parentStack: Node[] = []
const knownIds: Record<string, number> = Object.create(null)
const rootExp =
root.type === 'Program' &&
root.body[0].type === 'ExpressionStatement' &&
root.body[0].expression
;(walk as any)(root, {
enter(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
parent && parentStack.push(parent)
if (
parent &&
parent.type.startsWith('TS') &&
parent.type !== 'TSAsExpression' &&
parent.type !== 'TSNonNullExpression' &&
parent.type !== 'TSTypeAssertion'
) {
return this.skip()
}
if (onNode) onNode(node)
if (node.type === 'Identifier') {
const isLocal = !!knownIds[node.name]
const isRefed = isReferencedIdentifier(node, parent!, parentStack)
if (includeAll || (isRefed && !isLocal)) {
onIdentifier(node, parent!, parentStack, isRefed, isLocal)
}
} else if (
node.type === 'ObjectProperty' &&
parent!.type === 'ObjectPattern'
) {
// mark property in destructure pattern
;(node as any).inPattern = true
} else if (isFunctionType(node)) {
// walk function expressions and add its arguments to known identifiers
// so that we don't prefix them
walkFunctionParams(node, id => markScopeIdentifier(node, id, knownIds))
} else if (node.type === 'BlockStatement') {
// #3445 record block-level local variables
walkBlockDeclarations(node, id =>
markScopeIdentifier(node, id, knownIds)
)
}
},
leave(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
parent && parentStack.pop()
if (node !== rootExp && node.scopeIds) {
for (const id of node.scopeIds) {
knownIds[id]--
if (knownIds[id] === 0) {
delete knownIds[id]
}
}
}
}
})
}
export function isReferencedIdentifier(
id: Identifier,
parent: Node | null,
parentStack: Node[]
) {
if (!parent) {
return true
}
// is a special keyword but parsed as identifier
if (id.name === 'arguments') {
return false
}
if (isReferenced(id, parent)) {
return true
}
// babel's isReferenced check returns false for ids being assigned to, so we
// need to cover those cases here
switch (parent.type) {
case 'AssignmentExpression':
case 'AssignmentPattern':
return true
case 'ObjectPattern':
case 'ArrayPattern':
return isInDestructureAssignment(parent, parentStack)
}
return false
}
export function isInDestructureAssignment(
parent: Node,
parentStack: Node[]
): boolean {
if (
parent &&
(parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern')
) {
let i = parentStack.length
while (i--) {
const p = parentStack[i]
if (p.type === 'AssignmentExpression') {
return true
} else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) {
break
}
}
}
return false
}
export function walkFunctionParams(
node: Function,
onIdent: (id: Identifier) => void
) {
for (const p of node.params) {
for (const id of extractIdentifiers(p)) {
onIdent(id)
}
}
}
export function walkBlockDeclarations(
block: BlockStatement | Program,
onIdent: (node: Identifier) => void
) {
for (const stmt of block.body) {
if (stmt.type === 'VariableDeclaration') {
if (stmt.declare) continue
for (const decl of stmt.declarations) {
for (const id of extractIdentifiers(decl.id)) {
onIdent(id)
}
}
} else if (
stmt.type === 'FunctionDeclaration' ||
stmt.type === 'ClassDeclaration'
) {
if (stmt.declare || !stmt.id) continue
onIdent(stmt.id)
}
}
}
export function extractIdentifiers(
param: Node,
nodes: Identifier[] = []
): Identifier[] {
switch (param.type) {
case 'Identifier':
nodes.push(param)
break
case 'MemberExpression':
let object: any = param
while (object.type === 'MemberExpression') {
object = object.object
}
nodes.push(object)
break
case 'ObjectPattern':
for (const prop of param.properties) {
if (prop.type === 'RestElement') {
extractIdentifiers(prop.argument, nodes)
} else {
extractIdentifiers(prop.value, nodes)
}
}
break
case 'ArrayPattern':
param.elements.forEach(element => {
if (element) extractIdentifiers(element, nodes)
})
break
case 'RestElement':
extractIdentifiers(param.argument, nodes)
break
case 'AssignmentPattern':
extractIdentifiers(param.left, nodes)
break
}
return nodes
}
function markScopeIdentifier(
node: Node & { scopeIds?: Set<string> },
child: Identifier,
knownIds: Record<string, number>
) {
const { name } = child
if (node.scopeIds && node.scopeIds.has(name)) {
return
}
if (name in knownIds) {
knownIds[name]++
} else {
knownIds[name] = 1
}
;(node.scopeIds || (node.scopeIds = new Set())).add(name)
}
export const isFunctionType = (node: Node): node is Function => {
return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
}
export const isStaticProperty = (node: Node): node is ObjectProperty =>
node &&
(node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
!node.computed
export const isStaticPropertyKey = (node: Node, parent: Node) =>
isStaticProperty(parent) && parent.key === node
/**
* Copied from https://github.com/babel/babel/blob/main/packages/babel-types/src/validators/isReferenced.ts
* To avoid runtime dependency on @babel/types (which includes process references)
* This file should not change very often in babel but we may need to keep it
* up-to-date from time to time.
*
* https://github.com/babel/babel/blob/main/LICENSE
*
*/
function isReferenced(node: Node, parent: Node, grandparent?: Node): boolean {
switch (parent.type) {
// yes: PARENT[NODE]
// yes: NODE.child
// no: parent.NODE
case 'MemberExpression':
case 'OptionalMemberExpression':
if (parent.property === node) {
return !!parent.computed
}
return parent.object === node
case 'JSXMemberExpression':
return parent.object === node
// no: let NODE = init;
// yes: let id = NODE;
case 'VariableDeclarator':
return parent.init === node
// yes: () => NODE
// no: (NODE) => {}
case 'ArrowFunctionExpression':
return parent.body === node
// no: class { #NODE; }
// no: class { get #NODE() {} }
// no: class { #NODE() {} }
// no: class { fn() { return this.#NODE; } }
case 'PrivateName':
return false
// no: class { NODE() {} }
// yes: class { [NODE]() {} }
// no: class { foo(NODE) {} }
case 'ClassMethod':
case 'ClassPrivateMethod':
case 'ObjectMethod':
if (parent.key === node) {
return !!parent.computed
}
return false
// yes: { [NODE]: "" }
// no: { NODE: "" }
// depends: { NODE }
// depends: { key: NODE }
case 'ObjectProperty':
if (parent.key === node) {
return !!parent.computed
}
// parent.value === node
return !grandparent || grandparent.type !== 'ObjectPattern'
// no: class { NODE = value; }
// yes: class { [NODE] = value; }
// yes: class { key = NODE; }
case 'ClassProperty':
if (parent.key === node) {
return !!parent.computed
}
return true
case 'ClassPrivateProperty':
return parent.key !== node
// no: class NODE {}
// yes: class Foo extends NODE {}
case 'ClassDeclaration':
case 'ClassExpression':
return parent.superClass === node
// yes: left = NODE;
// no: NODE = right;
case 'AssignmentExpression':
return parent.right === node
// no: [NODE = foo] = [];
// yes: [foo = NODE] = [];
case 'AssignmentPattern':
return parent.right === node
// no: NODE: for (;;) {}
case 'LabeledStatement':
return false
// no: try {} catch (NODE) {}
case 'CatchClause':
return false
// no: function foo(...NODE) {}
case 'RestElement':
return false
case 'BreakStatement':
case 'ContinueStatement':
return false
// no: function NODE() {}
// no: function foo(NODE) {}
case 'FunctionDeclaration':
case 'FunctionExpression':
return false
// no: export NODE from "foo";
// no: export * as NODE from "foo";
case 'ExportNamespaceSpecifier':
case 'ExportDefaultSpecifier':
return false
// no: export { foo as NODE };
// yes: export { NODE as foo };
// no: export { NODE as foo } from "foo";
case 'ExportSpecifier':
// @ts-expect-error
if (grandparent?.source) {
return false
}
return parent.local === node
// no: import NODE from "foo";
// no: import * as NODE from "foo";
// no: import { NODE as foo } from "foo";
// no: import { foo as NODE } from "foo";
// no: import NODE from "bar";
case 'ImportDefaultSpecifier':
case 'ImportNamespaceSpecifier':
case 'ImportSpecifier':
return false
// no: import "foo" assert { NODE: "json" }
case 'ImportAttribute':
return false
// no: <div NODE="foo" />
case 'JSXAttribute':
return false
// no: [NODE] = [];
// no: ({ NODE }) = [];
case 'ObjectPattern':
case 'ArrayPattern':
return false
// no: new.NODE
// no: NODE.target
case 'MetaProperty':
return false
// yes: type X = { someProperty: NODE }
// no: type X = { NODE: OtherType }
case 'ObjectTypeProperty':
return parent.key !== node
// yes: enum X { Foo = NODE }
// no: enum X { NODE }
case 'TSEnumMember':
return parent.id !== node
// yes: { [NODE]: value }
// no: { NODE: value }
case 'TSPropertySignature':
if (parent.key === node) {
return !!parent.computed
}
return true
}
return true
}

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,4 @@
import {
VueTemplateCompiler,
VueTemplateCompilerOptions,
ErrorWithRange
} from './types'
import { VueTemplateCompiler, VueTemplateCompilerOptions } from './types'
import assetUrlsModule, {
AssetURLOptions,
TransformAssetUrlsOptions
@ -11,6 +7,7 @@ import srcsetModule from './templateCompilerModules/srcset'
import consolidate from '@vue/consolidate'
import * as _compiler from 'web/entry-compiler'
import { stripWith } from './stripWith'
import { WarningMessage } from 'types/compiler'
export interface TemplateCompileOptions {
source: string
@ -33,8 +30,8 @@ export interface TemplateCompileResult {
ast: Object | undefined
code: string
source: string
tips: (string | ErrorWithRange)[]
errors: (string | ErrorWithRange)[]
tips: (string | WarningMessage)[]
errors: (string | WarningMessage)[]
}
export function compileTemplate(

View File

@ -2,7 +2,8 @@ import deindent from 'de-indent'
import { parseHTML } from 'compiler/parser/html-parser'
import { makeMap } from 'shared/util'
import { ASTAttr, WarningMessage } from 'types/compiler'
import { RawSourceMap } from './types'
import { BindingMetadata, RawSourceMap } from './types'
import { hmrShouldReload, ImportBinding } from './compileScript'
const splitRE = /\r?\n/g
const replaceRE = /./g
@ -24,12 +25,39 @@ export interface SFCBlock extends SFCCustomBlock {
module?: string | boolean
}
export interface SFCScriptBlock extends SFCBlock {
type: 'script'
setup?: string | boolean
bindings?: BindingMetadata
imports?: Record<string, ImportBinding>
/**
* import('\@babel/types').Statement
*/
scriptAst?: any[]
/**
* import('\@babel/types').Statement
*/
scriptSetupAst?: any[]
}
export interface SFCDescriptor {
source: string
template: SFCBlock | null
script: SFCBlock | null
script: SFCScriptBlock | null
scriptSetup: SFCScriptBlock | null
styles: SFCBlock[]
customBlocks: SFCCustomBlock[]
errors: WarningMessage[]
/**
* compare with an existing descriptor to determine whether HMR should perform
* a reload vs. re-render.
*
* Note: this comparison assumes the prev/next script are already identical,
* and only checks the special case where <script setup lang="ts"> unused import
* pruning result changes due to template changes.
*/
shouldForceReload: (prevImports: Record<string, ImportBinding>) => boolean
}
export interface VueTemplateCompilerParseOptions {
@ -46,11 +74,14 @@ export function parseComponent(
options: VueTemplateCompilerParseOptions = {}
): SFCDescriptor {
const sfc: SFCDescriptor = {
source: content,
template: null,
script: null,
scriptSetup: null, // TODO
styles: [],
customBlocks: [],
errors: []
errors: [],
shouldForceReload: prevImports => hmrShouldReload(prevImports, sfc)
}
let depth = 0
let currentBlock: SFCBlock | null = null

View File

@ -1,16 +1,7 @@
import MagicString from 'magic-string'
import { parseExpression, ParserOptions, ParserPlugin } from '@babel/parser'
import { walk } from 'estree-walker'
import { makeMap } from 'shared/util'
import type {
Identifier,
Node,
Function,
ObjectProperty,
BlockStatement,
Program
} from '@babel/types'
import { walkIdentifiers } from './babelUtils'
const doNotPrefix = makeMap(
'Infinity,undefined,NaN,isFinite,isNaN,' +
@ -46,23 +37,15 @@ export function stripWith(
plugins
})
const parentStack: Node[] = []
const knownIds: Record<string, number> = Object.create(null)
// based on https://github.com/vuejs/core/blob/main/packages/compiler-core/src/babelUtils.ts
;(walk as any)(ast, {
enter(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
parent && parentStack.push(parent)
if (
parent &&
parent.type.startsWith('TS') &&
parent.type !== 'TSAsExpression' &&
parent.type !== 'TSNonNullExpression' &&
parent.type !== 'TSTypeAssertion'
) {
return this.skip()
walkIdentifiers(
ast,
ident => {
if (doNotPrefix(ident.name)) {
return
}
s.prependRight(ident.start!, '_vm.')
},
node => {
if (node.type === 'WithStatement') {
s.remove(node.start!, node.body.start! + 1)
s.remove(node.end! - 1, node.end!)
@ -70,383 +53,8 @@ export function stripWith(
s.prependRight(node.start!, `var _vm=this;var _c=_vm._self._c;`)
}
}
if (node.type === 'Identifier') {
const isLocal = !!knownIds[node.name]
const isRefed = isReferencedIdentifier(node, parent!, parentStack)
if (isRefed && !isLocal) {
if (doNotPrefix(node.name)) {
return
}
s.prependRight(node.start!, '_vm.')
}
} else if (
node.type === 'ObjectProperty' &&
parent!.type === 'ObjectPattern'
) {
// mark property in destructure pattern
;(node as any).inPattern = true
} else if (isFunctionType(node)) {
// walk function expressions and add its arguments to known identifiers
// so that we don't prefix them
walkFunctionParams(node, id => markScopeIdentifier(node, id, knownIds))
} else if (node.type === 'BlockStatement') {
// #3445 record block-level local variables
walkBlockDeclarations(node, id =>
markScopeIdentifier(node, id, knownIds)
)
}
},
leave(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
parent && parentStack.pop()
if (node !== ast && node.scopeIds) {
for (const id of node.scopeIds) {
knownIds[id]--
if (knownIds[id] === 0) {
delete knownIds[id]
}
}
}
}
})
)
return s.toString()
}
export function isReferencedIdentifier(
id: Identifier,
parent: Node | null,
parentStack: Node[]
) {
if (!parent) {
return true
}
// is a special keyword but parsed as identifier
if (id.name === 'arguments') {
return false
}
if (isReferenced(id, parent)) {
return true
}
// babel's isReferenced check returns false for ids being assigned to, so we
// need to cover those cases here
switch (parent.type) {
case 'AssignmentExpression':
case 'AssignmentPattern':
return true
case 'ObjectPattern':
case 'ArrayPattern':
return isInDestructureAssignment(parent, parentStack)
}
return false
}
export function isInDestructureAssignment(
parent: Node,
parentStack: Node[]
): boolean {
if (
parent &&
(parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern')
) {
let i = parentStack.length
while (i--) {
const p = parentStack[i]
if (p.type === 'AssignmentExpression') {
return true
} else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) {
break
}
}
}
return false
}
export function walkFunctionParams(
node: Function,
onIdent: (id: Identifier) => void
) {
for (const p of node.params) {
for (const id of extractIdentifiers(p)) {
onIdent(id)
}
}
}
export function walkBlockDeclarations(
block: BlockStatement | Program,
onIdent: (node: Identifier) => void
) {
for (const stmt of block.body) {
if (stmt.type === 'VariableDeclaration') {
if (stmt.declare) continue
for (const decl of stmt.declarations) {
for (const id of extractIdentifiers(decl.id)) {
onIdent(id)
}
}
} else if (
stmt.type === 'FunctionDeclaration' ||
stmt.type === 'ClassDeclaration'
) {
if (stmt.declare || !stmt.id) continue
onIdent(stmt.id)
}
}
}
export function extractIdentifiers(
param: Node,
nodes: Identifier[] = []
): Identifier[] {
switch (param.type) {
case 'Identifier':
nodes.push(param)
break
case 'MemberExpression':
let object: any = param
while (object.type === 'MemberExpression') {
object = object.object
}
nodes.push(object)
break
case 'ObjectPattern':
for (const prop of param.properties) {
if (prop.type === 'RestElement') {
extractIdentifiers(prop.argument, nodes)
} else {
extractIdentifiers(prop.value, nodes)
}
}
break
case 'ArrayPattern':
param.elements.forEach(element => {
if (element) extractIdentifiers(element, nodes)
})
break
case 'RestElement':
extractIdentifiers(param.argument, nodes)
break
case 'AssignmentPattern':
extractIdentifiers(param.left, nodes)
break
}
return nodes
}
export function markScopeIdentifier(
node: Node & { scopeIds?: Set<string> },
child: Identifier,
knownIds: Record<string, number>
) {
const { name } = child
if (node.scopeIds && node.scopeIds.has(name)) {
return
}
if (name in knownIds) {
knownIds[name]++
} else {
knownIds[name] = 1
}
;(node.scopeIds || (node.scopeIds = new Set())).add(name)
}
export const isFunctionType = (node: Node): node is Function => {
return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
}
export const isStaticProperty = (node: Node): node is ObjectProperty =>
node &&
(node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
!node.computed
export const isStaticPropertyKey = (node: Node, parent: Node) =>
isStaticProperty(parent) && parent.key === node
/**
* Copied from https://github.com/babel/babel/blob/main/packages/babel-types/src/validators/isReferenced.ts
* To avoid runtime dependency on @babel/types (which includes process references)
* This file should not change very often in babel but we may need to keep it
* up-to-date from time to time.
*
* https://github.com/babel/babel/blob/main/LICENSE
*
*/
function isReferenced(node: Node, parent: Node, grandparent?: Node): boolean {
switch (parent.type) {
// yes: PARENT[NODE]
// yes: NODE.child
// no: parent.NODE
case 'MemberExpression':
case 'OptionalMemberExpression':
if (parent.property === node) {
return !!parent.computed
}
return parent.object === node
case 'JSXMemberExpression':
return parent.object === node
// no: let NODE = init;
// yes: let id = NODE;
case 'VariableDeclarator':
return parent.init === node
// yes: () => NODE
// no: (NODE) => {}
case 'ArrowFunctionExpression':
return parent.body === node
// no: class { #NODE; }
// no: class { get #NODE() {} }
// no: class { #NODE() {} }
// no: class { fn() { return this.#NODE; } }
case 'PrivateName':
return false
// no: class { NODE() {} }
// yes: class { [NODE]() {} }
// no: class { foo(NODE) {} }
case 'ClassMethod':
case 'ClassPrivateMethod':
case 'ObjectMethod':
if (parent.key === node) {
return !!parent.computed
}
return false
// yes: { [NODE]: "" }
// no: { NODE: "" }
// depends: { NODE }
// depends: { key: NODE }
case 'ObjectProperty':
if (parent.key === node) {
return !!parent.computed
}
// parent.value === node
return !grandparent || grandparent.type !== 'ObjectPattern'
// no: class { NODE = value; }
// yes: class { [NODE] = value; }
// yes: class { key = NODE; }
case 'ClassProperty':
if (parent.key === node) {
return !!parent.computed
}
return true
case 'ClassPrivateProperty':
return parent.key !== node
// no: class NODE {}
// yes: class Foo extends NODE {}
case 'ClassDeclaration':
case 'ClassExpression':
return parent.superClass === node
// yes: left = NODE;
// no: NODE = right;
case 'AssignmentExpression':
return parent.right === node
// no: [NODE = foo] = [];
// yes: [foo = NODE] = [];
case 'AssignmentPattern':
return parent.right === node
// no: NODE: for (;;) {}
case 'LabeledStatement':
return false
// no: try {} catch (NODE) {}
case 'CatchClause':
return false
// no: function foo(...NODE) {}
case 'RestElement':
return false
case 'BreakStatement':
case 'ContinueStatement':
return false
// no: function NODE() {}
// no: function foo(NODE) {}
case 'FunctionDeclaration':
case 'FunctionExpression':
return false
// no: export NODE from "foo";
// no: export * as NODE from "foo";
case 'ExportNamespaceSpecifier':
case 'ExportDefaultSpecifier':
return false
// no: export { foo as NODE };
// yes: export { NODE as foo };
// no: export { NODE as foo } from "foo";
case 'ExportSpecifier':
// @ts-expect-error
if (grandparent?.source) {
return false
}
return parent.local === node
// no: import NODE from "foo";
// no: import * as NODE from "foo";
// no: import { NODE as foo } from "foo";
// no: import { foo as NODE } from "foo";
// no: import NODE from "bar";
case 'ImportDefaultSpecifier':
case 'ImportNamespaceSpecifier':
case 'ImportSpecifier':
return false
// no: import "foo" assert { NODE: "json" }
case 'ImportAttribute':
return false
// no: <div NODE="foo" />
case 'JSXAttribute':
return false
// no: [NODE] = [];
// no: ({ NODE }) = [];
case 'ObjectPattern':
case 'ArrayPattern':
return false
// no: new.NODE
// no: NODE.target
case 'MetaProperty':
return false
// yes: type X = { someProperty: NODE }
// no: type X = { NODE: OtherType }
case 'ObjectTypeProperty':
return parent.key !== node
// yes: enum X { Foo = NODE }
// no: enum X { NODE }
case 'TSEnumMember':
return parent.id !== node
// yes: { [NODE]: value }
// no: { NODE: value }
case 'TSPropertySignature':
if (parent.key === node) {
return !!parent.computed
}
return true
}
return true
}

View File

@ -1,3 +1,4 @@
import { WarningMessage } from 'types/compiler'
import { SFCDescriptor } from './parseComponent'
export interface StartOfSourceMap {
@ -37,16 +38,58 @@ export interface VueTemplateCompilerOptions {
directives?: { [key: string]: Function }
}
export interface ErrorWithRange {
msg: string
start: number
end: number
}
export interface VueTemplateCompilerResults {
ast: Object | undefined
render: string
staticRenderFns: string[]
errors: (string | ErrorWithRange)[]
tips: (string | ErrorWithRange)[]
errors: (string | WarningMessage)[]
tips: (string | WarningMessage)[]
}
export const enum BindingTypes {
/**
* returned from data()
*/
DATA = 'data',
/**
* declared as a prop
*/
PROPS = 'props',
/**
* a local alias of a `<script setup>` destructured prop.
* the original is stored in __propsAliases of the bindingMetadata object.
*/
PROPS_ALIASED = 'props-aliased',
/**
* a let binding (may or may not be a ref)
*/
SETUP_LET = 'setup-let',
/**
* a const binding that can never be a ref.
* these bindings don't need `unref()` calls when processed in inlined
* template expressions.
*/
SETUP_CONST = 'setup-const',
/**
* a const binding that does not need `unref()`, but may be mutated.
*/
SETUP_REACTIVE_CONST = 'setup-reactive-const',
/**
* a const binding that may be a ref.
*/
SETUP_MAYBE_REF = 'setup-maybe-ref',
/**
* bindings that are guaranteed to be refs
*/
SETUP_REF = 'setup-ref',
/**
* declared by other options, e.g. computed, inject
*/
OPTIONS = 'options'
}
export type BindingMetadata = {
[key: string]: BindingTypes | undefined
} & {
__isScriptSetup?: boolean
}

View File

@ -46,6 +46,8 @@ export function initSetup(vm: Component) {
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 if (__DEV__ && setupResult !== undefined) {